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