diff -r b2ed6fc37d95 -r fa97ff5ceb7b RemoteControlDevice.cs --- a/RemoteControlDevice.cs Wed Nov 05 10:19:02 2014 +0100 +++ b/RemoteControlDevice.cs Wed Nov 05 19:39:59 2014 +0100 @@ -4,8 +4,59 @@ using System.Diagnostics; -namespace BruceThomas.Devices.RemoteControl +namespace Devices.RemoteControl { + + public static class Hid + { + /// + /// From USB HID usage tables. + /// http://www.usb.org/developers/hidpage#HID_Usage + /// http://www.usb.org/developers/devclass_docs/Hut1_12v2.pdf + /// + public enum UsagePage : ushort + { + Undefined = 0, + GenericDesktopControl, + SimulationControl, + VirtualRealityControl, + SportControl, + GameControl, + GenericDeviceControl, + Keyboard, + LightEmittingDiode, + Button, + Ordinal, + Telephony, + Consumer, + Digitiser, + PhysicalInterfaceDevice = 0x0f, + Unicode = 0x10, + AlphaNumericDisplay = 0x14, + MedicalInstruments = 0x40, + MonitorPage0 = 0x80, + MonitorPage1, + MonitorPage2, + MonitorPage3, + PowerPage0, + PowerPage1, + PowerPage2, + PowerPage3, + BarCodeScanner = 0x8c, + Scale, + MagneticStripeReader, + ReservedPointOfSale, + CameraControl, + Arcade, + // http://msdn.microsoft.com/en-us/library/windows/desktop/bb417079.aspx + MceRemote = 0xffbc, + TerraTecRemote = 0xffcc + } + + public const ushort MceRemoteUsage = 0x88; + } + + public enum InputDevice { Key, @@ -70,14 +121,14 @@ } /// - /// + /// /// public enum MceButton { /// /// Not defined by the Microsoft specs. /// - Null = 0x00, + Null = 0x00, GreenStart = 0x0D, ClosedCaptioning = 0x2B, Teletext = 0x5A, @@ -193,7 +244,7 @@ NetworkSelection = 0x2C, BlueRayTool = 0x78, ChannelInfo = 0x41, - VideoSelection = 0x61 + VideoSelection = 0x61 } public enum HpMceButton @@ -395,14 +446,62 @@ } + [StructLayout(LayoutKind.Sequential, Pack=1)] + internal struct RID_DEVICE_INFO_MOUSE + { + public uint dwId; + public uint dwNumberOfButtons; + public uint dwSampleRate; + public bool fHasHorizontalWheel; + } + + + [StructLayout(LayoutKind.Sequential, Pack=1)] + internal struct RID_DEVICE_INFO_KEYBOARD + { + public uint dwType; + public uint dwSubType; + public uint dwKeyboardMode; + public uint dwNumberOfFunctionKeys; + public uint dwNumberOfIndicators; + public uint dwNumberOfKeysTotal; + } + + [StructLayout(LayoutKind.Sequential, Pack=1)] + internal struct RID_DEVICE_INFO_HID + { + public uint dwVendorId; + public uint dwProductId; + public uint dwVersionNumber; + public ushort usUsagePage; + public ushort usUsage; + } + + [StructLayout(LayoutKind.Explicit, Pack=1)] + internal struct RID_DEVICE_INFO + { + [FieldOffset(0)] + public uint cbSize; + [FieldOffset(4)] + public uint dwType; + [FieldOffset(8)] + public RID_DEVICE_INFO_MOUSE mouse; + [FieldOffset(8)] + public RID_DEVICE_INFO_KEYBOARD keyboard; + [FieldOffset(8)] + public RID_DEVICE_INFO_HID hid; + } + + + [DllImport("User32.dll")] extern static bool RegisterRawInputDevices(RAWINPUTDEVICE[] pRawInputDevice, uint uiNumDevices, uint cbSize); [DllImport("User32.dll")] extern static uint GetRawInputData(IntPtr hRawInput, uint uiCommand, IntPtr pData, ref uint pcbSize, uint cbSizeHeader); - [DllImport("User32.dll")] - extern static uint GetRawInputDeviceInfo(IntPtr hDevice, uint uiCommand, IntPtr pData, ref uint pcbSize); + [DllImport("User32.dll", SetLastError=true)] + extern static int GetRawInputDeviceInfo(IntPtr hDevice, uint uiCommand, IntPtr pData, ref uint pcbSize); private const int WM_KEYDOWN = 0x0100; @@ -425,10 +524,6 @@ private const int APPCOMMAND_MEDIA_CHANNEL_UP = 51; private const int APPCOMMAND_MEDIA_CHANNEL_DOWN = 52; - private const int RIM_TYPEMOUSE = 0; - private const int RIM_TYPEKEYBOARD = 1; - private const int RIM_TYPEHID = 2; - private const int RID_INPUT = 0x10000003; private const int RID_HEADER = 0x10000005; @@ -437,6 +532,37 @@ private const int FAPPCOMMAND_KEY = 0; private const int FAPPCOMMAND_OEM = 0x1000; + /// + /// GetRawInputDeviceInfo pData points to a string that contains the device name. + /// + public const uint RIDI_DEVICENAME = 0x20000007; + /// + /// GetRawInputDeviceInfo For this uiCommand only, the value in pcbSize is the character count (not the byte count). + /// + public const uint RIDI_DEVICEINFO = 0x2000000b; + /// + /// GetRawInputDeviceInfo pData points to an RID_DEVICE_INFO structure. + /// + public const uint RIDI_PREPARSEDDATA = 0x20000005; + + + /// + /// Data comes from a mouse. + /// + public const uint RIM_TYPEMOUSE = 0; + /// + /// Data comes from a keyboard. + /// + public const uint RIM_TYPEKEYBOARD = 1; + /// + /// Data comes from an HID that is not a keyboard or a mouse. + /// + public const uint RIM_TYPEHID = 2; + + + + + public delegate void RemoteControlDeviceEventHandler(object sender, RemoteControlEventArgs e); public event RemoteControlDeviceEventHandler ButtonPressed; @@ -629,7 +755,9 @@ private void ProcessInputCommand(ref Message message) { - RemoteControlButton rcb = RemoteControlButton.Unknown; + + + uint dwSize = 0; uint sizeOfHeader=(uint)Marshal.SizeOf(typeof(RAWINPUTHEADER)); @@ -638,20 +766,54 @@ GetRawInputData(message.LParam, RID_INPUT, IntPtr.Zero, ref dwSize, sizeOfHeader); //Allocate a large enough buffer - IntPtr buffer = Marshal.AllocHGlobal((int) dwSize); + IntPtr rawInputBuffer = Marshal.AllocHGlobal((int) dwSize); try { - if(buffer == IntPtr.Zero) + if(rawInputBuffer == IntPtr.Zero) return; //Now read our RAWINPUT data - if (GetRawInputData(message.LParam, RID_INPUT, buffer, ref dwSize, (uint) Marshal.SizeOf(typeof(RAWINPUTHEADER))) != dwSize) + if (GetRawInputData(message.LParam, RID_INPUT, rawInputBuffer, ref dwSize, (uint) Marshal.SizeOf(typeof(RAWINPUTHEADER))) != dwSize) { return; } //Cast our buffer - RAWINPUT raw = (RAWINPUT)Marshal.PtrToStructure(buffer, typeof(RAWINPUT)); + RAWINPUT raw = (RAWINPUT)Marshal.PtrToStructure(rawInputBuffer, typeof(RAWINPUT)); + + //Get Device Info + uint deviceInfoSize = (uint)Marshal.SizeOf(typeof(RID_DEVICE_INFO)); + IntPtr deviceInfoBuffer = Marshal.AllocHGlobal((int)deviceInfoSize); + + int res = GetRawInputDeviceInfo(raw.header.hDevice, RIDI_DEVICEINFO, deviceInfoBuffer, ref deviceInfoSize); + if (res <= 0) + { + Debug.WriteLine("WM_INPUT could not read device info: " + Marshal.GetLastWin32Error().ToString()); + return; + } + + //Cast our buffer + RID_DEVICE_INFO deviceInfo = (RID_DEVICE_INFO)Marshal.PtrToStructure(deviceInfoBuffer, typeof(RID_DEVICE_INFO)); + + //Check type of input device and quite if we don't like it + switch (deviceInfo.dwType) + { + case RIM_TYPEHID: + Debug.WriteLine("WM_INPUT source device is HID."); + break; + case RIM_TYPEMOUSE: + Debug.WriteLine("WM_INPUT source device is Mouse."); + return; + case RIM_TYPEKEYBOARD: + Debug.WriteLine("WM_INPUT source device is Keyboard."); + return; + default: + Debug.WriteLine("WM_INPUT source device is Unknown."); + return; + } + + //Get Usage Page and Usage + Debug.WriteLine("Usage Page: 0x" + deviceInfo.hid.usUsagePage.ToString("X4") + " Usage: 0x" + deviceInfo.hid.usUsage.ToString("X4")); //Check that our raw input is HID if (raw.header.dwType == RIM_TYPEHID && raw.hid.dwSizeHid>0) @@ -663,7 +825,7 @@ int pRawData = 0; unsafe { - byte* source = (byte*)buffer; + byte* source = (byte*)rawInputBuffer; source += sizeof(RAWINPUTHEADER) + sizeof(RAWHID); pRawData = (int)source; } @@ -674,7 +836,19 @@ //TODO: check size before access int rawData = bRawData[1]; //Get button code //Print HID codes in our debug output - Debug.WriteLine("HID " + raw.hid.dwCount + "/" + raw.hid.dwSizeHid + ":" + bRawData[0].ToString("X2") + bRawData[1].ToString("X2")); + string hidDump = "HID " + raw.hid.dwCount + "/" + raw.hid.dwSizeHid + ":"; + foreach (byte b in bRawData) + { + hidDump += b.ToString("X2"); + } + Debug.WriteLine(hidDump); + + //Make sure both usage page and usage are matching MCE remote + if (deviceInfo.hid.usUsagePage != (ushort)Hid.UsagePage.MceRemote || deviceInfo.hid.usUsage != (ushort)Hid.MceRemoteUsage) + { + Debug.WriteLine("Not MCE remote page and usage."); + return; + } if (Enum.IsDefined(typeof(MceButton), rawData) && rawData!=0) //Our button is a known MCE button { @@ -695,7 +869,7 @@ } finally { - Marshal.FreeHGlobal(buffer); + Marshal.FreeHGlobal(rawInputBuffer); } }