diff --git a/Source/Examples/OpenTK/Test/GameWindowStates.cs b/Source/Examples/OpenTK/Test/GameWindowStates.cs
index 1977b12c..f0248f41 100644
--- a/Source/Examples/OpenTK/Test/GameWindowStates.cs
+++ b/Source/Examples/OpenTK/Test/GameWindowStates.cs
@@ -301,6 +301,7 @@ namespace Examples.Tests
static int DrawMice(Graphics gfx, int line)
{
line++;
+ DrawString(gfx, String.Format("Cursor: {0}", OpenTK.Input.Mouse.GetCursorState()), line++);
DrawString(gfx, "Mouse:", line++);
for (int i = 0; i < 4; i++)
{
diff --git a/Source/OpenTK/Input/IMouseDriver2.cs b/Source/OpenTK/Input/IMouseDriver2.cs
index cc8ba5b4..45a85fa6 100644
--- a/Source/OpenTK/Input/IMouseDriver2.cs
+++ b/Source/OpenTK/Input/IMouseDriver2.cs
@@ -36,5 +36,6 @@ namespace OpenTK.Input
MouseState GetState();
MouseState GetState(int index);
void SetPosition(double x, double y);
+ MouseState GetCursorState();
}
}
diff --git a/Source/OpenTK/Input/Mouse.cs b/Source/OpenTK/Input/Mouse.cs
index f68d5ff2..6caeb835 100644
--- a/Source/OpenTK/Input/Mouse.cs
+++ b/Source/OpenTK/Input/Mouse.cs
@@ -48,8 +48,14 @@ namespace OpenTK.Input
///
/// Retrieves the combined for all specified mouse devices.
+ /// The X, Y and wheel values are defined in a hardware-specific coordinate system.
+ /// Pointer ballistics (acceleration) are NOT applied. Resolution is hardware-specific,
+ /// typically between 200 and 2000 DPI.
+ /// Use to retrieve the state of a specific mouse device.
+ /// Use to retrieve the absolute coordinates of the mouse cursor.
+ /// Use for event-based mouse input.
///
- /// A structure containing the combined state of all mouse devices.
+ /// A structure representing the combined state of all mouse devices.
public static MouseState GetState()
{
lock (SyncRoot)
@@ -60,9 +66,15 @@ namespace OpenTK.Input
///
/// Retrieves the for the specified mouse device.
+ /// The X, Y and wheel values are defined in a hardware-specific coordinate system.
+ /// Pointer ballistics (acceleration) are NOT applied. Resolution is hardware-specific,
+ /// typically between 200 and 2000 DPI.
+ /// Use to retrieve the combined state of all mouse devices.
+ /// Use to retrieve the absolute coordinates of the mouse cursor.
+ /// Use for event-based mouse input.
///
/// The index of the mouse device.
- /// A structure containing the state for the specified mouse device.
+ /// A structure representing the state for the specified mouse device.
public static MouseState GetState(int index)
{
if (index < 0)
@@ -74,6 +86,23 @@ namespace OpenTK.Input
}
}
+ ///
+ /// Retreves the for the mouse cursor.
+ /// The X and Y coordinates are defined in absolute desktop points, with the origin
+ /// placed at the top-left corner of .
+ /// Pointer ballistics (acceleration) are applied. Resolution is limited to the
+ /// resolution of the containing the cursor,
+ /// typically between 96 and 120 DPI.
+ ///
+ /// A structure representing the state of the mouse cursor.
+ public static MouseState GetCursorState()
+ {
+ lock (SyncRoot)
+ {
+ return driver.GetCursorState();
+ }
+ }
+
///
///Moves the mouse cursor to the specified screen position.
///
diff --git a/Source/OpenTK/OpenTK.csproj b/Source/OpenTK/OpenTK.csproj
index 2d033d82..eab2002c 100644
--- a/Source/OpenTK/OpenTK.csproj
+++ b/Source/OpenTK/OpenTK.csproj
@@ -1,4 +1,4 @@
-
+
Local
@@ -167,6 +167,7 @@
+
Code
diff --git a/Source/OpenTK/Platform/MacOS/HIDInput.cs b/Source/OpenTK/Platform/MacOS/HIDInput.cs
index 28a061c5..ecfa3e3d 100755
--- a/Source/OpenTK/Platform/MacOS/HIDInput.cs
+++ b/Source/OpenTK/Platform/MacOS/HIDInput.cs
@@ -834,6 +834,14 @@ namespace OpenTK.Platform.MacOS
return new MouseState();
}
+ MouseState IMouseDriver2.GetCursorState()
+ {
+ var state = new MouseState();
+ state.SetIsConnected(true);
+
+ return state;
+ }
+
void IMouseDriver2.SetPosition(double x, double y)
{
CG.SetLocalEventsSuppressionInterval(0.0);
diff --git a/Source/OpenTK/Platform/SDL2/Sdl2Mouse.cs b/Source/OpenTK/Platform/SDL2/Sdl2Mouse.cs
index 934096ef..4913c22d 100644
--- a/Source/OpenTK/Platform/SDL2/Sdl2Mouse.cs
+++ b/Source/OpenTK/Platform/SDL2/Sdl2Mouse.cs
@@ -119,6 +119,24 @@ namespace OpenTK.Platform.SDL2
return new MouseState();
}
+ public MouseState GetCursorState()
+ {
+ int x, y;
+ var buttons = SDL.GetMouseState(out x, out y);
+
+ var state = new MouseState();
+ state.SetIsConnected(true);
+ state.X = x;
+ state.Y = y;
+ state[MouseButton.Left] = (buttons & ButtonFlags.Left) != 0;
+ state[MouseButton.Middle] = (buttons & ButtonFlags.Middle) != 0;
+ state[MouseButton.Right] = (buttons & ButtonFlags.Right) != 0;
+ state[MouseButton.Button1] = (buttons & ButtonFlags.X1) != 0;
+ state[MouseButton.Button2] = (buttons & ButtonFlags.X2) != 0;
+
+ return state;
+ }
+
public void SetPosition(double x, double y)
{
SDL.WarpMouseInWindow(IntPtr.Zero, (int)x, (int)y);
diff --git a/Source/OpenTK/Platform/Windows/WinRawMouse.cs b/Source/OpenTK/Platform/Windows/WinRawMouse.cs
index c904ea1f..3b1fd9d4 100644
--- a/Source/OpenTK/Platform/Windows/WinRawMouse.cs
+++ b/Source/OpenTK/Platform/Windows/WinRawMouse.cs
@@ -348,6 +348,30 @@ namespace OpenTK.Platform.Windows
Functions.SetCursorPos((int)x, (int)y);
}
+ public MouseState GetCursorState()
+ {
+ var state = new MouseState();
+ state.SetIsConnected(true);
+
+ POINT p = new POINT();
+ Functions.GetCursorPos(ref p);
+ bool left = Functions.GetKeyState(VirtualKeys.LBUTTON) != 0;
+ bool right = Functions.GetKeyState(VirtualKeys.RBUTTON) != 0;
+ bool middle = Functions.GetKeyState(VirtualKeys.MBUTTON) != 0;
+ bool x1 = Functions.GetKeyState(VirtualKeys.XBUTTON1) != 0;
+ bool x2 = Functions.GetKeyState(VirtualKeys.XBUTTON2) != 0;
+
+ state.X = p.X;
+ state.Y = p.Y;
+ state[MouseButton.Left] = left;
+ state[MouseButton.Right] = right;
+ state[MouseButton.Middle] = middle;
+ state[MouseButton.Button1] = x1;
+ state[MouseButton.Button2] = x2;
+
+ return state;
+ }
+
#endregion
}
}
diff --git a/Source/OpenTK/Platform/X11/Bindings/XI.cs b/Source/OpenTK/Platform/X11/Bindings/XI.cs
new file mode 100644
index 00000000..418acfea
--- /dev/null
+++ b/Source/OpenTK/Platform/X11/Bindings/XI.cs
@@ -0,0 +1,127 @@
+#region License
+//
+// The Open Toolkit Library License
+//
+// Copyright (c) 2006 - 2014 Stefanos Apostolopoulos for 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.Runtime.InteropServices;
+using System.Text;
+
+namespace OpenTK.Platform.X11
+{
+ using Bool = Boolean;
+ using Cursor = IntPtr;
+ using Display = IntPtr;
+ using Status = ErrorCodes;
+ using Time = IntPtr;
+ using Window = IntPtr;
+
+ // Bindings for the XInput2 extension
+ class XI
+ {
+ const string lib = "libXi";
+
+ // mouse
+ internal static readonly IntPtr ButtonLeft = Functions.XInternAtom(API.DefaultDisplay, "Button Left", false);
+ internal static readonly IntPtr ButtonMiddle = Functions.XInternAtom(API.DefaultDisplay, "Button Middle", false);
+ internal static readonly IntPtr ButtonRight = Functions.XInternAtom(API.DefaultDisplay, "Button Right", false);
+ internal static readonly IntPtr ButtonWheelUp = Functions.XInternAtom(API.DefaultDisplay, "Button Wheel Up", false);
+ internal static readonly IntPtr ButtonWheelDown = Functions.XInternAtom(API.DefaultDisplay, "Button Wheel Down", false);
+ internal static readonly IntPtr ButtonWheelLeft = Functions.XInternAtom(API.DefaultDisplay, "Button Horiz Wheel Left", false);
+ internal static readonly IntPtr ButtonWheelRight = Functions.XInternAtom(API.DefaultDisplay, "Button Horiz Wheel Right", false);
+ internal static readonly IntPtr RelativeX = Functions.XInternAtom(API.DefaultDisplay, "Rel X", false);
+ internal static readonly IntPtr RelativeY = Functions.XInternAtom(API.DefaultDisplay, "Rel Y", false);
+ internal static readonly IntPtr RelativeHWheel = Functions.XInternAtom(API.DefaultDisplay, "Rel Horiz Wheel", false);
+ internal static readonly IntPtr RelativeVWheel = Functions.XInternAtom(API.DefaultDisplay, "Rel Vert Wheel", false);
+ internal static readonly IntPtr RelativeHScroll = Functions.XInternAtom(API.DefaultDisplay, "Rel Horiz Scroll", false);
+ internal static readonly IntPtr RelativeVScroll = Functions.XInternAtom(API.DefaultDisplay, "Rel Vert Scroll", false);
+
+ // multitouch
+ internal static readonly IntPtr TouchX = Functions.XInternAtom(API.DefaultDisplay, "Abs MT Position X", false);
+ internal static readonly IntPtr TouchY = Functions.XInternAtom(API.DefaultDisplay, "Abs MT Position Y", false);
+ internal static readonly IntPtr TouchMajor = Functions.XInternAtom(API.DefaultDisplay, "Abs MT Touch Major", false);
+ internal static readonly IntPtr TouchMinor = Functions.XInternAtom(API.DefaultDisplay, "Abs MT Touch Minor", false);
+ internal static readonly IntPtr TouchPressure = Functions.XInternAtom(API.DefaultDisplay, "Abs MT Pressure", false);
+ internal static readonly IntPtr TouchId = Functions.XInternAtom(API.DefaultDisplay, "Abs MT Tracking ID", false);
+ internal static readonly IntPtr TouchMaxContacts = Functions.XInternAtom(API.DefaultDisplay, "Max Contacts", false);
+
+ // tablet
+ internal static readonly IntPtr AbsoluteX = Functions.XInternAtom(API.DefaultDisplay, "Abs X", false);
+ internal static readonly IntPtr AbsoluteY = Functions.XInternAtom(API.DefaultDisplay, "Abs Y", false);
+ internal static readonly IntPtr AbsolutePressure = Functions.XInternAtom(API.DefaultDisplay, "Abs Pressure", false);
+ internal static readonly IntPtr AbsoluteTiltX = Functions.XInternAtom(API.DefaultDisplay, "Abs Tilt X", false);
+ internal static readonly IntPtr AbsoluteTiltY = Functions.XInternAtom(API.DefaultDisplay, "Abs Tilt Y", false);
+ internal static readonly IntPtr AbsoluteWheel = Functions.XInternAtom(API.DefaultDisplay, "Abs Wheel", false);
+ internal static readonly IntPtr AbsoluteDistance = Functions.XInternAtom(API.DefaultDisplay, "Abs Distance", false);
+
+ [DllImport(lib, EntryPoint = "XISelectEvents")]
+ static extern int SelectEvents(IntPtr dpy, Window win, [In] XIEventMask[] masks, int num_masks);
+
+ [DllImport(lib, EntryPoint = "XISelectEvents")]
+ static extern int SelectEvents(IntPtr dpy, Window win, [In] ref XIEventMask masks, int num_masks);
+
+ public static int SelectEvents(IntPtr dpy, Window win, XIEventMask[] masks)
+ {
+ return SelectEvents(dpy, win, masks, masks.Length);
+ }
+
+ public static int SelectEvents(IntPtr dpy, Window win, XIEventMask mask)
+ {
+ return SelectEvents(dpy, win, ref mask, 1);
+ }
+
+ [DllImport(lib, EntryPoint = "XIGrabDevice")]
+ static extern Status GrabDevice(IntPtr display, int deviceid, Window grab_window, Time time,
+ Cursor cursor, int grab_mode, int paired_device_mode, Bool owner_events, XIEventMask[] mask);
+
+ [DllImport(lib, EntryPoint = "XIUngrabDevice")]
+ static extern Status UngrabDevice(IntPtr display, int deviceid, Time time);
+
+ [DllImport(lib, EntryPoint = "XIWarpPointer")]
+ 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);
+
+ [DllImport(lib, EntryPoint = "XIQueryDevice")]
+ public static extern IntPtr QueryDevice(Display display, int id, out int count);
+
+ [DllImport(lib, EntryPoint = "XIFreeDeviceInfo")]
+ public static extern void FreeDeviceInfo(IntPtr devices);
+
+ [DllImport(lib, EntryPoint = "XIQueryPointer")]
+ public static extern Bool QueryPointer(Display display,
+ int deviceid, Window win,
+ out Window root_return, out Window child_return,
+ out double root_x_return, out double root_y_return,
+ out double win_x_return, out double win_y_return,
+ out XIButtonState buttons_return, out XIModifierState modifiers_return,
+ out XIGroupState group_return);
+
+ [DllImport(lib, EntryPoint = "XIQueryVersion")]
+ internal static extern Status QueryVersion(Display display, ref int major, ref int minor);
+ }
+}
diff --git a/Source/OpenTK/Platform/X11/Functions.cs b/Source/OpenTK/Platform/X11/Functions.cs
index 6fae04fa..4198a87a 100644
--- a/Source/OpenTK/Platform/X11/Functions.cs
+++ b/Source/OpenTK/Platform/X11/Functions.cs
@@ -516,34 +516,6 @@ namespace OpenTK.Platform.X11
[DllImport("libX11")]
public static extern void XSetClassHint(IntPtr display, IntPtr window, ref XClassHint hint);
- [DllImport("libXi")]
- static extern int XISelectEvents(IntPtr dpy, Window win, [In] XIEventMask[] masks, int num_masks);
- [DllImport("libXi")]
- static extern int XISelectEvents(IntPtr dpy, Window win, [In] ref XIEventMask masks, int num_masks);
-
- public static int XISelectEvents(IntPtr dpy, Window win, XIEventMask[] masks)
- {
- return XISelectEvents(dpy, win, masks, masks.Length);
- }
-
- public static int XISelectEvents(IntPtr dpy, Window win, XIEventMask mask)
- {
- return XISelectEvents(dpy, win, ref mask, 1);
- }
-
- [DllImport("libXi")]
- static extern Status XIGrabDevice(IntPtr display, int deviceid, Window grab_window, Time time,
- Cursor cursor, int grab_mode, int paired_device_mode, Bool owner_events, XIEventMask[] mask);
-
- [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)
diff --git a/Source/OpenTK/Platform/X11/Structs.cs b/Source/OpenTK/Platform/X11/Structs.cs
index 68622733..91b25de8 100644
--- a/Source/OpenTK/Platform/X11/Structs.cs
+++ b/Source/OpenTK/Platform/X11/Structs.cs
@@ -1671,6 +1671,13 @@ namespace OpenTK.Platform.X11
// XInput2 structures
+ enum XIClassType
+ {
+ Button = 1,
+ Valuator = 2,
+ Scroll = 3,
+ }
+
struct XIDeviceInfo
{
public int deviceid;
@@ -1684,10 +1691,33 @@ namespace OpenTK.Platform.X11
struct XIAnyClassInfo
{
- public int type;
+ public XIClassType type;
public int sourceid;
}
+ struct XIButtonClassInfo
+ {
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ struct XIValuatorClassInfo
+ {
+ public Atom label;
+ public double max;
+ public double min;
+ public int mode;
+ public int number;
+ public int resolution;
+ public int sourceid;
+ public int type;
+ public double value;
+ }
+
+ struct XIScrollClassInfo
+ {
+
+ }
+
struct XIDeviceEvent
{
public int type; /* GenericEvent */
diff --git a/Source/OpenTK/Platform/X11/X11Mouse.cs b/Source/OpenTK/Platform/X11/X11Mouse.cs
index 0dc5879d..6a137b26 100644
--- a/Source/OpenTK/Platform/X11/X11Mouse.cs
+++ b/Source/OpenTK/Platform/X11/X11Mouse.cs
@@ -48,6 +48,7 @@ namespace OpenTK.Platform.X11
readonly IntPtr display;
readonly IntPtr root_window;
MouseState mouse = new MouseState();
+ MouseState cursor = new MouseState();
// When the mouse warps, "detach" the current location
// from the pointer.
@@ -58,6 +59,7 @@ namespace OpenTK.Platform.X11
{
Debug.WriteLine("Using X11Mouse.");
mouse.IsConnected = true;
+ cursor.IsConnected = true;
display = API.DefaultDisplay;
root_window = Functions.XRootWindow(display, Functions.XDefaultScreen(display));
}
@@ -78,6 +80,12 @@ namespace OpenTK.Platform.X11
return new MouseState();
}
+ public MouseState GetCursorState()
+ {
+ ProcessEvents();
+ return cursor;
+ }
+
public void SetPosition(double x, double y)
{
// Update the current location, otherwise the pointer
@@ -97,14 +105,6 @@ namespace OpenTK.Platform.X11
}
}
- void WriteBit(MouseButton offset, int enabled)
- {
- if (enabled != 0)
- mouse.EnableBit((int)offset);
- else
- mouse.DisableBit((int)offset);
- }
-
void ProcessEvents()
{
IntPtr root, child;
@@ -117,6 +117,9 @@ namespace OpenTK.Platform.X11
Functions.XQueryPointer(display, window, out root, out child,
out root_x, out root_y, out win_x, out win_y, out buttons);
+ cursor.X = root_x;
+ cursor.Y = root_y;
+
if (!mouse_detached)
{
mouse.X = root_x;
@@ -129,9 +132,9 @@ namespace OpenTK.Platform.X11
mouse_detached_x = root_x;
mouse_detached_y = root_y;
}
- WriteBit(MouseButton.Left, buttons & (int)MouseMask.Button1Mask);
- WriteBit(MouseButton.Middle, buttons & (int)MouseMask.Button2Mask);
- WriteBit(MouseButton.Right, buttons & (int)MouseMask.Button3Mask);
+ cursor[MouseButton.Left] = mouse[MouseButton.Left] = (buttons & (int)MouseMask.Button1Mask) != 0;
+ cursor[MouseButton.Middle] = mouse[MouseButton.Middle] = (buttons & (int)MouseMask.Button2Mask) != 0;
+ cursor[MouseButton.Right] = mouse[MouseButton.Right] = (buttons & (int)MouseMask.Button3Mask) != 0;
// Note: this will never work right, wheel events have a duration of 0
// (yes, zero). They are impposible to catch via polling.
// After spending a week on this, I simply don't care anymore.
@@ -142,9 +145,9 @@ namespace OpenTK.Platform.X11
// mouse.WheelPrecise++;
//if ((buttons & (int)MouseMask.Button5Mask) != 0)
// mouse.WheelPrecise--;
- WriteBit(MouseButton.Button1, buttons & (int)MouseMask.Button6Mask);
- WriteBit(MouseButton.Button2, buttons & (int)MouseMask.Button7Mask);
- WriteBit(MouseButton.Button3, buttons & (int)MouseMask.Button8Mask);
+ cursor[MouseButton.Button1] = mouse[MouseButton.Button1] = (buttons & (int)MouseMask.Button6Mask) != 0;
+ cursor[MouseButton.Button2] = mouse[MouseButton.Button2] = (buttons & (int)MouseMask.Button7Mask) != 0;
+ cursor[MouseButton.Button3] = mouse[MouseButton.Button3] = (buttons & (int)MouseMask.Button8Mask) != 0;
}
}
}
diff --git a/Source/OpenTK/Platform/X11/XI2Mouse.cs b/Source/OpenTK/Platform/X11/XI2Mouse.cs
index 893783b0..9b860ea0 100644
--- a/Source/OpenTK/Platform/X11/XI2Mouse.cs
+++ b/Source/OpenTK/Platform/X11/XI2Mouse.cs
@@ -37,8 +37,15 @@ namespace OpenTK.Platform.X11
// This should be easy: just read the device id and route the data to the correct device.
sealed class XI2Mouse : IMouseDriver2
{
- List mice = new List();
- Dictionary rawids = new Dictionary(); // maps raw ids to mouse ids
+ class XIMouse
+ {
+ public MouseState State;
+ public XIDeviceInfo Info;
+ }
+ XIMouse master; // XIMouse for the mouse cursor
+ List devices = new List(); // List of connected mice
+ Dictionary rawids = new Dictionary(); // maps hardware device ids to XIMouse ids
+
internal readonly X11WindowInfo window;
internal static int XIOpCode { get; private set; }
static bool supported;
@@ -72,12 +79,20 @@ namespace OpenTK.Platform.X11
if (!IsSupported(window.Display))
throw new NotSupportedException("XInput2 not supported.");
- using (XIEventMask mask = new XIEventMask(1, XIEventMasks.RawButtonPressMask |
- XIEventMasks.RawButtonReleaseMask | XIEventMasks.RawMotionMask))
+ using (new XLock(API.DefaultDisplay))
+ using (XIEventMask mask = new XIEventMask(1,
+ XIEventMasks.RawButtonPressMask |
+ XIEventMasks.RawButtonReleaseMask |
+ XIEventMasks.RawMotionMask |
+ XIEventMasks.MotionMask |
+ XIEventMasks.ButtonPressMask |
+ XIEventMasks.ButtonReleaseMask |
+ XIEventMasks.DeviceChangedMask))
{
- Functions.XISelectEvents(window.Display, window.Handle, mask);
- Functions.XISelectEvents(window.Display, window.RootWindow, mask);
+ XI.SelectEvents(window.Display, window.Handle, mask);
}
+
+ UpdateDevices();
}
// Checks whether XInput2 is supported on the specified display.
@@ -90,14 +105,51 @@ namespace OpenTK.Platform.X11
using (new XLock(display))
{
int major, ev, error;
- if (Functions.XQueryExtension(display, "XInputExtension", out major, out ev, out error) == 0)
+ if (Functions.XQueryExtension(display, "XInputExtension", out major, out ev, out error) != 0)
{
- return false;
+ XIOpCode = major;
+
+ int minor = 2;
+ while (minor >= 0)
+ {
+ if (XI.QueryVersion(display, ref major, ref minor) == ErrorCodes.Success)
+ {
+ return true;
+ }
+ minor--;
+ }
}
- XIOpCode = major;
}
- return true;
+ return false;
+ }
+
+ void UpdateDevices()
+ {
+ int count;
+ unsafe
+ {
+ XIDeviceInfo* list = (XIDeviceInfo*)XI.QueryDevice(window.Display, 1, out count);
+ for (int i = 0; i < count; i++)
+ {
+ if (devices.Count < i)
+ {
+ devices.Add(new XIMouse());
+ }
+ XIMouse d = devices[i];
+ d.State.SetIsConnected(true);
+ d.Info = *(list + i);
+
+ // Map the hardware device id to the current XIMouse id
+ if (!rawids.ContainsKey(d.Info.deviceid))
+ {
+ rawids.Add(d.Info.deviceid, 0);
+ }
+ rawids[d.Info.deviceid] = i;
+ }
+ XI.FreeDeviceInfo((IntPtr)list);
+ }
+
}
#region IMouseDriver2 Members
@@ -106,9 +158,9 @@ namespace OpenTK.Platform.X11
{
ProcessEvents();
MouseState master = new MouseState();
- foreach (MouseState ms in mice)
+ foreach (var d in devices)
{
- master.MergeBits(ms);
+ master.MergeBits(d.State);
}
return master;
}
@@ -116,12 +168,17 @@ namespace OpenTK.Platform.X11
public MouseState GetState(int index)
{
ProcessEvents();
- if (mice.Count > index)
- return mice[index];
+ if (devices.Count > index)
+ return devices[index].State;
else
return new MouseState();
}
+ public MouseState GetCursorState()
+ {
+ return master.State;
+ }
+
public void SetPosition(double x, double y)
{
using (new XLock(window.Display))
@@ -169,79 +226,133 @@ namespace OpenTK.Platform.X11
cookie = e.GenericEventCookie;
if (Functions.XGetEventData(window.Display, ref cookie) != 0)
{
- XIRawEvent raw = (XIRawEvent)
- Marshal.PtrToStructure(cookie.data, typeof(XIRawEvent));
-
- if (!rawids.ContainsKey(raw.deviceid))
- {
- mice.Add(new MouseState());
- rawids.Add(raw.deviceid, mice.Count - 1);
- }
- MouseState state = mice[rawids[raw.deviceid]];
-
- switch (raw.evtype)
+ switch ((XIEventType)cookie.evtype)
{
case XIEventType.RawMotion:
- double x = 0, y = 0;
- if (IsBitSet(raw.valuators.mask, 0))
- {
- x = BitConverter.Int64BitsToDouble(Marshal.ReadInt64(raw.raw_values, 0));
- }
- if (IsBitSet(raw.valuators.mask, 1))
- {
- 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:
- switch (raw.detail)
- {
- case 1: state.EnableBit((int)MouseButton.Left); break;
- case 2: state.EnableBit((int)MouseButton.Middle); break;
- case 3: state.EnableBit((int)MouseButton.Right); break;
- case 4: state.SetScrollRelative(0, 1); break;
- case 5: state.SetScrollRelative(0, -1); break;
- case 6: state.EnableBit((int)MouseButton.Button1); break;
- case 7: state.EnableBit((int)MouseButton.Button2); break;
- case 8: state.EnableBit((int)MouseButton.Button3); break;
- case 9: state.EnableBit((int)MouseButton.Button4); break;
- case 10: state.EnableBit((int)MouseButton.Button5); break;
- case 11: state.EnableBit((int)MouseButton.Button6); break;
- case 12: state.EnableBit((int)MouseButton.Button7); break;
- case 13: state.EnableBit((int)MouseButton.Button8); break;
- case 14: state.EnableBit((int)MouseButton.Button9); break;
- }
+ case XIEventType.RawButtonRelease:
+ // Delivered to all XIMouse instances
+ ProcessRawEvent(ref cookie);
break;
- case XIEventType.RawButtonRelease:
- switch (raw.detail)
- {
- case 1: state.DisableBit((int)MouseButton.Left); break;
- case 2: state.DisableBit((int)MouseButton.Middle); break;
- case 3: state.DisableBit((int)MouseButton.Right); break;
- case 6: state.DisableBit((int)MouseButton.Button1); break;
- case 7: state.DisableBit((int)MouseButton.Button2); break;
- case 8: state.DisableBit((int)MouseButton.Button3); break;
- case 9: state.DisableBit((int)MouseButton.Button4); break;
- case 10: state.DisableBit((int)MouseButton.Button5); break;
- case 11: state.DisableBit((int)MouseButton.Button6); break;
- case 12: state.DisableBit((int)MouseButton.Button7); break;
- case 13: state.DisableBit((int)MouseButton.Button8); break;
- case 14: state.DisableBit((int)MouseButton.Button9); break;
- }
+ case XIEventType.Motion:
+ case XIEventType.ButtonPress:
+ case XIEventType.ButtonRelease:
+ // Delivered only to the actual mouse cursor XIMouse instance
+ ProcessEvent(ref cookie);
+ break;
+
+ case XIEventType.DeviceChanged:
+ UpdateDevices();
break;
}
- mice[rawids[raw.deviceid]] = state;
}
Functions.XFreeEventData(window.Display, ref cookie);
}
- }
+ }
+ }
+
+ void ProcessEvent(ref XGenericEventCookie cookie)
+ {
+ XIDeviceEvent e = (XIDeviceEvent)
+ Marshal.PtrToStructure(cookie.data, typeof(XIDeviceEvent));
+
+ master.State.SetIsConnected(true);
+ master.State.X = (int)Math.Round(e.root_x);
+ master.State.Y = (int)Math.Round(e.root_y);
+
+ }
+
+ void ProcessRawEvent(ref XGenericEventCookie cookie)
+ {
+ XIRawEvent raw = (XIRawEvent)
+ Marshal.PtrToStructure(cookie.data, typeof(XIRawEvent));
+
+ if (!rawids.ContainsKey(raw.deviceid))
+ {
+ Debug.Print("Unknown mouse device {0} encountered, ignoring.", raw.deviceid);
+ return;
+ }
+
+ var d = devices[rawids[raw.deviceid]];
+
+ switch (raw.evtype)
+ {
+ case XIEventType.RawMotion:
+ double x = 0, y = 0;
+ double h = 0, v = 0;
+ for (int i = 0; i < d.Info.num_classes; i++)
+ {
+ unsafe
+ {
+ XIAnyClassInfo* info = (XIAnyClassInfo*)d.Info.classes + i;
+ switch (info->type)
+ {
+ case XIClassType.Valuator:
+ {
+ XIValuatorClassInfo* n = (XIValuatorClassInfo*)info;
+ }
+ break;
+ }
+ }
+ }
+
+ if (IsBitSet(raw.valuators.mask, 0))
+ {
+ x = BitConverter.Int64BitsToDouble(Marshal.ReadInt64(raw.raw_values, 0));
+ }
+ if (IsBitSet(raw.valuators.mask, 1))
+ {
+ y = BitConverter.Int64BitsToDouble(Marshal.ReadInt64(raw.raw_values, 8));
+ }
+ break;
+ /*
+ if (!CheckMouseWarp(x, y))
+ {
+ state.X += (int)x;
+ state.Y += (int)y;
+ }
+ break;
+
+ case XIEventType.RawButtonPress:
+ switch (raw.detail)
+ {
+ case 1: state.EnableBit((int)MouseButton.Left); break;
+ case 2: state.EnableBit((int)MouseButton.Middle); break;
+ case 3: state.EnableBit((int)MouseButton.Right); break;
+ case 4: state.SetScrollRelative(0, 1); break;
+ case 5: state.SetScrollRelative(0, -1); break;
+ case 6: state.EnableBit((int)MouseButton.Button1); break;
+ case 7: state.EnableBit((int)MouseButton.Button2); break;
+ case 8: state.EnableBit((int)MouseButton.Button3); break;
+ case 9: state.EnableBit((int)MouseButton.Button4); break;
+ case 10: state.EnableBit((int)MouseButton.Button5); break;
+ case 11: state.EnableBit((int)MouseButton.Button6); break;
+ case 12: state.EnableBit((int)MouseButton.Button7); break;
+ case 13: state.EnableBit((int)MouseButton.Button8); break;
+ case 14: state.EnableBit((int)MouseButton.Button9); break;
+ }
+ break;
+
+ case XIEventType.RawButtonRelease:
+ switch (raw.detail)
+ {
+ case 1: state.DisableBit((int)MouseButton.Left); break;
+ case 2: state.DisableBit((int)MouseButton.Middle); break;
+ case 3: state.DisableBit((int)MouseButton.Right); break;
+ case 6: state.DisableBit((int)MouseButton.Button1); break;
+ case 7: state.DisableBit((int)MouseButton.Button2); break;
+ case 8: state.DisableBit((int)MouseButton.Button3); break;
+ case 9: state.DisableBit((int)MouseButton.Button4); break;
+ case 10: state.DisableBit((int)MouseButton.Button5); break;
+ case 11: state.DisableBit((int)MouseButton.Button6); break;
+ case 12: state.DisableBit((int)MouseButton.Button7); break;
+ case 13: state.DisableBit((int)MouseButton.Button8); break;
+ case 14: state.DisableBit((int)MouseButton.Button9); break;
+ }
+ break;*/
+ }
+ //mice[rawids[raw.deviceid]] = state;
}
static bool IsEventValid(IntPtr display, ref XEvent e, IntPtr arg)
@@ -249,7 +360,10 @@ namespace OpenTK.Platform.X11
return e.GenericEventCookie.extension == arg.ToInt32() &&
(e.GenericEventCookie.evtype == (int)XIEventType.RawMotion ||
e.GenericEventCookie.evtype == (int)XIEventType.RawButtonPress ||
- e.GenericEventCookie.evtype == (int)XIEventType.RawButtonRelease);
+ e.GenericEventCookie.evtype == (int)XIEventType.RawButtonRelease ||
+ e.GenericEventCookie.evtype == (int)XIEventType.Motion ||
+ e.GenericEventCookie.evtype == (int)XIEventType.ButtonPress ||
+ e.GenericEventCookie.evtype == (int)XIEventType.ButtonRelease);
}
static bool IsBitSet(IntPtr mask, int bit)