diff --git a/Source/OpenTK/OpenTK.csproj b/Source/OpenTK/OpenTK.csproj
index 502877fb..2e0a1526 100644
--- a/Source/OpenTK/OpenTK.csproj
+++ b/Source/OpenTK/OpenTK.csproj
@@ -697,7 +697,6 @@
-
Always
@@ -798,7 +797,11 @@
Code
+
+
+
+
diff --git a/Source/OpenTK/Platform/X11/Bindings/XI.cs b/Source/OpenTK/Platform/X11/Bindings/XI.cs
index 418acfea..7a4329c6 100644
--- a/Source/OpenTK/Platform/X11/Bindings/XI.cs
+++ b/Source/OpenTK/Platform/X11/Bindings/XI.cs
@@ -44,6 +44,9 @@ namespace OpenTK.Platform.X11
{
const string lib = "libXi";
+ internal const int XIAllDevices = 0;
+ internal const int XIAllMasterDevices = 1;
+
// 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);
@@ -124,4 +127,13 @@ namespace OpenTK.Platform.X11
[DllImport(lib, EntryPoint = "XIQueryVersion")]
internal static extern Status QueryVersion(Display display, ref int major, ref int minor);
}
+
+ enum XIDeviceType
+ {
+ MasterPointer = 1,
+ MasterKeyboard = 2,
+ SlavePointer = 3,
+ SlaveKeyboard = 4,
+ FloatingSlave = 5,
+ }
}
diff --git a/Source/OpenTK/Platform/X11/Bindings/Xkb.cs b/Source/OpenTK/Platform/X11/Bindings/Xkb.cs
new file mode 100644
index 00000000..0be69717
--- /dev/null
+++ b/Source/OpenTK/Platform/X11/Bindings/Xkb.cs
@@ -0,0 +1,302 @@
+#region License
+//
+// Xkb.cs
+//
+// Author:
+// Stefanos Apostolopoulos
+//
+// Copyright (c) 2006-2014
+//
+// 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.Diagnostics;
+using System.Runtime.InteropServices;
+
+namespace OpenTK.Platform.X11
+{
+ using Atom = IntPtr;
+ using KeyCode = Byte;
+ using XkbControlsPtr = IntPtr;
+ using XkbServerMapPtr = IntPtr;
+ using XkbClientMapPtr = IntPtr;
+ using XkbIndicatorPtr = IntPtr;
+ using XkbCompatMapPtr = IntPtr;
+ using XkbGeometryPtr = IntPtr;
+
+ class Xkb
+ {
+ const string lib = "libX11";
+
+ internal const int KeyNameLength = 4;
+ internal const int NumModifiers = 8;
+ internal const int NumVirtualMods = 16;
+ internal const int NumIndicators = 32;
+ internal const int NumKbdGroups = 4;
+ internal const int UseCoreKeyboard = 0x0100;
+
+ [DllImport(lib, EntryPoint = "XkbFreeKeyboard")]
+ unsafe internal extern static void FreeKeyboard(XkbDesc* descr, int which, bool free);
+
+ [DllImport(lib, EntryPoint = "XkbAllocKeyboard")]
+ unsafe internal extern static XkbDesc* AllocKeyboard(IntPtr display);
+
+ [DllImport(lib, EntryPoint = "XkbGetKeyboard")]
+ internal extern static IntPtr GetKeyboard(IntPtr display, XkbKeyboardMask which, int device_id);
+
+ [DllImport(lib, EntryPoint = "XkbGetMap")]
+ internal extern static IntPtr GetMap(IntPtr display, XkbKeyboardMask which, int device_spec);
+
+ [DllImport(lib, EntryPoint = "XkbGetNames")]
+ unsafe internal extern static IntPtr GetNames(IntPtr display, XkbNamesMask which, XkbDesc* xkb);
+
+ [DllImport(lib, EntryPoint = "XkbKeycodeToKeysym")]
+ internal extern static XKey KeycodeToKeysym(IntPtr display, int keycode, int group, int level);
+
+ [DllImport(lib, EntryPoint = "XkbQueryExtension")]
+ internal extern static bool QueryExtension(IntPtr display, out int opcode_rtrn, out int event_rtrn,
+ out int error_rtrn, ref int major_in_out, ref int minor_in_out);
+
+ [DllImport(lib, EntryPoint = "XkbSetDetectableAutoRepeat")]
+ internal extern static bool SetDetectableAutoRepeat(IntPtr display, bool detectable, out bool supported);
+
+ internal static bool IsSupported(IntPtr display)
+ {
+ // The XkbQueryExtension manpage says that we cannot
+ // use XQueryExtension with XKB.
+ int opcode, error, ev;
+ int major = 1;
+ int minor = 0;
+ bool supported = QueryExtension(display, out opcode, out ev, out error, ref major, ref minor);
+ Debug.Print("XKB extension is {0}.", supported ? "supported" : "not supported");
+ if (supported)
+ {
+ Debug.Print("XKB version is {0}.{1}", major, minor);
+ }
+ return supported;
+ }
+ }
+
+ [Flags]
+ enum XkbKeyboardMask
+ {
+ Controls = 1 << 0,
+ ServerMap = 1 << 1,
+ IClientMap = 1 << 2,
+ IndicatorMap = 1 << 3,
+ Names = 1 << 4,
+ CompatibilityMap = 1 << 5,
+ Geometry = 1 << 6,
+ AllComponents = 1 << 7,
+ }
+
+ [Flags]
+ enum XkbNamesMask
+ {
+ Keycodes = 1 << 0,
+ Geometry = 1 << 1,
+ Symbols = 1 << 2,
+ PhysSymbols = 1 << 3,
+ Types = 1 << 4,
+ Compat = 1 << 5,
+ KeyType = 1 << 6,
+ KTLevel = 1 << 7,
+ Indicator = 1 << 8,
+ Key = 1 << 9,
+ KeyAliasesMask = 1 << 10,
+ VirtualMod = 1 << 11,
+ Group = 1 << 12,
+ RG = 1 << 13,
+ Component = 0x3f,
+ All = 0x3fff
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ unsafe struct XkbDesc
+ {
+ internal IntPtr dpy;
+ internal ushort flags;
+ internal ushort device_spec;
+ internal KeyCode min_key_code;
+ internal KeyCode max_key_code;
+
+ internal XkbControlsPtr ctrls;
+ internal XkbServerMapPtr server;
+ internal XkbClientMapPtr map;
+ internal XkbIndicatorPtr indicators;
+ internal XkbNames* names;
+ internal XkbCompatMapPtr compat;
+ internal XkbGeometryPtr geom;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ unsafe struct XkbKeyAlias
+ {
+ internal fixed byte real[Xkb.KeyNameLength];
+ internal fixed byte alias[Xkb.KeyNameLength];
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ unsafe struct XkbKeyName
+ {
+ internal fixed byte name[Xkb.KeyNameLength];
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ unsafe struct XkbNames
+ {
+ #region Structs
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct Groups
+ {
+ Atom groups0;
+ Atom groups1;
+ Atom groups2;
+ Atom groups3;
+ internal Atom this[int i]
+ {
+ get
+ {
+ if (i < 0 || i > 3)
+ throw new IndexOutOfRangeException();
+
+ unsafe
+ {
+ fixed (Atom* ptr = &groups0)
+ {
+ return *(ptr + i);
+ }
+ }
+ }
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct Indicators
+ {
+ Atom indicators0;
+ Atom indicators1;
+ Atom indicators2;
+ Atom indicators3;
+ Atom indicators4;
+ Atom indicators5;
+ Atom indicators6;
+ Atom indicators7;
+ Atom indicators8;
+ Atom indicators9;
+ Atom indicators10;
+ Atom indicators11;
+ Atom indicators12;
+ Atom indicators13;
+ Atom indicators14;
+ Atom indicators15;
+ Atom indicators16;
+ Atom indicators17;
+ Atom indicators18;
+ Atom indicators19;
+ Atom indicators20;
+ Atom indicators21;
+ Atom indicators22;
+ Atom indicators23;
+ Atom indicators24;
+ Atom indicators25;
+ Atom indicators26;
+ Atom indicators27;
+ Atom indicators28;
+ Atom indicators29;
+ Atom indicators30;
+ Atom indicators31;
+ internal Atom this[int i]
+ {
+ get
+ {
+ if (i < 0 || i > 31)
+ throw new IndexOutOfRangeException();
+
+ unsafe
+ {
+ fixed (Atom* ptr = &indicators0)
+ {
+ return *(ptr + i);
+ } }
+ }
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct VMods
+ {
+ Atom vmods0;
+ Atom vmods1;
+ Atom vmods2;
+ Atom vmods3;
+ Atom vmods4;
+ Atom vmods5;
+ Atom vmods6;
+ Atom vmods7;
+ Atom vmods8;
+ Atom vmods9;
+ Atom vmods10;
+ Atom vmods11;
+ Atom vmods12;
+ Atom vmods13;
+ Atom vmods14;
+ Atom vmods15;
+ internal Atom this[int i]
+ {
+ get
+ {
+ if (i < 0 || i > 15)
+ throw new IndexOutOfRangeException();
+
+ unsafe
+ {
+ fixed (Atom* ptr = &vmods0)
+ {
+ return *(ptr + i);
+ }
+ }
+ }
+ }
+ }
+
+ #endregion
+
+ internal Atom keycodes;
+ internal Atom geometry;
+ internal Atom symbols;
+ internal Atom types;
+ internal Atom compat;
+ internal VMods vmods;
+ internal Indicators indicators;
+ internal Groups groups;
+ internal XkbKeyName* keys;
+ internal XkbKeyAlias* key_aliases;
+ internal Atom *radio_groups;
+ internal Atom phys_symbols;
+
+ internal byte num_keys;
+ internal byte num_key_aliases;
+ internal byte num_rg;
+ }
+}
+
diff --git a/Source/OpenTK/Platform/X11/Functions.cs b/Source/OpenTK/Platform/X11/Functions.cs
index 4198a87a..1439e01b 100644
--- a/Source/OpenTK/Platform/X11/Functions.cs
+++ b/Source/OpenTK/Platform/X11/Functions.cs
@@ -439,9 +439,6 @@ namespace OpenTK.Platform.X11
[DllImport("libX11", EntryPoint = "XFilterEvent")]
public extern static bool XFilterEvent(ref XEvent xevent, IntPtr window);
- [DllImport("libX11")]
- public extern static bool XkbSetDetectableAutoRepeat(IntPtr display, bool detectable, out bool supported);
-
[DllImport("libX11")]
public extern static void XPeekEvent(IntPtr display, ref XEvent xevent);
diff --git a/Source/OpenTK/Platform/X11/Structs.cs b/Source/OpenTK/Platform/X11/Structs.cs
index 5b339570..bc2c9adc 100644
--- a/Source/OpenTK/Platform/X11/Structs.cs
+++ b/Source/OpenTK/Platform/X11/Structs.cs
@@ -945,7 +945,7 @@ namespace OpenTK.Platform.X11
public byte pad;
}
- internal enum Atom
+ internal enum AtomName
{
AnyPropertyType = 0,
XA_PRIMARY = 1,
@@ -1688,7 +1688,7 @@ namespace OpenTK.Platform.X11
{
public int deviceid;
public IntPtr name; // byte*
- public int use;
+ public XIDeviceType use;
public int attachment;
public Bool enabled;
public int num_classes;
diff --git a/Source/OpenTK/Platform/X11/X11Factory.cs b/Source/OpenTK/Platform/X11/X11Factory.cs
index af68e9fb..2e5a6634 100644
--- a/Source/OpenTK/Platform/X11/X11Factory.cs
+++ b/Source/OpenTK/Platform/X11/X11Factory.cs
@@ -34,7 +34,25 @@ namespace OpenTK.Platform.X11
{
class X11Factory : PlatformFactoryBase
{
- static IMouseDriver2 MouseDriver;
+ IInputDriver2 input_driver;
+ IInputDriver2 InputDriver
+ {
+ get
+ {
+ if (input_driver == null)
+ {
+ if (XI2MouseKeyboard.IsSupported(IntPtr.Zero))
+ {
+ input_driver = new XI2Input();
+ }
+ else
+ {
+ input_driver = new X11Input();
+ }
+ }
+ return input_driver;
+ }
+ }
#region Constructors
@@ -83,25 +101,17 @@ namespace OpenTK.Platform.X11
public override IKeyboardDriver2 CreateKeyboardDriver()
{
- return new X11Keyboard();
+ return InputDriver.KeyboardDriver;
}
public override IMouseDriver2 CreateMouseDriver()
{
- lock (this)
- {
- MouseDriver =
- MouseDriver ??
- (XI2Mouse.IsSupported(IntPtr.Zero) ?
- (IMouseDriver2)new XI2Mouse() : // Requires xorg 1.7 or higher.
- (IMouseDriver2)new X11Mouse()); // Always supported.
- return MouseDriver;
- }
+ return InputDriver.MouseDriver;
}
public override IJoystickDriver2 CreateJoystickDriver()
{
- return new X11Joystick();
+ return InputDriver.JoystickDriver;
}
#endregion
@@ -109,12 +119,12 @@ namespace OpenTK.Platform.X11
protected override void Dispose(bool manual)
{
base.Dispose(manual);
- if (manual && MouseDriver != null)
+ if (manual)
{
- if (MouseDriver is IDisposable)
+ if (input_driver is IDisposable)
{
- (MouseDriver as IDisposable).Dispose();
- MouseDriver = null;
+ (input_driver as IDisposable).Dispose();
+ input_driver = null;
}
}
}
diff --git a/Source/OpenTK/Platform/X11/X11GLNative.cs b/Source/OpenTK/Platform/X11/X11GLNative.cs
index 658eb00b..7291a46e 100644
--- a/Source/OpenTK/Platform/X11/X11GLNative.cs
+++ b/Source/OpenTK/Platform/X11/X11GLNative.cs
@@ -55,7 +55,8 @@ namespace OpenTK.Platform.X11
const int _min_width = 30, _min_height = 30;
- X11WindowInfo window = new X11WindowInfo();
+ readonly X11WindowInfo window = new X11WindowInfo();
+ readonly X11KeyMap KeyMap;
// 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
@@ -230,20 +231,26 @@ namespace OpenTK.Platform.X11
Debug.WriteLine(String.Format("X11GLNative window created successfully (id: {0}).", Handle));
Debug.Unindent();
- // Request that auto-repeat is only set on devices that support it physically.
- // This typically means that it's turned off for keyboards (which is what we want).
- // We prefer this method over XAutoRepeatOff/On, because the latter needs to
- // be reset before the program exits.
- bool supported;
- Functions.XkbSetDetectableAutoRepeat(window.Display, true, out supported);
+ using (new XLock(window.Display))
+ {
+ // Request that auto-repeat is only set on devices that support it physically.
+ // This typically means that it's turned off for keyboards (which is what we want).
+ // We prefer this method over XAutoRepeatOff/On, because the latter needs to
+ // be reset before the program exits.
+ if (Xkb.IsSupported(window.Display))
+ {
+ bool supported;
+ Xkb.SetDetectableAutoRepeat(window.Display, true, out supported);
+ }
+ }
// The XInput2 extension makes keyboard and mouse handling much easier.
// Check whether it is available.
- xi2_supported = XI2Mouse.IsSupported(window.Display);
+ xi2_supported = XI2MouseKeyboard.IsSupported(window.Display);
if (xi2_supported)
{
- xi2_opcode = XI2Mouse.XIOpCode;
- xi2_version = XI2Mouse.XIVersion;
+ xi2_opcode = XI2MouseKeyboard.XIOpCode;
+ xi2_version = XI2MouseKeyboard.XIVersion;
}
exists = true;
@@ -270,6 +277,7 @@ namespace OpenTK.Platform.X11
{
window.Screen = Functions.XDefaultScreen(window.Display); //API.DefaultScreen;
window.RootWindow = Functions.XRootWindow(window.Display, window.Screen); // API.RootWindow;
+ KeyMap = new X11KeyMap(window.Display);
}
Debug.Print("Display: {0}, Screen {1}, Root window: {2}", window.Display, window.Screen,
@@ -658,7 +666,7 @@ namespace OpenTK.Platform.X11
{
Functions.XGetWindowProperty(window.Display, window.Handle,
_atom_net_frame_extents, IntPtr.Zero, new IntPtr(16), false,
- (IntPtr)Atom.XA_CARDINAL, out atom, out format, out nitems, out bytes_after, ref prop);
+ (IntPtr)AtomName.XA_CARDINAL, out atom, out format, out nitems, out bytes_after, ref prop);
}
if ((prop != IntPtr.Zero))
@@ -851,7 +859,7 @@ namespace OpenTK.Platform.X11
case XEventName.KeyRelease:
bool pressed = e.type == XEventName.KeyPress;
Key key;
- if (X11KeyMap.TranslateKey(ref e.KeyEvent, out key))
+ if (KeyMap.TranslateKey(ref e.KeyEvent, out key))
{
if (pressed)
{
@@ -986,6 +994,7 @@ namespace OpenTK.Platform.X11
{
Debug.Print("keybard mapping refreshed");
Functions.XRefreshKeyboardMapping(ref e.MappingEvent);
+ KeyMap.RefreshKeycodes(window.Display);
}
break;
@@ -1719,7 +1728,6 @@ namespace OpenTK.Platform.X11
}
window.Dispose();
- window = null;
}
}
else
diff --git a/Source/OpenTK/Platform/X11/X11Input.cs b/Source/OpenTK/Platform/X11/X11Input.cs
new file mode 100644
index 00000000..50bdf4a3
--- /dev/null
+++ b/Source/OpenTK/Platform/X11/X11Input.cs
@@ -0,0 +1,97 @@
+#region License
+//
+// X11Input.cs
+//
+// Author:
+// thefiddler
+//
+// Copyright (c) 2006-2014
+//
+// 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.Diagnostics;
+using OpenTK.Input;
+
+namespace OpenTK.Platform.X11
+{
+ class X11Input : IInputDriver2
+ {
+ readonly X11Mouse mouse = new X11Mouse();
+ readonly X11Keyboard keyboard = new X11Keyboard();
+ readonly X11Joystick joystick = new X11Joystick();
+ readonly IGamePadDriver gamepad = new MappedGamePadDriver();
+
+ internal X11Input()
+ {
+ Debug.WriteLine("Using X11 core input driver.");
+ Debug.WriteLine("[Warning] Mouse functionality will be significantly reduced.");
+ Debug.WriteLine("[Warning] Copy OpenTK.dll.config to use the XI2 input driver instead.");
+ }
+
+ #region IInputDriver2 Members
+
+ public IMouseDriver2 MouseDriver
+ {
+ get
+ {
+ return mouse;
+ }
+ }
+
+ public IKeyboardDriver2 KeyboardDriver
+ {
+ get
+ {
+ return keyboard;
+ }
+ }
+
+ public IGamePadDriver GamePadDriver
+ {
+ get
+ {
+ return gamepad;
+ }
+ }
+
+ public IJoystickDriver2 JoystickDriver
+ {
+ get
+ {
+ return joystick;
+ }
+ }
+
+ #endregion
+
+ #region IDisposable Members
+
+ public void Dispose()
+ {
+ joystick.Dispose();
+ }
+
+ #endregion
+
+ }
+}
+
diff --git a/Source/OpenTK/Platform/X11/X11KeyMap.cs b/Source/OpenTK/Platform/X11/X11KeyMap.cs
index 216671e8..39e88a2b 100644
--- a/Source/OpenTK/Platform/X11/X11KeyMap.cs
+++ b/Source/OpenTK/Platform/X11/X11KeyMap.cs
@@ -1,9 +1,30 @@
-#region --- License ---
-/* Licensed under the MIT/X11 license.
- * Copyright (c) 2006-2008 the OpenTK team.
- * This notice may not be removed.
- * See license.txt for licensing detailed licensing details.
- */
+#region License
+//
+// X11KeyMap.cs
+//
+// Author:
+// Stefanos Apostolopoulos
+//
+// Copyright (c) 2006-2014
+//
+// 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;
@@ -14,9 +35,230 @@ using OpenTK.Input;
namespace OpenTK.Platform.X11
{
- static class X11KeyMap
+ class X11KeyMap
{
- public static Key GetKey(XKey key)
+ // Keycode lookup table for current layout
+ readonly Key[] keycodes = new Key[256];
+ readonly bool xkb_supported;
+
+ internal X11KeyMap(IntPtr display)
+ {
+ xkb_supported = Xkb.IsSupported(display);
+ if (xkb_supported)
+ {
+ RefreshKeycodes(display);
+ }
+ }
+
+ // Refreshes the keycodes lookup table based
+ // on the current keyboard layout.
+ internal void RefreshKeycodes(IntPtr display)
+ {
+ // Approach inspired by GLFW: http://www.glfw.org/
+ // Huge props to the GLFW team!
+ if (xkb_supported)
+ {
+ unsafe
+ {
+ // Xkb.GetKeyboard appears to be broken (multiple bug reports across distros)
+ // so use Xkb.AllocKeyboard with Xkb.GetNames instead.
+ XkbDesc* keyboard = Xkb.AllocKeyboard(display);
+ if (keyboard != null)
+ {
+ Xkb.GetNames(display, XkbNamesMask.All, keyboard);
+
+ for (int i = 0; i < keycodes.Length; i++)
+ {
+ keycodes[i] = Key.Unknown;
+ }
+
+ // Map all alphanumeric keys of this layout
+ // Symbols are handled in GetKey() instead.
+ for (int i = keyboard->min_key_code; i <= keyboard->max_key_code; ++i)
+ {
+ string name = new string((sbyte*)keyboard->names->keys[i].name, 0, Xkb.KeyNameLength);
+
+ Key key = Key.Unknown;
+ switch (name)
+ {
+ case "TLDE":
+ key = Key.Tilde;
+ break;
+ case "AE01":
+ key = Key.Number1;
+ break;
+ case "AE02":
+ key = Key.Number2;
+ break;
+ case "AE03":
+ key = Key.Number3;
+ break;
+ case "AE04":
+ key = Key.Number4;
+ break;
+ case "AE05":
+ key = Key.Number5;
+ break;
+ case "AE06":
+ key = Key.Number6;
+ break;
+ case "AE07":
+ key = Key.Number7;
+ break;
+ case "AE08":
+ key = Key.Number8;
+ break;
+ case "AE09":
+ key = Key.Number9;
+ break;
+ case "AE10":
+ key = Key.Number0;
+ break;
+ case "AE11":
+ key = Key.Minus;
+ break;
+ case "AE12":
+ key = Key.Plus;
+ break;
+ case "AD01":
+ key = Key.Q;
+ break;
+ case "AD02":
+ key = Key.W;
+ break;
+ case "AD03":
+ key = Key.E;
+ break;
+ case "AD04":
+ key = Key.R;
+ break;
+ case "AD05":
+ key = Key.T;
+ break;
+ case "AD06":
+ key = Key.Y;
+ break;
+ case "AD07":
+ key = Key.U;
+ break;
+ case "AD08":
+ key = Key.I;
+ break;
+ case "AD09":
+ key = Key.O;
+ break;
+ case "AD10":
+ key = Key.P;
+ break;
+ case "AD11":
+ key = Key.BracketLeft;
+ break;
+ case "AD12":
+ key = Key.BracketRight;
+ break;
+ case "AC01":
+ key = Key.A;
+ break;
+ case "AC02":
+ key = Key.S;
+ break;
+ case "AC03":
+ key = Key.D;
+ break;
+ case "AC04":
+ key = Key.F;
+ break;
+ case "AC05":
+ key = Key.G;
+ break;
+ case "AC06":
+ key = Key.H;
+ break;
+ case "AC07":
+ key = Key.J;
+ break;
+ case "AC08":
+ key = Key.K;
+ break;
+ case "AC09":
+ key = Key.L;
+ break;
+ case "AC10":
+ key = Key.Semicolon;
+ break;
+ case "AC11":
+ key = Key.Quote;
+ break;
+ case "AB01":
+ key = Key.Z;
+ break;
+ case "AB02":
+ key = Key.X;
+ break;
+ case "AB03":
+ key = Key.C;
+ break;
+ case "AB04":
+ key = Key.V;
+ break;
+ case "AB05":
+ key = Key.B;
+ break;
+ case "AB06":
+ key = Key.N;
+ break;
+ case "AB07":
+ key = Key.M;
+ break;
+ case "AB08":
+ key = Key.Comma;
+ break;
+ case "AB09":
+ key = Key.Period;
+ break;
+ case "AB10":
+ key = Key.Slash;
+ break;
+ case "BKSL":
+ key = Key.BackSlash;
+ break;
+ case "LSGT":
+ key = Key.Unknown;
+ break;
+ default:
+ key = Key.Unknown;
+ break;
+ }
+
+ keycodes[i] = key;
+ }
+
+ Xkb.FreeKeyboard(keyboard, 0, true);
+ }
+ }
+ }
+
+ // Translate unknown keys (and symbols) using
+ // regular layout-dependent GetKey()
+ for (int i = 0; i < 256; i++)
+ {
+ if (keycodes[i] == Key.Unknown)
+ {
+ // TranslateKeyCode expects a XKeyEvent structure
+ // Make one up
+ XKeyEvent e = new XKeyEvent();
+ e.display = display;
+ e.keycode = i;
+ Key key = Key.Unknown;
+ if (TranslateKeyEvent(ref e, out key))
+ {
+ keycodes[i] = key;
+ }
+ }
+ }
+ }
+
+ static Key TranslateXKey(XKey key)
{
switch (key)
{
@@ -371,14 +613,26 @@ namespace OpenTK.Platform.X11
}
}
- internal static bool TranslateKey(ref XKeyEvent e, out Key key)
+ bool TranslateKeyEvent(ref XKeyEvent e, out Key key)
+ {
+ if (xkb_supported)
+ {
+ return TranslateKeyXkb(e.display, e.keycode, out key);
+ }
+ else
+ {
+ return TranslateKeyX11(ref e, out key);
+ }
+ }
+
+ bool TranslateKeyX11(ref XKeyEvent e, out Key key)
{
XKey keysym = (XKey)API.LookupKeysym(ref e, 0);
XKey keysym2 = (XKey)API.LookupKeysym(ref e, 1);
- key = X11KeyMap.GetKey(keysym);
+ key = X11KeyMap.TranslateXKey(keysym);
if (key == Key.Unknown)
{
- key = X11KeyMap.GetKey(keysym2);
+ key = X11KeyMap.TranslateXKey(keysym2);
}
if (key == Key.Unknown)
{
@@ -388,6 +642,59 @@ namespace OpenTK.Platform.X11
return key != Key.Unknown;
}
+ bool TranslateKeyXkb(IntPtr display, int keycode, out Key key)
+ {
+ if (keycode < 8 || keycode > 255)
+ {
+ key = Key.Unknown;
+ return false;
+ }
+
+ // Translate the numeric keypad using the secondary group
+ // (equivalent to NumLock = on)
+ XKey keysym = Xkb.KeycodeToKeysym(display, keycode, 0, 1);
+ switch (keysym)
+ {
+ case XKey.KP_0: key = Key.Keypad0; return true;
+ case XKey.KP_1: key = Key.Keypad1; return true;
+ case XKey.KP_2: key = Key.Keypad2; return true;
+ case XKey.KP_3: key = Key.Keypad3; return true;
+ case XKey.KP_4: key = Key.Keypad4; return true;
+ case XKey.KP_5: key = Key.Keypad5; return true;
+ case XKey.KP_6: key = Key.Keypad6; return true;
+ case XKey.KP_7: key = Key.Keypad7; return true;
+ case XKey.KP_8: key = Key.Keypad8; return true;
+ case XKey.KP_9: key = Key.Keypad9; return true;
+ case XKey.KP_Separator:
+ case XKey.KP_Decimal: key = Key.KeypadDecimal; return true;
+ case XKey.KP_Equal: key = Key.Unknown; return false; // Todo: fixme
+ case XKey.KP_Enter: key = Key.KeypadEnter; return true;
+ }
+
+ // Translate non-alphanumeric keys using the primary group
+ keysym = Xkb.KeycodeToKeysym(display, keycode, 0, 0);
+ key = TranslateXKey(keysym);
+ return key != Key.Unknown;
+ }
+
+ internal bool TranslateKey(int keycode, out Key key)
+ {
+ if (keycode < 0 || keycode > 255)
+ {
+ key = Key.Unknown;
+ }
+ else
+ {
+ key = keycodes[keycode];
+ }
+ return key != Key.Unknown;
+ }
+
+ internal bool TranslateKey(ref XKeyEvent e, out Key key)
+ {
+ return TranslateKey(e.keycode, out key);
+ }
+
internal static MouseButton TranslateButton(int button, out float wheelx, out float wheely)
{
wheelx = 0;
diff --git a/Source/OpenTK/Platform/X11/X11Keyboard.cs b/Source/OpenTK/Platform/X11/X11Keyboard.cs
index 3031aef7..288fba05 100644
--- a/Source/OpenTK/Platform/X11/X11Keyboard.cs
+++ b/Source/OpenTK/Platform/X11/X11Keyboard.cs
@@ -39,6 +39,7 @@ namespace OpenTK.Platform.X11
readonly static string name = "Core X11 keyboard";
readonly byte[] keys = new byte[32];
readonly int KeysymsPerKeycode;
+ readonly X11KeyMap KeyMap;
KeyboardState state = new KeyboardState();
public X11Keyboard()
@@ -59,17 +60,15 @@ namespace OpenTK.Platform.X11
ref KeysymsPerKeycode);
Functions.XFree(keysym_ptr);
- try
+ if (Xkb.IsSupported(display))
{
// Request that auto-repeat is only set on devices that support it physically.
// This typically means that it's turned off for keyboards what we want).
// We prefer this method over XAutoRepeatOff/On, because the latter needs to
// be reset before the program exits.
bool supported;
- Functions.XkbSetDetectableAutoRepeat(display, true, out supported);
- }
- catch
- {
+ Xkb.SetDetectableAutoRepeat(display, true, out supported);
+ KeyMap = new X11KeyMap(display);
}
}
}
@@ -104,23 +103,13 @@ namespace OpenTK.Platform.X11
using (new XLock(display))
{
Functions.XQueryKeymap(display, keys);
- for (int keycode = 8; keycode < 256; keycode++)
+ for (int keycode = 0; keycode < 256; keycode++)
{
bool pressed = (keys[keycode >> 3] >> (keycode & 0x07) & 0x01) != 0;
Key key;
-
- for (int mod = 0; mod < KeysymsPerKeycode; mod++)
+ if (KeyMap.TranslateKey(keycode, out key))
{
- IntPtr keysym = Functions.XKeycodeToKeysym(display, (byte)keycode, mod);
- if (keysym != IntPtr.Zero)
- {
- key = X11KeyMap.GetKey((XKey)keysym);
- if (pressed)
- state.EnableBit((int)key);
- else
- state.DisableBit((int)key);
- break;
- }
+ state[key] = pressed;
}
}
}
diff --git a/Source/OpenTK/Platform/X11/XI2Input.cs b/Source/OpenTK/Platform/X11/XI2Input.cs
new file mode 100644
index 00000000..c2e8528e
--- /dev/null
+++ b/Source/OpenTK/Platform/X11/XI2Input.cs
@@ -0,0 +1,95 @@
+#region License
+//
+// XI2Input.cs
+//
+// Author:
+// thefiddler
+//
+// Copyright (c) 2006-2014
+//
+// 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.Diagnostics;
+using OpenTK.Input;
+
+namespace OpenTK.Platform.X11
+{
+ class XI2Input : IInputDriver2
+ {
+ readonly XI2MouseKeyboard mouse_keyboard = new XI2MouseKeyboard();
+ readonly X11Joystick joystick = new X11Joystick();
+ readonly IGamePadDriver gamepad = new MappedGamePadDriver();
+
+ internal XI2Input()
+ {
+ Debug.WriteLine("Using XI2 input driver.");
+ }
+
+ #region IInputDriver2 Members
+
+ public IMouseDriver2 MouseDriver
+ {
+ get
+ {
+ return mouse_keyboard;
+ }
+ }
+
+ public IKeyboardDriver2 KeyboardDriver
+ {
+ get
+ {
+ return mouse_keyboard;
+ }
+ }
+
+ public IGamePadDriver GamePadDriver
+ {
+ get
+ {
+ return gamepad;
+ }
+ }
+
+ public IJoystickDriver2 JoystickDriver
+ {
+ get
+ {
+ return joystick;
+ }
+ }
+
+ #endregion
+
+ #region IDisposable Members
+
+ public void Dispose()
+ {
+ mouse_keyboard.Dispose();
+ joystick.Dispose();
+ }
+
+ #endregion
+
+ }
+}
+
diff --git a/Source/OpenTK/Platform/X11/XI2Mouse.cs b/Source/OpenTK/Platform/X11/XI2MouseKeyboard.cs
similarity index 55%
rename from Source/OpenTK/Platform/X11/XI2Mouse.cs
rename to Source/OpenTK/Platform/X11/XI2MouseKeyboard.cs
index ff6cffe7..1ef31796 100644
--- a/Source/OpenTK/Platform/X11/XI2Mouse.cs
+++ b/Source/OpenTK/Platform/X11/XI2MouseKeyboard.cs
@@ -34,10 +34,12 @@ using OpenTK.Input;
namespace OpenTK.Platform.X11
{
- sealed class XI2Mouse : IMouseDriver2, IDisposable
+ sealed class XI2MouseKeyboard : IKeyboardDriver2, IMouseDriver2, IDisposable
{
const XEventName ExitEvent = XEventName.LASTEvent + 1;
+ readonly object Sync = new object();
readonly Thread ProcessingThread;
+ readonly X11KeyMap KeyMap;
bool disposed;
class XIMouse
@@ -48,6 +50,14 @@ namespace OpenTK.Platform.X11
public XIScrollClassInfo ScrollY = new XIScrollClassInfo { number = -1 };
public XIValuatorClassInfo MotionX = new XIValuatorClassInfo { number = -1 };
public XIValuatorClassInfo MotionY = new XIValuatorClassInfo { number = -1 };
+ public string Name;
+ }
+
+ class XIKeyboard
+ {
+ public KeyboardState State;
+ public XIDeviceInfo DeviceInfo;
+ public string Name;
}
// Atoms
@@ -66,9 +76,12 @@ namespace OpenTK.Platform.X11
//static readonly IntPtr RelVertWheel;
long cursor_x, cursor_y; // For GetCursorState()
- List devices = new List(); // List of connected mice
+ List devices = new List(); // list of connected mice
Dictionary rawids = new Dictionary(); // maps hardware device ids to XIMouse ids
+ List keyboards = new List(); // list of connected keybords
+ Dictionary keyboard_ids = new Dictionary(); // maps hardware device ids to XIKeyboard ids
+
internal readonly X11WindowInfo window;
internal static int XIOpCode { get; private set; }
internal static int XIVersion { get; private set; }
@@ -76,7 +89,7 @@ namespace OpenTK.Platform.X11
static readonly Functions.EventPredicate PredicateImpl = IsEventValid;
readonly IntPtr Predicate = Marshal.GetFunctionPointerForDelegate(PredicateImpl);
- static XI2Mouse()
+ static XI2MouseKeyboard()
{
using (new XLock(API.DefaultDisplay))
{
@@ -106,9 +119,8 @@ namespace OpenTK.Platform.X11
}
}
- public XI2Mouse()
+ public XI2MouseKeyboard()
{
- Debug.WriteLine("Using XI2Mouse.");
window = new X11WindowInfo();
window.Display = Functions.XOpenDisplay(IntPtr.Zero);
@@ -117,6 +129,8 @@ namespace OpenTK.Platform.X11
window.Screen = Functions.XDefaultScreen(window.Display);
window.RootWindow = Functions.XRootWindow(window.Display, window.Screen);
window.Handle = window.RootWindow;
+
+ KeyMap = new X11KeyMap(window.Display);
}
if (!IsSupported(window.Display))
@@ -124,6 +138,8 @@ namespace OpenTK.Platform.X11
using (new XLock(window.Display))
using (XIEventMask mask = new XIEventMask(1,
+ XIEventMasks.RawKeyPressMask |
+ XIEventMasks.RawKeyReleaseMask |
XIEventMasks.RawButtonPressMask |
XIEventMasks.RawButtonReleaseMask |
XIEventMasks.RawMotionMask |
@@ -132,10 +148,9 @@ namespace OpenTK.Platform.X11
(XIEventMasks)(1 << (int)ExitEvent)))
{
XI.SelectEvents(window.Display, window.Handle, mask);
+ UpdateDevices();
}
- UpdateDevices();
-
ProcessingThread = new Thread(ProcessEvents);
ProcessingThread.IsBackground = true;
ProcessingThread.Start();
@@ -181,127 +196,95 @@ namespace OpenTK.Platform.X11
return false;
}
- void UpdateDevices()
+ #region IKeyboardDriver2 Members
+
+ KeyboardState IKeyboardDriver2.GetState()
{
- int count;
- unsafe
+ lock (Sync)
{
- XIDeviceInfo* list = (XIDeviceInfo*)XI.QueryDevice(window.Display, 1, out count);
-
- Debug.Print("Refreshing mouse device list");
- Debug.Print("{0} mouse devices detected", count);
-
- for (int i = 0; i < count; i++)
+ KeyboardState state = new KeyboardState();
+ foreach (XIKeyboard k in keyboards)
{
- if (devices.Count <= i)
- {
- devices.Add(new XIMouse());
- }
- XIMouse d = devices[i];
- d.DeviceInfo = *(list + i);
- d.State.SetIsConnected(d.DeviceInfo.enabled);
- Debug.Print("Device {0} is {1} and has:",
- i, d.DeviceInfo.enabled ? "enabled" : "disabled");
-
- // Decode the XIDeviceInfo to axes, buttons and scroll types
- for (int j = 0; j < d.DeviceInfo.num_classes; j++)
- {
- XIAnyClassInfo* class_info = *((XIAnyClassInfo**)d.DeviceInfo.classes + j);
- switch (class_info->type)
- {
- case XIClassType.Button:
- {
- XIButtonClassInfo* button = (XIButtonClassInfo*)class_info;
- Debug.Print("\t{0} buttons", button->num_buttons);
- }
- break;
-
- case XIClassType.Scroll:
- {
- XIScrollClassInfo* scroll = (XIScrollClassInfo*)class_info;
- switch (scroll->scroll_type)
- {
- case XIScrollType.Vertical:
- Debug.WriteLine("\tSmooth vertical scrolling");
- d.ScrollY = *scroll;
- break;
-
- case XIScrollType.Horizontal:
- Debug.WriteLine("\tSmooth horizontal scrolling");
- d.ScrollX = *scroll;
- break;
-
- default:
- Debug.Print("\tUnknown scrolling type {0}", scroll->scroll_type);
- break;
- }
- }
- break;
-
- case XIClassType.Valuator:
- {
- XIValuatorClassInfo* valuator = (XIValuatorClassInfo*)class_info;
- if (valuator->label == RelX)
- {
- Debug.WriteLine("\tRelative X movement");
- d.MotionX = *valuator;
- }
- else if (valuator->label == RelY)
- {
- Debug.WriteLine("\tRelative Y movement");
- d.MotionY = *valuator;
- }
- }
- break;
- }
- }
-
- // Map the hardware device id to the current XIMouse id
- if (!rawids.ContainsKey(d.DeviceInfo.deviceid))
- {
- rawids.Add(d.DeviceInfo.deviceid, 0);
- }
- rawids[d.DeviceInfo.deviceid] = i;
+ state.MergeBits(k.State);
}
- XI.FreeDeviceInfo((IntPtr)list);
+ return state;
}
-
}
+ KeyboardState IKeyboardDriver2.GetState(int index)
+ {
+ lock (Sync)
+ {
+ if (index >= 0 && index < keyboards.Count)
+ {
+ return keyboards[index].State;
+ }
+ return new KeyboardState();
+ }
+ }
+
+ string IKeyboardDriver2.GetDeviceName(int index)
+ {
+ lock (Sync)
+ {
+ if (index >= 0 && index < keyboards.Count)
+ {
+ return keyboards[index].Name;
+ }
+ return String.Empty;
+ }
+ }
+
+ #endregion
+
#region IMouseDriver2 Members
- public MouseState GetState()
+ MouseState IMouseDriver2.GetState()
{
- MouseState master = new MouseState();
- foreach (var d in devices)
+ lock (Sync)
{
- master.MergeBits(d.State);
+ MouseState master = new MouseState();
+ foreach (var d in devices)
+ {
+ master.MergeBits(d.State);
+ }
+ return master;
}
- return master;
}
- public MouseState GetState(int index)
+ MouseState IMouseDriver2.GetState(int index)
{
- if (devices.Count > index)
- return devices[index].State;
- else
+ lock (Sync)
+ {
+ if (index >= 0 && index < devices.Count)
+ {
+ return devices[index].State;
+ }
return new MouseState();
+ }
}
- public MouseState GetCursorState()
+ MouseState IMouseDriver2.GetCursorState()
{
- MouseState master = GetState();
- master.X = (int)Interlocked.Read(ref cursor_x);
- master.Y = (int)Interlocked.Read(ref cursor_y);
- return master;
+ lock (Sync)
+ {
+ MouseState master = (this as IMouseDriver2).GetState();
+ master.X = (int)Interlocked.Read(ref cursor_x);
+ master.Y = (int)Interlocked.Read(ref cursor_y);
+ return master;
+ }
}
- public void SetPosition(double x, double y)
+ void IMouseDriver2.SetPosition(double x, double y)
{
+ // Note: we cannot use window.Display here, because
+ // that will deadlock the input thread, which is
+ // blocking inside XIfEvent
using (new XLock(API.DefaultDisplay))
{
Functions.XWarpPointer(API.DefaultDisplay,
IntPtr.Zero, window.RootWindow, 0, 0, 0, 0, (int)x, (int)y);
+ Functions.XFlush(API.DefaultDisplay);
Interlocked.Exchange(ref cursor_x, (long)x);
Interlocked.Exchange(ref cursor_y, (long)y);
}
@@ -309,6 +292,127 @@ namespace OpenTK.Platform.X11
#endregion
+ #region Private Members
+
+ void UpdateDevices()
+ {
+ lock (Sync)
+ {
+ devices.Clear();
+ keyboards.Clear();
+
+ int count;
+ unsafe
+ {
+ XIDeviceInfo* list = (XIDeviceInfo*)XI.QueryDevice(window.Display,
+ XI.XIAllDevices, out count);
+
+ Debug.Print("Refreshing input device list");
+ Debug.Print("{0} input devices detected", count);
+
+ for (int i = 0; i < count; i++)
+ {
+ switch ((list + i)->use)
+ {
+ case XIDeviceType.MasterKeyboard:
+ //case XIDeviceType.SlaveKeyboard:
+ {
+ XIKeyboard k = new XIKeyboard();
+ k.DeviceInfo = *(list + i);
+ k.State.SetIsConnected(k.DeviceInfo.enabled);
+ k.Name = Marshal.PtrToStringAnsi(k.DeviceInfo.name);
+ int id = k.DeviceInfo.deviceid;
+ if (!keyboard_ids.ContainsKey(id))
+ {
+ keyboard_ids.Add(k.DeviceInfo.deviceid, 0);
+ }
+ keyboard_ids[id] = keyboards.Count;
+ keyboards.Add(k);
+ }
+ break;
+
+ case XIDeviceType.MasterPointer:
+ //case XIDeviceType.SlavePointer:
+ case XIDeviceType.FloatingSlave:
+ {
+ XIMouse d = new XIMouse();
+ d.DeviceInfo = *(list + i);
+
+ d.State.SetIsConnected(d.DeviceInfo.enabled);
+ d.Name = Marshal.PtrToStringAnsi(d.DeviceInfo.name);
+ Debug.Print("Device {0} \"{1}\" is {2} and has:",
+ i, d.Name, d.DeviceInfo.enabled ? "enabled" : "disabled");
+
+ // Decode the XIDeviceInfo to axes, buttons and scroll types
+ for (int j = 0; j < d.DeviceInfo.num_classes; j++)
+ {
+ XIAnyClassInfo* class_info = *((XIAnyClassInfo**)d.DeviceInfo.classes + j);
+ switch (class_info->type)
+ {
+ case XIClassType.Button:
+ {
+ XIButtonClassInfo* button = (XIButtonClassInfo*)class_info;
+ Debug.Print("\t{0} buttons", button->num_buttons);
+ }
+ break;
+
+ case XIClassType.Scroll:
+ {
+ XIScrollClassInfo* scroll = (XIScrollClassInfo*)class_info;
+ switch (scroll->scroll_type)
+ {
+ case XIScrollType.Vertical:
+ Debug.WriteLine("\tSmooth vertical scrolling");
+ d.ScrollY = *scroll;
+ break;
+
+ case XIScrollType.Horizontal:
+ Debug.WriteLine("\tSmooth horizontal scrolling");
+ d.ScrollX = *scroll;
+ break;
+
+ default:
+ Debug.Print("\tUnknown scrolling type {0}", scroll->scroll_type);
+ break;
+ }
+ }
+ break;
+
+ case XIClassType.Valuator:
+ {
+ XIValuatorClassInfo* valuator = (XIValuatorClassInfo*)class_info;
+ if (valuator->label == RelX)
+ {
+ Debug.WriteLine("\tRelative X movement");
+ d.MotionX = *valuator;
+ }
+ else if (valuator->label == RelY)
+ {
+ Debug.WriteLine("\tRelative Y movement");
+ d.MotionY = *valuator;
+ }
+ }
+ break;
+ }
+ }
+
+ // Map the hardware device id to the current XIMouse id
+ int id = d.DeviceInfo.deviceid;
+ if (!rawids.ContainsKey(id))
+ {
+ rawids.Add(id, 0);
+ }
+ rawids[id] = devices.Count;
+ devices.Add(d);
+ }
+ break;
+ }
+ }
+ XI.FreeDeviceInfo((IntPtr)list);
+ }
+ }
+ }
+
void ProcessEvents()
{
while (!disposed)
@@ -341,6 +445,8 @@ namespace OpenTK.Platform.X11
// Nothing to do
break;
+ case XIEventType.RawKeyPress:
+ case XIEventType.RawKeyRelease:
case XIEventType.RawMotion:
case XIEventType.RawButtonPress:
case XIEventType.RawButtonRelease:
@@ -360,59 +466,73 @@ namespace OpenTK.Platform.X11
void ProcessRawEvent(ref XGenericEventCookie cookie)
{
- unsafe
+ lock (Sync)
{
- XIRawEvent raw = *(XIRawEvent*)cookie.data;
-
- if (!rawids.ContainsKey(raw.deviceid))
+ unsafe
{
- Debug.Print("Unknown mouse device {0} encountered, ignoring.", raw.deviceid);
- return;
- }
+ XIRawEvent raw = *(XIRawEvent*)cookie.data;
+ XIMouse mouse;
+ XIKeyboard keyboard;
- var d = devices[rawids[raw.deviceid]];
+ switch (raw.evtype)
+ {
+ case XIEventType.RawMotion:
+ if (GetMouseDevice(raw.deviceid, out mouse))
+ {
+ ProcessRawMotion(mouse, ref raw);
+ }
+ break;
- switch (raw.evtype)
- {
- case XIEventType.RawMotion:
- ProcessRawMotion(d, ref raw);
- break;
+ case XIEventType.RawButtonPress:
+ case XIEventType.RawButtonRelease:
+ if (GetMouseDevice(raw.deviceid, out mouse))
+ {
+ float dx, dy;
+ MouseButton button = X11KeyMap.TranslateButton(raw.detail, out dx, out dy);
+ mouse.State[button] = raw.evtype == XIEventType.RawButtonPress;
+ }
+ break;
- case XIEventType.RawButtonPress:
- switch (raw.detail)
- {
- case 1: d.State.EnableBit((int)MouseButton.Left); break;
- case 2: d.State.EnableBit((int)MouseButton.Middle); break;
- case 3: d.State.EnableBit((int)MouseButton.Right); break;
- case 8: d.State.EnableBit((int)MouseButton.Button1); break;
- case 9: d.State.EnableBit((int)MouseButton.Button2); break;
- case 10: d.State.EnableBit((int)MouseButton.Button3); break;
- case 11: d.State.EnableBit((int)MouseButton.Button4); break;
- case 12: d.State.EnableBit((int)MouseButton.Button5); break;
- case 13: d.State.EnableBit((int)MouseButton.Button6); break;
- case 14: d.State.EnableBit((int)MouseButton.Button7); break;
- }
- break;
-
- case XIEventType.RawButtonRelease:
- switch (raw.detail)
- {
- case 1: d.State.DisableBit((int)MouseButton.Left); break;
- case 2: d.State.DisableBit((int)MouseButton.Middle); break;
- case 3: d.State.DisableBit((int)MouseButton.Right); break;
- case 8: d.State.DisableBit((int)MouseButton.Button1); break;
- case 9: d.State.DisableBit((int)MouseButton.Button2); break;
- case 10: d.State.DisableBit((int)MouseButton.Button3); break;
- case 11: d.State.DisableBit((int)MouseButton.Button4); break;
- case 12: d.State.DisableBit((int)MouseButton.Button5); break;
- case 13: d.State.DisableBit((int)MouseButton.Button6); break;
- case 14: d.State.DisableBit((int)MouseButton.Button7); break;
- }
- break;
+ case XIEventType.RawKeyPress:
+ case XIEventType.RawKeyRelease:
+ if (GetKeyboardDevice(raw.deviceid, out keyboard))
+ {
+ Key key;
+ if (KeyMap.TranslateKey(raw.detail, out key))
+ {
+ keyboard.State[key] = raw.evtype == XIEventType.RawKeyPress;
+ }
+ }
+ break;
+ }
}
}
}
+ bool GetMouseDevice(int deviceid, out XIMouse mouse)
+ {
+ if (!rawids.ContainsKey(deviceid))
+ {
+ Debug.Print("Unknown mouse device {0} encountered, ignoring.", deviceid);
+ mouse = null;
+ return false;
+ }
+ mouse = devices[rawids[deviceid]];
+ return true;
+ }
+
+ bool GetKeyboardDevice(int deviceid, out XIKeyboard keyboard)
+ {
+ if (!keyboard_ids.ContainsKey(deviceid))
+ {
+ Debug.Print("Unknown keyboard device {0} encountered, ignoring.", deviceid);
+ keyboard = null;
+ return false;
+ }
+ keyboard = keyboards[keyboard_ids[deviceid]];
+ return true;
+ }
+
unsafe static void ProcessRawMotion(XIMouse d, ref XIRawEvent raw)
{
// Note: we use the raw values here, without pointer
@@ -453,6 +573,8 @@ namespace OpenTK.Platform.X11
bool valid = false;
if ((long)e.GenericEventCookie.extension == arg.ToInt64())
{
+ valid |= e.GenericEventCookie.evtype == (int)XIEventType.RawKeyPress;
+ valid |= e.GenericEventCookie.evtype == (int)XIEventType.RawKeyRelease;
valid |= e.GenericEventCookie.evtype == (int)XIEventType.RawMotion;
valid |= e.GenericEventCookie.evtype == (int)XIEventType.RawButtonPress;
valid |= e.GenericEventCookie.evtype == (int)XIEventType.RawButtonRelease;
@@ -470,6 +592,8 @@ namespace OpenTK.Platform.X11
}
}
+ #endregion
+
#region IDisposable Members
public void Dispose()
@@ -496,7 +620,7 @@ namespace OpenTK.Platform.X11
}
}
- ~XI2Mouse()
+ ~XI2MouseKeyboard()
{
Dispose(false);
}