feat: add function key remapper#344
Draft
MichaelDanCurtis wants to merge 16 commits into
Draft
Conversation
Add an approved design for emitting mouse buttons 6–9 as pickable actions. Approach A — four unit variants mirroring MouseBack/ MouseForward — surfaced through the existing data-driven action stack (catalog/category/picker/inject). macOS and Linux get full support; Windows is a documented log-and-skip gap (SendInput caps at button 5).
Turn every capturable function-row key into a fully programmable trigger. Execution half (all buildable): reassign media keys, type macro strings, run AppleScript, run shell commands, run timed multi-step workflows. Capture half splits by risk — F1-F12 extend the existing CGEventTap (low, F1 proven); media keys need an NX_SYSDEFINED system-event tap (high, gated milestone M3, feasibility-untested). Three milestones: M1 F-key capture + action palette, M2 native Workflow sequencer, M3 media-key capture (gated on its own feasibility test). Includes Appendix A documenting the empirical finding that the Fn modifier is not usable as a trigger (flag attaches only to function-row keys).
Seven tasks: KeyEvent vocab + widened callback, macOS keyboard capture, three power-user Action variants, [keyboard] config + KeyTrigger parser, post_unicode primitive + execution arms, hook→binding dispatch, and hardware verification. Press-to-bind UI deferred to a later M1.x (flagged honestly). M2 Workflow and M3 media-key capture are separate plans.
Adds KeyEvent + KeyModifiers + HookEvent vocabulary alongside MouseEvent. Hook::start's callback (and all three platform start fns) now receive HookEvent; every callback invocation wraps MouseEvent in HookEvent::Mouse. hook_runtime wraps its existing MouseEvent body and passes keys through inertly. No behavior change — keyboard capture lands in the next task. All hook + agent-core tests pass (33 + agent-core).
Adds KeyDown/KeyUp/FlagsChanged to the tap mask, plus translate_key() which maps a key CGEvent to our KeyEvent (keycode from KEYBOARD_EVENT_KEYCODE, press state from the event type, detectable modifiers from the flags — SecondaryFn intentionally ignored as a firmware-internal, unreliable trigger). The callback now builds a HookEvent from whichever path (mouse or key) translates the event. F1-F12/Esc are now observed by the tap; nothing acts on them yet (hook_runtime passes them through inertly until Task 6 wires bindings).
Three power-user escape-hatch actions (excluded from the default catalog, classed as Editing). RunAppleScript spawns osascript, RunShellCommand spawns /bin/sh — both off the tap thread so a slow script can't wedge input. TypeText is a temporary no-op pending the post_unicode primitive (next task). Adds label/category/catalog wiring + roundtrip tests. 86 core tests + inject tests pass.
KeyTrigger parses '[mod+]+key' strings (f1, shift+cmd+f5, esc) into a keycode + modifier mask using the macOS F-key virtual keycodes. It serializes as its string form (Display) so it can be a TOML map key — [keyboard.bindings] keys are 'f1', 'shift+f2', etc. KeyModifiers is duplicated in core (leaf-level, no hook dep) and converted at the agent boundary in the wiring task. Modifier aliases: ctrl/alt/cmd. 35 config tests pass (incl. parse, reject-unknown, and full TOML roundtrip through Config); 89 core tests total.
post_unicode types a string one char at a time via a keyboard event whose unicode payload is set with CGEventKeyboardSetUnicodeString (layout-independent — the typed char comes from the string, not the keycode). TypeText now calls it instead of the temporary no-op. Run* actions were already wired in the previous task. inject builds + tests pass.
A key-down whose keycode+modifiers match a [keyboard.bindings] entry fires its action and suppresses the original key (so it doesn't also type / trigger its native function); unmatched keys pass through untouched. Key-up is ignored to avoid double-firing. SharedKeyboardBindings is seeded from config.keyboard.bindings in Orchestrator::new (single shared map — not per-app in M1). The hook- layer KeyModifiers is converted to the config-layer type at the agent boundary (core stays leaf-level). 33 agent-core tests pass. Full agent builds.
Adds Action::Workflow(Vec<WorkflowStep>) + the WorkflowStep enum (TypeText / PressKey / Delay / RunAppleScript / RunShellCommand). The sequencer (run_workflow) runs steps in order on a dedicated worker thread, awaiting Delay(millis) sleeps between them — the 'type, wait, Enter, wait, type, Escape' no-code macro flows from the spec. PressKey reuses KeyCombo via a new macos::post_keycombo helper (which mirrors the CustomShortcut flag construction, so the two never drift). Workflow spawns off the tap thread like the Run actions, so blocking delays never stall the event tap. Excluded from the default catalog (power-user escape hatch). label/category + roundtrip tests pass (91 core tests).
Contributor
|
Heads up @MichaelDanCurtis — #191 also touches |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
org.openlogi.openlogi.dev/org.openlogi.agent.devbefore signing, and route those builds through theopenlogi-devXDG profile so dev agents cannot occupy the production socket, lock, config, or asset cachemacos packageremains the production-ID DMG pathVerification
cargo fmt --checkgit diff --checkcargo check -p xtaskcargo test -p openlogi-agent-core --test wire_formatcargo test -p openlogi-guicargo test --workspacecargo run -p xtask -- macos bundleKeys,Lighting, andDevice, but notButtonsOpenLogi Dev/OpenLogi Agent Devwith.devbundle identifiersApple Development: michaeldancurtis@gmail.com (P7R2A675CN)and passcodesign --verify --stricttarget/release/bundle/dmg/OpenLogi.dmgis removed aftermacos bundle, avoiding the pre-helper cargo-bundle artifact/ApplicationsGUI + local helper state, launched the exact rebuilt dev app/helper path, and verified both running processes come fromtarget/release/bundle/osx/OpenLogi.app~/.config/openlogi-dev/agent.sock, the GUI process reportsorg.openlogi.openlogi.dev, TCC has an allowedorg.openlogi.agent.devrow, and the visible app shows Accessibility granted instead of the background-service failure