mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 06:37:35 +00:00
Move WindowServer into Servers.
This commit is contained in:
parent
9120b05a40
commit
d17a91f185
32 changed files with 14 additions and 14 deletions
3
Servers/WindowServer/.gitignore
vendored
Normal file
3
Servers/WindowServer/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
*.o
|
||||
*.d
|
||||
WindowServer
|
53
Servers/WindowServer/Makefile
Normal file
53
Servers/WindowServer/Makefile
Normal file
|
@ -0,0 +1,53 @@
|
|||
SHAREDGRAPHICS_OBJS = \
|
||||
../../SharedGraphics/Painter.o \
|
||||
../../SharedGraphics/Font.o \
|
||||
../../SharedGraphics/Rect.o \
|
||||
../../SharedGraphics/GraphicsBitmap.o \
|
||||
../../SharedGraphics/CharacterBitmap.o \
|
||||
../../SharedGraphics/DisjointRectSet.o \
|
||||
../../SharedGraphics/Color.o
|
||||
|
||||
WINDOWSERVER_OBJS = \
|
||||
WSMessageReceiver.o \
|
||||
WSMessageLoop.o \
|
||||
WSWindow.o \
|
||||
WSWindowManager.o \
|
||||
WSScreen.o \
|
||||
WSMenuBar.o \
|
||||
WSMenu.o \
|
||||
WSMenuItem.o \
|
||||
WSClientConnection.o \
|
||||
WSWindowSwitcher.o \
|
||||
WSClipboard.o \
|
||||
main.o
|
||||
|
||||
APP = WindowServer
|
||||
OBJS = $(SHAREDGRAPHICS_OBJS) $(WINDOWSERVER_OBJS)
|
||||
|
||||
STANDARD_FLAGS = -std=c++17
|
||||
WARNING_FLAGS = -Wextra -Wall -Wundef -Wcast-qual -Wwrite-strings -Wimplicit-fallthrough
|
||||
FLAVOR_FLAGS = -fno-exceptions -fno-rtti
|
||||
OPTIMIZATION_FLAGS = -Os
|
||||
INCLUDE_FLAGS = -I.. -I../.. -I. -I../../LibC
|
||||
LDFLAGS = -L../../LibC
|
||||
|
||||
DEFINES = -DSERENITY -DSANITIZE_PTRS -DUSERLAND
|
||||
|
||||
CXXFLAGS = -MMD -MP $(WARNING_FLAGS) $(OPTIMIZATION_FLAGS) $(FLAVOR_FLAGS) $(STANDARD_FLAGS) $(INCLUDE_FLAGS) $(DEFINES)
|
||||
CXX = i686-pc-serenity-g++
|
||||
LD = i686-pc-serenity-ld
|
||||
AR = i686-pc-serenity-ar
|
||||
|
||||
all: $(APP)
|
||||
|
||||
$(APP): $(OBJS)
|
||||
$(LD) -o $(APP) $(LDFLAGS) $(OBJS) -lc
|
||||
|
||||
.cpp.o:
|
||||
@echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $<
|
||||
|
||||
-include $(OBJS:%.o=%.d)
|
||||
|
||||
clean:
|
||||
@echo "CLEAN"; rm -f $(APPS) $(OBJS) *.d
|
||||
|
209
Servers/WindowServer/WSAPITypes.h
Normal file
209
Servers/WindowServer/WSAPITypes.h
Normal file
|
@ -0,0 +1,209 @@
|
|||
#pragma once
|
||||
|
||||
#include <SharedGraphics/Color.h>
|
||||
#include <SharedGraphics/Rect.h>
|
||||
|
||||
typedef unsigned WSAPI_Color;
|
||||
|
||||
struct WSAPI_Point {
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
struct WSAPI_Size {
|
||||
int width;
|
||||
int height;
|
||||
};
|
||||
|
||||
struct WSAPI_Rect {
|
||||
WSAPI_Point location;
|
||||
WSAPI_Size size;
|
||||
};
|
||||
|
||||
struct WSAPI_WindowParameters {
|
||||
WSAPI_Rect rect;
|
||||
Color background_color;
|
||||
unsigned flags { 0 };
|
||||
char title[128];
|
||||
};
|
||||
|
||||
struct WSAPI_WindowBackingStoreInfo {
|
||||
WSAPI_Size size;
|
||||
size_t bpp;
|
||||
size_t pitch;
|
||||
RGBA32* pixels;
|
||||
};
|
||||
|
||||
enum class WSAPI_MouseButton : unsigned char {
|
||||
NoButton = 0,
|
||||
Left = 1,
|
||||
Right = 2,
|
||||
Middle = 4,
|
||||
};
|
||||
|
||||
struct WSAPI_KeyModifiers { enum {
|
||||
Shift = 1 << 0,
|
||||
Alt = 1 << 1,
|
||||
Ctrl = 1 << 2,
|
||||
}; };
|
||||
|
||||
|
||||
struct WSAPI_ServerMessage {
|
||||
enum Type : unsigned {
|
||||
Invalid,
|
||||
Error,
|
||||
Paint,
|
||||
MouseMove,
|
||||
MouseDown,
|
||||
MouseUp,
|
||||
WindowEntered,
|
||||
WindowLeft,
|
||||
KeyDown,
|
||||
KeyUp,
|
||||
WindowActivated,
|
||||
WindowDeactivated,
|
||||
WindowResized,
|
||||
WindowCloseRequest,
|
||||
MenuItemActivated,
|
||||
DidCreateMenubar,
|
||||
DidDestroyMenubar,
|
||||
DidCreateMenu,
|
||||
DidDestroyMenu,
|
||||
DidAddMenuToMenubar,
|
||||
DidSetApplicationMenubar,
|
||||
DidAddMenuItem,
|
||||
DidAddMenuSeparator,
|
||||
DidCreateWindow,
|
||||
DidDestroyWindow,
|
||||
DidGetWindowTitle,
|
||||
DidGetWindowRect,
|
||||
DidGetWindowBackingStore,
|
||||
Greeting,
|
||||
DidGetClipboardContents,
|
||||
DidSetClipboardContents,
|
||||
DidSetWindowBackingStore,
|
||||
};
|
||||
Type type { Invalid };
|
||||
int window_id { -1 };
|
||||
int text_length { 0 };
|
||||
char text[256];
|
||||
|
||||
union {
|
||||
struct {
|
||||
int server_pid;
|
||||
} greeting;
|
||||
struct {
|
||||
WSAPI_Rect rect;
|
||||
WSAPI_Rect old_rect;
|
||||
} window;
|
||||
struct {
|
||||
WSAPI_Rect rect;
|
||||
WSAPI_Size window_size;
|
||||
} paint;
|
||||
struct {
|
||||
WSAPI_Point position;
|
||||
WSAPI_MouseButton button;
|
||||
unsigned buttons;
|
||||
byte modifiers;
|
||||
} mouse;
|
||||
struct {
|
||||
char character;
|
||||
byte key;
|
||||
byte modifiers;
|
||||
bool ctrl : 1;
|
||||
bool alt : 1;
|
||||
bool shift : 1;
|
||||
} key;
|
||||
struct {
|
||||
int menubar_id;
|
||||
int menu_id;
|
||||
unsigned identifier;
|
||||
} menu;
|
||||
struct {
|
||||
WSAPI_Size size;
|
||||
size_t bpp;
|
||||
size_t pitch;
|
||||
int shared_buffer_id;
|
||||
bool has_alpha_channel;
|
||||
} backing;
|
||||
struct {
|
||||
int shared_buffer_id;
|
||||
int contents_size;
|
||||
} clipboard;
|
||||
};
|
||||
};
|
||||
|
||||
struct WSAPI_ClientMessage {
|
||||
enum Type : unsigned {
|
||||
Invalid,
|
||||
CreateMenubar,
|
||||
DestroyMenubar,
|
||||
CreateMenu,
|
||||
DestroyMenu,
|
||||
AddMenuToMenubar,
|
||||
SetApplicationMenubar,
|
||||
AddMenuItem,
|
||||
AddMenuSeparator,
|
||||
CreateWindow,
|
||||
DestroyWindow,
|
||||
SetWindowTitle,
|
||||
GetWindowTitle,
|
||||
SetWindowRect,
|
||||
GetWindowRect,
|
||||
InvalidateRect,
|
||||
DidFinishPainting,
|
||||
GetWindowBackingStore,
|
||||
SetGlobalCursorTracking,
|
||||
SetWindowOpacity,
|
||||
SetWindowBackingStore,
|
||||
GetClipboardContents,
|
||||
SetClipboardContents,
|
||||
Greeting,
|
||||
};
|
||||
Type type { Invalid };
|
||||
int window_id { -1 };
|
||||
int text_length { 0 };
|
||||
char text[256];
|
||||
int value { 0 };
|
||||
|
||||
union {
|
||||
struct {
|
||||
int client_pid;
|
||||
} greeting;
|
||||
struct {
|
||||
int menubar_id;
|
||||
int menu_id;
|
||||
unsigned identifier;
|
||||
char shortcut_text[32];
|
||||
int shortcut_text_length;
|
||||
} menu;
|
||||
struct {
|
||||
WSAPI_Rect rect;
|
||||
bool has_alpha_channel;
|
||||
bool modal;
|
||||
bool resizable;
|
||||
float opacity;
|
||||
WSAPI_Size base_size;
|
||||
WSAPI_Size size_increment;
|
||||
} window;
|
||||
struct {
|
||||
WSAPI_Size size;
|
||||
size_t bpp;
|
||||
size_t pitch;
|
||||
int shared_buffer_id;
|
||||
bool has_alpha_channel;
|
||||
bool flush_immediately;
|
||||
} backing;
|
||||
struct {
|
||||
int shared_buffer_id;
|
||||
int contents_size;
|
||||
} clipboard;
|
||||
};
|
||||
};
|
||||
|
||||
inline Rect::Rect(const WSAPI_Rect& r) : Rect(r.location, r.size) { }
|
||||
inline Point::Point(const WSAPI_Point& p) : Point(p.x, p.y) { }
|
||||
inline Size::Size(const WSAPI_Size& s) : Size(s.width, s.height) { }
|
||||
inline Rect::operator WSAPI_Rect() const { return { m_location, m_size }; }
|
||||
inline Point::operator WSAPI_Point() const { return { m_x, m_y }; }
|
||||
inline Size::operator WSAPI_Size() const { return { m_width, m_height }; }
|
544
Servers/WindowServer/WSClientConnection.cpp
Normal file
544
Servers/WindowServer/WSClientConnection.cpp
Normal file
|
@ -0,0 +1,544 @@
|
|||
#include <WindowServer/WSClientConnection.h>
|
||||
#include <WindowServer/WSMessageLoop.h>
|
||||
#include <WindowServer/WSMenuBar.h>
|
||||
#include <WindowServer/WSMenu.h>
|
||||
#include <WindowServer/WSMenuItem.h>
|
||||
#include <WindowServer/WSWindow.h>
|
||||
#include <WindowServer/WSWindowManager.h>
|
||||
#include <WindowServer/WSAPITypes.h>
|
||||
#include <WindowServer/WSClipboard.h>
|
||||
#include <SharedBuffer.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
|
||||
HashMap<int, WSClientConnection*>* s_connections;
|
||||
|
||||
void WSClientConnection::for_each_client(Function<void(WSClientConnection&)> callback)
|
||||
{
|
||||
if (!s_connections)
|
||||
return;
|
||||
for (auto& it : *s_connections) {
|
||||
callback(*it.value);
|
||||
}
|
||||
}
|
||||
|
||||
WSClientConnection* WSClientConnection::from_client_id(int client_id)
|
||||
{
|
||||
if (!s_connections)
|
||||
return nullptr;
|
||||
auto it = s_connections->find(client_id);
|
||||
if (it == s_connections->end())
|
||||
return nullptr;
|
||||
return (*it).value;
|
||||
}
|
||||
|
||||
WSClientConnection::WSClientConnection(int fd)
|
||||
: m_fd(fd)
|
||||
{
|
||||
static int s_next_client_id = 0;
|
||||
m_client_id = ++s_next_client_id;
|
||||
|
||||
int rc = ioctl(m_fd, 413, (int)&m_pid);
|
||||
ASSERT(rc == 0);
|
||||
|
||||
if (!s_connections)
|
||||
s_connections = new HashMap<int, WSClientConnection*>;
|
||||
s_connections->set(m_client_id, this);
|
||||
|
||||
WSAPI_ServerMessage message;
|
||||
message.type = WSAPI_ServerMessage::Type::Greeting;
|
||||
message.greeting.server_pid = getpid();
|
||||
post_message(message);
|
||||
}
|
||||
|
||||
WSClientConnection::~WSClientConnection()
|
||||
{
|
||||
s_connections->remove(m_client_id);
|
||||
int rc = close(m_fd);
|
||||
ASSERT(rc == 0);
|
||||
}
|
||||
|
||||
void WSClientConnection::post_error(const String& error_message)
|
||||
{
|
||||
dbgprintf("WSClientConnection::post_error: client_id=%d: %s\n", m_client_id, error_message.characters());
|
||||
WSAPI_ServerMessage message;
|
||||
message.type = WSAPI_ServerMessage::Type::Error;
|
||||
ASSERT(error_message.length() < (ssize_t)sizeof(message.text));
|
||||
strcpy(message.text, error_message.characters());
|
||||
message.text_length = error_message.length();
|
||||
post_message(message);
|
||||
}
|
||||
|
||||
void WSClientConnection::post_message(const WSAPI_ServerMessage& message)
|
||||
{
|
||||
int nwritten = write(m_fd, &message, sizeof(message));
|
||||
if (nwritten < 0) {
|
||||
if (errno == EPIPE) {
|
||||
dbgprintf("WSClientConnection::post_message: Disconnected from peer.\n");
|
||||
return;
|
||||
}
|
||||
perror("WSClientConnection::post_message write");
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
ASSERT(nwritten == sizeof(message));
|
||||
}
|
||||
|
||||
RetainPtr<GraphicsBitmap> WSClientConnection::create_shared_bitmap(GraphicsBitmap::Format format, const Size& size)
|
||||
{
|
||||
auto shared_buffer = SharedBuffer::create(m_pid, size.area() * sizeof(RGBA32));
|
||||
ASSERT(shared_buffer);
|
||||
return GraphicsBitmap::create_with_shared_buffer(format, *shared_buffer, size);
|
||||
}
|
||||
|
||||
void WSClientConnection::on_message(WSMessage& message)
|
||||
{
|
||||
if (message.is_client_request()) {
|
||||
on_request(static_cast<WSAPIClientRequest&>(message));
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.type() == WSMessage::WM_ClientDisconnected) {
|
||||
int client_id = static_cast<WSClientDisconnectedNotification&>(message).client_id();
|
||||
dbgprintf("WSClientConnection: Client disconnected: %d\n", client_id);
|
||||
delete this;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void WSClientConnection::handle_request(WSAPICreateMenubarRequest&)
|
||||
{
|
||||
int menubar_id = m_next_menubar_id++;
|
||||
auto menubar = make<WSMenuBar>(*this, menubar_id);
|
||||
m_menubars.set(menubar_id, move(menubar));
|
||||
WSAPI_ServerMessage response;
|
||||
response.type = WSAPI_ServerMessage::Type::DidCreateMenubar;
|
||||
response.menu.menubar_id = menubar_id;
|
||||
post_message(response);
|
||||
}
|
||||
|
||||
void WSClientConnection::handle_request(WSAPIDestroyMenubarRequest& request)
|
||||
{
|
||||
int menubar_id = request.menubar_id();
|
||||
auto it = m_menubars.find(menubar_id);
|
||||
if (it == m_menubars.end()) {
|
||||
post_error("Bad menubar ID");
|
||||
return;
|
||||
}
|
||||
auto& menubar = *(*it).value;
|
||||
WSWindowManager::the().close_menubar(menubar);
|
||||
m_menubars.remove(it);
|
||||
WSAPI_ServerMessage response;
|
||||
response.type = WSAPI_ServerMessage::Type::DidDestroyMenubar;
|
||||
response.menu.menubar_id = menubar_id;
|
||||
post_message(response);
|
||||
}
|
||||
|
||||
void WSClientConnection::handle_request(WSAPICreateMenuRequest& request)
|
||||
{
|
||||
int menu_id = m_next_menu_id++;
|
||||
auto menu = make<WSMenu>(this, menu_id, request.text());
|
||||
m_menus.set(menu_id, move(menu));
|
||||
WSAPI_ServerMessage response;
|
||||
response.type = WSAPI_ServerMessage::Type::DidCreateMenu;
|
||||
response.menu.menu_id = menu_id;
|
||||
post_message(response);
|
||||
}
|
||||
|
||||
void WSClientConnection::handle_request(WSAPIDestroyMenuRequest& request)
|
||||
{
|
||||
int menu_id = static_cast<WSAPIDestroyMenuRequest&>(request).menu_id();
|
||||
auto it = m_menus.find(menu_id);
|
||||
if (it == m_menus.end()) {
|
||||
post_error("Bad menu ID");
|
||||
return;
|
||||
}
|
||||
auto& menu = *(*it).value;
|
||||
WSWindowManager::the().close_menu(menu);
|
||||
m_menus.remove(it);
|
||||
WSAPI_ServerMessage response;
|
||||
response.type = WSAPI_ServerMessage::Type::DidDestroyMenu;
|
||||
response.menu.menu_id = menu_id;
|
||||
post_message(response);
|
||||
}
|
||||
|
||||
void WSClientConnection::handle_request(WSAPISetApplicationMenubarRequest& request)
|
||||
{
|
||||
int menubar_id = request.menubar_id();
|
||||
auto it = m_menubars.find(menubar_id);
|
||||
if (it == m_menubars.end()) {
|
||||
post_error("Bad menubar ID");
|
||||
return;
|
||||
}
|
||||
auto& menubar = *(*it).value;
|
||||
m_app_menubar = menubar.make_weak_ptr();
|
||||
WSWindowManager::the().notify_client_changed_app_menubar(*this);
|
||||
WSAPI_ServerMessage response;
|
||||
response.type = WSAPI_ServerMessage::Type::DidSetApplicationMenubar;
|
||||
response.menu.menubar_id = menubar_id;
|
||||
post_message(response);
|
||||
}
|
||||
|
||||
void WSClientConnection::handle_request(WSAPIAddMenuToMenubarRequest& request)
|
||||
{
|
||||
int menubar_id = request.menubar_id();
|
||||
int menu_id = request.menu_id();
|
||||
auto it = m_menubars.find(menubar_id);
|
||||
auto jt = m_menus.find(menu_id);
|
||||
if (it == m_menubars.end()) {
|
||||
post_error("Bad menubar ID");
|
||||
return;
|
||||
}
|
||||
if (jt == m_menus.end()) {
|
||||
post_error("Bad menu ID");
|
||||
return;
|
||||
}
|
||||
auto& menubar = *(*it).value;
|
||||
auto& menu = *(*jt).value;
|
||||
menubar.add_menu(&menu);
|
||||
WSAPI_ServerMessage response;
|
||||
response.type = WSAPI_ServerMessage::Type::DidAddMenuToMenubar;
|
||||
response.menu.menubar_id = menubar_id;
|
||||
response.menu.menu_id = menu_id;
|
||||
post_message(response);
|
||||
}
|
||||
|
||||
void WSClientConnection::handle_request(WSAPIAddMenuItemRequest& request)
|
||||
{
|
||||
int menu_id = request.menu_id();
|
||||
unsigned identifier = request.identifier();
|
||||
auto it = m_menus.find(menu_id);
|
||||
if (it == m_menus.end()) {
|
||||
post_error("Bad menu ID");
|
||||
return;
|
||||
}
|
||||
auto& menu = *(*it).value;
|
||||
menu.add_item(make<WSMenuItem>(identifier, request.text(), request.shortcut_text()));
|
||||
WSAPI_ServerMessage response;
|
||||
response.type = WSAPI_ServerMessage::Type::DidAddMenuItem;
|
||||
response.menu.menu_id = menu_id;
|
||||
response.menu.identifier = identifier;
|
||||
post_message(response);
|
||||
}
|
||||
|
||||
void WSClientConnection::handle_request(WSAPIAddMenuSeparatorRequest& request)
|
||||
{
|
||||
int menu_id = request.menu_id();
|
||||
auto it = m_menus.find(menu_id);
|
||||
if (it == m_menus.end()) {
|
||||
post_error("Bad menu ID");
|
||||
return;
|
||||
}
|
||||
auto& menu = *(*it).value;
|
||||
menu.add_item(make<WSMenuItem>(WSMenuItem::Separator));
|
||||
WSAPI_ServerMessage response;
|
||||
response.type = WSAPI_ServerMessage::Type::DidAddMenuSeparator;
|
||||
response.menu.menu_id = menu_id;
|
||||
post_message(response);
|
||||
}
|
||||
|
||||
void WSClientConnection::handle_request(WSAPISetWindowOpacityRequest& request)
|
||||
{
|
||||
int window_id = request.window_id();
|
||||
auto it = m_windows.find(window_id);
|
||||
if (it == m_windows.end()) {
|
||||
post_error("Bad window ID");
|
||||
return;
|
||||
}
|
||||
auto& window = *(*it).value;
|
||||
window.set_opacity(request.opacity());
|
||||
}
|
||||
|
||||
void WSClientConnection::handle_request(WSAPISetWindowTitleRequest& request)
|
||||
{
|
||||
int window_id = request.window_id();
|
||||
auto it = m_windows.find(window_id);
|
||||
if (it == m_windows.end()) {
|
||||
post_error("Bad window ID");
|
||||
return;
|
||||
}
|
||||
auto& window = *(*it).value;
|
||||
window.set_title(request.title());
|
||||
}
|
||||
|
||||
void WSClientConnection::handle_request(WSAPIGetWindowTitleRequest& request)
|
||||
{
|
||||
int window_id = request.window_id();
|
||||
auto it = m_windows.find(window_id);
|
||||
if (it == m_windows.end()) {
|
||||
post_error("Bad window ID");
|
||||
return;
|
||||
}
|
||||
auto& window = *(*it).value;
|
||||
WSAPI_ServerMessage response;
|
||||
response.type = WSAPI_ServerMessage::Type::DidGetWindowTitle;
|
||||
response.window_id = window.window_id();
|
||||
ASSERT(window.title().length() < (ssize_t)sizeof(response.text));
|
||||
strcpy(response.text, window.title().characters());
|
||||
response.text_length = window.title().length();
|
||||
post_message(response);
|
||||
}
|
||||
|
||||
void WSClientConnection::handle_request(WSAPISetWindowRectRequest& request)
|
||||
{
|
||||
int window_id = request.window_id();
|
||||
auto it = m_windows.find(window_id);
|
||||
if (it == m_windows.end()) {
|
||||
post_error("Bad window ID");
|
||||
return;
|
||||
}
|
||||
auto& window = *(*it).value;
|
||||
window.set_rect(request.rect());
|
||||
}
|
||||
|
||||
void WSClientConnection::handle_request(WSAPIGetWindowRectRequest& request)
|
||||
{
|
||||
int window_id = request.window_id();
|
||||
auto it = m_windows.find(window_id);
|
||||
if (it == m_windows.end()) {
|
||||
post_error("Bad window ID");
|
||||
return;
|
||||
}
|
||||
auto& window = *(*it).value;
|
||||
WSAPI_ServerMessage response;
|
||||
response.type = WSAPI_ServerMessage::Type::DidGetWindowRect;
|
||||
response.window_id = window.window_id();
|
||||
response.window.rect = window.rect();
|
||||
post_message(response);
|
||||
}
|
||||
|
||||
void WSClientConnection::handle_request(WSAPISetClipboardContentsRequest& request)
|
||||
{
|
||||
auto shared_buffer = SharedBuffer::create_from_shared_buffer_id(request.shared_buffer_id());
|
||||
if (!shared_buffer) {
|
||||
post_error("Bad shared buffer ID");
|
||||
return;
|
||||
}
|
||||
WSClipboard::the().set_data(*shared_buffer, request.size());
|
||||
WSAPI_ServerMessage response;
|
||||
response.type = WSAPI_ServerMessage::Type::DidSetClipboardContents;
|
||||
response.clipboard.shared_buffer_id = shared_buffer->shared_buffer_id();
|
||||
post_message(response);
|
||||
}
|
||||
|
||||
void WSClientConnection::handle_request(WSAPIGetClipboardContentsRequest&)
|
||||
{
|
||||
WSAPI_ServerMessage response;
|
||||
response.type = WSAPI_ServerMessage::Type::DidGetClipboardContents;
|
||||
response.clipboard.shared_buffer_id = -1;
|
||||
response.clipboard.contents_size = 0;
|
||||
if (WSClipboard::the().size()) {
|
||||
// FIXME: Optimize case where an app is copy/pasting within itself.
|
||||
// We can just reuse the SharedBuffer then, since it will have the same peer PID.
|
||||
// It would be even nicer if a SharedBuffer could have an arbitrary number of clients..
|
||||
RetainPtr<SharedBuffer> shared_buffer = SharedBuffer::create(m_pid, WSClipboard::the().size());
|
||||
ASSERT(shared_buffer);
|
||||
memcpy(shared_buffer->data(), WSClipboard::the().data(), WSClipboard::the().size());
|
||||
shared_buffer->seal();
|
||||
response.clipboard.shared_buffer_id = shared_buffer->shared_buffer_id();
|
||||
response.clipboard.contents_size = WSClipboard::the().size();
|
||||
|
||||
// FIXME: This is a workaround for the fact that SharedBuffers will go away if neither side is retaining them.
|
||||
// After we respond to GetClipboardContents, we have to wait for the client to retain the buffer on his side.
|
||||
m_last_sent_clipboard_content = move(shared_buffer);
|
||||
}
|
||||
post_message(response);
|
||||
}
|
||||
|
||||
void WSClientConnection::handle_request(WSAPICreateWindowRequest& request)
|
||||
{
|
||||
int window_id = m_next_window_id++;
|
||||
auto window = make<WSWindow>(*this, window_id, request.is_modal());
|
||||
window->set_has_alpha_channel(request.has_alpha_channel());
|
||||
window->set_resizable(request.is_resizable());
|
||||
window->set_title(request.title());
|
||||
window->set_rect(request.rect());
|
||||
window->set_opacity(request.opacity());
|
||||
window->set_size_increment(request.size_increment());
|
||||
window->set_base_size(request.base_size());
|
||||
window->invalidate();
|
||||
m_windows.set(window_id, move(window));
|
||||
WSAPI_ServerMessage response;
|
||||
response.type = WSAPI_ServerMessage::Type::DidCreateWindow;
|
||||
response.window_id = window_id;
|
||||
post_message(response);
|
||||
}
|
||||
|
||||
void WSClientConnection::handle_request(WSAPIDestroyWindowRequest& request)
|
||||
{
|
||||
int window_id = request.window_id();
|
||||
auto it = m_windows.find(window_id);
|
||||
if (it == m_windows.end()) {
|
||||
post_error("Bad window ID");
|
||||
return;
|
||||
}
|
||||
auto& window = *(*it).value;
|
||||
WSWindowManager::the().invalidate(window);
|
||||
m_windows.remove(it);
|
||||
}
|
||||
|
||||
void WSClientConnection::handle_request(WSAPIInvalidateRectRequest& request)
|
||||
{
|
||||
int window_id = request.window_id();
|
||||
auto it = m_windows.find(window_id);
|
||||
if (it == m_windows.end()) {
|
||||
post_error("Bad window ID");
|
||||
return;
|
||||
}
|
||||
auto& window = *(*it).value;
|
||||
WSAPI_ServerMessage response;
|
||||
response.type = WSAPI_ServerMessage::Type::Paint;
|
||||
response.window_id = window_id;
|
||||
response.paint.rect = request.rect();
|
||||
response.paint.window_size = window.size();
|
||||
post_message(response);
|
||||
}
|
||||
|
||||
void WSClientConnection::handle_request(WSAPIDidFinishPaintingNotification& request)
|
||||
{
|
||||
int window_id = request.window_id();
|
||||
auto it = m_windows.find(window_id);
|
||||
if (it == m_windows.end()) {
|
||||
post_error("Bad window ID");
|
||||
return;
|
||||
}
|
||||
auto& window = *(*it).value;
|
||||
|
||||
if (!window.has_painted_since_last_resize()) {
|
||||
if (window.last_lazy_resize_rect().size() == request.rect().size()) {
|
||||
window.set_has_painted_since_last_resize(true);
|
||||
WSMessageLoop::the().post_message(window, make<WSResizeEvent>(window.last_lazy_resize_rect(), window.rect()));
|
||||
}
|
||||
}
|
||||
WSWindowManager::the().invalidate(window, request.rect());
|
||||
}
|
||||
|
||||
void WSClientConnection::handle_request(WSAPIGetWindowBackingStoreRequest& request)
|
||||
{
|
||||
int window_id = request.window_id();
|
||||
auto it = m_windows.find(window_id);
|
||||
if (it == m_windows.end()) {
|
||||
post_error("Bad window ID");
|
||||
return;
|
||||
}
|
||||
auto& window = *(*it).value;
|
||||
auto* backing_store = window.backing_store();
|
||||
|
||||
WSAPI_ServerMessage response;
|
||||
response.type = WSAPI_ServerMessage::Type::DidGetWindowBackingStore;
|
||||
response.window_id = window_id;
|
||||
response.backing.bpp = sizeof(RGBA32);
|
||||
response.backing.pitch = backing_store->pitch();
|
||||
response.backing.size = backing_store->size();
|
||||
response.backing.has_alpha_channel = backing_store->has_alpha_channel();
|
||||
response.backing.shared_buffer_id = backing_store->shared_buffer_id();
|
||||
post_message(response);
|
||||
}
|
||||
|
||||
void WSClientConnection::handle_request(WSAPISetWindowBackingStoreRequest& request)
|
||||
{
|
||||
int window_id = request.window_id();
|
||||
auto it = m_windows.find(window_id);
|
||||
if (it == m_windows.end()) {
|
||||
post_error("Bad window ID");
|
||||
return;
|
||||
}
|
||||
auto& window = *(*it).value;
|
||||
if (window.last_backing_store() && window.last_backing_store()->shared_buffer_id() == request.shared_buffer_id()) {
|
||||
window.swap_backing_stores();
|
||||
} else {
|
||||
auto shared_buffer = SharedBuffer::create_from_shared_buffer_id(request.shared_buffer_id());
|
||||
if (!shared_buffer)
|
||||
return;
|
||||
auto backing_store = GraphicsBitmap::create_with_shared_buffer(
|
||||
request.has_alpha_channel() ? GraphicsBitmap::Format::RGBA32 : GraphicsBitmap::Format::RGB32,
|
||||
*shared_buffer,
|
||||
request.size());
|
||||
window.set_backing_store(move(backing_store));
|
||||
}
|
||||
|
||||
if (request.flush_immediately())
|
||||
window.invalidate();
|
||||
|
||||
WSAPI_ServerMessage response;
|
||||
response.type = WSAPI_ServerMessage::Type::DidSetWindowBackingStore;
|
||||
response.window_id = window_id;
|
||||
response.backing.shared_buffer_id = request.shared_buffer_id();
|
||||
post_message(response);
|
||||
}
|
||||
|
||||
void WSClientConnection::handle_request(WSAPISetGlobalCursorTrackingRequest& request)
|
||||
{
|
||||
int window_id = request.window_id();
|
||||
auto it = m_windows.find(window_id);
|
||||
if (it == m_windows.end()) {
|
||||
post_error("Bad window ID");
|
||||
return;
|
||||
}
|
||||
auto& window = *(*it).value;
|
||||
window.set_global_cursor_tracking_enabled(request.value());
|
||||
}
|
||||
|
||||
void WSClientConnection::on_request(WSAPIClientRequest& request)
|
||||
{
|
||||
switch (request.type()) {
|
||||
case WSMessage::APICreateMenubarRequest:
|
||||
return handle_request(static_cast<WSAPICreateMenubarRequest&>(request));
|
||||
case WSMessage::APIDestroyMenubarRequest:
|
||||
return handle_request(static_cast<WSAPIDestroyMenubarRequest&>(request));
|
||||
case WSMessage::APICreateMenuRequest:
|
||||
return handle_request(static_cast<WSAPICreateMenuRequest&>(request));
|
||||
case WSMessage::APIDestroyMenuRequest:
|
||||
return handle_request(static_cast<WSAPIDestroyMenuRequest&>(request));
|
||||
case WSMessage::APISetApplicationMenubarRequest:
|
||||
return handle_request(static_cast<WSAPISetApplicationMenubarRequest&>(request));
|
||||
case WSMessage::APIAddMenuToMenubarRequest:
|
||||
return handle_request(static_cast<WSAPIAddMenuToMenubarRequest&>(request));
|
||||
case WSMessage::APIAddMenuItemRequest:
|
||||
return handle_request(static_cast<WSAPIAddMenuItemRequest&>(request));
|
||||
case WSMessage::APIAddMenuSeparatorRequest:
|
||||
return handle_request(static_cast<WSAPIAddMenuSeparatorRequest&>(request));
|
||||
case WSMessage::APISetWindowTitleRequest:
|
||||
return handle_request(static_cast<WSAPISetWindowTitleRequest&>(request));
|
||||
case WSMessage::APIGetWindowTitleRequest:
|
||||
return handle_request(static_cast<WSAPIGetWindowTitleRequest&>(request));
|
||||
case WSMessage::APISetWindowRectRequest:
|
||||
return handle_request(static_cast<WSAPISetWindowRectRequest&>(request));
|
||||
case WSMessage::APIGetWindowRectRequest:
|
||||
return handle_request(static_cast<WSAPIGetWindowRectRequest&>(request));
|
||||
case WSMessage::APISetClipboardContentsRequest:
|
||||
return handle_request(static_cast<WSAPISetClipboardContentsRequest&>(request));
|
||||
case WSMessage::APIGetClipboardContentsRequest:
|
||||
return handle_request(static_cast<WSAPIGetClipboardContentsRequest&>(request));
|
||||
case WSMessage::APICreateWindowRequest:
|
||||
return handle_request(static_cast<WSAPICreateWindowRequest&>(request));
|
||||
case WSMessage::APIDestroyWindowRequest:
|
||||
return handle_request(static_cast<WSAPIDestroyWindowRequest&>(request));
|
||||
case WSMessage::APIInvalidateRectRequest:
|
||||
return handle_request(static_cast<WSAPIInvalidateRectRequest&>(request));
|
||||
case WSMessage::APIDidFinishPaintingNotification:
|
||||
return handle_request(static_cast<WSAPIDidFinishPaintingNotification&>(request));
|
||||
case WSMessage::APIGetWindowBackingStoreRequest:
|
||||
return handle_request(static_cast<WSAPIGetWindowBackingStoreRequest&>(request));
|
||||
case WSMessage::APISetGlobalCursorTrackingRequest:
|
||||
return handle_request(static_cast<WSAPISetGlobalCursorTrackingRequest&>(request));
|
||||
case WSMessage::APISetWindowOpacityRequest:
|
||||
return handle_request(static_cast<WSAPISetWindowOpacityRequest&>(request));
|
||||
case WSMessage::APISetWindowBackingStoreRequest:
|
||||
return handle_request(static_cast<WSAPISetWindowBackingStoreRequest&>(request));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool WSClientConnection::is_showing_modal_window() const
|
||||
{
|
||||
for (auto& it : m_windows) {
|
||||
auto& window = *it.value;
|
||||
if (window.is_visible() && window.is_modal())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
101
Servers/WindowServer/WSClientConnection.h
Normal file
101
Servers/WindowServer/WSClientConnection.h
Normal file
|
@ -0,0 +1,101 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <AK/WeakPtr.h>
|
||||
#include <AK/Function.h>
|
||||
#include <SharedGraphics/GraphicsBitmap.h>
|
||||
#include <WindowServer/WSMessageReceiver.h>
|
||||
#include <WindowServer/WSMessage.h>
|
||||
|
||||
class WSWindow;
|
||||
class WSMenu;
|
||||
class WSMenuBar;
|
||||
struct WSAPI_ServerMessage;
|
||||
|
||||
class WSClientConnection final : public WSMessageReceiver {
|
||||
public:
|
||||
explicit WSClientConnection(int fd);
|
||||
virtual ~WSClientConnection() override;
|
||||
|
||||
static WSClientConnection* from_client_id(int client_id);
|
||||
static void for_each_client(Function<void(WSClientConnection&)>);
|
||||
|
||||
void post_message(const WSAPI_ServerMessage&);
|
||||
RetainPtr<GraphicsBitmap> create_shared_bitmap(GraphicsBitmap::Format, const Size&);
|
||||
|
||||
int client_id() const { return m_client_id; }
|
||||
WSMenuBar* app_menubar() { return m_app_menubar.ptr(); }
|
||||
|
||||
int fd() const { return m_fd; }
|
||||
pid_t pid() const { return m_pid; }
|
||||
|
||||
bool is_showing_modal_window() const;
|
||||
|
||||
template<typename Matching, typename Callback> void for_each_window_matching(Matching, Callback);
|
||||
template<typename Callback> void for_each_window(Callback);
|
||||
|
||||
private:
|
||||
virtual void on_message(WSMessage&) override;
|
||||
|
||||
void on_request(WSAPIClientRequest&);
|
||||
void handle_request(WSAPICreateMenubarRequest&);
|
||||
void handle_request(WSAPIDestroyMenubarRequest&);
|
||||
void handle_request(WSAPICreateMenuRequest&);
|
||||
void handle_request(WSAPIDestroyMenuRequest&);
|
||||
void handle_request(WSAPISetApplicationMenubarRequest&);
|
||||
void handle_request(WSAPIAddMenuToMenubarRequest&);
|
||||
void handle_request(WSAPIAddMenuItemRequest&);
|
||||
void handle_request(WSAPIAddMenuSeparatorRequest&);
|
||||
void handle_request(WSAPISetWindowTitleRequest&);
|
||||
void handle_request(WSAPIGetWindowTitleRequest&);
|
||||
void handle_request(WSAPISetWindowRectRequest&);
|
||||
void handle_request(WSAPIGetWindowRectRequest&);
|
||||
void handle_request(WSAPISetClipboardContentsRequest&);
|
||||
void handle_request(WSAPIGetClipboardContentsRequest&);
|
||||
void handle_request(WSAPICreateWindowRequest&);
|
||||
void handle_request(WSAPIDestroyWindowRequest&);
|
||||
void handle_request(WSAPIInvalidateRectRequest&);
|
||||
void handle_request(WSAPIDidFinishPaintingNotification&);
|
||||
void handle_request(WSAPIGetWindowBackingStoreRequest&);
|
||||
void handle_request(WSAPISetWindowBackingStoreRequest&);
|
||||
void handle_request(WSAPISetGlobalCursorTrackingRequest&);
|
||||
void handle_request(WSAPISetWindowOpacityRequest&);
|
||||
|
||||
void post_error(const String&);
|
||||
|
||||
int m_client_id { 0 };
|
||||
int m_fd { -1 };
|
||||
pid_t m_pid { 0 };
|
||||
|
||||
HashMap<int, OwnPtr<WSWindow>> m_windows;
|
||||
HashMap<int, OwnPtr<WSMenuBar>> m_menubars;
|
||||
HashMap<int, OwnPtr<WSMenu>> m_menus;
|
||||
WeakPtr<WSMenuBar> m_app_menubar;
|
||||
|
||||
int m_next_menubar_id { 10000 };
|
||||
int m_next_menu_id { 20000 };
|
||||
int m_next_window_id { 1982 };
|
||||
|
||||
RetainPtr<SharedBuffer> m_last_sent_clipboard_content;
|
||||
};
|
||||
|
||||
template<typename Matching, typename Callback>
|
||||
void WSClientConnection::for_each_window_matching(Matching matching, Callback callback)
|
||||
{
|
||||
for (auto& it : m_windows) {
|
||||
if (matching(*it.value)) {
|
||||
if (callback(*it.value) == IterationDecision::Abort)
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Callback>
|
||||
void WSClientConnection::for_each_window(Callback callback)
|
||||
{
|
||||
for (auto& it : m_windows) {
|
||||
if (callback(*it.value) == IterationDecision::Abort)
|
||||
return;
|
||||
}
|
||||
}
|
48
Servers/WindowServer/WSClipboard.cpp
Normal file
48
Servers/WindowServer/WSClipboard.cpp
Normal file
|
@ -0,0 +1,48 @@
|
|||
#include <WindowServer/WSClipboard.h>
|
||||
|
||||
WSClipboard& WSClipboard::the()
|
||||
{
|
||||
static WSClipboard* s_the;
|
||||
if (!s_the)
|
||||
s_the = new WSClipboard;
|
||||
return *s_the;
|
||||
}
|
||||
|
||||
WSClipboard::WSClipboard()
|
||||
{
|
||||
}
|
||||
|
||||
WSClipboard::~WSClipboard()
|
||||
{
|
||||
}
|
||||
|
||||
void WSClipboard::on_message(WSMessage&)
|
||||
{
|
||||
}
|
||||
|
||||
const byte* WSClipboard::data() const
|
||||
{
|
||||
if (!m_shared_buffer)
|
||||
return nullptr;
|
||||
return (const byte*)m_shared_buffer->data();
|
||||
}
|
||||
|
||||
int WSClipboard::size() const
|
||||
{
|
||||
if (!m_shared_buffer)
|
||||
return 0;
|
||||
return m_contents_size;
|
||||
}
|
||||
|
||||
void WSClipboard::clear()
|
||||
{
|
||||
m_shared_buffer = nullptr;
|
||||
m_contents_size = 0;
|
||||
}
|
||||
|
||||
void WSClipboard::set_data(Retained<SharedBuffer>&& data, int contents_size)
|
||||
{
|
||||
dbgprintf("WSClipboard::set_data <- %p (%u bytes)\n", data->data(), contents_size);
|
||||
m_shared_buffer = move(data);
|
||||
m_contents_size = contents_size;
|
||||
}
|
29
Servers/WindowServer/WSClipboard.h
Normal file
29
Servers/WindowServer/WSClipboard.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/AKString.h>
|
||||
#include <WindowServer/WSMessageReceiver.h>
|
||||
#include <SharedBuffer.h>
|
||||
|
||||
class WSClipboard final : public WSMessageReceiver {
|
||||
public:
|
||||
static WSClipboard& the();
|
||||
virtual ~WSClipboard() override;
|
||||
|
||||
bool has_data() const
|
||||
{
|
||||
return m_shared_buffer;
|
||||
}
|
||||
|
||||
const byte* data() const;
|
||||
int size() const;
|
||||
|
||||
void clear();
|
||||
void set_data(Retained<SharedBuffer>&&, int contents_size);
|
||||
|
||||
private:
|
||||
WSClipboard();
|
||||
virtual void on_message(WSMessage&) override;
|
||||
|
||||
RetainPtr<SharedBuffer> m_shared_buffer;
|
||||
int m_contents_size { 0 };
|
||||
};
|
168
Servers/WindowServer/WSMenu.cpp
Normal file
168
Servers/WindowServer/WSMenu.cpp
Normal file
|
@ -0,0 +1,168 @@
|
|||
#include "WSMenu.h"
|
||||
#include "WSMenuItem.h"
|
||||
#include "WSWindow.h"
|
||||
#include "WSMessage.h"
|
||||
#include "WSMessageLoop.h"
|
||||
#include "WSWindowManager.h"
|
||||
#include <WindowServer/WSAPITypes.h>
|
||||
#include <WindowServer/WSClientConnection.h>
|
||||
#include <SharedGraphics/Painter.h>
|
||||
#include <SharedGraphics/Font.h>
|
||||
|
||||
WSMenu::WSMenu(WSClientConnection* client, int menu_id, String&& name)
|
||||
: m_client(client)
|
||||
, m_menu_id(menu_id)
|
||||
, m_name(move(name))
|
||||
{
|
||||
}
|
||||
|
||||
WSMenu::~WSMenu()
|
||||
{
|
||||
}
|
||||
|
||||
const Font& WSMenu::font() const
|
||||
{
|
||||
return Font::default_font();
|
||||
}
|
||||
|
||||
int WSMenu::width() const
|
||||
{
|
||||
int longest = 0;
|
||||
for (auto& item : m_items) {
|
||||
if (item->type() == WSMenuItem::Text) {
|
||||
int item_width = font().width(item->text());
|
||||
if (!item->shortcut_text().is_empty())
|
||||
item_width += padding_between_text_and_shortcut() + font().width(item->shortcut_text());
|
||||
|
||||
longest = max(longest, item_width);
|
||||
}
|
||||
}
|
||||
|
||||
return max(longest, rect_in_menubar().width()) + horizontal_padding();
|
||||
}
|
||||
|
||||
int WSMenu::height() const
|
||||
{
|
||||
if (m_items.is_empty())
|
||||
return 0;
|
||||
return (m_items.last()->rect().bottom() - 1) + vertical_padding();
|
||||
}
|
||||
|
||||
void WSMenu::redraw()
|
||||
{
|
||||
ASSERT(menu_window());
|
||||
draw();
|
||||
menu_window()->invalidate();
|
||||
}
|
||||
|
||||
WSWindow& WSMenu::ensure_menu_window()
|
||||
{
|
||||
if (!m_menu_window) {
|
||||
Point next_item_location(1, vertical_padding() / 2);
|
||||
for (auto& item : m_items) {
|
||||
int height = 0;
|
||||
if (item->type() == WSMenuItem::Text)
|
||||
height = item_height();
|
||||
else if (item->type() == WSMenuItem::Separator)
|
||||
height = 7;
|
||||
item->set_rect({ next_item_location, { width() - 2, height } });
|
||||
next_item_location.move_by(0, height);
|
||||
}
|
||||
|
||||
auto window = make<WSWindow>(*this, WSWindowType::Menu);
|
||||
window->set_opacity(0.95f);
|
||||
window->set_rect(0, 0, width(), height());
|
||||
m_menu_window = move(window);
|
||||
draw();
|
||||
}
|
||||
return *m_menu_window;
|
||||
}
|
||||
|
||||
void WSMenu::draw()
|
||||
{
|
||||
ASSERT(menu_window());
|
||||
ASSERT(menu_window()->backing_store());
|
||||
Painter painter(*menu_window()->backing_store());
|
||||
|
||||
Rect rect { { }, menu_window()->size() };
|
||||
painter.draw_rect(rect, Color::White);
|
||||
painter.fill_rect(rect.shrunken(2, 2), Color::LightGray);
|
||||
|
||||
for (auto& item : m_items) {
|
||||
if (item->type() == WSMenuItem::Text) {
|
||||
Color text_color = Color::Black;
|
||||
if (item.ptr() == m_hovered_item) {
|
||||
painter.fill_rect(item->rect(), WSWindowManager::the().menu_selection_color());
|
||||
text_color = Color::White;
|
||||
}
|
||||
painter.draw_text(item->rect().translated(left_padding(), 0), item->text(), TextAlignment::CenterLeft, text_color);
|
||||
if (!item->shortcut_text().is_empty()) {
|
||||
painter.draw_text(item->rect().translated(-right_padding(), 0), item->shortcut_text(), TextAlignment::CenterRight, text_color);
|
||||
}
|
||||
} else if (item->type() == WSMenuItem::Separator) {
|
||||
Point p1(1, item->rect().center().y());
|
||||
Point p2(width() - 2, item->rect().center().y());
|
||||
painter.draw_line(p1, p2, Color::MidGray);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WSMenu::on_message(WSMessage& message)
|
||||
{
|
||||
ASSERT(menu_window());
|
||||
if (message.type() == WSMessage::MouseMove) {
|
||||
auto* item = item_at(static_cast<WSMouseEvent&>(message).position());
|
||||
if (!item || m_hovered_item == item)
|
||||
return;
|
||||
m_hovered_item = item;
|
||||
redraw();
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.type() == WSMessage::MouseUp) {
|
||||
if (!m_hovered_item)
|
||||
return;
|
||||
did_activate(*m_hovered_item);
|
||||
clear_hovered_item();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void WSMenu::clear_hovered_item()
|
||||
{
|
||||
if (!m_hovered_item)
|
||||
return;
|
||||
m_hovered_item = nullptr;
|
||||
redraw();
|
||||
}
|
||||
|
||||
void WSMenu::did_activate(WSMenuItem& item)
|
||||
{
|
||||
if (on_item_activation)
|
||||
on_item_activation(item);
|
||||
|
||||
close();
|
||||
|
||||
WSAPI_ServerMessage message;
|
||||
message.type = WSAPI_ServerMessage::Type::MenuItemActivated;
|
||||
message.menu.menu_id = m_menu_id;
|
||||
message.menu.identifier = item.identifier();
|
||||
|
||||
if (m_client)
|
||||
m_client->post_message(message);
|
||||
}
|
||||
|
||||
WSMenuItem* WSMenu::item_at(const Point& position)
|
||||
{
|
||||
for (auto& item : m_items) {
|
||||
if (!item->rect().contains(position))
|
||||
continue;
|
||||
return item.ptr();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void WSMenu::close()
|
||||
{
|
||||
WSWindowManager::the().close_menu(*this);
|
||||
};
|
90
Servers/WindowServer/WSMenu.h
Normal file
90
Servers/WindowServer/WSMenu.h
Normal file
|
@ -0,0 +1,90 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/AKString.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <AK/WeakPtr.h>
|
||||
#include <SharedGraphics/Rect.h>
|
||||
#include <WindowServer/WSMenuItem.h>
|
||||
#include <WindowServer/WSMessageReceiver.h>
|
||||
|
||||
class WSClientConnection;
|
||||
class WSMenuBar;
|
||||
class WSMessage;
|
||||
class WSWindow;
|
||||
class Font;
|
||||
|
||||
class WSMenu final : public WSMessageReceiver {
|
||||
public:
|
||||
WSMenu(WSClientConnection*, int menu_id, String&& name);
|
||||
virtual ~WSMenu() override;
|
||||
|
||||
WSClientConnection* client() { return m_client; }
|
||||
const WSClientConnection* client() const { return m_client; }
|
||||
int menu_id() const { return m_menu_id; }
|
||||
|
||||
WSMenuBar* menu_bar() { return m_menubar; }
|
||||
const WSMenuBar* menu_bar() const { return m_menubar; }
|
||||
|
||||
bool is_empty() const { return m_items.is_empty(); }
|
||||
int item_count() const { return m_items.size(); }
|
||||
WSMenuItem* item(int i) { return m_items[i].ptr(); }
|
||||
const WSMenuItem* item(int i) const { return m_items[i].ptr(); }
|
||||
|
||||
void add_item(OwnPtr<WSMenuItem>&& item) { m_items.append(move(item)); }
|
||||
|
||||
String name() const { return m_name; }
|
||||
|
||||
template<typename Callback>
|
||||
void for_each_item(Callback callback) const
|
||||
{
|
||||
for (auto& item : m_items)
|
||||
callback(*item);
|
||||
}
|
||||
|
||||
Rect text_rect_in_menubar() const { return m_text_rect_in_menubar; }
|
||||
void set_text_rect_in_menubar(const Rect& rect) { m_text_rect_in_menubar = rect; }
|
||||
|
||||
Rect rect_in_menubar() const { return m_rect_in_menubar; }
|
||||
void set_rect_in_menubar(const Rect& rect) { m_rect_in_menubar = rect; }
|
||||
|
||||
WSWindow* menu_window() { return m_menu_window.ptr(); }
|
||||
WSWindow& ensure_menu_window();
|
||||
|
||||
int width() const;
|
||||
int height() const;
|
||||
|
||||
int item_height() const { return 18; }
|
||||
int vertical_padding() const { return 4; }
|
||||
int horizontal_padding() const { return left_padding() + right_padding(); }
|
||||
int left_padding() const { return 14; }
|
||||
int right_padding() const { return 14; }
|
||||
|
||||
void draw();
|
||||
const Font& font() const;
|
||||
|
||||
WSMenuItem* item_at(const Point&);
|
||||
void redraw();
|
||||
|
||||
const WSMenuItem* hovered_item() const { return m_hovered_item; }
|
||||
void clear_hovered_item();
|
||||
|
||||
Function<void(WSMenuItem&)> on_item_activation;
|
||||
|
||||
void close();
|
||||
|
||||
private:
|
||||
virtual void on_message(WSMessage&) override;
|
||||
|
||||
int padding_between_text_and_shortcut() const { return 50; }
|
||||
void did_activate(WSMenuItem&);
|
||||
WSClientConnection* m_client { nullptr };
|
||||
int m_menu_id { 0 };
|
||||
String m_name;
|
||||
Rect m_rect_in_menubar;
|
||||
Rect m_text_rect_in_menubar;
|
||||
WSMenuBar* m_menubar { nullptr };
|
||||
WSMenuItem* m_hovered_item { nullptr };
|
||||
Vector<OwnPtr<WSMenuItem>> m_items;
|
||||
OwnPtr<WSWindow> m_menu_window;
|
||||
};
|
||||
|
14
Servers/WindowServer/WSMenuBar.cpp
Normal file
14
Servers/WindowServer/WSMenuBar.cpp
Normal file
|
@ -0,0 +1,14 @@
|
|||
#include "WSMenuBar.h"
|
||||
#include "WSMenu.h"
|
||||
#include "WSMenuItem.h"
|
||||
|
||||
WSMenuBar::WSMenuBar(WSClientConnection& client, int menubar_id)
|
||||
: m_client(client)
|
||||
, m_menubar_id(menubar_id)
|
||||
{
|
||||
}
|
||||
|
||||
WSMenuBar::~WSMenuBar()
|
||||
{
|
||||
}
|
||||
|
31
Servers/WindowServer/WSMenuBar.h
Normal file
31
Servers/WindowServer/WSMenuBar.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
#pragma once
|
||||
|
||||
#include "WSMenu.h"
|
||||
#include <AK/Vector.h>
|
||||
#include <AK/Weakable.h>
|
||||
#include <AK/WeakPtr.h>
|
||||
|
||||
class WSMenuBar : public Weakable<WSMenuBar> {
|
||||
public:
|
||||
WSMenuBar(WSClientConnection& client, int menubar_id);
|
||||
~WSMenuBar();
|
||||
|
||||
WSClientConnection& client() { return m_client; }
|
||||
const WSClientConnection& client() const { return m_client; }
|
||||
int menubar_id() const { return m_menubar_id; }
|
||||
void add_menu(WSMenu* menu) { m_menus.append(menu); }
|
||||
|
||||
template<typename Callback>
|
||||
void for_each_menu(Callback callback)
|
||||
{
|
||||
for (auto& menu : m_menus) {
|
||||
if (!callback(*menu))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
WSClientConnection& m_client;
|
||||
int m_menubar_id { 0 };
|
||||
Vector<WSMenu*> m_menus;
|
||||
};
|
18
Servers/WindowServer/WSMenuItem.cpp
Normal file
18
Servers/WindowServer/WSMenuItem.cpp
Normal file
|
@ -0,0 +1,18 @@
|
|||
#include "WSMenuItem.h"
|
||||
|
||||
WSMenuItem::WSMenuItem(unsigned identifier, const String& text, const String& shortcut_text)
|
||||
: m_type(Text)
|
||||
, m_identifier(identifier)
|
||||
, m_text(text)
|
||||
, m_shortcut_text(shortcut_text)
|
||||
{
|
||||
}
|
||||
|
||||
WSMenuItem::WSMenuItem(Type type)
|
||||
: m_type(type)
|
||||
{
|
||||
}
|
||||
|
||||
WSMenuItem::~WSMenuItem()
|
||||
{
|
||||
}
|
38
Servers/WindowServer/WSMenuItem.h
Normal file
38
Servers/WindowServer/WSMenuItem.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/AKString.h>
|
||||
#include <AK/Function.h>
|
||||
#include <SharedGraphics/Rect.h>
|
||||
|
||||
class WSMenuItem {
|
||||
public:
|
||||
enum Type {
|
||||
None,
|
||||
Text,
|
||||
Separator,
|
||||
};
|
||||
|
||||
explicit WSMenuItem(unsigned identifier, const String& text, const String& shortcut_text = { });
|
||||
explicit WSMenuItem(Type);
|
||||
~WSMenuItem();
|
||||
|
||||
Type type() const { return m_type; }
|
||||
bool enabled() const { return m_enabled; }
|
||||
|
||||
String text() const { return m_text; }
|
||||
String shortcut_text() const { return m_shortcut_text; }
|
||||
|
||||
void set_rect(const Rect& rect) { m_rect = rect; }
|
||||
Rect rect() const { return m_rect; }
|
||||
|
||||
unsigned identifier() const { return m_identifier; }
|
||||
|
||||
private:
|
||||
Type m_type { None };
|
||||
bool m_enabled { true };
|
||||
unsigned m_identifier { 0 };
|
||||
String m_text;
|
||||
String m_shortcut_text;
|
||||
Rect m_rect;
|
||||
};
|
||||
|
563
Servers/WindowServer/WSMessage.h
Normal file
563
Servers/WindowServer/WSMessage.h
Normal file
|
@ -0,0 +1,563 @@
|
|||
#pragma once
|
||||
|
||||
#include <SharedGraphics/Point.h>
|
||||
#include <SharedGraphics/Rect.h>
|
||||
#include <AK/AKString.h>
|
||||
#include <AK/Types.h>
|
||||
#include <Kernel/KeyCode.h>
|
||||
|
||||
class WSMessage {
|
||||
public:
|
||||
enum Type {
|
||||
Invalid = 0,
|
||||
WM_DeferredCompose,
|
||||
WM_ClientDisconnected,
|
||||
MouseMove,
|
||||
MouseDown,
|
||||
MouseUp,
|
||||
WindowEntered,
|
||||
WindowLeft,
|
||||
KeyDown,
|
||||
KeyUp,
|
||||
WindowActivated,
|
||||
WindowDeactivated,
|
||||
WindowCloseRequest,
|
||||
WindowResized,
|
||||
|
||||
__Begin_API_Client_Requests,
|
||||
APICreateMenubarRequest,
|
||||
APIDestroyMenubarRequest,
|
||||
APIAddMenuToMenubarRequest,
|
||||
APISetApplicationMenubarRequest,
|
||||
APICreateMenuRequest,
|
||||
APIDestroyMenuRequest,
|
||||
APIAddMenuItemRequest,
|
||||
APIAddMenuSeparatorRequest,
|
||||
APICreateWindowRequest,
|
||||
APIDestroyWindowRequest,
|
||||
APISetWindowTitleRequest,
|
||||
APIGetWindowTitleRequest,
|
||||
APISetWindowRectRequest,
|
||||
APIGetWindowRectRequest,
|
||||
APIInvalidateRectRequest,
|
||||
APIDidFinishPaintingNotification,
|
||||
APIGetWindowBackingStoreRequest,
|
||||
APISetGlobalCursorTrackingRequest,
|
||||
APISetWindowOpacityRequest,
|
||||
APISetWindowBackingStoreRequest,
|
||||
APISetClipboardContentsRequest,
|
||||
APIGetClipboardContentsRequest,
|
||||
__End_API_Client_Requests,
|
||||
};
|
||||
|
||||
WSMessage() { }
|
||||
explicit WSMessage(Type type) : m_type(type) { }
|
||||
virtual ~WSMessage() { }
|
||||
|
||||
Type type() const { return m_type; }
|
||||
|
||||
bool is_client_request() const { return m_type > __Begin_API_Client_Requests && m_type < __End_API_Client_Requests; }
|
||||
bool is_mouse_event() const { return m_type == MouseMove || m_type == MouseDown || m_type == MouseUp; }
|
||||
bool is_key_event() const { return m_type == KeyUp || m_type == KeyDown; }
|
||||
|
||||
private:
|
||||
Type m_type { Invalid };
|
||||
};
|
||||
|
||||
class WSClientDisconnectedNotification : public WSMessage {
|
||||
public:
|
||||
explicit WSClientDisconnectedNotification(int client_id)
|
||||
: WSMessage(WM_ClientDisconnected)
|
||||
, m_client_id(client_id)
|
||||
{
|
||||
}
|
||||
|
||||
int client_id() const { return m_client_id; }
|
||||
|
||||
private:
|
||||
int m_client_id { 0 };
|
||||
};
|
||||
|
||||
class WSAPIClientRequest : public WSMessage {
|
||||
public:
|
||||
WSAPIClientRequest(Type type, int client_id)
|
||||
: WSMessage(type)
|
||||
, m_client_id(client_id)
|
||||
{
|
||||
}
|
||||
|
||||
int client_id() const { return m_client_id; }
|
||||
|
||||
private:
|
||||
int m_client_id { 0 };
|
||||
};
|
||||
|
||||
class WSAPISetGlobalCursorTrackingRequest : public WSAPIClientRequest {
|
||||
public:
|
||||
WSAPISetGlobalCursorTrackingRequest(int client_id, int window_id, bool value)
|
||||
: WSAPIClientRequest(WSMessage::APISetGlobalCursorTrackingRequest, client_id)
|
||||
, m_window_id(window_id)
|
||||
, m_value(value)
|
||||
{
|
||||
}
|
||||
|
||||
int window_id() const { return m_window_id; }
|
||||
bool value() const { return m_value; }
|
||||
|
||||
private:
|
||||
int m_window_id { 0 };
|
||||
bool m_value { false };
|
||||
};
|
||||
|
||||
class WSAPICreateMenubarRequest : public WSAPIClientRequest {
|
||||
public:
|
||||
WSAPICreateMenubarRequest(int client_id)
|
||||
: WSAPIClientRequest(WSMessage::APICreateMenubarRequest, client_id)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class WSAPIDestroyMenubarRequest : public WSAPIClientRequest {
|
||||
public:
|
||||
WSAPIDestroyMenubarRequest(int client_id, int menubar_id)
|
||||
: WSAPIClientRequest(WSMessage::APIDestroyMenubarRequest, client_id)
|
||||
, m_menubar_id(menubar_id)
|
||||
{
|
||||
}
|
||||
|
||||
int menubar_id() const { return m_menubar_id; }
|
||||
|
||||
private:
|
||||
int m_menubar_id { 0 };
|
||||
};
|
||||
|
||||
class WSAPISetApplicationMenubarRequest : public WSAPIClientRequest {
|
||||
public:
|
||||
WSAPISetApplicationMenubarRequest(int client_id, int menubar_id)
|
||||
: WSAPIClientRequest(WSMessage::APISetApplicationMenubarRequest, client_id)
|
||||
, m_menubar_id(menubar_id)
|
||||
{
|
||||
}
|
||||
|
||||
int menubar_id() const { return m_menubar_id; }
|
||||
|
||||
private:
|
||||
int m_menubar_id { 0 };
|
||||
};
|
||||
|
||||
class WSAPIAddMenuToMenubarRequest : public WSAPIClientRequest {
|
||||
public:
|
||||
WSAPIAddMenuToMenubarRequest(int client_id, int menubar_id, int menu_id)
|
||||
: WSAPIClientRequest(WSMessage::APIAddMenuToMenubarRequest, client_id)
|
||||
, m_menubar_id(menubar_id)
|
||||
, m_menu_id(menu_id)
|
||||
{
|
||||
}
|
||||
|
||||
int menubar_id() const { return m_menubar_id; }
|
||||
int menu_id() const { return m_menu_id; }
|
||||
|
||||
private:
|
||||
int m_menubar_id { 0 };
|
||||
int m_menu_id { 0 };
|
||||
};
|
||||
|
||||
class WSAPICreateMenuRequest : public WSAPIClientRequest {
|
||||
public:
|
||||
WSAPICreateMenuRequest(int client_id, const String& text)
|
||||
: WSAPIClientRequest(WSMessage::APICreateMenuRequest, client_id)
|
||||
, m_text(text)
|
||||
{
|
||||
}
|
||||
|
||||
String text() const { return m_text; }
|
||||
|
||||
private:
|
||||
String m_text;
|
||||
};
|
||||
|
||||
class WSAPIDestroyMenuRequest : public WSAPIClientRequest {
|
||||
public:
|
||||
WSAPIDestroyMenuRequest(int client_id, int menu_id)
|
||||
: WSAPIClientRequest(WSMessage::APIDestroyMenuRequest, client_id)
|
||||
, m_menu_id(menu_id)
|
||||
{
|
||||
}
|
||||
|
||||
int menu_id() const { return m_menu_id; }
|
||||
|
||||
private:
|
||||
int m_menu_id { 0 };
|
||||
};
|
||||
|
||||
class WSAPIAddMenuItemRequest : public WSAPIClientRequest {
|
||||
public:
|
||||
WSAPIAddMenuItemRequest(int client_id, int menu_id, unsigned identifier, const String& text, const String& shortcut_text)
|
||||
: WSAPIClientRequest(WSMessage::APIAddMenuItemRequest, client_id)
|
||||
, m_menu_id(menu_id)
|
||||
, m_identifier(identifier)
|
||||
, m_text(text)
|
||||
, m_shortcut_text(shortcut_text)
|
||||
{
|
||||
}
|
||||
|
||||
int menu_id() const { return m_menu_id; }
|
||||
unsigned identifier() const { return m_identifier; }
|
||||
String text() const { return m_text; }
|
||||
String shortcut_text() const { return m_shortcut_text; }
|
||||
|
||||
private:
|
||||
int m_menu_id { 0 };
|
||||
unsigned m_identifier { 0 };
|
||||
String m_text;
|
||||
String m_shortcut_text;
|
||||
};
|
||||
|
||||
class WSAPIAddMenuSeparatorRequest : public WSAPIClientRequest {
|
||||
public:
|
||||
WSAPIAddMenuSeparatorRequest(int client_id, int menu_id)
|
||||
: WSAPIClientRequest(WSMessage::APIAddMenuSeparatorRequest, client_id)
|
||||
, m_menu_id(menu_id)
|
||||
{
|
||||
}
|
||||
|
||||
int menu_id() const { return m_menu_id; }
|
||||
|
||||
private:
|
||||
int m_menu_id { 0 };
|
||||
};
|
||||
|
||||
class WSAPISetWindowTitleRequest final : public WSAPIClientRequest {
|
||||
public:
|
||||
explicit WSAPISetWindowTitleRequest(int client_id, int window_id, String&& title)
|
||||
: WSAPIClientRequest(WSMessage::APISetWindowTitleRequest, client_id)
|
||||
, m_client_id(client_id)
|
||||
, m_window_id(window_id)
|
||||
, m_title(move(title))
|
||||
{
|
||||
}
|
||||
|
||||
int client_id() const { return m_client_id; }
|
||||
int window_id() const { return m_window_id; }
|
||||
String title() const { return m_title; }
|
||||
|
||||
private:
|
||||
int m_client_id { 0 };
|
||||
int m_window_id { 0 };
|
||||
String m_title;
|
||||
};
|
||||
|
||||
class WSAPIGetWindowTitleRequest final : public WSAPIClientRequest {
|
||||
public:
|
||||
explicit WSAPIGetWindowTitleRequest(int client_id, int window_id)
|
||||
: WSAPIClientRequest(WSMessage::APIGetWindowTitleRequest, client_id)
|
||||
, m_client_id(client_id)
|
||||
, m_window_id(window_id)
|
||||
{
|
||||
}
|
||||
|
||||
int client_id() const { return m_client_id; }
|
||||
int window_id() const { return m_window_id; }
|
||||
|
||||
private:
|
||||
int m_client_id { 0 };
|
||||
int m_window_id { 0 };
|
||||
};
|
||||
|
||||
class WSAPISetClipboardContentsRequest final : public WSAPIClientRequest {
|
||||
public:
|
||||
explicit WSAPISetClipboardContentsRequest(int client_id, int shared_buffer_id, int size)
|
||||
: WSAPIClientRequest(WSMessage::APISetClipboardContentsRequest, client_id)
|
||||
, m_client_id(client_id)
|
||||
, m_shared_buffer_id(shared_buffer_id)
|
||||
, m_size(size)
|
||||
{
|
||||
}
|
||||
|
||||
int client_id() const { return m_client_id; }
|
||||
int shared_buffer_id() const { return m_shared_buffer_id; }
|
||||
int size() const { return m_size; }
|
||||
|
||||
private:
|
||||
int m_client_id { 0 };
|
||||
int m_shared_buffer_id { 0 };
|
||||
int m_size { 0 };
|
||||
};
|
||||
|
||||
class WSAPIGetClipboardContentsRequest final : public WSAPIClientRequest {
|
||||
public:
|
||||
explicit WSAPIGetClipboardContentsRequest(int client_id)
|
||||
: WSAPIClientRequest(WSMessage::APIGetClipboardContentsRequest, client_id)
|
||||
, m_client_id(client_id)
|
||||
{
|
||||
}
|
||||
|
||||
int client_id() const { return m_client_id; }
|
||||
|
||||
private:
|
||||
int m_client_id { 0 };
|
||||
};
|
||||
|
||||
class WSAPISetWindowOpacityRequest final : public WSAPIClientRequest {
|
||||
public:
|
||||
explicit WSAPISetWindowOpacityRequest(int client_id, int window_id, float opacity)
|
||||
: WSAPIClientRequest(WSMessage::APISetWindowOpacityRequest, client_id)
|
||||
, m_client_id(client_id)
|
||||
, m_window_id(window_id)
|
||||
, m_opacity(opacity)
|
||||
{
|
||||
}
|
||||
|
||||
int client_id() const { return m_client_id; }
|
||||
int window_id() const { return m_window_id; }
|
||||
float opacity() const { return m_opacity; }
|
||||
|
||||
private:
|
||||
int m_client_id { 0 };
|
||||
int m_window_id { 0 };
|
||||
float m_opacity { 0 };
|
||||
};
|
||||
|
||||
class WSAPISetWindowBackingStoreRequest final : public WSAPIClientRequest {
|
||||
public:
|
||||
explicit WSAPISetWindowBackingStoreRequest(int client_id, int window_id, int shared_buffer_id, const Size& size, size_t bpp, size_t pitch, bool has_alpha_channel, bool flush_immediately)
|
||||
: WSAPIClientRequest(WSMessage::APISetWindowBackingStoreRequest, client_id)
|
||||
, m_client_id(client_id)
|
||||
, m_window_id(window_id)
|
||||
, m_shared_buffer_id(shared_buffer_id)
|
||||
, m_size(size)
|
||||
, m_bpp(bpp)
|
||||
, m_pitch(pitch)
|
||||
, m_has_alpha_channel(has_alpha_channel)
|
||||
, m_flush_immediately(flush_immediately)
|
||||
{
|
||||
}
|
||||
|
||||
int client_id() const { return m_client_id; }
|
||||
int window_id() const { return m_window_id; }
|
||||
int shared_buffer_id() const { return m_shared_buffer_id; }
|
||||
Size size() const { return m_size; }
|
||||
size_t bpp() const { return m_bpp; }
|
||||
size_t pitch() const { return m_pitch; }
|
||||
bool has_alpha_channel() const { return m_has_alpha_channel; }
|
||||
bool flush_immediately() const { return m_flush_immediately; }
|
||||
|
||||
private:
|
||||
int m_client_id { 0 };
|
||||
int m_window_id { 0 };
|
||||
int m_shared_buffer_id { 0 };
|
||||
Size m_size;
|
||||
size_t m_bpp;
|
||||
size_t m_pitch;
|
||||
bool m_has_alpha_channel;
|
||||
bool m_flush_immediately;
|
||||
};
|
||||
|
||||
class WSAPISetWindowRectRequest final : public WSAPIClientRequest {
|
||||
public:
|
||||
explicit WSAPISetWindowRectRequest(int client_id, int window_id, const Rect& rect)
|
||||
: WSAPIClientRequest(WSMessage::APISetWindowRectRequest, client_id)
|
||||
, m_client_id(client_id)
|
||||
, m_window_id(window_id)
|
||||
, m_rect(rect)
|
||||
{
|
||||
}
|
||||
|
||||
int client_id() const { return m_client_id; }
|
||||
int window_id() const { return m_window_id; }
|
||||
Rect rect() const { return m_rect; }
|
||||
|
||||
private:
|
||||
int m_client_id { 0 };
|
||||
int m_window_id { 0 };
|
||||
Rect m_rect;
|
||||
};
|
||||
|
||||
class WSAPIGetWindowRectRequest final : public WSAPIClientRequest {
|
||||
public:
|
||||
explicit WSAPIGetWindowRectRequest(int client_id, int window_id)
|
||||
: WSAPIClientRequest(WSMessage::APIGetWindowRectRequest, client_id)
|
||||
, m_client_id(client_id)
|
||||
, m_window_id(window_id)
|
||||
{
|
||||
}
|
||||
|
||||
int client_id() const { return m_client_id; }
|
||||
int window_id() const { return m_window_id; }
|
||||
|
||||
private:
|
||||
int m_client_id { 0 };
|
||||
int m_window_id { 0 };
|
||||
};
|
||||
|
||||
class WSAPICreateWindowRequest : public WSAPIClientRequest {
|
||||
public:
|
||||
WSAPICreateWindowRequest(int client_id, const Rect& rect, const String& title, bool has_alpha_channel, bool modal, bool resizable, float opacity, const Size& base_size, const Size& size_increment)
|
||||
: WSAPIClientRequest(WSMessage::APICreateWindowRequest, client_id)
|
||||
, m_rect(rect)
|
||||
, m_title(title)
|
||||
, m_opacity(opacity)
|
||||
, m_has_alpha_channel(has_alpha_channel)
|
||||
, m_modal(modal)
|
||||
, m_resizable(resizable)
|
||||
, m_size_increment(size_increment)
|
||||
, m_base_size(base_size)
|
||||
{
|
||||
}
|
||||
|
||||
Rect rect() const { return m_rect; }
|
||||
String title() const { return m_title; }
|
||||
bool has_alpha_channel() const { return m_has_alpha_channel; }
|
||||
bool is_modal() const { return m_modal; }
|
||||
bool is_resizable() const { return m_resizable; }
|
||||
float opacity() const { return m_opacity; }
|
||||
Size size_increment() const { return m_size_increment; }
|
||||
Size base_size() const { return m_base_size; }
|
||||
|
||||
private:
|
||||
Rect m_rect;
|
||||
String m_title;
|
||||
float m_opacity { 0 };
|
||||
bool m_has_alpha_channel { false };
|
||||
bool m_modal { false };
|
||||
bool m_resizable { false };
|
||||
Size m_size_increment;
|
||||
Size m_base_size;
|
||||
};
|
||||
|
||||
class WSAPIDestroyWindowRequest : public WSAPIClientRequest {
|
||||
public:
|
||||
WSAPIDestroyWindowRequest(int client_id, int window_id)
|
||||
: WSAPIClientRequest(WSMessage::APIDestroyWindowRequest, client_id)
|
||||
, m_window_id(window_id)
|
||||
{
|
||||
}
|
||||
|
||||
int window_id() const { return m_window_id; }
|
||||
|
||||
private:
|
||||
int m_window_id { 0 };
|
||||
};
|
||||
|
||||
class WSAPIInvalidateRectRequest final : public WSAPIClientRequest {
|
||||
public:
|
||||
explicit WSAPIInvalidateRectRequest(int client_id, int window_id, const Rect& rect)
|
||||
: WSAPIClientRequest(WSMessage::APIInvalidateRectRequest, client_id)
|
||||
, m_window_id(window_id)
|
||||
, m_rect(rect)
|
||||
{
|
||||
}
|
||||
|
||||
int window_id() const { return m_window_id; }
|
||||
Rect rect() const { return m_rect; }
|
||||
|
||||
private:
|
||||
int m_window_id { 0 };
|
||||
Rect m_rect;
|
||||
};
|
||||
|
||||
class WSAPIGetWindowBackingStoreRequest final : public WSAPIClientRequest {
|
||||
public:
|
||||
explicit WSAPIGetWindowBackingStoreRequest(int client_id, int window_id)
|
||||
: WSAPIClientRequest(WSMessage::APIGetWindowBackingStoreRequest, client_id)
|
||||
, m_window_id(window_id)
|
||||
{
|
||||
}
|
||||
|
||||
int window_id() const { return m_window_id; }
|
||||
|
||||
private:
|
||||
int m_window_id { 0 };
|
||||
};
|
||||
|
||||
class WSAPIDidFinishPaintingNotification final : public WSAPIClientRequest {
|
||||
public:
|
||||
explicit WSAPIDidFinishPaintingNotification(int client_id, int window_id, const Rect& rect)
|
||||
: WSAPIClientRequest(WSMessage::APIDidFinishPaintingNotification, client_id)
|
||||
, m_window_id(window_id)
|
||||
, m_rect(rect)
|
||||
{
|
||||
}
|
||||
|
||||
int window_id() const { return m_window_id; }
|
||||
Rect rect() const { return m_rect; }
|
||||
|
||||
private:
|
||||
int m_window_id { 0 };
|
||||
Rect m_rect;
|
||||
};
|
||||
|
||||
enum class MouseButton : byte {
|
||||
None = 0,
|
||||
Left = 1,
|
||||
Right = 2,
|
||||
Middle = 4,
|
||||
};
|
||||
|
||||
class WSKeyEvent final : public WSMessage {
|
||||
public:
|
||||
WSKeyEvent(Type type, int key, char character, byte modifiers)
|
||||
: WSMessage(type)
|
||||
, m_key(key)
|
||||
, m_character(character)
|
||||
, m_modifiers(modifiers)
|
||||
{
|
||||
}
|
||||
|
||||
int key() const { return m_key; }
|
||||
bool ctrl() const { return m_modifiers & Mod_Ctrl; }
|
||||
bool alt() const { return m_modifiers & Mod_Alt; }
|
||||
bool shift() const { return m_modifiers & Mod_Shift; }
|
||||
bool logo() const { return m_modifiers & Mod_Logo; }
|
||||
byte modifiers() const { return m_modifiers; }
|
||||
char character() const { return m_character; }
|
||||
|
||||
private:
|
||||
friend class WSMessageLoop;
|
||||
friend class WSScreen;
|
||||
int m_key { 0 };
|
||||
char m_character { 0 };
|
||||
byte m_modifiers { 0 };
|
||||
};
|
||||
|
||||
class WSMouseEvent final : public WSMessage {
|
||||
public:
|
||||
WSMouseEvent(Type type, const Point& position, unsigned buttons, MouseButton button, unsigned modifiers)
|
||||
: WSMessage(type)
|
||||
, m_position(position)
|
||||
, m_buttons(buttons)
|
||||
, m_button(button)
|
||||
, m_modifiers(modifiers)
|
||||
{
|
||||
}
|
||||
|
||||
Point position() const { return m_position; }
|
||||
int x() const { return m_position.x(); }
|
||||
int y() const { return m_position.y(); }
|
||||
MouseButton button() const { return m_button; }
|
||||
unsigned buttons() const { return m_buttons; }
|
||||
unsigned modifiers() const { return m_modifiers; }
|
||||
|
||||
private:
|
||||
Point m_position;
|
||||
unsigned m_buttons { 0 };
|
||||
MouseButton m_button { MouseButton::None };
|
||||
unsigned m_modifiers { 0 };
|
||||
};
|
||||
|
||||
class WSResizeEvent final : public WSMessage {
|
||||
public:
|
||||
WSResizeEvent(const Rect& old_rect, const Rect& rect)
|
||||
: WSMessage(WSMessage::WindowResized)
|
||||
, m_old_rect(old_rect)
|
||||
, m_rect(rect)
|
||||
{
|
||||
}
|
||||
|
||||
Rect old_rect() const { return m_old_rect; }
|
||||
Rect rect() const { return m_rect; }
|
||||
|
||||
private:
|
||||
Rect m_old_rect;
|
||||
Rect m_rect;
|
||||
};
|
332
Servers/WindowServer/WSMessageLoop.cpp
Normal file
332
Servers/WindowServer/WSMessageLoop.cpp
Normal file
|
@ -0,0 +1,332 @@
|
|||
#include <WindowServer/WSMessageLoop.h>
|
||||
#include <WindowServer/WSMessage.h>
|
||||
#include <WindowServer/WSMessageReceiver.h>
|
||||
#include <WindowServer/WSWindowManager.h>
|
||||
#include <WindowServer/WSScreen.h>
|
||||
#include <WindowServer/WSClientConnection.h>
|
||||
#include <WindowServer/WSAPITypes.h>
|
||||
#include <Kernel/KeyCode.h>
|
||||
#include <Kernel/MousePacket.h>
|
||||
#include <LibC/sys/socket.h>
|
||||
#include <LibC/sys/select.h>
|
||||
#include <LibC/unistd.h>
|
||||
#include <LibC/time.h>
|
||||
#include <LibC/fcntl.h>
|
||||
#include <LibC/stdio.h>
|
||||
#include <LibC/errno.h>
|
||||
|
||||
//#define WSMESSAGELOOP_DEBUG
|
||||
|
||||
static WSMessageLoop* s_the;
|
||||
|
||||
WSMessageLoop::WSMessageLoop()
|
||||
{
|
||||
if (!s_the)
|
||||
s_the = this;
|
||||
}
|
||||
|
||||
WSMessageLoop::~WSMessageLoop()
|
||||
{
|
||||
}
|
||||
|
||||
WSMessageLoop& WSMessageLoop::the()
|
||||
{
|
||||
ASSERT(s_the);
|
||||
return *s_the;
|
||||
}
|
||||
|
||||
int WSMessageLoop::exec()
|
||||
{
|
||||
m_keyboard_fd = open("/dev/keyboard", O_RDONLY | O_NONBLOCK | O_CLOEXEC);
|
||||
m_mouse_fd = open("/dev/psaux", O_RDONLY | O_NONBLOCK | O_CLOEXEC);
|
||||
|
||||
unlink("/tmp/wsportal");
|
||||
|
||||
m_server_fd = socket(AF_LOCAL, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
|
||||
ASSERT(m_server_fd >= 0);
|
||||
sockaddr_un address;
|
||||
address.sun_family = AF_LOCAL;
|
||||
strcpy(address.sun_path, "/tmp/wsportal");
|
||||
int rc = bind(m_server_fd, (const sockaddr*)&address, sizeof(address));
|
||||
ASSERT(rc == 0);
|
||||
rc = listen(m_server_fd, 5);
|
||||
ASSERT(rc == 0);
|
||||
|
||||
ASSERT(m_keyboard_fd >= 0);
|
||||
ASSERT(m_mouse_fd >= 0);
|
||||
|
||||
m_running = true;
|
||||
for (;;) {
|
||||
wait_for_message();
|
||||
Vector<QueuedMessage> messages = move(m_queued_messages);
|
||||
|
||||
for (auto& queued_message : messages) {
|
||||
auto* receiver = queued_message.receiver.ptr();
|
||||
auto& message = *queued_message.message;
|
||||
#ifdef WSMESSAGELOOP_DEBUG
|
||||
dbgprintf("WSMessageLoop: receiver{%p} message %u\n", receiver, (unsigned)message.type());
|
||||
#endif
|
||||
if (receiver)
|
||||
receiver->on_message(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WSMessageLoop::post_message(WSMessageReceiver& receiver, OwnPtr<WSMessage>&& message)
|
||||
{
|
||||
#ifdef WSMESSAGELOOP_DEBUG
|
||||
dbgprintf("WSMessageLoop::post_message: {%u} << receiver=%p, message=%p (type=%u)\n", m_queued_messages.size(), &receiver, message.ptr(), message->type());
|
||||
#endif
|
||||
m_queued_messages.append({ receiver.make_weak_ptr(), move(message) });
|
||||
}
|
||||
|
||||
void WSMessageLoop::Timer::reload()
|
||||
{
|
||||
struct timeval now;
|
||||
gettimeofday(&now, nullptr);
|
||||
next_fire_time = {
|
||||
now.tv_sec + (interval / 1000),
|
||||
now.tv_usec + (interval % 1000) * 1000
|
||||
};
|
||||
}
|
||||
|
||||
int WSMessageLoop::start_timer(int interval, Function<void()>&& callback)
|
||||
{
|
||||
auto timer = make<Timer>();
|
||||
int timer_id = m_next_timer_id++;
|
||||
timer->timer_id = timer_id;
|
||||
timer->callback = move(callback);
|
||||
timer->interval = interval;
|
||||
timer->reload();
|
||||
m_timers.set(timer_id, move(timer));
|
||||
return timer_id;
|
||||
}
|
||||
|
||||
int WSMessageLoop::stop_timer(int timer_id)
|
||||
{
|
||||
auto it = m_timers.find(timer_id);
|
||||
if (it == m_timers.end())
|
||||
return -1;
|
||||
m_timers.remove(it);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void WSMessageLoop::wait_for_message()
|
||||
{
|
||||
fd_set rfds;
|
||||
FD_ZERO(&rfds);
|
||||
int max_fd = 0;
|
||||
auto add_fd_to_set = [&max_fd] (int fd, auto& set) {
|
||||
FD_SET(fd, &set);
|
||||
if (fd > max_fd)
|
||||
max_fd = fd;
|
||||
};
|
||||
|
||||
add_fd_to_set(m_keyboard_fd, rfds);
|
||||
add_fd_to_set(m_mouse_fd, rfds);
|
||||
add_fd_to_set(m_server_fd, rfds);
|
||||
|
||||
WSClientConnection::for_each_client([&] (WSClientConnection& client) {
|
||||
add_fd_to_set(client.fd(), rfds);
|
||||
});
|
||||
|
||||
struct timeval timeout = { 0, 0 };
|
||||
|
||||
if (m_queued_messages.is_empty()) {
|
||||
bool had_any_timer = false;
|
||||
for (auto& it : m_timers) {
|
||||
auto& timer = *it.value;
|
||||
if (!had_any_timer) {
|
||||
timeout = timer.next_fire_time;
|
||||
had_any_timer = true;
|
||||
continue;
|
||||
}
|
||||
if (timer.next_fire_time.tv_sec > timeout.tv_sec || (timer.next_fire_time.tv_sec == timeout.tv_sec && timer.next_fire_time.tv_usec > timeout.tv_usec))
|
||||
timeout = timer.next_fire_time;
|
||||
}
|
||||
}
|
||||
|
||||
int rc = select(max_fd + 1, &rfds, nullptr, nullptr, m_queued_messages.is_empty() && m_timers.is_empty() ? nullptr : &timeout);
|
||||
if (rc < 0) {
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
struct timeval now;
|
||||
gettimeofday(&now, nullptr);
|
||||
for (auto& it : m_timers) {
|
||||
auto& timer = *it.value;
|
||||
if (now.tv_sec > timer.next_fire_time.tv_sec || (now.tv_sec == timer.next_fire_time.tv_sec && now.tv_usec > timer.next_fire_time.tv_usec)) {
|
||||
timer.callback();
|
||||
timer.reload();
|
||||
}
|
||||
}
|
||||
|
||||
if (FD_ISSET(m_keyboard_fd, &rfds))
|
||||
drain_keyboard();
|
||||
if (FD_ISSET(m_mouse_fd, &rfds))
|
||||
drain_mouse();
|
||||
if (FD_ISSET(m_server_fd, &rfds)) {
|
||||
sockaddr_un address;
|
||||
socklen_t address_size = sizeof(address);
|
||||
int client_fd = accept(m_server_fd, (sockaddr*)&address, &address_size);
|
||||
if (client_fd < 0) {
|
||||
dbgprintf("WindowServer: accept() failed: %s\n", strerror(errno));
|
||||
} else {
|
||||
new WSClientConnection(client_fd);
|
||||
}
|
||||
}
|
||||
WSClientConnection::for_each_client([&] (WSClientConnection& client) {
|
||||
if (!FD_ISSET(client.fd(), &rfds))
|
||||
return;
|
||||
unsigned messages_received = 0;
|
||||
for (;;) {
|
||||
WSAPI_ClientMessage message;
|
||||
// FIXME: Don't go one message at a time, that's so much context switching, oof.
|
||||
ssize_t nread = read(client.fd(), &message, sizeof(WSAPI_ClientMessage));
|
||||
if (nread == 0) {
|
||||
if (!messages_received)
|
||||
notify_client_disconnected(client.client_id());
|
||||
break;
|
||||
}
|
||||
if (nread < 0) {
|
||||
perror("read");
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
on_receive_from_client(client.client_id(), message);
|
||||
++messages_received;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void WSMessageLoop::drain_mouse()
|
||||
{
|
||||
auto& screen = WSScreen::the();
|
||||
unsigned prev_buttons = screen.mouse_button_state();
|
||||
int dx = 0;
|
||||
int dy = 0;
|
||||
unsigned buttons = prev_buttons;
|
||||
for (;;) {
|
||||
MousePacket packet;
|
||||
ssize_t nread = read(m_mouse_fd, &packet, sizeof(MousePacket));
|
||||
if (nread == 0)
|
||||
break;
|
||||
ASSERT(nread == sizeof(packet));
|
||||
buttons = packet.buttons;
|
||||
|
||||
dx += packet.dx;
|
||||
dy += -packet.dy;
|
||||
if (buttons != prev_buttons) {
|
||||
screen.on_receive_mouse_data(dx, dy, buttons);
|
||||
dx = 0;
|
||||
dy = 0;
|
||||
prev_buttons = buttons;
|
||||
}
|
||||
}
|
||||
if (dx || dy)
|
||||
screen.on_receive_mouse_data(dx, dy, buttons);
|
||||
}
|
||||
|
||||
void WSMessageLoop::drain_keyboard()
|
||||
{
|
||||
auto& screen = WSScreen::the();
|
||||
for (;;) {
|
||||
KeyEvent event;
|
||||
ssize_t nread = read(m_keyboard_fd, (byte*)&event, sizeof(KeyEvent));
|
||||
if (nread == 0)
|
||||
break;
|
||||
ASSERT(nread == sizeof(KeyEvent));
|
||||
screen.on_receive_keyboard_data(event);
|
||||
}
|
||||
}
|
||||
|
||||
void WSMessageLoop::notify_client_disconnected(int client_id)
|
||||
{
|
||||
auto* client = WSClientConnection::from_client_id(client_id);
|
||||
if (!client)
|
||||
return;
|
||||
post_message(*client, make<WSClientDisconnectedNotification>(client_id));
|
||||
}
|
||||
|
||||
void WSMessageLoop::on_receive_from_client(int client_id, const WSAPI_ClientMessage& message)
|
||||
{
|
||||
#if 0
|
||||
// FIXME: This should not be necessary.. why is this necessary?
|
||||
while (!running())
|
||||
sched_yield();
|
||||
#endif
|
||||
|
||||
WSClientConnection& client = *WSClientConnection::from_client_id(client_id);
|
||||
switch (message.type) {
|
||||
case WSAPI_ClientMessage::Type::CreateMenubar:
|
||||
post_message(client, make<WSAPICreateMenubarRequest>(client_id));
|
||||
break;
|
||||
case WSAPI_ClientMessage::Type::DestroyMenubar:
|
||||
post_message(client, make<WSAPIDestroyMenubarRequest>(client_id, message.menu.menubar_id));
|
||||
break;
|
||||
case WSAPI_ClientMessage::Type::SetApplicationMenubar:
|
||||
post_message(client, make<WSAPISetApplicationMenubarRequest>(client_id, message.menu.menubar_id));
|
||||
break;
|
||||
case WSAPI_ClientMessage::Type::AddMenuToMenubar:
|
||||
post_message(client, make<WSAPIAddMenuToMenubarRequest>(client_id, message.menu.menubar_id, message.menu.menu_id));
|
||||
break;
|
||||
case WSAPI_ClientMessage::Type::CreateMenu:
|
||||
ASSERT(message.text_length < (ssize_t)sizeof(message.text));
|
||||
post_message(client, make<WSAPICreateMenuRequest>(client_id, String(message.text, message.text_length)));
|
||||
break;
|
||||
case WSAPI_ClientMessage::Type::DestroyMenu:
|
||||
post_message(client, make<WSAPIDestroyMenuRequest>(client_id, message.menu.menu_id));
|
||||
break;
|
||||
case WSAPI_ClientMessage::Type::AddMenuItem:
|
||||
ASSERT(message.text_length < (ssize_t)sizeof(message.text));
|
||||
ASSERT(message.menu.shortcut_text_length < (ssize_t)sizeof(message.menu.shortcut_text));
|
||||
post_message(client, make<WSAPIAddMenuItemRequest>(client_id, message.menu.menu_id, message.menu.identifier, String(message.text, message.text_length), String(message.menu.shortcut_text, message.menu.shortcut_text_length)));
|
||||
break;
|
||||
case WSAPI_ClientMessage::Type::AddMenuSeparator:
|
||||
post_message(client, make<WSAPIAddMenuSeparatorRequest>(client_id, message.menu.menu_id));
|
||||
break;
|
||||
case WSAPI_ClientMessage::Type::CreateWindow:
|
||||
ASSERT(message.text_length < (ssize_t)sizeof(message.text));
|
||||
post_message(client, make<WSAPICreateWindowRequest>(client_id, message.window.rect, String(message.text, message.text_length), message.window.has_alpha_channel, message.window.modal, message.window.resizable, message.window.opacity, message.window.base_size, message.window.size_increment));
|
||||
break;
|
||||
case WSAPI_ClientMessage::Type::DestroyWindow:
|
||||
post_message(client, make<WSAPIDestroyWindowRequest>(client_id, message.window_id));
|
||||
break;
|
||||
case WSAPI_ClientMessage::Type::SetWindowTitle:
|
||||
ASSERT(message.text_length < (ssize_t)sizeof(message.text));
|
||||
post_message(client, make<WSAPISetWindowTitleRequest>(client_id, message.window_id, String(message.text, message.text_length)));
|
||||
break;
|
||||
case WSAPI_ClientMessage::Type::GetWindowTitle:
|
||||
ASSERT(message.text_length < (ssize_t)sizeof(message.text));
|
||||
post_message(client, make<WSAPIGetWindowTitleRequest>(client_id, message.window_id));
|
||||
break;
|
||||
case WSAPI_ClientMessage::Type::SetWindowRect:
|
||||
post_message(client, make<WSAPISetWindowRectRequest>(client_id, message.window_id, message.window.rect));
|
||||
break;
|
||||
case WSAPI_ClientMessage::Type::GetWindowRect:
|
||||
post_message(client, make<WSAPIGetWindowRectRequest>(client_id, message.window_id));
|
||||
break;
|
||||
case WSAPI_ClientMessage::Type::SetClipboardContents:
|
||||
post_message(client, make<WSAPISetClipboardContentsRequest>(client_id, message.clipboard.shared_buffer_id, message.clipboard.contents_size));
|
||||
break;
|
||||
case WSAPI_ClientMessage::Type::GetClipboardContents:
|
||||
post_message(client, make<WSAPIGetClipboardContentsRequest>(client_id));
|
||||
break;
|
||||
case WSAPI_ClientMessage::Type::InvalidateRect:
|
||||
post_message(client, make<WSAPIInvalidateRectRequest>(client_id, message.window_id, message.window.rect));
|
||||
break;
|
||||
case WSAPI_ClientMessage::Type::DidFinishPainting:
|
||||
post_message(client, make<WSAPIDidFinishPaintingNotification>(client_id, message.window_id, message.window.rect));
|
||||
break;
|
||||
case WSAPI_ClientMessage::Type::GetWindowBackingStore:
|
||||
post_message(client, make<WSAPIGetWindowBackingStoreRequest>(client_id, message.window_id));
|
||||
break;
|
||||
case WSAPI_ClientMessage::Type::SetWindowBackingStore:
|
||||
post_message(client, make<WSAPISetWindowBackingStoreRequest>(client_id, message.window_id, message.backing.shared_buffer_id, message.backing.size, message.backing.bpp, message.backing.pitch, message.backing.has_alpha_channel, message.backing.flush_immediately));
|
||||
break;
|
||||
case WSAPI_ClientMessage::Type::SetGlobalCursorTracking:
|
||||
post_message(client, make<WSAPISetGlobalCursorTrackingRequest>(client_id, message.window_id, message.value));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
62
Servers/WindowServer/WSMessageLoop.h
Normal file
62
Servers/WindowServer/WSMessageLoop.h
Normal file
|
@ -0,0 +1,62 @@
|
|||
#pragma once
|
||||
|
||||
#include "WSMessage.h"
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <AK/Function.h>
|
||||
#include <AK/WeakPtr.h>
|
||||
|
||||
class WSMessageReceiver;
|
||||
struct WSAPI_ClientMessage;
|
||||
struct WSAPI_ServerMessage;
|
||||
|
||||
class WSMessageLoop {
|
||||
public:
|
||||
WSMessageLoop();
|
||||
~WSMessageLoop();
|
||||
|
||||
int exec();
|
||||
|
||||
void post_message(WSMessageReceiver& receiver, OwnPtr<WSMessage>&&);
|
||||
|
||||
static WSMessageLoop& the();
|
||||
|
||||
bool running() const { return m_running; }
|
||||
|
||||
int start_timer(int ms, Function<void()>&&);
|
||||
int stop_timer(int timer_id);
|
||||
|
||||
void on_receive_from_client(int client_id, const WSAPI_ClientMessage&);
|
||||
|
||||
void notify_client_disconnected(int client_id);
|
||||
|
||||
private:
|
||||
void wait_for_message();
|
||||
void drain_mouse();
|
||||
void drain_keyboard();
|
||||
|
||||
struct QueuedMessage {
|
||||
WeakPtr<WSMessageReceiver> receiver;
|
||||
OwnPtr<WSMessage> message;
|
||||
};
|
||||
Vector<QueuedMessage> m_queued_messages;
|
||||
|
||||
bool m_running { false };
|
||||
|
||||
int m_keyboard_fd { -1 };
|
||||
int m_mouse_fd { -1 };
|
||||
int m_server_fd { -1 };
|
||||
|
||||
struct Timer {
|
||||
void reload();
|
||||
|
||||
int timer_id { 0 };
|
||||
int interval { 0 };
|
||||
struct timeval next_fire_time { 0, 0 };
|
||||
Function<void()> callback;
|
||||
};
|
||||
|
||||
int m_next_timer_id { 1 };
|
||||
HashMap<int, OwnPtr<Timer>> m_timers;
|
||||
};
|
9
Servers/WindowServer/WSMessageReceiver.cpp
Normal file
9
Servers/WindowServer/WSMessageReceiver.cpp
Normal file
|
@ -0,0 +1,9 @@
|
|||
#include "WSMessageReceiver.h"
|
||||
|
||||
WSMessageReceiver::WSMessageReceiver()
|
||||
{
|
||||
}
|
||||
|
||||
WSMessageReceiver::~WSMessageReceiver()
|
||||
{
|
||||
}
|
13
Servers/WindowServer/WSMessageReceiver.h
Normal file
13
Servers/WindowServer/WSMessageReceiver.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/Weakable.h>
|
||||
|
||||
class WSMessage;
|
||||
|
||||
class WSMessageReceiver : public Weakable<WSMessageReceiver> {
|
||||
public:
|
||||
WSMessageReceiver();
|
||||
virtual ~WSMessageReceiver();
|
||||
|
||||
virtual void on_message(WSMessage&) = 0;
|
||||
};
|
98
Servers/WindowServer/WSScreen.cpp
Normal file
98
Servers/WindowServer/WSScreen.cpp
Normal file
|
@ -0,0 +1,98 @@
|
|||
#include "WSScreen.h"
|
||||
#include "WSMessageLoop.h"
|
||||
#include "WSMessage.h"
|
||||
#include "WSWindowManager.h"
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
static WSScreen* s_the;
|
||||
|
||||
WSScreen& WSScreen::the()
|
||||
{
|
||||
ASSERT(s_the);
|
||||
return *s_the;
|
||||
}
|
||||
|
||||
WSScreen::WSScreen(unsigned width, unsigned height)
|
||||
: m_width(width)
|
||||
, m_height(height)
|
||||
{
|
||||
ASSERT(!s_the);
|
||||
s_the = this;
|
||||
m_cursor_location = rect().center();
|
||||
m_framebuffer_fd = open("/dev/bxvga", O_RDWR);
|
||||
ASSERT(m_framebuffer_fd >= 0);
|
||||
|
||||
set_resolution(width, height);
|
||||
}
|
||||
|
||||
WSScreen::~WSScreen()
|
||||
{
|
||||
}
|
||||
|
||||
void WSScreen::set_resolution(int width, int height)
|
||||
{
|
||||
struct BXVGAResolution {
|
||||
int width;
|
||||
int height;
|
||||
};
|
||||
BXVGAResolution resolution { (int)width, (int)height};
|
||||
int rc = ioctl(m_framebuffer_fd, 1985, (int)&resolution);
|
||||
ASSERT(rc == 0);
|
||||
|
||||
if (m_framebuffer) {
|
||||
size_t previous_size_in_bytes = m_width * m_height * sizeof(RGBA32) * 2;
|
||||
int rc = munmap(m_framebuffer, previous_size_in_bytes);
|
||||
ASSERT(rc == 0);
|
||||
}
|
||||
|
||||
size_t framebuffer_size_in_bytes = width * height * sizeof(RGBA32) * 2;
|
||||
m_framebuffer = (RGBA32*)mmap(nullptr, framebuffer_size_in_bytes, PROT_READ | PROT_WRITE, MAP_SHARED, m_framebuffer_fd, 0);
|
||||
ASSERT(m_framebuffer && m_framebuffer != (void*)-1);
|
||||
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
|
||||
m_cursor_location.constrain(rect());
|
||||
}
|
||||
|
||||
void WSScreen::on_receive_mouse_data(int dx, int dy, unsigned buttons)
|
||||
{
|
||||
auto prev_location = m_cursor_location;
|
||||
m_cursor_location.move_by(dx, dy);
|
||||
m_cursor_location.constrain(rect());
|
||||
unsigned prev_buttons = m_mouse_button_state;
|
||||
m_mouse_button_state = buttons;
|
||||
unsigned changed_buttons = prev_buttons ^ buttons;
|
||||
auto post_mousedown_or_mouseup_if_needed = [&] (MouseButton button) {
|
||||
if (!(changed_buttons & (unsigned)button))
|
||||
return;
|
||||
auto message = make<WSMouseEvent>(buttons & (unsigned)button ? WSMessage::MouseDown : WSMessage::MouseUp, m_cursor_location, buttons, button, m_modifiers);
|
||||
WSMessageLoop::the().post_message(WSWindowManager::the(), move(message));
|
||||
};
|
||||
post_mousedown_or_mouseup_if_needed(MouseButton::Left);
|
||||
post_mousedown_or_mouseup_if_needed(MouseButton::Right);
|
||||
post_mousedown_or_mouseup_if_needed(MouseButton::Middle);
|
||||
if (m_cursor_location != prev_location) {
|
||||
auto message = make<WSMouseEvent>(WSMessage::MouseMove, m_cursor_location, buttons, MouseButton::None, m_modifiers);
|
||||
WSMessageLoop::the().post_message(WSWindowManager::the(), move(message));
|
||||
}
|
||||
// NOTE: Invalidate the cursor if it moved, or if the left button changed state (for the cursor color inversion.)
|
||||
if (m_cursor_location != prev_location || changed_buttons & (unsigned)MouseButton::Left)
|
||||
WSWindowManager::the().invalidate_cursor();
|
||||
}
|
||||
|
||||
void WSScreen::on_receive_keyboard_data(KeyEvent kernel_event)
|
||||
{
|
||||
m_modifiers = kernel_event.modifiers();
|
||||
auto message = make<WSKeyEvent>(kernel_event.is_press() ? WSMessage::KeyDown : WSMessage::KeyUp, kernel_event.key, kernel_event.character, kernel_event.modifiers());
|
||||
WSMessageLoop::the().post_message(WSWindowManager::the(), move(message));
|
||||
}
|
||||
|
||||
void WSScreen::set_y_offset(int offset)
|
||||
{
|
||||
int rc = ioctl(m_framebuffer_fd, 1982, offset);
|
||||
ASSERT(rc == 0);
|
||||
}
|
48
Servers/WindowServer/WSScreen.h
Normal file
48
Servers/WindowServer/WSScreen.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
#pragma once
|
||||
|
||||
#include <SharedGraphics/Rect.h>
|
||||
#include <SharedGraphics/Size.h>
|
||||
#include <SharedGraphics/Color.h>
|
||||
#include <Kernel/KeyCode.h>
|
||||
|
||||
class WSScreen {
|
||||
public:
|
||||
WSScreen(unsigned width, unsigned height);
|
||||
~WSScreen();
|
||||
|
||||
void set_resolution(int width, int height);
|
||||
|
||||
int width() const { return m_width; }
|
||||
int height() const { return m_height; }
|
||||
RGBA32* scanline(int y);
|
||||
|
||||
static WSScreen& the();
|
||||
|
||||
Size size() const { return { width(), height() }; }
|
||||
Rect rect() const { return { 0, 0, width(), height() }; }
|
||||
|
||||
void set_y_offset(int);
|
||||
|
||||
Point cursor_location() const { return m_cursor_location; }
|
||||
unsigned mouse_button_state() const { return m_mouse_button_state; }
|
||||
|
||||
void on_receive_mouse_data(int dx, int dy, unsigned buttons);
|
||||
void on_receive_keyboard_data(KeyEvent);
|
||||
|
||||
private:
|
||||
RGBA32* m_framebuffer { nullptr };
|
||||
|
||||
int m_width { 0 };
|
||||
int m_height { 0 };
|
||||
int m_framebuffer_fd { -1 };
|
||||
|
||||
Point m_cursor_location;
|
||||
unsigned m_mouse_button_state { 0 };
|
||||
unsigned m_modifiers { 0 };
|
||||
};
|
||||
|
||||
inline RGBA32* WSScreen::scanline(int y)
|
||||
{
|
||||
size_t pitch = sizeof(RGBA32) * width();
|
||||
return reinterpret_cast<RGBA32*>(((byte*)m_framebuffer) + (y * pitch));
|
||||
}
|
180
Servers/WindowServer/WSWindow.cpp
Normal file
180
Servers/WindowServer/WSWindow.cpp
Normal file
|
@ -0,0 +1,180 @@
|
|||
#include "WSWindow.h"
|
||||
#include "WSWindowManager.h"
|
||||
#include "WSMessage.h"
|
||||
#include "WSMessageLoop.h"
|
||||
#include <WindowServer/WSAPITypes.h>
|
||||
#include <WindowServer/WSClientConnection.h>
|
||||
|
||||
static GraphicsBitmap& default_window_icon()
|
||||
{
|
||||
static GraphicsBitmap* s_icon;
|
||||
if (!s_icon)
|
||||
s_icon = GraphicsBitmap::load_from_file(GraphicsBitmap::Format::RGBA32, "/res/icons/window16.rgb", { 16, 16 }).leak_ref();
|
||||
return *s_icon;
|
||||
}
|
||||
|
||||
WSWindow::WSWindow(WSMessageReceiver& internal_owner, WSWindowType type)
|
||||
: m_internal_owner(&internal_owner)
|
||||
, m_type(type)
|
||||
, m_icon(default_window_icon())
|
||||
{
|
||||
WSWindowManager::the().add_window(*this);
|
||||
}
|
||||
|
||||
WSWindow::WSWindow(WSClientConnection& client, int window_id, bool modal)
|
||||
: m_client(&client)
|
||||
, m_type(WSWindowType::Normal)
|
||||
, m_modal(modal)
|
||||
, m_window_id(window_id)
|
||||
, m_icon(default_window_icon())
|
||||
{
|
||||
WSWindowManager::the().add_window(*this);
|
||||
}
|
||||
|
||||
WSWindow::~WSWindow()
|
||||
{
|
||||
WSWindowManager::the().remove_window(*this);
|
||||
}
|
||||
|
||||
void WSWindow::set_title(String&& title)
|
||||
{
|
||||
if (m_title == title)
|
||||
return;
|
||||
m_title = move(title);
|
||||
WSWindowManager::the().notify_title_changed(*this);
|
||||
}
|
||||
|
||||
void WSWindow::set_rect(const Rect& rect)
|
||||
{
|
||||
Rect old_rect;
|
||||
if (m_rect == rect)
|
||||
return;
|
||||
old_rect = m_rect;
|
||||
m_rect = rect;
|
||||
if (!m_client && (!m_backing_store || old_rect.size() != rect.size())) {
|
||||
m_backing_store = GraphicsBitmap::create(GraphicsBitmap::Format::RGB32, m_rect.size());
|
||||
}
|
||||
WSWindowManager::the().notify_rect_changed(*this, old_rect, rect);
|
||||
}
|
||||
|
||||
// FIXME: Just use the same types.
|
||||
static WSAPI_MouseButton to_api(MouseButton button)
|
||||
{
|
||||
switch (button) {
|
||||
case MouseButton::None: return WSAPI_MouseButton::NoButton;
|
||||
case MouseButton::Left: return WSAPI_MouseButton::Left;
|
||||
case MouseButton::Right: return WSAPI_MouseButton::Right;
|
||||
case MouseButton::Middle: return WSAPI_MouseButton::Middle;
|
||||
}
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
void WSWindow::on_message(WSMessage& message)
|
||||
{
|
||||
if (m_internal_owner)
|
||||
return m_internal_owner->on_message(message);
|
||||
|
||||
if (is_blocked_by_modal_window())
|
||||
return;
|
||||
|
||||
WSAPI_ServerMessage server_message;
|
||||
server_message.window_id = window_id();
|
||||
|
||||
switch (message.type()) {
|
||||
case WSMessage::MouseMove:
|
||||
server_message.type = WSAPI_ServerMessage::Type::MouseMove;
|
||||
server_message.mouse.position = static_cast<WSMouseEvent&>(message).position();
|
||||
server_message.mouse.button = WSAPI_MouseButton::NoButton;
|
||||
server_message.mouse.buttons = static_cast<WSMouseEvent&>(message).buttons();
|
||||
server_message.mouse.modifiers = static_cast<WSMouseEvent&>(message).modifiers();
|
||||
break;
|
||||
case WSMessage::MouseDown:
|
||||
server_message.type = WSAPI_ServerMessage::Type::MouseDown;
|
||||
server_message.mouse.position = static_cast<WSMouseEvent&>(message).position();
|
||||
server_message.mouse.button = to_api(static_cast<WSMouseEvent&>(message).button());
|
||||
server_message.mouse.buttons = static_cast<WSMouseEvent&>(message).buttons();
|
||||
server_message.mouse.modifiers = static_cast<WSMouseEvent&>(message).modifiers();
|
||||
break;
|
||||
case WSMessage::MouseUp:
|
||||
server_message.type = WSAPI_ServerMessage::Type::MouseUp;
|
||||
server_message.mouse.position = static_cast<WSMouseEvent&>(message).position();
|
||||
server_message.mouse.button = to_api(static_cast<WSMouseEvent&>(message).button());
|
||||
server_message.mouse.buttons = static_cast<WSMouseEvent&>(message).buttons();
|
||||
server_message.mouse.modifiers = static_cast<WSMouseEvent&>(message).modifiers();
|
||||
break;
|
||||
case WSMessage::WindowEntered:
|
||||
server_message.type = WSAPI_ServerMessage::Type::WindowEntered;
|
||||
break;
|
||||
case WSMessage::WindowLeft:
|
||||
server_message.type = WSAPI_ServerMessage::Type::WindowLeft;
|
||||
break;
|
||||
case WSMessage::KeyDown:
|
||||
server_message.type = WSAPI_ServerMessage::Type::KeyDown;
|
||||
server_message.key.character = static_cast<WSKeyEvent&>(message).character();
|
||||
server_message.key.key = static_cast<WSKeyEvent&>(message).key();
|
||||
server_message.key.modifiers = static_cast<WSKeyEvent&>(message).modifiers();
|
||||
break;
|
||||
case WSMessage::KeyUp:
|
||||
server_message.type = WSAPI_ServerMessage::Type::KeyUp;
|
||||
server_message.key.character = static_cast<WSKeyEvent&>(message).character();
|
||||
server_message.key.key = static_cast<WSKeyEvent&>(message).key();
|
||||
server_message.key.modifiers = static_cast<WSKeyEvent&>(message).modifiers();
|
||||
break;
|
||||
case WSMessage::WindowActivated:
|
||||
server_message.type = WSAPI_ServerMessage::Type::WindowActivated;
|
||||
break;
|
||||
case WSMessage::WindowDeactivated:
|
||||
server_message.type = WSAPI_ServerMessage::Type::WindowDeactivated;
|
||||
break;
|
||||
case WSMessage::WindowCloseRequest:
|
||||
server_message.type = WSAPI_ServerMessage::Type::WindowCloseRequest;
|
||||
break;
|
||||
case WSMessage::WindowResized:
|
||||
server_message.type = WSAPI_ServerMessage::Type::WindowResized;
|
||||
server_message.window.old_rect = static_cast<WSResizeEvent&>(message).old_rect();
|
||||
server_message.window.rect = static_cast<WSResizeEvent&>(message).rect();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (server_message.type == WSAPI_ServerMessage::Type::Invalid)
|
||||
return;
|
||||
|
||||
m_client->post_message(server_message);
|
||||
}
|
||||
|
||||
void WSWindow::set_global_cursor_tracking_enabled(bool enabled)
|
||||
{
|
||||
m_global_cursor_tracking_enabled = enabled;
|
||||
}
|
||||
|
||||
void WSWindow::set_visible(bool b)
|
||||
{
|
||||
if (m_visible == b)
|
||||
return;
|
||||
m_visible = b;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
void WSWindow::set_resizable(bool resizable)
|
||||
{
|
||||
if (m_resizable == resizable)
|
||||
return;
|
||||
m_resizable = resizable;
|
||||
}
|
||||
|
||||
void WSWindow::invalidate()
|
||||
{
|
||||
WSWindowManager::the().invalidate(*this);
|
||||
}
|
||||
|
||||
bool WSWindow::is_active() const
|
||||
{
|
||||
return WSWindowManager::the().active_window() == this;
|
||||
}
|
||||
|
||||
bool WSWindow::is_blocked_by_modal_window() const
|
||||
{
|
||||
return !is_modal() && client() && client()->is_showing_modal_window();
|
||||
}
|
126
Servers/WindowServer/WSWindow.h
Normal file
126
Servers/WindowServer/WSWindow.h
Normal file
|
@ -0,0 +1,126 @@
|
|||
#pragma once
|
||||
|
||||
#include <SharedGraphics/Rect.h>
|
||||
#include <SharedGraphics/GraphicsBitmap.h>
|
||||
#include <AK/AKString.h>
|
||||
#include <AK/InlineLinkedList.h>
|
||||
#include "WSMessageReceiver.h"
|
||||
#include <WindowServer/WSWindowType.h>
|
||||
|
||||
class WSClientConnection;
|
||||
class WSMenu;
|
||||
|
||||
class WSWindow final : public WSMessageReceiver, public InlineLinkedListNode<WSWindow> {
|
||||
public:
|
||||
WSWindow(WSClientConnection&, int window_id, bool modal);
|
||||
WSWindow(WSMessageReceiver&, WSWindowType);
|
||||
virtual ~WSWindow() override;
|
||||
|
||||
bool is_blocked_by_modal_window() const;
|
||||
|
||||
WSClientConnection* client() { return m_client; }
|
||||
const WSClientConnection* client() const { return m_client; }
|
||||
|
||||
WSWindowType type() const { return m_type; }
|
||||
int window_id() const { return m_window_id; }
|
||||
|
||||
String title() const { return m_title; }
|
||||
void set_title(String&&);
|
||||
|
||||
float opacity() const { return m_opacity; }
|
||||
void set_opacity(float opacity) { m_opacity = opacity; }
|
||||
|
||||
int x() const { return m_rect.x(); }
|
||||
int y() const { return m_rect.y(); }
|
||||
int width() const { return m_rect.width(); }
|
||||
int height() const { return m_rect.height(); }
|
||||
|
||||
bool is_active() const;
|
||||
|
||||
bool is_visible() const { return m_visible; }
|
||||
void set_visible(bool);
|
||||
|
||||
bool is_modal() const { return m_modal; }
|
||||
|
||||
bool is_resizable() const { return m_resizable; }
|
||||
void set_resizable(bool);
|
||||
|
||||
Rect rect() const { return m_rect; }
|
||||
void set_rect(const Rect&);
|
||||
void set_rect(int x, int y, int width, int height) { set_rect({ x, y, width, height }); }
|
||||
void set_rect_without_repaint(const Rect& rect) { m_rect = rect; }
|
||||
void set_rect_from_window_manager_resize(const Rect&);
|
||||
|
||||
void move_to(const Point& position) { set_rect({ position, size() }); }
|
||||
void move_to(int x, int y) { move_to({ x, y }); }
|
||||
|
||||
Point position() const { return m_rect.location(); }
|
||||
void set_position(const Point& position) { set_rect({ position.x(), position.y(), width(), height() }); }
|
||||
void set_position_without_repaint(const Point& position) { set_rect_without_repaint({ position.x(), position.y(), width(), height() }); }
|
||||
|
||||
Size size() const { return m_rect.size(); }
|
||||
|
||||
void invalidate();
|
||||
|
||||
virtual void on_message(WSMessage&) override;
|
||||
|
||||
GraphicsBitmap* backing_store() { return m_backing_store.ptr(); }
|
||||
void set_backing_store(RetainPtr<GraphicsBitmap>&& backing_store)
|
||||
{
|
||||
m_last_backing_store = move(m_backing_store);
|
||||
m_backing_store = move(backing_store);
|
||||
}
|
||||
void swap_backing_stores()
|
||||
{
|
||||
swap(m_backing_store, m_last_backing_store);
|
||||
}
|
||||
|
||||
GraphicsBitmap* last_backing_store() { return m_last_backing_store.ptr(); }
|
||||
|
||||
void set_global_cursor_tracking_enabled(bool);
|
||||
bool global_cursor_tracking() const { return m_global_cursor_tracking_enabled; }
|
||||
|
||||
bool has_alpha_channel() const { return m_has_alpha_channel; }
|
||||
void set_has_alpha_channel(bool value) { m_has_alpha_channel = value; }
|
||||
|
||||
void set_last_lazy_resize_rect(const Rect& rect) { m_last_lazy_resize_rect = rect; }
|
||||
Rect last_lazy_resize_rect() const { return m_last_lazy_resize_rect; }
|
||||
|
||||
bool has_painted_since_last_resize() const { return m_has_painted_since_last_resize; }
|
||||
void set_has_painted_since_last_resize(bool b) { m_has_painted_since_last_resize = b; }
|
||||
|
||||
Size size_increment() const { return m_size_increment; }
|
||||
void set_size_increment(const Size& increment) { m_size_increment = increment; }
|
||||
|
||||
Size base_size() const { return m_base_size; }
|
||||
void set_base_size(const Size& size) { m_base_size = size; }
|
||||
|
||||
const GraphicsBitmap& icon() const { return *m_icon; }
|
||||
void set_icon(Retained<GraphicsBitmap>&& icon) { m_icon = move(icon); }
|
||||
|
||||
// For InlineLinkedList.
|
||||
// FIXME: Maybe make a ListHashSet and then WSWindowManager can just use that.
|
||||
WSWindow* m_next { nullptr };
|
||||
WSWindow* m_prev { nullptr };
|
||||
|
||||
private:
|
||||
WSClientConnection* m_client { nullptr };
|
||||
WSMessageReceiver* m_internal_owner { nullptr };
|
||||
String m_title;
|
||||
Rect m_rect;
|
||||
WSWindowType m_type { WSWindowType::Normal };
|
||||
bool m_global_cursor_tracking_enabled { false };
|
||||
bool m_visible { true };
|
||||
bool m_has_alpha_channel { false };
|
||||
bool m_has_painted_since_last_resize { false };
|
||||
bool m_modal { false };
|
||||
bool m_resizable { false };
|
||||
RetainPtr<GraphicsBitmap> m_backing_store;
|
||||
RetainPtr<GraphicsBitmap> m_last_backing_store;
|
||||
int m_window_id { -1 };
|
||||
float m_opacity { 1 };
|
||||
Rect m_last_lazy_resize_rect;
|
||||
Size m_size_increment;
|
||||
Size m_base_size;
|
||||
Retained<GraphicsBitmap> m_icon;
|
||||
};
|
1241
Servers/WindowServer/WSWindowManager.cpp
Normal file
1241
Servers/WindowServer/WSWindowManager.cpp
Normal file
File diff suppressed because it is too large
Load diff
240
Servers/WindowServer/WSWindowManager.h
Normal file
240
Servers/WindowServer/WSWindowManager.h
Normal file
|
@ -0,0 +1,240 @@
|
|||
#pragma once
|
||||
|
||||
#include <SharedGraphics/Rect.h>
|
||||
#include <SharedGraphics/Color.h>
|
||||
#include <SharedGraphics/Painter.h>
|
||||
#include <SharedGraphics/DisjointRectSet.h>
|
||||
#include <AK/HashTable.h>
|
||||
#include <AK/InlineLinkedList.h>
|
||||
#include <AK/WeakPtr.h>
|
||||
#include <AK/HashMap.h>
|
||||
#include "WSMessageReceiver.h"
|
||||
#include "WSMenuBar.h"
|
||||
#include <WindowServer/WSWindowSwitcher.h>
|
||||
#include <WindowServer/WSWindowType.h>
|
||||
#include <WindowServer/WSWindow.h>
|
||||
#include <AK/CircularQueue.h>
|
||||
|
||||
class WSAPIClientRequest;
|
||||
class WSScreen;
|
||||
class WSMenuBar;
|
||||
class WSMouseEvent;
|
||||
class WSClientWantsToPaintMessage;
|
||||
class WSWindow;
|
||||
class WSClientConnection;
|
||||
class WSWindowSwitcher;
|
||||
class CharacterBitmap;
|
||||
class GraphicsBitmap;
|
||||
|
||||
enum class ResizeDirection { None, Left, UpLeft, Up, UpRight, Right, DownRight, Down, DownLeft };
|
||||
|
||||
class WSWindowManager : public WSMessageReceiver {
|
||||
friend class WSWindowSwitcher;
|
||||
public:
|
||||
static WSWindowManager& the();
|
||||
|
||||
WSWindowManager();
|
||||
virtual ~WSWindowManager() override;
|
||||
|
||||
void add_window(WSWindow&);
|
||||
void remove_window(WSWindow&);
|
||||
|
||||
void notify_title_changed(WSWindow&);
|
||||
void notify_rect_changed(WSWindow&, const Rect& oldRect, const Rect& newRect);
|
||||
void notify_client_changed_app_menubar(WSClientConnection&);
|
||||
|
||||
WSWindow* active_window() { return m_active_window.ptr(); }
|
||||
const WSClientConnection* active_client() const;
|
||||
|
||||
WSWindow* highlight_window() { return m_highlight_window.ptr(); }
|
||||
void set_highlight_window(WSWindow*);
|
||||
|
||||
void move_to_front(WSWindow&);
|
||||
|
||||
void invalidate_cursor();
|
||||
void draw_cursor();
|
||||
void draw_menubar();
|
||||
void draw_window_switcher();
|
||||
|
||||
Rect menubar_rect() const;
|
||||
WSMenuBar* current_menubar() { return m_current_menubar.ptr(); }
|
||||
void set_current_menubar(WSMenuBar*);
|
||||
WSMenu* current_menu() { return m_current_menu.ptr(); }
|
||||
void set_current_menu(WSMenu*);
|
||||
|
||||
void invalidate(const WSWindow&);
|
||||
void invalidate(const WSWindow&, const Rect&);
|
||||
void invalidate(const Rect&, bool should_schedule_compose_event = true);
|
||||
void invalidate();
|
||||
void recompose_immediately();
|
||||
void flush(const Rect&);
|
||||
|
||||
const Font& font() const;
|
||||
const Font& window_title_font() const;
|
||||
const Font& menu_font() const;
|
||||
const Font& app_menu_font() const;
|
||||
|
||||
void close_menu(WSMenu&);
|
||||
void close_menubar(WSMenuBar&);
|
||||
Color menu_selection_color() const { return m_menu_selection_color; }
|
||||
int menubar_menu_margin() const;
|
||||
|
||||
void set_resolution(int width, int height);
|
||||
|
||||
private:
|
||||
void process_mouse_event(WSMouseEvent&, WSWindow*& event_window);
|
||||
void handle_menu_mouse_event(WSMenu&, WSMouseEvent&);
|
||||
void handle_menubar_mouse_event(WSMouseEvent&);
|
||||
void handle_close_button_mouse_event(WSWindow&, WSMouseEvent&);
|
||||
void start_window_resize(WSWindow&, WSMouseEvent&);
|
||||
void start_window_drag(WSWindow&, WSMouseEvent&);
|
||||
void handle_client_request(WSAPIClientRequest&);
|
||||
void set_active_window(WSWindow*);
|
||||
void set_hovered_window(WSWindow*);
|
||||
template<typename Callback> IterationDecision for_each_visible_window_of_type_from_back_to_front(WSWindowType, Callback);
|
||||
template<typename Callback> IterationDecision for_each_visible_window_of_type_from_front_to_back(WSWindowType, Callback);
|
||||
template<typename Callback> IterationDecision for_each_visible_window_from_front_to_back(Callback);
|
||||
template<typename Callback> IterationDecision for_each_visible_window_from_back_to_front(Callback);
|
||||
template<typename Callback> void for_each_active_menubar_menu(Callback);
|
||||
void close_current_menu();
|
||||
virtual void on_message(WSMessage&) override;
|
||||
void compose();
|
||||
void paint_window_frame(WSWindow&);
|
||||
void flip_buffers();
|
||||
void tick_clock();
|
||||
|
||||
WSScreen& m_screen;
|
||||
Rect m_screen_rect;
|
||||
|
||||
Color m_background_color;
|
||||
Color m_active_window_border_color;
|
||||
Color m_active_window_border_color2;
|
||||
Color m_active_window_title_color;
|
||||
Color m_inactive_window_border_color;
|
||||
Color m_inactive_window_border_color2;
|
||||
Color m_inactive_window_title_color;
|
||||
Color m_dragging_window_border_color;
|
||||
Color m_dragging_window_border_color2;
|
||||
Color m_dragging_window_title_color;
|
||||
Color m_highlight_window_border_color;
|
||||
Color m_highlight_window_border_color2;
|
||||
Color m_highlight_window_title_color;
|
||||
|
||||
HashMap<int, OwnPtr<WSWindow>> m_windows_by_id;
|
||||
HashTable<WSWindow*> m_windows;
|
||||
InlineLinkedList<WSWindow> m_windows_in_order;
|
||||
|
||||
WeakPtr<WSWindow> m_active_window;
|
||||
WeakPtr<WSWindow> m_hovered_window;
|
||||
WeakPtr<WSWindow> m_highlight_window;
|
||||
|
||||
WeakPtr<WSWindow> m_drag_window;
|
||||
Point m_drag_origin;
|
||||
Point m_drag_window_origin;
|
||||
|
||||
WeakPtr<WSWindow> m_resize_window;
|
||||
Rect m_resize_window_original_rect;
|
||||
Point m_resize_origin;
|
||||
ResizeDirection m_resize_direction { ResizeDirection::None };
|
||||
|
||||
Rect m_last_cursor_rect;
|
||||
|
||||
unsigned m_compose_count { 0 };
|
||||
unsigned m_flush_count { 0 };
|
||||
|
||||
RetainPtr<GraphicsBitmap> m_front_bitmap;
|
||||
RetainPtr<GraphicsBitmap> m_back_bitmap;
|
||||
|
||||
DisjointRectSet m_dirty_rects;
|
||||
|
||||
bool m_pending_compose_event { false };
|
||||
|
||||
RetainPtr<CharacterBitmap> m_cursor_bitmap_inner;
|
||||
RetainPtr<CharacterBitmap> m_cursor_bitmap_outer;
|
||||
|
||||
OwnPtr<Painter> m_back_painter;
|
||||
OwnPtr<Painter> m_front_painter;
|
||||
|
||||
String m_wallpaper_path;
|
||||
RetainPtr<GraphicsBitmap> m_wallpaper;
|
||||
|
||||
bool m_flash_flush { false };
|
||||
bool m_buffers_are_flipped { false };
|
||||
|
||||
byte m_keyboard_modifiers { 0 };
|
||||
|
||||
OwnPtr<WSMenu> m_system_menu;
|
||||
Color m_menu_selection_color;
|
||||
WeakPtr<WSMenuBar> m_current_menubar;
|
||||
WeakPtr<WSMenu> m_current_menu;
|
||||
|
||||
WSWindowSwitcher m_switcher;
|
||||
|
||||
CircularQueue<float, 30> m_cpu_history;
|
||||
|
||||
String m_username;
|
||||
};
|
||||
|
||||
template<typename Callback>
|
||||
IterationDecision WSWindowManager::for_each_visible_window_of_type_from_back_to_front(WSWindowType type, Callback callback)
|
||||
{
|
||||
bool do_highlight_window_at_end = false;
|
||||
for (auto* window = m_windows_in_order.head(); window; window = window->next()) {
|
||||
if (!window->is_visible())
|
||||
continue;
|
||||
if (window->type() != type)
|
||||
continue;
|
||||
if (m_highlight_window.ptr() == window) {
|
||||
do_highlight_window_at_end = true;
|
||||
continue;
|
||||
}
|
||||
if (callback(*window) == IterationDecision::Abort)
|
||||
return IterationDecision::Abort;
|
||||
}
|
||||
if (do_highlight_window_at_end) {
|
||||
if (callback(*m_highlight_window) == IterationDecision::Abort)
|
||||
return IterationDecision::Abort;
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
}
|
||||
|
||||
template<typename Callback>
|
||||
IterationDecision WSWindowManager::for_each_visible_window_from_back_to_front(Callback callback)
|
||||
{
|
||||
if (for_each_visible_window_of_type_from_back_to_front(WSWindowType::Normal, callback) == IterationDecision::Abort)
|
||||
return IterationDecision::Abort;
|
||||
if (for_each_visible_window_of_type_from_back_to_front(WSWindowType::Menu, callback) == IterationDecision::Abort)
|
||||
return IterationDecision::Abort;
|
||||
return for_each_visible_window_of_type_from_back_to_front(WSWindowType::WindowSwitcher, callback);
|
||||
}
|
||||
|
||||
template<typename Callback>
|
||||
IterationDecision WSWindowManager::for_each_visible_window_of_type_from_front_to_back(WSWindowType type, Callback callback)
|
||||
{
|
||||
if (m_highlight_window && m_highlight_window->type() == type && m_highlight_window->is_visible()) {
|
||||
if (callback(*m_highlight_window) == IterationDecision::Abort)
|
||||
return IterationDecision::Abort;
|
||||
}
|
||||
|
||||
for (auto* window = m_windows_in_order.tail(); window; window = window->prev()) {
|
||||
if (!window->is_visible())
|
||||
continue;
|
||||
if (window->type() != type)
|
||||
continue;
|
||||
if (window == m_highlight_window.ptr())
|
||||
continue;
|
||||
if (callback(*window) == IterationDecision::Abort)
|
||||
return IterationDecision::Abort;
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
}
|
||||
|
||||
template<typename Callback>
|
||||
IterationDecision WSWindowManager::for_each_visible_window_from_front_to_back(Callback callback)
|
||||
{
|
||||
if (for_each_visible_window_of_type_from_front_to_back(WSWindowType::Menu, callback) == IterationDecision::Abort)
|
||||
return IterationDecision::Abort;
|
||||
if (for_each_visible_window_of_type_from_front_to_back(WSWindowType::Normal, callback) == IterationDecision::Abort)
|
||||
return IterationDecision::Abort;
|
||||
return for_each_visible_window_of_type_from_front_to_back(WSWindowType::WindowSwitcher, callback);
|
||||
}
|
123
Servers/WindowServer/WSWindowSwitcher.cpp
Normal file
123
Servers/WindowServer/WSWindowSwitcher.cpp
Normal file
|
@ -0,0 +1,123 @@
|
|||
#include <WindowServer/WSWindowSwitcher.h>
|
||||
#include <WindowServer/WSWindowManager.h>
|
||||
#include <WindowServer/WSMessage.h>
|
||||
#include <SharedGraphics/Font.h>
|
||||
|
||||
WSWindowSwitcher::WSWindowSwitcher()
|
||||
{
|
||||
}
|
||||
|
||||
WSWindowSwitcher::~WSWindowSwitcher()
|
||||
{
|
||||
}
|
||||
|
||||
void WSWindowSwitcher::set_visible(bool visible)
|
||||
{
|
||||
if (m_visible == visible)
|
||||
return;
|
||||
m_visible = visible;
|
||||
if (m_switcher_window)
|
||||
m_switcher_window->set_visible(visible);
|
||||
if (!m_visible)
|
||||
return;
|
||||
refresh();
|
||||
}
|
||||
|
||||
WSWindow* WSWindowSwitcher::selected_window()
|
||||
{
|
||||
if (m_selected_index < 0 || m_selected_index >= m_windows.size())
|
||||
return nullptr;
|
||||
return m_windows[m_selected_index].ptr();
|
||||
}
|
||||
|
||||
void WSWindowSwitcher::on_key_event(const WSKeyEvent& event)
|
||||
{
|
||||
if (event.type() == WSMessage::KeyUp) {
|
||||
if (event.key() == Key_Logo) {
|
||||
if (auto* window = selected_window()) {
|
||||
WSWindowManager::the().set_active_window(window);
|
||||
WSWindowManager::the().move_to_front(*window);
|
||||
}
|
||||
WSWindowManager::the().set_highlight_window(nullptr);
|
||||
hide();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (event.key() != Key_Tab) {
|
||||
WSWindowManager::the().set_highlight_window(nullptr);
|
||||
hide();
|
||||
return;
|
||||
}
|
||||
ASSERT(!m_windows.is_empty());
|
||||
m_selected_index = (m_selected_index + 1) % m_windows.size();
|
||||
ASSERT(m_selected_index < m_windows.size());
|
||||
auto* highlight_window = m_windows.at(m_selected_index).ptr();
|
||||
ASSERT(highlight_window);
|
||||
WSWindowManager::the().set_highlight_window(highlight_window);
|
||||
draw();
|
||||
WSWindowManager::the().invalidate(m_rect);
|
||||
}
|
||||
|
||||
void WSWindowSwitcher::draw()
|
||||
{
|
||||
Painter painter(*m_switcher_window->backing_store());
|
||||
painter.fill_rect({ { }, m_rect.size() }, Color::LightGray);
|
||||
painter.draw_rect({ { }, m_rect.size() }, Color::DarkGray);
|
||||
for (int index = 0; index < m_windows.size(); ++index) {
|
||||
auto& window = *m_windows.at(index);
|
||||
Rect item_rect {
|
||||
padding(),
|
||||
padding() + index * item_height(),
|
||||
m_rect.width() - padding() * 2,
|
||||
item_height()
|
||||
};
|
||||
Color text_color;
|
||||
Color rect_text_color;
|
||||
if (index == m_selected_index) {
|
||||
painter.fill_rect(item_rect, Color::from_rgb(0x84351a));
|
||||
text_color = Color::White;
|
||||
rect_text_color = Color::LightGray;
|
||||
} else {
|
||||
text_color = Color::Black;
|
||||
rect_text_color = Color::DarkGray;
|
||||
}
|
||||
painter.blit(item_rect.location().translated(0, (item_rect.height() - window.icon().height()) / 2), window.icon(), window.icon().rect());
|
||||
painter.draw_text(item_rect.translated(window.icon().width() + 4, 0), window.title(), WSWindowManager::the().window_title_font(), TextAlignment::CenterLeft, text_color);
|
||||
painter.draw_text(item_rect, window.rect().to_string(), TextAlignment::CenterRight, rect_text_color);
|
||||
}
|
||||
}
|
||||
|
||||
void WSWindowSwitcher::refresh()
|
||||
{
|
||||
WSWindow* selected_window = nullptr;
|
||||
if (m_selected_index > 0 && m_windows[m_selected_index])
|
||||
selected_window = m_windows[m_selected_index].ptr();
|
||||
m_windows.clear();
|
||||
m_selected_index = 0;
|
||||
int window_count = 0;
|
||||
int longest_title_width = 0;
|
||||
WSWindowManager::the().for_each_visible_window_of_type_from_back_to_front(WSWindowType::Normal, [&] (WSWindow& window) {
|
||||
++window_count;
|
||||
longest_title_width = max(longest_title_width, WSWindowManager::the().font().width(window.title()));
|
||||
if (selected_window == &window)
|
||||
m_selected_index = m_windows.size();
|
||||
m_windows.append(window.make_weak_ptr());
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
if (m_windows.is_empty()) {
|
||||
hide();
|
||||
return;
|
||||
}
|
||||
int space_for_window_rect = 180;
|
||||
m_rect.set_width(longest_title_width + space_for_window_rect + padding() * 2);
|
||||
m_rect.set_height(window_count * item_height() + padding() * 2);
|
||||
m_rect.center_within(WSWindowManager::the().m_screen_rect);
|
||||
if (!m_switcher_window)
|
||||
m_switcher_window = make<WSWindow>(*this, WSWindowType::WindowSwitcher);
|
||||
m_switcher_window->set_rect(m_rect);
|
||||
draw();
|
||||
}
|
||||
|
||||
void WSWindowSwitcher::on_message(WSMessage&)
|
||||
{
|
||||
}
|
43
Servers/WindowServer/WSWindowSwitcher.h
Normal file
43
Servers/WindowServer/WSWindowSwitcher.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
#pragma once
|
||||
|
||||
#include <SharedGraphics/Rect.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <AK/WeakPtr.h>
|
||||
#include <WindowServer/WSMessageReceiver.h>
|
||||
|
||||
class Painter;
|
||||
class WSKeyEvent;
|
||||
class WSWindow;
|
||||
|
||||
class WSWindowSwitcher : public WSMessageReceiver {
|
||||
public:
|
||||
WSWindowSwitcher();
|
||||
virtual ~WSWindowSwitcher() override;
|
||||
|
||||
bool is_visible() const { return m_visible; }
|
||||
void set_visible(bool);
|
||||
|
||||
void show() { set_visible(true); }
|
||||
void hide() { set_visible(false); }
|
||||
|
||||
void on_key_event(const WSKeyEvent&);
|
||||
void refresh();
|
||||
|
||||
void draw();
|
||||
|
||||
int item_height() { return 20; }
|
||||
int padding() { return 8; }
|
||||
|
||||
WSWindow* selected_window();
|
||||
|
||||
WSWindow* switcher_window() { return m_switcher_window.ptr(); }
|
||||
|
||||
private:
|
||||
virtual void on_message(WSMessage&) override;
|
||||
|
||||
OwnPtr<WSWindow> m_switcher_window;
|
||||
Rect m_rect;
|
||||
bool m_visible { false };
|
||||
Vector<WeakPtr<WSWindow>> m_windows;
|
||||
int m_selected_index { 0 };
|
||||
};
|
8
Servers/WindowServer/WSWindowType.h
Normal file
8
Servers/WindowServer/WSWindowType.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
enum class WSWindowType {
|
||||
Invalid = 0,
|
||||
Normal,
|
||||
Menu,
|
||||
WindowSwitcher,
|
||||
};
|
26
Servers/WindowServer/main.cpp
Normal file
26
Servers/WindowServer/main.cpp
Normal file
|
@ -0,0 +1,26 @@
|
|||
#include <WindowServer/WSScreen.h>
|
||||
#include <WindowServer/WSWindowManager.h>
|
||||
#include <WindowServer/WSMessageLoop.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
struct sigaction act;
|
||||
memset(&act, 0, sizeof(act));
|
||||
act.sa_flags = SA_NOCLDWAIT;
|
||||
act.sa_handler = SIG_IGN;
|
||||
int rc = sigaction(SIGCHLD, &act, nullptr);
|
||||
if (rc < 0) {
|
||||
perror("sigaction");
|
||||
return 1;
|
||||
}
|
||||
|
||||
WSMessageLoop loop;
|
||||
WSScreen screen(1024, 768);
|
||||
WSWindowManager window_manager;
|
||||
|
||||
dbgprintf("Entering WindowServer main loop.\n");
|
||||
WSMessageLoop::the().exec();
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue