Consolidating our HID input report parsing.
2 using System.Windows.Forms;
3 using System.Runtime.InteropServices;
4 using System.Diagnostics;
8 namespace Devices.RemoteControl
11 public enum InputDevice
19 public enum RemoteControlButton
76 #region RemoteControlEventArgs
78 public class RemoteControlEventArgs : EventArgs
80 RemoteControlButton _rcb;
84 public RemoteControlEventArgs(RemoteControlButton rcb, InputDevice device)
86 iMceButton = MceButton.Null;
91 public RemoteControlEventArgs(MceButton mce, InputDevice device)
94 _rcb = RemoteControlButton.Unknown;
98 public RemoteControlEventArgs()
100 iMceButton = MceButton.Null;
101 _rcb = RemoteControlButton.Unknown;
102 _device = InputDevice.Key;
105 public RemoteControlButton Button
108 set { _rcb = value; }
111 public MceButton MceButton
113 get { return iMceButton; }
114 set { iMceButton = value; }
117 public InputDevice Device
119 get { return _device; }
120 set { _device = value; }
124 #endregion RemoteControlEventArgs
127 public sealed class RemoteControlDevice
129 public delegate void RemoteControlDeviceEventHandler(object sender, RemoteControlEventArgs e);
130 public event RemoteControlDeviceEventHandler ButtonPressed;
132 public delegate void HidUsageHandler(ushort aUsage);
135 //-------------------------------------------------------------
137 //-------------------------------------------------------------
139 public RemoteControlDevice(IntPtr aHWND)
141 // Register the input device to receive the commands from the
142 // remote device. See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwmt/html/remote_control.asp
143 // for the vendor defined usage page.
145 RAWINPUTDEVICE[] rid = new RAWINPUTDEVICE[3];
147 rid[0].usUsagePage = 0xFFBC;
148 rid[0].usUsage = 0x88;
149 rid[0].dwFlags = Const.RIDEV_EXINPUTSINK;
150 rid[0].hwndTarget = aHWND;
152 rid[1].usUsagePage = 0x0C;
153 rid[1].usUsage = 0x01;
154 rid[1].dwFlags = Const.RIDEV_EXINPUTSINK;
155 rid[1].hwndTarget = aHWND;
157 rid[2].usUsagePage = 0x0C;
158 rid[2].usUsage = 0x80;
159 rid[2].dwFlags = Const.RIDEV_EXINPUTSINK;
160 rid[2].hwndTarget = aHWND;
162 if (!Function.RegisterRawInputDevices(rid,(uint) rid.Length,(uint) Marshal.SizeOf(rid[0])))
164 throw new ApplicationException("Failed to register raw input devices: " + Marshal.GetLastWin32Error().ToString());
169 //-------------------------------------------------------------
171 //-------------------------------------------------------------
173 public void ProcessMessage(Message message)
177 case Const.WM_KEYDOWN:
178 ProcessKeyDown(message.WParam);
180 case Const.WM_APPCOMMAND:
181 ProcessAppCommand(message.LParam);
184 ProcessInputCommand(ref message);
185 message.Result = new IntPtr(0);
192 //-------------------------------------------------------------
194 //-------------------------------------------------------------
196 private void ProcessKeyDown(IntPtr wParam)
198 RemoteControlButton rcb = RemoteControlButton.Unknown;
200 switch (wParam.ToInt32())
202 case (int) Keys.Escape:
203 rcb = RemoteControlButton.Clear;
205 case (int) Keys.Down:
206 rcb = RemoteControlButton.Down;
208 case (int) Keys.Left:
209 rcb = RemoteControlButton.Left;
212 rcb = RemoteControlButton.Digit0;
215 rcb = RemoteControlButton.Digit1;
218 rcb = RemoteControlButton.Digit2;
221 rcb = RemoteControlButton.Digit3;
224 rcb = RemoteControlButton.Digit4;
227 rcb = RemoteControlButton.Digit5;
230 rcb = RemoteControlButton.Digit6;
233 rcb = RemoteControlButton.Digit7;
236 rcb = RemoteControlButton.Digit8;
239 rcb = RemoteControlButton.Digit9;
241 case (int) Keys.Enter:
242 rcb = RemoteControlButton.Enter;
244 case (int) Keys.Right:
245 rcb = RemoteControlButton.Right;
248 rcb = RemoteControlButton.Up;
252 if (this.ButtonPressed != null && rcb != RemoteControlButton.Unknown)
253 this.ButtonPressed(this, new RemoteControlEventArgs(rcb, GetDevice(wParam)));
257 private void ProcessAppCommand(IntPtr lParam)
259 RemoteControlButton rcb = RemoteControlButton.Unknown;
261 int cmd = Macro.GET_APPCOMMAND_LPARAM(lParam);
262 //(int) (((ushort) (param >> 16)) & ~Const.FAPPCOMMAND_MASK);
266 case Const.APPCOMMAND_BROWSER_BACKWARD:
267 rcb = RemoteControlButton.Back;
269 case Const.APPCOMMAND_MEDIA_CHANNEL_DOWN:
270 rcb = RemoteControlButton.ChannelDown;
272 case Const.APPCOMMAND_MEDIA_CHANNEL_UP:
273 rcb = RemoteControlButton.ChannelUp;
275 case Const.APPCOMMAND_MEDIA_FAST_FORWARD:
276 rcb = RemoteControlButton.FastForward;
278 case Const.APPCOMMAND_VOLUME_MUTE:
279 rcb = RemoteControlButton.VolumeMute;
281 case Const.APPCOMMAND_MEDIA_PAUSE:
282 rcb = RemoteControlButton.Pause;
284 case Const.APPCOMMAND_MEDIA_PLAY:
285 rcb = RemoteControlButton.Play;
287 case Const.APPCOMMAND_MEDIA_PLAY_PAUSE:
288 rcb = RemoteControlButton.PlayPause;
290 case Const.APPCOMMAND_MEDIA_RECORD:
291 rcb = RemoteControlButton.Record;
293 case Const.APPCOMMAND_MEDIA_PREVIOUSTRACK:
294 rcb = RemoteControlButton.PreviousTrack;
296 case Const.APPCOMMAND_MEDIA_REWIND:
297 rcb = RemoteControlButton.Rewind;
299 case Const.APPCOMMAND_MEDIA_NEXTTRACK:
300 rcb = RemoteControlButton.NextTrack;
302 case Const.APPCOMMAND_MEDIA_STOP:
303 rcb = RemoteControlButton.Stop;
305 case Const.APPCOMMAND_VOLUME_DOWN:
306 rcb = RemoteControlButton.VolumeDown;
308 case Const.APPCOMMAND_VOLUME_UP:
309 rcb = RemoteControlButton.VolumeUp;
313 if (this.ButtonPressed != null && rcb != RemoteControlButton.Unknown)
314 this.ButtonPressed(this, new RemoteControlEventArgs(rcb, GetDevice(lParam)));
320 /// <param name="aUsage"></param>
321 private void HidConsumerDeviceHandler(ushort aUsage)
329 if (Enum.IsDefined(typeof(ConsumerControl), aUsage) && aUsage != 0) //Our button is a known consumer control
331 if (this.ButtonPressed != null)
333 RemoteControlButton button=RemoteControlButton.Unknown;
334 if (aUsage == (ushort)ConsumerControl.AppCtrlProperties)
336 button = RemoteControlButton.MoreInfo;
338 else if (aUsage == (ushort)ConsumerControl.AppCtrlPrint)
340 button = RemoteControlButton.Print;
342 else if (aUsage == (ushort)ConsumerControl.MediaSelectProgramGuide)
344 button = RemoteControlButton.Guide;
346 this.ButtonPressed(this, new RemoteControlEventArgs(button, InputDevice.OEM));
351 Debug.WriteLine("Unknown Consumer Control!");
358 /// <param name="aUsage"></param>
359 private void HidMceRemoteHandler(ushort aUsage)
368 if (Enum.IsDefined(typeof(MceButton), aUsage) && aUsage != 0) //Our button is a known MCE button
370 if (this.ButtonPressed != null)
372 this.ButtonPressed(this, new RemoteControlEventArgs((MceButton)aUsage, InputDevice.OEM));
377 Debug.WriteLine("Unknown MCE button!");
383 private void ProcessInputCommand(ref Message message)
385 //We received a WM_INPUT message
386 Debug.WriteLine("================WM_INPUT================");
388 //Check if we received this message while in background or foreground
389 if (Macro.GET_RAWINPUT_CODE_WPARAM(message.WParam) == Const.RIM_INPUT)
391 Debug.WriteLine("================FOREGROUND");
393 else if (Macro.GET_RAWINPUT_CODE_WPARAM(message.WParam) == Const.RIM_INPUTSINK)
395 Debug.WriteLine("================BACKGROUND");
398 //Declare some pointers
399 IntPtr rawInputBuffer = IntPtr.Zero;
400 //My understanding is that this is basically our HID descriptor
401 IntPtr preParsedData = IntPtr.Zero;
406 RAWINPUT rawInput = new RAWINPUT();
407 if (!RawInput.GetRawInputData(message.LParam, ref rawInput, ref rawInputBuffer))
413 RID_DEVICE_INFO deviceInfo = new RID_DEVICE_INFO();
414 if (!RawInput.GetDeviceInfo(rawInput.header.hDevice, ref deviceInfo))
420 if (rawInput.header.dwType == Const.RIM_TYPEHID) //Check that our raw input is HID
422 Debug.WriteLine("WM_INPUT source device is HID.");
423 //Get Usage Page and Usage
424 Debug.WriteLine("Usage Page: 0x" + deviceInfo.hid.usUsagePage.ToString("X4") + " Usage ID: 0x" + deviceInfo.hid.usUsage.ToString("X4"));
427 preParsedData = RawInput.GetPreParsedData(rawInput.header.hDevice);
430 HidUsageHandler usagePageHandler=null;
432 //Check if this an MCE remote HID message
433 if (deviceInfo.hid.usUsagePage == (ushort)Hid.UsagePage.MceRemote && deviceInfo.hid.usUsage == (ushort)Hid.UsageId.MceRemoteUsage)
435 usagePageHandler = HidMceRemoteHandler;
437 //Check if this is a consumer control HID message
438 else if (deviceInfo.hid.usUsagePage == (ushort)Hid.UsagePage.Consumer && deviceInfo.hid.usUsage == (ushort)Hid.UsageId.ConsumerControl)
440 usagePageHandler = HidConsumerDeviceHandler;
442 //Unknown HID message
445 Debug.WriteLine("Unknown HID message.");
449 if (!(rawInput.hid.dwSizeHid > 1 //Make sure our HID msg size more than 1. In fact the first ushort is irrelevant to us for now
450 && rawInput.hid.dwCount > 0)) //Check that we have at least one HID msg
456 //Allocate a buffer for one HID input
457 byte[] hidInputReport = new byte[rawInput.hid.dwSizeHid];
459 Debug.WriteLine("Raw input contains " + rawInput.hid.dwCount + " HID input report(s)");
461 //For each HID input report in our raw input
462 for (int i = 0; i < rawInput.hid.dwCount; i++)
464 //Compute the address from which to copy our HID input
465 int hidInputOffset = 0;
468 byte* source = (byte*)rawInputBuffer;
469 source += sizeof(RAWINPUTHEADER) + sizeof(RAWHID) + (rawInput.hid.dwSizeHid * i);
470 hidInputOffset = (int)source;
473 //Copy HID input into our buffer
474 Marshal.Copy(new IntPtr(hidInputOffset), hidInputReport, 0, (int)rawInput.hid.dwSizeHid);
476 //Print HID input report in our debug output
477 string hidDump = "HID input report: ";
478 foreach (byte b in hidInputReport)
480 hidDump += b.ToString("X2");
482 Debug.WriteLine(hidDump);
485 uint usageCount = 1; //Assuming a single usage per input report. Is that correct?
486 Win32.USAGE_AND_PAGE[] usages = new Win32.USAGE_AND_PAGE[usageCount];
487 Win32.HidStatus status = Win32.Function.HidP_GetUsagesEx(Win32.HIDP_REPORT_TYPE.HidP_Input, 0, usages, ref usageCount, preParsedData, hidInputReport, (uint)hidInputReport.Length);
488 if (status != Win32.HidStatus.HIDP_STATUS_SUCCESS)
490 Debug.WriteLine("Could not parse HID data!");
494 Debug.WriteLine("UsagePage: 0x" + usages[0].UsagePage.ToString("X4"));
495 Debug.WriteLine("Usage: 0x" + usages[0].Usage.ToString("X4"));
496 //Call on our Usage Page handler
497 usagePageHandler(usages[0].Usage);
502 else if (rawInput.header.dwType == Const.RIM_TYPEMOUSE)
504 Debug.WriteLine("WM_INPUT source device is Mouse.");
505 // do mouse handling...
507 else if (rawInput.header.dwType == Const.RIM_TYPEKEYBOARD)
509 Debug.WriteLine("WM_INPUT source device is Keyboard.");
510 // do keyboard handling...
516 //Always executed when leaving our try block
517 Marshal.FreeHGlobal(rawInputBuffer);
518 Marshal.FreeHGlobal(preParsedData);
523 private InputDevice GetDevice(IntPtr lParam)
525 InputDevice inputDevice;
527 switch (Macro.GET_DEVICE_LPARAM(lParam))
529 case Const.FAPPCOMMAND_OEM:
530 inputDevice = InputDevice.OEM;
532 case Const.FAPPCOMMAND_MOUSE:
533 inputDevice = InputDevice.Mouse;
536 inputDevice = InputDevice.Key;