From 23ea9f138a6a37b9ebbc36a0e8b700fa244f900a Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Mon, 6 Jan 2025 17:30:17 +0000 Subject: [PATCH 1/2] Codechange: Call VideoDriver to change mouse position fix state. This avoids the video driver needing to check if the state has changed. --- src/openttd.cpp | 2 +- src/smallmap_gui.cpp | 5 ++++- src/video/cocoa/cocoa_v.h | 1 + src/video/cocoa/cocoa_v.mm | 9 +++++++++ src/video/cocoa/cocoa_wnd.mm | 17 ++++------------- src/video/video_driver.hpp | 5 +++++ src/window.cpp | 9 ++++----- 7 files changed, 28 insertions(+), 20 deletions(-) diff --git a/src/openttd.cpp b/src/openttd.cpp index b47696151d..465dfbd41c 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -332,7 +332,7 @@ static void LoadIntroGame(bool load_newgrfs = true) FixTitleGameZoom(); _pause_mode = PM_UNPAUSED; - _cursor.fix_at = false; + VideoDriver::GetInstance()->FixMousePointer(false); CheckForMissingGlyphs(); diff --git a/src/smallmap_gui.cpp b/src/smallmap_gui.cpp index 0616535b76..470cf753a4 100644 --- a/src/smallmap_gui.cpp +++ b/src/smallmap_gui.cpp @@ -9,6 +9,7 @@ #include "stdafx.h" #include "core/backup_type.hpp" +#include "video/video_driver.hpp" #include "clear_map.h" #include "industry.h" #include "station_map.h" @@ -1866,7 +1867,9 @@ public: void OnScroll(Point delta) override { - if (_settings_client.gui.scroll_mode == VSM_VIEWPORT_RMB_FIXED || _settings_client.gui.scroll_mode == VSM_MAP_RMB_FIXED) _cursor.fix_at = true; + if (_settings_client.gui.scroll_mode == VSM_VIEWPORT_RMB_FIXED || _settings_client.gui.scroll_mode == VSM_MAP_RMB_FIXED) { + VideoDriver::GetInstance()->FixMousePointer(true); + } /* While tile is at (delta.x, delta.y)? */ int sub; diff --git a/src/video/cocoa/cocoa_v.h b/src/video/cocoa/cocoa_v.h index edfa39646d..deaf4a31ae 100644 --- a/src/video/cocoa/cocoa_v.h +++ b/src/video/cocoa/cocoa_v.h @@ -49,6 +49,7 @@ public: void ClearSystemSprites() override; void PopulateSystemSprites() override; + void FixMousePointer(bool fix_at) override; void EditBoxLostFocus() override; std::vector GetListOfMonitorRefreshRates() override; diff --git a/src/video/cocoa/cocoa_v.mm b/src/video/cocoa/cocoa_v.mm index 7b08367cdc..9172d8d3b6 100644 --- a/src/video/cocoa/cocoa_v.mm +++ b/src/video/cocoa/cocoa_v.mm @@ -240,6 +240,15 @@ bool VideoDriver_Cocoa::AfterBlitterChange() return true; } +void VideoDriver_Cocoa::FixMousePointer(bool fix_at) +{ + if (_cursor.fix_at == fix_at) return; + + this->VideoDriver::FixMousePointer(fix_at); + + CGAssociateMouseAndMouseCursorPosition(!_cursor.fix_at); +} + /** * An edit box lost the input focus. Abort character compositing if necessary. */ diff --git a/src/video/cocoa/cocoa_wnd.mm b/src/video/cocoa/cocoa_wnd.mm index 81b547aec4..4d0a03bcdb 100644 --- a/src/video/cocoa/cocoa_wnd.mm +++ b/src/video/cocoa/cocoa_wnd.mm @@ -674,15 +674,6 @@ void CocoaDialog(const char *title, const char *message, const char *buttonLabel HandleMouseEvents(); } -- (void)internalMouseButtonEvent -{ - bool cur_fix = _cursor.fix_at; - HandleMouseEvents(); - - /* Cursor fix mode was changed, synchronize with OS. */ - if (cur_fix != _cursor.fix_at) CGAssociateMouseAndMouseCursorPosition(!_cursor.fix_at); -} - - (BOOL)emulateRightButton:(NSEvent *)event { uint32_t keymask = 0; @@ -708,7 +699,7 @@ void CocoaDialog(const char *title, const char *message, const char *buttonLabel [ self rightMouseDown:event ]; } else { _left_button_down = true; - [ self internalMouseButtonEvent ]; + HandleMouseEvents(); } } - (void)mouseUp:(NSEvent *)event @@ -719,7 +710,7 @@ void CocoaDialog(const char *title, const char *message, const char *buttonLabel } else { _left_button_down = false; _left_button_clicked = false; - [ self internalMouseButtonEvent ]; + HandleMouseEvents(); } } @@ -731,12 +722,12 @@ void CocoaDialog(const char *title, const char *message, const char *buttonLabel { _right_button_down = true; _right_button_clicked = true; - [ self internalMouseButtonEvent ]; + HandleMouseEvents(); } - (void)rightMouseUp:(NSEvent *)event { _right_button_down = false; - [ self internalMouseButtonEvent ]; + HandleMouseEvents(); } - (void)scrollWheel:(NSEvent *)event diff --git a/src/video/video_driver.hpp b/src/video/video_driver.hpp index 72731f0113..76de0b280e 100644 --- a/src/video/video_driver.hpp +++ b/src/video/video_driver.hpp @@ -87,6 +87,11 @@ public: return true; } + /** + * Fix the mouse position position. + */ + virtual void FixMousePointer(bool fix_at) { _cursor.fix_at = fix_at; } + /** * Get whether the mouse cursor is drawn by the video driver. * @return True if cursor drawing is done by the video driver. diff --git a/src/window.cpp b/src/window.cpp index 2aa4b2bc08..db2ddf1a0a 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -2365,7 +2365,7 @@ static EventState HandleViewportScroll() if (_last_scroll_window == nullptr) _last_scroll_window = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y); if (_last_scroll_window == nullptr || !((_settings_client.gui.scroll_mode != VSM_MAP_LMB && _right_button_down) || scrollwheel_scrolling || (_settings_client.gui.scroll_mode == VSM_MAP_LMB && _left_button_down))) { - _cursor.fix_at = false; + VideoDriver::GetInstance()->FixMousePointer(false); _scrolling_viewport = false; _last_scroll_window = nullptr; return ES_NOT_HANDLED; @@ -2820,7 +2820,7 @@ static void MouseLoop(MouseClick click, int mousewheel) if (vp != nullptr) { if (scrollwheel_scrolling && !(w->flags & WF_DISABLE_VP_SCROLL)) { _scrolling_viewport = true; - _cursor.fix_at = true; + VideoDriver::GetInstance()->FixMousePointer(true); return; } @@ -2831,7 +2831,7 @@ static void MouseLoop(MouseClick click, int mousewheel) if (!(w->flags & WF_DISABLE_VP_SCROLL) && _settings_client.gui.scroll_mode == VSM_MAP_LMB) { _scrolling_viewport = true; - _cursor.fix_at = false; + VideoDriver::GetInstance()->FixMousePointer(false); return; } break; @@ -2840,8 +2840,7 @@ static void MouseLoop(MouseClick click, int mousewheel) if (!(w->flags & WF_DISABLE_VP_SCROLL) && _settings_client.gui.scroll_mode != VSM_MAP_LMB) { _scrolling_viewport = true; - _cursor.fix_at = (_settings_client.gui.scroll_mode == VSM_VIEWPORT_RMB_FIXED || - _settings_client.gui.scroll_mode == VSM_MAP_RMB_FIXED); + VideoDriver::GetInstance()->FixMousePointer(_settings_client.gui.scroll_mode == VSM_VIEWPORT_RMB_FIXED || _settings_client.gui.scroll_mode == VSM_MAP_RMB_FIXED); DispatchRightClickEvent(w, x - w->left, y - w->top); return; } From f1e310727a0882f9950289d8468088e72bf7ad35 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Mon, 6 Jan 2025 17:30:17 +0000 Subject: [PATCH 2/2] Fix #13286: Use relative mouse movement when cursor position is fixed. This ensures the movement isn't constrained by the window boundary. As this switches us from automatic to manual mouse capture modes, this also removes the `no_mouse_capture` driver option. --- src/video/sdl2_v.cpp | 42 ++++++++++++++++++++++++++++-------------- src/video/sdl2_v.h | 2 ++ 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/src/video/sdl2_v.cpp b/src/video/sdl2_v.cpp index 8cea6be506..58c7bc64c7 100644 --- a/src/video/sdl2_v.cpp +++ b/src/video/sdl2_v.cpp @@ -205,6 +205,20 @@ bool VideoDriver_SDL_Base::ClaimMousePointer() return true; } +void VideoDriver_SDL_Base::FixMousePointer(bool fix_at) +{ + if (_cursor.fix_at == fix_at) return; + + this->VideoDriver::FixMousePointer(fix_at); + + SDL_SetRelativeMouseMode(fix_at ? SDL_TRUE : SDL_FALSE); + + if (!fix_at) { + /* Move cursor back to where it was before being fixed. */ + SDL_WarpMouseInWindow(this->sdl_window, _cursor.pos.x, _cursor.pos.y); + } +} + /** * This is called to indicate that an edit box has gained focus, text input mode should be enabled. */ @@ -385,21 +399,23 @@ bool VideoDriver_SDL_Base::PollEvent() switch (ev.type) { case SDL_MOUSEMOTION: { - int32_t x = ev.motion.x; - int32_t y = ev.motion.y; - if (_cursor.fix_at) { + /* Use relative motion events. */ + int32_t x = ev.motion.xrel; + int32_t y = ev.motion.yrel; + /* Get all queued mouse events now in case we have to warp the cursor. In the * end, we only care about the current mouse position and not bygone events. */ while (SDL_PeepEvents(&ev, 1, SDL_GETEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION)) { - x = ev.motion.x; - y = ev.motion.y; + x += ev.motion.xrel; + y += ev.motion.yrel; } + + _cursor.UpdateCursorPositionRelative(x, y); + } else { + _cursor.UpdateCursorPosition(ev.motion.x, ev.motion.y); } - if (_cursor.UpdateCursorPosition(x, y)) { - SDL_WarpMouseInWindow(this->sdl_window, _cursor.pos.x, _cursor.pos.y); - } HandleMouseEvents(); break; } @@ -560,12 +576,10 @@ std::optional VideoDriver_SDL_Base::Start(const StringList &pa if (error) return error; #ifdef SDL_HINT_MOUSE_AUTO_CAPTURE - if (GetDriverParamBool(param, "no_mouse_capture")) { - /* By default SDL captures the mouse, while a button is pressed. - * This is annoying during debugging, when OpenTTD is suspended while the button was pressed. - */ - if (!SDL_SetHint(SDL_HINT_MOUSE_AUTO_CAPTURE, "0")) return SDL_GetError(); - } + /* By default SDL captures the mouse, while a button is pressed. + * This is annoying during debugging, when OpenTTD is suspended while the button was pressed. + */ + SDL_SetHint(SDL_HINT_MOUSE_AUTO_CAPTURE, "0"); #endif #ifdef SDL_HINT_APP_NAME diff --git a/src/video/sdl2_v.h b/src/video/sdl2_v.h index 45c064d0bc..bbd3cadb49 100644 --- a/src/video/sdl2_v.h +++ b/src/video/sdl2_v.h @@ -35,6 +35,8 @@ public: bool ClaimMousePointer() override; + void FixMousePointer(bool fix_at) override; + void EditBoxGainedFocus() override; void EditBoxLostFocus() override;