* OpenTK.csproj:

* Input/MouseState.cs:
* Platform/X11/Structs.cs:
* Platform/X11/XI2Mouse.cs:
* Platform/X11/Functions.cs:
* Platform/X11/X11Factory.cs: Added XInput2 driver for mice.
  Multi-mouse support pending.

* Platform/X11/X11Mouse.cs: Log the driver type for debugging
  purposes.
This commit is contained in:
the_fiddler 2010-10-21 12:32:00 +00:00
parent 23ad81d12b
commit 80ee257777
7 changed files with 462 additions and 2 deletions

View file

@ -207,6 +207,44 @@ namespace OpenTK.Input
return !left.Equals(right);
}
/// <summary>
/// Compares to an object instance for equality.
/// </summary>
/// <param name="obj">
/// The <see cref="System.Object"/> to compare to.
/// </param>
/// <returns>
/// True if this instance is equal to obj; false otherwise.
/// </returns>
public override bool Equals(object obj)
{
if (obj is MouseState)
{
return this == (MouseState)obj;
}
else
{
return false;
}
}
/// <summary>
/// Generates a hashcode for the current instance.
/// </summary>
/// <returns>
/// A <see cref="System.Int32"/> represting the hashcode for this instance.
/// </returns>
public override int GetHashCode()
{
unsafe
{
fixed (int* b = Buttons)
{
return b->GetHashCode() ^ X.GetHashCode() ^ Y.GetHashCode() ^ WheelPrecise.GetHashCode();
}
}
}
#endregion
#region Internal Members

View file

@ -762,6 +762,7 @@
<Compile Include="Platform\X11\X11Keyboard.cs" />
<Compile Include="Platform\X11\X11Mouse.cs" />
<Compile Include="Input\ButtonState.cs" />
<Compile Include="Platform\X11\XI2Mouse.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<PropertyGroup>

View file

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

View file

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

View file

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

View file

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

View file

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