diff options
Diffstat (limited to 'MediaBrowser.UI/UserInput/KeyboardListener.cs')
| -rw-r--r-- | MediaBrowser.UI/UserInput/KeyboardListener.cs | 215 |
1 files changed, 215 insertions, 0 deletions
diff --git a/MediaBrowser.UI/UserInput/KeyboardListener.cs b/MediaBrowser.UI/UserInput/KeyboardListener.cs new file mode 100644 index 000000000..779da42fb --- /dev/null +++ b/MediaBrowser.UI/UserInput/KeyboardListener.cs @@ -0,0 +1,215 @@ +using MediaBrowser.Common.Logging; +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Windows.Forms; + +namespace MediaBrowser.UI.UserInput +{ + /// <summary> + /// Provides a basic low-level keyboard listener + /// Inspired by http://blogs.msdn.com/b/toub/archive/2006/05/03/589423.aspx + /// Use the KeyDown event to listen for keys. + /// Make sure to detach from the event when not needed. + /// </summary> + public static class KeyboardListener + { + #region KeyDown EventHandler + /// <summary> + /// The _ key down + /// </summary> + static volatile EventHandler<KeyEventArgs> _KeyDown; + /// <summary> + /// Fires whenever CurrentItem changes + /// </summary> + public static event EventHandler<KeyEventArgs> KeyDown + { + add + { + if (_KeyDown == null) + { + StartListening(); + } + + _KeyDown += value; + } + remove + { + _KeyDown -= value; + + if (_KeyDown == null && _hookID != IntPtr.Zero) + { + StopListening(); + } + } + } + + /// <summary> + /// Raises the <see cref="E:KeyDown" /> event. + /// </summary> + /// <param name="e">The <see cref="KeyEventArgs" /> instance containing the event data.</param> + private static void OnKeyDown(KeyEventArgs e) + { + e.SuppressKeyPress = false; + + if (_KeyDown != null) + { + // For now, don't async this + // This will give listeners a chance to modify SuppressKeyPress if they want + try + { + _KeyDown(null, e); + } + catch (Exception ex) + { + Logger.LogException("KeyDown event listener had an error: ", ex); + } + } + } + #endregion + + /// <summary> + /// The W h_ KEYBOAR d_ LL + /// </summary> + private const int WH_KEYBOARD_LL = 13; + /// <summary> + /// The W m_ KEYDOWN + /// </summary> + private const int WM_KEYDOWN = 0x0100; + /// <summary> + /// The W m_ SYSKEYDOWN + /// </summary> + private const int WM_SYSKEYDOWN = 0x0104; + + /// <summary> + /// The _hook ID + /// </summary> + private static IntPtr _hookID = IntPtr.Zero; + /// <summary> + /// The _proc + /// </summary> + private static LowLevelKeyboardProc _proc = HookCallback; + + /// <summary> + /// Starts the listening. + /// </summary> + private static void StartListening() + { + Logger.LogInfo("Attaching low-level keyboard hook"); + _hookID = SetHook(_proc); + } + + /// <summary> + /// Stops the listening. + /// </summary> + private static void StopListening() + { + Logger.LogInfo("Detaching low-level keyboard hook"); + + UnhookWindowsHookEx(_hookID); + _hookID = IntPtr.Zero; + } + + /// <summary> + /// Sets the hook. + /// </summary> + /// <param name="proc">The proc.</param> + /// <returns>IntPtr.</returns> + private static IntPtr SetHook(LowLevelKeyboardProc proc) + { + using (var curProcess = Process.GetCurrentProcess()) + using (var curModule = curProcess.MainModule) + { + return SetWindowsHookEx(WH_KEYBOARD_LL, proc, + GetModuleHandle(curModule.ModuleName), 0); + } + } + + /// <summary> + /// Hooks the callback. + /// </summary> + /// <param name="nCode">The n code.</param> + /// <param name="wParam">The w param.</param> + /// <param name="lParam">The l param.</param> + /// <returns>IntPtr.</returns> + private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) + { + var suppressKeyPress = false; + + if (nCode >= 0) + { + if (wParam == (IntPtr)WM_KEYDOWN || wParam == (IntPtr)WM_SYSKEYDOWN) + { + var vkCode = Marshal.ReadInt32(lParam); + + var keyData = (Keys)vkCode; + + var e = new KeyEventArgs(keyData); + + OnKeyDown(e); + + suppressKeyPress = e.SuppressKeyPress; + } + } + + if (suppressKeyPress) + { + return IntPtr.Zero; + } + + return CallNextHookEx(_hookID, nCode, wParam, lParam); + } + + /// <summary> + /// Delegate LowLevelKeyboardProc + /// </summary> + /// <param name="nCode">The n code.</param> + /// <param name="wParam">The w param.</param> + /// <param name="lParam">The l param.</param> + /// <returns>IntPtr.</returns> + private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam); + + #region Imports + /// <summary> + /// Sets the windows hook ex. + /// </summary> + /// <param name="idHook">The id hook.</param> + /// <param name="lpfn">The LPFN.</param> + /// <param name="hMod">The h mod.</param> + /// <param name="dwThreadId">The dw thread id.</param> + /// <returns>IntPtr.</returns> + [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] + private static extern IntPtr SetWindowsHookEx(int idHook, + LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId); + + /// <summary> + /// Unhooks the windows hook ex. + /// </summary> + /// <param name="hhk">The HHK.</param> + /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> + [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool UnhookWindowsHookEx(IntPtr hhk); + + /// <summary> + /// Calls the next hook ex. + /// </summary> + /// <param name="hhk">The HHK.</param> + /// <param name="nCode">The n code.</param> + /// <param name="wParam">The w param.</param> + /// <param name="lParam">The l param.</param> + /// <returns>IntPtr.</returns> + [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] + private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, + IntPtr wParam, IntPtr lParam); + + /// <summary> + /// Gets the module handle. + /// </summary> + /// <param name="lpModuleName">Name of the lp module.</param> + /// <returns>IntPtr.</returns> + [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] + private static extern IntPtr GetModuleHandle(string lpModuleName); + #endregion + } +} |
