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