RemoteControlDevice.cs
author sl
Fri, 07 Nov 2014 22:12:22 +0100
changeset 10 17f8421146ba
parent 9 94850bfc12b5
child 11 2ff6dbe0d356
permissions -rw-r--r--
More cleanup and re-org.
     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 		Details,
    58 		DVDMenu,
    59 		DVDAngle,
    60 		DVDAudio,
    61 		DVDSubtitle,
    62 		MyMusic,
    63 		MyPictures,
    64 		MyVideos,
    65 		MyTV,
    66 		OEM1,
    67 		OEM2,
    68 		StandBy,
    69 		TVJump,
    70 
    71 		Unknown
    72 	}
    73 
    74 
    75 	#region RemoteControlEventArgs
    76 
    77 	public class RemoteControlEventArgs : EventArgs
    78 	{
    79         RemoteControlButton _rcb;
    80 		InputDevice _device;
    81         MceButton iMceButton;
    82 
    83         public RemoteControlEventArgs(RemoteControlButton rcb, InputDevice device)
    84 		{
    85             iMceButton = MceButton.Null;
    86 			_rcb = rcb;
    87 			_device = device;
    88 		}
    89 
    90         public RemoteControlEventArgs(MceButton mce, InputDevice device)
    91         {
    92             iMceButton = mce;
    93             _rcb = RemoteControlButton.Unknown;
    94             _device = device;
    95         }
    96 
    97 		public RemoteControlEventArgs()
    98 		{
    99             iMceButton = MceButton.Null;
   100 			_rcb = RemoteControlButton.Unknown;
   101 			_device = InputDevice.Key;
   102 		}
   103 
   104 		public RemoteControlButton Button
   105 		{
   106 			get { return _rcb;  }
   107 			set { _rcb = value; }
   108 		}
   109 
   110         public MceButton MceButton
   111         {
   112             get { return iMceButton; }
   113             set { iMceButton = value; }
   114         }
   115 
   116 		public InputDevice Device
   117 		{
   118 			get { return _device;  }
   119 			set { _device = value; }
   120 		}
   121 	}
   122 
   123 	#endregion RemoteControlEventArgs
   124 
   125 
   126 	public sealed class RemoteControlDevice
   127 	{
   128 		private const int WM_KEYDOWN	= 0x0100;
   129 		private const int WM_APPCOMMAND	= 0x0319;
   130 		private const int WM_INPUT		= 0x00FF;
   131 
   132 		private const int APPCOMMAND_BROWSER_BACKWARD   = 1;
   133 		private const int APPCOMMAND_VOLUME_MUTE        = 8;
   134 		private const int APPCOMMAND_VOLUME_DOWN        = 9;
   135 		private const int APPCOMMAND_VOLUME_UP          = 10;
   136 		private const int APPCOMMAND_MEDIA_NEXTTRACK    = 11;
   137 		private const int APPCOMMAND_MEDIA_PREVIOUSTRACK = 12;
   138 		private const int APPCOMMAND_MEDIA_STOP         = 13;
   139 		private const int APPCOMMAND_MEDIA_PLAY_PAUSE   = 14;
   140 		private const int APPCOMMAND_MEDIA_PLAY         = 46;
   141 		private const int APPCOMMAND_MEDIA_PAUSE        = 47;
   142 		private const int APPCOMMAND_MEDIA_RECORD       = 48;
   143 		private const int APPCOMMAND_MEDIA_FAST_FORWARD = 49;
   144 		private const int APPCOMMAND_MEDIA_REWIND       = 50;
   145 		private const int APPCOMMAND_MEDIA_CHANNEL_UP   = 51;
   146 		private const int APPCOMMAND_MEDIA_CHANNEL_DOWN = 52;
   147 
   148 		private const int FAPPCOMMAND_MASK				= 0xF000;
   149 		private const int FAPPCOMMAND_MOUSE				= 0x8000;
   150 		private const int FAPPCOMMAND_KEY				= 0;
   151 		private const int FAPPCOMMAND_OEM				= 0x1000;
   152 
   153 
   154 
   155 		public delegate void RemoteControlDeviceEventHandler(object sender, RemoteControlEventArgs e);
   156 		public event RemoteControlDeviceEventHandler ButtonPressed;
   157 
   158 
   159 		//-------------------------------------------------------------
   160 		// constructors
   161 		//-------------------------------------------------------------
   162 
   163 		public RemoteControlDevice()
   164 		{
   165 			// Register the input device to receive the commands from the
   166 			// remote device. See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwmt/html/remote_control.asp
   167 			// for the vendor defined usage page.
   168 
   169 			RAWINPUTDEVICE[] rid = new RAWINPUTDEVICE[3];
   170 
   171 			rid[0].usUsagePage = 0xFFBC;
   172 			rid[0].usUsage = 0x88;
   173 			rid[0].dwFlags = 0;
   174 
   175 			rid[1].usUsagePage = 0x0C;
   176 			rid[1].usUsage = 0x01;
   177 			rid[1].dwFlags = 0;
   178 
   179 			rid[2].usUsagePage = 0x0C;
   180 			rid[2].usUsage = 0x80;
   181 			rid[2].dwFlags = 0;
   182 
   183 			if (!Function.RegisterRawInputDevices(rid,
   184 				(uint) rid.Length,
   185 				(uint) Marshal.SizeOf(rid[0]))
   186 				)
   187 			{
   188 				throw new ApplicationException("Failed to register raw input devices.");
   189 			}
   190 		}
   191 
   192 
   193 		//-------------------------------------------------------------
   194 		// methods
   195 		//-------------------------------------------------------------
   196 
   197 		public void ProcessMessage(Message message)
   198 		{
   199 			int param;
   200 
   201 			switch (message.Msg)
   202 			{
   203 				case WM_KEYDOWN:
   204 					param = message.WParam.ToInt32();
   205 					ProcessKeyDown(param);
   206 					break;
   207 				case WM_APPCOMMAND:
   208 					param = message.LParam.ToInt32();
   209 					ProcessAppCommand(param);
   210 					break;
   211 				case WM_INPUT:
   212 					ProcessInputCommand(ref message);
   213                     message.Result = new IntPtr(0);
   214 					break;
   215 			}
   216 
   217 		}
   218 
   219 
   220 		//-------------------------------------------------------------
   221 		// methods (helpers)
   222 		//-------------------------------------------------------------
   223 
   224 		private void ProcessKeyDown(int param)
   225 		{
   226 			RemoteControlButton rcb = RemoteControlButton.Unknown;
   227 
   228 			switch (param)
   229 			{
   230 				case (int) Keys.Escape:
   231 					rcb = RemoteControlButton.Clear;
   232 					break;
   233 				case (int) Keys.Down:
   234 					rcb = RemoteControlButton.Down;
   235 					break;
   236 				case (int) Keys.Left:
   237 					rcb = RemoteControlButton.Left;
   238 					break;
   239 				case (int) Keys.D0:
   240 					rcb = RemoteControlButton.Digit0;
   241 					break;
   242 				case (int) Keys.D1:
   243 					rcb = RemoteControlButton.Digit1;
   244 					break;
   245 				case (int) Keys.D2:
   246 					rcb = RemoteControlButton.Digit2;
   247 					break;
   248 				case (int) Keys.D3:
   249 					rcb = RemoteControlButton.Digit3;
   250 					break;
   251 				case (int) Keys.D4:
   252 					rcb = RemoteControlButton.Digit4;
   253 					break;
   254 				case (int) Keys.D5:
   255 					rcb = RemoteControlButton.Digit5;
   256 					break;
   257 				case (int) Keys.D6:
   258 					rcb = RemoteControlButton.Digit6;
   259 					break;
   260 				case (int) Keys.D7:
   261 					rcb = RemoteControlButton.Digit7;
   262 					break;
   263 				case (int) Keys.D8:
   264 					rcb = RemoteControlButton.Digit8;
   265 					break;
   266 				case (int) Keys.D9:
   267 					rcb = RemoteControlButton.Digit9;
   268 					break;
   269 				case (int) Keys.Enter:
   270 					rcb = RemoteControlButton.Enter;
   271 					break;
   272 				case (int) Keys.Right:
   273 					rcb = RemoteControlButton.Right;
   274 					break;
   275 				case (int) Keys.Up:
   276 					rcb = RemoteControlButton.Up;
   277 					break;
   278 			}
   279 
   280 			if (this.ButtonPressed != null && rcb != RemoteControlButton.Unknown)
   281 				this.ButtonPressed(this, new RemoteControlEventArgs(rcb, GetDevice(param)));
   282 		}
   283 
   284 
   285 		private void ProcessAppCommand(int param)
   286 		{
   287 			RemoteControlButton rcb = RemoteControlButton.Unknown;
   288 
   289 			int cmd	= (int) (((ushort) (param >> 16)) & ~FAPPCOMMAND_MASK);
   290 
   291 			switch (cmd)
   292 			{
   293 				case APPCOMMAND_BROWSER_BACKWARD:
   294 					rcb = RemoteControlButton.Back;
   295 					break;
   296 				case APPCOMMAND_MEDIA_CHANNEL_DOWN:
   297 					rcb = RemoteControlButton.ChannelDown;
   298 					break;
   299 				case APPCOMMAND_MEDIA_CHANNEL_UP:
   300 					rcb = RemoteControlButton.ChannelUp;
   301 					break;
   302 				case APPCOMMAND_MEDIA_FAST_FORWARD:
   303 					rcb = RemoteControlButton.FastForward;
   304 					break;
   305 				case APPCOMMAND_VOLUME_MUTE:
   306 					rcb = RemoteControlButton.VolumeMute;
   307 					break;
   308 				case APPCOMMAND_MEDIA_PAUSE:
   309 					rcb = RemoteControlButton.Pause;
   310 					break;
   311 				case APPCOMMAND_MEDIA_PLAY:
   312 					rcb = RemoteControlButton.Play;
   313 					break;
   314                 case APPCOMMAND_MEDIA_PLAY_PAUSE:
   315                     rcb = RemoteControlButton.PlayPause;
   316                     break;
   317 				case APPCOMMAND_MEDIA_RECORD:
   318 					rcb = RemoteControlButton.Record;
   319 					break;
   320 				case APPCOMMAND_MEDIA_PREVIOUSTRACK:
   321 					rcb = RemoteControlButton.PreviousTrack;
   322 					break;
   323 				case APPCOMMAND_MEDIA_REWIND:
   324 					rcb = RemoteControlButton.Rewind;
   325 					break;
   326 				case APPCOMMAND_MEDIA_NEXTTRACK:
   327 					rcb = RemoteControlButton.NextTrack;
   328 					break;
   329 				case APPCOMMAND_MEDIA_STOP:
   330 					rcb = RemoteControlButton.Stop;
   331 					break;
   332 				case APPCOMMAND_VOLUME_DOWN:
   333 					rcb = RemoteControlButton.VolumeDown;
   334 					break;
   335 				case APPCOMMAND_VOLUME_UP:
   336 					rcb = RemoteControlButton.VolumeUp;
   337 					break;
   338 			}
   339 
   340 			if (this.ButtonPressed != null && rcb != RemoteControlButton.Unknown)
   341 				this.ButtonPressed(this, new RemoteControlEventArgs(rcb, GetDevice(param)));
   342 		}
   343 
   344 
   345 		private void ProcessInputCommand(ref Message message)
   346 		{
   347             Debug.WriteLine("================WM_INPUT================");
   348 
   349 
   350             //Declare a pointer
   351             IntPtr rawInputBuffer = IntPtr.Zero;
   352 
   353             try
   354             {
   355                 //Fetch raw input
   356                 RAWINPUT raw = new RAWINPUT();
   357                 if (!RawInput.GetRawInputData(message.LParam, ref raw, ref rawInputBuffer))
   358                 {
   359                     return;
   360                 }
   361 
   362                 //Fetch device info
   363                 RID_DEVICE_INFO deviceInfo = new RID_DEVICE_INFO();
   364                 if (!RawInput.GetDeviceInfo(raw.header.hDevice, ref deviceInfo))
   365                 {
   366                     return;
   367                 }
   368 
   369                 //Check type of input device and quite if we don't like it
   370                 switch (deviceInfo.dwType)
   371                 {
   372                     case Const.RIM_TYPEHID:
   373                         Debug.WriteLine("WM_INPUT source device is HID.");
   374                         break;
   375                     case Const.RIM_TYPEMOUSE:
   376                         Debug.WriteLine("WM_INPUT source device is Mouse.");
   377                         return;
   378                     case Const.RIM_TYPEKEYBOARD:
   379                         Debug.WriteLine("WM_INPUT source device is Keyboard.");
   380                         return;
   381                     default:
   382                         Debug.WriteLine("WM_INPUT source device is Unknown.");
   383                         return;
   384                 }
   385 
   386                 //Get Usage Page and Usage
   387                 Debug.WriteLine("Usage Page: 0x" + deviceInfo.hid.usUsagePage.ToString("X4") + " Usage: 0x" + deviceInfo.hid.usUsage.ToString("X4"));
   388 
   389 
   390                 if (raw.header.dwType == Const.RIM_TYPEHID  //Check that our raw input is HID
   391                     && raw.hid.dwSizeHid > 1    //Make sure our HID msg size more than 1. In fact the first ushort is irrelevant to us for now
   392                     && raw.hid.dwCount > 0)     //Check that we have at least one HID msg
   393                 {
   394                     //TODO: for each HID message
   395 
   396                     //Allocate a buffer for one HID message
   397                     byte[] bRawData = new byte[raw.hid.dwSizeHid];
   398 
   399                     //Compute the address from which to copy our HID message
   400                     int pRawData = 0;
   401                     unsafe
   402                     {
   403                         byte* source = (byte*)rawInputBuffer;
   404                         source += sizeof(RAWINPUTHEADER) + sizeof(RAWHID);
   405                         pRawData = (int)source;
   406                     }
   407 
   408                     //Copy HID message into our buffer
   409                     Marshal.Copy(new IntPtr(pRawData), bRawData, 0, raw.hid.dwSizeHid);
   410                     //bRawData[0] //Not sure what's the meaning of the code at offset 0
   411                     //TODO: check size before access
   412                     int rawData = bRawData[1]; //Get button code
   413                     //Print HID codes in our debug output
   414                     string hidDump = "HID " + raw.hid.dwCount + "/" + raw.hid.dwSizeHid + ":";
   415                     foreach (byte b in bRawData)
   416                     {
   417                         hidDump += b.ToString("X2");
   418                     }
   419                     Debug.WriteLine(hidDump);
   420 
   421 
   422                     //Make sure both usage page and usage are matching MCE remote
   423                     if (deviceInfo.hid.usUsagePage != (ushort)Hid.UsagePage.MceRemote || deviceInfo.hid.usUsage != (ushort)Hid.UsageId.MceRemoteUsage)
   424                     {
   425                         Debug.WriteLine("Not MCE remote page and usage.");
   426                         return;
   427                     }
   428 
   429                     if (Enum.IsDefined(typeof(MceButton), rawData) && rawData != 0) //Our button is a known MCE button
   430                     {
   431                         if (this.ButtonPressed != null) //What's that?
   432                         {
   433                             this.ButtonPressed(this, new RemoteControlEventArgs((MceButton)rawData, GetDevice(message.LParam.ToInt32())));
   434                         }
   435                     }
   436                 }
   437                 else if (raw.header.dwType == Const.RIM_TYPEMOUSE)
   438                 {
   439                     // do mouse handling...
   440                 }
   441                 else if (raw.header.dwType == Const.RIM_TYPEKEYBOARD)
   442                 {
   443                     // do keyboard handling...
   444                 }
   445             }
   446             finally
   447             {
   448                 //Always executed when leaving our try block
   449                 Marshal.FreeHGlobal(rawInputBuffer);
   450             }
   451 		}
   452 
   453 
   454 		private InputDevice GetDevice(int param)
   455 		{
   456 			InputDevice inputDevice;
   457 
   458 			switch ((int) (((ushort) (param >> 16)) & FAPPCOMMAND_MASK))
   459 			{
   460 				case FAPPCOMMAND_OEM:
   461 					inputDevice = InputDevice.OEM;
   462 					break;
   463 				case FAPPCOMMAND_MOUSE:
   464 					inputDevice = InputDevice.Mouse;
   465 					break;
   466 				default:
   467 					inputDevice = InputDevice.Key;
   468 					break;
   469 			}
   470 
   471 			return inputDevice;
   472 		}
   473 	}
   474 }