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@12: MoreInfo, sl@12: Print, 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: public delegate void RemoteControlDeviceEventHandler(object sender, RemoteControlEventArgs e); sl@0: public event RemoteControlDeviceEventHandler ButtonPressed; sl@0: sl@12: public delegate void HidUsageHandler(ushort aUsage); sl@12: sl@12: sl@0: //------------------------------------------------------------- sl@0: // constructors sl@0: //------------------------------------------------------------- sl@0: sl@15: public RemoteControlDevice(IntPtr aHWND) 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@15: rid[0].dwFlags = Const.RIDEV_EXINPUTSINK; sl@15: rid[0].hwndTarget = aHWND; sl@0: sl@0: rid[1].usUsagePage = 0x0C; sl@0: rid[1].usUsage = 0x01; sl@15: rid[1].dwFlags = Const.RIDEV_EXINPUTSINK; sl@15: rid[1].hwndTarget = aHWND; sl@0: sl@0: rid[2].usUsagePage = 0x0C; sl@0: rid[2].usUsage = 0x80; sl@15: rid[2].dwFlags = Const.RIDEV_EXINPUTSINK; sl@15: rid[2].hwndTarget = aHWND; sl@0: sl@15: if (!Function.RegisterRawInputDevices(rid,(uint) rid.Length,(uint) Marshal.SizeOf(rid[0]))) sl@0: { sl@15: throw new ApplicationException("Failed to register raw input devices: " + Marshal.GetLastWin32Error().ToString()); 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: switch (message.Msg) sl@0: { sl@15: case Const.WM_KEYDOWN: sl@15: ProcessKeyDown(message.WParam); sl@0: break; sl@15: case Const.WM_APPCOMMAND: sl@15: ProcessAppCommand(message.LParam); sl@0: break; sl@15: case Const.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@15: private void ProcessKeyDown(IntPtr wParam) sl@0: { sl@0: RemoteControlButton rcb = RemoteControlButton.Unknown; sl@0: sl@15: switch (wParam.ToInt32()) 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@15: this.ButtonPressed(this, new RemoteControlEventArgs(rcb, GetDevice(wParam))); sl@0: } sl@0: sl@0: sl@15: private void ProcessAppCommand(IntPtr lParam) sl@0: { sl@0: RemoteControlButton rcb = RemoteControlButton.Unknown; sl@0: sl@15: int cmd = Macro.GET_APPCOMMAND_LPARAM(lParam); sl@15: //(int) (((ushort) (param >> 16)) & ~Const.FAPPCOMMAND_MASK); sl@0: sl@0: switch (cmd) sl@0: { sl@15: case Const.APPCOMMAND_BROWSER_BACKWARD: sl@0: rcb = RemoteControlButton.Back; sl@0: break; sl@15: case Const.APPCOMMAND_MEDIA_CHANNEL_DOWN: sl@0: rcb = RemoteControlButton.ChannelDown; sl@0: break; sl@15: case Const.APPCOMMAND_MEDIA_CHANNEL_UP: sl@0: rcb = RemoteControlButton.ChannelUp; sl@0: break; sl@15: case Const.APPCOMMAND_MEDIA_FAST_FORWARD: sl@0: rcb = RemoteControlButton.FastForward; sl@0: break; sl@15: case Const.APPCOMMAND_VOLUME_MUTE: sl@0: rcb = RemoteControlButton.VolumeMute; sl@0: break; sl@15: case Const.APPCOMMAND_MEDIA_PAUSE: sl@0: rcb = RemoteControlButton.Pause; sl@0: break; sl@15: case Const.APPCOMMAND_MEDIA_PLAY: sl@0: rcb = RemoteControlButton.Play; sl@0: break; sl@15: case Const.APPCOMMAND_MEDIA_PLAY_PAUSE: sl@0: rcb = RemoteControlButton.PlayPause; sl@0: break; sl@15: case Const.APPCOMMAND_MEDIA_RECORD: sl@0: rcb = RemoteControlButton.Record; sl@0: break; sl@15: case Const.APPCOMMAND_MEDIA_PREVIOUSTRACK: sl@0: rcb = RemoteControlButton.PreviousTrack; sl@0: break; sl@15: case Const.APPCOMMAND_MEDIA_REWIND: sl@0: rcb = RemoteControlButton.Rewind; sl@0: break; sl@15: case Const.APPCOMMAND_MEDIA_NEXTTRACK: sl@0: rcb = RemoteControlButton.NextTrack; sl@0: break; sl@15: case Const.APPCOMMAND_MEDIA_STOP: sl@0: rcb = RemoteControlButton.Stop; sl@0: break; sl@15: case Const.APPCOMMAND_VOLUME_DOWN: sl@0: rcb = RemoteControlButton.VolumeDown; sl@0: break; sl@15: case Const.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@15: this.ButtonPressed(this, new RemoteControlEventArgs(rcb, GetDevice(lParam))); sl@0: } sl@0: sl@12: /// sl@12: /// sl@12: /// sl@12: /// sl@12: private void HidConsumerDeviceHandler(ushort aUsage) sl@12: { sl@12: if (aUsage == 0) sl@12: { sl@12: //Just skip those sl@12: return; sl@12: } sl@12: sl@12: if (Enum.IsDefined(typeof(ConsumerControl), aUsage) && aUsage != 0) //Our button is a known consumer control sl@12: { sl@12: if (this.ButtonPressed != null) sl@12: { sl@12: RemoteControlButton button=RemoteControlButton.Unknown; sl@16: if (aUsage == (ushort)ConsumerControl.AppCtrlProperties || aUsage == (ushort)ConsumerControl.MceProperties) sl@12: { sl@12: button = RemoteControlButton.MoreInfo; sl@12: } sl@16: else if (aUsage == (ushort)ConsumerControl.AppCtrlPrint) sl@12: { sl@12: button = RemoteControlButton.Print; sl@12: } sl@16: else if (aUsage == (ushort)ConsumerControl.MediaSelectProgramGuide || aUsage == (ushort)ConsumerControl.MceProgramGuide) sl@12: { sl@12: button = RemoteControlButton.Guide; sl@12: } sl@12: this.ButtonPressed(this, new RemoteControlEventArgs(button, InputDevice.OEM)); sl@12: } sl@12: } sl@12: else sl@12: { sl@12: Debug.WriteLine("Unknown Consumer Control!"); sl@12: } sl@12: } sl@12: sl@12: /// sl@12: /// sl@12: /// sl@12: /// sl@12: private void HidMceRemoteHandler(ushort aUsage) sl@12: { sl@12: if (aUsage == 0) sl@12: { sl@12: //Just skip those sl@12: return; sl@12: } sl@12: sl@12: sl@12: if (Enum.IsDefined(typeof(MceButton), aUsage) && aUsage != 0) //Our button is a known MCE button sl@12: { sl@12: if (this.ButtonPressed != null) sl@12: { sl@12: this.ButtonPressed(this, new RemoteControlEventArgs((MceButton)aUsage, InputDevice.OEM)); sl@12: } sl@12: } sl@12: else sl@12: { sl@12: Debug.WriteLine("Unknown MCE button!"); sl@12: } sl@12: sl@12: } sl@12: sl@0: sl@0: private void ProcessInputCommand(ref Message message) sl@0: { sl@15: //We received a WM_INPUT message sl@7: Debug.WriteLine("================WM_INPUT================"); sl@6: sl@15: //Check if we received this message while in background or foreground sl@15: if (Macro.GET_RAWINPUT_CODE_WPARAM(message.WParam) == Const.RIM_INPUT) sl@15: { sl@15: Debug.WriteLine("================FOREGROUND"); sl@15: } sl@15: else if (Macro.GET_RAWINPUT_CODE_WPARAM(message.WParam) == Const.RIM_INPUTSINK) sl@15: { sl@15: Debug.WriteLine("================BACKGROUND"); sl@15: } 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@13: Debug.WriteLine("Usage Page: 0x" + deviceInfo.hid.usUsagePage.ToString("X4") + " Usage ID: 0x" + deviceInfo.hid.usUsage.ToString("X4")); sl@10: sl@12: // sl@13: HidUsageHandler usagePageHandler=null; sl@12: sl@14: //Check if this an MCE remote HID message sl@14: if (deviceInfo.hid.usUsagePage == (ushort)Hid.UsagePage.MceRemote && deviceInfo.hid.usUsage == (ushort)Hid.UsageId.MceRemoteUsage) sl@12: { sl@13: usagePageHandler = HidMceRemoteHandler; sl@12: } sl@14: //Check if this is a consumer control HID message sl@14: else if (deviceInfo.hid.usUsagePage == (ushort)Hid.UsagePage.Consumer && deviceInfo.hid.usUsage == (ushort)Hid.UsageId.ConsumerControl) sl@12: { sl@13: usagePageHandler = HidConsumerDeviceHandler; sl@12: } sl@14: //Unknown HID message sl@12: else sl@6: { sl@14: Debug.WriteLine("Unknown HID message."); 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@12: usage = (ushort)((hidInput[2] << 8) + hidInput[1]); sl@11: } sl@11: sl@13: Debug.WriteLine("Usage: 0x" + usage.ToString("X4")); sl@13: sl@13: //Call on our Usage Page handler sl@13: usagePageHandler(usage); 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@15: private InputDevice GetDevice(IntPtr lParam) sl@0: { sl@0: InputDevice inputDevice; sl@0: sl@15: switch (Macro.GET_DEVICE_LPARAM(lParam)) sl@0: { sl@15: case Const.FAPPCOMMAND_OEM: sl@0: inputDevice = InputDevice.OEM; sl@0: break; sl@15: case Const.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: }