aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.UI/UserInput/KeyboardListener.cs
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.UI/UserInput/KeyboardListener.cs')
-rw-r--r--MediaBrowser.UI/UserInput/KeyboardListener.cs215
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
+ }
+}