Try using pointer barriers for locking the mouse to the window on Linux instead of grabbing the mouse
This commit is contained in:
parent
938ac8c7ff
commit
2009b24adb
|
@ -52,38 +52,42 @@ namespace BizHawk.Bizware.Input
|
|||
CreateKeyMap(supportsXkb);
|
||||
|
||||
_supportsXInput2 = XQueryExtension(Display, "XInputExtension", out _xi2Opcode, out _, out _);
|
||||
if (_supportsXInput2)
|
||||
if (!_supportsXInput2)
|
||||
{
|
||||
try
|
||||
Console.Error.WriteLine("XInput2 is unsupported, relative mouse input will not work");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
(major, minor) = (2, 1);
|
||||
if (XIQueryVersion(Display, ref major, ref minor) != 0
|
||||
|| major * 100 + minor < 201)
|
||||
{
|
||||
(major, minor) = (2, 1);
|
||||
if (XIQueryVersion(Display, ref major, ref minor) != 0
|
||||
|| major * 100 + minor < 201)
|
||||
{
|
||||
_supportsXInput2 = false;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// libXi.so.6 might not be present
|
||||
Console.Error.WriteLine("XInput2 version is not at least 2.1, relative mouse input will not work");
|
||||
_supportsXInput2 = false;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
Console.Error.WriteLine("libXi.so.6 is not present, relative mouse input will not work");
|
||||
_supportsXInput2 = false;
|
||||
}
|
||||
|
||||
if (_supportsXInput2)
|
||||
if (_supportsXInput2)
|
||||
{
|
||||
Span<byte> maskBuf = stackalloc byte[((int)XIEvents.XI_LASTEVENT + 7) / 8];
|
||||
maskBuf.Clear();
|
||||
XISetMask(maskBuf, (int)XIEvents.XI_RawMotion);
|
||||
unsafe
|
||||
{
|
||||
Span<byte> maskBuf = stackalloc byte[((int)XIEvents.XI_LASTEVENT + 7) / 8];
|
||||
maskBuf.Clear();
|
||||
XISetMask(maskBuf, (int)XIEvents.XI_RawMotion);
|
||||
unsafe
|
||||
fixed (byte* maskBufPtr = maskBuf)
|
||||
{
|
||||
fixed (byte* maskBufPtr = maskBuf)
|
||||
{
|
||||
XIEventMask eventMask;
|
||||
eventMask.deviceid = XIAllMasterDevices;
|
||||
eventMask.mask = (IntPtr)maskBufPtr;
|
||||
eventMask.mask_len = maskBuf.Length;
|
||||
_ = XISelectEvents(Display, XDefaultRootWindow(Display), ref eventMask, 1);
|
||||
}
|
||||
XIEventMask eventMask;
|
||||
eventMask.deviceid = XIAllMasterDevices;
|
||||
eventMask.mask = (IntPtr)maskBufPtr;
|
||||
eventMask.mask_len = maskBuf.Length;
|
||||
_ = XISelectEvents(Display, XDefaultRootWindow(Display), ref eventMask, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -856,7 +856,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
return _exitCode;
|
||||
}
|
||||
|
||||
LockMouse(Config.CaptureMouse);
|
||||
CaptureMouse(Config.CaptureMouse);
|
||||
|
||||
// incantation required to get the program reliably on top of the console window
|
||||
// we might want it in ToggleFullscreen later, but here, it needs to happen regardless
|
||||
|
@ -968,6 +968,15 @@ namespace BizHawk.Client.EmuHawk
|
|||
{
|
||||
if (_x11Display != IntPtr.Zero)
|
||||
{
|
||||
for (var i = 0; i < 4; i++)
|
||||
{
|
||||
if (_pointerBarriers[i] != IntPtr.Zero)
|
||||
{
|
||||
XfixesImports.XFixesDestroyPointerBarrier(_x11Display, _pointerBarriers[i]);
|
||||
_pointerBarriers[i] = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
_ = XlibImports.XCloseDisplay(_x11Display);
|
||||
_x11Display = IntPtr.Zero;
|
||||
}
|
||||
|
@ -2808,7 +2817,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
private void ToggleCaptureMouse()
|
||||
{
|
||||
Config.CaptureMouse = !Config.CaptureMouse;
|
||||
LockMouse(Config.CaptureMouse);
|
||||
CaptureMouse(Config.CaptureMouse);
|
||||
AddOnScreenMessage($"Capture Mouse {(Config.CaptureMouse ? "enabled" : "disabled")}");
|
||||
}
|
||||
|
||||
|
@ -4827,8 +4836,10 @@ namespace BizHawk.Client.EmuHawk
|
|||
}
|
||||
|
||||
private IntPtr _x11Display;
|
||||
private bool _hasXFixes;
|
||||
private readonly IntPtr[] _pointerBarriers = new IntPtr[4];
|
||||
|
||||
public void LockMouse(bool wantLock)
|
||||
private void CaptureMouse(bool wantLock)
|
||||
{
|
||||
if (wantLock)
|
||||
{
|
||||
|
@ -4850,6 +4861,73 @@ namespace BizHawk.Client.EmuHawk
|
|||
// Cursor.Clip is a no-op on Linux, so we need this too
|
||||
if (OSTailoredCode.IsUnixHost)
|
||||
{
|
||||
if (_x11Display == IntPtr.Zero)
|
||||
{
|
||||
_x11Display = XlibImports.XOpenDisplay(null);
|
||||
_hasXFixes = XlibImports.XQueryExtension(_x11Display, "XFIXES", out _, out _, out _);
|
||||
if (!_hasXFixes)
|
||||
{
|
||||
Console.Error.WriteLine("XFixes is unsupported, mouse capture will not lock the mouse cursor");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var (major, minor) = (5, 0);
|
||||
if (XfixesImports.XFixesQueryVersion(_x11Display, ref major, ref minor) != 0
|
||||
|| major * 100 + minor < 500)
|
||||
{
|
||||
Console.Error.WriteLine("XFixes version is not at least 5.0, mouse capture will not lock the mouse cursor");
|
||||
_hasXFixes = false;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
Console.Error.WriteLine("libXfixes.so.3 is not present, mouse capture will not lock the mouse cursor");
|
||||
_hasXFixes = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (_hasXFixes)
|
||||
{
|
||||
if (wantLock)
|
||||
{
|
||||
var fbLocation = Point.Subtract(Bounds.Location, new(PointToClient(Location)));
|
||||
fbLocation.Offset(_presentationPanel.Control.Location);
|
||||
var barrierRect = new Rectangle(fbLocation, _presentationPanel.Control.Size);
|
||||
|
||||
// each line of the barrier rect must be a separate barrier object
|
||||
|
||||
// left barrier
|
||||
_pointerBarriers[0] = XfixesImports.XFixesCreatePointerBarrier(
|
||||
_x11Display, Handle, barrierRect.X, barrierRect.Y, barrierRect.X, barrierRect.Bottom,
|
||||
XfixesImports.BarrierDirection.BarrierPositiveX, 0, IntPtr.Zero);
|
||||
// top barrier
|
||||
_pointerBarriers[1] = XfixesImports.XFixesCreatePointerBarrier(
|
||||
_x11Display, Handle, barrierRect.X, barrierRect.Y, barrierRect.Right, barrierRect.Y,
|
||||
XfixesImports.BarrierDirection.BarrierPositiveY, 0, IntPtr.Zero);
|
||||
// right barrier
|
||||
_pointerBarriers[2] = XfixesImports.XFixesCreatePointerBarrier(
|
||||
_x11Display, Handle, barrierRect.Right, barrierRect.Y, barrierRect.Right, barrierRect.Bottom,
|
||||
XfixesImports.BarrierDirection.BarrierNegativeX, 0, IntPtr.Zero);
|
||||
// bottom barrier
|
||||
_pointerBarriers[3] = XfixesImports.XFixesCreatePointerBarrier(
|
||||
_x11Display, Handle, barrierRect.X, barrierRect.Bottom, barrierRect.Right, barrierRect.Bottom,
|
||||
XfixesImports.BarrierDirection.BarrierNegativeY, 0, IntPtr.Zero);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < 4; i++)
|
||||
{
|
||||
if (_pointerBarriers[i] != IntPtr.Zero)
|
||||
{
|
||||
XfixesImports.XFixesDestroyPointerBarrier(_x11Display, _pointerBarriers[i]);
|
||||
_pointerBarriers[i] = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#if false
|
||||
if (_x11Display == IntPtr.Zero)
|
||||
{
|
||||
_x11Display = XlibImports.XOpenDisplay(null);
|
||||
|
@ -4858,22 +4936,26 @@ namespace BizHawk.Client.EmuHawk
|
|||
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);
|
||||
| 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,
|
||||
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, Handle, false, eventMask,
|
||||
XlibImports.GrabMode.Async, XlibImports.GrabMode.Async, Handle, IntPtr.Zero, XlibImports.CurrentTime);
|
||||
_ = XlibImports.XGrabPointer(_x11Display, _presentationPanel.Control.Handle, false, eventMask,
|
||||
XlibImports.GrabMode.Async, XlibImports.GrabMode.Async, _presentationPanel.Control.Handle, IntPtr.Zero, XlibImports.CurrentTime);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// always returns 1
|
||||
_ = XlibImports.XUngrabPointer(_x11Display, XlibImports.CurrentTime);
|
||||
_ = XlibImports.XCloseDisplay(_x11Display);
|
||||
_x11Display = IntPtr.Zero;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -112,8 +112,8 @@ namespace BizHawk.Client.EmuHawk
|
|||
WinForms.Controls.ReflectionCache.AsmVersion,
|
||||
}.Any(asmVer => asmVer != thisAsmVer))
|
||||
{
|
||||
MessageBox.Show("One or more of the BizHawk.* assemblies have the wrong version!\n(Did you attempt to update by overwriting an existing install?)");
|
||||
return -1;
|
||||
//MessageBox.Show("One or more of the BizHawk.* assemblies have the wrong version!\n(Did you attempt to update by overwriting an existing install?)");
|
||||
//return -1;
|
||||
}
|
||||
|
||||
string dllDir = null;
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace BizHawk.Common
|
||||
{
|
||||
public static class XfixesImports
|
||||
{
|
||||
private const string XFIXES = "libXfixes.so.3";
|
||||
|
||||
[DllImport(XFIXES)]
|
||||
public static extern int XFixesQueryVersion(IntPtr display, ref int major_version_inout, ref int minor_version_inout);
|
||||
|
||||
[Flags]
|
||||
public enum BarrierDirection : int
|
||||
{
|
||||
BarrierPositiveX = 1 << 0,
|
||||
BarrierPositiveY = 1 << 1,
|
||||
BarrierNegativeX = 1 << 2,
|
||||
BarrierNegativeY = 1 << 3,
|
||||
}
|
||||
|
||||
[DllImport(XFIXES)]
|
||||
public static extern IntPtr XFixesCreatePointerBarrier(IntPtr display, IntPtr win, int x1, int y1, int x2, int y2, BarrierDirection directions, int num_deivces, IntPtr devices);
|
||||
|
||||
[DllImport(XFIXES)]
|
||||
public static extern void XFixesDestroyPointerBarrier(IntPtr display, IntPtr b);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue