Refusing Caps, Num and Scroll Lock retired all remote input, so a controller using Caps Lock as its screen-reader modifier lost keyboard control on the first press. Lock keys are now owned like other keys and released once at retirement without another toggle. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
129 lines
7.3 KiB
Markdown
129 lines
7.3 KiB
Markdown
# Quarantined Remote destination-key prerequisite
|
|
|
|
The reader has a destination-key ledger and an unconnected Xorg backend.
|
|
`internal/input/remote_key_ledger.go` owns at most 32 native destination codes;
|
|
`internal/platform/xorg/remote_keys.go` resolves and applies the supported
|
|
logical keys on a dedicated X connection. The [source-built helper](remote-input-helper.md)
|
|
and private fixtures construct this target; no reader owner launches that helper
|
|
or attaches `remote.key`. The Remote
|
|
command still discards relay keyboard traffic and offers no keyboard grant.
|
|
|
|
This is a native prerequisite, not a usable input service. It must remain
|
|
quarantined until production helper supervision, input provenance and physical
|
|
collision handling are established. The source helper now demonstrates
|
|
responsive-server EOF/watchdog cleanup in private process tests.
|
|
|
|
## Ownership and cleanup
|
|
|
|
One ledger belongs to one serial native owner and helper lifetime. Its caller
|
|
must validate the original lease immediately before each effect, retire on
|
|
cancellation, and finish cleanup before allowing replacement input. The ledger
|
|
itself neither issues consent nor checks lease epochs. It must not be shared
|
|
between leases or mutated concurrently.
|
|
|
|
A new logical down resolves one destination code and rejects invalid codes,
|
|
another owned name resolving to that code, or an already-held server key. It
|
|
reserves the code before attempting the down, because a native failure can leave
|
|
the outcome uncertain. Duplicate downs are idempotent and perform no additional
|
|
injection; native repeat fidelity remains unfinished. An unowned up performs
|
|
no native operation. An owned up uses its reserved code without resolving the
|
|
name again, including after layout/Num Lock changes.
|
|
|
|
Mapping failure, collision, capacity overflow or native failure stops admission.
|
|
`Retire` attempts every owned up in reverse down order. Successful ups are
|
|
removed; failed ups retain their exact code and original relative order for
|
|
explicit cleanup retry. Retirement never resumes admission. Backend errors
|
|
become constant local errors, without native diagnostic text or key names.
|
|
|
|
The native wrapper closes its connection only after successful ledger cleanup.
|
|
Failed cleanup leaves the connection and pending identities available for retry.
|
|
Opening replacement input must wait for confirmed old cleanup; a new consent
|
|
epoch does not make an old native release harmless. Closing an XTEST connection
|
|
or killing this process does not release held keys. The earlier
|
|
[Python feasibility harness](remote-input-feasibility.md) demonstrates that
|
|
limitation. The ledger alone has no EOF/watchdog mechanism; the separate Go
|
|
helper now supplies that process-lifetime owner. Helper death and blocked-server
|
|
recovery remain unresolved.
|
|
|
|
## Native resolution and limits
|
|
|
|
The logical-key vocabulary now belongs to `input.ValidRemoteKeyName`; the wire
|
|
and Go SDK validators delegate to it, preserving the existing IPC schema and
|
|
Python conformance. Native input therefore uses the same bounded names as the
|
|
public SDK, without depending on the Tentacle wire package.
|
|
|
|
The backend fetches a fresh XKB map/state for each new resolution and requires
|
|
one unambiguous matching destination code. It supports group zero only;
|
|
nonzero groups are unavailable. Shift/Caps state is removed for logical base-key
|
|
resolution while Num Lock is retained. Shift-only punctuation, unavailable or
|
|
ambiguous symbols are rejected. It does not synthesize modifiers, assume a
|
|
Windows scan code equals an X code, or rewrite the global keymap. The private
|
|
fixture changes maps and lock state explicitly to exercise these cases.
|
|
See the [XKB library specification](https://xorg.freedesktop.org/archive/current/doc/libX11/XKB/xkblib.html)
|
|
for `XkbGetMap`, `XkbGetState` and `XkbTranslateKeyCode`.
|
|
|
|
(Superseded: see Lock keys below.) Lock-key downs were unavailable until lock synchronization and reader modifier
|
|
handling have a defined policy. This includes Caps Lock even when a reader
|
|
configuration would otherwise use it as a modifier. AltGr, IME/dead keys,
|
|
general group/level handling and exact remote autorepeat remain unfinished.
|
|
|
|
The already-held-key check is not atomic with injection. A later physical or
|
|
other-client press of an owned code is not separately represented by the global
|
|
bitmap. This backend cannot preserve that press during cleanup. Fresh map reads
|
|
also do not freeze configuration changes by other clients. No physical-device,
|
|
concurrent-map-change or same-code collision guarantee is claimed.
|
|
|
|
All native wrapper methods require one serial caller on a locked OS thread.
|
|
Xlib calls and the error guard can block or terminate a process after X-server
|
|
failure. The source-built helper now provides independent execution, bounded
|
|
inherited-descriptor IPC and watchdog/EOF cleanup. Production launch must preserve
|
|
exclusive ownership, provenance, policy cancellation and explicit failure
|
|
reporting. Do not attach this synchronous wrapper to the reader event loop or
|
|
directly to its mailbox.
|
|
|
|
## Private verification
|
|
|
|
From the reader checkout:
|
|
|
|
```sh
|
|
go test -race ./internal/input ./internal/tentaclewire -count=1
|
|
python3 -B tests/native_remote_keys.py
|
|
python3 -B tests/native_remote_input.py
|
|
```
|
|
|
|
The new harness requires Go, Python, Xvfb, private D-Bus and `setxkbmap`. It starts
|
|
a 24-bit Xvfb server before private D-Bus using `native_test_session.py`, with a
|
|
disposable runtime directory. It never uses the active reader, display or
|
|
accessibility bus. Missing dependencies fail the harness rather than count as
|
|
skipped acceptance. The native tests skip in ordinary unflagged Go suites;
|
|
only an actual harness run establishes their private results.
|
|
|
|
The harness now builds and runs the source helper and tests its private process
|
|
lifetime in addition to the destination target; see the linked helper contract.
|
|
Unit tests cover stored-code releases, idempotent downs, unowned ups, alias and
|
|
preexisting collisions, constant errors, held-key limits, uncertain down/up
|
|
outcomes, partial cleanup and retry order. Six new native Go race tests verify
|
|
owned modifier cleanup, preservation of an already-held key from another
|
|
synthetic client, US/German map replacement, Num Lock changes, unsupported-group
|
|
and shift-only-symbol rejection, and lock-key refusal. They inspect server key
|
|
state, not application command delivery or hardware behavior.
|
|
|
|
The new native harness, existing ten-experiment/six-native-test feasibility
|
|
harness, input/wire/Xorg race suites, focused host keyboard/lifecycle race tests,
|
|
external Remote race suite, affected-package vet, a separate reader build,
|
|
the existing speech regression harness and Tentacles `make check` passed.
|
|
No full reader suite, installed-binary behavior,
|
|
audible speech, physical input/Braille or live NVDA/Orca acceptance was performed.
|
|
|
|
## Lock keys (2026-11-06)
|
|
|
|
Caps Lock, Num Lock and Scroll Lock are now owned like other keys instead of
|
|
being refused. Refusal retired all remote input, so a controller using Caps
|
|
Lock as its NVDA or Cthulhu modifier lost keyboard control on the first press.
|
|
A remote press and release toggles the lock here, as the same physical key
|
|
would. Retirement releases a held lock key once and never re-toggles a lock;
|
|
when Caps Lock is the controlled reader's Cthulhu modifier, the command owner
|
|
keeps its local lock state. A private Xvfb test checks that each lock key is
|
|
owned and released at retirement. Lock-state synchronization between computers
|
|
is not attempted.
|