From a30af547f98250b6b60b7dd373c2f3287f93c8c2 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Tue, 13 May 2014 22:31:45 +0200 Subject: [PATCH] [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. --- Source/OpenTK/Platform/X11/X11GLNative.cs | 83 +++++++++++------------ 1 file changed, 38 insertions(+), 45 deletions(-) diff --git a/Source/OpenTK/Platform/X11/X11GLNative.cs b/Source/OpenTK/Platform/X11/X11GLNative.cs index a5517af6..29dc6835 100644 --- a/Source/OpenTK/Platform/X11/X11GLNative.cs +++ b/Source/OpenTK/Platform/X11/X11GLNative.cs @@ -91,11 +91,13 @@ namespace OpenTK.Platform.X11 //IntPtr _atom_motif_wm_hints; //IntPtr _atom_kde_wm_hints; //IntPtr _atom_kde_net_wm_hints; - + static readonly IntPtr _atom_remove = (IntPtr)0; static readonly IntPtr _atom_add = (IntPtr)1; static readonly IntPtr _atom_toggle = (IntPtr)2; - + + // Used by OpenTK to detect mouse warp events + Rectangle bounds, client_rectangle; int border_left, border_right, border_top, border_bottom; Icon icon; @@ -121,7 +123,6 @@ namespace OpenTK.Platform.X11 MouseCursor cursor = MouseCursor.Default; IntPtr cursorHandle; bool cursor_visible = true; - int mouse_rel_x, mouse_rel_y; // Keyboard input readonly byte[] ascii = new byte[16]; @@ -129,8 +130,6 @@ namespace OpenTK.Platform.X11 readonly IntPtr EmptyCursor; - public static bool MouseWarpActive = false; - readonly bool xi2_supported; readonly int xi2_opcode; @@ -905,49 +904,15 @@ namespace OpenTK.Platform.X11 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 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 (!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 + if (x != 0 || y != 0) { OnMouseMove( MathHelper.Clamp(x, 0, Width), MathHelper.Clamp(y, 0, Height)); - mouse_rel_x = x; - mouse_rel_y = y; } - break; } @@ -971,7 +936,6 @@ namespace OpenTK.Platform.X11 { int dx, dy; MouseButton button = X11KeyMap.TranslateButton(e.ButtonEvent.button, out dx, out dy); - if (button != MouseButton.LastButton) { OnMouseUp(button); @@ -985,6 +949,11 @@ namespace OpenTK.Platform.X11 has_focus = true; if (has_focus != previous_focus) OnFocusedChanged(EventArgs.Empty); + + if (Focused && !CursorVisible) + { + GrabMouse(); + } } break; @@ -1000,6 +969,12 @@ namespace OpenTK.Platform.X11 case XEventName.LeaveNotify: 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); } break; @@ -1499,12 +1474,13 @@ namespace OpenTK.Platform.X11 { using (new XLock(window.Display)) { + UngrabMouse(); + Point p = PointToScreen(new Point(MouseState.X, MouseState.Y)); Mouse.SetPosition(p.X, p.Y); - Functions.XFlush(window.Display); - // Note: if cursorHandle = IntPtr.Zero, this function - // is equivalent to XUndefineCursor. + // Note: if cursorHandle = IntPtr.Zero, this restores the default cursor + // (equivalent to calling XUndefineCursor) Functions.XDefineCursor(window.Display, window.Handle, cursorHandle); cursor_visible = true; } @@ -1513,13 +1489,30 @@ namespace OpenTK.Platform.X11 { using (new XLock(window.Display)) { - Functions.XDefineCursor(window.Display, window.Handle, EmptyCursor); + GrabMouse(); 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