Skip to content

Unified keybind input — chords, mouse buttons, and wheel at every bind site - #35

Open
Amryu wants to merge 8 commits into
isomerc:masterfrom
Amryu:feature/keybind-rework
Open

Amryu wants to merge 8 commits into
isomerc:masterfrom
Amryu:feature/keybind-rework

Conversation

@Amryu

@Amryu Amryu commented Jun 22, 2026

Copy link
Copy Markdown

Reworks how every keybinding is set and what a binding can be — one widget, used at every bind site.

Input widget

  • Replaces the per-site key field + modifier dropdown with a single keybind chip used everywhere: per-character, forward, backward, and toggle-overlay. Left-click to record the next input, right-click to clear, with a tooltip.
  • A binding can now be a modifier chord (e.g. Ctrl+Shift+J), a mouse button, or a wheel notch — not just a bare key.
  • The per-character row collapses from a two-row (name + modifier dropdown + key) block to one compact line.

Model

  • New unified Hotkey type (modifier set + key/mouse/wheel trigger) replaces the old CharacterHotkey and the separate modifier_key / toggle_previews_modifier fields. Backward is now just its own chord (default Shift+forward) instead of a shared-key + modifier trick.

Linux runtime

  • Keyboard listener matches chords with exact-set modifier comparison.
  • Mouse listener fires mouse-button and wheel bindings, including wheel-to-cycle, and shares a held-modifier set with the keyboard listener so mouse+modifier chords resolve across the two evdev devices.

Windows runtime

  • Keyboard bindings: RegisterHotKey gets the full chord via an OR'd fsModifiers mask (adds Win); toggle-overlay registers from its Hotkey.
  • Mouse/wheel bindings: the low-level mouse hook resolves mouse-button and wheel triggers (with GetAsyncKeyState modifiers) against the configured cycle / toggle / per-character bindings and posts them to the listener; bindings refresh on every re-register so panel edits apply live. The legacy enable_mouse_buttons XBUTTON cycle remains as a fallback.
  • The trigger-matching logic lives in windows_helpers and is unit-tested on Linux.

Config compatibility

  • forward_key / backward_key / toggle_previews_key and [character_hotkeys."Name"] are now Hotkey tables (mods, kind, code). Pre-upgrade configs that stored a bare integer key code still load (the integer becomes a plain-key binding); the removed modifier_key / toggle_previews_modifier keys are ignored.

README updated for the new binding model.

Note

  • Built and tested on Linux. The Windows cfg path is migrated to pattern and reviewed by inspection, but wasn't compiled in this environment (no MinGW to cross-build a C dependency), so it should be smoke-tested on Windows. The pure trigger-matching/planning logic it relies on is covered by tests that do run.

Amryu added 8 commits June 22, 2026 19:09
Foundation for the keybind rework: a single Hotkey type carrying a set of
held modifiers plus a trigger that can be a keyboard key, a mouse button, or
a wheel notch — replacing the scattered single-key + optional-single-modifier
fields. Exact-set modifier matching (so Ctrl+J doesn't fire a bare-J bind),
sorted/deduped mods for stable serialization, and TOML round-trip. Wired into
the bind sites + UI in following commits.
Replace the per-character modifier dropdown + separate hotkey row with one
compact keybind chip (left-click to record, right-click to clear, tooltip).
Captures full modifier chords (Ctrl+Shift+J) into the unified Hotkey type;
the keyboard listener matches them with exact-set modifier comparison.
Mouse/wheel triggers and rollout to the other bind sites follow.
The keybind chip now records mouse buttons and wheel notches, with held
modifiers (Ctrl + Mouse4). A shared held-modifier set, written by the
keyboard listener and read by the mouse listener, makes mouse/wheel chords
work even though the two listeners poll separate evdev devices. The mouse
listener resolves and fires per-character mouse/wheel hotkeys (staying
attached for them even when side-button cycling is off). iced buttons map to
evdev BTN_* codes (Linux) / XBUTTON numbering (Windows).
Forward, backward and toggle-overlay now use the same keybind chip as the
per-character bindings — key chords, mouse buttons, or wheel notches — instead
of a key field plus a modifier dropdown. The separate modifier_key /
toggle_previews_modifier fields are gone: backward is just its own chord
(default Shift+forward) and each binding carries its own modifiers.

- config: forward_key / backward_key / toggle_previews_key are now Hotkey;
  modifier_key and toggle_previews_modifier removed.
- keyboard listener matches them via exact-set modifier comparison.
- mouse listener fires mouse/wheel-bound cycle, toggle and character hotkeys
  (wheel-to-cycle), sharing the held-modifier set; toggle flips show_previews.
- panel: chip + right-click-clear + tooltip at every bind site; dead modifier
  dropdown code removed.
- windows_helpers: plan_cycle_hotkeys / plan_character_hotkeys now take
  Hotkey, emit a modifier *chord* (Vec<ModifierKind>) per registration, and
  skip mouse/wheel and unbound bindings (the mouse hook handles those). Adds
  a Win modifier. Tests updated.
- windows_input: RegisterHotKey gets the full chord via an OR'd fsModifiers
  mask; toggle-overlay registers from its Hotkey; per-character unchanged in
  spirit. XBUTTON cycle path is untouched.
- config: drop the obsolete CharacterHotkey struct; daemon rebind signature
  follows the new fields.

Note: built and tested on Linux; the Windows cfg path is migrated to pattern
but not compiled here (no MinGW to cross-build ring). Windows mouse/wheel-bound
character/toggle hotkeys still need wiring into the low-level mouse hook.
- Keyboard Bindings: cycle bindings are now Hotkey tables (mods/kind/code);
  modifier_key is gone (backward is just its own chord).
- Document the keybind chip (left-click record, right-click clear) at every
  bind site, and mouse-button / wheel triggers incl. wheel-to-cycle.
- character_hotkeys TOML shape updated to the Hotkey fields.
forward_key / backward_key / toggle_previews_key changed from a bare key code
to a Hotkey table, which would make pre-upgrade config.toml files fail to
parse. Accept either form on read (a bare integer becomes a plain-key Hotkey);
removed fields like modifier_key are simply ignored. Saves rewrite in the new
format.
The low-level mouse hook now resolves mouse-button and wheel triggers against
the configured bindings (cycle, toggle, and per-character), not just the legacy
XBUTTON cycle:

- windows_helpers::resolve_mouse_action (unit-tested on Linux) maps a trigger +
  held modifiers to an action, mirroring the Linux mouse listener's priority.
- The hook reads held modifiers via GetAsyncKeyState, handles WM_XBUTTONDOWN
  and WM_MOUSEWHEEL, and posts cycle / toggle / character messages to the
  listener; bindings refresh on every (re)register so panel edits apply live.
- The legacy enable_mouse_buttons XBUTTON cycle remains as a fallback.

Closes the previous gap where mouse/wheel bindings were captured but inert on
Windows. Logic is tested on Linux; the Win32 glue is migrated to pattern but
not compiled here (no MinGW to cross-build ring).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant