[X11] Improved CursorVisible = false behavior

We now use an XGrabPointer to confine the cursor to the window,
instead of the XWarpPointer hack. Fixes issue #28 and #36.
This commit is contained in:
thefiddler 2014-05-13 22:31:45 +02:00
parent 7e3b99c636
commit a30af547f9

View file

@ -96,6 +96,8 @@ namespace OpenTK.Platform.X11
static readonly IntPtr _atom_add = (IntPtr)1; static readonly IntPtr _atom_add = (IntPtr)1;
static readonly IntPtr _atom_toggle = (IntPtr)2; static readonly IntPtr _atom_toggle = (IntPtr)2;
// Used by OpenTK to detect mouse warp events
Rectangle bounds, client_rectangle; Rectangle bounds, client_rectangle;
int border_left, border_right, border_top, border_bottom; int border_left, border_right, border_top, border_bottom;
Icon icon; Icon icon;
@ -121,7 +123,6 @@ namespace OpenTK.Platform.X11
MouseCursor cursor = MouseCursor.Default; MouseCursor cursor = MouseCursor.Default;
IntPtr cursorHandle; IntPtr cursorHandle;
bool cursor_visible = true; bool cursor_visible = true;
int mouse_rel_x, mouse_rel_y;
// Keyboard input // Keyboard input
readonly byte[] ascii = new byte[16]; readonly byte[] ascii = new byte[16];
@ -129,8 +130,6 @@ namespace OpenTK.Platform.X11
readonly IntPtr EmptyCursor; readonly IntPtr EmptyCursor;
public static bool MouseWarpActive = false;
readonly bool xi2_supported; readonly bool xi2_supported;
readonly int xi2_opcode; readonly int xi2_opcode;
@ -905,49 +904,15 @@ namespace OpenTK.Platform.X11
case XEventName.MotionNotify: case XEventName.MotionNotify:
{ {
// Try to detect and ignore events from XWarpPointer, below.
// This heuristic will fail if the user actually moves the pointer
// to the dead center of the window. Fortunately, this situation
// is very very uncommon. Todo: Can this be remedied?
int x = e.MotionEvent.x; int x = e.MotionEvent.x;
int y = e.MotionEvent.y; int y = e.MotionEvent.y;
// TODO: Have offset as a stored field, only update it when the window moves
// The middle point cannot be the average of the Bounds.left/right/top/bottom,
// because these fields take into account window decoration (borders, etc),
// which we do not want to account for.
Point offset = this.PointToClient(Point.Empty);
int middle_x = Width/2-offset.X;
int middle_y = Height/2-offset.Y;
Point screen_xy = PointToScreen(new Point(x, y)); if (x != 0 || y != 0)
if (!CursorVisible && MouseWarpActive &&
screen_xy.X == middle_x && screen_xy.Y == middle_y)
{
MouseWarpActive = false;
mouse_rel_x = x;
mouse_rel_y = y;
}
else if (!CursorVisible)
{
OnMouseMove(
MathHelper.Clamp(MouseState.X + x - mouse_rel_x, 0, Width),
MathHelper.Clamp(MouseState.Y + y - mouse_rel_y, 0, Height));
mouse_rel_x = x;
mouse_rel_y = y;
// Warp cursor to center of window.
MouseWarpActive = true;
Mouse.SetPosition(middle_x, middle_y);
}
else
{ {
OnMouseMove( OnMouseMove(
MathHelper.Clamp(x, 0, Width), MathHelper.Clamp(x, 0, Width),
MathHelper.Clamp(y, 0, Height)); MathHelper.Clamp(y, 0, Height));
mouse_rel_x = x;
mouse_rel_y = y;
} }
break; break;
} }
@ -971,7 +936,6 @@ namespace OpenTK.Platform.X11
{ {
int dx, dy; int dx, dy;
MouseButton button = X11KeyMap.TranslateButton(e.ButtonEvent.button, out dx, out dy); MouseButton button = X11KeyMap.TranslateButton(e.ButtonEvent.button, out dx, out dy);
if (button != MouseButton.LastButton) if (button != MouseButton.LastButton)
{ {
OnMouseUp(button); OnMouseUp(button);
@ -985,6 +949,11 @@ namespace OpenTK.Platform.X11
has_focus = true; has_focus = true;
if (has_focus != previous_focus) if (has_focus != previous_focus)
OnFocusedChanged(EventArgs.Empty); OnFocusedChanged(EventArgs.Empty);
if (Focused && !CursorVisible)
{
GrabMouse();
}
} }
break; break;
@ -1000,6 +969,12 @@ namespace OpenTK.Platform.X11
case XEventName.LeaveNotify: case XEventName.LeaveNotify:
if (CursorVisible) if (CursorVisible)
{ {
int x = MathHelper.Clamp(e.CrossingEvent.x, 0, Width);
int y = MathHelper.Clamp(e.CrossingEvent.y, 0, Height);
if (x != MouseState.X || y != MouseState.Y)
{
OnMouseMove(x, y);
}
OnMouseLeave(EventArgs.Empty); OnMouseLeave(EventArgs.Empty);
} }
break; break;
@ -1499,12 +1474,13 @@ namespace OpenTK.Platform.X11
{ {
using (new XLock(window.Display)) using (new XLock(window.Display))
{ {
UngrabMouse();
Point p = PointToScreen(new Point(MouseState.X, MouseState.Y)); Point p = PointToScreen(new Point(MouseState.X, MouseState.Y));
Mouse.SetPosition(p.X, p.Y); Mouse.SetPosition(p.X, p.Y);
Functions.XFlush(window.Display);
// Note: if cursorHandle = IntPtr.Zero, this function // Note: if cursorHandle = IntPtr.Zero, this restores the default cursor
// is equivalent to XUndefineCursor. // (equivalent to calling XUndefineCursor)
Functions.XDefineCursor(window.Display, window.Handle, cursorHandle); Functions.XDefineCursor(window.Display, window.Handle, cursorHandle);
cursor_visible = true; cursor_visible = true;
} }
@ -1513,13 +1489,30 @@ namespace OpenTK.Platform.X11
{ {
using (new XLock(window.Display)) using (new XLock(window.Display))
{ {
Functions.XDefineCursor(window.Display, window.Handle, EmptyCursor); GrabMouse();
cursor_visible = false; cursor_visible = false;
} }
} }
} }
} }
void GrabMouse()
{
Functions.XGrabPointer(window.Display, window.Handle, false,
EventMask.PointerMotionMask |
EventMask.ButtonMotionMask | EventMask.Button1MotionMask |
EventMask.Button2MotionMask | EventMask.Button3MotionMask |
EventMask.Button4MotionMask | EventMask.Button5MotionMask |
EventMask.ButtonPressMask | EventMask.ButtonReleaseMask,
GrabMode.GrabModeAsync, GrabMode.GrabModeAsync,
window.Handle, EmptyCursor, IntPtr.Zero);
}
void UngrabMouse()
{
Functions.XUngrabPointer(window.Display, IntPtr.Zero);
}
#endregion #endregion
#endregion #endregion