From a5b23d81765dd9611b8464857b15396a8f994fc2 Mon Sep 17 00:00:00 2001 From: Francisco Magdaleno Date: Thu, 26 Sep 2019 15:23:58 -0700 Subject: [PATCH] Adds support for 5 mouse buttons (#12450) --- .../platform/windows/win32_flutter_window.cc | 74 +++++++++++++++---- shell/platform/windows/win32_flutter_window.h | 4 +- shell/platform/windows/win32_window.cc | 31 +++++++- shell/platform/windows/win32_window.h | 48 +++++++++++- 4 files changed, 134 insertions(+), 23 deletions(-) diff --git a/shell/platform/windows/win32_flutter_window.cc b/shell/platform/windows/win32_flutter_window.cc index 46125c046..1ea055c12 100644 --- a/shell/platform/windows/win32_flutter_window.cc +++ b/shell/platform/windows/win32_flutter_window.cc @@ -81,6 +81,27 @@ static FlutterDesktopMessage ConvertToDesktopMessage( return message; } +// Translates button codes from Win32 API to FlutterPointerMouseButtons. +static uint64_t ConvertWinButtonToFlutterButton(UINT button) { + switch (button) { + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + return kFlutterPointerButtonMousePrimary; + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + return kFlutterPointerButtonMouseSecondary; + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + return kFlutterPointerButtonMouseMiddle; + case XBUTTON1: + return kFlutterPointerButtonMouseBack; + case XBUTTON2: + return kFlutterPointerButtonMouseForward; + } + std::cerr << "Mouse button not recognized: " << button << std::endl; + return 0; +} + // The Flutter Engine calls out to this function when new platform messages // are available. void Win32FlutterWindow::HandlePlatformMessage( @@ -113,15 +134,25 @@ void Win32FlutterWindow::OnPointerMove(double x, double y) { } } -void Win32FlutterWindow::OnPointerDown(double x, double y) { +void Win32FlutterWindow::OnPointerDown(double x, double y, UINT button) { if (process_events_) { - SendPointerDown(x, y); + uint64_t flutter_button = ConvertWinButtonToFlutterButton(button); + if (flutter_button != 0) { + uint64_t mouse_buttons = GetMouseState().buttons | flutter_button; + SetMouseButtons(mouse_buttons); + SendPointerDown(x, y); + } } } -void Win32FlutterWindow::OnPointerUp(double x, double y) { +void Win32FlutterWindow::OnPointerUp(double x, double y, UINT button) { if (process_events_) { - SendPointerUp(x, y); + uint64_t flutter_button = ConvertWinButtonToFlutterButton(button); + if (flutter_button != 0) { + uint64_t mouse_buttons = GetMouseState().buttons & ~flutter_button; + SetMouseButtons(mouse_buttons); + SendPointerUp(x, y); + } } } @@ -190,8 +221,15 @@ void Win32FlutterWindow::SetEventLocationFromCursorPosition( // primary mouse button state. void Win32FlutterWindow::SetEventPhaseFromCursorButtonState( FlutterPointerEvent* event_data) { - event_data->phase = pointer_is_down_ ? FlutterPointerPhase::kMove - : FlutterPointerPhase::kHover; + MouseState state = GetMouseState(); + // For details about this logic, see FlutterPointerPhase in the embedder.h + // file. + event_data->phase = state.buttons == 0 ? state.flutter_state_is_down + ? FlutterPointerPhase::kUp + : FlutterPointerPhase::kHover + : state.flutter_state_is_down + ? FlutterPointerPhase::kMove + : FlutterPointerPhase::kDown; } void Win32FlutterWindow::SendPointerMove(double x, double y) { @@ -203,21 +241,23 @@ void Win32FlutterWindow::SendPointerMove(double x, double y) { } void Win32FlutterWindow::SendPointerDown(double x, double y) { - pointer_is_down_ = true; FlutterPointerEvent event = {}; - event.phase = FlutterPointerPhase::kDown; + SetEventPhaseFromCursorButtonState(&event); event.x = x; event.y = y; SendPointerEventWithData(event); + SetMouseFlutterStateDown(true); } void Win32FlutterWindow::SendPointerUp(double x, double y) { - pointer_is_down_ = false; FlutterPointerEvent event = {}; - event.phase = FlutterPointerPhase::kUp; + SetEventPhaseFromCursorButtonState(&event); event.x = x; event.y = y; SendPointerEventWithData(event); + if (event.phase == FlutterPointerPhase::kUp) { + SetMouseFlutterStateDown(false); + } } void Win32FlutterWindow::SendPointerLeave() { @@ -253,24 +293,29 @@ void Win32FlutterWindow::SendScroll(double delta_x, double delta_y) { void Win32FlutterWindow::SendPointerEventWithData( const FlutterPointerEvent& event_data) { + MouseState mouse_state = GetMouseState(); // If sending anything other than an add, and the pointer isn't already added, // synthesize an add to satisfy Flutter's expectations about events. - if (!pointer_currently_added_ && + if (!mouse_state.flutter_state_is_added && event_data.phase != FlutterPointerPhase::kAdd) { FlutterPointerEvent event = {}; event.phase = FlutterPointerPhase::kAdd; event.x = event_data.x; event.y = event_data.y; + event.buttons = 0; SendPointerEventWithData(event); } // Don't double-add (e.g., if events are delivered out of order, so an add has // already been synthesized). - if (pointer_currently_added_ && + if (mouse_state.flutter_state_is_added && event_data.phase == FlutterPointerPhase::kAdd) { return; } FlutterPointerEvent event = event_data; + event.device_kind = kFlutterPointerDeviceKindMouse; + event.buttons = mouse_state.buttons; + // Set metadata that's always the same regardless of the event. event.struct_size = sizeof(event); event.timestamp = @@ -288,9 +333,10 @@ void Win32FlutterWindow::SendPointerEventWithData( FlutterEngineSendPointerEvent(engine_, &event, 1); if (event_data.phase == FlutterPointerPhase::kAdd) { - pointer_currently_added_ = true; + SetMouseFlutterStateAdded(true); } else if (event_data.phase == FlutterPointerPhase::kRemove) { - pointer_currently_added_ = false; + SetMouseFlutterStateAdded(false); + ResetMouseState(); } } diff --git a/shell/platform/windows/win32_flutter_window.h b/shell/platform/windows/win32_flutter_window.h index 72d5dc65f..4d08b4117 100644 --- a/shell/platform/windows/win32_flutter_window.h +++ b/shell/platform/windows/win32_flutter_window.h @@ -48,10 +48,10 @@ class Win32FlutterWindow : public Win32Window { void OnPointerMove(double x, double y) override; // |Win32Window| - void OnPointerDown(double x, double y) override; + void OnPointerDown(double x, double y, UINT button) override; // |Win32Window| - void OnPointerUp(double x, double y) override; + void OnPointerUp(double x, double y, UINT button) override; // |Win32Window| void OnPointerLeave() override; diff --git a/shell/platform/windows/win32_window.cc b/shell/platform/windows/win32_window.cc index c6432c85e..43690efec 100644 --- a/shell/platform/windows/win32_window.cc +++ b/shell/platform/windows/win32_window.cc @@ -118,7 +118,7 @@ Win32Window::MessageHandler(HWND hwnd, UINT width = 0, height = 0; auto window = reinterpret_cast(GetWindowLongPtr(hwnd, GWLP_USERDATA)); - + UINT button_pressed = 0; if (window != nullptr) { switch (message) { case WM_DPICHANGED: @@ -158,16 +158,41 @@ Win32Window::MessageHandler(HWND hwnd, tracking_mouse_leave_ = false; break; case WM_LBUTTONDOWN: + case WM_RBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_XBUTTONDOWN: + if (message == WM_LBUTTONDOWN) { + // Capture the pointer in case the user drags outside the client area. + // In this case, the "mouse leave" event is delayed until the user + // releases the button. It's only activated on left click given that + // it's more common for apps to handle dragging with only the left + // button. + SetCapture(hwnd); + } + button_pressed = message; + if (message == WM_XBUTTONDOWN) { + button_pressed = GET_XBUTTON_WPARAM(wparam); + } xPos = GET_X_LPARAM(lparam); yPos = GET_Y_LPARAM(lparam); window->OnPointerDown(static_cast(xPos), - static_cast(yPos)); + static_cast(yPos), button_pressed); break; case WM_LBUTTONUP: + case WM_RBUTTONUP: + case WM_MBUTTONUP: + case WM_XBUTTONUP: + if (message == WM_LBUTTONUP) { + ReleaseCapture(); + } + button_pressed = message; + if (message == WM_XBUTTONUP) { + button_pressed = GET_XBUTTON_WPARAM(wparam); + } xPos = GET_X_LPARAM(lparam); yPos = GET_Y_LPARAM(lparam); window->OnPointerUp(static_cast(xPos), - static_cast(yPos)); + static_cast(yPos), button_pressed); break; case WM_MOUSEWHEEL: window->OnScroll( diff --git a/shell/platform/windows/win32_window.h b/shell/platform/windows/win32_window.h index dc209ca9e..e670a5251 100644 --- a/shell/platform/windows/win32_window.h +++ b/shell/platform/windows/win32_window.h @@ -15,6 +15,22 @@ namespace flutter { +// Struct holding the mouse state. The engine doesn't keep track of which mouse +// buttons have been pressed, so it's the embedding's responsibility. +struct MouseState { + // True if the last event sent to Flutter had at least one mouse button + // pressed. + bool flutter_state_is_down = false; + + // True if kAdd has been sent to Flutter. Used to determine whether + // to send a kAdd event before sending an incoming mouse event, since Flutter + // expects pointers to be added before events are sent for them. + bool flutter_state_is_added = false; + + // The currently pressed buttons, as represented in FlutterPointerEvent. + uint64_t buttons = 0; +}; + // A class abstraction for a high DPI aware Win32 Window. Intended to be // inherited from by classes that wish to specialize with custom // rendering and input handling. @@ -81,12 +97,12 @@ class Win32Window { // window bounds. virtual void OnPointerMove(double x, double y) = 0; - // Called when the left mouse button goes down - virtual void OnPointerDown(double x, double y) = 0; + // Called when the a mouse button, determined by |button|, goes down. + virtual void OnPointerDown(double x, double y, UINT button) = 0; - // Called when the left mouse button goes from + // Called when the a mouse button, determined by |button|, goes from // down to up - virtual void OnPointerUp(double x, double y) = 0; + virtual void OnPointerUp(double x, double y, UINT button) = 0; // Called when the mouse leaves the window. virtual void OnPointerLeave() = 0; @@ -112,6 +128,27 @@ class Win32Window { UINT GetCurrentHeight(); + // Gets the current mouse state. + MouseState GetMouseState() { return mouse_state_; } + + // Resets the mouse state to its default values. + void ResetMouseState() { mouse_state_ = MouseState(); } + + // Updates the mouse state to whether the last event to Flutter had at least + // one mouse button pressed. + void SetMouseFlutterStateDown(bool is_down) { + mouse_state_.flutter_state_is_down = is_down; + } + + // Updates the mouse state to whether the last event to Flutter was a kAdd + // event. + void SetMouseFlutterStateAdded(bool is_added) { + mouse_state_.flutter_state_is_added = is_added; + } + + // Updates the currently pressed buttons. + void SetMouseButtons(uint64_t buttons) { mouse_state_.buttons = buttons; } + private: // Activates tracking for a "mouse leave" event. void TrackMouseLeaveEvent(HWND hwnd); @@ -142,6 +179,9 @@ class Win32Window { // Set to true to be notified when the mouse leaves the window. bool tracking_mouse_leave_ = false; + + // Keeps track of mouse state in relation to the window. + MouseState mouse_state_; }; } // namespace flutter -- GitLab