From dab9901235f92925617a4c2d229831e029dc2d50 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Mon, 13 May 2019 19:52:57 +0200 Subject: [PATCH] WindowServer+LibGUI: Handle mouse wheel deltas in the mouse event stream. The wheel events will end up in GWidget::mousewheel_event(GMouseEvent&) on the client-side. This patch also implements basic wheel scrolling in GScrollableWidget via this mechanism. :^) --- LibGUI/GEvent.h | 7 +++++-- LibGUI/GEventLoop.cpp | 6 ++++-- LibGUI/GScrollableWidget.cpp | 6 ++++++ LibGUI/GScrollableWidget.h | 1 + LibGUI/GWidget.cpp | 6 ++++++ LibGUI/GWidget.h | 1 + LibGUI/GWindow.cpp | 24 +++++++++++------------- Servers/WindowServer/WSAPITypes.h | 2 ++ Servers/WindowServer/WSEvent.h | 10 +++++++--- Servers/WindowServer/WSEventLoop.cpp | 9 ++++++--- Servers/WindowServer/WSScreen.cpp | 7 ++++++- Servers/WindowServer/WSScreen.h | 2 +- Servers/WindowServer/WSWindow.cpp | 2 ++ 13 files changed, 58 insertions(+), 25 deletions(-) diff --git a/LibGUI/GEvent.h b/LibGUI/GEvent.h index 06a844fee4..89238eb777 100644 --- a/LibGUI/GEvent.h +++ b/LibGUI/GEvent.h @@ -19,6 +19,7 @@ public: MouseMove, MouseDown, MouseUp, + MouseWheel, Enter, Leave, KeyDown, @@ -44,7 +45,6 @@ public: explicit GEvent(Type type) : CEvent(type) { } virtual ~GEvent() { } - bool is_mouse_event() const { return type() == MouseMove || type() == MouseDown || type() == MouseUp; } bool is_key_event() const { return type() == KeyUp || type() == KeyDown; } bool is_paint_event() const { return type() == Paint; } }; @@ -244,12 +244,13 @@ private: class GMouseEvent final : public GEvent { public: - GMouseEvent(Type type, const Point& position, unsigned buttons, GMouseButton button, unsigned modifiers) + GMouseEvent(Type type, const Point& position, unsigned buttons, GMouseButton button, unsigned modifiers, int wheel_delta) : GEvent(type) , m_position(position) , m_buttons(buttons) , m_button(button) , m_modifiers(modifiers) + , m_wheel_delta(wheel_delta) { } @@ -259,10 +260,12 @@ public: GMouseButton button() const { return m_button; } unsigned buttons() const { return m_buttons; } unsigned modifiers() const { return m_modifiers; } + int wheel_delta() const { return m_wheel_delta; } private: Point m_position; unsigned m_buttons { 0 }; GMouseButton m_button { GMouseButton::None }; unsigned m_modifiers { 0 }; + int m_wheel_delta { 0 }; }; diff --git a/LibGUI/GEventLoop.cpp b/LibGUI/GEventLoop.cpp index 19ce764658..0e3216895f 100644 --- a/LibGUI/GEventLoop.cpp +++ b/LibGUI/GEventLoop.cpp @@ -152,13 +152,14 @@ void GEventLoop::handle_key_event(const WSAPI_ServerMessage& event, GWindow& win void GEventLoop::handle_mouse_event(const WSAPI_ServerMessage& event, GWindow& window) { #ifdef GEVENTLOOP_DEBUG - dbgprintf("WID=%x MouseEvent %d,%d\n", event.window_id, event.mouse.position.x, event.mouse.position.y); + 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::MouseWheel: type = GEvent::MouseWheel; break; default: ASSERT_NOT_REACHED(); break; } GMouseButton button { GMouseButton::None }; @@ -169,7 +170,7 @@ void GEventLoop::handle_mouse_event(const WSAPI_ServerMessage& event, GWindow& w case WSAPI_MouseButton::Middle: button = GMouseButton::Middle; break; default: ASSERT_NOT_REACHED(); break; } - post_event(window, make(type, event.mouse.position, event.mouse.buttons, button, event.mouse.modifiers)); + post_event(window, make(type, event.mouse.position, event.mouse.buttons, button, event.mouse.modifiers, event.mouse.wheel_delta)); } void GEventLoop::handle_menu_event(const WSAPI_ServerMessage& event) @@ -276,6 +277,7 @@ void GEventLoop::process_unprocessed_bundles() case WSAPI_ServerMessage::Type::MouseDown: 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: diff --git a/LibGUI/GScrollableWidget.cpp b/LibGUI/GScrollableWidget.cpp index b2a64f125b..9ae217e792 100644 --- a/LibGUI/GScrollableWidget.cpp +++ b/LibGUI/GScrollableWidget.cpp @@ -27,6 +27,12 @@ GScrollableWidget::~GScrollableWidget() { } +void GScrollableWidget::mousewheel_event(GMouseEvent& event) +{ + // FIXME: The wheel delta multiplier should probably come from... somewhere? + vertical_scrollbar().set_value(vertical_scrollbar().value() + event.wheel_delta() * 20); +} + void GScrollableWidget::resize_event(GResizeEvent& event) { auto inner_rect = frame_inner_rect_for_size(event.size()); diff --git a/LibGUI/GScrollableWidget.h b/LibGUI/GScrollableWidget.h index 60c80a321f..f2b0c17a86 100644 --- a/LibGUI/GScrollableWidget.h +++ b/LibGUI/GScrollableWidget.h @@ -39,6 +39,7 @@ public: protected: explicit GScrollableWidget(GWidget* parent); virtual void resize_event(GResizeEvent&) override; + virtual void mousewheel_event(GMouseEvent&) override; virtual void did_scroll() { } void set_content_size(const Size&); void set_size_occupied_by_fixed_elements(const Size&); diff --git a/LibGUI/GWidget.cpp b/LibGUI/GWidget.cpp index 985ed540c1..1d98c4d2e1 100644 --- a/LibGUI/GWidget.cpp +++ b/LibGUI/GWidget.cpp @@ -86,6 +86,8 @@ void GWidget::event(CEvent& event) return handle_mousedown_event(static_cast(event)); case GEvent::MouseUp: return handle_mouseup_event(static_cast(event)); + case GEvent::MouseWheel: + return mousewheel_event(static_cast(event)); case GEvent::Enter: return handle_enter_event(event); case GEvent::Leave: @@ -261,6 +263,10 @@ void GWidget::mousemove_event(GMouseEvent&) { } +void GWidget::mousewheel_event(GMouseEvent&) +{ +} + void GWidget::context_menu_event(GContextMenuEvent&) { } diff --git a/LibGUI/GWidget.h b/LibGUI/GWidget.h index 9be5d9b02e..daee5cbf8a 100644 --- a/LibGUI/GWidget.h +++ b/LibGUI/GWidget.h @@ -58,6 +58,7 @@ public: virtual void mousemove_event(GMouseEvent&); virtual void mousedown_event(GMouseEvent&); virtual void mouseup_event(GMouseEvent&); + virtual void mousewheel_event(GMouseEvent&); virtual void click_event(GMouseEvent&); virtual void doubleclick_event(GMouseEvent&); virtual void context_menu_event(GContextMenuEvent&); diff --git a/LibGUI/GWindow.cpp b/LibGUI/GWindow.cpp index 6e9bc12e6d..02c8c84a5c 100644 --- a/LibGUI/GWindow.cpp +++ b/LibGUI/GWindow.cpp @@ -170,19 +170,19 @@ void GWindow::set_override_cursor(GStandardCursor cursor) void GWindow::event(CEvent& event) { - if (event.type() == GEvent::MouseUp || event.type() == GEvent::MouseDown || event.type() == GEvent::MouseMove) { + if (event.type() == GEvent::MouseUp || event.type() == GEvent::MouseDown || event.type() == GEvent::MouseMove || event.type() == GEvent::MouseWheel) { auto& mouse_event = static_cast(event); if (m_global_cursor_tracking_widget) { auto window_relative_rect = m_global_cursor_tracking_widget->window_relative_rect(); Point local_point { mouse_event.x() - window_relative_rect.x(), mouse_event.y() - window_relative_rect.y() }; - auto local_event = make((GEvent::Type)event.type(), local_point, mouse_event.buttons(), mouse_event.button(), mouse_event.modifiers()); + auto local_event = make((GEvent::Type)event.type(), local_point, mouse_event.buttons(), mouse_event.button(), mouse_event.modifiers(), mouse_event.wheel_delta()); m_global_cursor_tracking_widget->event(*local_event); return; } if (m_automatic_cursor_tracking_widget) { auto window_relative_rect = m_automatic_cursor_tracking_widget->window_relative_rect(); Point local_point { mouse_event.x() - window_relative_rect.x(), mouse_event.y() - window_relative_rect.y() }; - auto local_event = make((GEvent::Type)event.type(), local_point, mouse_event.buttons(), mouse_event.button(), mouse_event.modifiers()); + auto local_event = make((GEvent::Type)event.type(), local_point, mouse_event.buttons(), mouse_event.button(), mouse_event.modifiers(), mouse_event.wheel_delta()); m_automatic_cursor_tracking_widget->event(*local_event); if (mouse_event.buttons() == 0) m_automatic_cursor_tracking_widget = nullptr; @@ -190,16 +190,14 @@ void GWindow::event(CEvent& event) } if (!m_main_widget) return; - if (m_main_widget) { - auto result = m_main_widget->hit_test(mouse_event.position()); - auto local_event = make((GEvent::Type)event.type(), result.local_position, mouse_event.buttons(), mouse_event.button(), mouse_event.modifiers()); - ASSERT(result.widget); - set_hovered_widget(result.widget); - if (mouse_event.buttons() != 0 && !m_automatic_cursor_tracking_widget) - m_automatic_cursor_tracking_widget = result.widget->make_weak_ptr(); - if (result.widget != m_global_cursor_tracking_widget.ptr()) - return result.widget->event(*local_event); - } + auto result = m_main_widget->hit_test(mouse_event.position()); + auto local_event = make((GEvent::Type)event.type(), result.local_position, mouse_event.buttons(), mouse_event.button(), mouse_event.modifiers(), mouse_event.wheel_delta()); + ASSERT(result.widget); + set_hovered_widget(result.widget); + if (mouse_event.buttons() != 0 && !m_automatic_cursor_tracking_widget) + m_automatic_cursor_tracking_widget = result.widget->make_weak_ptr(); + if (result.widget != m_global_cursor_tracking_widget.ptr()) + return result.widget->event(*local_event); return; } diff --git a/Servers/WindowServer/WSAPITypes.h b/Servers/WindowServer/WSAPITypes.h index e5f3be767a..aa7f22f5bd 100644 --- a/Servers/WindowServer/WSAPITypes.h +++ b/Servers/WindowServer/WSAPITypes.h @@ -74,6 +74,7 @@ struct WSAPI_ServerMessage { MouseMove, MouseDown, MouseUp, + MouseWheel, WindowEntered, WindowLeft, KeyDown, @@ -155,6 +156,7 @@ struct WSAPI_ServerMessage { WSAPI_MouseButton button; unsigned buttons; byte modifiers; + int wheel_delta; } mouse; struct { char character; diff --git a/Servers/WindowServer/WSEvent.h b/Servers/WindowServer/WSEvent.h index d0033ae48e..8dc1f67fea 100644 --- a/Servers/WindowServer/WSEvent.h +++ b/Servers/WindowServer/WSEvent.h @@ -18,6 +18,7 @@ public: MouseMove, MouseDown, MouseUp, + MouseWheel, WindowEntered, WindowLeft, KeyDown, @@ -74,7 +75,7 @@ public: virtual ~WSEvent() { } bool is_client_request() const { return type() > __Begin_API_Client_Requests && type() < __End_API_Client_Requests; } - bool is_mouse_event() const { return type() == MouseMove || type() == MouseDown || type() == MouseUp; } + bool is_mouse_event() const { return type() == MouseMove || type() == MouseDown || type() == MouseUp || type() == MouseWheel; } bool is_key_event() const { return type() == KeyUp || type() == KeyDown; } }; @@ -718,12 +719,13 @@ private: class WSMouseEvent final : public WSEvent { public: - WSMouseEvent(Type type, const Point& position, unsigned buttons, MouseButton button, unsigned modifiers) + WSMouseEvent(Type type, const Point& position, unsigned buttons, MouseButton button, unsigned modifiers, int wheel_delta = 0) : WSEvent(type) , m_position(position) , m_buttons(buttons) , m_button(button) , m_modifiers(modifiers) + , m_wheel_delta(wheel_delta) { } @@ -733,14 +735,16 @@ public: MouseButton button() const { return m_button; } unsigned buttons() const { return m_buttons; } unsigned modifiers() const { return m_modifiers; } + int wheel_delta() const { return m_wheel_delta; } - WSMouseEvent translated(const Point& delta) const { return WSMouseEvent((Type)type(), m_position.translated(delta), m_buttons, m_button, m_modifiers); } + WSMouseEvent translated(const Point& delta) const { return WSMouseEvent((Type)type(), m_position.translated(delta), m_buttons, m_button, m_modifiers, m_wheel_delta); } private: Point m_position; unsigned m_buttons { 0 }; MouseButton m_button { MouseButton::None }; unsigned m_modifiers { 0 }; + int m_wheel_delta { 0 }; }; class WSResizeEvent final : public WSEvent { diff --git a/Servers/WindowServer/WSEventLoop.cpp b/Servers/WindowServer/WSEventLoop.cpp index 89449d0e62..adb2873b78 100644 --- a/Servers/WindowServer/WSEventLoop.cpp +++ b/Servers/WindowServer/WSEventLoop.cpp @@ -62,6 +62,7 @@ void WSEventLoop::drain_mouse() unsigned prev_buttons = screen.mouse_button_state(); int dx = 0; int dy = 0; + int dz = 0; unsigned buttons = prev_buttons; for (;;) { MousePacket packet; @@ -73,15 +74,17 @@ void WSEventLoop::drain_mouse() dx += packet.dx; dy += -packet.dy; + dz += packet.dz; if (buttons != prev_buttons) { - screen.on_receive_mouse_data(dx, dy, buttons); + screen.on_receive_mouse_data(dx, dy, dz, buttons); dx = 0; dy = 0; + dz = 0; prev_buttons = buttons; } } - if (dx || dy) - screen.on_receive_mouse_data(dx, dy, buttons); + if (dx || dy || dz) + screen.on_receive_mouse_data(dx, dy, dz, buttons); } void WSEventLoop::drain_keyboard() diff --git a/Servers/WindowServer/WSScreen.cpp b/Servers/WindowServer/WSScreen.cpp index 210da79663..03ef728abb 100644 --- a/Servers/WindowServer/WSScreen.cpp +++ b/Servers/WindowServer/WSScreen.cpp @@ -58,7 +58,7 @@ void WSScreen::set_resolution(int width, int height) m_cursor_location.constrain(rect()); } -void WSScreen::on_receive_mouse_data(int dx, int dy, unsigned buttons) +void WSScreen::on_receive_mouse_data(int dx, int dy, int dz, unsigned buttons) { auto prev_location = m_cursor_location; m_cursor_location.move_by(dx, dy); @@ -80,6 +80,11 @@ void WSScreen::on_receive_mouse_data(int dx, int dy, unsigned buttons) WSEventLoop::the().post_event(WSWindowManager::the(), move(message)); } + if (dz) { + auto message = make(WSEvent::MouseWheel, m_cursor_location, buttons, MouseButton::None, m_modifiers, dz); + WSEventLoop::the().post_event(WSWindowManager::the(), move(message)); + } + if (m_cursor_location != prev_location) WSWindowManager::the().invalidate_cursor(); } diff --git a/Servers/WindowServer/WSScreen.h b/Servers/WindowServer/WSScreen.h index 0d71f816ac..891ac527b0 100644 --- a/Servers/WindowServer/WSScreen.h +++ b/Servers/WindowServer/WSScreen.h @@ -26,7 +26,7 @@ public: 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_mouse_data(int dx, int dy, int dz, unsigned buttons); void on_receive_keyboard_data(KeyEvent); private: diff --git a/Servers/WindowServer/WSWindow.cpp b/Servers/WindowServer/WSWindow.cpp index 3b29e016fd..0295b1fd81 100644 --- a/Servers/WindowServer/WSWindow.cpp +++ b/Servers/WindowServer/WSWindow.cpp @@ -95,6 +95,7 @@ void WSWindow::handle_mouse_event(const WSMouseEvent& event) case WSEvent::MouseMove: server_message.type = WSAPI_ServerMessage::Type::MouseMove; break; case WSEvent::MouseDown: server_message.type = WSAPI_ServerMessage::Type::MouseDown; break; case WSEvent::MouseUp: server_message.type = WSAPI_ServerMessage::Type::MouseUp; break; + case WSEvent::MouseWheel: server_message.type = WSAPI_ServerMessage::Type::MouseWheel; break; default: ASSERT_NOT_REACHED(); } @@ -102,6 +103,7 @@ void WSWindow::handle_mouse_event(const WSMouseEvent& event) server_message.mouse.button = to_api(event.button()); server_message.mouse.buttons = event.buttons(); server_message.mouse.modifiers = event.modifiers(); + server_message.mouse.wheel_delta = event.wheel_delta(); m_client->post_message(server_message); }