sl@0: using System; sl@0: using System.Windows.Forms; sl@0: using System.Runtime.InteropServices; sl@0: using System.Diagnostics; sl@8: using Hid.UsageTables; sl@9: using Win32; sl@0: sl@6: namespace Devices.RemoteControl sl@0: { sl@6: sl@0: public enum InputDevice sl@0: { sl@0: Key, sl@0: Mouse, sl@0: OEM sl@0: } sl@0: sl@0: sl@0: public enum RemoteControlButton sl@0: { sl@0: Clear, sl@0: Down, sl@0: Left, sl@0: Digit0, sl@0: Digit1, sl@0: Digit2, sl@0: Digit3, sl@0: Digit4, sl@0: Digit5, sl@0: Digit6, sl@0: Digit7, sl@0: Digit8, sl@0: Digit9, sl@0: Enter, sl@0: Right, sl@0: Up, sl@0: sl@0: Back, sl@0: ChannelDown, sl@0: ChannelUp, sl@0: FastForward, sl@0: VolumeMute, sl@0: Pause, sl@0: Play, sl@0: PlayPause, sl@0: Record, sl@0: PreviousTrack, sl@0: Rewind, sl@0: NextTrack, sl@0: Stop, sl@0: VolumeDown, sl@0: VolumeUp, sl@0: sl@0: RecordedTV, sl@0: Guide, sl@0: LiveTV, sl@0: Details, sl@0: DVDMenu, sl@0: DVDAngle, sl@0: DVDAudio, sl@0: DVDSubtitle, sl@0: MyMusic, sl@0: MyPictures, sl@0: MyVideos, sl@0: MyTV, sl@0: OEM1, sl@0: OEM2, sl@0: StandBy, sl@0: TVJump, sl@0: sl@0: Unknown sl@0: } sl@0: sl@0: sl@0: #region RemoteControlEventArgs sl@0: sl@0: public class RemoteControlEventArgs : EventArgs sl@0: { sl@3: RemoteControlButton _rcb; sl@0: InputDevice _device; sl@3: MceButton iMceButton; sl@0: sl@3: public RemoteControlEventArgs(RemoteControlButton rcb, InputDevice device) sl@0: { sl@3: iMceButton = MceButton.Null; sl@0: _rcb = rcb; sl@0: _device = device; sl@0: } sl@0: sl@3: public RemoteControlEventArgs(MceButton mce, InputDevice device) sl@3: { sl@3: iMceButton = mce; sl@3: _rcb = RemoteControlButton.Unknown; sl@3: _device = device; sl@3: } sl@0: sl@0: public RemoteControlEventArgs() sl@0: { sl@3: iMceButton = MceButton.Null; sl@0: _rcb = RemoteControlButton.Unknown; sl@0: _device = InputDevice.Key; sl@0: } sl@0: sl@0: public RemoteControlButton Button sl@0: { sl@0: get { return _rcb; } sl@0: set { _rcb = value; } sl@0: } sl@0: sl@3: public MceButton MceButton sl@3: { sl@3: get { return iMceButton; } sl@3: set { iMceButton = value; } sl@3: } sl@3: sl@0: public InputDevice Device sl@0: { sl@0: get { return _device; } sl@0: set { _device = value; } sl@0: } sl@0: } sl@0: sl@0: #endregion RemoteControlEventArgs sl@0: sl@0: sl@0: public sealed class RemoteControlDevice sl@0: { sl@0: private const int WM_KEYDOWN = 0x0100; sl@0: private const int WM_APPCOMMAND = 0x0319; sl@0: private const int WM_INPUT = 0x00FF; sl@0: sl@0: private const int APPCOMMAND_BROWSER_BACKWARD = 1; sl@0: private const int APPCOMMAND_VOLUME_MUTE = 8; sl@0: private const int APPCOMMAND_VOLUME_DOWN = 9; sl@0: private const int APPCOMMAND_VOLUME_UP = 10; sl@0: private const int APPCOMMAND_MEDIA_NEXTTRACK = 11; sl@0: private const int APPCOMMAND_MEDIA_PREVIOUSTRACK = 12; sl@0: private const int APPCOMMAND_MEDIA_STOP = 13; sl@0: private const int APPCOMMAND_MEDIA_PLAY_PAUSE = 14; sl@0: private const int APPCOMMAND_MEDIA_PLAY = 46; sl@0: private const int APPCOMMAND_MEDIA_PAUSE = 47; sl@0: private const int APPCOMMAND_MEDIA_RECORD = 48; sl@0: private const int APPCOMMAND_MEDIA_FAST_FORWARD = 49; sl@0: private const int APPCOMMAND_MEDIA_REWIND = 50; sl@0: private const int APPCOMMAND_MEDIA_CHANNEL_UP = 51; sl@0: private const int APPCOMMAND_MEDIA_CHANNEL_DOWN = 52; sl@0: sl@0: private const int FAPPCOMMAND_MASK = 0xF000; sl@0: private const int FAPPCOMMAND_MOUSE = 0x8000; sl@0: private const int FAPPCOMMAND_KEY = 0; sl@0: private const int FAPPCOMMAND_OEM = 0x1000; sl@0: sl@6: sl@6: sl@0: public delegate void RemoteControlDeviceEventHandler(object sender, RemoteControlEventArgs e); sl@0: public event RemoteControlDeviceEventHandler ButtonPressed; sl@0: sl@0: sl@0: //------------------------------------------------------------- sl@0: // constructors sl@0: //------------------------------------------------------------- sl@0: sl@0: public RemoteControlDevice() sl@0: { sl@0: // Register the input device to receive the commands from the sl@0: // remote device. See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwmt/html/remote_control.asp sl@0: // for the vendor defined usage page. sl@0: sl@0: RAWINPUTDEVICE[] rid = new RAWINPUTDEVICE[3]; sl@0: sl@0: rid[0].usUsagePage = 0xFFBC; sl@0: rid[0].usUsage = 0x88; sl@0: rid[0].dwFlags = 0; sl@0: sl@0: rid[1].usUsagePage = 0x0C; sl@0: rid[1].usUsage = 0x01; sl@0: rid[1].dwFlags = 0; sl@0: sl@0: rid[2].usUsagePage = 0x0C; sl@0: rid[2].usUsage = 0x80; sl@0: rid[2].dwFlags = 0; sl@0: sl@9: if (!Function.RegisterRawInputDevices(rid, sl@0: (uint) rid.Length, sl@0: (uint) Marshal.SizeOf(rid[0])) sl@0: ) sl@0: { sl@0: throw new ApplicationException("Failed to register raw input devices."); sl@0: } sl@0: } sl@0: sl@0: sl@0: //------------------------------------------------------------- sl@0: // methods sl@0: //------------------------------------------------------------- sl@0: sl@0: public void ProcessMessage(Message message) sl@0: { sl@0: int param; sl@0: sl@0: switch (message.Msg) sl@0: { sl@0: case WM_KEYDOWN: sl@0: param = message.WParam.ToInt32(); sl@0: ProcessKeyDown(param); sl@0: break; sl@0: case WM_APPCOMMAND: sl@0: param = message.LParam.ToInt32(); sl@0: ProcessAppCommand(param); sl@0: break; sl@0: case WM_INPUT: sl@0: ProcessInputCommand(ref message); sl@0: message.Result = new IntPtr(0); sl@0: break; sl@0: } sl@0: sl@0: } sl@0: sl@0: sl@0: //------------------------------------------------------------- sl@0: // methods (helpers) sl@0: //------------------------------------------------------------- sl@0: sl@0: private void ProcessKeyDown(int param) sl@0: { sl@0: RemoteControlButton rcb = RemoteControlButton.Unknown; sl@0: sl@0: switch (param) sl@0: { sl@0: case (int) Keys.Escape: sl@0: rcb = RemoteControlButton.Clear; sl@0: break; sl@0: case (int) Keys.Down: sl@0: rcb = RemoteControlButton.Down; sl@0: break; sl@0: case (int) Keys.Left: sl@0: rcb = RemoteControlButton.Left; sl@0: break; sl@0: case (int) Keys.D0: sl@0: rcb = RemoteControlButton.Digit0; sl@0: break; sl@0: case (int) Keys.D1: sl@0: rcb = RemoteControlButton.Digit1; sl@0: break; sl@0: case (int) Keys.D2: sl@0: rcb = RemoteControlButton.Digit2; sl@0: break; sl@0: case (int) Keys.D3: sl@0: rcb = RemoteControlButton.Digit3; sl@0: break; sl@0: case (int) Keys.D4: sl@0: rcb = RemoteControlButton.Digit4; sl@0: break; sl@0: case (int) Keys.D5: sl@0: rcb = RemoteControlButton.Digit5; sl@0: break; sl@0: case (int) Keys.D6: sl@0: rcb = RemoteControlButton.Digit6; sl@0: break; sl@0: case (int) Keys.D7: sl@0: rcb = RemoteControlButton.Digit7; sl@0: break; sl@0: case (int) Keys.D8: sl@0: rcb = RemoteControlButton.Digit8; sl@0: break; sl@0: case (int) Keys.D9: sl@0: rcb = RemoteControlButton.Digit9; sl@0: break; sl@0: case (int) Keys.Enter: sl@0: rcb = RemoteControlButton.Enter; sl@0: break; sl@0: case (int) Keys.Right: sl@0: rcb = RemoteControlButton.Right; sl@0: break; sl@0: case (int) Keys.Up: sl@0: rcb = RemoteControlButton.Up; sl@0: break; sl@0: } sl@0: sl@0: if (this.ButtonPressed != null && rcb != RemoteControlButton.Unknown) sl@0: this.ButtonPressed(this, new RemoteControlEventArgs(rcb, GetDevice(param))); sl@0: } sl@0: sl@0: sl@0: private void ProcessAppCommand(int param) sl@0: { sl@0: RemoteControlButton rcb = RemoteControlButton.Unknown; sl@0: sl@0: int cmd = (int) (((ushort) (param >> 16)) & ~FAPPCOMMAND_MASK); sl@0: sl@0: switch (cmd) sl@0: { sl@0: case APPCOMMAND_BROWSER_BACKWARD: sl@0: rcb = RemoteControlButton.Back; sl@0: break; sl@0: case APPCOMMAND_MEDIA_CHANNEL_DOWN: sl@0: rcb = RemoteControlButton.ChannelDown; sl@0: break; sl@0: case APPCOMMAND_MEDIA_CHANNEL_UP: sl@0: rcb = RemoteControlButton.ChannelUp; sl@0: break; sl@0: case APPCOMMAND_MEDIA_FAST_FORWARD: sl@0: rcb = RemoteControlButton.FastForward; sl@0: break; sl@0: case APPCOMMAND_VOLUME_MUTE: sl@0: rcb = RemoteControlButton.VolumeMute; sl@0: break; sl@0: case APPCOMMAND_MEDIA_PAUSE: sl@0: rcb = RemoteControlButton.Pause; sl@0: break; sl@0: case APPCOMMAND_MEDIA_PLAY: sl@0: rcb = RemoteControlButton.Play; sl@0: break; sl@0: case APPCOMMAND_MEDIA_PLAY_PAUSE: sl@0: rcb = RemoteControlButton.PlayPause; sl@0: break; sl@0: case APPCOMMAND_MEDIA_RECORD: sl@0: rcb = RemoteControlButton.Record; sl@0: break; sl@0: case APPCOMMAND_MEDIA_PREVIOUSTRACK: sl@0: rcb = RemoteControlButton.PreviousTrack; sl@0: break; sl@0: case APPCOMMAND_MEDIA_REWIND: sl@0: rcb = RemoteControlButton.Rewind; sl@0: break; sl@0: case APPCOMMAND_MEDIA_NEXTTRACK: sl@0: rcb = RemoteControlButton.NextTrack; sl@0: break; sl@0: case APPCOMMAND_MEDIA_STOP: sl@0: rcb = RemoteControlButton.Stop; sl@0: break; sl@0: case APPCOMMAND_VOLUME_DOWN: sl@0: rcb = RemoteControlButton.VolumeDown; sl@0: break; sl@0: case APPCOMMAND_VOLUME_UP: sl@0: rcb = RemoteControlButton.VolumeUp; sl@0: break; sl@0: } sl@0: sl@0: if (this.ButtonPressed != null && rcb != RemoteControlButton.Unknown) sl@0: this.ButtonPressed(this, new RemoteControlEventArgs(rcb, GetDevice(param))); sl@0: } sl@0: sl@0: sl@0: private void ProcessInputCommand(ref Message message) sl@0: { sl@7: Debug.WriteLine("================WM_INPUT================"); sl@6: sl@0: sl@10: //Declare a pointer sl@10: IntPtr rawInputBuffer = IntPtr.Zero; sl@0: sl@10: try sl@10: { sl@10: //Fetch raw input sl@11: RAWINPUT rawInput = new RAWINPUT(); sl@11: if (!RawInput.GetRawInputData(message.LParam, ref rawInput, ref rawInputBuffer)) sl@6: { sl@6: return; sl@6: } sl@6: sl@10: //Fetch device info sl@10: RID_DEVICE_INFO deviceInfo = new RID_DEVICE_INFO(); sl@11: if (!RawInput.GetDeviceInfo(rawInput.header.hDevice, ref deviceInfo)) sl@10: { sl@10: return; sl@10: } sl@11: sl@6: sl@11: if (rawInput.header.dwType == Const.RIM_TYPEHID) //Check that our raw input is HID sl@6: { sl@11: Debug.WriteLine("WM_INPUT source device is HID."); sl@11: //Get Usage Page and Usage sl@11: Debug.WriteLine("Usage Page: 0x" + deviceInfo.hid.usUsagePage.ToString("X4") + " Usage: 0x" + deviceInfo.hid.usUsage.ToString("X4")); sl@10: sl@6: //Make sure both usage page and usage are matching MCE remote sl@11: //TODO: handle more that just MCE usage page. sl@8: if (deviceInfo.hid.usUsagePage != (ushort)Hid.UsagePage.MceRemote || deviceInfo.hid.usUsage != (ushort)Hid.UsageId.MceRemoteUsage) sl@6: { sl@6: Debug.WriteLine("Not MCE remote page and usage."); sl@6: return; sl@6: } sl@0: sl@11: if (!(rawInput.hid.dwSizeHid > 1 //Make sure our HID msg size more than 1. In fact the first ushort is irrelevant to us for now sl@11: && rawInput.hid.dwCount > 0)) //Check that we have at least one HID msg sl@3: { sl@11: return; sl@11: } sl@11: sl@11: sl@11: //Allocate a buffer for one HID input sl@11: byte[] hidInput = new byte[rawInput.hid.dwSizeHid]; sl@11: sl@11: //For each HID input in our raw input sl@11: for (int i = 0; i < rawInput.hid.dwCount; i++) sl@11: { sl@11: //Compute the address from which to copy our HID input sl@11: int hidInputOffset = 0; sl@11: unsafe sl@3: { sl@11: byte* source = (byte*)rawInputBuffer; sl@11: source += sizeof(RAWINPUTHEADER) + sizeof(RAWHID) + (rawInput.hid.dwSizeHid * i); sl@11: hidInputOffset = (int)source; sl@11: } sl@11: sl@11: //Copy HID input into our buffer sl@11: Marshal.Copy(new IntPtr(hidInputOffset), hidInput, 0, rawInput.hid.dwSizeHid); sl@11: sl@11: //Print HID raw input in our debug output sl@11: string hidDump = "HID " + rawInput.hid.dwCount + "/" + rawInput.hid.dwSizeHid + ":"; sl@11: foreach (byte b in hidInput) sl@11: { sl@11: hidDump += b.ToString("X2"); sl@11: } sl@11: Debug.WriteLine(hidDump); sl@11: sl@11: ushort usage = 0; sl@11: //hidInput[0] //Not sure what's the meaning of the code at offset 0 sl@11: if (hidInput.Length == 2) sl@11: { sl@11: //Single byte code sl@11: usage = hidInput[1]; //Get button code sl@11: } sl@11: else if (hidInput.Length > 2) //Defensive sl@11: { sl@11: //Assuming double bytes code sl@11: usage = (ushort)((hidInput[1] << 2) + hidInput[2]); sl@11: } sl@11: sl@11: // sl@11: if (Enum.IsDefined(typeof(MceButton), usage) && usage != 0) //Our button is a known MCE button sl@11: { sl@11: if (this.ButtonPressed != null) //What's that? sl@11: { sl@11: this.ButtonPressed(this, new RemoteControlEventArgs((MceButton)usage, InputDevice.OEM)); sl@11: } sl@3: } sl@3: } sl@11: sl@10: } sl@11: else if (rawInput.header.dwType == Const.RIM_TYPEMOUSE) sl@10: { sl@11: Debug.WriteLine("WM_INPUT source device is Mouse."); sl@10: // do mouse handling... sl@10: } sl@11: else if (rawInput.header.dwType == Const.RIM_TYPEKEYBOARD) sl@10: { sl@11: Debug.WriteLine("WM_INPUT source device is Keyboard."); sl@10: // do keyboard handling... sl@11: sl@10: } sl@10: } sl@10: finally sl@10: { sl@10: //Always executed when leaving our try block sl@10: Marshal.FreeHGlobal(rawInputBuffer); sl@10: } sl@0: } sl@0: sl@0: sl@0: private InputDevice GetDevice(int param) sl@0: { sl@0: InputDevice inputDevice; sl@0: sl@0: switch ((int) (((ushort) (param >> 16)) & FAPPCOMMAND_MASK)) sl@0: { sl@0: case FAPPCOMMAND_OEM: sl@0: inputDevice = InputDevice.OEM; sl@0: break; sl@0: case FAPPCOMMAND_MOUSE: sl@0: inputDevice = InputDevice.Mouse; sl@0: break; sl@0: default: sl@0: inputDevice = InputDevice.Key; sl@0: break; sl@0: } sl@0: sl@0: return inputDevice; sl@0: } sl@0: } sl@0: }