Skip to content

Tolerate Escape grab conflict on X11 listener - #107

Merged
kitlangton merged 2 commits into
anomalyco:mainfrom
prathamdby:main
Oct 4, 2026
Merged

kitlangton merged 2 commits into
anomalyco:mainfrom
prathamdby:main

Conversation

@prathamdby

Copy link
Copy Markdown
Contributor

Hey Anamoly team! I installed Hex on my machine, which runs on EndeavourOS, and I'm using i3wm with the ALT key as my $mod value and my Hex keybind set to CTRL + \, and while testing Hex, I ran into this problem below:

hex-escape-error

So, I asked my agent to fix it, and I tested the whole thing locally to ensure it works as you do expect. And now I'm able to use Hex flawlessly on my machine. So, I'm raising this PR in hopes of fixing it for anyone else who runs into the same situation. And I have also pasted a description of the entire PR made by my agent so you can understand the technical parts of it.

Description

  • The change makes the X11 Escape-cancel grab best-effort and keeps dictation running on conflict.
  • It treats only BadAccess as a grab conflict and keeps other grab errors fatal.
  • It adds classifier unit tests and documents the fix in recovery notes.
flowchart LR
  A["Hold hotkey Start"] --> B["Grab Escape for cancel"]
  B --> C{"Grab result"}
  C --> D["Success: enable Escape-cancel"]
  C --> E["BadAccess: warn, dictate without cancel"]
  C --> F["Other error: stop listener"]
Loading

Validation (CONTRIBUTING.md checklist, EndeavourOS i3/X11 host)

  • cargo fmt --check - clean
  • cargo test - 151 passed, 0 failed, 8 ignored (keyboard-layout test skips, macOS-only)
  • cargo clippy --all-targets --all-features -- -D warnings - clean
  • git diff --check - clean
  • Live check: held escape from a second X11 client; listener stayed Listening with no error; real dictation completed afterward

@prathamdby

Copy link
Copy Markdown
Contributor Author

Hey @kitlangton, I've fixed the failing CI; it was due to a difference in CI's Rust version and my local installed version, I updated on my end and then fixed the error that came, please let me know if you need anything else from me on this PR.

@kitlangton kitlangton left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for tracking this down, and for the live i3 check. Treating only BadAccess on the Escape grab as non-fatal is the right call, and the error-kind match plus the escape_grabbed bookkeeping look correct: a refused grab leaves nothing to ungrab.

A few things before this lands:

  • Make the degraded state visible. Right now the conflict only reaches the process log, so hex status and Settings still report a healthy Listening while Escape-cancel is gone. The Linux service is expected to surface recovery and errors to its clients. Could you add a non-fatal warning to the service snapshot (for example "Escape cancel unavailable: another application holds Escape") so hex status and Settings show it?
  • Log the conflict once per episode. The warning currently repeats on every dictation while the conflict lasts. A flag that resets after the next successful grab would keep the log readable.
  • Docs. 2.1.22 is already released, so "Fixed in 2.1.22 (unreleased)" should become "Fixed after 2.1.22". Please also drop the re-indented Sources/Checks lines (unrelated churn) and wrap the last paragraph like the rest of recovery.md.

Optional follow-up, not needed for this PR: because the grab uses AnyModifier, a window manager that holds only something like Mod4+Escape still costs us plain Escape. Falling back to grabbing bare Escape with just the lock-modifier variants (as the trigger key does) would keep cancel working in that case.

Two housekeeping notes: Linux CI ran on this branch and only failed on an unrelated Rust 1.99 lint that is now fixed on main, and the branch now conflicts with main, so it needs a rebase before the next run.

- Warn and continue without Escape-cancel on grab conflicts
- Keep non-conflict grab errors fatal with classifier tests
- Cover conflicted Escape dictation with ignored X11 test
@prathamdby

Copy link
Copy Markdown
Contributor Author

Thanks for tracking this down, and for the live i3 check. Treating only BadAccess on the Escape grab as non-fatal is the right call, and the error-kind match plus the escape_grabbed bookkeeping look correct: a refused grab leaves nothing to ungrab.

A few things before this lands:

  • Make the degraded state visible. Right now the conflict only reaches the process log, so hex status and Settings still report a healthy Listening while Escape-cancel is gone. The Linux service is expected to surface recovery and errors to its clients. Could you add a non-fatal warning to the service snapshot (for example "Escape cancel unavailable: another application holds Escape") so hex status and Settings show it?
  • Log the conflict once per episode. The warning currently repeats on every dictation while the conflict lasts. A flag that resets after the next successful grab would keep the log readable.
  • Docs. 2.1.22 is already released, so "Fixed in 2.1.22 (unreleased)" should become "Fixed after 2.1.22". Please also drop the re-indented Sources/Checks lines (unrelated churn) and wrap the last paragraph like the rest of recovery.md.

Optional follow-up, not needed for this PR: because the grab uses AnyModifier, a window manager that holds only something like Mod4+Escape still costs us plain Escape. Falling back to grabbing bare Escape with just the lock-modifier variants (as the trigger key does) would keep cancel working in that case.

Two housekeeping notes: Linux CI ran on this branch and only failed on an unrelated Rust 1.99 lint that is now fixed on main, and the branch now conflicts with main, so it needs a rebase before the next run.

Hey @kitlangton, I have made all the changes you have requested, and I have also compiled and tested this locally end-to-end. The escape error no longer happens, so I don't think that hex status is reproducible, but still I've made the appropriate changes so that if it does happen, it will show up on the hex status command, and yeah, that's all. This is a really cool product, and I'm really glad we got it working on i3. Written with Hex.

@kitlangton kitlangton left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this is great. The status warning, the once-per-episode logging, and especially the bare-Escape fallback with real X11 tests all look right. Merging.

@kitlangton
kitlangton merged commit 0207c12 into anomalyco:main Oct 4, 2026
2 of 4 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