← Keyhac crftwr/keyhac on GitHub · craftware

Design overview

Why Keyhac 2 exists and the founding decisions behind it. This is the developer-facing design rationale; user documentation starts at ../../README.md.

What Keyhac is

Keyhac is a keyboard customization tool: it installs a system-wide keyboard hook and lets the user script every behavior in Python — key-to-key remapping, app-specific keymaps, multi-stroke keys, user-defined modifiers, one-shot modifiers, clipboard history, launcher/candidate windows, and keyboard macros. The user writes a config.py that defines configure(keymap).

Two independent implementations preceded Keyhac 2:

  keyhac-win (v1.83) keyhac-mac (v1.68)
Host / UI Thin C++ launcher; UI in Python on ckit (author’s Win32 C++ toolkit) Swift/SwiftUI menu-bar app; C++ PythonBridge embeds CPython
Input layer pyauto (author’s C++ extension): WH_KEYBOARD_LL, SendInput, Win32 window API Swift: CGEventTap, CGEventPost, AXUIElement
Python 3.14 embedded (PEP 587, isolated) 3.14 embedded (PEP 587, isolated)
Config API camelCase (defineWindowKeymap, InputKeyCommand, …) snake_case (define_keytable, ThreadedAction, …) — newer, cleaner
Feature set Larger (mouse, macro, balloons, themes, migemo, tray menu…) Smaller but modernized (UIElement/AX automation, focus paths, chooser)

Why Keyhac2

Keyhac2’s premise: everything that can be shared, is shared — one keymap engine, one config API, one UI codebase on PuiKit — and the per-OS surface shrinks to the smallest possible layer: the low-level hook, event injection, focus/window queries, and packaging.

Goals

  1. One repository, one Python codebase, producing a native app for Windows 10/11 (x64) and macOS 15+.
  2. One documented user-facing config API; a single config.py can run on both OSes.
  3. Feature parity with the union of keyhac-win and keyhac-mac (see design-notes.md).
  4. A pure-Python, fully unit-testable core (the old codebases were essentially untested).
  5. PuiKit becomes strictly better through the extensions Keyhac2 needs (tray, secondary windows, …), benefiting other PuiKit apps.

Non-goals

The founding questions, answered

Short answers; each links to the full analysis.

Q1. What languages do we use other than Python?

Almost none at runtime. Python 3.14 everywhere; Windows OS access via ctypes (as PuiKit’s Windows backend already proves viable, including COM), macOS via PyObjC (Quartz/ApplicationServices provide CGEventTap and AXUIElement). The only non-Python code is a tiny embedding launcher per OS (~150 lines of C/C++ using the PEP 587 PyConfig API, patterned on keyhac-win’s main.cpp and keyhac-mac’s PythonBridge.cpp) whose job is app identity + starting the interpreter. No ckit, no pyauto, no Swift app layer. → packaging.md

Q2. Which parts have to be OS-specific?

The list is short but deep: key hook & recovery, event injection & ordering, keycode tables/layouts, focus & window queries, window actions, clipboard monitoring, permissions, packaging. Notably, both hooks are synchronous consume-decisions (the “sync vs async” difference is real but lives in the surrounding machinery: macOS needs injected-vs-real event reordering and tap re-enable; Windows needs silent-unhook detection). → platform-layer.md

Q3. Can we use the same config.py format / user-facing APIs?

Yes for Keyhac2 going forward; no for keyhac-win compatibility. The two existing APIs already diverged (camelCase vs snake_case, defineWindowKeymap(exe_name=...) vs define_keytable(focus_path_pattern=...)). Keyhac2 adopts keyhac-mac’s API as the base — existing keyhac-mac configs should run nearly unchanged — and adds portable focus matching (app=, title=) plus the missing keyhac-win features. One config file can serve both OSes, with keymap.platform branches for genuinely OS-specific parts. → ../configuration.md

Q4. Do we have to extend PuiKit?

Yes — and it happened. PuiKit was “one backend = one standard resizable window as a regular app”. Keyhac2 needed: multiple simultaneous windows (console + chooser + balloon), frameless / always-on-top / no-activate popup styles, an agent-app mode (no Dock icon), a system tray icon / menu-bar extra, screen geometry queries, runtime window control (show/hide/move), and a call_later timer. All were added additively under PuiKit’s capability model and shipped in puikit ≥ 1.0.8. → puikit.md

Naming