* Input/Mouse.cs:
* Input/IMouseDriver2.cs: * Platform/X11/X11Mouse.cs: * Platform/X11/XI2Mouse.cs: * Platform/X11/Functions.cs: * Platform/Windows/WMInput.cs: * Platform/X11/X11GLNative.cs: * Platform/Windows/WinRawMouse.cs: Added ability to set the position of the mouse cursor. [X11] Avoid grabbing the pointer, as this causes unexpected side-effects (XInput2 stops working, debugging becomes difficult). We now use XWarpPointer and try to discard the spurious MouseMove events it generates. [X11] Make cursor visible when window loses focus, to make debugging easier. Restore previous state when it regains focus.
This commit is contained in:
parent
82e5401779
commit
76e1d4064b
8 changed files with 189 additions and 22 deletions
|
@ -1,4 +1,31 @@
|
|||
using System;
|
||||
#region License
|
||||
//
|
||||
// The Open Toolkit Library License
|
||||
//
|
||||
// Copyright (c) 2006 - 2010 the Open Toolkit library.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do
|
||||
// so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
|
@ -6,17 +33,8 @@ namespace OpenTK.Input
|
|||
{
|
||||
interface IMouseDriver2
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves the MouseState for the default keyboard device.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="OpenTK.Input.MouseState"/> structure containing the state of the mouse device.</returns>
|
||||
MouseState GetState();
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the MouseState for the specified keyboard device.
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the keyboard device.</param>
|
||||
/// <returns>A <see cref="OpenTK.Input.MouseState"/> structure containing the state of the mouse device.</returns>
|
||||
MouseState GetState(int index);
|
||||
void SetPosition(double x, double y);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,6 +74,23 @@ namespace OpenTK.Input
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///Moves the mouse cursor to the specified screen position.
|
||||
/// </summary>
|
||||
/// <param name="x">
|
||||
/// A <see cref="System.Double"/> that represents the absolute x position of the cursor in screen coordinates.
|
||||
/// </param>
|
||||
/// <param name="y">
|
||||
/// A <see cref="System.Double"/> that represents the absolute y position of the cursor in screen coordinates.
|
||||
/// </param>
|
||||
public static void SetPosition(double x, double y)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
driver.SetPosition(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
@ -137,6 +137,11 @@ namespace OpenTK.Platform.Windows
|
|||
}
|
||||
}
|
||||
|
||||
public void SetPosition(double x, double y)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IKeyboardDriver2 Members
|
||||
|
|
|
@ -278,6 +278,11 @@ namespace OpenTK.Platform.Windows
|
|||
}
|
||||
}
|
||||
|
||||
public void SetPosition(double x, double y)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
@ -529,6 +529,12 @@ namespace OpenTK.Platform.X11
|
|||
[DllImport("libXi")]
|
||||
static extern Status XIUngrabDevice(IntPtr display, int deviceid, Time time);
|
||||
|
||||
[DllImport("libXi")]
|
||||
public static extern Bool XIWarpPointer(Display display,
|
||||
int deviceid, Window src_w, Window dest_w,
|
||||
double src_x, double src_y, int src_width, int src_height,
|
||||
double dest_x, double dest_y);
|
||||
|
||||
static readonly IntPtr CopyFromParent = IntPtr.Zero;
|
||||
|
||||
public static void SendNetWMMessage(X11WindowInfo window, IntPtr message_type, IntPtr l0, IntPtr l1, IntPtr l2)
|
||||
|
|
|
@ -54,7 +54,10 @@ namespace OpenTK.Platform.X11
|
|||
const int _min_width = 30, _min_height = 30;
|
||||
|
||||
X11WindowInfo window = new X11WindowInfo();
|
||||
|
||||
// Legacy input support
|
||||
X11Input driver;
|
||||
MouseDevice mouse;
|
||||
|
||||
// Window manager hints for fullscreen windows.
|
||||
// Not used right now (the code is written, but is not 64bit-correct), but could be useful for older WMs which
|
||||
|
@ -107,6 +110,8 @@ namespace OpenTK.Platform.X11
|
|||
bool isExiting;
|
||||
|
||||
bool _decorations_hidden = false;
|
||||
bool cursor_visible = true;
|
||||
int mouse_rel_x, mouse_rel_y;
|
||||
|
||||
// Keyboard input
|
||||
readonly byte[] ascii = new byte[16];
|
||||
|
@ -115,6 +120,8 @@ namespace OpenTK.Platform.X11
|
|||
|
||||
readonly IntPtr EmptyCursor;
|
||||
|
||||
public static bool MouseWarpActive = false;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
@ -196,6 +203,7 @@ namespace OpenTK.Platform.X11
|
|||
RefreshWindowBounds(ref e);
|
||||
|
||||
driver = new X11Input(window);
|
||||
mouse = driver.Mouse[0];
|
||||
|
||||
EmptyCursor = CreateEmptyCursor(window);
|
||||
|
||||
|
@ -692,6 +700,17 @@ namespace OpenTK.Platform.X11
|
|||
return cursor;
|
||||
}
|
||||
|
||||
static void SetMouseClamped(MouseDevice mouse, int x, int y,
|
||||
int left, int top, int width, int height)
|
||||
{
|
||||
// Clamp mouse to the specified rectangle.
|
||||
x = Math.Max(x, left);
|
||||
x = Math.Min(x, width);
|
||||
y = Math.Max(y, top);
|
||||
y = Math.Min(y, height);
|
||||
mouse.Position = new Point(x, y);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region INativeWindow Members
|
||||
|
@ -794,6 +813,45 @@ namespace OpenTK.Platform.X11
|
|||
break;
|
||||
|
||||
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;
|
||||
int middle_x = (Bounds.Left + Bounds.Right) / 2;
|
||||
int middle_y = (Bounds.Top + Bounds.Bottom) / 2;
|
||||
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)
|
||||
{
|
||||
SetMouseClamped(mouse,
|
||||
mouse.X + x - mouse_rel_x,
|
||||
mouse.Y + y - mouse_rel_y,
|
||||
0, 0, Width, Height);
|
||||
mouse_rel_x = x;
|
||||
mouse_rel_y = y;
|
||||
|
||||
// Warp cursor to center of window.
|
||||
MouseWarpActive = true;
|
||||
Mouse.SetPosition(middle_x, middle_y);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetMouseClamped(mouse, x, y, 0, 0, Width, Height);
|
||||
mouse_rel_x = x;
|
||||
mouse_rel_y = y;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case XEventName.ButtonPress:
|
||||
case XEventName.ButtonRelease:
|
||||
driver.ProcessEvent(ref e);
|
||||
|
@ -818,7 +876,10 @@ namespace OpenTK.Platform.X11
|
|||
break;
|
||||
|
||||
case XEventName.LeaveNotify:
|
||||
MouseLeave(this, EventArgs.Empty);
|
||||
if (CursorVisible)
|
||||
{
|
||||
MouseLeave(this, EventArgs.Empty);
|
||||
}
|
||||
break;
|
||||
|
||||
case XEventName.EnterNotify:
|
||||
|
@ -1291,15 +1352,15 @@ namespace OpenTK.Platform.X11
|
|||
|
||||
public bool CursorVisible
|
||||
{
|
||||
get { return true; } // Not used
|
||||
get { return cursor_visible; }
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
using (new XLock(window.Display))
|
||||
{
|
||||
Functions.XUngrabPointer(window.Display, IntPtr.Zero);
|
||||
Functions.XUndefineCursor(window.Display, window.WindowHandle);
|
||||
cursor_visible = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -1307,11 +1368,7 @@ namespace OpenTK.Platform.X11
|
|||
using (new XLock(window.Display))
|
||||
{
|
||||
Functions.XDefineCursor(window.Display, window.WindowHandle, EmptyCursor);
|
||||
Functions.XGrabPointer(window.Display, window.WindowHandle, true,
|
||||
EventMask.PointerMotionMask | EventMask.ButtonPressMask | EventMask.ButtonReleaseMask,
|
||||
GrabMode.GrabModeAsync, GrabMode.GrabModeAsync, window.WindowHandle, IntPtr.Zero,
|
||||
IntPtr.Zero);
|
||||
|
||||
cursor_visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,6 +61,11 @@ namespace OpenTK.Platform.X11
|
|||
return new MouseState();
|
||||
}
|
||||
|
||||
public void SetPosition(double x, double y)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
void WriteBit(MouseButton offset, int enabled)
|
||||
{
|
||||
if (enabled != 0)
|
||||
|
|
|
@ -39,12 +39,22 @@ namespace OpenTK.Platform.X11
|
|||
{
|
||||
List<MouseState> mice = new List<MouseState>();
|
||||
Dictionary<int, int> rawids = new Dictionary<int, int>(); // maps raw ids to mouse ids
|
||||
readonly X11WindowInfo window;
|
||||
internal readonly X11WindowInfo window;
|
||||
static int XIOpCode;
|
||||
|
||||
static readonly Functions.EventPredicate PredicateImpl = IsEventValid;
|
||||
readonly IntPtr Predicate = Marshal.GetFunctionPointerForDelegate(PredicateImpl);
|
||||
|
||||
// Store information on a mouse warp event, so it can be ignored.
|
||||
struct MouseWarp : IEquatable<MouseWarp>
|
||||
{
|
||||
public MouseWarp(double x, double y) { X = x; Y = y; }
|
||||
double X, Y;
|
||||
public bool Equals(MouseWarp warp) { return X == warp.X && Y == warp.Y; }
|
||||
}
|
||||
MouseWarp? mouse_warp_event;
|
||||
int mouse_warp_event_count;
|
||||
|
||||
public XI2Mouse()
|
||||
{
|
||||
Debug.WriteLine("Using XI2Mouse.");
|
||||
|
@ -65,6 +75,7 @@ namespace OpenTK.Platform.X11
|
|||
XIEventMasks.RawButtonReleaseMask | XIEventMasks.RawMotionMask))
|
||||
{
|
||||
Functions.XISelectEvents(window.Display, window.WindowHandle, mask);
|
||||
Functions.XISelectEvents(window.Display, window.RootWindow, mask);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -110,8 +121,40 @@ namespace OpenTK.Platform.X11
|
|||
return new MouseState();
|
||||
}
|
||||
|
||||
public void SetPosition(double x, double y)
|
||||
{
|
||||
using (new XLock(window.Display))
|
||||
{
|
||||
// Functions.XIWarpPointer(window.Display, 0,
|
||||
// IntPtr.Zero, window.RootWindow, 0, 0, 0, 0, x, y);
|
||||
Functions.XWarpPointer(window.Display,
|
||||
IntPtr.Zero, window.RootWindow, 0, 0, 0, 0, (int)x, (int)y);
|
||||
|
||||
// Mark the expected warp-event so it can be ignored.
|
||||
if (mouse_warp_event == null)
|
||||
mouse_warp_event_count = 0;
|
||||
mouse_warp_event_count++;
|
||||
mouse_warp_event = new MouseWarp((int)x, (int)y);
|
||||
}
|
||||
|
||||
ProcessEvents();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
bool CheckMouseWarp(double x, double y)
|
||||
{
|
||||
// Check if a mouse warp with the specified destination exists.
|
||||
bool is_warp =
|
||||
mouse_warp_event.HasValue &&
|
||||
mouse_warp_event.Value.Equals(new MouseWarp((int)x, (int)y));
|
||||
|
||||
if (is_warp && --mouse_warp_event_count <= 0)
|
||||
mouse_warp_event = null;
|
||||
|
||||
return is_warp;
|
||||
}
|
||||
|
||||
void ProcessEvents()
|
||||
{
|
||||
while (true)
|
||||
|
@ -140,10 +183,21 @@ namespace OpenTK.Platform.X11
|
|||
switch (raw.evtype)
|
||||
{
|
||||
case XIEventType.RawMotion:
|
||||
double x = 0, y = 0;
|
||||
if (IsBitSet(raw.valuators.mask, 0))
|
||||
state.X += (int)BitConverter.Int64BitsToDouble(Marshal.ReadInt64(raw.raw_values, 0));
|
||||
{
|
||||
x = BitConverter.Int64BitsToDouble(Marshal.ReadInt64(raw.raw_values, 0));
|
||||
}
|
||||
if (IsBitSet(raw.valuators.mask, 1))
|
||||
state.Y += (int)BitConverter.Int64BitsToDouble(Marshal.ReadInt64(raw.raw_values, 8));
|
||||
{
|
||||
y = BitConverter.Int64BitsToDouble(Marshal.ReadInt64(raw.raw_values, 8));
|
||||
}
|
||||
|
||||
if (!CheckMouseWarp(x, y))
|
||||
{
|
||||
state.X += (int)x;
|
||||
state.Y += (int)y;
|
||||
}
|
||||
break;
|
||||
|
||||
case XIEventType.RawButtonPress:
|
||||
|
|
Loading…
Reference in a new issue