Proper support for all MCE buttons.
2 using System.Windows.Forms;
3 using System.Runtime.InteropServices;
4 using System.Diagnostics;
7 namespace BruceThomas.Devices.RemoteControl
9 public enum InputDevice
17 public enum RemoteControlButton
77 Null = 0x00, //Not defined by the specs
79 ClosedCaptioning = 0x2B,
83 TeletextYellow = 0x5D,
119 NetworkSelection = 0x2C,
122 VideoSelection = 0x61
126 #region RemoteControlEventArgs
128 public class RemoteControlEventArgs : EventArgs
130 RemoteControlButton _rcb;
132 MceButton iMceButton;
134 public RemoteControlEventArgs(RemoteControlButton rcb, InputDevice device)
136 iMceButton = MceButton.Null;
141 public RemoteControlEventArgs(MceButton mce, InputDevice device)
144 _rcb = RemoteControlButton.Unknown;
148 public RemoteControlEventArgs()
150 iMceButton = MceButton.Null;
151 _rcb = RemoteControlButton.Unknown;
152 _device = InputDevice.Key;
155 public RemoteControlButton Button
158 set { _rcb = value; }
161 public MceButton MceButton
163 get { return iMceButton; }
164 set { iMceButton = value; }
167 public InputDevice Device
169 get { return _device; }
170 set { _device = value; }
174 #endregion RemoteControlEventArgs
177 public sealed class RemoteControlDevice
180 [StructLayout(LayoutKind.Sequential, Pack = 1)]
181 internal struct RAWINPUTDEVICE
183 [MarshalAs(UnmanagedType.U2)]
184 public ushort usUsagePage;
185 [MarshalAs(UnmanagedType.U2)]
186 public ushort usUsage;
187 [MarshalAs(UnmanagedType.U4)]
189 public IntPtr hwndTarget;
193 [StructLayout(LayoutKind.Sequential, Pack = 1)]
194 internal struct RAWINPUTHEADER
196 [MarshalAs(UnmanagedType.U4)]
198 [MarshalAs(UnmanagedType.U4)]
200 public IntPtr hDevice;
201 [MarshalAs(UnmanagedType.U4)]
206 [StructLayout(LayoutKind.Sequential, Pack = 1)]
207 internal struct RAWHID
209 [MarshalAs(UnmanagedType.U4)]
210 public int dwSizeHid;
211 [MarshalAs(UnmanagedType.U4)]
218 [StructLayout(LayoutKind.Sequential, Pack = 1)]
219 internal struct BUTTONSSTR
221 [MarshalAs(UnmanagedType.U2)]
222 public ushort usButtonFlags;
223 [MarshalAs(UnmanagedType.U2)]
224 public ushort usButtonData;
228 [StructLayout(LayoutKind.Explicit, Pack = 1)]
229 internal struct RAWMOUSE
231 [MarshalAs(UnmanagedType.U2)]
232 [FieldOffset (0)] public ushort usFlags;
233 [MarshalAs(UnmanagedType.U4)]
234 [FieldOffset (4)] public uint ulButtons;
235 [FieldOffset (4)] public BUTTONSSTR buttonsStr;
236 [MarshalAs(UnmanagedType.U4)]
237 [FieldOffset (8)] public uint ulRawButtons;
238 [MarshalAs(UnmanagedType.U4)]
239 [FieldOffset (12)] public int lLastX;
240 [MarshalAs(UnmanagedType.U4)]
241 [FieldOffset (16)] public int lLastY;
242 [MarshalAs(UnmanagedType.U4)]
243 [FieldOffset (20)] public uint ulExtraInformation;
246 [StructLayout(LayoutKind.Sequential, Pack = 1)]
247 internal struct RAWKEYBOARD
249 [MarshalAs(UnmanagedType.U2)]
250 public ushort MakeCode;
251 [MarshalAs(UnmanagedType.U2)]
253 [MarshalAs(UnmanagedType.U2)]
254 public ushort Reserved;
255 [MarshalAs(UnmanagedType.U2)]
257 [MarshalAs(UnmanagedType.U4)]
259 [MarshalAs(UnmanagedType.U4)]
260 public uint ExtraInformation;
264 [StructLayout(LayoutKind.Explicit, Pack=1)]
265 internal struct RAWINPUT
267 [FieldOffset (0)] public RAWINPUTHEADER header;
268 [FieldOffset (16)] public RAWMOUSE mouse;
269 [FieldOffset (16)] public RAWKEYBOARD keyboard;
270 [FieldOffset (16)] public RAWHID hid;
274 [DllImport("User32.dll")]
275 extern static bool RegisterRawInputDevices(RAWINPUTDEVICE[] pRawInputDevice, uint uiNumDevices, uint cbSize);
277 [DllImport("User32.dll")]
278 extern static uint GetRawInputData(IntPtr hRawInput, uint uiCommand, IntPtr pData, ref uint pcbSize, uint cbSizeHeader);
281 private const int WM_KEYDOWN = 0x0100;
282 private const int WM_APPCOMMAND = 0x0319;
283 private const int WM_INPUT = 0x00FF;
285 private const int APPCOMMAND_BROWSER_BACKWARD = 1;
286 private const int APPCOMMAND_VOLUME_MUTE = 8;
287 private const int APPCOMMAND_VOLUME_DOWN = 9;
288 private const int APPCOMMAND_VOLUME_UP = 10;
289 private const int APPCOMMAND_MEDIA_NEXTTRACK = 11;
290 private const int APPCOMMAND_MEDIA_PREVIOUSTRACK = 12;
291 private const int APPCOMMAND_MEDIA_STOP = 13;
292 private const int APPCOMMAND_MEDIA_PLAY_PAUSE = 14;
293 private const int APPCOMMAND_MEDIA_PLAY = 46;
294 private const int APPCOMMAND_MEDIA_PAUSE = 47;
295 private const int APPCOMMAND_MEDIA_RECORD = 48;
296 private const int APPCOMMAND_MEDIA_FAST_FORWARD = 49;
297 private const int APPCOMMAND_MEDIA_REWIND = 50;
298 private const int APPCOMMAND_MEDIA_CHANNEL_UP = 51;
299 private const int APPCOMMAND_MEDIA_CHANNEL_DOWN = 52;
301 private const int RIM_TYPEMOUSE = 0;
302 private const int RIM_TYPEKEYBOARD = 1;
303 private const int RIM_TYPEHID = 2;
305 private const int RID_INPUT = 0x10000003;
306 private const int RID_HEADER = 0x10000005;
308 private const int FAPPCOMMAND_MASK = 0xF000;
309 private const int FAPPCOMMAND_MOUSE = 0x8000;
310 private const int FAPPCOMMAND_KEY = 0;
311 private const int FAPPCOMMAND_OEM = 0x1000;
313 public delegate void RemoteControlDeviceEventHandler(object sender, RemoteControlEventArgs e);
314 public event RemoteControlDeviceEventHandler ButtonPressed;
317 //-------------------------------------------------------------
319 //-------------------------------------------------------------
321 public RemoteControlDevice()
323 // Register the input device to receive the commands from the
324 // remote device. See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwmt/html/remote_control.asp
325 // for the vendor defined usage page.
327 RAWINPUTDEVICE[] rid = new RAWINPUTDEVICE[3];
329 rid[0].usUsagePage = 0xFFBC;
330 rid[0].usUsage = 0x88;
333 rid[1].usUsagePage = 0x0C;
334 rid[1].usUsage = 0x01;
337 rid[2].usUsagePage = 0x0C;
338 rid[2].usUsage = 0x80;
341 if (!RegisterRawInputDevices(rid,
343 (uint) Marshal.SizeOf(rid[0]))
346 throw new ApplicationException("Failed to register raw input devices.");
351 //-------------------------------------------------------------
353 //-------------------------------------------------------------
355 public void ProcessMessage(Message message)
362 param = message.WParam.ToInt32();
363 ProcessKeyDown(param);
366 param = message.LParam.ToInt32();
367 ProcessAppCommand(param);
370 ProcessInputCommand(ref message);
371 message.Result = new IntPtr(0);
378 //-------------------------------------------------------------
380 //-------------------------------------------------------------
382 private void ProcessKeyDown(int param)
384 RemoteControlButton rcb = RemoteControlButton.Unknown;
388 case (int) Keys.Escape:
389 rcb = RemoteControlButton.Clear;
391 case (int) Keys.Down:
392 rcb = RemoteControlButton.Down;
394 case (int) Keys.Left:
395 rcb = RemoteControlButton.Left;
398 rcb = RemoteControlButton.Digit0;
401 rcb = RemoteControlButton.Digit1;
404 rcb = RemoteControlButton.Digit2;
407 rcb = RemoteControlButton.Digit3;
410 rcb = RemoteControlButton.Digit4;
413 rcb = RemoteControlButton.Digit5;
416 rcb = RemoteControlButton.Digit6;
419 rcb = RemoteControlButton.Digit7;
422 rcb = RemoteControlButton.Digit8;
425 rcb = RemoteControlButton.Digit9;
427 case (int) Keys.Enter:
428 rcb = RemoteControlButton.Enter;
430 case (int) Keys.Right:
431 rcb = RemoteControlButton.Right;
434 rcb = RemoteControlButton.Up;
438 if (this.ButtonPressed != null && rcb != RemoteControlButton.Unknown)
439 this.ButtonPressed(this, new RemoteControlEventArgs(rcb, GetDevice(param)));
443 private void ProcessAppCommand(int param)
445 RemoteControlButton rcb = RemoteControlButton.Unknown;
447 int cmd = (int) (((ushort) (param >> 16)) & ~FAPPCOMMAND_MASK);
451 case APPCOMMAND_BROWSER_BACKWARD:
452 rcb = RemoteControlButton.Back;
454 case APPCOMMAND_MEDIA_CHANNEL_DOWN:
455 rcb = RemoteControlButton.ChannelDown;
457 case APPCOMMAND_MEDIA_CHANNEL_UP:
458 rcb = RemoteControlButton.ChannelUp;
460 case APPCOMMAND_MEDIA_FAST_FORWARD:
461 rcb = RemoteControlButton.FastForward;
463 case APPCOMMAND_VOLUME_MUTE:
464 rcb = RemoteControlButton.VolumeMute;
466 case APPCOMMAND_MEDIA_PAUSE:
467 rcb = RemoteControlButton.Pause;
469 case APPCOMMAND_MEDIA_PLAY:
470 rcb = RemoteControlButton.Play;
472 case APPCOMMAND_MEDIA_PLAY_PAUSE:
473 rcb = RemoteControlButton.PlayPause;
475 case APPCOMMAND_MEDIA_RECORD:
476 rcb = RemoteControlButton.Record;
478 case APPCOMMAND_MEDIA_PREVIOUSTRACK:
479 rcb = RemoteControlButton.PreviousTrack;
481 case APPCOMMAND_MEDIA_REWIND:
482 rcb = RemoteControlButton.Rewind;
484 case APPCOMMAND_MEDIA_NEXTTRACK:
485 rcb = RemoteControlButton.NextTrack;
487 case APPCOMMAND_MEDIA_STOP:
488 rcb = RemoteControlButton.Stop;
490 case APPCOMMAND_VOLUME_DOWN:
491 rcb = RemoteControlButton.VolumeDown;
493 case APPCOMMAND_VOLUME_UP:
494 rcb = RemoteControlButton.VolumeUp;
498 if (this.ButtonPressed != null && rcb != RemoteControlButton.Unknown)
499 this.ButtonPressed(this, new RemoteControlEventArgs(rcb, GetDevice(param)));
503 private void ProcessInputCommand(ref Message message)
505 RemoteControlButton rcb = RemoteControlButton.Unknown;
508 uint sizeOfHeader=(uint)Marshal.SizeOf(typeof(RAWINPUTHEADER));
510 //Get the size of our raw input data.
511 GetRawInputData(message.LParam, RID_INPUT, IntPtr.Zero, ref dwSize, sizeOfHeader);
513 //Allocate a large enough buffer
514 IntPtr buffer = Marshal.AllocHGlobal((int) dwSize);
517 if(buffer == IntPtr.Zero)
520 //Now read our RAWINPUT data
521 if (GetRawInputData(message.LParam, RID_INPUT, buffer, ref dwSize, (uint) Marshal.SizeOf(typeof(RAWINPUTHEADER))) != dwSize)
527 RAWINPUT raw = (RAWINPUT)Marshal.PtrToStructure(buffer, typeof(RAWINPUT));
529 //Check that our raw input is HID
530 if (raw.header.dwType == RIM_TYPEHID && raw.hid.dwSizeHid>0)
532 //Allocate a buffer for one HID message
533 byte[] bRawData = new byte[raw.hid.dwSizeHid];
535 //Compute the address from which to copy our HID message
539 byte* source = (byte*)buffer;
540 source += sizeof(RAWINPUTHEADER) + sizeof(RAWHID);
542 pRawData = (int)source;
544 //int pRawData = buffer.ToUint32() + Marshal.SizeOf(typeof(RAWINPUT)) + 1;
546 //Marshal.Copy(new IntPtr(pRawData), bRawData, 0, raw.hid.dwSizeHid - 1);
547 Marshal.Copy(new IntPtr(pRawData), bRawData, 0, raw.hid.dwSizeHid);
548 //int rawData = bRawData[0] | bRawData[1] << 8;
549 int rawData = bRawData[1]; //Get button code
550 Debug.WriteLine("HID " + raw.hid.dwCount + "/" + raw.hid.dwSizeHid + ":" + bRawData[0].ToString("X2") + bRawData[1].ToString("X2"));
552 if (Enum.IsDefined(typeof(MceButton), rawData) && rawData!=0) //Our button is a known MCE button
554 if (this.ButtonPressed != null) //What's that?
556 this.ButtonPressed(this, new RemoteControlEventArgs((MceButton)rawData, GetDevice(message.LParam.ToInt32())));
560 else if(raw.header.dwType == RIM_TYPEMOUSE)
562 // do mouse handling...
564 else if(raw.header.dwType == RIM_TYPEKEYBOARD)
566 // do keyboard handling...
571 Marshal.FreeHGlobal(buffer);
576 private InputDevice GetDevice(int param)
578 InputDevice inputDevice;
580 switch ((int) (((ushort) (param >> 16)) & FAPPCOMMAND_MASK))
582 case FAPPCOMMAND_OEM:
583 inputDevice = InputDevice.OEM;
585 case FAPPCOMMAND_MOUSE:
586 inputDevice = InputDevice.Mouse;
589 inputDevice = InputDevice.Key;