Cast fix and moving Win32 stuff in a dedicated file.
2 using System.Windows.Forms;
3 using System.Runtime.InteropServices;
4 using System.Diagnostics;
8 namespace Devices.RemoteControl
11 public enum InputDevice
19 public enum RemoteControlButton
75 #region RemoteControlEventArgs
77 public class RemoteControlEventArgs : EventArgs
79 RemoteControlButton _rcb;
83 public RemoteControlEventArgs(RemoteControlButton rcb, InputDevice device)
85 iMceButton = MceButton.Null;
90 public RemoteControlEventArgs(MceButton mce, InputDevice device)
93 _rcb = RemoteControlButton.Unknown;
97 public RemoteControlEventArgs()
99 iMceButton = MceButton.Null;
100 _rcb = RemoteControlButton.Unknown;
101 _device = InputDevice.Key;
104 public RemoteControlButton Button
107 set { _rcb = value; }
110 public MceButton MceButton
112 get { return iMceButton; }
113 set { iMceButton = value; }
116 public InputDevice Device
118 get { return _device; }
119 set { _device = value; }
123 #endregion RemoteControlEventArgs
126 public sealed class RemoteControlDevice
128 private const int WM_KEYDOWN = 0x0100;
129 private const int WM_APPCOMMAND = 0x0319;
130 private const int WM_INPUT = 0x00FF;
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;
148 private const int RID_INPUT = 0x10000003;
149 private const int RID_HEADER = 0x10000005;
151 private const int FAPPCOMMAND_MASK = 0xF000;
152 private const int FAPPCOMMAND_MOUSE = 0x8000;
153 private const int FAPPCOMMAND_KEY = 0;
154 private const int FAPPCOMMAND_OEM = 0x1000;
158 public delegate void RemoteControlDeviceEventHandler(object sender, RemoteControlEventArgs e);
159 public event RemoteControlDeviceEventHandler ButtonPressed;
162 //-------------------------------------------------------------
164 //-------------------------------------------------------------
166 public RemoteControlDevice()
168 // Register the input device to receive the commands from the
169 // remote device. See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwmt/html/remote_control.asp
170 // for the vendor defined usage page.
172 RAWINPUTDEVICE[] rid = new RAWINPUTDEVICE[3];
174 rid[0].usUsagePage = 0xFFBC;
175 rid[0].usUsage = 0x88;
178 rid[1].usUsagePage = 0x0C;
179 rid[1].usUsage = 0x01;
182 rid[2].usUsagePage = 0x0C;
183 rid[2].usUsage = 0x80;
186 if (!Function.RegisterRawInputDevices(rid,
188 (uint) Marshal.SizeOf(rid[0]))
191 throw new ApplicationException("Failed to register raw input devices.");
196 //-------------------------------------------------------------
198 //-------------------------------------------------------------
200 public void ProcessMessage(Message message)
207 param = message.WParam.ToInt32();
208 ProcessKeyDown(param);
211 param = message.LParam.ToInt32();
212 ProcessAppCommand(param);
215 ProcessInputCommand(ref message);
216 message.Result = new IntPtr(0);
223 //-------------------------------------------------------------
225 //-------------------------------------------------------------
227 private void ProcessKeyDown(int param)
229 RemoteControlButton rcb = RemoteControlButton.Unknown;
233 case (int) Keys.Escape:
234 rcb = RemoteControlButton.Clear;
236 case (int) Keys.Down:
237 rcb = RemoteControlButton.Down;
239 case (int) Keys.Left:
240 rcb = RemoteControlButton.Left;
243 rcb = RemoteControlButton.Digit0;
246 rcb = RemoteControlButton.Digit1;
249 rcb = RemoteControlButton.Digit2;
252 rcb = RemoteControlButton.Digit3;
255 rcb = RemoteControlButton.Digit4;
258 rcb = RemoteControlButton.Digit5;
261 rcb = RemoteControlButton.Digit6;
264 rcb = RemoteControlButton.Digit7;
267 rcb = RemoteControlButton.Digit8;
270 rcb = RemoteControlButton.Digit9;
272 case (int) Keys.Enter:
273 rcb = RemoteControlButton.Enter;
275 case (int) Keys.Right:
276 rcb = RemoteControlButton.Right;
279 rcb = RemoteControlButton.Up;
283 if (this.ButtonPressed != null && rcb != RemoteControlButton.Unknown)
284 this.ButtonPressed(this, new RemoteControlEventArgs(rcb, GetDevice(param)));
288 private void ProcessAppCommand(int param)
290 RemoteControlButton rcb = RemoteControlButton.Unknown;
292 int cmd = (int) (((ushort) (param >> 16)) & ~FAPPCOMMAND_MASK);
296 case APPCOMMAND_BROWSER_BACKWARD:
297 rcb = RemoteControlButton.Back;
299 case APPCOMMAND_MEDIA_CHANNEL_DOWN:
300 rcb = RemoteControlButton.ChannelDown;
302 case APPCOMMAND_MEDIA_CHANNEL_UP:
303 rcb = RemoteControlButton.ChannelUp;
305 case APPCOMMAND_MEDIA_FAST_FORWARD:
306 rcb = RemoteControlButton.FastForward;
308 case APPCOMMAND_VOLUME_MUTE:
309 rcb = RemoteControlButton.VolumeMute;
311 case APPCOMMAND_MEDIA_PAUSE:
312 rcb = RemoteControlButton.Pause;
314 case APPCOMMAND_MEDIA_PLAY:
315 rcb = RemoteControlButton.Play;
317 case APPCOMMAND_MEDIA_PLAY_PAUSE:
318 rcb = RemoteControlButton.PlayPause;
320 case APPCOMMAND_MEDIA_RECORD:
321 rcb = RemoteControlButton.Record;
323 case APPCOMMAND_MEDIA_PREVIOUSTRACK:
324 rcb = RemoteControlButton.PreviousTrack;
326 case APPCOMMAND_MEDIA_REWIND:
327 rcb = RemoteControlButton.Rewind;
329 case APPCOMMAND_MEDIA_NEXTTRACK:
330 rcb = RemoteControlButton.NextTrack;
332 case APPCOMMAND_MEDIA_STOP:
333 rcb = RemoteControlButton.Stop;
335 case APPCOMMAND_VOLUME_DOWN:
336 rcb = RemoteControlButton.VolumeDown;
338 case APPCOMMAND_VOLUME_UP:
339 rcb = RemoteControlButton.VolumeUp;
343 if (this.ButtonPressed != null && rcb != RemoteControlButton.Unknown)
344 this.ButtonPressed(this, new RemoteControlEventArgs(rcb, GetDevice(param)));
348 private void ProcessInputCommand(ref Message message)
350 Debug.WriteLine("================WM_INPUT================");
354 uint sizeOfHeader=(uint)Marshal.SizeOf(typeof(RAWINPUTHEADER));
356 //Get the size of our raw input data.
357 Win32.Function.GetRawInputData(message.LParam, RID_INPUT, IntPtr.Zero, ref dwSize, sizeOfHeader);
359 //Allocate a large enough buffer
360 IntPtr rawInputBuffer = Marshal.AllocHGlobal((int) dwSize);
363 if(rawInputBuffer == IntPtr.Zero)
366 //Now read our RAWINPUT data
367 if (Win32.Function.GetRawInputData(message.LParam, RID_INPUT, rawInputBuffer, ref dwSize, (uint)Marshal.SizeOf(typeof(RAWINPUTHEADER))) != dwSize)
373 RAWINPUT raw = (RAWINPUT)Marshal.PtrToStructure(rawInputBuffer, typeof(RAWINPUT));
376 uint deviceInfoSize = (uint)Marshal.SizeOf(typeof(RID_DEVICE_INFO));
377 IntPtr deviceInfoBuffer = Marshal.AllocHGlobal((int)deviceInfoSize);
379 int res = Win32.Function.GetRawInputDeviceInfo(raw.header.hDevice, Const.RIDI_DEVICEINFO, deviceInfoBuffer, ref deviceInfoSize);
382 Debug.WriteLine("WM_INPUT could not read device info: " + Marshal.GetLastWin32Error().ToString());
387 RID_DEVICE_INFO deviceInfo = (RID_DEVICE_INFO)Marshal.PtrToStructure(deviceInfoBuffer, typeof(RID_DEVICE_INFO));
389 //Check type of input device and quite if we don't like it
390 switch (deviceInfo.dwType)
392 case Const.RIM_TYPEHID:
393 Debug.WriteLine("WM_INPUT source device is HID.");
395 case Const.RIM_TYPEMOUSE:
396 Debug.WriteLine("WM_INPUT source device is Mouse.");
398 case Const.RIM_TYPEKEYBOARD:
399 Debug.WriteLine("WM_INPUT source device is Keyboard.");
402 Debug.WriteLine("WM_INPUT source device is Unknown.");
406 //Get Usage Page and Usage
407 Debug.WriteLine("Usage Page: 0x" + deviceInfo.hid.usUsagePage.ToString("X4") + " Usage: 0x" + deviceInfo.hid.usUsage.ToString("X4"));
409 //Check that our raw input is HID
410 if (raw.header.dwType == Const.RIM_TYPEHID && raw.hid.dwSizeHid>0)
412 //Allocate a buffer for one HID message
413 byte[] bRawData = new byte[raw.hid.dwSizeHid];
415 //Compute the address from which to copy our HID message
419 byte* source = (byte*)rawInputBuffer;
420 source += sizeof(RAWINPUTHEADER) + sizeof(RAWHID);
421 pRawData = (int)source;
424 //Copy HID message into our buffer
425 Marshal.Copy(new IntPtr(pRawData), bRawData, 0, raw.hid.dwSizeHid);
426 //bRawData[0] //Not sure what's the meaning of the code at offset 0
427 //TODO: check size before access
428 int rawData = bRawData[1]; //Get button code
429 //Print HID codes in our debug output
430 string hidDump = "HID " + raw.hid.dwCount + "/" + raw.hid.dwSizeHid + ":";
431 foreach (byte b in bRawData)
433 hidDump += b.ToString("X2");
435 Debug.WriteLine(hidDump);
437 //Make sure both usage page and usage are matching MCE remote
438 if (deviceInfo.hid.usUsagePage != (ushort)Hid.UsagePage.MceRemote || deviceInfo.hid.usUsage != (ushort)Hid.UsageId.MceRemoteUsage)
440 Debug.WriteLine("Not MCE remote page and usage.");
444 if (Enum.IsDefined(typeof(MceButton), rawData) && rawData!=0) //Our button is a known MCE button
446 if (this.ButtonPressed != null) //What's that?
448 this.ButtonPressed(this, new RemoteControlEventArgs((MceButton)rawData, GetDevice(message.LParam.ToInt32())));
452 else if(raw.header.dwType == Const.RIM_TYPEMOUSE)
454 // do mouse handling...
456 else if(raw.header.dwType == Const.RIM_TYPEKEYBOARD)
458 // do keyboard handling...
463 Marshal.FreeHGlobal(rawInputBuffer);
468 private InputDevice GetDevice(int param)
470 InputDevice inputDevice;
472 switch ((int) (((ushort) (param >> 16)) & FAPPCOMMAND_MASK))
474 case FAPPCOMMAND_OEM:
475 inputDevice = InputDevice.OEM;
477 case FAPPCOMMAND_MOUSE:
478 inputDevice = InputDevice.Mouse;
481 inputDevice = InputDevice.Key;