diff --git a/Source/OpenTK/Input/MouseState.cs b/Source/OpenTK/Input/MouseState.cs
index feae30d5..937833df 100644
--- a/Source/OpenTK/Input/MouseState.cs
+++ b/Source/OpenTK/Input/MouseState.cs
@@ -207,6 +207,44 @@ namespace OpenTK.Input
return !left.Equals(right);
}
+ ///
+ /// Compares to an object instance for equality.
+ ///
+ ///
+ /// The to compare to.
+ ///
+ ///
+ /// True if this instance is equal to obj; false otherwise.
+ ///
+ public override bool Equals(object obj)
+ {
+ if (obj is MouseState)
+ {
+ return this == (MouseState)obj;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ ///
+ /// Generates a hashcode for the current instance.
+ ///
+ ///
+ /// A represting the hashcode for this instance.
+ ///
+ public override int GetHashCode()
+ {
+ unsafe
+ {
+ fixed (int* b = Buttons)
+ {
+ return b->GetHashCode() ^ X.GetHashCode() ^ Y.GetHashCode() ^ WheelPrecise.GetHashCode();
+ }
+ }
+ }
+
#endregion
#region Internal Members
diff --git a/Source/OpenTK/OpenTK.csproj b/Source/OpenTK/OpenTK.csproj
index 693d87bb..8719568a 100644
--- a/Source/OpenTK/OpenTK.csproj
+++ b/Source/OpenTK/OpenTK.csproj
@@ -762,6 +762,7 @@
+
diff --git a/Source/OpenTK/Platform/X11/Functions.cs b/Source/OpenTK/Platform/X11/Functions.cs
index 88a53e40..68a93386 100644
--- a/Source/OpenTK/Platform/X11/Functions.cs
+++ b/Source/OpenTK/Platform/X11/Functions.cs
@@ -94,7 +94,16 @@ namespace OpenTK.Platform.X11
public extern static Bool XCheckWindowEvent(Display display, Window w, EventMask event_mask, ref XEvent event_return);
[DllImport("libX11")]
public extern static Bool XCheckTypedWindowEvent(Display display, Window w, XEventName event_type, ref XEvent event_return);
-
+ [DllImport("libX11")]
+ public extern static Bool XCheckTypedEvent(Display display, XEventName event_type, out XEvent event_return);
+
+
+ public delegate Bool EventPredicate(IntPtr display, ref XEvent e, IntPtr arg);
+ [DllImport("libX11")]
+ public extern static Bool XIfEvent(Display display, ref XEvent e, IntPtr predicate, IntPtr arg );
+ [DllImport("libX11")]
+ public extern static Bool XCheckIfEvent(Display display, ref XEvent e, IntPtr predicate, IntPtr arg );
+
[DllImport("libX11")]
public extern static int XConnectionNumber(IntPtr diplay);
[DllImport("libX11")]
@@ -325,7 +334,7 @@ namespace OpenTK.Platform.X11
public extern static int XQueryBestCursor(IntPtr display, IntPtr drawable, int width, int height, out int best_width, out int best_height);
[DllImport("libX11", EntryPoint = "XQueryExtension")]
- public extern static int XQueryExtension(IntPtr display, string extension_name, ref int major, ref int first_event, ref int first_error);
+ public extern static int XQueryExtension(IntPtr display, string extension_name, out int major, out int first_event, out int first_error);
[DllImport("libX11", EntryPoint = "XWhitePixel")]
public extern static IntPtr XWhitePixel(IntPtr display, int screen_no);
@@ -462,6 +471,27 @@ namespace OpenTK.Platform.X11
[DllImport("libX11")]
public static extern int XRefreshKeyboardMapping(ref XMappingEvent event_map);
+ [DllImport("libX11")]
+ public static extern int XGetEventData(IntPtr display, ref XGenericEventCookie cookie);
+
+ [DllImport("libX11")]
+ public static extern void XFreeEventData(IntPtr display, ref XGenericEventCookie cookie);
+
+ [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);
+ }
+
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 bc7e84a7..180f455d 100644
--- a/Source/OpenTK/Platform/X11/Structs.cs
+++ b/Source/OpenTK/Platform/X11/Structs.cs
@@ -36,6 +36,10 @@ using System.Runtime.InteropServices;
// X11 Version
namespace OpenTK.Platform.X11
{
+ using Bool = System.Boolean;
+ using Time = System.IntPtr;
+ using Window = System.IntPtr;
+
//
// In the structures below, fields of type long are mapped to IntPtr.
// This will work on all platforms where sizeof(long)==sizeof(void*), which
@@ -545,6 +549,29 @@ namespace OpenTK.Platform.X11
public IntPtr pad23;
}
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct XGenericEvent
+ {
+ public int type; // of event. Always GenericEvent
+ public IntPtr serial; // # of last request processed
+ public bool send_event; // true if from SendEvent request
+ public IntPtr display; // Display the event was read from
+ public int extension; // major opcode of extension that caused the event
+ public int evtype; // actual event type.
+ }
+
+ internal struct XGenericEventCookie
+ {
+ public int type; // of event. Always GenericEvent
+ public IntPtr serial; // # of last request processed
+ public bool send_event; // true if from SendEvent request
+ public IntPtr display; // Display the event was read from
+ public int extension; // major opcode of extension that caused the event
+ public int evtype; // actual event type.
+ public uint cookie; // unique event cookie
+ public IntPtr data; // actual event data
+ }
+
[StructLayout(LayoutKind.Explicit)]
internal struct XEvent
{
@@ -612,6 +639,10 @@ namespace OpenTK.Platform.X11
public XErrorEvent ErrorEvent;
[FieldOffset(0)]
public XKeymapEvent KeymapEvent;
+ [FieldOffset(0)]
+ public XGenericEvent GenericEvent;
+ [FieldOffset(0)]
+ public XGenericEventCookie GenericEventCookie;
//[MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst=24)]
//[ FieldOffset(0) ] public int[] pad;
@@ -753,6 +784,7 @@ namespace OpenTK.Platform.X11
ColormapNotify = 32,
ClientMessage = 33,
MappingNotify = 34,
+ GenericEvent = 35,
LASTEvent
}
@@ -1623,4 +1655,164 @@ namespace OpenTK.Platform.X11
XYPixmap = 1,
ZPixmap
}
+
+ // XInput2 structures
+
+ struct XIDeviceInfo
+ {
+ public int deviceid;
+ public IntPtr name; // byte*
+ public int use;
+ public int attachment;
+ public Bool enabled;
+ public int num_classes;
+ public IntPtr classes; // XIAnyClassInfo **
+ }
+
+ struct XIAnyClassInfo
+ {
+ public int type;
+ public int sourceid;
+ }
+
+ struct XIDeviceEvent
+ {
+ public int type; /* GenericEvent */
+ public IntPtr serial; /* # of last request processed by server */
+ public bool send_event; /* true if this came from a SendEvent request */
+ public IntPtr display; /* Display the event was read from */
+ public int extension; /* XI extension offset */
+ public XIEventType evtype;
+ public Time time;
+ public int deviceid;
+ public int sourceid;
+ public int detail;
+ public Window root;
+ public Window @event;
+ public Window child;
+ public double root_x;
+ public double root_y;
+ public double event_x;
+ public double event_y;
+ public int flags;
+ public XIButtonState buttons;
+ public XIValuatorState valuators;
+ public XIModifierState mods;
+ public XIGroupState @group;
+ }
+
+ struct XIRawEvent
+ {
+ public int type; /* GenericEvent */
+ public IntPtr serial; /* # of last request processed by server */
+ public Bool send_event; /* true if this came from a SendEvent request */
+ public IntPtr display; /* Display the event was read from */
+ public int extension; /* XI extension offset */
+ public XIEventType evtype; /* XI_RawKeyPress, XI_RawKeyRelease, etc. */
+ public Time time;
+ public int deviceid;
+ public int sourceid;
+ public int detail;
+ public int flags;
+ public XIValuatorState valuators;
+ public IntPtr raw_values; // double *
+ }
+
+ struct XIButtonState
+ {
+ public int mask_len;
+ public IntPtr mask; // byte*
+ }
+
+ struct XIModifierState
+ {
+ public int @base;
+ public int latched;
+ public int locked;
+ public int effective;
+ }
+
+ struct XIGroupState
+ {
+ public int @base;
+ public int latched;
+ public int locked;
+ public int effective;
+ }
+
+ struct XIValuatorState
+ {
+ public int mask_len;
+ public IntPtr mask; // byte*
+ public IntPtr values; // double*
+ }
+
+ struct XIEventMask : IDisposable
+ {
+ public int deviceid; // 0 = XIAllDevices, 1 = XIAllMasterDevices
+ int mask_len;
+ unsafe XIEventMasks* mask;
+
+ public XIEventMask(int id, XIEventMasks m)
+ {
+ deviceid = id;
+ mask_len = sizeof(XIEventMasks);
+ unsafe
+ {
+ mask = (XIEventMasks*)Marshal.AllocHGlobal(mask_len);
+ *mask = m;
+ }
+ }
+
+ public void Dispose()
+ {
+ unsafe
+ {
+ Marshal.FreeHGlobal(new IntPtr((void*)mask));
+ }
+ }
+ }
+
+ enum XIEventType
+ {
+ DeviceChanged = 1,
+ KeyPress,
+ KeyRelease,
+ ButtonPress,
+ ButtonRelease,
+ Motion,
+ Enter,
+ Leave,
+ FocusIn,
+ FocusOut,
+ HierarchyChanged,
+ PropertyEvent,
+ RawKeyPress,
+ RawKeyRelease,
+ RawButtonPress,
+ RawButtonRelease,
+ RawMotion,
+ LastEvent = RawMotion
+ }
+
+ enum XIEventMasks
+ {
+ DeviceChangedMask = (1 << (int)XIEventType.DeviceChanged),
+ KeyPressMask = (1 << (int)XIEventType.KeyPress),
+ KeyReleaseMask = (1 << (int)XIEventType.KeyRelease),
+ ButtonPressMask = (1 << (int)XIEventType.ButtonPress),
+ ButtonReleaseMask = (1 << (int)XIEventType.ButtonRelease),
+ MotionMask = (1 << (int)XIEventType.Motion),
+ EnterMask = (1 << (int)XIEventType.Enter),
+ LeaveMask = (1 << (int)XIEventType.Leave),
+ FocusInMask = (1 << (int)XIEventType.FocusIn),
+ FocusOutMask = (1 << (int)XIEventType.FocusOut),
+ HierarchyChangedMask = (1 << (int)XIEventType.HierarchyChanged),
+ PropertyEventMask = (1 << (int)XIEventType.PropertyEvent),
+ RawKeyPressMask = (1 << (int)XIEventType.RawKeyPress),
+ RawKeyReleaseMask = (1 << (int)XIEventType.RawKeyRelease),
+ RawButtonPressMask = (1 << (int)XIEventType.RawButtonPress),
+ RawButtonReleaseMask = (1 << (int)XIEventType.RawButtonRelease),
+ RawMotionMask = (1 << (int)XIEventType.RawMotion),
+ }
}
diff --git a/Source/OpenTK/Platform/X11/X11Factory.cs b/Source/OpenTK/Platform/X11/X11Factory.cs
index b1212377..96dd6162 100644
--- a/Source/OpenTK/Platform/X11/X11Factory.cs
+++ b/Source/OpenTK/Platform/X11/X11Factory.cs
@@ -85,6 +85,14 @@ namespace OpenTK.Platform.X11
public virtual OpenTK.Input.IMouseDriver CreateMouseDriver()
{
+ try
+ {
+ // Only supported on xorg 1.7 or higher.
+ return new XI2Mouse(null);
+ }
+ catch (NotSupportedException) { }
+
+ // Should always be supported.
return new X11Mouse(null);
}
diff --git a/Source/OpenTK/Platform/X11/X11Mouse.cs b/Source/OpenTK/Platform/X11/X11Mouse.cs
index 49c8bedc..4b636a99 100644
--- a/Source/OpenTK/Platform/X11/X11Mouse.cs
+++ b/Source/OpenTK/Platform/X11/X11Mouse.cs
@@ -27,6 +27,7 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using OpenTK.Input;
namespace OpenTK.Platform.X11
@@ -60,6 +61,8 @@ namespace OpenTK.Platform.X11
// IntPtr.Zero, IntPtr.Zero);
//Functions.XSelectInput(window.Display, window.RootWindow, new IntPtr((int)window.EventMask));
}
+
+ Debug.WriteLine("Using X11Mouse.");
}
}
diff --git a/Source/OpenTK/Platform/X11/XI2Mouse.cs b/Source/OpenTK/Platform/X11/XI2Mouse.cs
new file mode 100644
index 00000000..0451aa27
--- /dev/null
+++ b/Source/OpenTK/Platform/X11/XI2Mouse.cs
@@ -0,0 +1,188 @@
+ #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.Diagnostics;
+using System.Runtime.InteropServices;
+using OpenTK.Input;
+
+namespace OpenTK.Platform.X11
+{
+ // Todo: multi-mouse support. Right now we aggregate all data into a single mouse device.
+ // This should be easy: just read the device id and route the data to the correct device.
+ sealed class XI2Mouse : IMouseDriver
+ {
+ MouseState state = new MouseState();
+ X11WindowInfo window;
+ readonly int XIOpCode;
+
+ static readonly Functions.EventPredicate PredicateImpl = IsEventValid;
+ readonly IntPtr Predicate = Marshal.GetFunctionPointerForDelegate(PredicateImpl);
+
+ // Can either attach itself to the specified window or can hook the root window.
+ public XI2Mouse(X11WindowInfo win)
+ {
+ if (win != null)
+ {
+ window = win;
+ }
+ else
+ {
+ using (new XLock(API.DefaultDisplay))
+ {
+ window = new X11WindowInfo();
+ window.Display = API.DefaultDisplay;
+ window.Screen = Functions.XDefaultScreen(window.Display);
+ window.RootWindow = Functions.XRootWindow(window.Display, window.Screen);
+ window.WindowHandle = window.RootWindow;
+ }
+ }
+
+ using (new XLock(window.Display))
+ {
+ int major, ev, error;
+ if (Functions.XQueryExtension(window.Display, "XInputExtension", out major, out ev, out error) == 0)
+ {
+ Debug.WriteLine("XInput2 not supported.");
+ throw new NotSupportedException();
+ }
+ XIOpCode = major;
+
+ using (XIEventMask mask = new XIEventMask(1, XIEventMasks.RawButtonPressMask |
+ XIEventMasks.RawButtonReleaseMask | XIEventMasks.RawMotionMask))
+ {
+ Functions.XISelectEvents(window.Display, window.WindowHandle, mask);
+ }
+ }
+ Debug.WriteLine("Using XI2Mouse.");
+ }
+
+ // Todo: remove this
+ public IList Mouse { get { throw new NotSupportedException(); } }
+
+ public MouseState GetState()
+ {
+ ProcessEvents();
+ return state;
+ }
+
+ public MouseState GetState(int index)
+ {
+ ProcessEvents();
+ return state;
+ }
+
+ void ProcessEvents()
+ {
+ while (true)
+ {
+ XEvent e = new XEvent();
+ XGenericEventCookie cookie;
+
+ using (new XLock(window.Display))
+ {
+ if (!Functions.XCheckIfEvent(window.Display, ref e, Predicate, new IntPtr(XIOpCode)))
+ return;
+
+ cookie = e.GenericEventCookie;
+ if (Functions.XGetEventData(window.Display, ref cookie) != 0)
+ {
+ XIRawEvent raw = (XIRawEvent)
+ Marshal.PtrToStructure(cookie.data, typeof(XIRawEvent));
+
+ switch (raw.evtype)
+ {
+ case XIEventType.RawMotion:
+ if (IsBitSet(raw.valuators.mask, 0))
+ state.X += (int)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));
+ 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.WheelPrecise++; break;
+ case 5: state.WheelPrecise--; 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;
+ }
+ }
+ Functions.XFreeEventData(window.Display, ref cookie);
+ }
+ }
+ }
+
+ static bool IsEventValid(IntPtr display, ref XEvent e, IntPtr arg)
+ {
+ return e.GenericEventCookie.extension == arg.ToInt32() &&
+ (e.GenericEventCookie.evtype == (int)XIEventType.RawMotion ||
+ e.GenericEventCookie.evtype == (int)XIEventType.RawButtonPress ||
+ e.GenericEventCookie.evtype == (int)XIEventType.RawButtonRelease);
+ }
+
+ static bool IsBitSet(IntPtr mask, int bit)
+ {
+ unsafe
+ {
+ return (*((byte*)mask + (bit >> 3)) & (1 << (bit & 7))) != 0;
+ }
+ }
+ }
+}
+