1.1 --- a/RemoteControlDevice.cs Wed Nov 05 10:19:02 2014 +0100
1.2 +++ b/RemoteControlDevice.cs Wed Nov 05 19:39:59 2014 +0100
1.3 @@ -4,8 +4,59 @@
1.4 using System.Diagnostics;
1.5
1.6
1.7 -namespace BruceThomas.Devices.RemoteControl
1.8 +namespace Devices.RemoteControl
1.9 {
1.10 +
1.11 + public static class Hid
1.12 + {
1.13 + /// <summary>
1.14 + /// From USB HID usage tables.
1.15 + /// http://www.usb.org/developers/hidpage#HID_Usage
1.16 + /// http://www.usb.org/developers/devclass_docs/Hut1_12v2.pdf
1.17 + /// </summary>
1.18 + public enum UsagePage : ushort
1.19 + {
1.20 + Undefined = 0,
1.21 + GenericDesktopControl,
1.22 + SimulationControl,
1.23 + VirtualRealityControl,
1.24 + SportControl,
1.25 + GameControl,
1.26 + GenericDeviceControl,
1.27 + Keyboard,
1.28 + LightEmittingDiode,
1.29 + Button,
1.30 + Ordinal,
1.31 + Telephony,
1.32 + Consumer,
1.33 + Digitiser,
1.34 + PhysicalInterfaceDevice = 0x0f,
1.35 + Unicode = 0x10,
1.36 + AlphaNumericDisplay = 0x14,
1.37 + MedicalInstruments = 0x40,
1.38 + MonitorPage0 = 0x80,
1.39 + MonitorPage1,
1.40 + MonitorPage2,
1.41 + MonitorPage3,
1.42 + PowerPage0,
1.43 + PowerPage1,
1.44 + PowerPage2,
1.45 + PowerPage3,
1.46 + BarCodeScanner = 0x8c,
1.47 + Scale,
1.48 + MagneticStripeReader,
1.49 + ReservedPointOfSale,
1.50 + CameraControl,
1.51 + Arcade,
1.52 + // http://msdn.microsoft.com/en-us/library/windows/desktop/bb417079.aspx
1.53 + MceRemote = 0xffbc,
1.54 + TerraTecRemote = 0xffcc
1.55 + }
1.56 +
1.57 + public const ushort MceRemoteUsage = 0x88;
1.58 + }
1.59 +
1.60 +
1.61 public enum InputDevice
1.62 {
1.63 Key,
1.64 @@ -70,14 +121,14 @@
1.65 }
1.66
1.67 /// <summary>
1.68 - ///
1.69 + ///
1.70 /// </summary>
1.71 public enum MceButton
1.72 {
1.73 /// <summary>
1.74 /// Not defined by the Microsoft specs.
1.75 /// </summary>
1.76 - Null = 0x00,
1.77 + Null = 0x00,
1.78 GreenStart = 0x0D,
1.79 ClosedCaptioning = 0x2B,
1.80 Teletext = 0x5A,
1.81 @@ -193,7 +244,7 @@
1.82 NetworkSelection = 0x2C,
1.83 BlueRayTool = 0x78,
1.84 ChannelInfo = 0x41,
1.85 - VideoSelection = 0x61
1.86 + VideoSelection = 0x61
1.87 }
1.88
1.89 public enum HpMceButton
1.90 @@ -395,14 +446,62 @@
1.91 }
1.92
1.93
1.94 + [StructLayout(LayoutKind.Sequential, Pack=1)]
1.95 + internal struct RID_DEVICE_INFO_MOUSE
1.96 + {
1.97 + public uint dwId;
1.98 + public uint dwNumberOfButtons;
1.99 + public uint dwSampleRate;
1.100 + public bool fHasHorizontalWheel;
1.101 + }
1.102 +
1.103 +
1.104 + [StructLayout(LayoutKind.Sequential, Pack=1)]
1.105 + internal struct RID_DEVICE_INFO_KEYBOARD
1.106 + {
1.107 + public uint dwType;
1.108 + public uint dwSubType;
1.109 + public uint dwKeyboardMode;
1.110 + public uint dwNumberOfFunctionKeys;
1.111 + public uint dwNumberOfIndicators;
1.112 + public uint dwNumberOfKeysTotal;
1.113 + }
1.114 +
1.115 + [StructLayout(LayoutKind.Sequential, Pack=1)]
1.116 + internal struct RID_DEVICE_INFO_HID
1.117 + {
1.118 + public uint dwVendorId;
1.119 + public uint dwProductId;
1.120 + public uint dwVersionNumber;
1.121 + public ushort usUsagePage;
1.122 + public ushort usUsage;
1.123 + }
1.124 +
1.125 + [StructLayout(LayoutKind.Explicit, Pack=1)]
1.126 + internal struct RID_DEVICE_INFO
1.127 + {
1.128 + [FieldOffset(0)]
1.129 + public uint cbSize;
1.130 + [FieldOffset(4)]
1.131 + public uint dwType;
1.132 + [FieldOffset(8)]
1.133 + public RID_DEVICE_INFO_MOUSE mouse;
1.134 + [FieldOffset(8)]
1.135 + public RID_DEVICE_INFO_KEYBOARD keyboard;
1.136 + [FieldOffset(8)]
1.137 + public RID_DEVICE_INFO_HID hid;
1.138 + }
1.139 +
1.140 +
1.141 +
1.142 [DllImport("User32.dll")]
1.143 extern static bool RegisterRawInputDevices(RAWINPUTDEVICE[] pRawInputDevice, uint uiNumDevices, uint cbSize);
1.144
1.145 [DllImport("User32.dll")]
1.146 extern static uint GetRawInputData(IntPtr hRawInput, uint uiCommand, IntPtr pData, ref uint pcbSize, uint cbSizeHeader);
1.147
1.148 - [DllImport("User32.dll")]
1.149 - extern static uint GetRawInputDeviceInfo(IntPtr hDevice, uint uiCommand, IntPtr pData, ref uint pcbSize);
1.150 + [DllImport("User32.dll", SetLastError=true)]
1.151 + extern static int GetRawInputDeviceInfo(IntPtr hDevice, uint uiCommand, IntPtr pData, ref uint pcbSize);
1.152
1.153
1.154 private const int WM_KEYDOWN = 0x0100;
1.155 @@ -425,10 +524,6 @@
1.156 private const int APPCOMMAND_MEDIA_CHANNEL_UP = 51;
1.157 private const int APPCOMMAND_MEDIA_CHANNEL_DOWN = 52;
1.158
1.159 - private const int RIM_TYPEMOUSE = 0;
1.160 - private const int RIM_TYPEKEYBOARD = 1;
1.161 - private const int RIM_TYPEHID = 2;
1.162 -
1.163 private const int RID_INPUT = 0x10000003;
1.164 private const int RID_HEADER = 0x10000005;
1.165
1.166 @@ -437,6 +532,37 @@
1.167 private const int FAPPCOMMAND_KEY = 0;
1.168 private const int FAPPCOMMAND_OEM = 0x1000;
1.169
1.170 + /// <summary>
1.171 + /// GetRawInputDeviceInfo pData points to a string that contains the device name.
1.172 + /// </summary>
1.173 + public const uint RIDI_DEVICENAME = 0x20000007;
1.174 + /// <summary>
1.175 + /// GetRawInputDeviceInfo For this uiCommand only, the value in pcbSize is the character count (not the byte count).
1.176 + /// </summary>
1.177 + public const uint RIDI_DEVICEINFO = 0x2000000b;
1.178 + /// <summary>
1.179 + /// GetRawInputDeviceInfo pData points to an RID_DEVICE_INFO structure.
1.180 + /// </summary>
1.181 + public const uint RIDI_PREPARSEDDATA = 0x20000005;
1.182 +
1.183 +
1.184 + /// <summary>
1.185 + /// Data comes from a mouse.
1.186 + /// </summary>
1.187 + public const uint RIM_TYPEMOUSE = 0;
1.188 + /// <summary>
1.189 + /// Data comes from a keyboard.
1.190 + /// </summary>
1.191 + public const uint RIM_TYPEKEYBOARD = 1;
1.192 + /// <summary>
1.193 + /// Data comes from an HID that is not a keyboard or a mouse.
1.194 + /// </summary>
1.195 + public const uint RIM_TYPEHID = 2;
1.196 +
1.197 +
1.198 +
1.199 +
1.200 +
1.201 public delegate void RemoteControlDeviceEventHandler(object sender, RemoteControlEventArgs e);
1.202 public event RemoteControlDeviceEventHandler ButtonPressed;
1.203
1.204 @@ -629,7 +755,9 @@
1.205
1.206 private void ProcessInputCommand(ref Message message)
1.207 {
1.208 - RemoteControlButton rcb = RemoteControlButton.Unknown;
1.209 +
1.210 +
1.211 +
1.212 uint dwSize = 0;
1.213
1.214 uint sizeOfHeader=(uint)Marshal.SizeOf(typeof(RAWINPUTHEADER));
1.215 @@ -638,20 +766,54 @@
1.216 GetRawInputData(message.LParam, RID_INPUT, IntPtr.Zero, ref dwSize, sizeOfHeader);
1.217
1.218 //Allocate a large enough buffer
1.219 - IntPtr buffer = Marshal.AllocHGlobal((int) dwSize);
1.220 + IntPtr rawInputBuffer = Marshal.AllocHGlobal((int) dwSize);
1.221 try
1.222 {
1.223 - if(buffer == IntPtr.Zero)
1.224 + if(rawInputBuffer == IntPtr.Zero)
1.225 return;
1.226
1.227 //Now read our RAWINPUT data
1.228 - if (GetRawInputData(message.LParam, RID_INPUT, buffer, ref dwSize, (uint) Marshal.SizeOf(typeof(RAWINPUTHEADER))) != dwSize)
1.229 + if (GetRawInputData(message.LParam, RID_INPUT, rawInputBuffer, ref dwSize, (uint) Marshal.SizeOf(typeof(RAWINPUTHEADER))) != dwSize)
1.230 {
1.231 return;
1.232 }
1.233
1.234 //Cast our buffer
1.235 - RAWINPUT raw = (RAWINPUT)Marshal.PtrToStructure(buffer, typeof(RAWINPUT));
1.236 + RAWINPUT raw = (RAWINPUT)Marshal.PtrToStructure(rawInputBuffer, typeof(RAWINPUT));
1.237 +
1.238 + //Get Device Info
1.239 + uint deviceInfoSize = (uint)Marshal.SizeOf(typeof(RID_DEVICE_INFO));
1.240 + IntPtr deviceInfoBuffer = Marshal.AllocHGlobal((int)deviceInfoSize);
1.241 +
1.242 + int res = GetRawInputDeviceInfo(raw.header.hDevice, RIDI_DEVICEINFO, deviceInfoBuffer, ref deviceInfoSize);
1.243 + if (res <= 0)
1.244 + {
1.245 + Debug.WriteLine("WM_INPUT could not read device info: " + Marshal.GetLastWin32Error().ToString());
1.246 + return;
1.247 + }
1.248 +
1.249 + //Cast our buffer
1.250 + RID_DEVICE_INFO deviceInfo = (RID_DEVICE_INFO)Marshal.PtrToStructure(deviceInfoBuffer, typeof(RID_DEVICE_INFO));
1.251 +
1.252 + //Check type of input device and quite if we don't like it
1.253 + switch (deviceInfo.dwType)
1.254 + {
1.255 + case RIM_TYPEHID:
1.256 + Debug.WriteLine("WM_INPUT source device is HID.");
1.257 + break;
1.258 + case RIM_TYPEMOUSE:
1.259 + Debug.WriteLine("WM_INPUT source device is Mouse.");
1.260 + return;
1.261 + case RIM_TYPEKEYBOARD:
1.262 + Debug.WriteLine("WM_INPUT source device is Keyboard.");
1.263 + return;
1.264 + default:
1.265 + Debug.WriteLine("WM_INPUT source device is Unknown.");
1.266 + return;
1.267 + }
1.268 +
1.269 + //Get Usage Page and Usage
1.270 + Debug.WriteLine("Usage Page: 0x" + deviceInfo.hid.usUsagePage.ToString("X4") + " Usage: 0x" + deviceInfo.hid.usUsage.ToString("X4"));
1.271
1.272 //Check that our raw input is HID
1.273 if (raw.header.dwType == RIM_TYPEHID && raw.hid.dwSizeHid>0)
1.274 @@ -663,7 +825,7 @@
1.275 int pRawData = 0;
1.276 unsafe
1.277 {
1.278 - byte* source = (byte*)buffer;
1.279 + byte* source = (byte*)rawInputBuffer;
1.280 source += sizeof(RAWINPUTHEADER) + sizeof(RAWHID);
1.281 pRawData = (int)source;
1.282 }
1.283 @@ -674,7 +836,19 @@
1.284 //TODO: check size before access
1.285 int rawData = bRawData[1]; //Get button code
1.286 //Print HID codes in our debug output
1.287 - Debug.WriteLine("HID " + raw.hid.dwCount + "/" + raw.hid.dwSizeHid + ":" + bRawData[0].ToString("X2") + bRawData[1].ToString("X2"));
1.288 + string hidDump = "HID " + raw.hid.dwCount + "/" + raw.hid.dwSizeHid + ":";
1.289 + foreach (byte b in bRawData)
1.290 + {
1.291 + hidDump += b.ToString("X2");
1.292 + }
1.293 + Debug.WriteLine(hidDump);
1.294 +
1.295 + //Make sure both usage page and usage are matching MCE remote
1.296 + if (deviceInfo.hid.usUsagePage != (ushort)Hid.UsagePage.MceRemote || deviceInfo.hid.usUsage != (ushort)Hid.MceRemoteUsage)
1.297 + {
1.298 + Debug.WriteLine("Not MCE remote page and usage.");
1.299 + return;
1.300 + }
1.301
1.302 if (Enum.IsDefined(typeof(MceButton), rawData) && rawData!=0) //Our button is a known MCE button
1.303 {
1.304 @@ -695,7 +869,7 @@
1.305 }
1.306 finally
1.307 {
1.308 - Marshal.FreeHGlobal(buffer);
1.309 + Marshal.FreeHGlobal(rawInputBuffer);
1.310 }
1.311 }
1.312