🏡/repos/pqrs-org/

Karabiner-Elements

What is Karabiner-Elements?

Karabiner-Elements is a powerful keyboard customizer for macOS that intercepts keyboard events at the lowest possible level, transforms them according to user-defined rules, and injects the modified events back into the system via a virtual HID device. It enables complex remappings, application-specific shortcuts, and advanced keyboard workflows.

High-Level Architecture

┌─────────────────────────────────────────────────────────────────┐
│                    User Applications                             │
│           (SettingsWindow, EventViewer, Menu, etc.)              │
└────────────────────────────┬────────────────────────────────────┘
                             │ LaunchAgents
┌────────────────────────────▼────────────────────────────────────┐
│        karabiner_console_user_server (user privilege)           │
│  • Monitors frontmost app, input source, system prefs           │
│  • Executes shell_command, software_function                    │
│  • Watches configuration files (~/.config/karabiner/)           │
└────────────────────────────┬────────────────────────────────────┘
                             │ Unix datagram socket
┌────────────────────────────▼────────────────────────────────────┐
│      Karabiner-Core-Service (root privilege / LaunchDaemon)     │
│  • Event grabbing (IOKit device seizure)                        │
│  • Event manipulation pipeline                                  │
│  • Virtual HID device injection                                 │
└────────────────┬───────────────────┬────────────────────────────┘
                 │                   │
    ┌────────────▼────┐    ┌─────────▼─────────┐
    │ Physical Devices │    │ Virtual HID Device │
    │  (IOKit seizure) │    │   (DriverKit)      │
    └─────────────────┘    └───────────────────┘

Runtime Topology

Karabiner-Core-Service

root (LaunchDaemon)

The heart of Karabiner. Owns device access and event modification. Exposes Unix domain sockets and talks to the DriverKit virtual HID device.

src/core/CoreService/src/main.cpp
  • IOKit device seizure (kIOHIDOptionsTypeSeizeDevice)
  • Event manipulation pipeline
  • Virtual HID report generation
  • Socket-based IPC with user processes

karabiner_session_monitor

setuid root

Determines the current console user via CGSessionCopyCurrentDictionary and informs Core Service which UID should own its socket.

src/core/session_monitor/src/main.cpp
  • Console user detection
  • Socket ownership handoff
  • Fast user switching support

karabiner_console_user_server

user (LaunchAgent)

Bridge to user context. Activates virtual HID driver, registers agents, watches configuration, and forwards environment changes to Core Service.

src/core/console_user_server/src/main.cpp
  • Virtual HID driver activation
  • Configuration file monitoring
  • Frontmost app / input source tracking
  • shell_command execution

Virtual HID Device

System Extension

DriverKit-based virtual keyboard/mouse that produces HID reports macOS treats as real hardware input.

vendor/Karabiner-DriverKit-VirtualHIDDevice/
  • Virtual keyboard reports
  • Virtual pointing device reports
  • Transparent to macOS
  • Special keys work correctly

Event Capture Mechanisms

📡

IOKit Device Seizure

Primary capture path. Opens HID devices with kIOHIDOptionsTypeSeizeDevice flag, giving Karabiner exclusive access to keyboard/mouse events.

// device_grabber.hpp
hid_manager_ = std::make_unique<
  pqrs::osx::iokit_hid_manager>(
    weak_dispatcher_,
    run_loop_thread,
    matching_dictionaries,
    std::chrono::milliseconds(1000)
);

// Each device opened with:
IOHIDDeviceOpen(device,
  kIOHIDOptionsTypeSeizeDevice);
Grabbed devices:
  • Keyboards (generic_desktop::keyboard)
  • Mice and Pointers
  • Joysticks/Gamepads
  • Consumer devices (headsets, buttons)
👆

CGEventTap (Trackpad)

Apple trackpad drivers bypass IOKit, requiring a separate CGEventTap in listen-only mode to capture trackpad events.

// event_tap_monitor.hpp
event_tap_ = CGEventTapCreate(
  kCGHIDEventTap,
  kCGTailAppendEventTap,
  kCGEventTapOptionListenOnly,
  mask,
  static_callback,
  this
);
Tracked events:
  • Mouse down/up (all buttons)
  • Mouse motion and dragging
  • Scroll wheel events
  • Modifier flags changed

Event Processing Pipeline

Events flow through a deterministic queue-based transformation chain. Each stage processes events in timestamp order, ensuring consistent behavior.

1
merged_input_event_queue
Raw HID events from all grabbed devices merged into single queue
2
simple_modifications_manipulator_manager
Per-device key remaps (caps_lock → control, etc.)
3
complex_modifications_manipulator_manager
Rules from karabiner.json with conditions, variables, multi-step actions
4
fn_function_keys_manipulator_manager
Rewrites media/Fn-layer keys according to profile settings
5
post_event_to_virtual_devices
Converts events to HID reports sent to virtual keyboard/mouse

Event Types

Physical Events

  • momentary_switch_event (keyboard keys)
  • pointing_motion (mouse movement)
  • pointing_button (mouse buttons)

Virtual Events

  • shell_command
  • select_input_source
  • set_variable
  • software_function
  • sticky_modifier

Passive Events

  • device_grabbed / ungrabbed
  • caps_lock_state_changed
  • frontmost_application_changed
  • input_source_changed

Manipulator System

Basic Manipulator

The workhorse of Karabiner. Handles key-to-key remapping with advanced features like "to_if_alone" and "to_if_held_down".

{
  "type": "basic",
  "from": {
    "key_code": "caps_lock",
    "modifiers": { "optional": ["any"] }
  },
  "to": [
    { "key_code": "left_control" }
  ],
  "to_if_alone": [
    { "key_code": "escape" }
  ],
  "conditions": [
    {
      "type": "frontmost_application_if",
      "bundle_identifiers": ["^com\.apple\.Terminal$"]
    }
  ]
}

Advanced Features

to_if_alone
Post alternative event if key pressed in isolation (e.g., caps → escape when tapped, control when held)
to_after_key_up
Post additional events after physical key release
to_if_held_down
Different behavior for held keys with configurable threshold
to_delayed_action
Delay action execution (useful for hold-to-modify patterns)
simultaneous
Trigger when multiple keys pressed together within threshold

Conditions System

Sophisticated filtering allows rules to activate only in specific contexts.

device_if / device_unless

Filter by vendor/product ID, device type

"vendor_id": 1452, "product_id": 832
frontmost_application_if

App-specific rules using bundle identifiers

"bundle_identifiers": ["^com\.apple\.Terminal$"]
input_source_if

Language/keyboard layout specific rules

"input_sources": [{ "language": "en" }]
variable_if

Custom variables set by set_variable events

"name": "vim_mode", "value": 1
keyboard_type_if

Profile-specific keyboard types

"keyboard_types": ["ansi", "iso"]
expression_if

Complex boolean expressions

"expression": "vim_mode == 1 && shift_held"

Inter-Process Communication

Core ⇄ Console User Server

Transport: Unix datagram socket
Format: MsgPack-encoded JSON
Path: /Library/Application Support/org.pqrs/tmp/krbn_core_service/*.sock
// Message types (operation_type)
  • • connect_console_user_server
  • • frontmost_application_changed
  • • input_source_changed
  • • system_preferences_updated
  • • temporarily_ignore_all_devices

Core ⇐ Session Monitor

Purpose: Deliver console UID
Path: /Library/Application Support/org.pqrs/tmp/rootonly/krbn_session/*.sock
// session_monitor_receiver
// Receives console UID
// Core service uses to chown
// main socket for correct user

Virtual HID Injection

Instead of using CGEventPost, Karabiner streams events to a DriverKit virtual HID device. macOS consumes these reports as if they came from real hardware, ensuring special keys (Mission Control, Launchpad, etc.) work correctly.

// post_event_to_virtual_devices.hpp
1. Front event from input queue
2. Convert to HID report:
   • momentary_switch_event → keyboard report (key codes + modifiers)
   • pointing_motion → pointing device report (x/y/buttons)
3. Send via virtual_hid_device_service_client_
4. macOS processes as real hardware input
✓ Why Virtual HID?
  • • Special keys work (Mission Control, etc.)
  • • Input Monitoring rules satisfied
  • • No API limitations
  • • Transparent to applications
State Management
  • • Monitor driver activation
  • • Handle version mismatches
  • • Recovery from connection loss
  • • Re-grab devices on reconnect

Configuration System

Configuration Structure

// ~/.config/karabiner/karabiner.json
{
  "global": {
    "check_for_updates_on_startup": true,
    "show_in_menu_bar": true
  },
  "profiles": [{
    "name": "Default",
    "selected": true,
    "simple_modifications": [...],
    "complex_modifications": {
      "rules": [...],
      "parameters": {
        "basic.to_if_alone_timeout_milliseconds": 800
      }
    },
    "devices": [{
      "identifiers": {
        "vendor_id": 1234,
        "product_id": 5678
      },
      "simple_modifications": [...]
    }]
  }]
}

Hot Reload

Configuration changes are detected immediately and applied without restart.

// configuration_monitor.hpp
1. FSEvents watches karabiner.json
2. On modification:
   → Parse new configuration
   → Send to Core Service via socket
3. Core Service:
   → Invalidate manipulators
   → Rebuild pipeline from JSON
   → Continue processing
Config locations:
  • ~/.config/karabiner/karabiner.json
  • ~/.local/share/karabiner/assets/complex_modifications/

Startup & Permissions Sequence

1
System Start
LaunchDaemon starts Karabiner-Core-Service as root
2
Input Monitoring Check
Calls IOHIDRequestAccess and waits for user approval in Security & Privacy
3
Session Monitor Launch
Determines console user via CGSessionCopyCurrentDictionary, notifies Core Service
4
Console User Server Start
Activates virtual HID driver, registers agents (menu, notifications), connects to Core Service
5
Device Grabbing
Core Service enumerates IOKit devices, seizes keyboards/mice, begins event capture

Example: caps_lock → control

Capture
Physical caps_lock pressed → IOHIDQueue callback → HID value (0x39) converted to momentary_switch_event → merged_input_event_queue
Simple Modifications
Manipulator matches caps_lock → control rule → Creates momentary_switch_event(left_control, key_down) → simple_modifications_applied_event_queue
Complex Modifications
No matching rules → Event passes through unchanged
Fn Keys
Not an Fn key → Event passes through
Virtual HID Injection
Convert to HID keyboard report: modifiers = LEFT_CONTROL → Send via virtual_hid_device_service_client_ → macOS sees control key pressed

Security Considerations

Root Privilege Justification

  • kIOHIDOptionsTypeSeizeDevice requires root
  • Socket ownership managed per-user
  • Session monitor prevents privilege escalation

Code Signing Verification

  • EventViewer connections verified via team ID
  • Prevents non-Karabiner apps from manipulating state

Input Monitoring Permission

  • Requires explicit user grant in macOS settings
  • Checked at startup and on reconnection

Virtual HID Driver

  • System extension signed and notarized
  • Loaded only after DriverKit activation

Source Code Structure

/src
├── apps/                          # User-facing applications
│   ├── SettingsWindow/           # Configuration UI
│   ├── EventViewer/              # Event debugging tool
│   ├── Menu/                     # Menu bar app
│   └── MultitouchExtension/      # Trackpad gesture handler
├── core/
│   ├── CoreService/              # Main daemon (root)
│   │   └── include/core_service/
│   │       ├── device_grabber.hpp          # IOKit seizure
│   │       ├── receiver.hpp                # IPC handler
│   │       └── device_grabber_details/     # Per-device manipulators
│   ├── console_user_server/      # User context bridge
│   └── session_monitor/          # Console user detector
└── share/                        # Shared libraries
    ├── event_queue/              # Event pipeline queues
    ├── manipulator/              # Transformation system
    │   ├── manipulators/
    │   │   ├── basic/            # Simple key remapping
    │   │   ├── mouse_basic/      # Mouse manipulation
    │   │   └── post_event_to_virtual_devices/
    │   └── conditions/           # Filtering predicates
    └── monitor/                  # File/app/session monitoring

Key Takeaways

System-level capture via IOKit device seizure and CGEventTap
Multi-process architecture with privilege separation
Deterministic queue-based processing through manipulator chain
Virtual HID injection for transparent event posting
Rich condition system for context-aware remapping
Hot-reloadable config without process restart