docs/karabiner-keybinding-lifecycle.md
This guide explains what happens inside Karabiner-Elements from the moment you press a key to the moment your configured action runs.
It is intentionally written for newcomers who want to understand the internals, debug behavior, and reason about latency.
For a typical complex modification rule, the path is:
from/conditions.to events.post_event_to_virtual_devices schedules output events.console_user_server (shell command, send user command, input source change, software function).That split is central to understanding Karabiner internals.
Karabiner core service is responsible for:
console_user_server.console_user_server sideconsole_user_server handles operations that should run in the logged-in user context, such as:
shell_command_execution,send_user_command,select_input_source,It verifies peer identity/shared secret before accepting many operations.
A JSON rule in karabiner.json is parsed into internal event definitions.
Key parser/type entry points:
src/share/manipulator/types/event_definition.hppsrc/share/manipulator/types/to_event_definition.hppto entries become typed internal events, for example:
momentary_switch_event (virtual keyboard/mouse outputs),shell_command,send_user_command,select_input_source,software_function.Manipulator evaluation checks:
from key/button code,key_down, key_up, single).If matched, to actions are emitted as internal events.
post_event_to_virtual_devices is where outputs are queued and dispatched.
Main implementation:
src/share/manipulator/manipulators/post_event_to_virtual_devices/post_event_to_virtual_devices.hppsrc/share/manipulator/manipulators/post_event_to_virtual_devices/queue.hppImportant behavior:
shell_command, send_user_command, etc.) skip modifier-order timing adjustments and are queued as async work items.For key/mouse-like outputs, queue dispatcher posts reports to virtual HID service client.
This path drives synthetic keypresses/clicks seen by applications.
For operations like send_user_command:
console_user_server_client to send msgpack IPC.console_user_server receiver validates request.Relevant files:
src/share/console_user_server_client.hppsrc/core/console_user_server/include/console_user_server/receiver.hppsrc/core/console_user_server/include/console_user_server/send_user_command_handler.hppFor send_user_command, handler serializes payload JSON and sends it to configured UNIX datagram endpoint.
Karabiner intentionally separates concerns:
console_user_server)Benefits:
For a keybinding that triggers app activation or command execution, latency typically comes from:
console_user_server (for user-context operations)For many workflows, stage 4/5 (application behavior + display frame timing) dominates perceived delay.
Symptoms:
Common causes:
key_up/key_down assumptions),Symptoms:
Common causes:
console_user_server not connected/ready,send_user_command),Common causes:
shell_command paths launching shells/tools),For newcomers, this order gives the fastest mental model:
src/share/manipulator/types/event_definition.hppsrc/share/manipulator/types/to_event_definition.hppsrc/share/manipulator/manipulators/post_event_to_virtual_devices/post_event_to_virtual_devices.hppsrc/share/manipulator/manipulators/post_event_to_virtual_devices/queue.hppsrc/share/console_user_server_client.hppsrc/core/console_user_server/include/console_user_server/receiver.hppsend_user_command_handler.hpp, shell command handler, software function handlerWhen debugging a binding, split the question into stages:
to event type was emitted?This stage-by-stage approach avoids chasing the wrong layer.
Karabiner keybindings are not a single monolithic action. They are a pipeline:
Understanding that pipeline is the key to both reliability tuning and latency tuning.