feat: phase 9 — encoding 9 (IIP) with tile-versioned delta cache
All checks were successful
CI / check (push) Successful in 1m22s
Publish / frontend (push) Successful in 43s
CI / fmt (push) Successful in 54s
CI / clippy (push) Successful in 1m40s
Publish / backend (push) Successful in 2m27s

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>
This commit is contained in:
2026-05-07 09:39:49 +03:00
parent e39555196d
commit acf99f849b
4 changed files with 285 additions and 3 deletions

View File

@@ -1,7 +1,7 @@
use std::io::{BufReader, BufWriter};
use std::net::TcpStream;
use crate::codec::{hextile, raw_tile, tight};
use crate::codec::{hextile, iip, raw_tile, tight};
use crate::framebuffer::Framebuffer;
use crate::handshake::{self, Config, ServerInit};
use crate::msg::{self, ServerMsg};
@@ -46,19 +46,23 @@ pub struct ActiveSession {
pub writer: BufWriter<TcpStream>,
pub server_init: ServerInit,
zlib: tight::ZlibStreams,
tile_cache: iip::TileCache,
}
impl ActiveSession {
/// Connect, handshake, send SetEncodings + initial FBUpdateRequest.
pub fn connect(cfg: &Config, encodings: &[i32]) -> Result<Self, SessionError> {
let raw = handshake::connect(cfg)?;
let w = raw.server_init.width;
let h = raw.server_init.height;
let mut session = Self {
framebuffer: Framebuffer::new(raw.server_init.width, raw.server_init.height),
framebuffer: Framebuffer::new(w, h),
server_name: raw.server_name,
reader: raw.reader,
writer: raw.writer,
server_init: raw.server_init,
zlib: tight::ZlibStreams::new(),
tile_cache: iip::TileCache::new(w, h),
};
// Tell server to send 8bpp RGB332 pixels
@@ -157,6 +161,8 @@ impl ActiveSession {
if self.server_init.width != old_w || self.server_init.height != old_h {
self.framebuffer
.resize(self.server_init.width, self.server_init.height);
self.tile_cache
.resize(self.server_init.width, self.server_init.height);
return Ok(Some(Event::Resize {
width: self.server_init.width,
height: self.server_init.height,
@@ -221,6 +227,18 @@ impl ActiveSession {
hdr.h,
)?;
}
9 => {
iip::decode_iip(
&mut self.reader,
&mut self.framebuffer,
&mut self.tile_cache,
&mut self.zlib,
hdr.x,
hdr.y,
hdr.w,
hdr.h,
)?;
}
10 => {
raw_tile::decode_raw_tile(
&mut self.reader,