RemoteControlDevice.cs
author sl
Sat, 06 Dec 2014 02:46:46 +0100
changeset 22 8b2a3da54187
parent 21 fb4f80d5cf57
child 23 743cadfacda0
permissions -rw-r--r--
Adding method to get the device name.
     1 using System;
     2 using System.Windows.Forms;
     3 using System.Runtime.InteropServices;
     4 using System.Diagnostics;
     5 using Hid.UsageTables;
     6 using Win32;
     7 
     8 namespace Devices.RemoteControl
     9 {
    10 
    11 	public enum InputDevice
    12 	{
    13 		Key,
    14 		Mouse,
    15 		OEM
    16 	}
    17 
    18 
    19 	public enum RemoteControlButton
    20 	{
    21 		Clear,
    22 		Down,
    23 		Left,
    24 		Digit0,
    25 		Digit1,
    26 		Digit2,
    27 		Digit3,
    28 		Digit4,
    29 		Digit5,
    30 		Digit6,
    31 		Digit7,
    32 		Digit8,
    33 		Digit9,
    34 		Enter,
    35 		Right,
    36 		Up,
    37 
    38 		Back,
    39 		ChannelDown,
    40 		ChannelUp,
    41 		FastForward,
    42 		VolumeMute,
    43 		Pause,
    44 		Play,
    45         PlayPause,
    46 		Record,
    47 		PreviousTrack,
    48 		Rewind,
    49 		NextTrack,
    50 		Stop,
    51 		VolumeDown,
    52 		VolumeUp,
    53 
    54 		RecordedTV,
    55 		Guide,
    56 		LiveTV,
    57 		MoreInfo,
    58         Print,
    59 		DVDMenu,
    60 		DVDAngle,
    61 		DVDAudio,
    62 		DVDSubtitle,
    63 		MyMusic,
    64 		MyPictures,
    65 		MyVideos,
    66 		MyTV,
    67 		OEM1,
    68 		OEM2,
    69 		StandBy,
    70 		TVJump,
    71 
    72 		Unknown
    73 	}
    74 
    75 
    76 	#region RemoteControlEventArgs
    77 
    78 	public class RemoteControlEventArgs : EventArgs
    79 	{
    80         RemoteControlButton _rcb;
    81 		InputDevice _device;
    82         MceButton iMceButton;
    83         ConsumerControl iConsumerControl;
    84 
    85         public RemoteControlEventArgs(RemoteControlButton rcb, InputDevice device)
    86 		{
    87             SetNullButtons();
    88             //
    89 			_rcb = rcb;
    90 			_device = device;            
    91 		}
    92 
    93         public RemoteControlEventArgs(ConsumerControl aConsumerControl, InputDevice device)
    94         {
    95             SetNullButtons();
    96             //
    97             iConsumerControl = aConsumerControl;            
    98             _device = device;
    99         }
   100 
   101 
   102         public RemoteControlEventArgs(MceButton mce, InputDevice device)
   103         {
   104             SetNullButtons();
   105             //
   106             iMceButton = mce;            
   107             _device = device;
   108         }
   109 
   110         private void SetNullButtons()
   111         {
   112             iConsumerControl = ConsumerControl.Null;
   113             iMceButton = MceButton.Null;
   114             _rcb = RemoteControlButton.Unknown;
   115         }
   116 
   117 		public RemoteControlEventArgs()
   118 		{
   119             iMceButton = MceButton.Null;
   120 			_rcb = RemoteControlButton.Unknown;
   121 			_device = InputDevice.Key;
   122 		}
   123 
   124 		public RemoteControlButton Button
   125 		{
   126 			get { return _rcb;  }
   127 			set { _rcb = value; }
   128 		}
   129 
   130         public MceButton MceButton
   131         {
   132             get { return iMceButton; }
   133             set { iMceButton = value; }
   134         }
   135 
   136         public ConsumerControl ConsumerControl
   137         {
   138             get { return iConsumerControl; }
   139             set { iConsumerControl = value; }
   140         }
   141 
   142 		public InputDevice Device
   143 		{
   144 			get { return _device;  }
   145 			set { _device = value; }
   146 		}
   147 	}
   148 
   149 	#endregion RemoteControlEventArgs
   150 
   151 
   152 	public sealed class RemoteControlDevice
   153 	{
   154 		public delegate bool RemoteControlDeviceEventHandler(object sender, RemoteControlEventArgs e);
   155 		public event RemoteControlDeviceEventHandler ButtonPressed;
   156 
   157         /// <summary>
   158         /// Return true if the usage was processed.
   159         /// </summary>
   160         /// <param name="aUsage"></param>
   161         /// <returns></returns>
   162         public delegate bool HidUsageHandler(ushort aUsage);
   163         
   164 
   165 		//-------------------------------------------------------------
   166 		// constructors
   167 		//-------------------------------------------------------------
   168 
   169 		public RemoteControlDevice(IntPtr aHWND)
   170 		{
   171 			// Register the input device to receive the commands from the
   172 			// remote device. See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwmt/html/remote_control.asp
   173 			// for the vendor defined usage page.
   174 
   175 			RAWINPUTDEVICE[] rid = new RAWINPUTDEVICE[5];
   176 
   177             int i = 0;
   178 			rid[i].usUsagePage = (ushort)Hid.UsagePage.MceRemote;
   179             rid[i].usUsage = (ushort)Hid.UsageIdMce.MceRemote;
   180             rid[i].dwFlags = Const.RIDEV_EXINPUTSINK;
   181             rid[i].hwndTarget = aHWND;
   182 
   183             i++;
   184             rid[i].usUsagePage = (ushort)Hid.UsagePage.Consumer;
   185             rid[i].usUsage = (ushort)Hid.UsageIdConsumer.ConsumerControl;
   186             rid[i].dwFlags = Const.RIDEV_EXINPUTSINK;
   187             rid[i].hwndTarget = aHWND;
   188 
   189             i++;
   190             rid[i].usUsagePage = (ushort)Hid.UsagePage.Consumer;
   191             rid[i].usUsage = (ushort)Hid.UsageIdConsumer.Selection;
   192             rid[i].dwFlags = Const.RIDEV_EXINPUTSINK;
   193             rid[i].hwndTarget = aHWND;
   194 
   195             i++;
   196             rid[i].usUsagePage = (ushort)Hid.UsagePage.GenericDesktopControl;
   197             rid[i].usUsage = (ushort)Hid.UsageIdGenericDesktop.SystemControl;
   198             rid[i].dwFlags = Const.RIDEV_EXINPUTSINK;
   199             rid[i].hwndTarget = aHWND;
   200 
   201             i++;
   202             rid[i].usUsagePage = (ushort)Hid.UsagePage.GenericDesktopControl;
   203             rid[i].usUsage = (ushort)Hid.UsageIdGenericDesktop.Keyboard;
   204             rid[i].dwFlags = Const.RIDEV_EXINPUTSINK;
   205             rid[i].hwndTarget = aHWND;
   206 
   207             //i++;
   208             //rid[i].usUsagePage = (ushort)Hid.UsagePage.GenericDesktopControl;
   209             //rid[i].usUsage = (ushort)Hid.UsageIdGenericDesktop.Mouse;
   210             //rid[i].dwFlags = Const.RIDEV_EXINPUTSINK;
   211             //rid[i].hwndTarget = aHWND;
   212 
   213 
   214 			if (!Function.RegisterRawInputDevices(rid,(uint) rid.Length,(uint) Marshal.SizeOf(rid[0])))
   215 			{
   216                 throw new ApplicationException("Failed to register raw input devices: " + Marshal.GetLastWin32Error().ToString());
   217 			}
   218 		}
   219 
   220 
   221 		//-------------------------------------------------------------
   222 		// methods
   223 		//-------------------------------------------------------------
   224 
   225 		public void ProcessMessage(Message message)
   226 		{
   227 			switch (message.Msg)
   228 			{
   229 				case Const.WM_KEYDOWN:
   230                     ProcessKeyDown(message.WParam);
   231 					break;
   232                 case Const.WM_INPUT:
   233                     //Returning zero means we processed that message.
   234                     message.Result = new IntPtr(0);
   235 					ProcessInputCommand(ref message);
   236 					break;
   237 			}
   238 
   239 		}
   240 
   241 
   242 		//-------------------------------------------------------------
   243 		// methods (helpers)
   244 		//-------------------------------------------------------------
   245 
   246 		private void ProcessKeyDown(IntPtr wParam)
   247 		{
   248 			RemoteControlButton rcb = RemoteControlButton.Unknown;
   249 
   250             switch (wParam.ToInt32())
   251 			{
   252 				case (int) Keys.Escape:
   253 					rcb = RemoteControlButton.Clear;
   254                     break;
   255                 case (int)Keys.Up:
   256                     rcb = RemoteControlButton.Up;
   257                     break;
   258 				case (int) Keys.Down:
   259 					rcb = RemoteControlButton.Down;
   260 					break;
   261 				case (int) Keys.Left:
   262 					rcb = RemoteControlButton.Left;
   263                     break;
   264                 case (int)Keys.Right:
   265                     rcb = RemoteControlButton.Right;
   266                     break;
   267                 case (int)Keys.Enter:
   268                     rcb = RemoteControlButton.Enter;
   269                     break;
   270 				case (int) Keys.D0:
   271 					rcb = RemoteControlButton.Digit0;
   272 					break;
   273 				case (int) Keys.D1:
   274 					rcb = RemoteControlButton.Digit1;
   275 					break;
   276 				case (int) Keys.D2:
   277 					rcb = RemoteControlButton.Digit2;
   278 					break;
   279 				case (int) Keys.D3:
   280 					rcb = RemoteControlButton.Digit3;
   281 					break;
   282 				case (int) Keys.D4:
   283 					rcb = RemoteControlButton.Digit4;
   284 					break;
   285 				case (int) Keys.D5:
   286 					rcb = RemoteControlButton.Digit5;
   287 					break;
   288 				case (int) Keys.D6:
   289 					rcb = RemoteControlButton.Digit6;
   290 					break;
   291 				case (int) Keys.D7:
   292 					rcb = RemoteControlButton.Digit7;
   293 					break;
   294 				case (int) Keys.D8:
   295 					rcb = RemoteControlButton.Digit8;
   296 					break;
   297 				case (int) Keys.D9:
   298 					rcb = RemoteControlButton.Digit9;
   299                     break;
   300 			}
   301 
   302             if (this.ButtonPressed != null && rcb != RemoteControlButton.Unknown)
   303             {
   304                 Debug.WriteLine("KeyDown: " + rcb.ToString());
   305                 this.ButtonPressed(this, new RemoteControlEventArgs(rcb, InputDevice.Key));
   306             }
   307 		}
   308 
   309 
   310         /// <summary>
   311         /// 
   312         /// </summary>
   313         /// <param name="aUsage"></param>
   314         private bool HidConsumerDeviceHandler(ushort aUsage)
   315         {
   316             if (aUsage == 0)
   317             {
   318                 //Just skip those
   319                 return false;
   320             }
   321 
   322             if (Enum.IsDefined(typeof(ConsumerControl), aUsage) && aUsage != 0) //Our button is a known consumer control
   323             {
   324                 if (this.ButtonPressed != null)
   325                 {
   326                     return this.ButtonPressed(this, new RemoteControlEventArgs((ConsumerControl)aUsage, InputDevice.OEM));
   327                 }
   328                 return false;
   329             }
   330             else
   331             {
   332                 Debug.WriteLine("Unknown Consumer Control!");
   333                 return false;
   334             }
   335         }
   336 
   337         /// <summary>
   338         /// 
   339         /// </summary>
   340         /// <param name="aUsage"></param>
   341         private bool HidMceRemoteHandler(ushort aUsage)
   342         {
   343             if (aUsage == 0)
   344             {
   345                 //Just skip those
   346                 return false;
   347             }
   348 
   349 
   350             if (Enum.IsDefined(typeof(MceButton), aUsage) && aUsage != 0) //Our button is a known MCE button
   351             {
   352                 if (this.ButtonPressed != null)
   353                 {
   354                     return this.ButtonPressed(this, new RemoteControlEventArgs((MceButton)aUsage, InputDevice.OEM));                    
   355                 }
   356                 return false;
   357             }
   358             else
   359             {
   360                 Debug.WriteLine("Unknown MCE button!");
   361                 return false;
   362             }
   363         }
   364 
   365 
   366 		private void ProcessInputCommand(ref Message message)
   367 		{
   368             //We received a WM_INPUT message
   369             Debug.WriteLine("================WM_INPUT================");
   370 
   371             //Check if we received this message while in background or foreground
   372             if (Macro.GET_RAWINPUT_CODE_WPARAM(message.WParam) == Const.RIM_INPUT)
   373             {
   374                 Debug.WriteLine("================FOREGROUND");
   375             }
   376             else if (Macro.GET_RAWINPUT_CODE_WPARAM(message.WParam) == Const.RIM_INPUTSINK)
   377             {
   378                 Debug.WriteLine("================BACKGROUND");
   379             }
   380 
   381             //Declare some pointers
   382             IntPtr rawInputBuffer = IntPtr.Zero;
   383             //My understanding is that this is basically our HID descriptor
   384             IntPtr preParsedData = IntPtr.Zero;
   385 
   386             try
   387             {
   388                 //Fetch raw input
   389                 RAWINPUT rawInput = new RAWINPUT();
   390                 if (!RawInput.GetRawInputData(message.LParam, ref rawInput, ref rawInputBuffer))
   391                 {
   392                     return;
   393                 }
   394 
   395                 //Fetch device info
   396                 RID_DEVICE_INFO deviceInfo = new RID_DEVICE_INFO();
   397                 if (!RawInput.GetDeviceInfo(rawInput.header.hDevice, ref deviceInfo))
   398                 {
   399                     return;
   400                 }
   401 
   402                 //
   403                 Debug.WriteLine(RawInput.GetDeviceName(rawInput.header.hDevice));
   404                
   405 
   406                 if (rawInput.header.dwType == Const.RIM_TYPEHID)  //Check that our raw input is HID                        
   407                 {
   408                     Debug.WriteLine("WM_INPUT source device is HID.");
   409                     //Get Usage Page and Usage
   410                     Debug.WriteLine("Usage Page: 0x" + deviceInfo.hid.usUsagePage.ToString("X4") + " Usage ID: 0x" + deviceInfo.hid.usUsage.ToString("X4"));
   411 
   412 
   413                     preParsedData = RawInput.GetPreParsedData(rawInput.header.hDevice);
   414 
   415                     //
   416                     HidUsageHandler usagePageHandler=null;
   417 
   418                     //Check if this an MCE remote HID message
   419                     if (deviceInfo.hid.usUsagePage == (ushort)Hid.UsagePage.MceRemote && deviceInfo.hid.usUsage == (ushort)Hid.UsageIdMce.MceRemote)
   420                     {                        
   421                         usagePageHandler = HidMceRemoteHandler;
   422                     }
   423                     //Check if this is a consumer control HID message
   424                     else if (deviceInfo.hid.usUsagePage == (ushort)Hid.UsagePage.Consumer && deviceInfo.hid.usUsage == (ushort)Hid.UsageIdConsumer.ConsumerControl)
   425                     {
   426                         usagePageHandler = HidConsumerDeviceHandler;
   427                     }
   428                     //Unknown HID message
   429                     else
   430                     {
   431                         Debug.WriteLine("Unknown HID message.");
   432                         return;
   433                     }
   434 
   435                     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
   436                         && rawInput.hid.dwCount > 0))    //Check that we have at least one HID msg
   437                     {
   438                         return;
   439                     }
   440 
   441 
   442                     //Allocate a buffer for one HID input
   443                     byte[] hidInputReport = new byte[rawInput.hid.dwSizeHid];
   444 
   445                     Debug.WriteLine("Raw input contains " + rawInput.hid.dwCount + " HID input report(s)");
   446 
   447                     //For each HID input report in our raw input
   448                     for (int i = 0; i < rawInput.hid.dwCount; i++)
   449                     {
   450                         //Compute the address from which to copy our HID input
   451                         int hidInputOffset = 0;
   452                         unsafe
   453                         {
   454                             byte* source = (byte*)rawInputBuffer;
   455                             source += sizeof(RAWINPUTHEADER) + sizeof(RAWHID) + (rawInput.hid.dwSizeHid * i);
   456                             hidInputOffset = (int)source;
   457                         }
   458 
   459                         //Copy HID input into our buffer
   460                         Marshal.Copy(new IntPtr(hidInputOffset), hidInputReport, 0, (int)rawInput.hid.dwSizeHid);
   461 
   462                         //Print HID input report in our debug output
   463                         string hidDump = "HID input report: ";
   464                         foreach (byte b in hidInputReport)
   465                         {
   466                             hidDump += b.ToString("X2");
   467                         }
   468                         Debug.WriteLine(hidDump);
   469                         
   470                         //Proper parsing now
   471                         uint usageCount = 1; //Assuming a single usage per input report. Is that correct?
   472                         Win32.USAGE_AND_PAGE[] usages = new Win32.USAGE_AND_PAGE[usageCount];
   473                         Win32.HidStatus status = Win32.Function.HidP_GetUsagesEx(Win32.HIDP_REPORT_TYPE.HidP_Input, 0, usages, ref usageCount, preParsedData, hidInputReport, (uint)hidInputReport.Length);
   474                         if (status != Win32.HidStatus.HIDP_STATUS_SUCCESS)
   475                         {
   476                             Debug.WriteLine("Could not parse HID data!");
   477                         }
   478                         else
   479                         {
   480                             Debug.WriteLine("UsagePage: 0x" + usages[0].UsagePage.ToString("X4"));
   481                             Debug.WriteLine("Usage: 0x" + usages[0].Usage.ToString("X4"));
   482                             //Call on our Usage Page handler
   483                             usagePageHandler(usages[0].Usage);
   484                         }
   485                     }
   486 
   487                 }
   488                 else if (rawInput.header.dwType == Const.RIM_TYPEMOUSE)
   489                 {
   490                     Debug.WriteLine("WM_INPUT source device is Mouse.");
   491                     // do mouse handling...
   492                 }
   493                 else if (rawInput.header.dwType == Const.RIM_TYPEKEYBOARD)
   494                 {
   495                     Debug.WriteLine("WM_INPUT source device is Keyboard.");
   496                     // do keyboard handling...
   497                     Debug.WriteLine("Type: " + deviceInfo.keyboard.dwType.ToString());
   498                     Debug.WriteLine("SubType: " + deviceInfo.keyboard.dwSubType.ToString());
   499                     Debug.WriteLine("Mode: " + deviceInfo.keyboard.dwKeyboardMode.ToString());
   500                     Debug.WriteLine("Number of function keys: " + deviceInfo.keyboard.dwNumberOfFunctionKeys.ToString());
   501                     Debug.WriteLine("Number of indicators: " + deviceInfo.keyboard.dwNumberOfIndicators.ToString());
   502                     Debug.WriteLine("Number of keys total: " + deviceInfo.keyboard.dwNumberOfKeysTotal.ToString());                   
   503                 }
   504             }
   505             finally
   506             {
   507                 //Always executed when leaving our try block
   508                 Marshal.FreeHGlobal(rawInputBuffer);
   509                 Marshal.FreeHGlobal(preParsedData);
   510             }
   511 		}
   512 	}
   513 }