Opentk/Source/OpenTK/Platform/Windows/WinRawKeyboard.cs

382 lines
15 KiB
C#
Raw Normal View History

#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<KeyboardDevice> keyboards = new List<KeyboardDevice>();
private IntPtr window;
#region internal static Dictionary<VirtualKeys, Input.Key> KeyMap
internal static Dictionary<VirtualKeys, Input.Key> KeyMap =
new Dictionary<VirtualKeys, Input.Key>((int)VirtualKeys.Last);
private static bool keyMapExists;
/// <summary>
/// Initializes the map between VirtualKeys and OpenTK.Key
/// </summary>
private static void InitKeyMap()
{
if (!keyMapExists)
{
try
{
KeyMap.Add(VirtualKeys.ESCAPE, Key.Escape);
// Function keys
for (int i = 0; i < 24; i++)
{
KeyMap.Add((VirtualKeys)((int)VirtualKeys.F1 + i), Key.F1 + i);
}
// Number keys (0-9)
for (int i = 0; i <= 9; i++)
{
KeyMap.Add((VirtualKeys)(0x30 + i), Key.Number0 + i);
}
// Letters (A-Z)
for (int i = 0; i < 26; i++)
{
KeyMap.Add((VirtualKeys)(0x41 + i), Key.A + i);
}
KeyMap.Add(VirtualKeys.TAB, Key.Tab);
KeyMap.Add(VirtualKeys.CAPITAL, Key.CapsLock);
KeyMap.Add(VirtualKeys.LCONTROL, Key.ControlLeft);
KeyMap.Add(VirtualKeys.LSHIFT, Key.ShiftLeft);
KeyMap.Add(VirtualKeys.LWIN, Key.WinLeft);
KeyMap.Add(VirtualKeys.LMENU, Key.AltLeft);
KeyMap.Add(VirtualKeys.SPACE, Key.Space);
KeyMap.Add(VirtualKeys.RMENU, Key.AltRight);
KeyMap.Add(VirtualKeys.RWIN, Key.WinRight);
KeyMap.Add(VirtualKeys.APPS, Key.Menu);
KeyMap.Add(VirtualKeys.RCONTROL, Key.ControlRight);
KeyMap.Add(VirtualKeys.RSHIFT, Key.ShiftRight);
KeyMap.Add(VirtualKeys.RETURN, Key.Enter);
KeyMap.Add(VirtualKeys.BACK, Key.BackSpace);
KeyMap.Add(VirtualKeys.OEM_1, Key.Semicolon); // Varies by keyboard, ;: on Win2K/US
KeyMap.Add(VirtualKeys.OEM_2, Key.Slash); // Varies by keyboard, /? on Win2K/US
KeyMap.Add(VirtualKeys.OEM_3, Key.Tilde); // Varies by keyboard, `~ on Win2K/US
KeyMap.Add(VirtualKeys.OEM_4, Key.BracketLeft); // Varies by keyboard, [{ on Win2K/US
KeyMap.Add(VirtualKeys.OEM_5, Key.BackSlash); // Varies by keyboard, \| on Win2K/US
KeyMap.Add(VirtualKeys.OEM_6, Key.BracketRight); // Varies by keyboard, ]} on Win2K/US
KeyMap.Add(VirtualKeys.OEM_7, Key.Quote); // Varies by keyboard, '" on Win2K/US
KeyMap.Add(VirtualKeys.OEM_PLUS, Key.Plus); // Invariant: +
KeyMap.Add(VirtualKeys.OEM_COMMA, Key.Comma); // Invariant: ,
KeyMap.Add(VirtualKeys.OEM_MINUS, Key.Minus); // Invariant: -
KeyMap.Add(VirtualKeys.OEM_PERIOD, Key.Period); // Invariant: .
KeyMap.Add(VirtualKeys.HOME, Key.Home);
KeyMap.Add(VirtualKeys.END, Key.End);
KeyMap.Add(VirtualKeys.DELETE, Key.Delete);
KeyMap.Add(VirtualKeys.PRIOR, Key.PageUp);
KeyMap.Add(VirtualKeys.NEXT, Key.PageDown);
KeyMap.Add(VirtualKeys.PRINT, Key.PrintScreen);
KeyMap.Add(VirtualKeys.PAUSE, Key.Pause);
KeyMap.Add(VirtualKeys.NUMLOCK, Key.NumLock);
KeyMap.Add(VirtualKeys.SCROLL, Key.ScrollLock);
KeyMap.Add(VirtualKeys.SNAPSHOT, Key.PrintScreen);
KeyMap.Add(VirtualKeys.CLEAR, Key.Clear);
KeyMap.Add(VirtualKeys.INSERT, Key.Insert);
KeyMap.Add(VirtualKeys.SLEEP, Key.Sleep);
// Keypad
for (int i = 0; i <= 9; i++)
{
KeyMap.Add((VirtualKeys)((int)VirtualKeys.NUMPAD0 + i), Key.Keypad0 + i);
}
KeyMap.Add(VirtualKeys.DECIMAL, Key.KeypadDecimal);
KeyMap.Add(VirtualKeys.ADD, Key.KeypadAdd);
KeyMap.Add(VirtualKeys.SUBTRACT, Key.KeypadSubtract);
KeyMap.Add(VirtualKeys.DIVIDE, Key.KeypadDivide);
KeyMap.Add(VirtualKeys.MULTIPLY, Key.KeypadMultiply);
// Navigation
KeyMap.Add(VirtualKeys.UP, Key.Up);
KeyMap.Add(VirtualKeys.DOWN, Key.Down);
KeyMap.Add(VirtualKeys.LEFT, Key.Left);
KeyMap.Add(VirtualKeys.RIGHT, Key.Right);
}
catch (ArgumentException e)
{
Debug.Print("Exception while creating keymap: '{0}'.", e.ToString());
System.Windows.Forms.MessageBox.Show(
String.Format("Exception while creating keymap: '{0}'.", e.ToString()));
}
finally
{
keyMapExists = true;
}
}
}
#endregion
#region --- Constructors ---
internal WinRawKeyboard()
: this(IntPtr.Zero)
{
}
internal WinRawKeyboard(IntPtr windowHandle)
{
Debug.WriteLine("Initializing keyboard driver (WinRawKeyboard).");
Debug.Indent();
this.window = windowHandle;
InitKeyMap();
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)
/// <summary>
/// Processes raw input events.
/// </summary>
/// <param name="rin"></param>
/// <returns></returns>
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.
int index = keyboards.FindIndex(delegate(KeyboardDevice kb)
{
return kb.DeviceID == rin.Header.Device;
});
if (index == -1) index = 0;
// 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 (!WinRawKeyboard.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][WinRawKeyboard.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<KeyboardDevice> 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
}
}