OPEN and RESIZE carried a hardcoded 80x24, so the remote shell wrapped to
a width the phone does not have. The viewport is now measured in monospace
cells and the session waits for that measurement, so OPEN carries the real
size - 'stty size' on a medium_phone emulator reports 29 54.
Output sits in a SelectionContainer, so a command's output can be copied
out instead of retyped.
Three gaps the rig made obvious:
- The ViewModel tracked a full session lifecycle that nothing rendered, so
a refused or stalled session was indistinguishable from a working one -
both are a blank screen. A status bar now reports anything that is not
OPEN and offers Reconnect.
- Pending input was its own list row, putting the caret under the prompt
rather than after it, which reads as a hung terminal.
- No way to send Ctrl-C, Tab, Esc or arrows. A key bar sends them, and
typeControlSequence() bypasses the keystroke debounce so an interrupt is
not queued behind a 500 ms batch window.
The input sink moved into its own composable with hoisted callbacks.
Verified against meshtasticd: 'uname -s' echoes exactly and returns Linux;
Ctrl-C interrupts and returns a fresh prompt.
Three things stood between the April branch and a working session:
- The CRT theatrics are gone - scanlines, phosphor presets, flicker, and
the AGSL curvature shader, which rendered the block cursor as a skewed
amber quad in the corner. The terminal is now monospace text on the app
theme.
- Turn-taking ('talking stick', RemoteShell.flags) landed on the firmware
side after this branch was written. DMShellModule marks every session
turn-managed and withholds output until the peer grants a turn, so a
client that never grants sees an open session and no prompt, and the
device re-sends RTS every 250 ms until its TX queue fills. We now grant
on every frame we originate and answer any request immediately.
- The invisible input sink passed a constant "" as its value, so Compose
re-delivered characters we had already consumed: 'whoami' reached the
PTY as 'wwhwhowhoa'. It now tracks what it consumed and forwards the
delta, mapping a shrinking field to backspaces.
Verified against meshtasticd: session opens, 'whoami' echoes exactly and
returns 'root'.
- license header year: the tree standardised on 2026, not 2025-2026
- spotless: ktlint's blank-line-between-when-conditions is new since
- MeshDataHandlerImpl: drop shouldBroadcast, removed with the AIDL
service rework (#5586); nothing broadcasts on this path any more
- RemoteShellViewModel: DataPacket.nodeNumToDefaultId and
DataPacket.PKC_CHANNEL_INDEX both moved to NodeAddress
Conflicts, all re-integration of the April branch into the current tree:
- MeshDataHandlerImpl/Test: keep main's constructor (ServiceScope, session
context, geofence/beacon collaborators) and re-add remoteShellHandler.
- Capabilities.kt: re-add supportsRemoteShell, still gated to UNRELEASED.
- strings.xml: main re-sorted the file; re-insert remote_shell and
phosphor_colour in their sorted positions.
- NodesNavigation/AdministrationSection: union both sides.