# How YimMenuV2's HotkeySystem Registers and Handles Keyboard Shortcuts

> Discover how YimMenuV2's HotkeySystem registers and handles keyboard shortcuts. Learn about its startup command registration, GetAsyncKeyState polling, and command execution for a seamless user experience.

- Repository: [YimMenu/YimMenuV2](https://github.com/YimMenu/YimMenuV2)
- Tags: internals
- Published: 2026-07-17

---

**YimMenuV2 implements a robust hotkey engine in [`src/core/commands/HotkeySystem.cpp`](https://github.com/YimMenu/YimMenuV2/blob/main/src/core/commands/HotkeySystem.cpp) that registers commands during startup, polls `GetAsyncKeyState` in a dedicated script loop to detect key combinations, and executes bound commands while respecting UI focus and edit states.**

YimMenuV2 provides users with granular keyboard shortcut customization through its `HotkeySystem` architecture. This system allows binding arbitrary key chains—such as `Ctrl + F`—to any registered command using Windows virtual-key codes. Understanding how YimMenuV2 registers these shortcuts and processes input at runtime reveals a sophisticated implementation built on fiber-based script execution and stateful serialization.

## Command Registration During Startup

At initialization, the `HotkeySystem` iterates over the global command list to build its internal registry. In [`src/main.cpp`](https://github.com/YimMenu/YimMenuV2/blob/main/src/main.cpp), the call to `g_HotkeySystem.RegisterCommands()` triggers this process.

The method populates `m_CommandHotkeys`—a map that associates command hashes with `CommandLink` entries. Each command receives a dedicated entry regardless of whether it has a default binding. The system also pre-binds the special "chathelper" command to the **T** key (0x54) to ensure the chat overlay remains immediately accessible.

*Source:* [`HotkeySystem.cpp:21-33`](https://github.com/YimMenu/YimMenuV2/blob/enhanced/src/core/commands/HotkeySystem.cpp#L21-L33)

## Real-Time Key Press Detection

Once registered, the system monitors input through `HotkeySystem::RunScriptImpl()`, which executes within its own script fiber added via `ScriptMgr::AddScript`. This loop runs continuously while the menu is active.

Each tick performs three critical validations:

- The game window is foregrounded and not paused
- The UI is not currently capturing keyboard input  
- The user is not editing a hotkey (`m_BeingModified` is false)

For every registered command, the system walks the stored `m_Chain`—a `std::vector<int>` containing virtual-key codes. Using `GetAsyncKeyState`, it verifies that **all** keys in the chain are simultaneously pressed. If the complete combination is detected and at least 100 milliseconds have elapsed since the last trigger, the system retrieves the command object via `Commands::GetCommand(hash)` and executes it. Chat commands are dispatched on a separate fiber to prevent blocking the main loop.

*Source:* [`HotkeySystem.cpp:95-132`](https://github.com/YimMenu/YimMenuV2/blob/enhanced/src/core/commands/HotkeySystem.cpp#L95-L132)

## Creating and Editing Hotkeys

When users open the hotkey editor in [`src/game/frontend/items/DrawHotkey.cpp`](https://github.com/YimMenu/YimMenuV2/blob/main/src/game/frontend/items/DrawHotkey.cpp), the system enters modification mode. Calling `HotkeySystem::SetBeingModifed(true)` immediately pauses the listening loop to prevent accidental triggers during configuration.

The editor invokes `CreateHotkey(chain)`, which internally calls `ListenAndApply(pressed_key, chain)`. This helper scans the entire virtual-key range (0 to `VK_OEM_CLEAR`) and returns the first pressed key not present on the default blacklist (which contains `0`). A lambda function checks `is_key_unique` to prevent duplicate entries in the chain. Once validated, the new key appends to the vector and marks the state as dirty for persistence.

*Source:* [`HotkeySystem.cpp:71-92`](https://github.com/YimMenu/YimMenuV2/blob/enhanced/src/core/commands/HotkeySystem.cpp#L71-L92)

## Persistence Through State Serialization

Hotkey bindings survive game restarts through the `IStateSerializer` interface. The `SaveStateImpl` method iterates through `m_CommandHotkeys` and writes each non-empty chain to a JSON object keyed by command hash. Conversely, `LoadStateImpl` restores these bindings during startup by repopulating the command chains from the saved JSON configuration.

*Source:* [`HotkeySystem.cpp:43-67`](https://github.com/YimMenu/YimMenuV2/blob/enhanced/src/core/commands/HotkeySystem.cpp#L43-L67)

## Implementation Examples

The following patterns demonstrate how the system integrates with YimMenuV2's core lifecycle.

Register all commands at startup and add the monitoring script:

```cpp
// In src/main.cpp
g_HotkeySystem.RegisterCommands();
ScriptMgr::AddScript(std::make_unique<Script>(&HotkeySystem::RunScript));

```

Toggle modification mode when opening the hotkey editor:

```cpp
// In src/game/frontend/items/DrawHotkey.cpp
HotkeySystem::SetBeingModifed(true);   // Pause listening
g_HotkeySystem.CreateHotkey(link->m_Chain); // Capture user input
HotkeySystem::SetBeingModifed(false);  // Resume normal operation

```

## Summary

- **Registration happens at startup** via `RegisterCommands()`, which creates `CommandLink` entries for every available command in `m_CommandHotkeys`.
- **Input polling runs continuously** in `RunScriptImpl()`, using `GetAsyncKeyState` to detect complete key chains with a 100ms cooldown between triggers.
- **Editing mode pauses detection** through `SetBeingModifed()`, preventing conflicts while users define new bindings through `CreateHotkey()`.
- **Bindings persist across sessions** via `SaveStateImpl()` and `LoadStateImpl()`, which serialize chains to JSON keyed by command hash.
- **Special handling** ensures chat commands execute on separate fibers and critical functions like the chat overlay remain bound to default keys.

## Frequently Asked Questions

### How does YimMenuV2 prevent hotkeys from firing while editing them?

The system sets an internal flag via `SetBeingModifed(true)` when the user opens the hotkey editor. This boolean (`m_BeingModified`) is checked at the start of every iteration in `RunScriptImpl()`, causing the loop to skip all key detection logic until the user finishes editing and the flag is cleared.

### What Windows API does YimMenuV2 use to detect key presses?

The `HotkeySystem` relies on `GetAsyncKeyState` to poll the current state of each virtual key in a command's chain. This Windows API function returns the most recent key state for a specific virtual-key code, allowing the system to verify that all keys in a combination are simultaneously pressed.

### Where are hotkey bindings stored in YimMenuV2?

Bindings persist through the `IStateSerializer` interface. The `SaveStateImpl` method in [`HotkeySystem.cpp`](https://github.com/YimMenu/YimMenuV2/blob/main/HotkeySystem.cpp) writes non-empty key chains to a JSON configuration file, using the command hash as the key. These values are restored during initialization by `LoadStateImpl()`, ensuring user preferences survive game restarts.

### Why does the chathelper command have a hardcoded binding?

The chathelper command is pre-bound to the **T** key (0x54) during `RegisterCommands()` to guarantee that the chat overlay remains accessible even if the user clears or unbinds other shortcuts. This ensures critical UI functionality is always available through a predictable key.