Internal
Public Access
Add VNC Tight decoding
Implements RFC 6143's Tight encoding: a compression-control byte (low 4 bits reset one of 4 persistent zlib streams; high nibble selects Fill/JPEG/Basic mode) followed by Fill's 3-byte solid color, JPEG's compact-length-prefixed baseline JPEG covering the whole rectangle (decoded via libjpeg-turbo directly, not QImage's plugin, to avoid a packaging-dependent runtime failure mode), or Basic mode's compact-length-prefixed zlib payload plus a filter (Copy, Palette, or Gradient) applied after decompression. Unlike Hextile/ZRLE, Tight has no internal tiling -- one rectangle is one filtered/compressed unit. The three filters live in vnc_pixel_codecs.h/.cpp alongside the Hextile/ZRLE decoders. Adds find_package(JPEG REQUIRED) + JPEG::JPEG as a new build dependency (confirmed available via libjpeg-turbo on this dev machine). 5 new tests cover Fill, Basic+Copy, Basic+Palette, JPEG (round-tripped through a real libjpeg-turbo-encoded fixture, compared with tolerance since JPEG is lossy), and the stream-reset flag correctly tearing down and reinitializing a targeted stream rather than erroring on stale state. Known, documented gap: this decoder always treats Basic-mode payloads as zlib-compressed; the real protocol allows very small payloads to skip compression, which couldn't be verified with confidence against the RFC text alone and is narrow enough in practice (tiny solid areas are virtually always sent as Fill instead) to leave unhandled for now -- it fails that one rectangle's decode cleanly rather than misinterpreting it silently. The Gradient filter is implemented from the spec description but is the least exercised of the three in this pass. Live-verified against the TightVNC test server that nothing regressed; that server still consistently chose Raw for actual framebuffer content regardless of announced encodings, so Tight's live decode path isn't independently confirmed against a real server here either -- the unit tests are the primary evidence. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+44
-19
@@ -121,25 +121,26 @@ Delivered:
|
||||
keyboard (Qt key -> X11 keysym mapping) and mouse/wheel input forwarding
|
||||
- `VncDisplayWidget` mirroring `RdpDisplayWidget`'s scale-to-fit rendering
|
||||
and input-forwarding shape
|
||||
- 36 unit tests (`tests/test_vnc_session_backend.cpp`): pure-function
|
||||
- 41 unit tests (`tests/test_vnc_session_backend.cpp`): pure-function
|
||||
coverage (DES key prep verified against an independently documented test
|
||||
vector, keysym mapping, socket-error mapping) plus state-machine
|
||||
coverage against a scripted in-process fake RFB server (all three
|
||||
protocol-version handshake shapes, auth success/failure, unsupported
|
||||
security types, pixel-accurate Raw/Hextile/ZRLE decoding including a
|
||||
ZRLE zlib-stream-persistence test across two separate
|
||||
`FramebufferUpdate` messages, cursor/clipboard round trips) -- caught
|
||||
and fixed a real re-entrancy bug (`abort()` synchronously re-firing
|
||||
`disconnected()` mid-`failConnection()`, silently overwriting a specific
|
||||
error with a generic one)
|
||||
security types, pixel-accurate Raw/Hextile/ZRLE/Tight decoding including
|
||||
a ZRLE zlib-stream-persistence test across two separate
|
||||
`FramebufferUpdate` messages, a Tight stream-reset-flag test, cursor/
|
||||
clipboard round trips) -- caught and fixed a real re-entrancy bug
|
||||
(`abort()` synchronously re-firing `disconnected()` mid-
|
||||
`failConnection()`, silently overwriting a specific error with a
|
||||
generic one)
|
||||
- Verified live against a real, independently implemented VNC server
|
||||
(TightVNC on Windows): connect with VNC Authentication, correct
|
||||
framebuffer dimensions and pixel data, clean disconnect, reconnect,
|
||||
live cursor-shape and clipboard-send checks. That particular server
|
||||
consistently chose Raw for actual framebuffer content regardless of
|
||||
which compression encodings were announced, so Hextile/ZRLE's
|
||||
which compression encodings were announced, so Hextile/ZRLE/Tight's
|
||||
real-world decode path isn't independently confirmed live -- the unit
|
||||
tests are the primary correctness evidence for those two
|
||||
tests are the primary correctness evidence for those
|
||||
- Per-tab VNC-only display mode toggle (tab-bar right-click ->
|
||||
`Display Mode`): `Scale to Fit` (default, matches RDP's behavior) or
|
||||
`Actual Size (Scrollbars)` -- renders the remote framebuffer at its
|
||||
@@ -158,22 +159,46 @@ Delivered:
|
||||
Unicode extension) in both directions
|
||||
- Remote cursor shape sync via RFB's Cursor pseudo-encoding, mirroring
|
||||
`RdpDisplayWidget`'s cursor handling in `VncDisplayWidget`
|
||||
- Hextile and ZRLE compression encodings, in addition to Raw + CopyRect --
|
||||
meaningfully reduces bandwidth over slower links versus Raw alone. Pure
|
||||
tile/pixel decode logic lives in `src/vnc_pixel_codecs.h/.cpp`, kept
|
||||
separate from the wire-sequencing state machine so it's unit-testable
|
||||
without a socket. ZRLE decoding links `ZLIB::ZLIB` (found via a fresh
|
||||
top-level `find_package(ZLIB REQUIRED)`, independent of whether
|
||||
vendored FreeRDP's own internal zlib usage stays enabled)
|
||||
- Hextile, ZRLE, and Tight compression encodings, in addition to Raw +
|
||||
CopyRect -- meaningfully reduces bandwidth over slower links versus Raw
|
||||
alone; Tight in particular is the encoding most real VNC servers prefer
|
||||
when the client offers it. Pure tile/pixel decode logic (including
|
||||
Tight's three filters -- Copy, Palette, Gradient) lives in
|
||||
`src/vnc_pixel_codecs.h/.cpp`, kept separate from the wire-sequencing
|
||||
state machine so it's unit-testable without a socket. ZRLE and Tight's
|
||||
Basic mode share the same "persistent zlib stream(s), decompress
|
||||
in-memory, decode synchronously" approach (Tight has 4 independent
|
||||
streams selected per-rectangle, individually reset via the
|
||||
compression-control byte's low 4 bits). Tight's JPEG sub-mode decodes
|
||||
via libjpeg-turbo directly (`find_package(JPEG REQUIRED)` ->
|
||||
`JPEG::JPEG`), not `QImage`'s own JPEG plugin, to avoid a
|
||||
packaging-dependent runtime failure mode. ZRLE/Tight link `ZLIB::ZLIB`
|
||||
(found via a fresh top-level `find_package(ZLIB REQUIRED)`, independent
|
||||
of whether vendored FreeRDP's own internal zlib usage stays enabled)
|
||||
- 41 unit tests total, 15 of them for Hextile/ZRLE/Tight specifically,
|
||||
including a ZRLE zlib-stream-persistence test across two separate
|
||||
`FramebufferUpdate` messages and a Tight stream-reset-flag test proving
|
||||
the low 4 control-byte bits actually tear down and reinitialize the
|
||||
targeted stream rather than erroring out on stale state
|
||||
|
||||
Known gaps (explicit scope decisions, not oversights -- see issue #3 for
|
||||
follow-up tracking):
|
||||
- Apple's Screen Sharing authentication (Diffie-Hellman + AES, security
|
||||
type 30) isn't implemented, so this can't yet reach macOS's built-in VNC
|
||||
server -- only standard VNC Authentication (type 2) and no-auth (type 1)
|
||||
- Tight compression isn't implemented yet (Raw + CopyRect + Hextile + ZRLE
|
||||
are); Tight typically compresses best of all of these, so bandwidth over
|
||||
very slow links is still not as good as a full VNC client would achieve
|
||||
- Tight's Basic compression mode always assumes zlib-compressed payloads;
|
||||
the real protocol permits the server to skip compression for very small
|
||||
(filtered byte count under ~12) payloads, which this decoder doesn't
|
||||
special-case (the exact trigger/wire-signaling for that couldn't be
|
||||
verified with confidence against the RFC text alone). In practice this
|
||||
only affects rare, tiny rectangles -- solid or near-solid tiny areas are
|
||||
virtually always sent as Fill instead -- and fails that one rectangle's
|
||||
decode cleanly (disconnects with a clear error) rather than silently
|
||||
misinterpreting it
|
||||
- Tight's Gradient filter is implemented from RFC 6143's description but
|
||||
is the least exercised/confirmed of the three filters against a real
|
||||
server in this pass (most real-world Tight traffic uses Copy or
|
||||
Palette)
|
||||
- No dynamic resize (connects at the server's native resolution; the
|
||||
`Scale to Fit`/`Actual Size` toggle changes how that fixed resolution is
|
||||
displayed locally, not what resolution is requested from the guest --
|
||||
|
||||
Reference in New Issue
Block a user