How YimMenuV2's HotkeySystem Registers and Handles Keyboard Shortcuts

YimMenuV2 implements a robust hotkey engine in 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, 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

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

Creating and Editing Hotkeys

When users open the hotkey editor in 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

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

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:

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

Toggle modification mode when opening the hotkey editor:

// 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 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →