More cleanup and re-org.
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 FAPPCOMMAND_MASK = 0xF000;
149 private const int FAPPCOMMAND_MOUSE = 0x8000;
150 private const int FAPPCOMMAND_KEY = 0;
151 private const int FAPPCOMMAND_OEM = 0x1000;
155 public delegate void RemoteControlDeviceEventHandler(object sender, RemoteControlEventArgs e);
156 public event RemoteControlDeviceEventHandler ButtonPressed;
159 //-------------------------------------------------------------
161 //-------------------------------------------------------------
163 public RemoteControlDevice()
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.
169 RAWINPUTDEVICE[] rid = new RAWINPUTDEVICE[3];
171 rid[0].usUsagePage = 0xFFBC;
172 rid[0].usUsage = 0x88;
175 rid[1].usUsagePage = 0x0C;
176 rid[1].usUsage = 0x01;
179 rid[2].usUsagePage = 0x0C;
180 rid[2].usUsage = 0x80;
183 if (!Function.RegisterRawInputDevices(rid,
185 (uint) Marshal.SizeOf(rid[0]))
188 throw new ApplicationException("Failed to register raw input devices.");
193 //-------------------------------------------------------------
195 //-------------------------------------------------------------
197 public void ProcessMessage(Message message)
204 param = message.WParam.ToInt32();
205 ProcessKeyDown(param);
208 param = message.LParam.ToInt32();
209 ProcessAppCommand(param);
212 ProcessInputCommand(ref message);
213 message.Result = new IntPtr(0);
220 //-------------------------------------------------------------
222 //-------------------------------------------------------------
224 private void ProcessKeyDown(int param)
226 RemoteControlButton rcb = RemoteControlButton.Unknown;
230 case (int) Keys.Escape:
231 rcb = RemoteControlButton.Clear;
233 case (int) Keys.Down:
234 rcb = RemoteControlButton.Down;
236 case (int) Keys.Left:
237 rcb = RemoteControlButton.Left;
240 rcb = RemoteControlButton.Digit0;
243 rcb = RemoteControlButton.Digit1;
246 rcb = RemoteControlButton.Digit2;
249 rcb = RemoteControlButton.Digit3;
252 rcb = RemoteControlButton.Digit4;
255 rcb = RemoteControlButton.Digit5;
258 rcb = RemoteControlButton.Digit6;
261 rcb = RemoteControlButton.Digit7;
264 rcb = RemoteControlButton.Digit8;
267 rcb = RemoteControlButton.Digit9;
269 case (int) Keys.Enter:
270 rcb = RemoteControlButton.Enter;
272 case (int) Keys.Right:
273 rcb = RemoteControlButton.Right;
276 rcb = RemoteControlButton.Up;
280 if (this.ButtonPressed != null && rcb != RemoteControlButton.Unknown)
281 this.ButtonPressed(this, new RemoteControlEventArgs(rcb, GetDevice(param)));
285 private void ProcessAppCommand(int param)
287 RemoteControlButton rcb = RemoteControlButton.Unknown;
289 int cmd = (int) (((ushort) (param >> 16)) & ~FAPPCOMMAND_MASK);
293 case APPCOMMAND_BROWSER_BACKWARD:
294 rcb = RemoteControlButton.Back;
296 case APPCOMMAND_MEDIA_CHANNEL_DOWN:
297 rcb = RemoteControlButton.ChannelDown;
299 case APPCOMMAND_MEDIA_CHANNEL_UP:
300 rcb = RemoteControlButton.ChannelUp;
302 case APPCOMMAND_MEDIA_FAST_FORWARD:
303 rcb = RemoteControlButton.FastForward;
305 case APPCOMMAND_VOLUME_MUTE:
306 rcb = RemoteControlButton.VolumeMute;
308 case APPCOMMAND_MEDIA_PAUSE:
309 rcb = RemoteControlButton.Pause;
311 case APPCOMMAND_MEDIA_PLAY:
312 rcb = RemoteControlButton.Play;
314 case APPCOMMAND_MEDIA_PLAY_PAUSE:
315 rcb = RemoteControlButton.PlayPause;
317 case APPCOMMAND_MEDIA_RECORD:
318 rcb = RemoteControlButton.Record;
320 case APPCOMMAND_MEDIA_PREVIOUSTRACK:
321 rcb = RemoteControlButton.PreviousTrack;
323 case APPCOMMAND_MEDIA_REWIND:
324 rcb = RemoteControlButton.Rewind;
326 case APPCOMMAND_MEDIA_NEXTTRACK:
327 rcb = RemoteControlButton.NextTrack;
329 case APPCOMMAND_MEDIA_STOP:
330 rcb = RemoteControlButton.Stop;
332 case APPCOMMAND_VOLUME_DOWN:
333 rcb = RemoteControlButton.VolumeDown;
335 case APPCOMMAND_VOLUME_UP:
336 rcb = RemoteControlButton.VolumeUp;
340 if (this.ButtonPressed != null && rcb != RemoteControlButton.Unknown)
341 this.ButtonPressed(this, new RemoteControlEventArgs(rcb, GetDevice(param)));
345 private void ProcessInputCommand(ref Message message)
347 Debug.WriteLine("================WM_INPUT================");
351 IntPtr rawInputBuffer = IntPtr.Zero;
356 RAWINPUT raw = new RAWINPUT();
357 if (!RawInput.GetRawInputData(message.LParam, ref raw, ref rawInputBuffer))
363 RID_DEVICE_INFO deviceInfo = new RID_DEVICE_INFO();
364 if (!RawInput.GetDeviceInfo(raw.header.hDevice, ref deviceInfo))
369 //Check type of input device and quite if we don't like it
370 switch (deviceInfo.dwType)
372 case Const.RIM_TYPEHID:
373 Debug.WriteLine("WM_INPUT source device is HID.");
375 case Const.RIM_TYPEMOUSE:
376 Debug.WriteLine("WM_INPUT source device is Mouse.");
378 case Const.RIM_TYPEKEYBOARD:
379 Debug.WriteLine("WM_INPUT source device is Keyboard.");
382 Debug.WriteLine("WM_INPUT source device is Unknown.");
386 //Get Usage Page and Usage
387 Debug.WriteLine("Usage Page: 0x" + deviceInfo.hid.usUsagePage.ToString("X4") + " Usage: 0x" + deviceInfo.hid.usUsage.ToString("X4"));
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
394 //TODO: for each HID message
396 //Allocate a buffer for one HID message
397 byte[] bRawData = new byte[raw.hid.dwSizeHid];
399 //Compute the address from which to copy our HID message
403 byte* source = (byte*)rawInputBuffer;
404 source += sizeof(RAWINPUTHEADER) + sizeof(RAWHID);
405 pRawData = (int)source;
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)
417 hidDump += b.ToString("X2");
419 Debug.WriteLine(hidDump);
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)
425 Debug.WriteLine("Not MCE remote page and usage.");
429 if (Enum.IsDefined(typeof(MceButton), rawData) && rawData != 0) //Our button is a known MCE button
431 if (this.ButtonPressed != null) //What's that?
433 this.ButtonPressed(this, new RemoteControlEventArgs((MceButton)rawData, GetDevice(message.LParam.ToInt32())));
437 else if (raw.header.dwType == Const.RIM_TYPEMOUSE)
439 // do mouse handling...
441 else if (raw.header.dwType == Const.RIM_TYPEKEYBOARD)
443 // do keyboard handling...
448 //Always executed when leaving our try block
449 Marshal.FreeHGlobal(rawInputBuffer);
454 private InputDevice GetDevice(int param)
456 InputDevice inputDevice;
458 switch ((int) (((ushort) (param >> 16)) & FAPPCOMMAND_MASK))
460 case FAPPCOMMAND_OEM:
461 inputDevice = InputDevice.OEM;
463 case FAPPCOMMAND_MOUSE:
464 inputDevice = InputDevice.Mouse;
467 inputDevice = InputDevice.Key;