RemoteControlDevice.cs
author sl
Wed, 05 Nov 2014 20:58:15 +0100
changeset 7 5ecebf1bd397
parent 6 fa97ff5ceb7b
child 8 0d0c62b1df48
permissions -rw-r--r--
Adding debug output formating.
     1 using System;
     2 using System.Windows.Forms;
     3 using System.Runtime.InteropServices;
     4 using System.Diagnostics;
     5 
     6 
     7 namespace Devices.RemoteControl
     8 {
     9 
    10     public static class Hid
    11     {
    12         /// <summary>
    13         /// From USB HID usage tables.
    14         /// http://www.usb.org/developers/hidpage#HID_Usage
    15         /// http://www.usb.org/developers/devclass_docs/Hut1_12v2.pdf
    16         /// </summary>
    17         public enum UsagePage : ushort
    18         {
    19             Undefined = 0,
    20             GenericDesktopControl,
    21             SimulationControl,
    22             VirtualRealityControl,
    23             SportControl,
    24             GameControl,
    25             GenericDeviceControl,
    26             Keyboard,
    27             LightEmittingDiode,
    28             Button,
    29             Ordinal,
    30             Telephony,
    31             Consumer,
    32             Digitiser,
    33             PhysicalInterfaceDevice = 0x0f,
    34             Unicode = 0x10,
    35             AlphaNumericDisplay = 0x14,
    36             MedicalInstruments = 0x40,
    37             MonitorPage0 = 0x80,
    38             MonitorPage1,
    39             MonitorPage2,
    40             MonitorPage3,
    41             PowerPage0,
    42             PowerPage1,
    43             PowerPage2,
    44             PowerPage3,
    45             BarCodeScanner = 0x8c,
    46             Scale,
    47             MagneticStripeReader,
    48             ReservedPointOfSale,
    49             CameraControl,
    50             Arcade,
    51             // http://msdn.microsoft.com/en-us/library/windows/desktop/bb417079.aspx
    52             MceRemote = 0xffbc,
    53             TerraTecRemote = 0xffcc
    54         }
    55 
    56         public const ushort MceRemoteUsage = 0x88;
    57     }
    58 
    59 
    60 	public enum InputDevice
    61 	{
    62 		Key,
    63 		Mouse,
    64 		OEM
    65 	}
    66 
    67 
    68 	public enum RemoteControlButton
    69 	{
    70 		Clear,
    71 		Down,
    72 		Left,
    73 		Digit0,
    74 		Digit1,
    75 		Digit2,
    76 		Digit3,
    77 		Digit4,
    78 		Digit5,
    79 		Digit6,
    80 		Digit7,
    81 		Digit8,
    82 		Digit9,
    83 		Enter,
    84 		Right,
    85 		Up,
    86 
    87 		Back,
    88 		ChannelDown,
    89 		ChannelUp,
    90 		FastForward,
    91 		VolumeMute,
    92 		Pause,
    93 		Play,
    94         PlayPause,
    95 		Record,
    96 		PreviousTrack,
    97 		Rewind,
    98 		NextTrack,
    99 		Stop,
   100 		VolumeDown,
   101 		VolumeUp,
   102 
   103 		RecordedTV,
   104 		Guide,
   105 		LiveTV,
   106 		Details,
   107 		DVDMenu,
   108 		DVDAngle,
   109 		DVDAudio,
   110 		DVDSubtitle,
   111 		MyMusic,
   112 		MyPictures,
   113 		MyVideos,
   114 		MyTV,
   115 		OEM1,
   116 		OEM2,
   117 		StandBy,
   118 		TVJump,
   119 
   120 		Unknown
   121 	}
   122 
   123     /// <summary>
   124     ///
   125     /// </summary>
   126     public enum MceButton
   127     {
   128         /// <summary>
   129         /// Not defined by the Microsoft specs.
   130         /// </summary>
   131         Null                    =   0x00,
   132         GreenStart              =   0x0D,
   133         ClosedCaptioning        =   0x2B,
   134         Teletext                =   0x5A,
   135         TeletextRed             =   0x5B,
   136         TeletextGreen           =   0x5C,
   137         TeletextYellow          =   0x5D,
   138         TeletextBlue            =   0x5E,
   139         LiveTv                  =   0x25,
   140         Music                   =   0x47,
   141         RecordedTv              =   0x48,
   142         Pictures                =   0x49,
   143         Videos                  =   0x4A,
   144         FmRadio                 =   0x50,
   145         Extras                  =   0x3C,
   146         ExtrasApp               =   0x3D,
   147         DvdMenu                 =   0x24,
   148         DvdAngle                =   0x4B,
   149         DvdAudio                =   0x4C,
   150         DvdSubtitle             =   0x4D,
   151         /// <summary>
   152         /// First press action: Ejects a DVD drive.
   153         /// <para />
   154         /// Second press action: Repeats first press action.
   155         /// <para />
   156         /// Notably issued by XBOX360 remote as defined in irplus - Remote Control - Android application.
   157         /// </summary>
   158         Eject                   =   0x28,
   159         DvdTopMenu              =   0x43,
   160         /// <summary>
   161         /// First press action: Generates EXTn HID message in the Media Center Vendor Specific
   162         /// Collection (page 0xFFBC, usage 0x88).
   163         /// <para />
   164         /// Second press action: Repeats message.
   165         /// <para />
   166         /// Auto-repeat: No
   167         /// <para />
   168         /// Notably sent by the 'Visualization' button of HP Windows Media Center Remote (TSGH-IR08).
   169         /// <para />
   170         /// According to HP specs it displays visual imagery that is synchronized to the sound of your music tracks.
   171         /// </summary>
   172         Ext0                    =   0x32,
   173         /// <summary>
   174         /// First press action: Generates EXTn HID message in the Media Center Vendor Specific
   175         /// Collection (page 0xFFBC, usage 0x88).
   176         /// <para />
   177         /// Second press action: Repeats message.
   178         /// <para />
   179         /// Auto-repeat: No
   180         /// <para />
   181         /// Notably sent by the 'Slide Show' button of HP Windows Media Center Remote (TSGH-IR08).
   182         /// <para />
   183         /// According to HP specs it plays a slide show of all the pictures on your hard disk drive.
   184         /// </summary>
   185         Ext1                    =   0x33,
   186         /// <summary>
   187         /// First press action: Generates EXTn HID message in the Media Center Vendor Specific
   188         /// Collection (page 0xFFBC, usage 0x88).
   189         /// <para />
   190         /// Second press action: Repeats message.
   191         /// <para />
   192         /// Auto-repeat: No
   193         /// <para />
   194         /// Notably sent by the 'Eject' button of HP Windows Media Center Remote (TSGH-IR08).
   195         /// Also interpreted as 'Eject' action by SoundGraph iMON Manager in MCE mode (OrigenAE VF310).
   196         /// </summary>
   197         Ext2                    =   0x34,
   198         /// <summary>
   199         /// First press action: Generates EXTn HID message in the Media Center Vendor Specific
   200         /// Collection (page 0xFFBC, usage 0x88).
   201         /// <para />
   202         /// Second press action: Repeats message.
   203         /// <para />
   204         /// Auto-repeat: No
   205         /// <para />
   206         /// Notably sent by the 'Input selection' button of HP Windows Media Center Remote (TSGH-IR08).
   207         /// </summary>
   208         Ext3                    =   0x35,
   209         Ext4                    =   0x36,
   210         Ext5                    =   0x37,
   211         Ext6                    =   0x38,
   212         Ext7                    =   0x39,
   213         Ext8                    =   0x3A,
   214         Ext9                    =   0x80,
   215         Ext10                   =   0x81,
   216         Ext11                   =   0x6F,
   217         Zoom                    =   0x27,
   218         ChannelInput            =   0x42,
   219         SubAudio                =   0x2D,
   220         Channel10               =   0x3E,
   221         Channel11               =   0x3F,
   222         Channel12               =   0x40,
   223         /// <summary>
   224         /// First press action: Generates OEM2 HID message in the Media Center Vendor Specific
   225         /// Collection. This button is intended to control the front panel display of home entertainment
   226         /// computers. When this button is pressed, the display could be turned on or off, or the display
   227         /// mode could change.
   228         /// <para />
   229         /// Second press action: Repeats message.
   230         /// <para />
   231         /// Auto-repeat: No
   232         /// <para />
   233         /// Notably issued by XBOX360 remote as defined in irplus - Remote Control - Android application.
   234         /// </summary>
   235         Display                 =   0x4F,
   236         /// <summary>
   237         /// First press action: To be determined.
   238         /// <para />
   239         /// Second press action: Repeats message.
   240         /// <para />
   241         /// Auto-repeat: No
   242         /// </summary>
   243         Kiosk                   =   0x6A,
   244         NetworkSelection        =   0x2C,
   245         BlueRayTool             =   0x78,
   246         ChannelInfo             =   0x41,
   247         VideoSelection          =   0x61
   248     }
   249 
   250     public enum HpMceButton
   251     {
   252         /// <summary>
   253         /// Displays visual imagery that is synchronized to the sound of your music tracks.
   254         /// <para />
   255         /// Second press action: Repeats message.
   256         /// <para />
   257         /// Auto-repeat: No
   258         /// <para />
   259         /// Notably sent by the 'Visualization' button of HP Windows Media Center Remote (TSGH-IR08).
   260         /// <para />
   261         /// According to HP specs it displays visual imagery that is synchronized to the sound of your music tracks.
   262         /// </summary>
   263         Visualization = MceButton.Ext0,
   264         /// <summary>
   265         /// Plays a slide show of all the pictures on your hard disk drive.
   266         /// <para />
   267         /// Second press action: Repeats message.
   268         /// <para />
   269         /// Auto-repeat: No
   270         /// <para />
   271         /// Notably sent by the 'Slide Show' button of HP Windows Media Center Remote (TSGH-IR08).
   272         /// <para />
   273         /// According to HP specs it plays a slide show of all the pictures on your hard disk drive.
   274         /// </summary>
   275         SlideShow = MceButton.Ext1,
   276         /// <summary>
   277         /// Eject optical drive.
   278         /// <para />
   279         /// Second press action: Repeats message.
   280         /// <para />
   281         /// Auto-repeat: No
   282         /// <para />
   283         /// Notably sent by the 'Eject' button of HP Windows Media Center Remote (TSGH-IR08).
   284         /// Also interpreted as 'Eject' action by SoundGraph iMON Manager in MCE mode (OrigenAE VF310).
   285         /// </summary>
   286         Eject = MceButton.Ext2,
   287         /// <summary>
   288         /// Not sure what this should do.
   289         /// <para />
   290         /// Second press action: Repeats message.
   291         /// <para />
   292         /// Auto-repeat: No
   293         /// <para />
   294         /// Notably sent by the 'Input selection' button of HP Windows Media Center Remote (TSGH-IR08).
   295         /// </summary>
   296         InputSelection = MceButton.Ext3,
   297     }
   298 
   299 
   300 
   301 	#region RemoteControlEventArgs
   302 
   303 	public class RemoteControlEventArgs : EventArgs
   304 	{
   305         RemoteControlButton _rcb;
   306 		InputDevice _device;
   307         MceButton iMceButton;
   308 
   309         public RemoteControlEventArgs(RemoteControlButton rcb, InputDevice device)
   310 		{
   311             iMceButton = MceButton.Null;
   312 			_rcb = rcb;
   313 			_device = device;
   314 		}
   315 
   316         public RemoteControlEventArgs(MceButton mce, InputDevice device)
   317         {
   318             iMceButton = mce;
   319             _rcb = RemoteControlButton.Unknown;
   320             _device = device;
   321         }
   322 
   323 		public RemoteControlEventArgs()
   324 		{
   325             iMceButton = MceButton.Null;
   326 			_rcb = RemoteControlButton.Unknown;
   327 			_device = InputDevice.Key;
   328 		}
   329 
   330 		public RemoteControlButton Button
   331 		{
   332 			get { return _rcb;  }
   333 			set { _rcb = value; }
   334 		}
   335 
   336         public MceButton MceButton
   337         {
   338             get { return iMceButton; }
   339             set { iMceButton = value; }
   340         }
   341 
   342 		public InputDevice Device
   343 		{
   344 			get { return _device;  }
   345 			set { _device = value; }
   346 		}
   347 	}
   348 
   349 	#endregion RemoteControlEventArgs
   350 
   351 
   352 	public sealed class RemoteControlDevice
   353 	{
   354 
   355         [StructLayout(LayoutKind.Sequential, Pack = 1)]
   356 		internal struct RAWINPUTDEVICE
   357 		{
   358 			[MarshalAs(UnmanagedType.U2)]
   359 			public ushort usUsagePage;
   360 			[MarshalAs(UnmanagedType.U2)]
   361 			public ushort usUsage;
   362 			[MarshalAs(UnmanagedType.U4)]
   363 			public int	 dwFlags;
   364 			public IntPtr hwndTarget;
   365 		}
   366 
   367 
   368         [StructLayout(LayoutKind.Sequential, Pack = 1)]
   369 		internal struct RAWINPUTHEADER
   370 		{
   371 			[MarshalAs(UnmanagedType.U4)]
   372 			public int dwType;
   373 			[MarshalAs(UnmanagedType.U4)]
   374 			public int dwSize;
   375             public IntPtr hDevice;
   376 			[MarshalAs(UnmanagedType.U4)]
   377 			public int wParam;
   378 		}
   379 
   380 
   381         [StructLayout(LayoutKind.Sequential, Pack = 1)]
   382 		internal struct RAWHID
   383 		{
   384 			[MarshalAs(UnmanagedType.U4)]
   385 			public int dwSizeHid;
   386 			[MarshalAs(UnmanagedType.U4)]
   387 			public int dwCount;
   388             //
   389             //BYTE  bRawData[1];
   390 		}
   391 
   392 
   393         [StructLayout(LayoutKind.Sequential, Pack = 1)]
   394 		internal struct BUTTONSSTR
   395 		{
   396 			[MarshalAs(UnmanagedType.U2)]
   397 			public ushort usButtonFlags;
   398 			[MarshalAs(UnmanagedType.U2)]
   399 			public ushort usButtonData;
   400 		}
   401 
   402 
   403         [StructLayout(LayoutKind.Explicit, Pack = 1)]
   404 		internal struct RAWMOUSE
   405 		{
   406 			[MarshalAs(UnmanagedType.U2)]
   407 			[FieldOffset (0)] public ushort usFlags;
   408 			[MarshalAs(UnmanagedType.U4)]
   409 			[FieldOffset (4)] public uint ulButtons;
   410 			[FieldOffset (4)] public BUTTONSSTR buttonsStr;
   411 			[MarshalAs(UnmanagedType.U4)]
   412 			[FieldOffset (8)] public uint ulRawButtons;
   413             [MarshalAs(UnmanagedType.U4)]
   414             [FieldOffset (12)] public int lLastX;
   415             [MarshalAs(UnmanagedType.U4)]
   416             [FieldOffset (16)] public int lLastY;
   417 			[MarshalAs(UnmanagedType.U4)]
   418 			[FieldOffset (20)] public uint ulExtraInformation;
   419 		}
   420 
   421         [StructLayout(LayoutKind.Sequential, Pack = 1)]
   422 		internal struct RAWKEYBOARD
   423 		{
   424 			[MarshalAs(UnmanagedType.U2)]
   425 			public ushort MakeCode;
   426 			[MarshalAs(UnmanagedType.U2)]
   427 			public ushort Flags;
   428 			[MarshalAs(UnmanagedType.U2)]
   429 			public ushort Reserved;
   430 			[MarshalAs(UnmanagedType.U2)]
   431 			public ushort VKey;
   432 			[MarshalAs(UnmanagedType.U4)]
   433 			public uint Message;
   434 			[MarshalAs(UnmanagedType.U4)]
   435 			public uint ExtraInformation;
   436 		}
   437 
   438 
   439 		[StructLayout(LayoutKind.Explicit, Pack=1)]
   440 		internal struct RAWINPUT
   441 		{
   442 			[FieldOffset  (0)] public RAWINPUTHEADER header;
   443 			[FieldOffset (16)] public RAWMOUSE mouse;
   444 			[FieldOffset (16)] public RAWKEYBOARD keyboard;
   445 			[FieldOffset (16)] public RAWHID hid;
   446 		}
   447 
   448 
   449         [StructLayout(LayoutKind.Sequential, Pack=1)]
   450         internal struct RID_DEVICE_INFO_MOUSE
   451 		{
   452             public uint dwId;
   453             public uint dwNumberOfButtons;
   454             public uint dwSampleRate;
   455             public bool fHasHorizontalWheel;
   456 		}
   457 
   458 
   459         [StructLayout(LayoutKind.Sequential, Pack=1)]
   460         internal struct RID_DEVICE_INFO_KEYBOARD
   461 		{
   462             public uint dwType;
   463             public uint dwSubType;
   464             public uint dwKeyboardMode;
   465             public uint dwNumberOfFunctionKeys;
   466             public uint dwNumberOfIndicators;
   467             public uint dwNumberOfKeysTotal;
   468         }
   469 
   470         [StructLayout(LayoutKind.Sequential, Pack=1)]
   471         internal struct RID_DEVICE_INFO_HID
   472 		{
   473             public uint dwVendorId;
   474             public uint dwProductId;
   475             public uint dwVersionNumber;
   476             public ushort usUsagePage;
   477             public ushort usUsage;
   478         }
   479 
   480         [StructLayout(LayoutKind.Explicit, Pack=1)]
   481         internal struct RID_DEVICE_INFO
   482 		{
   483             [FieldOffset(0)]
   484             public uint cbSize;
   485             [FieldOffset(4)]
   486             public uint dwType;
   487             [FieldOffset(8)]
   488             public RID_DEVICE_INFO_MOUSE mouse;
   489             [FieldOffset(8)]
   490             public RID_DEVICE_INFO_KEYBOARD keyboard;
   491             [FieldOffset(8)]
   492             public RID_DEVICE_INFO_HID hid;
   493 		}
   494 
   495 
   496 
   497 		[DllImport("User32.dll")]
   498 		extern static bool RegisterRawInputDevices(RAWINPUTDEVICE[] pRawInputDevice, uint uiNumDevices, uint cbSize);
   499 
   500 		[DllImport("User32.dll")]
   501 		extern static uint GetRawInputData(IntPtr hRawInput, uint uiCommand, IntPtr pData, ref uint pcbSize, uint cbSizeHeader);
   502 
   503    		[DllImport("User32.dll", SetLastError=true)]
   504 		extern static int GetRawInputDeviceInfo(IntPtr hDevice, uint uiCommand, IntPtr pData, ref uint pcbSize);
   505 
   506 
   507 		private const int WM_KEYDOWN	= 0x0100;
   508 		private const int WM_APPCOMMAND	= 0x0319;
   509 		private const int WM_INPUT		= 0x00FF;
   510 
   511 		private const int APPCOMMAND_BROWSER_BACKWARD   = 1;
   512 		private const int APPCOMMAND_VOLUME_MUTE        = 8;
   513 		private const int APPCOMMAND_VOLUME_DOWN        = 9;
   514 		private const int APPCOMMAND_VOLUME_UP          = 10;
   515 		private const int APPCOMMAND_MEDIA_NEXTTRACK    = 11;
   516 		private const int APPCOMMAND_MEDIA_PREVIOUSTRACK = 12;
   517 		private const int APPCOMMAND_MEDIA_STOP         = 13;
   518 		private const int APPCOMMAND_MEDIA_PLAY_PAUSE   = 14;
   519 		private const int APPCOMMAND_MEDIA_PLAY         = 46;
   520 		private const int APPCOMMAND_MEDIA_PAUSE        = 47;
   521 		private const int APPCOMMAND_MEDIA_RECORD       = 48;
   522 		private const int APPCOMMAND_MEDIA_FAST_FORWARD = 49;
   523 		private const int APPCOMMAND_MEDIA_REWIND       = 50;
   524 		private const int APPCOMMAND_MEDIA_CHANNEL_UP   = 51;
   525 		private const int APPCOMMAND_MEDIA_CHANNEL_DOWN = 52;
   526 
   527 		private const int RID_INPUT						= 0x10000003;
   528 		private const int RID_HEADER					= 0x10000005;
   529 
   530 		private const int FAPPCOMMAND_MASK				= 0xF000;
   531 		private const int FAPPCOMMAND_MOUSE				= 0x8000;
   532 		private const int FAPPCOMMAND_KEY				= 0;
   533 		private const int FAPPCOMMAND_OEM				= 0x1000;
   534 
   535         /// <summary>
   536         /// GetRawInputDeviceInfo pData points to a string that contains the device name.
   537         /// </summary>
   538         public const uint RIDI_DEVICENAME = 0x20000007;
   539         /// <summary>
   540         /// GetRawInputDeviceInfo For this uiCommand only, the value in pcbSize is the character count (not the byte count).
   541         /// </summary>
   542         public const uint RIDI_DEVICEINFO = 0x2000000b;
   543         /// <summary>
   544         /// GetRawInputDeviceInfo pData points to an RID_DEVICE_INFO structure.
   545         /// </summary>
   546         public const uint RIDI_PREPARSEDDATA = 0x20000005;
   547 
   548 
   549         /// <summary>
   550         /// Data comes from a mouse.
   551         /// </summary>
   552         public const uint RIM_TYPEMOUSE = 0;
   553         /// <summary>
   554         /// Data comes from a keyboard.
   555         /// </summary>
   556         public const uint RIM_TYPEKEYBOARD = 1;
   557         /// <summary>
   558         /// Data comes from an HID that is not a keyboard or a mouse.
   559         /// </summary>
   560         public const uint RIM_TYPEHID = 2;
   561 
   562 
   563 
   564 
   565 
   566 		public delegate void RemoteControlDeviceEventHandler(object sender, RemoteControlEventArgs e);
   567 		public event RemoteControlDeviceEventHandler ButtonPressed;
   568 
   569 
   570 		//-------------------------------------------------------------
   571 		// constructors
   572 		//-------------------------------------------------------------
   573 
   574 		public RemoteControlDevice()
   575 		{
   576 			// Register the input device to receive the commands from the
   577 			// remote device. See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwmt/html/remote_control.asp
   578 			// for the vendor defined usage page.
   579 
   580 			RAWINPUTDEVICE[] rid = new RAWINPUTDEVICE[3];
   581 
   582 			rid[0].usUsagePage = 0xFFBC;
   583 			rid[0].usUsage = 0x88;
   584 			rid[0].dwFlags = 0;
   585 
   586 			rid[1].usUsagePage = 0x0C;
   587 			rid[1].usUsage = 0x01;
   588 			rid[1].dwFlags = 0;
   589 
   590 			rid[2].usUsagePage = 0x0C;
   591 			rid[2].usUsage = 0x80;
   592 			rid[2].dwFlags = 0;
   593 
   594 			if (!RegisterRawInputDevices(rid,
   595 				(uint) rid.Length,
   596 				(uint) Marshal.SizeOf(rid[0]))
   597 				)
   598 			{
   599 				throw new ApplicationException("Failed to register raw input devices.");
   600 			}
   601 		}
   602 
   603 
   604 		//-------------------------------------------------------------
   605 		// methods
   606 		//-------------------------------------------------------------
   607 
   608 		public void ProcessMessage(Message message)
   609 		{
   610 			int param;
   611 
   612 			switch (message.Msg)
   613 			{
   614 				case WM_KEYDOWN:
   615 					param = message.WParam.ToInt32();
   616 					ProcessKeyDown(param);
   617 					break;
   618 				case WM_APPCOMMAND:
   619 					param = message.LParam.ToInt32();
   620 					ProcessAppCommand(param);
   621 					break;
   622 				case WM_INPUT:
   623 					ProcessInputCommand(ref message);
   624                     message.Result = new IntPtr(0);
   625 					break;
   626 			}
   627 
   628 		}
   629 
   630 
   631 		//-------------------------------------------------------------
   632 		// methods (helpers)
   633 		//-------------------------------------------------------------
   634 
   635 		private void ProcessKeyDown(int param)
   636 		{
   637 			RemoteControlButton rcb = RemoteControlButton.Unknown;
   638 
   639 			switch (param)
   640 			{
   641 				case (int) Keys.Escape:
   642 					rcb = RemoteControlButton.Clear;
   643 					break;
   644 				case (int) Keys.Down:
   645 					rcb = RemoteControlButton.Down;
   646 					break;
   647 				case (int) Keys.Left:
   648 					rcb = RemoteControlButton.Left;
   649 					break;
   650 				case (int) Keys.D0:
   651 					rcb = RemoteControlButton.Digit0;
   652 					break;
   653 				case (int) Keys.D1:
   654 					rcb = RemoteControlButton.Digit1;
   655 					break;
   656 				case (int) Keys.D2:
   657 					rcb = RemoteControlButton.Digit2;
   658 					break;
   659 				case (int) Keys.D3:
   660 					rcb = RemoteControlButton.Digit3;
   661 					break;
   662 				case (int) Keys.D4:
   663 					rcb = RemoteControlButton.Digit4;
   664 					break;
   665 				case (int) Keys.D5:
   666 					rcb = RemoteControlButton.Digit5;
   667 					break;
   668 				case (int) Keys.D6:
   669 					rcb = RemoteControlButton.Digit6;
   670 					break;
   671 				case (int) Keys.D7:
   672 					rcb = RemoteControlButton.Digit7;
   673 					break;
   674 				case (int) Keys.D8:
   675 					rcb = RemoteControlButton.Digit8;
   676 					break;
   677 				case (int) Keys.D9:
   678 					rcb = RemoteControlButton.Digit9;
   679 					break;
   680 				case (int) Keys.Enter:
   681 					rcb = RemoteControlButton.Enter;
   682 					break;
   683 				case (int) Keys.Right:
   684 					rcb = RemoteControlButton.Right;
   685 					break;
   686 				case (int) Keys.Up:
   687 					rcb = RemoteControlButton.Up;
   688 					break;
   689 			}
   690 
   691 			if (this.ButtonPressed != null && rcb != RemoteControlButton.Unknown)
   692 				this.ButtonPressed(this, new RemoteControlEventArgs(rcb, GetDevice(param)));
   693 		}
   694 
   695 
   696 		private void ProcessAppCommand(int param)
   697 		{
   698 			RemoteControlButton rcb = RemoteControlButton.Unknown;
   699 
   700 			int cmd	= (int) (((ushort) (param >> 16)) & ~FAPPCOMMAND_MASK);
   701 
   702 			switch (cmd)
   703 			{
   704 				case APPCOMMAND_BROWSER_BACKWARD:
   705 					rcb = RemoteControlButton.Back;
   706 					break;
   707 				case APPCOMMAND_MEDIA_CHANNEL_DOWN:
   708 					rcb = RemoteControlButton.ChannelDown;
   709 					break;
   710 				case APPCOMMAND_MEDIA_CHANNEL_UP:
   711 					rcb = RemoteControlButton.ChannelUp;
   712 					break;
   713 				case APPCOMMAND_MEDIA_FAST_FORWARD:
   714 					rcb = RemoteControlButton.FastForward;
   715 					break;
   716 				case APPCOMMAND_VOLUME_MUTE:
   717 					rcb = RemoteControlButton.VolumeMute;
   718 					break;
   719 				case APPCOMMAND_MEDIA_PAUSE:
   720 					rcb = RemoteControlButton.Pause;
   721 					break;
   722 				case APPCOMMAND_MEDIA_PLAY:
   723 					rcb = RemoteControlButton.Play;
   724 					break;
   725                 case APPCOMMAND_MEDIA_PLAY_PAUSE:
   726                     rcb = RemoteControlButton.PlayPause;
   727                     break;
   728 				case APPCOMMAND_MEDIA_RECORD:
   729 					rcb = RemoteControlButton.Record;
   730 					break;
   731 				case APPCOMMAND_MEDIA_PREVIOUSTRACK:
   732 					rcb = RemoteControlButton.PreviousTrack;
   733 					break;
   734 				case APPCOMMAND_MEDIA_REWIND:
   735 					rcb = RemoteControlButton.Rewind;
   736 					break;
   737 				case APPCOMMAND_MEDIA_NEXTTRACK:
   738 					rcb = RemoteControlButton.NextTrack;
   739 					break;
   740 				case APPCOMMAND_MEDIA_STOP:
   741 					rcb = RemoteControlButton.Stop;
   742 					break;
   743 				case APPCOMMAND_VOLUME_DOWN:
   744 					rcb = RemoteControlButton.VolumeDown;
   745 					break;
   746 				case APPCOMMAND_VOLUME_UP:
   747 					rcb = RemoteControlButton.VolumeUp;
   748 					break;
   749 			}
   750 
   751 			if (this.ButtonPressed != null && rcb != RemoteControlButton.Unknown)
   752 				this.ButtonPressed(this, new RemoteControlEventArgs(rcb, GetDevice(param)));
   753 		}
   754 
   755 
   756 		private void ProcessInputCommand(ref Message message)
   757 		{
   758             Debug.WriteLine("================WM_INPUT================");
   759 
   760 			uint dwSize = 0;
   761 
   762             uint sizeOfHeader=(uint)Marshal.SizeOf(typeof(RAWINPUTHEADER));
   763 
   764             //Get the size of our raw input data.
   765 			GetRawInputData(message.LParam,	RID_INPUT, IntPtr.Zero,	ref dwSize,	sizeOfHeader);
   766 
   767             //Allocate a large enough buffer
   768 			IntPtr rawInputBuffer = Marshal.AllocHGlobal((int) dwSize);
   769 			try
   770 			{
   771 				if(rawInputBuffer == IntPtr.Zero)
   772 					return;
   773 
   774                 //Now read our RAWINPUT data
   775 				if (GetRawInputData(message.LParam,	RID_INPUT, rawInputBuffer, ref dwSize, (uint) Marshal.SizeOf(typeof(RAWINPUTHEADER))) != dwSize)
   776 				{
   777 					return;
   778 				}
   779 
   780                 //Cast our buffer
   781                 RAWINPUT raw = (RAWINPUT)Marshal.PtrToStructure(rawInputBuffer, typeof(RAWINPUT));
   782 
   783                 //Get Device Info
   784                 uint deviceInfoSize = (uint)Marshal.SizeOf(typeof(RID_DEVICE_INFO));
   785                 IntPtr deviceInfoBuffer = Marshal.AllocHGlobal((int)deviceInfoSize);
   786 
   787                 int res = GetRawInputDeviceInfo(raw.header.hDevice, RIDI_DEVICEINFO, deviceInfoBuffer, ref deviceInfoSize);
   788                 if (res <= 0)
   789                 {
   790                     Debug.WriteLine("WM_INPUT could not read device info: " + Marshal.GetLastWin32Error().ToString());
   791                     return;
   792                 }
   793 
   794                 //Cast our buffer
   795                 RID_DEVICE_INFO deviceInfo = (RID_DEVICE_INFO)Marshal.PtrToStructure(deviceInfoBuffer, typeof(RID_DEVICE_INFO));
   796 
   797                 //Check type of input device and quite if we don't like it
   798                 switch (deviceInfo.dwType)
   799                 {
   800                     case RIM_TYPEHID:
   801                         Debug.WriteLine("WM_INPUT source device is HID.");
   802                         break;
   803                     case RIM_TYPEMOUSE:
   804                         Debug.WriteLine("WM_INPUT source device is Mouse.");
   805                         return;
   806                     case RIM_TYPEKEYBOARD:
   807                         Debug.WriteLine("WM_INPUT source device is Keyboard.");
   808                         return;
   809                     default:
   810                         Debug.WriteLine("WM_INPUT source device is Unknown.");
   811                         return;
   812                 }
   813 
   814                 //Get Usage Page and Usage
   815                 Debug.WriteLine("Usage Page: 0x" + deviceInfo.hid.usUsagePage.ToString("X4") + " Usage: 0x" + deviceInfo.hid.usUsage.ToString("X4"));
   816 
   817                 //Check that our raw input is HID
   818                 if (raw.header.dwType == RIM_TYPEHID && raw.hid.dwSizeHid>0)
   819 				{
   820                     //Allocate a buffer for one HID message
   821 					byte[] bRawData = new byte[raw.hid.dwSizeHid];
   822 
   823                     //Compute the address from which to copy our HID message
   824                     int pRawData = 0;
   825                     unsafe
   826                     {
   827                         byte* source = (byte*)rawInputBuffer;
   828                         source += sizeof(RAWINPUTHEADER) + sizeof(RAWHID);
   829                         pRawData = (int)source;
   830                     }
   831 
   832                     //Copy HID message into our buffer
   833                     Marshal.Copy(new IntPtr(pRawData), bRawData, 0, raw.hid.dwSizeHid);
   834                     //bRawData[0] //Not sure what's the meaning of the code at offset 0
   835                     //TODO: check size before access
   836                     int rawData = bRawData[1]; //Get button code
   837                     //Print HID codes in our debug output
   838                     string hidDump = "HID " + raw.hid.dwCount + "/" + raw.hid.dwSizeHid + ":";
   839                     foreach (byte b in bRawData)
   840                     {
   841                         hidDump += b.ToString("X2");
   842                     }
   843                     Debug.WriteLine(hidDump);
   844 
   845                     //Make sure both usage page and usage are matching MCE remote
   846                     if (deviceInfo.hid.usUsagePage != (ushort)Hid.UsagePage.MceRemote || deviceInfo.hid.usUsage != (ushort)Hid.MceRemoteUsage)
   847                     {
   848                         Debug.WriteLine("Not MCE remote page and usage.");
   849                         return;
   850                     }
   851 
   852                     if (Enum.IsDefined(typeof(MceButton), rawData) && rawData!=0) //Our button is a known MCE button
   853                     {
   854                         if (this.ButtonPressed != null) //What's that?
   855                         {
   856                             this.ButtonPressed(this, new RemoteControlEventArgs((MceButton)rawData, GetDevice(message.LParam.ToInt32())));
   857                         }
   858                     }
   859 				}
   860 				else if(raw.header.dwType == RIM_TYPEMOUSE)
   861 				{
   862 					// do mouse handling...
   863 				}
   864 				else if(raw.header.dwType == RIM_TYPEKEYBOARD)
   865 				{
   866 					// do keyboard handling...
   867 				}
   868 			}
   869 			finally
   870 			{
   871 				Marshal.FreeHGlobal(rawInputBuffer);
   872 			}
   873 		}
   874 
   875 
   876 		private InputDevice GetDevice(int param)
   877 		{
   878 			InputDevice inputDevice;
   879 
   880 			switch ((int) (((ushort) (param >> 16)) & FAPPCOMMAND_MASK))
   881 			{
   882 				case FAPPCOMMAND_OEM:
   883 					inputDevice = InputDevice.OEM;
   884 					break;
   885 				case FAPPCOMMAND_MOUSE:
   886 					inputDevice = InputDevice.Mouse;
   887 					break;
   888 				default:
   889 					inputDevice = InputDevice.Key;
   890 					break;
   891 			}
   892 
   893 			return inputDevice;
   894 		}
   895 	}
   896 }