Add Remote controller capture IPC to the host and both SDKs

remote.focus lets a controller keyboard-capture lease ask core to enter
or leave remote focus; the reply acknowledges admission only. Core
reports the actual state with remote.focus events and forwards routed
transitions as remote.input events carrying the source keycode and, on
fresh presses, a canonical key name. A full focus mailbox revokes the
lease; revocation drains pending focus work. No native capture owner is
attached yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Chase CichorzandClaude Opus 5.5 committed 2026-11-06 16:06:38 -05:00
1 parent 27017bef11
commit 2d5327adf4
10 files changed
+583 -10

No files matched your search

+50
View File
@@ -0,0 +1,50 @@
# Remote controller keyboard capture IPC
Protocol 1.1 now carries controller keyboard capture between the reader and a
Remote Tentacle. This document describes the wire contract and host checks;
native capture is described in the following sections as it is implemented.
## Wire contract
Child-to-host `remote.focus` asks core to enter or leave remote focus:
```json
{"lease":"0123456789abcdef0123456789abcdef","epoch":1,"sequence":3,"remote":true}
```
All four fields are required. It needs the current process session's live
controller lease with the separately approved `keyboard-capture` grant and
shares the strictly increasing operation sequence. The reply `{}` acknowledges
admission only. Without an attached reader capture owner it returns
owner-unavailable. A full four-request mailbox revokes the lease, so a lost
request to return to local focus can never leave capture active.
Host-to-child `remote.focus` events report the actual state, `focus` being
`local` or `remote` and `reason` one of `requested`, `escape`, `local` (reader
policy such as lock, handoff or reader UI) or `unavailable`. A child request
never proves that capture is physically active.
Host-to-child `remote.input` events carry one normalized, routed transition:
```json
{"code":38,"name":"a","pressed":true}
{"code":38,"pressed":true,"repeat":true}
{"code":38,"pressed":false}
```
`code` is the source Xorg keycode (8–255), used only to pair repeats and
releases. `name` is a canonical unshifted key name from
`input.ValidRemoteKeyName` and is present only on a fresh press; releases and
repeats omit it so a layout change cannot strand a key. A full event queue
fails the session rather than losing a release.
Reader code attaches the mailbox with `Session.EnableRemoteCapture` and
publishes with `PublishRemoteInput` and `PublishRemoteFocus`; both require the
live controller `keyboard-capture` lease. Go exposes `RemoteCapturedKey`,
`RemoteFocusRequest` and validators through `sdk/go/tentacle`; Python exposes
`valid_remote_captured_key` and `valid_remote_focus_request` and validates both
event kinds. Revocation drains pending focus work.
Go/Python SDK-process tests cover owner absence, admission and replay,
role/grant denial, invalid parameters, saturation revocation, event delivery
and rejection after revocation.