From c5abbe80307e6ecdf32b326ec8470f4128c46767 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Mon, 14 Jul 2014 23:15:44 +0000 Subject: [PATCH] [Linux] Implemented libinput keyboard input --- Source/OpenTK/OpenTK.csproj | 1 + Source/OpenTK/Platform/DeviceCollection.cs | 144 +++++++++++++++++ .../Platform/Linux/Bindings/LibInput.cs | 41 +++++ Source/OpenTK/Platform/Linux/LinuxInput.cs | 149 +++++++++++++++++- 4 files changed, 331 insertions(+), 4 deletions(-) create mode 100644 Source/OpenTK/Platform/DeviceCollection.cs diff --git a/Source/OpenTK/OpenTK.csproj b/Source/OpenTK/OpenTK.csproj index 76514e64..b5f973ce 100644 --- a/Source/OpenTK/OpenTK.csproj +++ b/Source/OpenTK/OpenTK.csproj @@ -820,6 +820,7 @@ + diff --git a/Source/OpenTK/Platform/DeviceCollection.cs b/Source/OpenTK/Platform/DeviceCollection.cs new file mode 100644 index 00000000..57306785 --- /dev/null +++ b/Source/OpenTK/Platform/DeviceCollection.cs @@ -0,0 +1,144 @@ +#region License +// +// DeviceCollection.cs +// +// Author: +// Stefanos A. +// +// 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.Collections.Generic; +using System.Diagnostics; + +namespace OpenTK +{ + // Holds a collection of hardware devices with an associated id. + // Note: 'id' refers to a unique hardware-specific device identifier. + // Note: 'index' refers to the offset of the device in the Devices array. + // Indices are allocated sequentially as devices are added to the system. + // If a device is removed, its index will be reused for the next device + // that is added. + class DeviceCollection : IEnumerable + { + readonly object Sync = new object(); + readonly Dictionary Map = new Dictionary(); + readonly List Devices = new List(); + + #region IEnumerable Members + + public IEnumerator GetEnumerator() + { + return Devices.GetEnumerator(); + } + + #endregion + + #region IEnumerable implementation + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + #endregion + + #region Public Members + + public void Add(int id, T device) + { + if (!Map.ContainsKey(id)) + { + int index = GetIndex(); + Map.Add(id, index); + } + + Devices[Map[id]] = device; + } + + public void Remove(int id) + { + if (!Map.ContainsKey(id)) + { + Debug.Print("Invalid DeviceCollection<{0}> id: {1}", typeof(T).FullName, id); + return; + } + + Devices[Map[id]] = default(T); + Map.Remove(id); + } + + public T FromIndex(int index) + { + if (index >= 0 && index < Devices.Count) + { + return Devices[index]; + } + else + { + return default(T); + } + } + + public T FromHardwareId(int id) + { + if (Map.ContainsKey(id)) + { + return FromIndex(Map[id]); + } + else + { + return default(T); + } + } + + public int Count + { + get { return Map.Count; } + } + + #endregion + + #region Private Members + + // Return the index of the first empty slot in Devices. + // If no empty slot exists, append a new one and return + // that index. + int GetIndex() + { + for (int i = 0; i < Devices.Count; i++) + { + if (Devices[i] == null) + { + return i; + } + } + + Devices.Add(default(T)); + return Devices.Count - 1; + } + + #endregion + } +} + diff --git a/Source/OpenTK/Platform/Linux/Bindings/LibInput.cs b/Source/OpenTK/Platform/Linux/Bindings/LibInput.cs index b5cdd9ef..c557719d 100644 --- a/Source/OpenTK/Platform/Linux/Bindings/LibInput.cs +++ b/Source/OpenTK/Platform/Linux/Bindings/LibInput.cs @@ -55,9 +55,43 @@ namespace OpenTK.Platform.Linux [DllImport(lib, EntryPoint = "libinput_event_destroy", CallingConvention = CallingConvention.Cdecl)] public static extern void DestroyEvent(IntPtr @event); + [DllImport(lib, EntryPoint = "libinput_device_get_sysname", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr DeviceGetNameInternal(IntPtr device); + public static string DeviceGetName(IntPtr device) + { + unsafe + { + return new string((sbyte*)DeviceGetNameInternal(device)); + } + } + + [DllImport(lib, EntryPoint = "libinput_device_get_user_data", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr DeviceGetData(IntPtr device); + + [DllImport(lib, EntryPoint = "libinput_device_set_user_data", CallingConvention = CallingConvention.Cdecl)] + public static extern void DeviceSetData(IntPtr device, IntPtr user_data); + + [DllImport(lib, EntryPoint = "libinput_device_get_output_name", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr DeviceGetOutputNameInternal(IntPtr device); + public static string DeviceGetOutputName(IntPtr device) + { + unsafe + { + sbyte* pname = (sbyte*)DeviceGetOutputNameInternal(device); + return pname == null ? String.Empty : new string(pname); + } + } + + [DllImport(lib, EntryPoint = "libinput_device_has_capability", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool DeviceHasCapability(IntPtr device, DeviceCapability capability); + [DllImport(lib, EntryPoint = "libinput_dispatch", CallingConvention = CallingConvention.Cdecl)] public static extern int Dispatch(IntPtr libinput); + [DllImport(lib, EntryPoint = "libinput_event_get_device", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr GetDevice(IntPtr @event); + [DllImport(lib, EntryPoint = "libinput_get_event", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr GetEvent(IntPtr libinput); @@ -77,6 +111,13 @@ namespace OpenTK.Platform.Linux public static extern void Suspend(IntPtr libinput); } + enum DeviceCapability + { + Keyboard = 0, + Mouse, + Touch + } + enum InputEventType { None = 0, diff --git a/Source/OpenTK/Platform/Linux/LinuxInput.cs b/Source/OpenTK/Platform/Linux/LinuxInput.cs index 19081c9e..d3853d82 100644 --- a/Source/OpenTK/Platform/Linux/LinuxInput.cs +++ b/Source/OpenTK/Platform/Linux/LinuxInput.cs @@ -37,6 +37,62 @@ namespace OpenTK.Platform.Linux { class LinuxInput : IKeyboardDriver2, IMouseDriver2, IDisposable { + class KeyboardDevice + { + readonly IntPtr Device; + string name; + string output; + + public KeyboardDevice(IntPtr device, int id) + { + Device = device; + Id = id; + State.SetIsConnected(true); + } + + public int Id + { + get + { + return GetId(Device); + } + set + { + LibInput.DeviceSetData(Device, (IntPtr)value); + } + } + + public string Name + { + get + { + name = name ?? LibInput.DeviceGetName(Device); + return name; + } + } + + public string Output + { + get + { + output = output ?? LibInput.DeviceGetOutputName(Device); + return output; + } + } + + public KeyboardState State; + } + + class MouseDevice + { + public int FD; + public string Name; + public MouseState State; + } + + DeviceCollection Keyboards = new DeviceCollection(); + DeviceCollection Mice = new DeviceCollection(); + IntPtr udev; IntPtr input_context; InputInterface input_interface = new InputInterface( @@ -102,6 +158,8 @@ namespace OpenTK.Platform.Linux poll_fd.fd = fd; poll_fd.events = PollFlags.In; + LibInput.Resume(input_context); + while (Interlocked.Read(ref exit) == 0) { int ret = Libc.poll(ref poll_fd, 1, -1); @@ -124,8 +182,23 @@ namespace OpenTK.Platform.Linux continue; } + IntPtr device = LibInput.GetDevice(pevent); InputEventType type = LibInput.GetEventType(pevent); - Debug.Print(type.ToString()); + switch (type) + { + case InputEventType.DeviceAdded: + HandleDeviceAdded(input_context, device); + break; + + case InputEventType.DeviceRemoved: + HandleDeviceRemoved(input_context, device); + break; + + case InputEventType.KeyboardKey: + HandleKeyboard(input_context, device); + break; + } + Debug.WriteLine(type.ToString()); LibInput.DestroyEvent(pevent); } @@ -139,23 +212,91 @@ namespace OpenTK.Platform.Linux } } + void HandleDeviceAdded(IntPtr context, IntPtr device) + { + if (LibInput.DeviceHasCapability(device, DeviceCapability.Keyboard)) + { + KeyboardDevice keyboard = new KeyboardDevice(device, Keyboards.Count); + Keyboards.Add(keyboard.Id, keyboard); + } + + if (LibInput.DeviceHasCapability(device, DeviceCapability.Mouse)) + { + Debug.Print("[Linux] Todo: libinput mouse device."); + } + + if (LibInput.DeviceHasCapability(device, DeviceCapability.Touch)) + { + Debug.Print("[Linux] Todo: libinput touch device."); + } + } + + void HandleDeviceRemoved(IntPtr context, IntPtr device) + { + if (LibInput.DeviceHasCapability(device, DeviceCapability.Keyboard)) + { + int id = GetId(device); + Keyboards.Remove(id); + } + } + + void HandleKeyboard(IntPtr context, IntPtr device) + { + int id = GetId(device); + KeyboardDevice keyboard = Keyboards.FromHardwareId(id); + if (keyboard != null) + { + // Todo: update keyboard state + } + else + { + Debug.Print("[Linux] libinput ignoring invalid device id {0}", id); + } + } + + static int GetId(IntPtr device) + { + return LibInput.DeviceGetData(device).ToInt32(); + } + #endregion #region IKeyboardDriver2 implementation KeyboardState IKeyboardDriver2.GetState() { - return new KeyboardState(); + KeyboardState state = new KeyboardState(); + foreach (KeyboardDevice keyboard in Keyboards) + { + state.MergeBits(keyboard.State); + } + return state; } KeyboardState IKeyboardDriver2.GetState(int index) { - return new KeyboardState(); + KeyboardDevice device = Keyboards.FromIndex(index); + if (device != null) + { + return device.State; + } + else + { + return new KeyboardState(); + } } string IKeyboardDriver2.GetDeviceName(int index) { - return String.Empty; + KeyboardDevice device = Keyboards.FromIndex(index); + if (device != null) + { + return device.Name; + } + else + { + return String.Empty; + } } #endregion