Milestone 6: Implement VNC support #3

Closed
opened 2026-09-07 11:27:57 -06:00 by Agent · 1 comment
Owner

VNC currently routes to UnsupportedSessionBackend with an explicit not-implemented message. This milestone was deferred; reopening as a tracked issue so it isn't lost.

Planned scope (per docs/PROGRESS.md Milestone 6):

  • Replace the unsupported VNC path with a complete VNC implementation
  • Deliver usable in-app VNC session behavior aligned to SSH/RDP UX
  • Implement VNC connect/disconnect/reconnect lifecycle handling
  • Extend profile/session connect options needed by VNC
  • Standardize event log and error mapping behavior with SSH/RDP
VNC currently routes to `UnsupportedSessionBackend` with an explicit not-implemented message. This milestone was deferred; reopening as a tracked issue so it isn't lost. Planned scope (per docs/PROGRESS.md Milestone 6): - Replace the unsupported VNC path with a complete VNC implementation - Deliver usable in-app VNC session behavior aligned to SSH/RDP UX - Implement VNC connect/disconnect/reconnect lifecycle handling - Extend profile/session connect options needed by VNC - Standardize event log and error mapping behavior with SSH/RDP
Author
Owner

Implemented (initial scope)

No permissively licensed VNC client library exists to vendor the way FreeRDP was for RDP -- LibVNCClient is GPLv2, gtk-vnc is LGPL but GTK-tied, and vendoring either would force a licensing decision on the whole (MIT) project. Per discussion, this is an original RFB (RFC 6143) implementation directly against QTcpSocket instead, in VncSessionBackend/VncDisplayWidget, threaded like SshSessionBackend (a QObject on its own QThread, driven by Qt's async socket signals) rather than RdpSessionBackend's manual worker-thread pattern, since there's no legacy synchronous C API here to wrangle.

Delivered:

  • Full connect/disconnect/reconnect lifecycle
  • RFB protocol-version negotiation, handling the real 3.3/3.7/3.8 handshake differences explicitly (the SecurityResult message only exists in 3.8 -- pre-3.8 servers signal auth failure by just closing the socket)
  • VNC Authentication (DES challenge-response, via OpenSSL's classic DES API, key-prep verified against an independently documented test vector) and no-auth security types
  • Raw + CopyRect framebuffer decoding, keyboard (Qt key -> X11 keysym) and mouse/wheel input forwarding
  • Wired into the same SessionTab/session_backend_factory dispatch SSH and RDP already use

Tested:

  • 19 unit tests against a scripted in-process fake RFB server (all three handshake versions, auth success/failure, unsupported security types, pixel-accurate decoding) -- caught and fixed a real re-entrancy bug (QAbstractSocket::abort() synchronously re-firing disconnected() before returning, silently overwriting a specific error message with a generic one)
  • Verified live against a real, independently implemented VNC server (TightVNC on Windows, at the user's suggestion): connect with VNC Authentication, correct framebuffer dimensions and pixel data, clean disconnect, reconnect -- all confirmed working

Explicit gaps (not oversights -- tracked here for follow-up)

  • Apple 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).
  • Raw + CopyRect encodings only -- no Hextile/ZRLE/Tight compression, so bandwidth usage is higher than a full VNC client over slow links.
  • No dynamic resize -- connects at the server's native resolution, scaled to fit locally (matches how RdpDisplayWidget already renders regardless of server resolution, so not a UX regression vs. RDP).
  • No remote cursor shape sync -- local default cursor only.
  • No clipboard sync.

Closing this as the initial VNC milestone is complete; happy to open follow-up issues for any of the above gaps if/when they become worth doing.

## Implemented (initial scope) No permissively licensed VNC client library exists to vendor the way FreeRDP was for RDP -- LibVNCClient is GPLv2, gtk-vnc is LGPL but GTK-tied, and vendoring either would force a licensing decision on the whole (MIT) project. Per discussion, this is an original RFB (RFC 6143) implementation directly against `QTcpSocket` instead, in `VncSessionBackend`/`VncDisplayWidget`, threaded like `SshSessionBackend` (a `QObject` on its own `QThread`, driven by Qt's async socket signals) rather than `RdpSessionBackend`'s manual worker-thread pattern, since there's no legacy synchronous C API here to wrangle. **Delivered:** - Full connect/disconnect/reconnect lifecycle - RFB protocol-version negotiation, handling the real 3.3/3.7/3.8 handshake differences explicitly (the `SecurityResult` message only exists in 3.8 -- pre-3.8 servers signal auth failure by just closing the socket) - VNC Authentication (DES challenge-response, via OpenSSL's classic DES API, key-prep verified against an independently documented test vector) and no-auth security types - Raw + CopyRect framebuffer decoding, keyboard (Qt key -> X11 keysym) and mouse/wheel input forwarding - Wired into the same `SessionTab`/`session_backend_factory` dispatch SSH and RDP already use **Tested:** - 19 unit tests against a scripted in-process fake RFB server (all three handshake versions, auth success/failure, unsupported security types, pixel-accurate decoding) -- caught and fixed a real re-entrancy bug (`QAbstractSocket::abort()` synchronously re-firing `disconnected()` before returning, silently overwriting a specific error message with a generic one) - Verified live against a real, independently implemented VNC server (TightVNC on Windows, at the user's suggestion): connect with VNC Authentication, correct framebuffer dimensions and pixel data, clean disconnect, reconnect -- all confirmed working ## Explicit gaps (not oversights -- tracked here for follow-up) - **Apple 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). - **Raw + CopyRect encodings only** -- no Hextile/ZRLE/Tight compression, so bandwidth usage is higher than a full VNC client over slow links. - **No dynamic resize** -- connects at the server's native resolution, scaled to fit locally (matches how `RdpDisplayWidget` already renders regardless of server resolution, so not a UX regression vs. RDP). - **No remote cursor shape sync** -- local default cursor only. - **No clipboard sync.** Closing this as the initial VNC milestone is complete; happy to open follow-up issues for any of the above gaps if/when they become worth doing.
Agent closed this issue 2026-09-15 18:03:09 -06:00
Sign in to join this conversation.