mirror of
https://github.com/SerenityOS/serenity.git
synced 2025-01-25 19:02:07 -05:00
d4892b3fdc
Now that we can set icons directly "by bitmap", there's no need for passing around the icon paths anymore, so get rid of all the IPC and API related to that. :^)
324 lines
12 KiB
C++
324 lines
12 KiB
C++
#include "GEventLoop.h"
|
|
#include "GEvent.h"
|
|
#include "GWindow.h"
|
|
#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/GDesktop.h>
|
|
#include <LibGUI/GMenu.h>
|
|
#include <LibGUI/GWidget.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();
|
|
}
|
|
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);
|
|
}
|
|
|
|
GEventLoop::GEventLoop()
|
|
{
|
|
// ensure the WS connection is up, as our users might be expecting it to be
|
|
// valid very early (via e.g. GDesktop) :)
|
|
GWindowServerConnection::the();
|
|
}
|
|
|
|
GEventLoop::~GEventLoop()
|
|
{
|
|
}
|
|
|
|
void GWindowServerConnection::handle_paint_event(const WSAPI_ServerMessage& event, GWindow& window, const ByteBuffer& extra_data)
|
|
{
|
|
#ifdef GEVENTLOOP_DEBUG
|
|
dbgprintf("WID=%x Paint\n", event.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* action = GApplication::the().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);
|
|
return;
|
|
}
|
|
if (auto* action = menu->action_at(event.menu.identifier))
|
|
action->activate();
|
|
return;
|
|
}
|
|
ASSERT_NOT_REACHED();
|
|
}
|
|
|
|
void GWindowServerConnection::handle_wm_event(const WSAPI_ServerMessage& event, GWindow& window)
|
|
{
|
|
#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();
|
|
}
|
|
|
|
void GWindowServerConnection::postprocess_bundles(Vector<IncomingMessageBundle>& bundles)
|
|
{
|
|
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::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);
|
|
#endif
|
|
}
|
|
|
|
void GWindowServerConnection::handle_greeting(WSAPI_ServerMessage& 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);
|
|
}
|