The mapping was built assuming keynr followed physical keyboard order
with Escape=0. In reality, KeyTranslator.java maps Java VK_* codes to
keynr values with a different layout:
- keynr 0 = Backquote (not Escape)
- keynr 59 = Escape
- keynr 27 = Enter (not 40)
- keynr 40 = Backslash (not 42)
This caused the number row and QWERTY row to be off by 1 (Escape
was inserted at position 0, pushing everything). The home row
(CapsLock=28, A=29...L=37) happened to align by coincidence, which
is why 'g'(33) worked but 'r'(should be 18, was 19=T) didn't.
Also fixes: F1-F12 off by 1, PrintScreen/ScrollLock/Pause off by 1,
numpad operator keys swapped with numpad 7/8/9.
Corrected both Rust (input.rs) and TypeScript (input.ts) mappings
to match the authoritative KeyTranslator.java VK→keynr table.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When pal_selector=0 (no predefined palette), the Java code reads two
separate full bytes for the 2-color palette (line 421-422 of
ByteColorRFBRenderer.java). We were reading one packed byte and
splitting into nibbles, consuming 1 byte too few and misaligning all
subsequent reads including the zlib compressed data.
This caused "deflate decompression error" on stream 1 because the
zlib header (78 da) was offset by 1 byte.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
codec/iip.rs:
- TileCache: (fb_width/16 × fb_height/16) tiles, 8 versions × 256 bytes
each, matching t.java's (8, 16*16) allocation
- TileEntry: versioned read/write at byte offsets within tile data
- decode_iip() handles all 4 modes from the control byte:
- Mode 0/12 (cache-read): tile control bytes select which cached
version of each 16x16 tile to display, no new pixel data on wire
- Mode 4 (write-only): Tight-decoded pixel data written to cache
- Mode 8 (update+read): conditionally writes new data to cache
(bit 7 of control byte = 0 means update), then reads from cache
- Tile control bytes compressed via zlib (varint length) when >= 12
- Sub-types 1-4/8 map to bit-depths 1/2/4/4/8 bpp
- Cache resized on framebuffer resize (ModeChange msg 128)
Wired into session dispatch for encoding 9. Not advertised in default
encoding list — only active if explicitly requested.
codec/tight.rs: made get_or_init() pub for IIP's zlib access.
39 tests passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Frontend reconnection:
- WebSocket auto-reconnects with exponential backoff (1s → 30s)
- Re-authenticates with OmniView to get fresh APPLET_ID on reconnect
- Credentials stored in sessionStorage for automatic re-login
- Status bar shows connection state and reconnect countdown
- Disconnect button returns to login screen
Encoding 10 (Raw with tile interleave):
- codec/raw_tile.rs: decodes encoding 10 per ByteColorRFBRenderer.for()
- Flag byte bit 0 selects plain raw vs 16x16 tile-interleaved data
- Deinterleave handles edge tiles smaller than 16x16
- Wired into session dispatch
- 2 unit tests
39 tests passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
codec/hextile.rs:
- Full Hextile (encoding 5) decoder per ByteColorRFBRenderer.int()
- Handles: Raw tiles, BackgroundSpecified, ForegroundSpecified,
AnySubrects, SubrectsColoured flags
- Background/foreground colors persist across tiles
- 4 unit tests covering all subencoding paths
framebuffer.rs:
- Added fill_rect() for Hextile background/subrect fills
session.rs:
- Wired Hextile encoding 5 into the rect dispatch
examples/record.rs:
- 30-second (configurable) recording session
- Saves 1 PNG per second to out/ directory
- Requests encodings [5, 1, 0] (Hextile, CopyRect, Raw)
- Tested against real OmniView: 10 frames in 10s, no errors
27 tests passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The OmniView defaults to 16bpp when no SetPixelFormat is sent.
The Java applet sends SetPixelFormat (msg type 0) at
ByteColorRFBRenderer.new() line 76 to request 8bpp RGB332
(bpp=8, depth=8, true_color, red_max=7, green_max=7, blue_max=3,
shifts 0/3/6). Without this, pixel data arrives as 16bpp pairs
that produce green stripe artifacts when interpreted as 8bpp.
Verified: snapshot now matches the reference screenshot from
http://10.3.0.130/screenshot.jpg (dark screen with "Free" label).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fixes CI fmt check failure — rustfmt wants multi-line assert_eq! for
long struct literals and the varint roundtrip assertion.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>