Third adversarial review pass over the client side.
Rect reassembly moves out of DisplayMirrorManagerImpl into
MirrorRectCompositor: the manager was carrying two unrelated state
machines and 14 methods. Every rect field is now validated before
anything is allocated (total_size is device-controlled), geometry is
latched from the chunk at offset 0 so a later chunk cannot transpose
the blit, continuations must match the buffer that was actually
allocated rather than their own claim, and bounds use the subtraction
form because rect_x + rect_width overflows for hostile values. A rect
that omits width or height covers the full panel in that axis.
remoteKeyInput no longer swallows modified keys, so Cmd+Q, Ctrl+C and
Alt+Tab stay with the desktop instead of being forwarded to the device.
MirrorFrameRenderer tracks colorized runs as a raw Int with a sentinel
rather than a boxed Color?, and MirrorFrame.hashCode includes the
format so a mono and an RGB565 frame of the same size cannot collide.
Drops mirror_view_only, orphaned since MUI gained remote input.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
DisplayMirrorManager reassembles RGB565 rect chunks (sequence, geometry,
and panel-bound validation) and composites them into a persistent
little-endian RGB565 canvas emitted per completed rect; the renderer gains
a true-color branch using the same run-length row drawing. Mono frames are
untouched.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Circular D-pad ring replaces the cross cluster (wedge hit-testing with a
radial dead band, hold-to-repeat, per-wedge semantics, haptics), keyboard
capture becomes an explicit chip with tap-to-toggle, and taps on touch
devices forward real panel coordinates (long-press = SELECT, matching the
physical touch driver). Manager gains reset() (wired to ViewModel creation
and disconnect), a palette region cap, and first-chunk geometry pinning;
renderer uses bit-replicated RGB565 and run-length row drawing with the
pixel resolver extracted pure and tested; KeyUp of captured keys is
consumed; ViewModel moved to its own file; all strings localized; the
stray .bak test file is gone.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
DisplayMirrorManager reassembles FromRadio.display_palette chunks
(signature-keyed, defaults captured on the offset-0 chunk) into a
MirrorPalette; frames carry palette_signature and the renderer resolves
each pixel through the region table (last overlapping region wins,
matching firmware precedence) with RGB565 conversion, falling back to
monochrome when the referenced palette hasn't arrived. Renderer split
into MirrorFrameRenderer.kt.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Manager: validate format (MONO_VLSB only) and geometry (width/height must
describe exactly total_size bytes) so malformed device input can't reach
aspectRatio and crash; unit tests for reassembly, drops, restarts.
AdminController: setDisplayMirror/sendInputEvent become non-suspend
immediate sends (teardown-safe, input never queues behind bulk admin) and
gain requestDisplayFrame. Mirror tab: render each frame once into a 1:1
ImageBitmap scaled with FilterQuality.None (kills the fractional-scale
seams), gate controls on connection state, reset the toggle on disconnect,
stop the stream on tab dispose and ViewModel clear, add a one-shot Refresh,
inject AdminController/ConnectionStateProvider instead of RadioController.
Dispatch: display_frame test coverage incl. revoked-session.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Consumes the DisplayFrame PoC protobufs (2.99.0-screen-mirror-poc-SNAPSHOT
from mavenLocal, useMavenLocal committed on this branch): DisplayMirrorManager
reassembles FromRadio.display_frame chunks into MirrorFrame snapshots,
AdminController gains setDisplayMirror + sendInputEvent, and the Debug panel
gains a Mirror tab rendering the device's 1bpp framebuffer live with a D-pad
driving the firmware InputBroker. PoC: tab strings deliberately unlocalized.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>