diff --git a/Source/Core/Core/HotkeyManager.cpp b/Source/Core/Core/HotkeyManager.cpp index 02c0191256..b7c5c161c2 100644 --- a/Source/Core/Core/HotkeyManager.cpp +++ b/Source/Core/Core/HotkeyManager.cpp @@ -35,6 +35,7 @@ constexpr std::array s_hotkey_labels{{ _trans("Take Screenshot"), _trans("Exit"), _trans("Unlock Cursor"), + _trans("Center Mouse"), _trans("Activate NetPlay Chat"), _trans("Control NetPlay Golf Mode"), diff --git a/Source/Core/Core/HotkeyManager.h b/Source/Core/Core/HotkeyManager.h index b214c95a66..b58f9c2828 100644 --- a/Source/Core/Core/HotkeyManager.h +++ b/Source/Core/Core/HotkeyManager.h @@ -29,6 +29,7 @@ enum Hotkey HK_SCREENSHOT, HK_EXIT, HK_UNLOCK_CURSOR, + HK_CENTER_MOUSE, HK_ACTIVATE_CHAT, HK_REQUEST_GOLF_CONTROL, diff --git a/Source/Core/DolphinQt/HotkeyScheduler.cpp b/Source/Core/DolphinQt/HotkeyScheduler.cpp index 52fd1ac00a..6d7c91a6ee 100644 --- a/Source/Core/DolphinQt/HotkeyScheduler.cpp +++ b/Source/Core/DolphinQt/HotkeyScheduler.cpp @@ -233,6 +233,9 @@ void HotkeyScheduler::Run() if (IsHotkey(HK_UNLOCK_CURSOR)) emit UnlockCursor(); + if (IsHotkey(HK_CENTER_MOUSE, true)) + g_controller_interface.SetMouseCenteringRequested(true); + auto& settings = Settings::Instance(); // Toggle Chat diff --git a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp index 1c4dc71da8..442e5502fa 100644 --- a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp +++ b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp @@ -421,6 +421,16 @@ Common::Vec2 ControllerInterface::GetWindowInputScale() const return {1 / ar, 1.f}; } +void ControllerInterface::SetMouseCenteringRequested(bool center) +{ + m_requested_mouse_centering = center; +} + +bool ControllerInterface::IsMouseCenteringRequested() const +{ + return m_requested_mouse_centering.load(); +} + // Register a callback to be called when a device is added or removed (as from the input backends' // hotplug thread), or when devices are refreshed // Returns a handle for later removing the callback. diff --git a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h index 2f43cacc42..3a0276de41 100644 --- a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h +++ b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h @@ -106,6 +106,11 @@ public: // Inputs based on window coordinates should be multiplied by this. Common::Vec2 GetWindowInputScale() const; + // Request that the mouse cursor should be centered in the render window at the next opportunity. + void SetMouseCenteringRequested(bool center); + + bool IsMouseCenteringRequested() const; + HotplugCallbackHandle RegisterDevicesChangedCallback(std::function callback); void UnregisterDevicesChangedCallback(const HotplugCallbackHandle& handle); void InvokeDevicesChangedCallbacks() const; @@ -127,6 +132,7 @@ private: std::atomic m_populating_devices_counter; WindowSystemInfo m_wsi; std::atomic m_aspect_ratio_adjustment = 1; + std::atomic m_requested_mouse_centering = false; }; namespace ciface diff --git a/Source/Core/InputCommon/ControllerInterface/DInput/DInputKeyboardMouse.cpp b/Source/Core/InputCommon/ControllerInterface/DInput/DInputKeyboardMouse.cpp index 3fe850715c..551afde423 100644 --- a/Source/Core/InputCommon/ControllerInterface/DInput/DInputKeyboardMouse.cpp +++ b/Source/Core/InputCommon/ControllerInterface/DInput/DInputKeyboardMouse.cpp @@ -6,7 +6,9 @@ #include #include "Common/Logging/Log.h" + #include "Core/Core.h" +#include "Core/Host.h" #include "InputCommon/ControllerInterface/ControllerInterface.h" #include "InputCommon/ControllerInterface/DInput/DInput.h" @@ -168,13 +170,6 @@ KeyboardMouse::KeyboardMouse(const LPDIRECTINPUTDEVICE8 kb_device, void KeyboardMouse::UpdateCursorInput() { - POINT point = {}; - GetCursorPos(&point); - - // Get the cursor position relative to the upper left corner of the current window - // (separate or render to main) - ScreenToClient(s_hwnd, &point); - // Get the size of the current window (in my case Rect.top and Rect.left was zero). RECT rect; GetClientRect(s_hwnd, &rect); @@ -183,6 +178,26 @@ void KeyboardMouse::UpdateCursorInput() const auto win_width = std::max(rect.right - rect.left, 1l); const auto win_height = std::max(rect.bottom - rect.top, 1l); + POINT point = {}; + if (g_controller_interface.IsMouseCenteringRequested() && Host_RendererHasFocus()) + { + point.x = win_width / 2; + point.y = win_height / 2; + + POINT screen_point = point; + ClientToScreen(s_hwnd, &screen_point); + SetCursorPos(screen_point.x, screen_point.y); + g_controller_interface.SetMouseCenteringRequested(false); + } + else + { + GetCursorPos(&point); + + // Get the cursor position relative to the upper left corner of the current window + // (separate or render to main) + ScreenToClient(s_hwnd, &point); + } + const auto window_scale = g_controller_interface.GetWindowInputScale(); // Convert the cursor position to a range from -1 to 1. diff --git a/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.mm b/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.mm index 6b1790149b..b8ae0219be 100644 --- a/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.mm +++ b/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.mm @@ -8,6 +8,8 @@ #include #include +#include "Core/Host.h" + #include "InputCommon/ControllerInterface/ControllerInterface.h" namespace ciface::Quartz @@ -192,16 +194,36 @@ void KeyboardAndMouse::UpdateInput() CFRelease(windowDescriptions); CFRelease(windowArray); - CGEventRef event = CGEventCreate(nil); - CGPoint loc = CGEventGetLocation(event); - CFRelease(event); + const double window_width = std::max(bounds.size.width, 1.0); + const double window_height = std::max(bounds.size.height, 1.0); - const auto window_scale = g_controller_interface.GetWindowInputScale(); + if (g_controller_interface.IsMouseCenteringRequested() && Host_RendererHasFocus()) + { + m_cursor.x = 0; + m_cursor.y = 0; - loc.x -= bounds.origin.x; - loc.y -= bounds.origin.y; - m_cursor.x = (loc.x / std::max(bounds.size.width, 1.0) * 2 - 1.0) * window_scale.x; - m_cursor.y = (loc.y / std::max(bounds.size.height, 1.0) * 2 - 1.0) * window_scale.y; + const CGPoint window_center_global_coordinates = + CGPointMake(bounds.origin.x + window_width / 2.0, bounds.origin.y + window_height / 2.0); + CGWarpMouseCursorPosition(window_center_global_coordinates); + // Without this line there is a short but obvious delay after centering the cursor before it can + // be moved again + CGAssociateMouseAndMouseCursorPosition(true); + + g_controller_interface.SetMouseCenteringRequested(false); + } + else + { + CGEventRef event = CGEventCreate(nil); + CGPoint loc = CGEventGetLocation(event); + CFRelease(event); + + const auto window_scale = g_controller_interface.GetWindowInputScale(); + + loc.x -= bounds.origin.x; + loc.y -= bounds.origin.y; + m_cursor.x = (loc.x / window_width * 2 - 1.0) * window_scale.x; + m_cursor.y = (loc.y / window_height * 2 - 1.0) * window_scale.y; + } } std::string KeyboardAndMouse::GetName() const diff --git a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp index 1f8e107987..0c5f5ffb01 100644 --- a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp +++ b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp @@ -15,6 +15,8 @@ #include "Common/StringUtil.h" +#include "Core/Host.h" + // This is an input plugin using the XInput 2.0 extension to the X11 protocol, // loosely based on the old XLib plugin. (Has nothing to do with the XInput // API on Windows.) @@ -209,30 +211,44 @@ KeyboardMouse::~KeyboardMouse() } // Update the mouse cursor controls -void KeyboardMouse::UpdateCursor() +void KeyboardMouse::UpdateCursor(bool should_center_mouse) { double root_x, root_y, win_x, win_y; Window root, child; - // unused-- we're not interested in button presses here, as those are - // updated using events - XIButtonState button_state; - XIModifierState mods; - XIGroupState group; - - XIQueryPointer(m_display, pointer_deviceid, m_window, &root, &child, &root_x, &root_y, &win_x, - &win_y, &button_state, &mods, &group); - - free(button_state.mask); - XWindowAttributes win_attribs; XGetWindowAttributes(m_display, m_window, &win_attribs); + const auto win_width = std::max(win_attribs.width, 1); + const auto win_height = std::max(win_attribs.height, 1); + + if (should_center_mouse) + { + win_x = win_width / 2; + win_y = win_height / 2; + + XIWarpPointer(m_display, pointer_deviceid, None, m_window, 0.0, 0.0, 0, 0, win_x, win_y); + + g_controller_interface.SetMouseCenteringRequested(false); + } + else + { + // unused-- we're not interested in button presses here, as those are + // updated using events + XIButtonState button_state; + XIModifierState mods; + XIGroupState group; + + XIQueryPointer(m_display, pointer_deviceid, m_window, &root, &child, &root_x, &root_y, &win_x, + &win_y, &button_state, &mods, &group); + + free(button_state.mask); + } const auto window_scale = g_controller_interface.GetWindowInputScale(); // the mouse position as a range from -1 to 1 - m_state.cursor.x = (win_x / std::max(win_attribs.width, 1) * 2 - 1) * window_scale.x; - m_state.cursor.y = (win_y / std::max(win_attribs.height, 1) * 2 - 1) * window_scale.y; + m_state.cursor.x = (win_x / win_width * 2 - 1) * window_scale.x; + m_state.cursor.y = (win_y / win_height * 2 - 1) * window_scale.y; } void KeyboardMouse::UpdateInput() @@ -318,8 +334,10 @@ void KeyboardMouse::UpdateInput() m_state.axis.y /= MOUSE_AXIS_SMOOTHING + 1.0f; // Get the absolute position of the mouse pointer - if (mouse_moved) - UpdateCursor(); + const bool should_center_mouse = + g_controller_interface.IsMouseCenteringRequested() && Host_RendererHasFocus(); + if (mouse_moved || should_center_mouse) + UpdateCursor(should_center_mouse); // KeyRelease and FocusOut events are sometimes not received. // Cycling Alt-Tab and landing on the same window results in a stuck "Alt" key. diff --git a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.h b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.h index 482bc37830..b07f6325c7 100644 --- a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.h +++ b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.h @@ -108,7 +108,7 @@ private: private: void SelectEventsForDevice(XIEventMask* mask, int deviceid); - void UpdateCursor(); + void UpdateCursor(bool should_center_mouse); public: void UpdateInput() override;