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>
Simplified deploy steps — rsync directly to final paths using
sudo rsync on the remote instead of temp-file staging.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
publish.yml — triggered on push to main, two parallel jobs:
frontend:
- Builds Vite frontend (fnm + npm ci + npm run build)
- Rsyncs dist/ to gitea_ci@UI_HOST:UI_PATH/
- Rsyncs nginx config to UI_HOST, creates sites-enabled symlink,
runs nginx -t && systemctl reload nginx
backend:
- Builds release binary (cargo build --release -p ericrfb-proxy)
- Stops blekin.service on WS_HOST
- Rsyncs binary to WS_HOST:/usr/local/bin/ericrfb-proxy via sudo rsync
- Rsyncs systemd unit to WS_HOST:/etc/systemd/system/blekin.service
- Enables and starts the service
asset/nginx/blekin.kosherinata.internal.conf:
- Serves static frontend from UI_PATH
- Reverse proxies /api/ to frootmig:3000 with WebSocket upgrade
- 24h read/send timeouts for long-lived KVM sessions
asset/systemd/blekin.service:
- Runs ericrfb-proxy with BLEKIN_HOST=10.3.0.130
- Restart on failure with 5s backoff
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>
Three parallel jobs on the rust runner:
- cargo check --workspace
- rustfmt --check on all .rs files
- cargo clippy --workspace -- -D warnings
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cross-referenced every wire-format claim against rc-src and corrected
several errors that would have caused stream desync during implementation:
- rect header is 4×u16 + i32 (12 bytes), not varints + u8
- ping payload is i32 (4 bytes), not 1 byte
- PointerEvent is 8 bytes (includes trailing extra_u16)
- ServerInit is 19 bytes, not 16
- pixel-format struct is variable-length, not fixed 25 bytes
- string primitive uses modified-UTF-8, not ISO-8859-1
- bandwidth probe is read-only (no echo response)
- Phase 1 uses sync Read traits (async deferred to Phase 2)
- corrected all stale line-number references in ac.java/aw.java
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>