Skip to content

feat(auto-input): add per-app keyboard mode for the top row - #487

Merged
ggbond268 merged 2 commits into
mactools-app:mainfrom
AndrewDi:feat/auto-input-keyboard-mode
Oct 20, 2026
Merged

ggbond268 merged 2 commits into
mactools-app:mainfrom
AndrewDi:feat/auto-input-keyboard-mode

Conversation

@AndrewDi

@AndrewDi AndrewDi commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Problem and result

Auto Input rules could only pick an input source, so anyone who keeps some apps (IDE, terminal, games) on standard function keys had to flip the global "Use F1, F2, etc. as standard function keys" setting by hand whenever they switched apps.

Each fixed rule can now also choose a keyboard mode: follow the system, use standard F1–F12 function keys, or keep the MacBook top-row shortcuts. The mode applies while the rule's app is frontmost, and the top row returns to the setting it had before when you switch away.

Changes

  • Added a three-value per-rule keyboardMode (system, standardFunctionKeys, mediaShortcuts). Rules saved by earlier versions decode as system, so existing stores keep working.
  • Added SystemAutoInputFunctionKeyController, which reads and writes the global com.apple.keyboard.fnState preference. macOS only re-reads that preference at login, so the controller also updates the live HIDFKeyMode value on IOHIDSystem through IOKit, the same mechanism System Settings uses, and the change takes effect at once.
  • AutoInputController applies the rule's mode on app activation and restores the previous top-row behavior on stop, when the plugin goes background, or when the frontmost app has no keyboard-mode rule. Application monitoring now also runs when only keyboard-mode rules exist.
  • The settings rule row shows the input source and keyboard mode pickers on one row; added an "输入法" accessibility label and five new localized strings in all 12 languages.
  • Root README overviews mention the per-app input source and top-row switching.

Permissions and compatibility: IOHIDSystem parameter access needs no extra entitlement and Auto Input is not sandboxed. No new workspace, catalog, or manifest fields.

Verification

  • make build-plugin PLUGIN=AutoInput
  • make test TEST_FILTER="AutoInputStoreTests AutoInputControllerTests AutoInputPluginPanelTests" — 23 tests pass, including new coverage for keyboard-mode persistence, decoding older rule payloads, and apply/restore behavior.
  • make script-tests — 232 tests pass, including changelog and 12-language localization validation.
  • Manual: on a MacBook the top row follows the rule while its app is frontmost, changes immediately when the mode is switched, and returns to the previous setting after switching away. The runtime state was also confirmed through the HIDFKeyMode property on IOHIDSystem.

UI evidence

The rule row now hosts both pickers on one line, so a narrow window truncates the app name before the controls. Screenshots are not attached in this PR yet; I can add light and dark captures of the updated rules row if maintainers want them.

Checklist

  • Core behavior and relevant risks are verified using existing tests, focused additions, or manual checks; results or limitations are recorded.
  • UI evidence is attached where applicable; shared components, themes, localization, and accessibility are respected.
  • User-facing documentation and changelog fragments are updated when required.
  • Plugin metadata, runtime contracts, action policies, and minimum-host compatibility agree.
  • Licensing and third-party notices are complete; no credentials, local configuration, or unrelated generated files are included.

Fixed rules can now choose a keyboard mode per app: follow the system,
use standard F1-F12 function keys, or keep the MacBook top-row
shortcuts. The mode applies while the rule's app is frontmost, and the
top row returns to its previous setting when you switch away.

Writing the preference alone is only re-read at login, so the new
controller also updates the live HIDFKeyMode value through IOKit to
apply the change immediately.
@ggbond268

Copy link
Copy Markdown
Collaborator

Thanks for adding per-app keyboard modes! I reproduced two issues that would be good to fix before merging:

  1. Preserve the user's latest system setting. If the user changes the keyboard mode in System Settings while no rule is active, then enters a rule-controlled app and switches away, the controller restores the older cached value. Please capture the current system setting when a rule first takes control, preserve it while switching between rule-controlled apps, and clear it after successful restoration.

  2. Keep failed writes retryable. The applied mode is recorded before the system write succeeds, and the restore state is cleared even if restoration fails. This prevents a later refresh from retrying correctly and can lose the original setting. Please update the applied state only after success, retain the original value when restoration fails, and allow a later retry with a brief user-visible error.

These affect the global keyboard setting, so the impact can extend to other apps. The existing 23 tests pass; three additional checks reproduce these cases using a mock controller, without changing real keyboard settings.

Capture the system function-key setting only when a rule first takes
control, keep it while switching between rule-controlled apps, and
clear it after a successful restore. This preserves a setting the user
changes in System Settings while no rule is active.

Record the applied mode only after the system write succeeds, keep the
original value when a restore fails so a later refresh can retry, and
surface a localized error until a retry succeeds.
@AndrewDi

Copy link
Copy Markdown
Contributor Author

Thanks for the review — both issues are fixed in 734297c.

  1. The original value is now captured only when a rule first takes control, kept while switching between rule-controlled apps, and cleared after a successful restore. A change made in System Settings while no rule is active is no longer overwritten; the next rule activation captures the new value.
  2. appliedStandardFunctionKeys is now assigned only after the write succeeds. A failed apply leaves no baseline (the next attempt captures a fresh one), and a failed restore keeps both the original value and the applied value so a later activation or refresh() retries. Failures publish keyboardModeError, which the panel row shows through a new localized error.keyboardModeFailed string (all 12 languages), and it clears once a retry succeeds.

Added four mock-controller tests: latest System Settings value after an unmanaged change, preserving the original value across two rule apps, retrying a failed apply, and retrying a failed restore.

Verification: make build-plugin PLUGIN=AutoInput, the AutoInput test classes (18 + 5 + 4 tests, all passing), make validate-localization, and make script-tests (232 tests).

@AndrewDi

Copy link
Copy Markdown
Contributor Author

@ggbond268 PTAL again

@ggbond268
ggbond268 merged commit 9bcc2bf into mactools-app:main Oct 20, 2026
2 checks passed
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.

2 participants