* 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:
the_fiddler 2010-11-23 17:17:13 +00:00
parent 82e5401779
commit 76e1d4064b
8 changed files with 189 additions and 22 deletions

View file

@ -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);
}
}

View file

@ -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
}
}

View file

@ -137,6 +137,11 @@ namespace OpenTK.Platform.Windows
}
}
public void SetPosition(double x, double y)
{
throw new NotImplementedException();
}
#endregion
#region IKeyboardDriver2 Members

View file

@ -278,6 +278,11 @@ namespace OpenTK.Platform.Windows
}
}
public void SetPosition(double x, double y)
{
throw new NotImplementedException();
}
#endregion
}
}

View file

@ -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)

View file

@ -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;
}
}
}

View file

@ -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)

View file

@ -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: