[X11] Implemented XI2 keyboard input
authorthefiddler <stapostol@gmail.com>
Thu, 15 May 2014 08:21:06 +0000 (10:21 +0200)
committerthefiddler <stapostol@gmail.com>
Thu, 15 May 2014 08:21:06 +0000 (10:21 +0200)
Source/OpenTK/Platform/X11/XI2Mouse.cs

index ff6cffe..a76d21f 100644 (file)
@@ -34,10 +34,12 @@ using OpenTK.Input;
 
 namespace OpenTK.Platform.X11
 {
-    sealed class XI2Mouse : IMouseDriver2, IDisposable
+    sealed class XI2Mouse : 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<XIMouse> devices = new List<XIMouse>(); // List of connected mice
+        List<XIMouse> devices = new List<XIMouse>(); // list of connected mice
         Dictionary<int, int> rawids = new Dictionary<int, int>(); // maps hardware device ids to XIMouse ids
 
+        List<XIKeyboard> keyboards = new List<XIKeyboard>(); // list of connected keybords
+        Dictionary<int, int> keyboard_ids = new Dictionary<int, int>(); // 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; }
@@ -108,7 +121,6 @@ namespace OpenTK.Platform.X11
 
         public XI2Mouse()
         {
-            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;
-                        }
-                    }
+                    state.MergeBits(k.State);
+                }
+                return state;
+            }
+        }
 
-                    // 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;
+        KeyboardState IKeyboardDriver2.GetState(int index)
+        {
+            lock (Sync)
+            {
+                if (index >= 0 && index < keyboards.Count)
+                {
+                    return keyboards[index].State;
                 }
-                XI.FreeDeviceInfo((IntPtr)list);
+                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;
-                }
-
-                var d = devices[rawids[raw.deviceid]];
+                    XIRawEvent raw = *(XIRawEvent*)cookie.data;
+                    XIMouse mouse;
+                    XIKeyboard keyboard;
 
-                switch (raw.evtype)
-                {
-                    case XIEventType.RawMotion:
-                        ProcessRawMotion(d, ref raw);
-                        break;
+                    switch (raw.evtype)
+                    {
+                        case XIEventType.RawMotion:
+                            if (GetMouseDevice(raw.deviceid, out mouse))
+                            {
+                                ProcessRawMotion(mouse, ref raw);
+                            }
+                            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.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.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()