feat: enter a client with a key bind instead of a screen edge - #482
Open
khac88 wants to merge 1 commit into
Open
feat: enter a client with a key bind instead of a screen edge#482khac88 wants to merge 1 commit into
khac88 wants to merge 1 commit into
Conversation
Adds optional `[enter_binds]`: key combinations that begin capture at a position without the pointer having to travel into the corresponding screen edge. A bind produces exactly the capture an edge crossing produces — the backend calls the same `start_capture()` and emits the same `CaptureEvent::Begin` — so everything downstream (CaptureTask, service, enter_hook, release_bind) works unchanged and there is no second notion of "entered". Consequently a bind only fires while capture is inactive, and only for a position that currently has an active client. Binds are keyed by position rather than by client because entering is position-based: crossing an edge enters every client at that edge. The matching lives in one backend-independent type (input-capture/src/enter_bind.rs) that both the macOS and Windows backends drive, with unit tests. `Capture::set_enter_binds` defaults to a no-op, so the Linux backends are untouched — the input capture portal does not deliver input while capture is inactive. Closes feschber#260
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.
Closes #260.
Adds optional
[enter_binds]: key combinations that begin capture at a positionwithout the pointer having to travel into the corresponding screen edge.
Motivation from the issue: people who navigate by keyboard don't want to reach for
the mouse just to change machines, edges get crossed by accident, and games that
capture the pointer make the edge unreachable.
Approach
A bind produces exactly the capture an edge crossing produces — the backend calls
the same
start_capture()and emits the sameCaptureEvent::Begin— so everythingdownstream (
CaptureTask, service,enter_hook,release_bind) works unchanged andthere is no second notion of "entered".
Consequences that fall out of that, rather than being special-cased:
release_bindis still what returnscontrol to the local machine
never enter a client an edge crossing could not
Binds are keyed by position, not by client, for the same reason: crossing an edge
enters every client at that edge, so a per-client bind would have been a per-position
bind wearing a disguise — with the added problem that the frontend's
save_configround-trip has no representation for it and would have had to reconstruct it.
The matching itself lives in one small, backend-independent type
(
input-capture/src/enter_bind.rs) that both backends drive, with unit testscovering: firing on the last key down, key order independence, never firing on
release, ignoring positions without a client, extra held keys not blocking the bind,
and the two ways stale held-keys could otherwise fire a bind (below).
Capture::set_enter_bindshas a default no-op implementation, so the Linux backendsare untouched and keep entering by edge only — the input capture portal doesn't
deliver input while capture is inactive, as noted in #260.
Notes for review
observed for the duration of a capture, so without this a key held when capture
began (and released during it, unseen) would still count as held afterwards, and
could complete a bind on its own later.
keys held. That mirrors
release_capture, which deliberately releases everythingbefore handing control back — clean slate in both directions. Carrying held
modifiers across the switch instead would be a separate change.
get_events()ratherthan reading the
CGEventseparately, so modifier bookkeeping stays continuousacross the idle → capturing transition instead of a modifier held at capture start
being re-reported as a fresh press.
window server for the real cursor position instead of reading it off the triggering
event;
start_capturenow takes aCGPointfor that reason. Windows usesGetCursorPosand seeds bothENTRY_POINTandPREV_POSso the first motiondelta after entering is ~0 rather than a jump.
Testing
cargo fmt --check,cargo clippy --workspace --all-targets --all-features -D warningsand
cargo test --workspace --all-featuresare clean on macOS, andcargo check/cargo clippyare clean forx86_64-pc-windows-msvc.macOS verified end-to-end against a Windows peer over Tailscale: pressing the bind
from the idle state enters the client exactly as an edge crossing does —
Confirmed on real hardware too: pressing the bind physically on the Mac hands input
to the Windows machine — moving the trackpad afterwards moves the remote pointer.
Entering by edge afterwards is unaffected and
release_bindstill hands controlback.
Note the pointer lands wherever it was left on the remote rather than at a matching
position, since
Entercarries no coordinates — same as an edge crossing, andorthogonal to this PR (cf. the
CursorPosproposal in feat(cursor-sync): CursorPos protocol + cursor warps without prior Bounds #429).Windows: implemented and compile/clippy-checked for
x86_64-pc-windows-msvc, butnot exercised at runtime — the hook path mirrors the macOS one and shares the tested
matcher, so a confirmation from someone running the Windows capture backend would be
welcome.
Possible follow-ups (deliberately not in this PR)
(today: release, then bind)
of people asked for in Feature: Add a keyboard shortcut to change to different screens #260
the wire, which is its own discussion (feat(cursor-sync): CursorPos protocol + cursor warps without prior Bounds #429), and binds inherit whatever edge
crossings end up doing