RemoteControlDevice.cs
author sl
Tue, 04 Nov 2014 19:33:40 +0100
changeset 1 4cf25f956ab2
child 2 505a13a09336
permissions -rw-r--r--
Fixing unsafe compile error.
     1 using System;
     2 using System.Windows.Forms;
     3 using System.Runtime.InteropServices;
     4 using System.Diagnostics;
     5 
     6 
     7 namespace BruceThomas.Devices.RemoteControl
     8 {
     9 	public enum InputDevice
    10 	{
    11 		Key,
    12 		Mouse,
    13 		OEM
    14 	}
    15 
    16 
    17 	public enum RemoteControlButton
    18 	{
    19 		Clear,
    20 		Down,
    21 		Left,
    22 		Digit0,
    23 		Digit1,
    24 		Digit2,
    25 		Digit3,
    26 		Digit4,
    27 		Digit5,
    28 		Digit6,
    29 		Digit7,
    30 		Digit8,
    31 		Digit9,
    32 		Enter,
    33 		Right,
    34 		Up,
    35 
    36 		Back,
    37 		ChannelDown,
    38 		ChannelUp,
    39 		FastForward,
    40 		VolumeMute,
    41 		Pause,
    42 		Play,
    43         PlayPause,
    44 		Record,
    45 		PreviousTrack,
    46 		Rewind,
    47 		NextTrack,
    48 		Stop,
    49 		VolumeDown,
    50 		VolumeUp,
    51 
    52 		RecordedTV,
    53 		Guide,
    54 		LiveTV,
    55 		Details,
    56 		DVDMenu,
    57 		DVDAngle,
    58 		DVDAudio,
    59 		DVDSubtitle,
    60 		MyMusic,
    61 		MyPictures,
    62 		MyVideos,
    63 		MyTV,
    64 		OEM1,
    65 		OEM2,
    66 		StandBy,
    67 		TVJump,
    68 
    69 		Unknown
    70 	}
    71 
    72 
    73 	#region RemoteControlEventArgs
    74 
    75 	public class RemoteControlEventArgs : EventArgs
    76 	{
    77 		RemoteControlButton _rcb;
    78 		InputDevice _device;
    79 
    80 		public RemoteControlEventArgs(RemoteControlButton rcb, InputDevice device)
    81 		{
    82 			_rcb = rcb;
    83 			_device = device;
    84 		}
    85 
    86 
    87 		public RemoteControlEventArgs()
    88 		{
    89 			_rcb = RemoteControlButton.Unknown;
    90 			_device = InputDevice.Key;
    91 		}
    92 
    93 
    94 		public RemoteControlButton Button
    95 		{
    96 			get { return _rcb;  }
    97 			set { _rcb = value; }
    98 		}
    99 
   100 		public InputDevice Device
   101 		{
   102 			get { return _device;  }
   103 			set { _device = value; }
   104 		}
   105 	}
   106 
   107 	#endregion RemoteControlEventArgs
   108 
   109 
   110 	public sealed class RemoteControlDevice
   111 	{
   112 
   113         [StructLayout(LayoutKind.Sequential, Pack = 1)]
   114 		internal struct RAWINPUTDEVICE
   115 		{
   116 			[MarshalAs(UnmanagedType.U2)]
   117 			public ushort usUsagePage;
   118 			[MarshalAs(UnmanagedType.U2)]
   119 			public ushort usUsage;
   120 			[MarshalAs(UnmanagedType.U4)]
   121 			public int	 dwFlags;
   122 			public IntPtr hwndTarget;
   123 		}
   124 
   125 
   126         [StructLayout(LayoutKind.Sequential, Pack = 1)]
   127 		internal struct RAWINPUTHEADER
   128 		{
   129 			[MarshalAs(UnmanagedType.U4)]
   130 			public int dwType;
   131 			[MarshalAs(UnmanagedType.U4)]
   132 			public int dwSize;
   133             public IntPtr hDevice;
   134 			[MarshalAs(UnmanagedType.U4)]
   135 			public int wParam;
   136 		}
   137 
   138 
   139         [StructLayout(LayoutKind.Sequential, Pack = 1)]
   140 		internal struct RAWHID
   141 		{
   142 			[MarshalAs(UnmanagedType.U4)]
   143 			public int dwSizeHid;
   144 			[MarshalAs(UnmanagedType.U4)]
   145 			public int dwCount;
   146             //
   147             //BYTE  bRawData[1];
   148 		}
   149 
   150 
   151         [StructLayout(LayoutKind.Sequential, Pack = 1)]
   152 		internal struct BUTTONSSTR
   153 		{
   154 			[MarshalAs(UnmanagedType.U2)]
   155 			public ushort usButtonFlags;
   156 			[MarshalAs(UnmanagedType.U2)]
   157 			public ushort usButtonData;
   158 		}
   159 
   160 
   161         [StructLayout(LayoutKind.Explicit, Pack = 1)]
   162 		internal struct RAWMOUSE
   163 		{
   164 			[MarshalAs(UnmanagedType.U2)]
   165 			[FieldOffset (0)] public ushort usFlags;
   166 			[MarshalAs(UnmanagedType.U4)]
   167 			[FieldOffset (4)] public uint ulButtons;
   168 			[FieldOffset (4)] public BUTTONSSTR buttonsStr;
   169 			[MarshalAs(UnmanagedType.U4)]
   170 			[FieldOffset (8)] public uint ulRawButtons;
   171             [MarshalAs(UnmanagedType.U4)]
   172             [FieldOffset (12)] public int lLastX;
   173             [MarshalAs(UnmanagedType.U4)]
   174             [FieldOffset (16)] public int lLastY;
   175 			[MarshalAs(UnmanagedType.U4)]
   176 			[FieldOffset (20)] public uint ulExtraInformation;
   177 		}
   178 
   179         [StructLayout(LayoutKind.Sequential, Pack = 1)]
   180 		internal struct RAWKEYBOARD
   181 		{
   182 			[MarshalAs(UnmanagedType.U2)]
   183 			public ushort MakeCode;
   184 			[MarshalAs(UnmanagedType.U2)]
   185 			public ushort Flags;
   186 			[MarshalAs(UnmanagedType.U2)]
   187 			public ushort Reserved;
   188 			[MarshalAs(UnmanagedType.U2)]
   189 			public ushort VKey;
   190 			[MarshalAs(UnmanagedType.U4)]
   191 			public uint Message;
   192 			[MarshalAs(UnmanagedType.U4)]
   193 			public uint ExtraInformation;
   194 		}
   195 
   196 
   197 		[StructLayout(LayoutKind.Explicit, Pack=1)]
   198 		internal struct RAWINPUT
   199 		{
   200 			[FieldOffset  (0)] public RAWINPUTHEADER header;
   201 			[FieldOffset (16)] public RAWMOUSE mouse;
   202 			[FieldOffset (16)] public RAWKEYBOARD keyboard;
   203 			[FieldOffset (16)] public RAWHID hid;
   204 		}
   205 
   206 
   207 		[DllImport("User32.dll")]
   208 		extern static bool RegisterRawInputDevices(RAWINPUTDEVICE[] pRawInputDevice, uint uiNumDevices, uint cbSize);
   209 
   210 		[DllImport("User32.dll")]
   211 		extern static uint GetRawInputData(IntPtr hRawInput, uint uiCommand, IntPtr pData, ref uint pcbSize, uint cbSizeHeader);
   212 
   213 
   214 		private const int WM_KEYDOWN	= 0x0100;
   215 		private const int WM_APPCOMMAND	= 0x0319;
   216 		private const int WM_INPUT		= 0x00FF;
   217 
   218 		private const int APPCOMMAND_BROWSER_BACKWARD   = 1;
   219 		private const int APPCOMMAND_VOLUME_MUTE        = 8;
   220 		private const int APPCOMMAND_VOLUME_DOWN        = 9;
   221 		private const int APPCOMMAND_VOLUME_UP          = 10;
   222 		private const int APPCOMMAND_MEDIA_NEXTTRACK    = 11;
   223 		private const int APPCOMMAND_MEDIA_PREVIOUSTRACK = 12;
   224 		private const int APPCOMMAND_MEDIA_STOP         = 13;
   225 		private const int APPCOMMAND_MEDIA_PLAY_PAUSE   = 14;
   226 		private const int APPCOMMAND_MEDIA_PLAY         = 46;
   227 		private const int APPCOMMAND_MEDIA_PAUSE        = 47;
   228 		private const int APPCOMMAND_MEDIA_RECORD       = 48;
   229 		private const int APPCOMMAND_MEDIA_FAST_FORWARD = 49;
   230 		private const int APPCOMMAND_MEDIA_REWIND       = 50;
   231 		private const int APPCOMMAND_MEDIA_CHANNEL_UP   = 51;
   232 		private const int APPCOMMAND_MEDIA_CHANNEL_DOWN = 52;
   233 
   234 		private const int RAWINPUT_DETAILS				= 0x209;
   235 		private const int RAWINPUT_GUIDE				= 0x8D;
   236 		private const int RAWINPUT_TVJUMP				= 0x25;
   237 		private const int RAWINPUT_STANDBY				= 0x82;
   238 		private const int RAWINPUT_OEM1					= 0x80;
   239 		private const int RAWINPUT_OEM2					= 0x81;
   240 		private const int RAWINPUT_MYTV					= 0x46;
   241 		private const int RAWINPUT_MYVIDEOS				= 0x4A;
   242 		private const int RAWINPUT_MYPICTURES			= 0x49;
   243 		private const int RAWINPUT_MYMUSIC				= 0x47;
   244 		private const int RAWINPUT_RECORDEDTV			= 0x48;
   245 		private const int RAWINPUT_DVDANGLE				= 0x4B;
   246 		private const int RAWINPUT_DVDAUDIO				= 0x4C;
   247 		private const int RAWINPUT_DVDMENU				= 0x24;
   248 		private const int RAWINPUT_DVDSUBTITLE			= 0x4D;
   249 
   250 		private const int RIM_TYPEMOUSE					= 0;
   251 		private const int RIM_TYPEKEYBOARD				= 1;
   252 		private const int RIM_TYPEHID					= 2;
   253 
   254 		private const int RID_INPUT						= 0x10000003;
   255 		private const int RID_HEADER					= 0x10000005;
   256 
   257 		private const int FAPPCOMMAND_MASK				= 0xF000;
   258 		private const int FAPPCOMMAND_MOUSE				= 0x8000;
   259 		private const int FAPPCOMMAND_KEY				= 0;
   260 		private const int FAPPCOMMAND_OEM				= 0x1000;
   261 
   262 		public delegate void RemoteControlDeviceEventHandler(object sender, RemoteControlEventArgs e);
   263 		public event RemoteControlDeviceEventHandler ButtonPressed;
   264 
   265 
   266 		//-------------------------------------------------------------
   267 		// constructors
   268 		//-------------------------------------------------------------
   269 
   270 		public RemoteControlDevice()
   271 		{
   272 			// Register the input device to receive the commands from the
   273 			// remote device. See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwmt/html/remote_control.asp
   274 			// for the vendor defined usage page.
   275 
   276 			RAWINPUTDEVICE[] rid = new RAWINPUTDEVICE[3];
   277 
   278 			rid[0].usUsagePage = 0xFFBC;
   279 			rid[0].usUsage = 0x88;
   280 			rid[0].dwFlags = 0;
   281 
   282 			rid[1].usUsagePage = 0x0C;
   283 			rid[1].usUsage = 0x01;
   284 			rid[1].dwFlags = 0;
   285 
   286 			rid[2].usUsagePage = 0x0C;
   287 			rid[2].usUsage = 0x80;
   288 			rid[2].dwFlags = 0;
   289 
   290 			if (!RegisterRawInputDevices(rid,
   291 				(uint) rid.Length,
   292 				(uint) Marshal.SizeOf(rid[0]))
   293 				)
   294 			{
   295 				throw new ApplicationException("Failed to register raw input devices.");
   296 			}
   297 		}
   298 
   299 
   300 		//-------------------------------------------------------------
   301 		// methods
   302 		//-------------------------------------------------------------
   303 
   304 		public void ProcessMessage(Message message)
   305 		{
   306 			int param;
   307 
   308 			switch (message.Msg)
   309 			{
   310 				case WM_KEYDOWN:
   311 					param = message.WParam.ToInt32();
   312 					ProcessKeyDown(param);
   313 					break;
   314 				case WM_APPCOMMAND:
   315 					param = message.LParam.ToInt32();
   316 					ProcessAppCommand(param);
   317 					break;
   318 				case WM_INPUT:
   319 					ProcessInputCommand(ref message);
   320                     message.Result = new IntPtr(0);
   321 					break;
   322 			}
   323 
   324 		}
   325 
   326 
   327 		//-------------------------------------------------------------
   328 		// methods (helpers)
   329 		//-------------------------------------------------------------
   330 
   331 		private void ProcessKeyDown(int param)
   332 		{
   333 			RemoteControlButton rcb = RemoteControlButton.Unknown;
   334 
   335 			switch (param)
   336 			{
   337 				case (int) Keys.Escape:
   338 					rcb = RemoteControlButton.Clear;
   339 					break;
   340 				case (int) Keys.Down:
   341 					rcb = RemoteControlButton.Down;
   342 					break;
   343 				case (int) Keys.Left:
   344 					rcb = RemoteControlButton.Left;
   345 					break;
   346 				case (int) Keys.D0:
   347 					rcb = RemoteControlButton.Digit0;
   348 					break;
   349 				case (int) Keys.D1:
   350 					rcb = RemoteControlButton.Digit1;
   351 					break;
   352 				case (int) Keys.D2:
   353 					rcb = RemoteControlButton.Digit2;
   354 					break;
   355 				case (int) Keys.D3:
   356 					rcb = RemoteControlButton.Digit3;
   357 					break;
   358 				case (int) Keys.D4:
   359 					rcb = RemoteControlButton.Digit4;
   360 					break;
   361 				case (int) Keys.D5:
   362 					rcb = RemoteControlButton.Digit5;
   363 					break;
   364 				case (int) Keys.D6:
   365 					rcb = RemoteControlButton.Digit6;
   366 					break;
   367 				case (int) Keys.D7:
   368 					rcb = RemoteControlButton.Digit7;
   369 					break;
   370 				case (int) Keys.D8:
   371 					rcb = RemoteControlButton.Digit8;
   372 					break;
   373 				case (int) Keys.D9:
   374 					rcb = RemoteControlButton.Digit9;
   375 					break;
   376 				case (int) Keys.Enter:
   377 					rcb = RemoteControlButton.Enter;
   378 					break;
   379 				case (int) Keys.Right:
   380 					rcb = RemoteControlButton.Right;
   381 					break;
   382 				case (int) Keys.Up:
   383 					rcb = RemoteControlButton.Up;
   384 					break;
   385 			}
   386 
   387 			if (this.ButtonPressed != null && rcb != RemoteControlButton.Unknown)
   388 				this.ButtonPressed(this, new RemoteControlEventArgs(rcb, GetDevice(param)));
   389 		}
   390 
   391 
   392 		private void ProcessAppCommand(int param)
   393 		{
   394 			RemoteControlButton rcb = RemoteControlButton.Unknown;
   395 
   396 			int cmd	= (int) (((ushort) (param >> 16)) & ~FAPPCOMMAND_MASK);
   397 
   398 			switch (cmd)
   399 			{
   400 				case APPCOMMAND_BROWSER_BACKWARD:
   401 					rcb = RemoteControlButton.Back;
   402 					break;
   403 				case APPCOMMAND_MEDIA_CHANNEL_DOWN:
   404 					rcb = RemoteControlButton.ChannelDown;
   405 					break;
   406 				case APPCOMMAND_MEDIA_CHANNEL_UP:
   407 					rcb = RemoteControlButton.ChannelUp;
   408 					break;
   409 				case APPCOMMAND_MEDIA_FAST_FORWARD:
   410 					rcb = RemoteControlButton.FastForward;
   411 					break;
   412 				case APPCOMMAND_VOLUME_MUTE:
   413 					rcb = RemoteControlButton.VolumeMute;
   414 					break;
   415 				case APPCOMMAND_MEDIA_PAUSE:
   416 					rcb = RemoteControlButton.Pause;
   417 					break;
   418 				case APPCOMMAND_MEDIA_PLAY:
   419 					rcb = RemoteControlButton.Play;
   420 					break;
   421                 case APPCOMMAND_MEDIA_PLAY_PAUSE:
   422                     rcb = RemoteControlButton.PlayPause;
   423                     break;
   424 				case APPCOMMAND_MEDIA_RECORD:
   425 					rcb = RemoteControlButton.Record;
   426 					break;
   427 				case APPCOMMAND_MEDIA_PREVIOUSTRACK:
   428 					rcb = RemoteControlButton.PreviousTrack;
   429 					break;
   430 				case APPCOMMAND_MEDIA_REWIND:
   431 					rcb = RemoteControlButton.Rewind;
   432 					break;
   433 				case APPCOMMAND_MEDIA_NEXTTRACK:
   434 					rcb = RemoteControlButton.NextTrack;
   435 					break;
   436 				case APPCOMMAND_MEDIA_STOP:
   437 					rcb = RemoteControlButton.Stop;
   438 					break;
   439 				case APPCOMMAND_VOLUME_DOWN:
   440 					rcb = RemoteControlButton.VolumeDown;
   441 					break;
   442 				case APPCOMMAND_VOLUME_UP:
   443 					rcb = RemoteControlButton.VolumeUp;
   444 					break;
   445 			}
   446 
   447 			if (this.ButtonPressed != null && rcb != RemoteControlButton.Unknown)
   448 				this.ButtonPressed(this, new RemoteControlEventArgs(rcb, GetDevice(param)));
   449 		}
   450 
   451 
   452 		private void ProcessInputCommand(ref Message message)
   453 		{
   454 			RemoteControlButton rcb = RemoteControlButton.Unknown;
   455 			uint dwSize = 0;
   456 
   457             uint sizeOfHeader=(uint)Marshal.SizeOf(typeof(RAWINPUTHEADER));
   458 
   459             //Get the size of our raw input data.
   460 			GetRawInputData(message.LParam,	RID_INPUT, IntPtr.Zero,	ref dwSize,	sizeOfHeader);
   461 
   462             //Allocate a large enough buffer
   463 			IntPtr buffer = Marshal.AllocHGlobal((int) dwSize);
   464 			try
   465 			{
   466 				if(buffer == IntPtr.Zero)
   467 					return;
   468 
   469                 //Now read our RAWINPUT data
   470 				if (GetRawInputData(message.LParam,	RID_INPUT, buffer, ref dwSize, (uint) Marshal.SizeOf(typeof(RAWINPUTHEADER))) != dwSize)
   471 				{
   472 					return;
   473 				}
   474 
   475                 //Cast our buffer
   476                 RAWINPUT raw = (RAWINPUT)Marshal.PtrToStructure(buffer, typeof(RAWINPUT));
   477 
   478                 //Check that our raw input is HID
   479                 if (raw.header.dwType == RIM_TYPEHID && raw.hid.dwSizeHid>0)
   480 				{
   481                     //Allocate a buffer for one HID message
   482 					byte[] bRawData = new byte[raw.hid.dwSizeHid];
   483 
   484                     //Compute the address from which to copy our HID message
   485                     int pRawData = 0;
   486                     unsafe
   487                     {
   488                         byte* source = (byte*)buffer;
   489                         source += sizeof(RAWINPUTHEADER) + sizeof(RAWHID);
   490                         //source += 1;
   491                         pRawData = (int)source;
   492                     }
   493                     //int pRawData = buffer.ToUint32() + Marshal.SizeOf(typeof(RAWINPUT)) + 1;
   494 
   495 					//Marshal.Copy(new IntPtr(pRawData), bRawData, 0, raw.hid.dwSizeHid - 1);
   496                     Marshal.Copy(new IntPtr(pRawData), bRawData, 0, raw.hid.dwSizeHid);
   497 					//int rawData = bRawData[0] | bRawData[1] << 8;
   498                     int rawData = bRawData[1]; //Get button code
   499                     Debug.WriteLine("HID " + raw.hid.dwCount + "/" + raw.hid.dwSizeHid + ":" + bRawData[0].ToString("X") + bRawData[1].ToString("X"));
   500 
   501 					switch (rawData)
   502 					{
   503 						case RAWINPUT_DETAILS:
   504 							rcb = RemoteControlButton.Details;
   505 							break;
   506 						case RAWINPUT_GUIDE:
   507 							rcb = RemoteControlButton.Guide;
   508 							break;
   509 						case RAWINPUT_TVJUMP:
   510 							rcb = RemoteControlButton.TVJump;
   511 							break;
   512 						case RAWINPUT_STANDBY:
   513 							rcb = RemoteControlButton.StandBy;
   514 							break;
   515 						case RAWINPUT_OEM1:
   516 							rcb = RemoteControlButton.OEM1;
   517 							break;
   518 						case RAWINPUT_OEM2:
   519 							rcb = RemoteControlButton.OEM2;
   520 							break;
   521 						case RAWINPUT_MYTV:
   522 							rcb = RemoteControlButton.MyTV;
   523 							break;
   524 						case RAWINPUT_MYVIDEOS:
   525 							rcb = RemoteControlButton.MyVideos;
   526 							break;
   527 						case RAWINPUT_MYPICTURES:
   528 							rcb = RemoteControlButton.MyPictures;
   529 							break;
   530 						case RAWINPUT_MYMUSIC:
   531 							rcb = RemoteControlButton.MyMusic;
   532 							break;
   533 						case RAWINPUT_RECORDEDTV:
   534 							rcb = RemoteControlButton.RecordedTV;
   535 							break;
   536 						case RAWINPUT_DVDANGLE:
   537 							rcb = RemoteControlButton.DVDAngle;
   538 							break;
   539 						case RAWINPUT_DVDAUDIO:
   540 							rcb = RemoteControlButton.DVDAudio;
   541 							break;
   542 						case RAWINPUT_DVDMENU:
   543 							rcb = RemoteControlButton.DVDMenu;
   544 							break;
   545 						case RAWINPUT_DVDSUBTITLE:
   546 							rcb = RemoteControlButton.DVDSubtitle;
   547 							break;
   548 					}
   549 
   550 					if (rcb != RemoteControlButton.Unknown && this.ButtonPressed != null)
   551 						this.ButtonPressed(this, new RemoteControlEventArgs(rcb, GetDevice(message.LParam.ToInt32())));
   552 				}
   553 				else if(raw.header.dwType == RIM_TYPEMOUSE)
   554 				{
   555 					// do mouse handling...
   556 				}
   557 				else if(raw.header.dwType == RIM_TYPEKEYBOARD)
   558 				{
   559 					// do keyboard handling...
   560 				}
   561 			}
   562 			finally
   563 			{
   564 				Marshal.FreeHGlobal(buffer);
   565 			}
   566 		}
   567 
   568 
   569 		private InputDevice GetDevice(int param)
   570 		{
   571 			InputDevice inputDevice;
   572 
   573 			switch ((int) (((ushort) (param >> 16)) & FAPPCOMMAND_MASK))
   574 			{
   575 				case FAPPCOMMAND_OEM:
   576 					inputDevice = InputDevice.OEM;
   577 					break;
   578 				case FAPPCOMMAND_MOUSE:
   579 					inputDevice = InputDevice.Mouse;
   580 					break;
   581 				default:
   582 					inputDevice = InputDevice.Key;
   583 					break;
   584 			}
   585 
   586 			return inputDevice;
   587 		}
   588 	}
   589 }