Now supporting some HID consumer codes.
Architecture to support multiple Usage Page.
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 private const int WM_KEYDOWN = 0x0100;
130 private const int WM_APPCOMMAND = 0x0319;
131 private const int WM_INPUT = 0x00FF;
133 private const int APPCOMMAND_BROWSER_BACKWARD = 1;
134 private const int APPCOMMAND_VOLUME_MUTE = 8;
135 private const int APPCOMMAND_VOLUME_DOWN = 9;
136 private const int APPCOMMAND_VOLUME_UP = 10;
137 private const int APPCOMMAND_MEDIA_NEXTTRACK = 11;
138 private const int APPCOMMAND_MEDIA_PREVIOUSTRACK = 12;
139 private const int APPCOMMAND_MEDIA_STOP = 13;
140 private const int APPCOMMAND_MEDIA_PLAY_PAUSE = 14;
141 private const int APPCOMMAND_MEDIA_PLAY = 46;
142 private const int APPCOMMAND_MEDIA_PAUSE = 47;
143 private const int APPCOMMAND_MEDIA_RECORD = 48;
144 private const int APPCOMMAND_MEDIA_FAST_FORWARD = 49;
145 private const int APPCOMMAND_MEDIA_REWIND = 50;
146 private const int APPCOMMAND_MEDIA_CHANNEL_UP = 51;
147 private const int APPCOMMAND_MEDIA_CHANNEL_DOWN = 52;
149 private const int FAPPCOMMAND_MASK = 0xF000;
150 private const int FAPPCOMMAND_MOUSE = 0x8000;
151 private const int FAPPCOMMAND_KEY = 0;
152 private const int FAPPCOMMAND_OEM = 0x1000;
156 public delegate void RemoteControlDeviceEventHandler(object sender, RemoteControlEventArgs e);
157 public event RemoteControlDeviceEventHandler ButtonPressed;
159 public delegate void HidUsageHandler(ushort aUsage);
163 //-------------------------------------------------------------
165 //-------------------------------------------------------------
167 public RemoteControlDevice()
169 // Register the input device to receive the commands from the
170 // remote device. See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwmt/html/remote_control.asp
171 // for the vendor defined usage page.
173 RAWINPUTDEVICE[] rid = new RAWINPUTDEVICE[3];
175 rid[0].usUsagePage = 0xFFBC;
176 rid[0].usUsage = 0x88;
179 rid[1].usUsagePage = 0x0C;
180 rid[1].usUsage = 0x01;
183 rid[2].usUsagePage = 0x0C;
184 rid[2].usUsage = 0x80;
187 if (!Function.RegisterRawInputDevices(rid,
189 (uint) Marshal.SizeOf(rid[0]))
192 throw new ApplicationException("Failed to register raw input devices.");
197 //-------------------------------------------------------------
199 //-------------------------------------------------------------
201 public void ProcessMessage(Message message)
208 param = message.WParam.ToInt32();
209 ProcessKeyDown(param);
212 param = message.LParam.ToInt32();
213 ProcessAppCommand(param);
216 ProcessInputCommand(ref message);
217 message.Result = new IntPtr(0);
224 //-------------------------------------------------------------
226 //-------------------------------------------------------------
228 private void ProcessKeyDown(int param)
230 RemoteControlButton rcb = RemoteControlButton.Unknown;
234 case (int) Keys.Escape:
235 rcb = RemoteControlButton.Clear;
237 case (int) Keys.Down:
238 rcb = RemoteControlButton.Down;
240 case (int) Keys.Left:
241 rcb = RemoteControlButton.Left;
244 rcb = RemoteControlButton.Digit0;
247 rcb = RemoteControlButton.Digit1;
250 rcb = RemoteControlButton.Digit2;
253 rcb = RemoteControlButton.Digit3;
256 rcb = RemoteControlButton.Digit4;
259 rcb = RemoteControlButton.Digit5;
262 rcb = RemoteControlButton.Digit6;
265 rcb = RemoteControlButton.Digit7;
268 rcb = RemoteControlButton.Digit8;
271 rcb = RemoteControlButton.Digit9;
273 case (int) Keys.Enter:
274 rcb = RemoteControlButton.Enter;
276 case (int) Keys.Right:
277 rcb = RemoteControlButton.Right;
280 rcb = RemoteControlButton.Up;
284 if (this.ButtonPressed != null && rcb != RemoteControlButton.Unknown)
285 this.ButtonPressed(this, new RemoteControlEventArgs(rcb, GetDevice(param)));
289 private void ProcessAppCommand(int param)
291 RemoteControlButton rcb = RemoteControlButton.Unknown;
293 int cmd = (int) (((ushort) (param >> 16)) & ~FAPPCOMMAND_MASK);
297 case APPCOMMAND_BROWSER_BACKWARD:
298 rcb = RemoteControlButton.Back;
300 case APPCOMMAND_MEDIA_CHANNEL_DOWN:
301 rcb = RemoteControlButton.ChannelDown;
303 case APPCOMMAND_MEDIA_CHANNEL_UP:
304 rcb = RemoteControlButton.ChannelUp;
306 case APPCOMMAND_MEDIA_FAST_FORWARD:
307 rcb = RemoteControlButton.FastForward;
309 case APPCOMMAND_VOLUME_MUTE:
310 rcb = RemoteControlButton.VolumeMute;
312 case APPCOMMAND_MEDIA_PAUSE:
313 rcb = RemoteControlButton.Pause;
315 case APPCOMMAND_MEDIA_PLAY:
316 rcb = RemoteControlButton.Play;
318 case APPCOMMAND_MEDIA_PLAY_PAUSE:
319 rcb = RemoteControlButton.PlayPause;
321 case APPCOMMAND_MEDIA_RECORD:
322 rcb = RemoteControlButton.Record;
324 case APPCOMMAND_MEDIA_PREVIOUSTRACK:
325 rcb = RemoteControlButton.PreviousTrack;
327 case APPCOMMAND_MEDIA_REWIND:
328 rcb = RemoteControlButton.Rewind;
330 case APPCOMMAND_MEDIA_NEXTTRACK:
331 rcb = RemoteControlButton.NextTrack;
333 case APPCOMMAND_MEDIA_STOP:
334 rcb = RemoteControlButton.Stop;
336 case APPCOMMAND_VOLUME_DOWN:
337 rcb = RemoteControlButton.VolumeDown;
339 case APPCOMMAND_VOLUME_UP:
340 rcb = RemoteControlButton.VolumeUp;
344 if (this.ButtonPressed != null && rcb != RemoteControlButton.Unknown)
345 this.ButtonPressed(this, new RemoteControlEventArgs(rcb, GetDevice(param)));
351 /// <param name="aUsage"></param>
352 private void HidConsumerDeviceHandler(ushort aUsage)
360 if (Enum.IsDefined(typeof(ConsumerControl), aUsage) && aUsage != 0) //Our button is a known consumer control
362 if (this.ButtonPressed != null)
364 RemoteControlButton button=RemoteControlButton.Unknown;
365 if (aUsage== (ushort)ConsumerControl.AppCtrlProperties)
367 button = RemoteControlButton.MoreInfo;
369 else if (aUsage==(ushort)ConsumerControl.AppCtrlPrint)
371 button = RemoteControlButton.Print;
373 else if (aUsage==(ushort)ConsumerControl.MediaSelectProgramGuide)
375 button = RemoteControlButton.Guide;
377 this.ButtonPressed(this, new RemoteControlEventArgs(button, InputDevice.OEM));
382 Debug.WriteLine("Unknown Consumer Control!");
389 /// <param name="aUsage"></param>
390 private void HidMceRemoteHandler(ushort aUsage)
399 if (Enum.IsDefined(typeof(MceButton), aUsage) && aUsage != 0) //Our button is a known MCE button
401 if (this.ButtonPressed != null)
403 this.ButtonPressed(this, new RemoteControlEventArgs((MceButton)aUsage, InputDevice.OEM));
408 Debug.WriteLine("Unknown MCE button!");
414 private void ProcessInputCommand(ref Message message)
416 Debug.WriteLine("================WM_INPUT================");
420 IntPtr rawInputBuffer = IntPtr.Zero;
425 RAWINPUT rawInput = new RAWINPUT();
426 if (!RawInput.GetRawInputData(message.LParam, ref rawInput, ref rawInputBuffer))
432 RID_DEVICE_INFO deviceInfo = new RID_DEVICE_INFO();
433 if (!RawInput.GetDeviceInfo(rawInput.header.hDevice, ref deviceInfo))
439 if (rawInput.header.dwType == Const.RIM_TYPEHID) //Check that our raw input is HID
441 Debug.WriteLine("WM_INPUT source device is HID.");
442 //Get Usage Page and Usage
443 Debug.WriteLine("Usage Page: 0x" + deviceInfo.hid.usUsagePage.ToString("X4") + " Usage: 0x" + deviceInfo.hid.usUsage.ToString("X4"));
446 HidUsageHandler handler=null;
448 //Make sure both usage page and usage are matching MCE remote
449 //TODO: handle more that just MCE usage page.
450 if (deviceInfo.hid.usUsagePage == (ushort)Hid.UsagePage.MceRemote || deviceInfo.hid.usUsage == (ushort)Hid.UsageId.MceRemoteUsage)
452 handler = HidMceRemoteHandler;
454 else if (deviceInfo.hid.usUsagePage == (ushort)Hid.UsagePage.Consumer || deviceInfo.hid.usUsage == (ushort)Hid.UsageId.ConsumerControl)
456 handler = HidConsumerDeviceHandler;
460 Debug.WriteLine("Not MCE remote page and usage.");
464 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
465 && rawInput.hid.dwCount > 0)) //Check that we have at least one HID msg
471 //Allocate a buffer for one HID input
472 byte[] hidInput = new byte[rawInput.hid.dwSizeHid];
474 //For each HID input in our raw input
475 for (int i = 0; i < rawInput.hid.dwCount; i++)
477 //Compute the address from which to copy our HID input
478 int hidInputOffset = 0;
481 byte* source = (byte*)rawInputBuffer;
482 source += sizeof(RAWINPUTHEADER) + sizeof(RAWHID) + (rawInput.hid.dwSizeHid * i);
483 hidInputOffset = (int)source;
486 //Copy HID input into our buffer
487 Marshal.Copy(new IntPtr(hidInputOffset), hidInput, 0, rawInput.hid.dwSizeHid);
489 //Print HID raw input in our debug output
490 string hidDump = "HID " + rawInput.hid.dwCount + "/" + rawInput.hid.dwSizeHid + ":";
491 foreach (byte b in hidInput)
493 hidDump += b.ToString("X2");
495 Debug.WriteLine(hidDump);
498 //hidInput[0] //Not sure what's the meaning of the code at offset 0
499 if (hidInput.Length == 2)
502 usage = hidInput[1]; //Get button code
504 else if (hidInput.Length > 2) //Defensive
506 //Assuming double bytes code
507 usage = (ushort)((hidInput[2] << 8) + hidInput[1]);
515 else if (rawInput.header.dwType == Const.RIM_TYPEMOUSE)
517 Debug.WriteLine("WM_INPUT source device is Mouse.");
518 // do mouse handling...
520 else if (rawInput.header.dwType == Const.RIM_TYPEKEYBOARD)
522 Debug.WriteLine("WM_INPUT source device is Keyboard.");
523 // do keyboard handling...
529 //Always executed when leaving our try block
530 Marshal.FreeHGlobal(rawInputBuffer);
535 private InputDevice GetDevice(int param)
537 InputDevice inputDevice;
539 switch ((int) (((ushort) (param >> 16)) & FAPPCOMMAND_MASK))
541 case FAPPCOMMAND_OEM:
542 inputDevice = InputDevice.OEM;
544 case FAPPCOMMAND_MOUSE:
545 inputDevice = InputDevice.Mouse;
548 inputDevice = InputDevice.Key;