sl@0: using System; sl@0: using System.Windows.Forms; sl@0: using System.Runtime.InteropServices; sl@0: using System.Diagnostics; sl@23: using System.Text; sl@23: sl@8: using Hid.UsageTables; sl@9: using Win32; sl@0: sl@23: 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@19: ConsumerControl iConsumerControl; sl@0: sl@3: public RemoteControlEventArgs(RemoteControlButton rcb, InputDevice device) sl@0: { sl@19: SetNullButtons(); sl@19: // sl@0: _rcb = rcb; sl@19: _device = device; sl@0: } sl@0: sl@19: public RemoteControlEventArgs(ConsumerControl aConsumerControl, InputDevice device) sl@19: { sl@19: SetNullButtons(); sl@19: // sl@19: iConsumerControl = aConsumerControl; sl@19: _device = device; sl@19: } sl@19: sl@19: sl@3: public RemoteControlEventArgs(MceButton mce, InputDevice device) sl@3: { sl@19: SetNullButtons(); sl@19: // sl@19: iMceButton = mce; sl@19: _device = device; sl@19: } sl@19: sl@19: private void SetNullButtons() sl@19: { sl@19: iConsumerControl = ConsumerControl.Null; sl@19: iMceButton = MceButton.Null; sl@3: _rcb = RemoteControlButton.Unknown; 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@19: public ConsumerControl ConsumerControl sl@19: { sl@19: get { return iConsumerControl; } sl@19: set { iConsumerControl = value; } sl@19: } sl@19: 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@19: public delegate bool RemoteControlDeviceEventHandler(object sender, RemoteControlEventArgs e); sl@0: public event RemoteControlDeviceEventHandler ButtonPressed; sl@0: sl@19: /// sl@19: /// Return true if the usage was processed. sl@19: /// sl@19: /// sl@19: /// sl@19: public delegate bool 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@22: RAWINPUTDEVICE[] rid = new RAWINPUTDEVICE[5]; sl@0: sl@21: int i = 0; sl@21: rid[i].usUsagePage = (ushort)Hid.UsagePage.MceRemote; sl@21: rid[i].usUsage = (ushort)Hid.UsageIdMce.MceRemote; sl@21: rid[i].dwFlags = Const.RIDEV_EXINPUTSINK; sl@21: rid[i].hwndTarget = aHWND; sl@0: sl@21: i++; sl@21: rid[i].usUsagePage = (ushort)Hid.UsagePage.Consumer; sl@21: rid[i].usUsage = (ushort)Hid.UsageIdConsumer.ConsumerControl; sl@21: rid[i].dwFlags = Const.RIDEV_EXINPUTSINK; sl@21: rid[i].hwndTarget = aHWND; sl@0: sl@21: i++; sl@21: rid[i].usUsagePage = (ushort)Hid.UsagePage.Consumer; sl@21: rid[i].usUsage = (ushort)Hid.UsageIdConsumer.Selection; sl@21: rid[i].dwFlags = Const.RIDEV_EXINPUTSINK; sl@21: rid[i].hwndTarget = aHWND; sl@21: sl@22: i++; sl@22: rid[i].usUsagePage = (ushort)Hid.UsagePage.GenericDesktopControl; sl@22: rid[i].usUsage = (ushort)Hid.UsageIdGenericDesktop.SystemControl; sl@22: rid[i].dwFlags = Const.RIDEV_EXINPUTSINK; sl@22: rid[i].hwndTarget = aHWND; sl@21: sl@22: i++; sl@22: rid[i].usUsagePage = (ushort)Hid.UsagePage.GenericDesktopControl; sl@22: rid[i].usUsage = (ushort)Hid.UsageIdGenericDesktop.Keyboard; sl@22: rid[i].dwFlags = Const.RIDEV_EXINPUTSINK; sl@22: rid[i].hwndTarget = aHWND; sl@21: sl@21: //i++; sl@21: //rid[i].usUsagePage = (ushort)Hid.UsagePage.GenericDesktopControl; sl@21: //rid[i].usUsage = (ushort)Hid.UsageIdGenericDesktop.Mouse; sl@21: //rid[i].dwFlags = Const.RIDEV_EXINPUTSINK; sl@21: //rid[i].hwndTarget = aHWND; sl@21: 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_INPUT: sl@19: //Returning zero means we processed that message. sl@19: message.Result = new IntPtr(0); sl@0: ProcessInputCommand(ref message); 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@22: break; sl@22: case (int)Keys.Up: sl@22: rcb = RemoteControlButton.Up; sl@22: 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@22: break; sl@22: case (int)Keys.Right: sl@22: rcb = RemoteControlButton.Right; sl@22: break; sl@22: case (int)Keys.Enter: sl@22: rcb = RemoteControlButton.Enter; sl@22: 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@22: break; sl@0: } sl@0: sl@22: if (this.ButtonPressed != null && rcb != RemoteControlButton.Unknown) sl@22: { sl@22: Debug.WriteLine("KeyDown: " + rcb.ToString()); sl@20: this.ButtonPressed(this, new RemoteControlEventArgs(rcb, InputDevice.Key)); sl@22: } sl@0: } sl@0: sl@0: sl@12: /// sl@12: /// sl@12: /// sl@12: /// sl@19: private bool HidConsumerDeviceHandler(ushort aUsage) sl@12: { sl@12: if (aUsage == 0) sl@12: { sl@12: //Just skip those sl@19: return false; 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@19: return this.ButtonPressed(this, new RemoteControlEventArgs((ConsumerControl)aUsage, InputDevice.OEM)); sl@12: } sl@19: return false; sl@12: } sl@12: else sl@12: { sl@12: Debug.WriteLine("Unknown Consumer Control!"); sl@19: return false; sl@12: } sl@12: } sl@12: sl@12: /// sl@12: /// sl@12: /// sl@12: /// sl@19: private bool HidMceRemoteHandler(ushort aUsage) sl@12: { sl@12: if (aUsage == 0) sl@12: { sl@12: //Just skip those sl@19: return false; 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@19: return this.ButtonPressed(this, new RemoteControlEventArgs((MceButton)aUsage, InputDevice.OEM)); sl@12: } sl@19: return false; sl@12: } sl@12: else sl@12: { sl@12: Debug.WriteLine("Unknown MCE button!"); sl@19: return false; 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@17: //Declare some pointers sl@10: IntPtr rawInputBuffer = IntPtr.Zero; sl@17: //My understanding is that this is basically our HID descriptor sl@17: IntPtr preParsedData = 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@23: /* sl@23: //TODO: This needs create file from the device name/path sl@23: //Get device manufacturer sl@23: StringBuilder manufacturerString = new StringBuilder(256); sl@23: bool returnStatus = Win32.Function.HidD_GetManufacturerString(rawInput.header.hDevice, manufacturerString, manufacturerString.Capacity); sl@23: if (returnStatus) sl@23: { sl@23: Debug.WriteLine("Manufacturer name is {0}", manufacturerString.ToString()); sl@23: } sl@23: */ sl@23: sl@23: 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@22: sl@22: // sl@22: Debug.WriteLine(RawInput.GetDeviceName(rawInput.header.hDevice)); 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@17: sl@17: preParsedData = RawInput.GetPreParsedData(rawInput.header.hDevice); sl@17: sl@12: // sl@13: HidUsageHandler usagePageHandler=null; sl@12: sl@14: //Check if this an MCE remote HID message sl@21: if (deviceInfo.hid.usUsagePage == (ushort)Hid.UsagePage.MceRemote && deviceInfo.hid.usUsage == (ushort)Hid.UsageIdMce.MceRemote) sl@12: { sl@13: usagePageHandler = HidMceRemoteHandler; sl@12: } sl@14: //Check if this is a consumer control HID message sl@21: else if (deviceInfo.hid.usUsagePage == (ushort)Hid.UsagePage.Consumer && deviceInfo.hid.usUsage == (ushort)Hid.UsageIdConsumer.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@17: byte[] hidInputReport = new byte[rawInput.hid.dwSizeHid]; sl@11: sl@18: Debug.WriteLine("Raw input contains " + rawInput.hid.dwCount + " HID input report(s)"); sl@18: sl@18: //For each HID input report 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@17: Marshal.Copy(new IntPtr(hidInputOffset), hidInputReport, 0, (int)rawInput.hid.dwSizeHid); sl@11: sl@18: //Print HID input report in our debug output sl@18: string hidDump = "HID input report: "; sl@17: foreach (byte b in hidInputReport) sl@11: { sl@11: hidDump += b.ToString("X2"); sl@11: } sl@11: Debug.WriteLine(hidDump); sl@11: sl@17: //Proper parsing now sl@18: uint usageCount = 1; //Assuming a single usage per input report. Is that correct? sl@17: Win32.USAGE_AND_PAGE[] usages = new Win32.USAGE_AND_PAGE[usageCount]; sl@17: Win32.HidStatus status = Win32.Function.HidP_GetUsagesEx(Win32.HIDP_REPORT_TYPE.HidP_Input, 0, usages, ref usageCount, preParsedData, hidInputReport, (uint)hidInputReport.Length); sl@17: if (status != Win32.HidStatus.HIDP_STATUS_SUCCESS) sl@17: { sl@17: Debug.WriteLine("Could not parse HID data!"); sl@17: } sl@17: else sl@17: { sl@17: Debug.WriteLine("UsagePage: 0x" + usages[0].UsagePage.ToString("X4")); sl@17: Debug.WriteLine("Usage: 0x" + usages[0].Usage.ToString("X4")); sl@18: //Call on our Usage Page handler sl@18: usagePageHandler(usages[0].Usage); sl@17: } 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@22: Debug.WriteLine("Type: " + deviceInfo.keyboard.dwType.ToString()); sl@22: Debug.WriteLine("SubType: " + deviceInfo.keyboard.dwSubType.ToString()); sl@22: Debug.WriteLine("Mode: " + deviceInfo.keyboard.dwKeyboardMode.ToString()); sl@22: Debug.WriteLine("Number of function keys: " + deviceInfo.keyboard.dwNumberOfFunctionKeys.ToString()); sl@22: Debug.WriteLine("Number of indicators: " + deviceInfo.keyboard.dwNumberOfIndicators.ToString()); sl@23: Debug.WriteLine("Number of keys total: " + deviceInfo.keyboard.dwNumberOfKeysTotal.ToString()); sl@10: } sl@10: } sl@10: finally sl@10: { sl@10: //Always executed when leaving our try block sl@10: Marshal.FreeHGlobal(rawInputBuffer); sl@17: Marshal.FreeHGlobal(preParsedData); sl@10: } sl@0: } sl@0: } sl@0: }