1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-16 02:54:59 +00:00

WindowServer: Port to the new IPC system

This patch introduces code generation for the WindowServer IPC with
its clients. The client/server endpoints are defined by the two .ipc
files in Servers/WindowServer/: WindowServer.ipc and WindowClient.ipc

It now becomes significantly easier to add features and capabilities
to WindowServer since you don't have to know nearly as much about all
the intricate paths that IPC messages take between LibGUI and WSWindow.

The new system also uses significantly less IPC bandwidth since we're
now doing packed serialization instead of passing fixed-sized structs
of ~600 bytes for each message.

Some repaint coalescing optimizations are lost in this conversion and
we'll need to look at how to implement those in the new world.

The old CoreIPC::Client::Connection and CoreIPC::Server::Connection
classes are removed by this patch and replaced by use of ConnectionNG,
which will be renamed eventually.

Goodbye, old WindowServer IPC. You served us well :^)
This commit is contained in:
Andreas Kling 2019-12-02 09:33:37 +01:00
parent 30db7813de
commit 272d65e3e2
42 changed files with 843 additions and 2853 deletions

View file

@ -1,15 +1,3 @@
#include <LibC/errno.h>
#include <LibC/fcntl.h>
#include <LibC/stdio.h>
#include <LibC/stdlib.h>
#include <LibC/string.h>
#include <LibC/sys/select.h>
#include <LibC/sys/socket.h>
#include <LibC/sys/time.h>
#include <LibC/time.h>
#include <LibC/unistd.h>
#include <LibCore/CNotifier.h>
#include <LibCore/CObject.h>
#include <LibGUI/GAction.h>
#include <LibGUI/GApplication.h>
#include <LibGUI/GClipboard.h>
@ -19,301 +7,258 @@
#include <LibGUI/GWidget.h>
#include <LibGUI/GWindow.h>
#include <LibGUI/GWindowServerConnection.h>
#include <sys/uio.h>
//#define GEVENTLOOP_DEBUG
//#define COALESCING_DEBUG
GWindowServerConnection& GWindowServerConnection::the()
{
static GWindowServerConnection* s_connection = nullptr;
if (!s_connection) {
s_connection = new GWindowServerConnection();
s_connection->handshake();
}
if (!s_connection)
s_connection = new GWindowServerConnection;
return *s_connection;
}
void GWindowServerConnection::handshake()
{
WSAPI_ClientMessage request;
request.type = WSAPI_ClientMessage::Type::Greeting;
request.greeting.client_pid = getpid();
auto response = sync_request(request, WSAPI_ServerMessage::Type::Greeting);
handle_greeting(response);
auto response = send_sync<WindowServer::Greet>(getpid());
set_server_pid(response->server_pid());
set_my_client_id(response->client_id());
GDesktop::the().did_receive_screen_rect({}, response->screen_rect());
}
void GWindowServerConnection::handle_paint_event(const WSAPI_ServerMessage& event, GWindow& window, const ByteBuffer& extra_data)
void GWindowServerConnection::handle(const WindowClient::Paint& message)
{
#ifdef GEVENTLOOP_DEBUG
dbgprintf("WID=%x Paint\n", event.window_id);
dbgprintf("WID=%d Paint\n", message.window_id());
#endif
Vector<Rect, 32> rects;
for (int i = 0; i < min(WSAPI_ServerMessage::max_inline_rect_count, event.rect_count); ++i)
rects.append(event.rects[i]);
if (event.extra_size) {
auto* extra_rects = reinterpret_cast<const WSAPI_Rect*>(extra_data.data());
for (int i = 0; i < event.rect_count - WSAPI_ServerMessage::max_inline_rect_count; ++i)
rects.append(extra_rects[i]);
}
CEventLoop::current().post_event(window, make<GMultiPaintEvent>(rects, event.paint.window_size));
}
void GWindowServerConnection::handle_resize_event(const WSAPI_ServerMessage& event, GWindow& window)
{
CEventLoop::current().post_event(window, make<GResizeEvent>(event.window.old_rect.size, event.window.rect.size));
}
void GWindowServerConnection::handle_window_activation_event(const WSAPI_ServerMessage& event, GWindow& window)
{
#ifdef GEVENTLOOP_DEBUG
dbgprintf("WID=%x WindowActivation\n", event.window_id);
#endif
CEventLoop::current().post_event(window, make<GEvent>(event.type == WSAPI_ServerMessage::Type::WindowActivated ? GEvent::WindowBecameActive : GEvent::WindowBecameInactive));
}
void GWindowServerConnection::handle_window_close_request_event(const WSAPI_ServerMessage&, GWindow& window)
{
CEventLoop::current().post_event(window, make<GEvent>(GEvent::WindowCloseRequest));
}
void GWindowServerConnection::handle_window_entered_or_left_event(const WSAPI_ServerMessage& message, GWindow& window)
{
CEventLoop::current().post_event(window, make<GEvent>(message.type == WSAPI_ServerMessage::Type::WindowEntered ? GEvent::WindowEntered : GEvent::WindowLeft));
}
void GWindowServerConnection::handle_key_event(const WSAPI_ServerMessage& event, GWindow& window)
{
#ifdef GEVENTLOOP_DEBUG
dbgprintf("WID=%x KeyEvent character=0x%b\n", event.window_id, event.key.character);
#endif
auto key_event = make<GKeyEvent>(event.type == WSAPI_ServerMessage::Type::KeyDown ? GEvent::KeyDown : GEvent::KeyUp, event.key.key, event.key.modifiers);
if (event.key.character != '\0')
key_event->m_text = String(&event.key.character, 1);
if (event.type == WSAPI_ServerMessage::Type::KeyDown) {
if (auto* focused_widget = window.focused_widget()) {
if (auto* action = focused_widget->action_for_key_event(*key_event)) {
if (action->is_enabled()) {
action->activate();
return;
}
}
if (auto* window = GWindow::from_window_id(message.window_id())) {
Vector<Rect, 32> rects;
for (auto& r : message.rects()) {
rects.append(r);
}
CEventLoop::current().post_event(*window, make<GMultiPaintEvent>(rects, message.window_size()));
}
}
if (auto* action = GApplication::the().action_for_key_event(*key_event)) {
void GWindowServerConnection::handle(const WindowClient::WindowResized& message)
{
if (auto* window = GWindow::from_window_id(message.window_id())) {
CEventLoop::current().post_event(*window, make<GResizeEvent>(message.old_rect().size(), message.new_rect().size()));
}
}
void GWindowServerConnection::handle(const WindowClient::WindowActivated& message)
{
#ifdef GEVENTLOOP_DEBUG
dbgprintf("(%d) WID=%d WindowActivated\n", getpid(), message.window_id());
#endif
if (auto* window = GWindow::from_window_id(message.window_id()))
CEventLoop::current().post_event(*window, make<GEvent>(GEvent::WindowBecameActive));
}
void GWindowServerConnection::handle(const WindowClient::WindowDeactivated& message)
{
#ifdef GEVENTLOOP_DEBUG
dbgprintf("(%d) WID=%d WindowDeactivated\n", getpid(), message.window_id());
#endif
if (auto* window = GWindow::from_window_id(message.window_id()))
CEventLoop::current().post_event(*window, make<GEvent>(GEvent::WindowBecameInactive));
}
void GWindowServerConnection::handle(const WindowClient::WindowCloseRequest& message)
{
if (auto* window = GWindow::from_window_id(message.window_id()))
CEventLoop::current().post_event(*window, make<GEvent>(GEvent::WindowCloseRequest));
}
void GWindowServerConnection::handle(const WindowClient::WindowEntered& message)
{
if (auto* window = GWindow::from_window_id(message.window_id()))
CEventLoop::current().post_event(*window, make<GEvent>(GEvent::WindowEntered));
}
void GWindowServerConnection::handle(const WindowClient::WindowLeft& message)
{
if (auto* window = GWindow::from_window_id(message.window_id()))
CEventLoop::current().post_event(*window, make<GEvent>(GEvent::WindowLeft));
}
void GWindowServerConnection::handle(const WindowClient::KeyDown& message)
{
#ifdef GEVENTLOOP_DEBUG
dbgprintf("WID=%d KeyDown character=0x%02x\n", message.window_id(), message.character());
#endif
auto* window = GWindow::from_window_id(message.window_id());
if (!window)
return;
auto key_event = make<GKeyEvent>(GEvent::KeyDown, message.key(), message.modifiers());
if (message.character() != '\0') {
char ch = message.character();
key_event->m_text = String(&ch, 1);
}
if (auto* focused_widget = window->focused_widget()) {
if (auto* action = focused_widget->action_for_key_event(*key_event)) {
if (action->is_enabled()) {
action->activate();
return;
}
}
}
CEventLoop::current().post_event(window, move(key_event));
}
void GWindowServerConnection::handle_mouse_event(const WSAPI_ServerMessage& event, GWindow& window)
{
#ifdef GEVENTLOOP_DEBUG
dbgprintf("WID=%x MouseEvent %d,%d,%d\n", event.window_id, event.mouse.position.x, event.mouse.position.y, event.mouse.wheel_delta);
#endif
GMouseEvent::Type type;
switch (event.type) {
case WSAPI_ServerMessage::Type::MouseMove:
type = GEvent::MouseMove;
break;
case WSAPI_ServerMessage::Type::MouseUp:
type = GEvent::MouseUp;
break;
case WSAPI_ServerMessage::Type::MouseDown:
type = GEvent::MouseDown;
break;
case WSAPI_ServerMessage::Type::MouseDoubleClick:
type = GEvent::MouseDoubleClick;
break;
case WSAPI_ServerMessage::Type::MouseWheel:
type = GEvent::MouseWheel;
break;
default:
ASSERT_NOT_REACHED();
break;
}
GMouseButton button { GMouseButton::None };
switch (event.mouse.button) {
case WSAPI_MouseButton::NoButton:
button = GMouseButton::None;
break;
case WSAPI_MouseButton::Left:
button = GMouseButton::Left;
break;
case WSAPI_MouseButton::Right:
button = GMouseButton::Right;
break;
case WSAPI_MouseButton::Middle:
button = GMouseButton::Middle;
break;
default:
ASSERT_NOT_REACHED();
break;
}
CEventLoop::current().post_event(window, make<GMouseEvent>(type, event.mouse.position, event.mouse.buttons, button, event.mouse.modifiers, event.mouse.wheel_delta));
}
void GWindowServerConnection::handle_menu_event(const WSAPI_ServerMessage& event)
{
if (event.type == WSAPI_ServerMessage::Type::MenuItemActivated) {
auto* menu = GMenu::from_menu_id(event.menu.menu_id);
if (!menu) {
dbgprintf("GEventLoop received event for invalid window ID %d\n", event.window_id);
if (auto* action = GApplication::the().action_for_key_event(*key_event)) {
if (action->is_enabled()) {
action->activate();
return;
}
if (auto* action = menu->action_at(event.menu.identifier))
action->activate();
return;
}
ASSERT_NOT_REACHED();
CEventLoop::current().post_event(*window, move(key_event));
}
void GWindowServerConnection::handle_wm_event(const WSAPI_ServerMessage& event, GWindow& window)
void GWindowServerConnection::handle(const WindowClient::KeyUp& message)
{
#ifdef GEVENTLOOP_DEBUG
dbgprintf("WID=%d KeyUp character=0x%02x\n", message.window_id(), message.character());
#endif
auto* window = GWindow::from_window_id(message.window_id());
if (!window)
return;
auto key_event = make<GKeyEvent>(GEvent::KeyUp, message.key(), message.modifiers());
if (message.character() != '\0') {
char ch = message.character();
key_event->m_text = String(&ch, 1);
}
CEventLoop::current().post_event(*window, move(key_event));
}
GMouseButton to_gmousebutton(u32 button)
{
switch (button) {
case 0:
return GMouseButton::None;
case 1:
return GMouseButton::Left;
case 2:
return GMouseButton::Right;
case 4:
return GMouseButton::Middle;
default:
ASSERT_NOT_REACHED();
break;
}
}
void GWindowServerConnection::handle(const WindowClient::MouseDown& message)
{
#ifdef GEVENTLOOP_DEBUG
dbgprintf("WID=%d MouseDown %d,%d,%d\n", message.window_id(), message.mouse_position().x(), message.mouse_position().y(), message.wheel_delta();
#endif
if (auto* window = GWindow::from_window_id(message.window_id()))
CEventLoop::current().post_event(*window, make<GMouseEvent>(GEvent::MouseDown, message.mouse_position(), message.buttons(), to_gmousebutton(message.button()), message.modifiers(), message.wheel_delta()));
}
void GWindowServerConnection::handle(const WindowClient::MouseUp& message)
{
#ifdef GEVENTLOOP_DEBUG
dbgprintf("WID=%d MouseUp %d,%d,%d\n", message.window_id(), message.mouse_position().x(), message.mouse_position().y(), message.wheel_delta();
#endif
if (auto* window = GWindow::from_window_id(message.window_id()))
CEventLoop::current().post_event(*window, make<GMouseEvent>(GEvent::MouseUp, message.mouse_position(), message.buttons(), to_gmousebutton(message.button()), message.modifiers(), message.wheel_delta()));
}
void GWindowServerConnection::handle(const WindowClient::MouseMove& message)
{
#ifdef GEVENTLOOP_DEBUG
dbgprintf("WID=%d MouseMove %d,%d,%d\n", message.window_id(), message.mouse_position().x(), message.mouse_position().y(), message.wheel_delta();
#endif
if (auto* window = GWindow::from_window_id(message.window_id()))
CEventLoop::current().post_event(*window, make<GMouseEvent>(GEvent::MouseMove, message.mouse_position(), message.buttons(), to_gmousebutton(message.button()), message.modifiers(), message.wheel_delta()));
}
void GWindowServerConnection::handle(const WindowClient::MouseDoubleClick& message)
{
#ifdef GEVENTLOOP_DEBUG
dbgprintf("WID=%d MouseDoubleClick %d,%d,%d\n", message.window_id(), message.mouse_position().x(), message.mouse_position().y(), message.wheel_delta();
#endif
if (auto* window = GWindow::from_window_id(message.window_id()))
CEventLoop::current().post_event(*window, make<GMouseEvent>(GEvent::MouseDoubleClick, message.mouse_position(), message.buttons(), to_gmousebutton(message.button()), message.modifiers(), message.wheel_delta()));
}
void GWindowServerConnection::handle(const WindowClient::MouseWheel& message)
{
#ifdef GEVENTLOOP_DEBUG
dbgprintf("WID=%d MouseWheel %d,%d,%d\n", message.window_id(), message.mouse_position().x(), message.mouse_position().y(), message.wheel_delta();
#endif
if (auto* window = GWindow::from_window_id(message.window_id()))
CEventLoop::current().post_event(*window, make<GMouseEvent>(GEvent::MouseWheel, message.mouse_position(), message.buttons(), to_gmousebutton(message.button()), message.modifiers(), message.wheel_delta()));
}
void GWindowServerConnection::handle(const WindowClient::MenuItemActivated& message)
{
auto* menu = GMenu::from_menu_id(message.menu_id());
if (!menu) {
dbgprintf("GEventLoop received event for invalid menu ID %d\n", message.menu_id());
return;
}
if (auto* action = menu->action_at(message.identifier()))
action->activate();
}
void GWindowServerConnection::handle(const WindowClient::WM_WindowStateChanged& message)
{
#ifdef GEVENTLOOP_DEBUG
dbgprintf("GEventLoop: handle_wm_event: %d\n", (int)event.type);
#endif
if (event.type == WSAPI_ServerMessage::WM_WindowStateChanged)
CEventLoop::current().post_event(window, make<GWMWindowStateChangedEvent>(event.wm.client_id, event.wm.window_id, String(event.text, event.text_length), event.wm.rect, event.wm.is_active, (GWindowType)event.wm.window_type, event.wm.is_minimized));
else if (event.type == WSAPI_ServerMessage::WM_WindowRectChanged)
CEventLoop::current().post_event(window, make<GWMWindowRectChangedEvent>(event.wm.client_id, event.wm.window_id, event.wm.rect));
else if (event.type == WSAPI_ServerMessage::WM_WindowIconBitmapChanged)
CEventLoop::current().post_event(window, make<GWMWindowIconBitmapChangedEvent>(event.wm.client_id, event.wm.window_id, event.wm.icon_buffer_id, event.wm.icon_size));
else if (event.type == WSAPI_ServerMessage::WM_WindowRemoved)
CEventLoop::current().post_event(window, make<GWMWindowRemovedEvent>(event.wm.client_id, event.wm.window_id));
else
ASSERT_NOT_REACHED();
if (auto* window = GWindow::from_window_id(message.window_id()))
CEventLoop::current().post_event(*window, make<GWMWindowStateChangedEvent>(message.client_id(), message.window_id(), message.title(), message.rect(), message.is_active(), (GWindowType)message.window_type(), message.is_minimized()));
}
void GWindowServerConnection::postprocess_bundles(Vector<IncomingMessageBundle>& bundles)
void GWindowServerConnection::handle(const WindowClient::WM_WindowRectChanged& message)
{
int coalesced_paints = 0;
int coalesced_resizes = 0;
auto unprocessed_bundles = move(bundles);
HashMap<int, Size> latest_size_for_window_id;
for (auto& bundle : unprocessed_bundles) {
auto& event = bundle.message;
if (event.type == WSAPI_ServerMessage::Type::WindowResized) {
latest_size_for_window_id.set(event.window_id, event.window.rect.size);
}
}
int paint_count = 0;
HashMap<int, Size> latest_paint_size_for_window_id;
for (auto& bundle : unprocessed_bundles) {
auto& event = bundle.message;
if (event.type == WSAPI_ServerMessage::Type::Paint) {
++paint_count;
#ifdef COALESCING_DEBUG
dbgprintf(" (window: %s)\n", Size(event.paint.window_size).to_string().characters());
#endif
latest_paint_size_for_window_id.set(event.window_id, event.paint.window_size);
}
}
#ifdef COALESCING_DEBUG
dbgprintf("paint_count: %d\n", paint_count);
#endif
for (auto& bundle : unprocessed_bundles) {
auto& event = bundle.message;
if (event.type == WSAPI_ServerMessage::Type::Greeting) {
// Shouldn't get a second greeting
dbg() << "Got second Greeting!?";
ASSERT_NOT_REACHED();
continue;
}
if (event.type == WSAPI_ServerMessage::Type::ScreenRectChanged) {
GDesktop::the().did_receive_screen_rect({}, event.screen.rect);
continue;
}
if (event.type == WSAPI_ServerMessage::Type::ClipboardContentsChanged) {
GClipboard::the().did_receive_clipboard_contents_changed({}, String(event.text, event.text_length));
continue;
}
if (event.type == WSAPI_ServerMessage::Error) {
dbgprintf("GEventLoop got error message from server\n");
dbgprintf(" - error message: %s\n", String(event.text, event.text_length).characters());
CEventLoop::current().quit(1);
return;
}
switch (event.type) {
case WSAPI_ServerMessage::MenuItemActivated:
handle_menu_event(event);
continue;
default:
break;
}
auto* window = GWindow::from_window_id(event.window_id);
if (!window) {
dbgprintf("GEventLoop received event for invalid window ID %d\n", event.window_id);
continue;
}
switch (event.type) {
case WSAPI_ServerMessage::Type::Paint:
if (Size(event.paint.window_size) != latest_paint_size_for_window_id.get(event.window_id).value_or({})) {
++coalesced_paints;
break;
}
handle_paint_event(event, *window, bundle.extra_data);
break;
case WSAPI_ServerMessage::Type::MouseDown:
case WSAPI_ServerMessage::Type::MouseDoubleClick:
case WSAPI_ServerMessage::Type::MouseUp:
case WSAPI_ServerMessage::Type::MouseMove:
case WSAPI_ServerMessage::Type::MouseWheel:
handle_mouse_event(event, *window);
break;
case WSAPI_ServerMessage::Type::WindowActivated:
case WSAPI_ServerMessage::Type::WindowDeactivated:
handle_window_activation_event(event, *window);
break;
case WSAPI_ServerMessage::Type::WindowCloseRequest:
handle_window_close_request_event(event, *window);
break;
case WSAPI_ServerMessage::Type::KeyDown:
case WSAPI_ServerMessage::Type::KeyUp:
handle_key_event(event, *window);
break;
case WSAPI_ServerMessage::Type::WindowEntered:
case WSAPI_ServerMessage::Type::WindowLeft:
handle_window_entered_or_left_event(event, *window);
break;
case WSAPI_ServerMessage::Type::WindowResized:
if (Size(event.window.rect.size) != latest_size_for_window_id.get(event.window_id).value_or({})) {
++coalesced_resizes;
break;
}
handle_resize_event(event, *window);
break;
default:
if (event.type > WSAPI_ServerMessage::__Begin_WM_Events__ && event.type < WSAPI_ServerMessage::Type::__End_WM_Events__)
handle_wm_event(event, *window);
break;
}
}
#ifdef COALESCING_DEBUG
if (coalesced_paints)
dbgprintf("Coalesced %d paints\n", coalesced_paints);
if (coalesced_resizes)
dbgprintf("Coalesced %d resizes\n", coalesced_resizes);
#ifdef GEVENTLOOP_DEBUG
dbgprintf("GEventLoop: handle_wm_event: %d\n", (int)event.type);
#endif
if (auto* window = GWindow::from_window_id(message.window_id()))
CEventLoop::current().post_event(*window, make<GWMWindowRectChangedEvent>(message.client_id(), message.window_id(), message.rect()));
}
void GWindowServerConnection::handle_greeting(WSAPI_ServerMessage& message)
void GWindowServerConnection::handle(const WindowClient::WM_WindowIconBitmapChanged& message)
{
set_server_pid(message.greeting.server_pid);
set_my_client_id(message.greeting.your_client_id);
GDesktop::the().did_receive_screen_rect({}, message.greeting.screen_rect);
#ifdef GEVENTLOOP_DEBUG
dbgprintf("GEventLoop: handle_wm_event: %d\n", (int)event.type);
#endif
if (auto* window = GWindow::from_window_id(message.window_id()))
CEventLoop::current().post_event(*window, make<GWMWindowIconBitmapChangedEvent>(message.client_id(), message.window_id(), message.icon_buffer_id(), message.icon_size()));
}
void GWindowServerConnection::handle(const WindowClient::WM_WindowRemoved& message)
{
#ifdef GEVENTLOOP_DEBUG
dbgprintf("GEventLoop: handle_wm_event: %d\n", (int)event.type);
#endif
if (auto* window = GWindow::from_window_id(message.window_id()))
CEventLoop::current().post_event(*window, make<GWMWindowRemovedEvent>(message.client_id(), message.window_id()));
}
void GWindowServerConnection::handle(const WindowClient::ScreenRectChanged& message)
{
GDesktop::the().did_receive_screen_rect({}, message.rect());
}
void GWindowServerConnection::handle(const WindowClient::ClipboardContentsChanged& message)
{
GClipboard::the().did_receive_clipboard_contents_changed({}, message.content_type());
}
void GWindowServerConnection::handle(const WindowClient::AsyncSetWallpaperFinished&)
{
// This is handled manually by GDesktop::set_wallpaper().
}