From c5029cb2bb510bfdfd10b33c6e1489f57376cc02 Mon Sep 17 00:00:00 2001 From: CasualPokePlayer <50538166+CasualPokePlayer@users.noreply.github.com> Date: Sun, 23 Feb 2025 02:53:36 -0800 Subject: [PATCH] Add function to lock the mouse Unused currently. Cursor.Clip code copied from feos. Hopefully works on Linux too --- src/BizHawk.Client.EmuHawk/MainForm.cs | 54 +++++++++++++++++++++++++ src/BizHawk.Common/LSB/XlibImports.cs | 56 ++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) diff --git a/src/BizHawk.Client.EmuHawk/MainForm.cs b/src/BizHawk.Client.EmuHawk/MainForm.cs index 19d99143d7..d43a3671d7 100644 --- a/src/BizHawk.Client.EmuHawk/MainForm.cs +++ b/src/BizHawk.Client.EmuHawk/MainForm.cs @@ -962,6 +962,15 @@ namespace BizHawk.Client.EmuHawk SingleInstanceDispose(); } + if (OSTailoredCode.IsUnixHost) + { + if (_x11Display != IntPtr.Zero) + { + XlibImports.XCloseDisplay(_x11Display); + _x11Display = IntPtr.Zero; + } + } + base.Dispose(disposing); } @@ -4807,5 +4816,50 @@ namespace BizHawk.Client.EmuHawk RA?.Restart(); } + + private IntPtr _x11Display; + + public void LockMouse(bool wantLock) + { + if (wantLock) + { + var fbLocation = Point.Subtract(Bounds.Location, new(PointToClient(Location))); + fbLocation.Offset(_presentationPanel.Control.Location); + Cursor.Clip = new(fbLocation, _presentationPanel.Control.Size); + } + else + { + Cursor.Clip = Rectangle.Empty; + } + + // Cursor.Clip is a no-op on Linux, so we need this too + if (OSTailoredCode.IsUnixHost) + { + if (_x11Display == IntPtr.Zero) + { + _x11Display = XlibImports.XOpenDisplay(null); + } + + if (wantLock) + { + const XlibImports.EventMask eventMask = XlibImports.EventMask.ButtonPressMask | XlibImports.EventMask.ButtonMotionMask + | XlibImports.EventMask.ButtonReleaseMask | XlibImports.EventMask.PointerMotionMask + | XlibImports.EventMask.PointerMotionHintMask | XlibImports.EventMask.LeaveWindowMask; + var grabResult = XlibImports.XGrabPointer(_x11Display, Handle, false, eventMask, + XlibImports.GrabMode.Async, XlibImports.GrabMode.Async, Handle, IntPtr.Zero, XlibImports.CurrentTime); + if (grabResult == XlibImports.GrabResult.AlreadyGrabbed) + { + // try to grab again after releasing whatever current active grab + _ = XlibImports.XUngrabPointer(_x11Display, XlibImports.CurrentTime); + _ = XlibImports.XGrabPointer(_x11Display, Handle, false, eventMask, + XlibImports.GrabMode.Async, XlibImports.GrabMode.Async, Handle, IntPtr.Zero, XlibImports.CurrentTime); + } + } + else + { + _ = XlibImports.XUngrabPointer(_x11Display, XlibImports.CurrentTime); + } + } + } } } diff --git a/src/BizHawk.Common/LSB/XlibImports.cs b/src/BizHawk.Common/LSB/XlibImports.cs index f760c3196e..ba96ea2fd5 100644 --- a/src/BizHawk.Common/LSB/XlibImports.cs +++ b/src/BizHawk.Common/LSB/XlibImports.cs @@ -138,6 +138,62 @@ namespace BizHawk.Common [DllImport(XLIB)] public static extern int XFreeEventData(IntPtr display, ref XGenericEventCookie cookie); + [Flags] + public enum EventMask : uint + { + NoEventMask = 0, + KeyPressMask = 1 << 0, + KeyReleaseMask = 1 << 1, + ButtonPressMask = 1 << 2, + ButtonReleaseMask = 1 << 3, + EnterWindowMask = 1 << 4, + LeaveWindowMask = 1 << 5, + PointerMotionMask = 1 << 6, + PointerMotionHintMask = 1 << 7, + Button1MotionMask = 1 << 8, + Button2MotionMask = 1 << 9, + Button3MotionMask = 1 << 10, + Button4MotionMask = 1 << 11, + Button5MotionMask = 1 << 12, + ButtonMotionMask = 1 << 13, + KeymapStateMask = 1 << 14, + ExposureMask = 1 << 15, + VisibilityChangeMask = 1 << 16, + StructureNotifyMask = 1 << 17, + ResizeRedirectMask = 1 << 18, + SubstructureNotifyMask = 1 << 19, + SubstructureRedirectMask= 1 << 20, + FocusChangeMask = 1 << 21, + PropertyChangeMask = 1 << 22, + ColormapChangeMask = 1 << 23, + OwnerGrabButtonMask = 1 << 24 + } + + public enum GrabMode : int + { + Sync = 0, + Async = 1, + } + + // special Time + public const uint CurrentTime = 0; + + public enum GrabResult : int + { + GrabSuccess = 0, + AlreadyGrabbed = 1, + GrabInvalidTime = 2, + GrabNotViewable = 3, + GrabFrozen = 4, + } + + [DllImport(XLIB)] + public static extern GrabResult XGrabPointer(IntPtr display, IntPtr grab_window, [MarshalAs(UnmanagedType.Bool)] bool owner_events, + EventMask event_mask, GrabMode pointer_mode, GrabMode keyboard_mode, IntPtr confine_to, IntPtr cursor, nuint time); + + [DllImport(XLIB)] + public static extern int XUngrabPointer(IntPtr display, nuint time); + [DllImport(XLIB)] public static extern unsafe int XQueryKeymap(IntPtr display, byte* keys_return);