jellyfin/MediaBrowser.UI/UserInput/KeyboardListener.cs
2013-02-21 15:36:36 -05:00

211 lines
6.6 KiB
C#

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)
{
}
}
}
#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()
{
_hookID = SetHook(_proc);
}
/// <summary>
/// Stops the listening.
/// </summary>
private static void StopListening()
{
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
}
}