Make Linux mouse capture work more or less properly
There's 3 approaches here, of which only 1 appears to work more or less correctly 1. Use XFixes "pointer barriers." Introduced in XFixes version 5.0 (end of 2010), these effectively allow 1:1 mapping with Windows ClipCursor, giving behavior BizHawk wants. 2. Use XGrabPointer, with the confine_to argument set to the presentation panel. This mostly works, however, it changes how some events operate and for whatever reason prevents Mono from responding to mouse buttons. This approach can be used with 1. too for a simple warp of the mouse cursor over to wherever the window is. 3. Use Mono's internal CaptureWithConfine function. This is just internally using XGrabPointer with the confine_to argument. Somehow it makes mouse buttons work, but it ends up working too well as it can respond to the menu bar somehow, and interacting with such or the right click menu cancels the grab (seems to be internal Mono code doing such in this case here?). Note a lot of weirdness with previous code came down to testing being in a VM and mouse integration being enabled. That apparently just prevents any kind of mouse warping from happening so nothing appeared to work correctly. Disabling such allows these approaches to work as above, also makes relative mouse values sane (previous values with mouse integration were completely bonkers). TODO: Need to check if this works on XWayland (maybe it does?). Also need to refresh mouse capture on window resize and move (needed in general for both Windows and Linux)
This commit is contained in:
parent
2f2c3e4f68
commit
c3b0f46ae3
|
@ -4839,9 +4839,19 @@ namespace BizHawk.Client.EmuHawk
|
|||
private bool _hasXFixes;
|
||||
private readonly IntPtr[] _pointerBarriers = new IntPtr[4];
|
||||
|
||||
private void CaptureMouse(bool wantLock)
|
||||
#if false
|
||||
private delegate void CaptureWithConfineDelegate(Control control, Control confineWindow);
|
||||
|
||||
private static readonly Lazy<CaptureWithConfineDelegate> _captureWithConfine = new(() =>
|
||||
{
|
||||
if (wantLock)
|
||||
var mi = typeof(Control).GetMethod("CaptureWithConfine", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
return (CaptureWithConfineDelegate)Delegate.CreateDelegate(typeof(CaptureWithConfineDelegate), mi!);
|
||||
});
|
||||
#endif
|
||||
|
||||
private void CaptureMouse(bool wantCapture)
|
||||
{
|
||||
if (wantCapture)
|
||||
{
|
||||
var fbLocation = Point.Subtract(Bounds.Location, new(PointToClient(Location)));
|
||||
fbLocation.Offset(_presentationPanel.Control.Location);
|
||||
|
@ -4861,6 +4871,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
// Cursor.Clip is a no-op on Linux, so we need this too
|
||||
if (OSTailoredCode.IsUnixHost)
|
||||
{
|
||||
#if true
|
||||
if (_x11Display == IntPtr.Zero)
|
||||
{
|
||||
_x11Display = XlibImports.XOpenDisplay(null);
|
||||
|
@ -4893,7 +4904,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
if (_hasXFixes)
|
||||
{
|
||||
if (wantLock)
|
||||
if (wantCapture)
|
||||
{
|
||||
var fbLocation = Point.Subtract(Bounds.Location, new(PointToClient(Location)));
|
||||
fbLocation.Offset(_presentationPanel.Control.Location);
|
||||
|
@ -4921,6 +4932,12 @@ namespace BizHawk.Client.EmuHawk
|
|||
_pointerBarriers[3] = XfixesImports.XFixesCreatePointerBarrier(
|
||||
_x11Display, Handle, screenRect.X, barrierRect.Bottom, screenRect.Right, barrierRect.Bottom,
|
||||
XfixesImports.BarrierDirection.BarrierNegativeY, 0, IntPtr.Zero);
|
||||
|
||||
// after creating pointer barriers, warp our cursor over to the presentation panel
|
||||
_ = XlibImports.XUngrabPointer(_x11Display, XlibImports.CurrentTime); // just in case someone else has grabbed the pointer
|
||||
_ = XlibImports.XGrabPointer(_x11Display, Handle, false, 0,
|
||||
XlibImports.GrabMode.Async, XlibImports.GrabMode.Async, _presentationPanel.Control.Handle, IntPtr.Zero, XlibImports.CurrentTime);
|
||||
_ = XlibImports.XUngrabPointer(_x11Display, XlibImports.CurrentTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -4933,25 +4950,29 @@ namespace BizHawk.Client.EmuHawk
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ = XlibImports.XFlush(_x11Display);
|
||||
}
|
||||
#if false
|
||||
#elif false
|
||||
// approach just using XGrabPointer
|
||||
// (doesn't work, Mono won't respond to mouse buttons for whatever reason)
|
||||
if (_x11Display == IntPtr.Zero)
|
||||
{
|
||||
_x11Display = XlibImports.XOpenDisplay(null);
|
||||
}
|
||||
|
||||
if (wantLock)
|
||||
if (wantCapture)
|
||||
{
|
||||
const XlibImports.EventMask eventMask = XlibImports.EventMask.ButtonPressMask | XlibImports.EventMask.ButtonMotionMask
|
||||
| XlibImports.EventMask.ButtonReleaseMask | XlibImports.EventMask.PointerMotionMask | XlibImports.EventMask.PointerMotionHintMask
|
||||
| XlibImports.EventMask.EnterWindowMask | XlibImports.EventMask.LeaveWindowMask | XlibImports.EventMask.FocusChangeMask;
|
||||
var grabResult = XlibImports.XGrabPointer(_x11Display, _presentationPanel.Control.Handle, false, eventMask,
|
||||
var grabResult = XlibImports.XGrabPointer(_x11Display, Handle, false, eventMask,
|
||||
XlibImports.GrabMode.Async, XlibImports.GrabMode.Async, _presentationPanel.Control.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, _presentationPanel.Control.Handle, false, eventMask,
|
||||
_ = XlibImports.XGrabPointer(_x11Display, Handle, false, eventMask,
|
||||
XlibImports.GrabMode.Async, XlibImports.GrabMode.Async, _presentationPanel.Control.Handle, IntPtr.Zero, XlibImports.CurrentTime);
|
||||
}
|
||||
}
|
||||
|
@ -4959,8 +4980,20 @@ namespace BizHawk.Client.EmuHawk
|
|||
{
|
||||
// always returns 1
|
||||
_ = XlibImports.XUngrabPointer(_x11Display, XlibImports.CurrentTime);
|
||||
_ = XlibImports.XCloseDisplay(_x11Display);
|
||||
_x11Display = IntPtr.Zero;
|
||||
}
|
||||
|
||||
_ = XlibImports.XFlush(_x11Display);
|
||||
#else
|
||||
// approach using internal Mono function that ends up just using XGrabPointer
|
||||
// (doesn't work either, while Mono does respond to mouse buttons, it ends up being able to respond to the top menu bar somehow)
|
||||
// (also interacting with other windows (e.g. right click menu) cancels the capture)
|
||||
if (wantCapture)
|
||||
{
|
||||
_captureWithConfine.Value(this, _presentationPanel.Control);
|
||||
}
|
||||
else
|
||||
{
|
||||
Capture = false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -76,6 +76,9 @@ namespace BizHawk.Common
|
|||
[DllImport(XLIB)]
|
||||
public static extern IntPtr XDefaultRootWindow(IntPtr display);
|
||||
|
||||
[DllImport(XLIB)]
|
||||
public static extern int XFlush(IntPtr display);
|
||||
|
||||
[DllImport(XLIB)]
|
||||
public static extern int XPending(IntPtr display);
|
||||
|
||||
|
|
Loading…
Reference in New Issue