JVNC Architecture & Code Map

How the code is organised, where the speed comes from, and how to add a feature without touching the parts that make it fast. Read this before implementing anything from the Roadmap.


1. The two hot paths (do not touch)

SERVER                                                     CLIENT
ScreenCapture.Capture()  DXGI Desktop Duplication per       ReceiveLoop: MsgFramebufferUpdate
   │  output (BitBlt fallback) + cursor, double-buffered
   │  every 8 ms while nothing changed (≤ ~120 fps)            │  read rect headers
   ▼                                                           ▼
RfbServer.Diff()  64×64 tiles vs previous frame, in place  ReadRect: Raw copy / Zlib inflate / JPEG /
   │  changed tiles merged horizontally (+ DirtyTiles)         │  CopyRect (CopyWithin), straight into
   ▼                                                           │  Framebuffer (BGRA32)
DetectScroll()  per-tile vertical match → CopyRects           │
SplitToBudget() link budget: pointer-near rects now,          │
   │            rest deferred as DirtyTiles                    ▼
RfbServer.Encode()  one pooled buffer, persistent zlib     UpdateReceived → ScreenView.QueueRects
   │  (Z_SYNC_FLUSH per rect)                                  │  dirty rects, coalesced
   ▼                                                           ▼
Write() under Session.WriteLock  → one socket write        Paint on UI thread @ Input priority, ≤40/s
INPUT: ScreenView mouse/keys ──► RfbClient.SendPointer/SendKey ──► reader loop ──► InputInjector (SetCursorPos / SendInput)
       (motion coalesced ≤125/s; buttons immediate)                 (handled inline, never queued behind anything)

Properties that must survive any change:

Invariant Why
Server: reader task and update task are separate; the reader never waits for a frame. Input latency stays ~0 even while a 20 MB frame is being encoded.
One frame in flight: the client requests the next update only after the current one fully arrived. Keeps Wi-Fi uplink free for input; frame rate adapts to the link.
Client receive thread never waits on the UI thread (BeginInvoke only); paint is coalesced. A busy UI can't stall the network and vice versa.
Every socket write goes through the per-session write lock; frames are one write. Extension messages interleave safely between frames.
Zlib ChunkSource is append-only. ZLibStream reads ahead; dropping bytes desyncs the stream (see Developer.md).
Extension messages are dispatched from a 256-slot table after the RFB switch. O(1), no allocation, and RFB's own messages are matched first.

Rule of thumb: a feature may produce extension messages from a background task and consume them in its handler; it may not add work to capture/diff/encode/decode/paint or to the input cases.


2. Code map

JVNC.Core/                          protocol + pure logic, no UI, no Win32 (except DPAPI)
  Rfb.cs                            RFB constants, message type numbers (incl. extension table 240-254), stream helpers
  RfbPixelFormat.cs                 PIXEL_FORMAT
  Extensibility.cs                  MessageDispatcher, capabilities message, PeerCapabilities   ← plug-in plumbing
  Extensions.cs                     MonitorInfo/MonitorsMsg, AudioMsg, ImaAdpcm codec
  FileTransfer.cs                   file messages, streamed Incoming with deferred save decision
  Security.cs                       pre-handshake, Password (PBKDF2), Totp, Certs, IpAllowList, IpClass, Lockout
  ClipboardMsg.cs · ClipboardSync.cs (Win32 clipboard + sequence-number watcher)
  Dpapi.cs · JsonSettings.cs · AppLog.cs

JVNC.Server/                        WPF tray app
  RfbServer.cs                      HOT PATH + connection lifecycle + security handshake + dispatcher wiring
  ScreenCapture.cs                  GDI capture, cursor compositing, monitor enumeration
  InputInjector.cs                  keysym/pointer → Win32 input
  AudioCapture.cs                   WASAPI loopback (COM interop)
  Features/ServerFeatures.cs        IServerFeature, IServerFeatureSession, ServerSessionContext, IServerUiFeature, registry
  Features/FileTransferServerFeature.cs · MonitorsServerFeature.cs · AudioServerFeature.cs · ClipboardServerFeature.cs
  MainWindow / SecurityWindow / PasswordPrompt / App (single instance, tray)

JVNC.Client/                        WPF viewer
  RfbClient.cs                      HOT PATH + handshake + input senders + dispatcher wiring
  ScreenView.xaml(.cs)              one view of a framebuffer region: painting + input mapping
  MonitorWindow.xaml(.cs)           multi-head window hosting a ScreenView
  WinKeyHook.cs · KeySym.cs · LocalMonitors.cs · WaveOut.cs
  Features/ClientFeatures.cs        IClientFeature, IClientUiFeature, ClientUiHost, ClientContext, registry
  Features/FileTransferClientFeature.cs · MonitorsClientFeature.cs · AudioClientFeature.cs (UI example) · ClipboardClientFeature.cs
  MainWindow / SecurityWindow / PasswordPrompt

Installer/                          setup program + release scripts (bre/brn), see Installer/README.md
docs/                               these manuals (render.py → html)

3. The feature framework

3.1 Life cycle

  1. Registration (compile time): add the feature to ServerFeatureRegistry.Defaults and/or ClientFeatureRegistry.CreateDefaults(). Each feature declares the message types it handles (incoming) and sends (outgoing) from the extension range 240–254 (Rfb.cs is the allocation table — add your constant there with a one-line wire description).
  2. Attach (per connection, after ServerInit): the core creates a ServerSessionContext / ClientContext, calls Attach, and registers every handled type in the session's MessageDispatcher. Registering a type twice throws — no silent collisions.
  3. Capabilities: the client sends MsgCapabilities (240) listing the extension types it can receive; the server answers with its own. Check ctx.PeerSupports(type) before sending anything optional. Peers that never announce (third-party viewers, pre-1.0.3 JVNC) are assumed to support only the pre-negotiation types (Extension.LegacyTypes).
  4. Handle: HandleAsync(type, stream, ct) runs on the reader / receive thread with the type byte already consumed. Read exactly your message and return. Do not block (no dialogs, no long work): hand off with ctx.Track(...) (server) or a background task / Dispatcher.BeginInvoke (client).
  5. Send: ctx.WriteAsync / ctx.SendAsync take the shared write lock. Keep messages ≤ a few hundred KB so a frame is never stuck behind you; batch small things (audio batches ~60 ms).
  6. Detach/Dispose: the connection's cancellation token is already signalled; release native resources, stop threads. Tracked tasks are awaited by the core.

3.2 UI

3.3 Threads at a glance

Thread Server Client
Reader / receive RFB messages inline; extension handlers frame decode; extension handlers
Update task capture/diff/encode/write —
Feature tasks ctx.Track (audio stream, file send) features' own tasks
UI tray, dialogs (async), feature UI painting (coalesced), input, feature UI

4. Adding a feature — checklist

  1. Pick message type(s) in 240–254; add constants + wire format comment to Rfb.cs; add to the table in Developer.md.
  2. Put shared encode/decode helpers in JVNC.Core (no UI, no Win32).
  3. Server: Features/XServerFeature.cs implementing IServerFeature; register in the registry. Win32/COM interop belongs in the server project.
  4. Client: Features/XClientFeature.cs (+ IClientUiFeature if it has controls); register.
  5. Gate optional sends on PeerSupports.
  6. Test with the stress harness (JVNC_FEATURES=1 style check) and a 60 s churn run — the churn run is the regression test for the hot paths.
  7. Document it: User Manual (what the user sees), Security Guide if it changes the threat model, Comparison if it is a differentiator, Roadmap (move it to Done).

Message type allocation (200–254; 200–239 opened in 1.0.30):

Type Owner Direction
200 PointerEx (touch/pen) client → server
201 PointerRel (relative mouse) client → server
202 Focus (foreground window rect) server → client
203 Ping (RTT) both
204 LinkCap (bandwidth limit) client → server
240 Capabilities both
241 MonitorsRequest client → server
242 AudioEnable client → server
243 Privacy both
244 Chat both
245 Monitors server → client
246 AudioFormat server → client
247 AudioData server → client
248 Clipboard both
249 HostInfo server → client
250 FileHeader both
251 FileData both
252 FileRequest client → server
253 FileStatus server → client
254 TransferCtl (meta/probe/have/cancel/abort) both

Print jobs, drag-and-drop, folder sends and clipboard files/images reuse the FileTransfer messages (250–254) through Core/TransferEngine.cs (one queue per connection, published via ServerSessionContext.Publish/Get); recordings never touch the wire.


5. Performance budget (what "fast" means here)