RemoteControlDevice.cs
changeset 6 fa97ff5ceb7b
parent 5 b2ed6fc37d95
child 7 5ecebf1bd397
     1.1 --- a/RemoteControlDevice.cs	Wed Nov 05 10:19:02 2014 +0100
     1.2 +++ b/RemoteControlDevice.cs	Wed Nov 05 19:39:59 2014 +0100
     1.3 @@ -4,8 +4,59 @@
     1.4  using System.Diagnostics;
     1.5  
     1.6  
     1.7 -namespace BruceThomas.Devices.RemoteControl
     1.8 +namespace Devices.RemoteControl
     1.9  {
    1.10 +
    1.11 +    public static class Hid
    1.12 +    {
    1.13 +        /// <summary>
    1.14 +        /// From USB HID usage tables.
    1.15 +        /// http://www.usb.org/developers/hidpage#HID_Usage
    1.16 +        /// http://www.usb.org/developers/devclass_docs/Hut1_12v2.pdf
    1.17 +        /// </summary>
    1.18 +        public enum UsagePage : ushort
    1.19 +        {
    1.20 +            Undefined = 0,
    1.21 +            GenericDesktopControl,
    1.22 +            SimulationControl,
    1.23 +            VirtualRealityControl,
    1.24 +            SportControl,
    1.25 +            GameControl,
    1.26 +            GenericDeviceControl,
    1.27 +            Keyboard,
    1.28 +            LightEmittingDiode,
    1.29 +            Button,
    1.30 +            Ordinal,
    1.31 +            Telephony,
    1.32 +            Consumer,
    1.33 +            Digitiser,
    1.34 +            PhysicalInterfaceDevice = 0x0f,
    1.35 +            Unicode = 0x10,
    1.36 +            AlphaNumericDisplay = 0x14,
    1.37 +            MedicalInstruments = 0x40,
    1.38 +            MonitorPage0 = 0x80,
    1.39 +            MonitorPage1,
    1.40 +            MonitorPage2,
    1.41 +            MonitorPage3,
    1.42 +            PowerPage0,
    1.43 +            PowerPage1,
    1.44 +            PowerPage2,
    1.45 +            PowerPage3,
    1.46 +            BarCodeScanner = 0x8c,
    1.47 +            Scale,
    1.48 +            MagneticStripeReader,
    1.49 +            ReservedPointOfSale,
    1.50 +            CameraControl,
    1.51 +            Arcade,
    1.52 +            // http://msdn.microsoft.com/en-us/library/windows/desktop/bb417079.aspx
    1.53 +            MceRemote = 0xffbc,
    1.54 +            TerraTecRemote = 0xffcc
    1.55 +        }
    1.56 +
    1.57 +        public const ushort MceRemoteUsage = 0x88;
    1.58 +    }
    1.59 +
    1.60 +
    1.61  	public enum InputDevice
    1.62  	{
    1.63  		Key,
    1.64 @@ -70,14 +121,14 @@
    1.65  	}
    1.66  
    1.67      /// <summary>
    1.68 -    /// 
    1.69 +    ///
    1.70      /// </summary>
    1.71      public enum MceButton
    1.72      {
    1.73          /// <summary>
    1.74          /// Not defined by the Microsoft specs.
    1.75          /// </summary>
    1.76 -        Null                    =   0x00, 
    1.77 +        Null                    =   0x00,
    1.78          GreenStart              =   0x0D,
    1.79          ClosedCaptioning        =   0x2B,
    1.80          Teletext                =   0x5A,
    1.81 @@ -193,7 +244,7 @@
    1.82          NetworkSelection        =   0x2C,
    1.83          BlueRayTool             =   0x78,
    1.84          ChannelInfo             =   0x41,
    1.85 -        VideoSelection          =   0x61                
    1.86 +        VideoSelection          =   0x61
    1.87      }
    1.88  
    1.89      public enum HpMceButton
    1.90 @@ -395,14 +446,62 @@
    1.91  		}
    1.92  
    1.93  
    1.94 +        [StructLayout(LayoutKind.Sequential, Pack=1)]
    1.95 +        internal struct RID_DEVICE_INFO_MOUSE
    1.96 +		{
    1.97 +            public uint dwId;
    1.98 +            public uint dwNumberOfButtons;
    1.99 +            public uint dwSampleRate;
   1.100 +            public bool fHasHorizontalWheel;
   1.101 +		}
   1.102 +
   1.103 +
   1.104 +        [StructLayout(LayoutKind.Sequential, Pack=1)]
   1.105 +        internal struct RID_DEVICE_INFO_KEYBOARD
   1.106 +		{
   1.107 +            public uint dwType;
   1.108 +            public uint dwSubType;
   1.109 +            public uint dwKeyboardMode;
   1.110 +            public uint dwNumberOfFunctionKeys;
   1.111 +            public uint dwNumberOfIndicators;
   1.112 +            public uint dwNumberOfKeysTotal;
   1.113 +        }
   1.114 +
   1.115 +        [StructLayout(LayoutKind.Sequential, Pack=1)]
   1.116 +        internal struct RID_DEVICE_INFO_HID
   1.117 +		{
   1.118 +            public uint dwVendorId;
   1.119 +            public uint dwProductId;
   1.120 +            public uint dwVersionNumber;
   1.121 +            public ushort usUsagePage;
   1.122 +            public ushort usUsage;
   1.123 +        }
   1.124 +
   1.125 +        [StructLayout(LayoutKind.Explicit, Pack=1)]
   1.126 +        internal struct RID_DEVICE_INFO
   1.127 +		{
   1.128 +            [FieldOffset(0)]
   1.129 +            public uint cbSize;
   1.130 +            [FieldOffset(4)]
   1.131 +            public uint dwType;
   1.132 +            [FieldOffset(8)]
   1.133 +            public RID_DEVICE_INFO_MOUSE mouse;
   1.134 +            [FieldOffset(8)]
   1.135 +            public RID_DEVICE_INFO_KEYBOARD keyboard;
   1.136 +            [FieldOffset(8)]
   1.137 +            public RID_DEVICE_INFO_HID hid;
   1.138 +		}
   1.139 +
   1.140 +
   1.141 +
   1.142  		[DllImport("User32.dll")]
   1.143  		extern static bool RegisterRawInputDevices(RAWINPUTDEVICE[] pRawInputDevice, uint uiNumDevices, uint cbSize);
   1.144  
   1.145  		[DllImport("User32.dll")]
   1.146  		extern static uint GetRawInputData(IntPtr hRawInput, uint uiCommand, IntPtr pData, ref uint pcbSize, uint cbSizeHeader);
   1.147  
   1.148 -   		[DllImport("User32.dll")]
   1.149 -		extern static uint GetRawInputDeviceInfo(IntPtr hDevice, uint uiCommand, IntPtr pData, ref uint pcbSize);
   1.150 +   		[DllImport("User32.dll", SetLastError=true)]
   1.151 +		extern static int GetRawInputDeviceInfo(IntPtr hDevice, uint uiCommand, IntPtr pData, ref uint pcbSize);
   1.152  
   1.153  
   1.154  		private const int WM_KEYDOWN	= 0x0100;
   1.155 @@ -425,10 +524,6 @@
   1.156  		private const int APPCOMMAND_MEDIA_CHANNEL_UP   = 51;
   1.157  		private const int APPCOMMAND_MEDIA_CHANNEL_DOWN = 52;
   1.158  
   1.159 -		private const int RIM_TYPEMOUSE					= 0;
   1.160 -		private const int RIM_TYPEKEYBOARD				= 1;
   1.161 -		private const int RIM_TYPEHID					= 2;
   1.162 -
   1.163  		private const int RID_INPUT						= 0x10000003;
   1.164  		private const int RID_HEADER					= 0x10000005;
   1.165  
   1.166 @@ -437,6 +532,37 @@
   1.167  		private const int FAPPCOMMAND_KEY				= 0;
   1.168  		private const int FAPPCOMMAND_OEM				= 0x1000;
   1.169  
   1.170 +        /// <summary>
   1.171 +        /// GetRawInputDeviceInfo pData points to a string that contains the device name.
   1.172 +        /// </summary>
   1.173 +        public const uint RIDI_DEVICENAME = 0x20000007;
   1.174 +        /// <summary>
   1.175 +        /// GetRawInputDeviceInfo For this uiCommand only, the value in pcbSize is the character count (not the byte count).
   1.176 +        /// </summary>
   1.177 +        public const uint RIDI_DEVICEINFO = 0x2000000b;
   1.178 +        /// <summary>
   1.179 +        /// GetRawInputDeviceInfo pData points to an RID_DEVICE_INFO structure.
   1.180 +        /// </summary>
   1.181 +        public const uint RIDI_PREPARSEDDATA = 0x20000005;
   1.182 +
   1.183 +
   1.184 +        /// <summary>
   1.185 +        /// Data comes from a mouse.
   1.186 +        /// </summary>
   1.187 +        public const uint RIM_TYPEMOUSE = 0;
   1.188 +        /// <summary>
   1.189 +        /// Data comes from a keyboard.
   1.190 +        /// </summary>
   1.191 +        public const uint RIM_TYPEKEYBOARD = 1;
   1.192 +        /// <summary>
   1.193 +        /// Data comes from an HID that is not a keyboard or a mouse.
   1.194 +        /// </summary>
   1.195 +        public const uint RIM_TYPEHID = 2;
   1.196 +
   1.197 +
   1.198 +
   1.199 +
   1.200 +
   1.201  		public delegate void RemoteControlDeviceEventHandler(object sender, RemoteControlEventArgs e);
   1.202  		public event RemoteControlDeviceEventHandler ButtonPressed;
   1.203  
   1.204 @@ -629,7 +755,9 @@
   1.205  
   1.206  		private void ProcessInputCommand(ref Message message)
   1.207  		{
   1.208 -			RemoteControlButton rcb = RemoteControlButton.Unknown;
   1.209 +
   1.210 +
   1.211 +
   1.212  			uint dwSize = 0;
   1.213  
   1.214              uint sizeOfHeader=(uint)Marshal.SizeOf(typeof(RAWINPUTHEADER));
   1.215 @@ -638,20 +766,54 @@
   1.216  			GetRawInputData(message.LParam,	RID_INPUT, IntPtr.Zero,	ref dwSize,	sizeOfHeader);
   1.217  
   1.218              //Allocate a large enough buffer
   1.219 -			IntPtr buffer = Marshal.AllocHGlobal((int) dwSize);
   1.220 +			IntPtr rawInputBuffer = Marshal.AllocHGlobal((int) dwSize);
   1.221  			try
   1.222  			{
   1.223 -				if(buffer == IntPtr.Zero)
   1.224 +				if(rawInputBuffer == IntPtr.Zero)
   1.225  					return;
   1.226  
   1.227                  //Now read our RAWINPUT data
   1.228 -				if (GetRawInputData(message.LParam,	RID_INPUT, buffer, ref dwSize, (uint) Marshal.SizeOf(typeof(RAWINPUTHEADER))) != dwSize)
   1.229 +				if (GetRawInputData(message.LParam,	RID_INPUT, rawInputBuffer, ref dwSize, (uint) Marshal.SizeOf(typeof(RAWINPUTHEADER))) != dwSize)
   1.230  				{
   1.231  					return;
   1.232  				}
   1.233  
   1.234                  //Cast our buffer
   1.235 -                RAWINPUT raw = (RAWINPUT)Marshal.PtrToStructure(buffer, typeof(RAWINPUT));
   1.236 +                RAWINPUT raw = (RAWINPUT)Marshal.PtrToStructure(rawInputBuffer, typeof(RAWINPUT));
   1.237 +
   1.238 +                //Get Device Info
   1.239 +                uint deviceInfoSize = (uint)Marshal.SizeOf(typeof(RID_DEVICE_INFO));
   1.240 +                IntPtr deviceInfoBuffer = Marshal.AllocHGlobal((int)deviceInfoSize);
   1.241 +
   1.242 +                int res = GetRawInputDeviceInfo(raw.header.hDevice, RIDI_DEVICEINFO, deviceInfoBuffer, ref deviceInfoSize);
   1.243 +                if (res <= 0)
   1.244 +                {
   1.245 +                    Debug.WriteLine("WM_INPUT could not read device info: " + Marshal.GetLastWin32Error().ToString());
   1.246 +                    return;
   1.247 +                }
   1.248 +
   1.249 +                //Cast our buffer
   1.250 +                RID_DEVICE_INFO deviceInfo = (RID_DEVICE_INFO)Marshal.PtrToStructure(deviceInfoBuffer, typeof(RID_DEVICE_INFO));
   1.251 +
   1.252 +                //Check type of input device and quite if we don't like it
   1.253 +                switch (deviceInfo.dwType)
   1.254 +                {
   1.255 +                    case RIM_TYPEHID:
   1.256 +                        Debug.WriteLine("WM_INPUT source device is HID.");
   1.257 +                        break;
   1.258 +                    case RIM_TYPEMOUSE:
   1.259 +                        Debug.WriteLine("WM_INPUT source device is Mouse.");
   1.260 +                        return;
   1.261 +                    case RIM_TYPEKEYBOARD:
   1.262 +                        Debug.WriteLine("WM_INPUT source device is Keyboard.");
   1.263 +                        return;
   1.264 +                    default:
   1.265 +                        Debug.WriteLine("WM_INPUT source device is Unknown.");
   1.266 +                        return;
   1.267 +                }
   1.268 +
   1.269 +                //Get Usage Page and Usage
   1.270 +                Debug.WriteLine("Usage Page: 0x" + deviceInfo.hid.usUsagePage.ToString("X4") + " Usage: 0x" + deviceInfo.hid.usUsage.ToString("X4"));
   1.271  
   1.272                  //Check that our raw input is HID
   1.273                  if (raw.header.dwType == RIM_TYPEHID && raw.hid.dwSizeHid>0)
   1.274 @@ -663,7 +825,7 @@
   1.275                      int pRawData = 0;
   1.276                      unsafe
   1.277                      {
   1.278 -                        byte* source = (byte*)buffer;
   1.279 +                        byte* source = (byte*)rawInputBuffer;
   1.280                          source += sizeof(RAWINPUTHEADER) + sizeof(RAWHID);
   1.281                          pRawData = (int)source;
   1.282                      }
   1.283 @@ -674,7 +836,19 @@
   1.284                      //TODO: check size before access
   1.285                      int rawData = bRawData[1]; //Get button code
   1.286                      //Print HID codes in our debug output
   1.287 -                    Debug.WriteLine("HID " + raw.hid.dwCount + "/" + raw.hid.dwSizeHid + ":" + bRawData[0].ToString("X2") + bRawData[1].ToString("X2"));
   1.288 +                    string hidDump = "HID " + raw.hid.dwCount + "/" + raw.hid.dwSizeHid + ":";
   1.289 +                    foreach (byte b in bRawData)
   1.290 +                    {
   1.291 +                        hidDump += b.ToString("X2");
   1.292 +                    }
   1.293 +                    Debug.WriteLine(hidDump);
   1.294 +
   1.295 +                    //Make sure both usage page and usage are matching MCE remote
   1.296 +                    if (deviceInfo.hid.usUsagePage != (ushort)Hid.UsagePage.MceRemote || deviceInfo.hid.usUsage != (ushort)Hid.MceRemoteUsage)
   1.297 +                    {
   1.298 +                        Debug.WriteLine("Not MCE remote page and usage.");
   1.299 +                        return;
   1.300 +                    }
   1.301  
   1.302                      if (Enum.IsDefined(typeof(MceButton), rawData) && rawData!=0) //Our button is a known MCE button
   1.303                      {
   1.304 @@ -695,7 +869,7 @@
   1.305  			}
   1.306  			finally
   1.307  			{
   1.308 -				Marshal.FreeHGlobal(buffer);
   1.309 +				Marshal.FreeHGlobal(rawInputBuffer);
   1.310  			}
   1.311  		}
   1.312