#region --- License --- /* Copyright (c) 2007 Stefanos Apostolopoulos * See license.txt for license info */ #endregion #region --- Using directives --- using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; using System.Diagnostics; using Microsoft.Win32; using OpenTK.Input; #endregion namespace OpenTK.Platform.Windows { internal class WinRawKeyboard : IKeyboardDriver, IDisposable { private List keyboards = new List(); private IntPtr window; #region --- Constructors --- internal WinRawKeyboard() : this(IntPtr.Zero) { } internal WinRawKeyboard(IntPtr windowHandle) { Debug.WriteLine("Initializing keyboard driver (WinRawKeyboard)."); Debug.Indent(); this.window = windowHandle; UpdateKeyboardList(); Debug.Unindent(); } #endregion #region internal static void UpdateKeyboardList() internal void UpdateKeyboardList() { int count = WinRawInput.DeviceCount; RawInputDeviceList[] ridl = new RawInputDeviceList[count]; for (int i = 0; i < count; i++) ridl[i] = new RawInputDeviceList(); Functions.GetRawInputDeviceList(ridl, ref count, API.RawInputDeviceListSize); // Discover keyboard devices: for (int i = 0; i < count; i++) { uint size = 0; Functions.GetRawInputDeviceInfo(ridl[i].Device, RawInputDeviceInfoEnum.DEVICENAME, IntPtr.Zero, ref size); IntPtr name_ptr = Marshal.AllocHGlobal((IntPtr)size); Functions.GetRawInputDeviceInfo(ridl[i].Device, RawInputDeviceInfoEnum.DEVICENAME, name_ptr, ref size); string name = Marshal.PtrToStringAnsi(name_ptr); Marshal.FreeHGlobal(name_ptr); if (name.ToLower().Contains("root")) { // This is a terminal services device, skip it. continue; } else if (ridl[i].Type == RawInputDeviceType.KEYBOARD || ridl[i].Type == RawInputDeviceType.HID) { // This is a keyboard or USB keyboard device. In the latter case, discover if it really is a // keyboard device by qeurying the registry. // remove the \??\ name = name.Substring(4); string[] split = name.Split('#'); string id_01 = split[0]; // ACPI (Class code) string id_02 = split[1]; // PNP0303 (SubClass code) string id_03 = split[2]; // 3&13c0b0c5&0 (Protocol code) // The final part is the class GUID and is not needed here string findme = string.Format( @"System\CurrentControlSet\Enum\{0}\{1}\{2}", id_01, id_02, id_03); RegistryKey regkey = Registry.LocalMachine.OpenSubKey(findme); string deviceDesc = (string)regkey.GetValue("DeviceDesc"); string deviceClass = (string)regkey.GetValue("Class"); if (!String.IsNullOrEmpty(deviceClass) && deviceClass.ToLower().Equals("keyboard")) { KeyboardDevice kb = new KeyboardDevice(); kb.Description = deviceDesc; // Register the keyboard: RawInputDeviceInfo info = new RawInputDeviceInfo(); int devInfoSize = API.RawInputDeviceInfoSize; Functions.GetRawInputDeviceInfo(ridl[i].Device, RawInputDeviceInfoEnum.DEVICEINFO, info, ref devInfoSize); kb.NumberOfLeds = info.Device.Keyboard.NumberOfIndicators; kb.NumberOfFunctionKeys = info.Device.Keyboard.NumberOfFunctionKeys; kb.NumberOfKeys = info.Device.Keyboard.NumberOfKeysTotal; //kb.DeviceID = (info.Device.Keyboard.Type << 32) + info.Device.Keyboard.SubType; kb.DeviceID = ridl[i].Device; //if (!keyboards.Contains(kb)) //{ this.RegisterKeyboardDevice(kb); keyboards.Add(kb); //} } } } } #endregion #region internal void RegisterKeyboardDevice(Keyboard kb) internal void RegisterKeyboardDevice(KeyboardDevice kb) { RawInputDevice[] rid = new RawInputDevice[1]; // Keyboard is 1/6 (page/id). See http://www.microsoft.com/whdc/device/input/HID_HWID.mspx rid[0] = new RawInputDevice(); rid[0].UsagePage = 1; rid[0].Usage = 6; rid[0].Flags = RawInputDeviceFlags.INPUTSINK; rid[0].Target = window; if (!Functions.RegisterRawInputDevices(rid, 1, API.RawInputDeviceSize)) { throw new ApplicationException( String.Format( "Raw input registration failed with error: {0}. Device: {1}", Marshal.GetLastWin32Error(), rid[0].ToString()) ); } else { Debug.Print("Registered keyboard {0}", kb.ToString()); } } #endregion #region internal bool ProcessKeyboardEvent(API.RawInput rin) /// /// Processes raw input events. /// /// /// internal bool ProcessKeyboardEvent(RawInput rin) { //Keyboard key = keyboards[0]; //rin.Header.Device; switch (rin.Header.Type) { case RawInputDeviceType.KEYBOARD: bool pressed = rin.Data.Keyboard.Message == (int)WindowMessage.KEYDOWN || rin.Data.Keyboard.Message == (int)WindowMessage.SYSKEYDOWN; // Find the device where the button was pressed. It can be that the input notification // came not from a physical keyboard device but from a code-generated input message - in // that case, the event goes to the default (first) keyboard. // TODO: Send the event to all keyboards instead of the default one. // TODO: Optimize this! The predicate allocates way to much memory. //int index = keyboards.FindIndex(delegate(KeyboardDevice kb) //{ // return kb.DeviceID == rin.Header.Device; //}); //if (index == -1) index = 0; int index; if (keyboards.Count > 0) index = 0; else return false; // Generic control, shift, alt keys may be sent instead of left/right. // It seems you have to explicitly register left/right events. switch (rin.Data.Keyboard.VKey) { case VirtualKeys.SHIFT: keyboards[index][Input.Key.ShiftLeft] = keyboards[index][Input.Key.ShiftRight] = pressed; return true; case VirtualKeys.CONTROL: keyboards[index][Input.Key.ControlLeft] = keyboards[index][Input.Key.ControlRight] = pressed; return true; case VirtualKeys.MENU: keyboards[index][Input.Key.AltLeft] = keyboards[index][Input.Key.AltRight] = pressed; return true; default: if (!WMInput.KeyMap.ContainsKey(rin.Data.Keyboard.VKey)) Debug.Print("Virtual key {0} ({1}) not mapped.", rin.Data.Keyboard.VKey, (int)rin.Data.Keyboard.VKey); else keyboards[index][WMInput.KeyMap[rin.Data.Keyboard.VKey]] = pressed; return false; } default: throw new ApplicationException("Windows raw keyboard driver received invalid data."); } } #endregion #region --- IInputDevice Members --- public string Description { get { throw new Exception("The method or operation is not implemented."); } } public Input.InputDeviceType DeviceType { get { return Input.InputDeviceType.Keyboard; } } #endregion #region --- IKeyboardDriver Members --- public IList Keyboard { get { return keyboards; } } #endregion #region --- IDisposable Members --- private bool disposed; public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } private void Dispose(bool manual) { if (!disposed) { if (manual) { keyboards.Clear(); } disposed = true; } } ~WinRawKeyboard() { Dispose(false); } #endregion } }