Skip to content

Commit

Permalink
YAMDCC hotkey handler: initial version
Browse files Browse the repository at this point in the history
Currently the only hotkey supported is the MSI Center key (which opens the YAMDCC config editor by default, and will be configurable).

Please test and report if the MSI Center key isn't working with Hotkey Handler.
  • Loading branch information
Sparronator9999 committed Feb 4, 2025
1 parent ba29372 commit 6e4fb21
Show file tree
Hide file tree
Showing 12 changed files with 1,362 additions and 0 deletions.
10 changes: 10 additions & 0 deletions YAMDCC.HotkeyHandler/App.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<System.Windows.Forms.ApplicationConfigurationSection>
<add key="DpiAwareness" value="PerMonitorV2"/>
<add key="EnableWindowsFormsHighDpiAutoResizing" value="true"/>
</System.Windows.Forms.ApplicationConfigurationSection>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
</startup>
</configuration>
160 changes: 160 additions & 0 deletions YAMDCC.HotkeyHandler/KeyHook.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using YAMDCC.HotkeyHandler.Win32;

namespace YAMDCC.HotkeyHandler;

internal class KeyHook : IDisposable
{
private readonly HookProc KbdHookProc;
private IntPtr KbdHookPtr;

/// <summary>
/// Used to determine if we should raise the <see cref="KeyDown"/>
/// event when <see cref="IgnoreRepeats"/> is <see langword="true"/>.
/// </summary>
private readonly HashSet<uint> HeldKeys = [];

/// <summary>
/// Gets or sets whether to raise <see cref="KeyDown"/> events when
/// a key press is automatically repeated due to the key being held down.
/// </summary>
public bool IgnoreRepeats { get; set; }

/// <summary>
/// Gets or sets whether to raise <see cref="KeyDown"/> or <see cref="KeyUp"/>
/// events when a modifier key (Ctrl, Shift, or Alt) is pressed.
/// </summary>
public bool IgnoreModifiers { get; set; }

/// <summary>
/// Gets the currently held modifier keys.
/// </summary>
public ConsoleModifiers Modifiers { get; private set; }

/// <summary>
/// Occurs when a key is pressed.
/// </summary>
public event EventHandler<KeyHookEventArgs> KeyDown;

/// <summary>
/// Occurs when a key is released.
/// </summary>
public event EventHandler<KeyHookEventArgs> KeyUp;

public KeyHook(bool ignoreRepeats = true, bool ignoreModifiers = false)
{
KbdHookProc = new HookProc(HookProcCallback);
IgnoreRepeats = ignoreRepeats;
IgnoreModifiers = ignoreModifiers;
}

/// <summary>
/// Uninstalls the keyboard hook and releases all unmanaged
/// resources associated with this <see cref="KeyHook"/> instance.
/// </summary>
public void Dispose()
{
Uninstall();
GC.SuppressFinalize(this);
}

~KeyHook()
{
Uninstall();
}

public bool Install()
{
if (KbdHookPtr == IntPtr.Zero)
{
KbdHookPtr = User32.SetWindowsHookEx(13, KbdHookProc,
Marshal.GetHINSTANCE(typeof(KeyHook).Module), 0);
return KbdHookPtr != IntPtr.Zero;
}
// keyboard hook already installed
return true;
}

public bool Uninstall()
{
// uninstall keyboard hook if installed (KbdHookPtr != IntPtr.Zero),
// otherwise just return true
return KbdHookPtr == IntPtr.Zero || User32.UnhookWindowsHookEx(KbdHookPtr);
}

private IntPtr HookProcCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam.ToInt32() is 0x100 or 0x101 or 0x104 or 0x105)
{
KbdLLHookStruct kbdStruct = Marshal.PtrToStructure<KbdLLHookStruct>(lParam);
KeyHookEventArgs e = new(
kbdStruct.KeyCode,
kbdStruct.ScanCode,
(kbdStruct.Flags & 0x01) == 1,
(kbdStruct.Flags & 0x02) == 1,
(kbdStruct.Flags & 0x10) == 1);

switch (wParam.ToInt32())
{
case 0x100: // WM_KEYDOWN
case 0x104: // WM_SYSKEYDOWN
if (HandleModifiers(e.KeyCode, true) &&
(!IgnoreRepeats || !HeldKeys.Contains(e.ScanCode)))
{
KeyDown?.Invoke(this, e);
}
HeldKeys.Add(e.ScanCode);
break;
case 0x101: // WM_KEYUP
case 0x105: // WM_SYSKEYUP
if (HandleModifiers(e.KeyCode, false))
{
KeyUp?.Invoke(this, e);
}
HeldKeys.Remove(e.ScanCode);
break;
}

if (e.SuppressKeyPress)
{
return new IntPtr(1);
}
}
return User32.CallNextHookEx(KbdHookPtr, nCode, wParam, lParam);
}

private bool HandleModifiers(Keys key, bool pressed)
{
ConsoleModifiers modifier;
switch (key)
{
case Keys.LMenu:
case Keys.RMenu:
modifier = ConsoleModifiers.Alt;
break;
case Keys.LShiftKey:
case Keys.RShiftKey:
modifier = ConsoleModifiers.Shift;
break;
case Keys.LControlKey:
case Keys.RControlKey:
modifier = ConsoleModifiers.Control;
break;
default:
return true;
}

if (pressed)
{
Modifiers |= modifier;
}
else
{
Modifiers &= ~modifier;
}
return !IgnoreModifiers;
}
}
40 changes: 40 additions & 0 deletions YAMDCC.HotkeyHandler/KeyHookEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System;
using System.Windows.Forms;

namespace YAMDCC.HotkeyHandler;

internal class KeyHookEventArgs : EventArgs
{
public Keys KeyCode { get; }
public uint ScanCode { get; }
public bool ExtendedKey { get; }
public bool LowerILInjected { get; }
public bool Injected { get; }

/// <summary>
/// Gets or sets a value indicating whether the
/// key event should be sent to other applications.
/// </summary>
/// <returns>
/// <see langword="true"/> if the key event should not be sent
/// to other applications, otherwise <see langword="false"/>.
/// </returns>
public bool SuppressKeyPress { get; set; }

/// <summary>
/// Initialises a new instance of the <see cref="KeyHookEventArgs"/> class.
/// </summary>
/// <param name="keyCode"></param>
/// <param name="scanCode"></param>
/// <param name="extendedKey"></param>
/// <param name="lowerILInjected"></param>
/// <param name="injected"></param>
public KeyHookEventArgs(Keys keyCode, uint scanCode, bool extendedKey, bool lowerILInjected, bool injected)
{
KeyCode = keyCode;
ScanCode = scanCode;
ExtendedKey = extendedKey;
LowerILInjected = lowerILInjected;
Injected = injected;
}
}
Loading

2 comments on commit 6e4fb21

@porkmanager
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems doesent work for my gf66 12UG, i tried fn+f7 combo (switch msi center profiles)

@Sparronator9999
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That key combo should open the YAMDCC config editor as long as Hotkey Handler is running (assuming the key combo is the same on your laptop, see the next paragraph).

On my laptop, the F7 key has a "G" symbol on it that opens MSI Center (if installed). Try looking for a similar key on your laptop.

If the key combo is indeed Fn+F7 like on my laptop, the hardware scan codes might differ between MSI laptops.

I will update Hotkey Handler Soon™ with functionality to view detected key presses.

Please sign in to comment.