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.
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.
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)
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).ServerSessionContext /
ClientContext, calls Attach, and registers every handled type in the session's
MessageDispatcher. Registering a type twice throws — no silent collisions.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).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).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).IClientUiFeature.CreateToolbarUi(host) and return a control; it is
placed in the FeatureBar panel. Use host.GetSetting/SetSetting/SaveSettings with keys like
"myfeature.option" (stored in client.json → FeatureSettings). OnConnectionChanged is your
hook to enable controls and kick off per-connection behaviour. AudioClientFeature is the model.IServerUiFeature.CreateTrayItems(host) to add menu items.MainWindow/ScreenView;
a feature exposes data/events (e.g. MonitorsClientFeature.MonitorsReceived) and the window reacts.| 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 |
Rfb.cs; add to the
table in Developer.md.JVNC.Core (no UI, no Win32).Features/XServerFeature.cs implementing IServerFeature; register in the registry.
Win32/COM interop belongs in the server project.Features/XClientFeature.cs (+ IClientUiFeature if it has controls); register.PeerSupports.JVNC_FEATURES=1 style check) and a 60 s churn run — the
churn run is the regression test for the hot paths.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.