Skip to content
Permalink
Browse files
Merge pull request #10858 from AdmiralCurtiss/mouse-center-hotkey
Add hotkey for centering mouse in render window.
  • Loading branch information
AdmiralCurtiss committed Jul 26, 2022
2 parents c7752f8 + d14bd10 commit 9d15a1c
Show file tree
Hide file tree
Showing 9 changed files with 107 additions and 31 deletions.
@@ -35,6 +35,7 @@ constexpr std::array<const char*, NUM_HOTKEYS> s_hotkey_labels{{
_trans("Take Screenshot"),
_trans("Exit"),
_trans("Unlock Cursor"),
_trans("Center Mouse"),
_trans("Activate NetPlay Chat"),
_trans("Control NetPlay Golf Mode"),

@@ -29,6 +29,7 @@ enum Hotkey
HK_SCREENSHOT,
HK_EXIT,
HK_UNLOCK_CURSOR,
HK_CENTER_MOUSE,
HK_ACTIVATE_CHAT,
HK_REQUEST_GOLF_CONTROL,

@@ -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
@@ -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.
@@ -106,6 +106,11 @@ class ControllerInterface : public ciface::Core::DeviceContainer
// 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<void(void)> callback);
void UnregisterDevicesChangedCallback(const HotplugCallbackHandle& handle);
void InvokeDevicesChangedCallbacks() const;
@@ -127,6 +132,7 @@ class ControllerInterface : public ciface::Core::DeviceContainer
std::atomic<int> m_populating_devices_counter;
WindowSystemInfo m_wsi;
std::atomic<float> m_aspect_ratio_adjustment = 1;
std::atomic<bool> m_requested_mouse_centering = false;
};

namespace ciface
@@ -6,7 +6,9 @@
#include <algorithm>

#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.
@@ -8,6 +8,8 @@
#include <Carbon/Carbon.h>
#include <Cocoa/Cocoa.h>

#include "Core/Host.h"

#include "InputCommon/ControllerInterface/ControllerInterface.h"

namespace ciface::Quartz
@@ -192,16 +194,36 @@
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);

if (g_controller_interface.IsMouseCenteringRequested() && Host_RendererHasFocus())
{
m_cursor.x = 0;
m_cursor.y = 0;

const auto window_scale = g_controller_interface.GetWindowInputScale();
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);

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;
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
@@ -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;
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);

XIQueryPointer(m_display, pointer_deviceid, m_window, &root, &child, &root_x, &root_y, &win_x,
&win_y, &button_state, &mods, &group);
if (should_center_mouse)
{
win_x = win_width / 2;
win_y = win_height / 2;

free(button_state.mask);
XIWarpPointer(m_display, pointer_deviceid, None, m_window, 0.0, 0.0, 0, 0, win_x, win_y);

XWindowAttributes win_attribs;
XGetWindowAttributes(m_display, m_window, &win_attribs);
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.
@@ -108,7 +108,7 @@ class KeyboardMouse : public Core::Device

private:
void SelectEventsForDevice(XIEventMask* mask, int deviceid);
void UpdateCursor();
void UpdateCursor(bool should_center_mouse);

public:
void UpdateInput() override;

0 comments on commit 9d15a1c

Please sign in to comment.