mirror of https://github.com/PCSX2/pcsx2.git
HostSys: Implement platform Specific mouse lock osx/windows/x11
This commit is contained in:
parent
75c0236e1e
commit
e252cb6643
|
@ -168,6 +168,7 @@ else()
|
|||
${DBUS_LINK_LIBRARIES}
|
||||
X11::X11
|
||||
X11::Xrandr
|
||||
X11::Xi
|
||||
)
|
||||
if(USE_BACKTRACE)
|
||||
target_compile_definitions(common PRIVATE "HAS_LIBBACKTRACE=1")
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <mach/task.h>
|
||||
#include <mach/vm_map.h>
|
||||
#include <mutex>
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#include <IOKit/pwr_mgt/IOPMLib.h>
|
||||
|
||||
// Darwin (OSX) is a bit different from Linux when requesting properties of
|
||||
|
@ -127,6 +128,69 @@ bool Common::InhibitScreensaver(bool inhibit)
|
|||
return true;
|
||||
}
|
||||
|
||||
void Common::SetMousePosition(int x, int y)
|
||||
{
|
||||
// Little bit ugly but;
|
||||
// Creating mouse move events and posting them wasn't very reliable.
|
||||
// Calling CGWarpMouseCursorPosition without CGAssociateMouseAndMouseCursorPosition(false)
|
||||
// ends up with the cursor feeling "sticky".
|
||||
CGAssociateMouseAndMouseCursorPosition(false);
|
||||
CGWarpMouseCursorPosition(CGPointMake(x, y));
|
||||
CGAssociateMouseAndMouseCursorPosition(true); // The default state
|
||||
return;
|
||||
}
|
||||
|
||||
CFMachPortRef mouseEventTap = nullptr;
|
||||
CFRunLoopSourceRef mouseRunLoopSource = nullptr;
|
||||
|
||||
static std::function<void(int, int)> fnMouseMoveCb;
|
||||
CGEventRef mouseMoveCallback(CGEventTapProxy, CGEventType type, CGEventRef event, void* arg)
|
||||
{
|
||||
if (type == kCGEventMouseMoved)
|
||||
{
|
||||
const CGPoint location = CGEventGetLocation(event);
|
||||
fnMouseMoveCb(location.x, location.y);
|
||||
}
|
||||
return event;
|
||||
}
|
||||
|
||||
bool Common::AttachMousePositionCb(std::function<void(int, int)> cb)
|
||||
{
|
||||
if (!AXIsProcessTrusted())
|
||||
{
|
||||
Console.Warning("Process isn't trusted with accessibility permissions. Mouse tracking will not work!");
|
||||
}
|
||||
|
||||
fnMouseMoveCb = cb;
|
||||
mouseEventTap = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap, kCGEventTapOptionDefault,
|
||||
CGEventMaskBit(kCGEventMouseMoved), mouseMoveCallback, nullptr);
|
||||
if (!mouseEventTap)
|
||||
{
|
||||
Console.Warning("Unable to create mouse moved event tap. Mouse tracking will not work!");
|
||||
return false;
|
||||
}
|
||||
|
||||
mouseRunLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, mouseEventTap, 0);
|
||||
CFRunLoopAddSource(CFRunLoopGetCurrent(), mouseRunLoopSource, kCFRunLoopCommonModes);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Common::DetachMousePositionCb()
|
||||
{
|
||||
if (mouseRunLoopSource)
|
||||
{
|
||||
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), mouseRunLoopSource, kCFRunLoopCommonModes);
|
||||
CFRelease(mouseRunLoopSource);
|
||||
}
|
||||
if (mouseEventTap)
|
||||
{
|
||||
CFRelease(mouseEventTap);
|
||||
}
|
||||
mouseRunLoopSource = nullptr;
|
||||
mouseEventTap = nullptr;
|
||||
}
|
||||
|
||||
void Threading::Sleep(int ms)
|
||||
{
|
||||
usleep(1000 * ms);
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "common/Pcsx2Defs.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
@ -198,4 +199,8 @@ namespace Common
|
|||
/// Abstracts platform-specific code for asynchronously playing a sound.
|
||||
/// On Windows, this will use PlaySound(). On Linux, it will shell out to aplay. On MacOS, it uses NSSound.
|
||||
bool PlaySoundAsync(const char* path);
|
||||
|
||||
void SetMousePosition(int x, int y);
|
||||
bool AttachMousePositionCb(std::function<void(int,int)> cb);
|
||||
void DetachMousePositionCb();
|
||||
} // namespace Common
|
||||
|
|
|
@ -13,17 +13,20 @@
|
|||
|
||||
#include "fmt/format.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <optional>
|
||||
#include <dbus/dbus.h>
|
||||
#include <spawn.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <dbus/dbus.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/extensions/XInput2.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <ctype.h>
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
|
||||
// Returns 0 on failure (not supported by the operating system).
|
||||
u64 GetPhysicalMemory()
|
||||
|
@ -177,6 +180,111 @@ bool Common::InhibitScreensaver(bool inhibit)
|
|||
return SetScreensaverInhibitDBus(inhibit, "PCSX2", "PCSX2 VM is running.");
|
||||
}
|
||||
|
||||
void Common::SetMousePosition(int x, int y)
|
||||
{
|
||||
Display* display = XOpenDisplay(nullptr);
|
||||
if (!display)
|
||||
return;
|
||||
|
||||
Window root = DefaultRootWindow(display);
|
||||
XWarpPointer(display, None, root, 0, 0, 0, 0, x, y);
|
||||
XFlush(display);
|
||||
|
||||
XCloseDisplay(display);
|
||||
}
|
||||
|
||||
static std::function<void(int, int)> fnMouseMoveCb;
|
||||
static std::atomic<bool> trackingMouse = false;
|
||||
static std::thread mouseThread;
|
||||
|
||||
void mouseEventLoop()
|
||||
{
|
||||
Threading::SetNameOfCurrentThread("X11 Mouse Thread");
|
||||
Display* display = XOpenDisplay(nullptr);
|
||||
if (!display)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int opcode, eventcode, error;
|
||||
if (!XQueryExtension(display, "XInputExtension", &opcode, &eventcode, &error))
|
||||
{
|
||||
XCloseDisplay(display);
|
||||
return;
|
||||
}
|
||||
|
||||
const Window root = DefaultRootWindow(display);
|
||||
XIEventMask evmask;
|
||||
unsigned char mask[(XI_LASTEVENT + 7) / 8] = {0};
|
||||
|
||||
evmask.deviceid = XIAllDevices;
|
||||
evmask.mask_len = sizeof(mask);
|
||||
evmask.mask = mask;
|
||||
XISetMask(mask, XI_RawMotion);
|
||||
|
||||
XISelectEvents(display, root, &evmask, 1);
|
||||
XSync(display, False);
|
||||
|
||||
XEvent event;
|
||||
while (trackingMouse)
|
||||
{
|
||||
// XNextEvent is blocking, this is a zombie process risk if no events arrive
|
||||
// while we are trying to shutdown.
|
||||
// https://nrk.neocities.org/articles/x11-timeout-with-xsyncalarm might be
|
||||
// a better solution than using XPending.
|
||||
if (!XPending(display))
|
||||
{
|
||||
Threading::Sleep(1);
|
||||
Threading::SpinWait();
|
||||
continue;
|
||||
}
|
||||
|
||||
XNextEvent(display, &event);
|
||||
if (event.xcookie.type == GenericEvent &&
|
||||
event.xcookie.extension == opcode &&
|
||||
XGetEventData(display, &event.xcookie))
|
||||
{
|
||||
XIRawEvent* raw_event = reinterpret_cast<XIRawEvent*>(event.xcookie.data);
|
||||
if (raw_event->evtype == XI_RawMotion)
|
||||
{
|
||||
Window w;
|
||||
int root_x, root_y, win_x, win_y;
|
||||
unsigned int mask;
|
||||
XQueryPointer(display, root, &w, &w, &root_x, &root_y, &win_x, &win_y, &mask);
|
||||
|
||||
if (fnMouseMoveCb)
|
||||
fnMouseMoveCb(root_x, root_y);
|
||||
}
|
||||
XFreeEventData(display, &event.xcookie);
|
||||
}
|
||||
}
|
||||
|
||||
XCloseDisplay(display);
|
||||
}
|
||||
|
||||
bool Common::AttachMousePositionCb(std::function<void(int, int)> cb)
|
||||
{
|
||||
fnMouseMoveCb = cb;
|
||||
|
||||
if (trackingMouse)
|
||||
return true;
|
||||
|
||||
trackingMouse = true;
|
||||
mouseThread = std::thread(mouseEventLoop);
|
||||
mouseThread.detach();
|
||||
return true;
|
||||
}
|
||||
|
||||
void Common::DetachMousePositionCb()
|
||||
{
|
||||
trackingMouse = false;
|
||||
fnMouseMoveCb = nullptr;
|
||||
if (mouseThread.joinable())
|
||||
{
|
||||
mouseThread.join();
|
||||
}
|
||||
}
|
||||
|
||||
bool Common::PlaySoundAsync(const char* path)
|
||||
{
|
||||
#ifdef __linux__
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#include "common/Console.h"
|
||||
#include "common/FileSystem.h"
|
||||
#include "common/HostSys.h"
|
||||
#include "common/RedtapeWindows.h"
|
||||
|
@ -86,6 +87,61 @@ bool Common::InhibitScreensaver(bool inhibit)
|
|||
return true;
|
||||
}
|
||||
|
||||
void Common::SetMousePosition(int x, int y)
|
||||
{
|
||||
SetCursorPos(x, y);
|
||||
}
|
||||
|
||||
/*
|
||||
static HHOOK mouseHook = nullptr;
|
||||
static std::function<void(int, int)> fnMouseMoveCb;
|
||||
LRESULT CALLBACK Mousecb(int nCode, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
if (nCode >= 0 && wParam == WM_MOUSEMOVE)
|
||||
{
|
||||
MSLLHOOKSTRUCT* mouse = (MSLLHOOKSTRUCT*)lParam;
|
||||
fnMouseMoveCb(mouse->pt.x, mouse->pt.y);
|
||||
}
|
||||
return CallNextHookEx(mouseHook, nCode, wParam, lParam);
|
||||
}
|
||||
*/
|
||||
|
||||
// This (and the above) works, but is not recommended on Windows and is only here for consistency.
|
||||
// Defer to using raw input instead.
|
||||
bool Common::AttachMousePositionCb(std::function<void(int, int)> cb)
|
||||
{
|
||||
/*
|
||||
if (mouseHook)
|
||||
Common::DetachMousePositionCb();
|
||||
|
||||
fnMouseMoveCb = cb;
|
||||
mouseHook = SetWindowsHookEx(WH_MOUSE_LL, Mousecb, GetModuleHandle(NULL), 0);
|
||||
if (!mouseHook)
|
||||
{
|
||||
Console.Warning("Failed to set mouse hook: %d", GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(PCSX2_DEBUG) || defined(PCSX2_DEVBUILD)
|
||||
static bool warned = false;
|
||||
if (!warned)
|
||||
{
|
||||
Console.Warning("Mouse hooks are enabled, and this isn't a release build! Using a debugger, or loading symbols, _will_ stall the hook and cause global mouse lag.");
|
||||
warned = true;
|
||||
}
|
||||
#endif
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
void Common::DetachMousePositionCb()
|
||||
{
|
||||
/*
|
||||
UnhookWindowsHookEx(mouseHook);
|
||||
mouseHook = nullptr;
|
||||
*/
|
||||
}
|
||||
|
||||
bool Common::PlaySoundAsync(const char* path)
|
||||
{
|
||||
const std::wstring wpath = FileSystem::GetWin32Path(path);
|
||||
|
|
|
@ -107,7 +107,6 @@ MainWindow::MainWindow()
|
|||
#if !defined(_WIN32) && !defined(__APPLE__)
|
||||
s_use_central_widget = DisplayContainer::isRunningOnWayland();
|
||||
#endif
|
||||
createCheckMousePositionTimer();
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
|
@ -116,6 +115,8 @@ MainWindow::~MainWindow()
|
|||
cancelGameListRefresh();
|
||||
destroySubWindows();
|
||||
|
||||
Common::DetachMousePositionCb();
|
||||
|
||||
// we compare here, since recreate destroys the window later
|
||||
if (g_main_window == this)
|
||||
g_main_window = nullptr;
|
||||
|
@ -151,6 +152,9 @@ void MainWindow::initialize()
|
|||
#ifdef _WIN32
|
||||
registerForDeviceNotifications();
|
||||
#endif
|
||||
|
||||
if (Host::GetBoolSettingValue("EmuCore", "EnableMouseLock", false))
|
||||
setupMouseMoveHandler();
|
||||
}
|
||||
|
||||
// TODO: Figure out how to set this in the .ui file
|
||||
|
@ -1072,12 +1076,12 @@ bool MainWindow::shouldHideMainWindow() const
|
|||
QtHost::InNoGUIMode();
|
||||
}
|
||||
|
||||
bool MainWindow::shouldMouseGrab() const
|
||||
bool MainWindow::shouldMouseLock() const
|
||||
{
|
||||
if (!s_vm_valid || s_vm_paused)
|
||||
return false;
|
||||
|
||||
if (!Host::GetBoolSettingValue("EmuCore", "EnableMouseGrab", false))
|
||||
if (!Host::GetBoolSettingValue("EmuCore", "EnableMouseLock", false))
|
||||
return false;
|
||||
|
||||
bool windowsHidden = (!m_debugger_window || m_debugger_window->isHidden()) &&
|
||||
|
@ -2250,6 +2254,15 @@ void MainWindow::registerForDeviceNotifications()
|
|||
DEV_BROADCAST_DEVICEINTERFACE_W filter = {sizeof(DEV_BROADCAST_DEVICEINTERFACE_W), DBT_DEVTYP_DEVICEINTERFACE};
|
||||
m_device_notification_handle =
|
||||
RegisterDeviceNotificationW((HANDLE)winId(), &filter, DEVICE_NOTIFY_WINDOW_HANDLE | DEVICE_NOTIFY_ALL_INTERFACE_CLASSES);
|
||||
|
||||
// Set up the raw input device for mouse grabbing
|
||||
RAWINPUTDEVICE rid;
|
||||
rid.usUsagePage = 0x01; // Generic desktop controls
|
||||
rid.usUsage = 0x02; // Mouse
|
||||
rid.dwFlags = RIDEV_INPUTSINK;
|
||||
rid.hwndTarget = (HWND)winId();
|
||||
|
||||
RegisterRawInputDevices(&rid, 1, sizeof(RAWINPUTDEVICE));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -2278,6 +2291,26 @@ bool MainWindow::nativeEvent(const QByteArray& eventType, void* message, qintptr
|
|||
*result = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (msg->message == WM_INPUT)
|
||||
{
|
||||
UINT dwSize = 40;
|
||||
static BYTE lpb[40];
|
||||
if (GetRawInputData((HRAWINPUT)msg->lParam, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER)))
|
||||
{
|
||||
const RAWINPUT* raw = (RAWINPUT*)lpb;
|
||||
if (raw->header.dwType == RIM_TYPEMOUSE)
|
||||
{
|
||||
const RAWMOUSE& mouse = raw->data.mouse;
|
||||
if (mouse.usFlags == MOUSE_MOVE_ABSOLUTE || mouse.usFlags == MOUSE_MOVE_RELATIVE)
|
||||
{
|
||||
POINT cursorPos;
|
||||
GetCursorPos(&cursorPos);
|
||||
checkMousePosition(cursorPos.x, cursorPos.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return QMainWindow::nativeEvent(eventType, message, result);
|
||||
|
@ -2557,35 +2590,53 @@ QWidget* MainWindow::getDisplayContainer() const
|
|||
return (m_display_container ? static_cast<QWidget*>(m_display_container) : static_cast<QWidget*>(m_display_widget));
|
||||
}
|
||||
|
||||
void MainWindow::createCheckMousePositionTimer()
|
||||
void MainWindow::setupMouseMoveHandler()
|
||||
{
|
||||
m_mouse_check_timer = new QTimer(this);
|
||||
connect(m_mouse_check_timer, &QTimer::timeout, this, &MainWindow::checkMousePosition);
|
||||
m_mouse_check_timer->start(16);
|
||||
auto mouse_cb_fn = [](int x, int y)
|
||||
{
|
||||
if(g_main_window)
|
||||
g_main_window->checkMousePosition(x, y);
|
||||
};
|
||||
|
||||
if(!Common::AttachMousePositionCb(mouse_cb_fn))
|
||||
{
|
||||
Console.Warning("Unable to setup mouse position cb!");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void MainWindow::checkMousePosition()
|
||||
void MainWindow::checkMousePosition(int x, int y)
|
||||
{
|
||||
if (!shouldMouseGrab())
|
||||
if (!shouldMouseLock())
|
||||
return;
|
||||
|
||||
QPoint globalCursorPos = QCursor::pos();
|
||||
const QRect& windowBounds = isRenderingFullscreen() ? screen()->geometry() : geometry();
|
||||
|
||||
const QPoint globalCursorPos = {x, y};
|
||||
QRect windowBounds = isRenderingFullscreen() ? screen()->geometry() : geometry();
|
||||
if (windowBounds.contains(globalCursorPos))
|
||||
return;
|
||||
|
||||
QCursor::setPos(
|
||||
Common::SetMousePosition(
|
||||
std::clamp(globalCursorPos.x(), windowBounds.left(), windowBounds.right()),
|
||||
std::clamp(globalCursorPos.y(), windowBounds.top(), windowBounds.bottom()));
|
||||
}
|
||||
|
||||
void MainWindow::mouseMoveEvent(QMouseEvent* event)
|
||||
{
|
||||
QWidget::mouseMoveEvent(event);
|
||||
/*
|
||||
Provided below is how we would handle this if we were using low level hooks (What is used in Common::AttachMouseCb)
|
||||
We currently use rawmouse on Windows, so Common::SetMousePosition called directly works fine.
|
||||
*/
|
||||
#if 0
|
||||
// We are currently in a low level hook. SetCursorPos here (what is in Common::SetMousePosition) will not work!
|
||||
// Let's (a)buse Qt's event loop to dispatch the call at a later time, outside of the hook.
|
||||
QMetaObject::invokeMethod(
|
||||
this, [=]() {
|
||||
Common::SetMousePosition(
|
||||
std::clamp(globalCursorPos.x(), windowBounds.left(), windowBounds.right()),
|
||||
std::clamp(globalCursorPos.y(), windowBounds.top(), windowBounds.bottom()));
|
||||
},
|
||||
Qt::QueuedConnection);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void MainWindow::saveDisplayWindowGeometryToConfig()
|
||||
{
|
||||
QWidget* container = getDisplayContainer();
|
||||
|
|
|
@ -104,7 +104,7 @@ public:
|
|||
void rescanFile(const std::string& path);
|
||||
|
||||
void openDebugger();
|
||||
|
||||
void checkMousePosition(int x, int y);
|
||||
public Q_SLOTS:
|
||||
void checkForUpdates(bool display_message, bool force_check);
|
||||
void refreshGameList(bool invalidate_cache);
|
||||
|
@ -128,7 +128,7 @@ private Q_SLOTS:
|
|||
void mouseModeRequested(bool relative_mode, bool hide_cursor);
|
||||
void releaseRenderWindow();
|
||||
void focusDisplayWidget();
|
||||
void createCheckMousePositionTimer();
|
||||
void setupMouseMoveHandler();
|
||||
void onGameListRefreshComplete();
|
||||
void onGameListRefreshProgress(const QString& status, int current, int total);
|
||||
void onGameListSelectionChanged();
|
||||
|
@ -188,8 +188,6 @@ private Q_SLOTS:
|
|||
void onVMResumed();
|
||||
void onVMStopped();
|
||||
|
||||
void checkMousePosition();
|
||||
|
||||
void onGameChanged(const QString& title, const QString& elf_override, const QString& disc_path,
|
||||
const QString& serial, quint32 disc_crc, quint32 crc);
|
||||
|
||||
|
@ -207,7 +205,6 @@ protected:
|
|||
void dropEvent(QDropEvent* event) override;
|
||||
void moveEvent(QMoveEvent* event) override;
|
||||
void resizeEvent(QResizeEvent* event) override;
|
||||
void mouseMoveEvent(QMouseEvent* event) override;
|
||||
|
||||
#ifdef _WIN32
|
||||
bool nativeEvent(const QByteArray& eventType, void* message, qintptr* result) override;
|
||||
|
@ -242,7 +239,7 @@ private:
|
|||
bool isRenderingToMain() const;
|
||||
bool shouldHideMouseCursor() const;
|
||||
bool shouldHideMainWindow() const;
|
||||
bool shouldMouseGrab() const;
|
||||
bool shouldMouseLock() const;
|
||||
void switchToGameListView();
|
||||
void switchToEmulationView();
|
||||
|
||||
|
@ -315,8 +312,6 @@ private:
|
|||
|
||||
QString m_last_fps_status;
|
||||
|
||||
QTimer* m_mouse_check_timer = nullptr;
|
||||
|
||||
#ifdef _WIN32
|
||||
void* m_device_notification_handle = nullptr;
|
||||
#endif
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "InterfaceSettingsWidget.h"
|
||||
#include "AutoUpdaterDialog.h"
|
||||
#include "Common.h"
|
||||
#include "MainWindow.h"
|
||||
#include "SettingWidgetBinder.h"
|
||||
#include "SettingsWindow.h"
|
||||
|
@ -83,8 +84,13 @@ InterfaceSettingsWidget::InterfaceSettingsWidget(SettingsWindow* dialog, QWidget
|
|||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pauseOnControllerDisconnection, "UI", "PauseOnControllerDisconnection", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.discordPresence, "EmuCore", "EnableDiscordPresence", false);
|
||||
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.mouseGrab, "EmuCore", "EnableMouseGrab", false);
|
||||
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.mouseLock, "EmuCore", "EnableMouseLock", false);
|
||||
connect(m_ui.mouseLock, &QCheckBox::checkStateChanged, [](Qt::CheckState state) {
|
||||
if (state == Qt::Checked)
|
||||
Common::AttachMousePositionCb([](int x, int y) { g_main_window->checkMousePosition(x, y); });
|
||||
else
|
||||
Common::DetachMousePositionCb();
|
||||
});
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.startFullscreen, "UI", "StartFullscreen", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.doubleClickTogglesFullscreen, "UI", "DoubleClickTogglesFullscreen",
|
||||
true);
|
||||
|
@ -167,8 +173,13 @@ InterfaceSettingsWidget::InterfaceSettingsWidget(SettingsWindow* dialog, QWidget
|
|||
m_ui.discordPresence, tr("Enable Discord Presence"), tr("Unchecked"),
|
||||
tr("Shows the game you are currently playing as part of your profile in Discord."));
|
||||
dialog->registerWidgetHelp(
|
||||
<<<<<<< Updated upstream
|
||||
m_ui.mouseGrab, tr("Enable Mouse Grab"), tr("Unchecked"),
|
||||
tr("Locks the mouse cursor to the windows when PCSX2 is in focus."));
|
||||
=======
|
||||
m_ui.mouseLock, tr("Enable Mouse Lock"), tr("Unchecked"),
|
||||
tr("Locks the mouse cursor to the windows when PCSX2 is in focus and all other windows are closed.<br><b>Unavailable on Linux Wayland.</b><br><b>Requires accessibility permissions on macOS.</b>"));
|
||||
>>>>>>> Stashed changes
|
||||
dialog->registerWidgetHelp(
|
||||
m_ui.doubleClickTogglesFullscreen, tr("Double-Click Toggles Fullscreen"), tr("Checked"),
|
||||
tr("Allows switching in and out of fullscreen mode by double-clicking the game window."));
|
||||
|
|
|
@ -51,9 +51,9 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="mouseGrab">
|
||||
<widget class="QCheckBox" name="mouseLock">
|
||||
<property name="text">
|
||||
<string>Enable Mouse Grab</string>
|
||||
<string>Enable Mouse Lock</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
Loading…
Reference in New Issue