VNC has no equivalent of RDP's MS-RDPEDISP to request a different
resolution from the guest, so a high-resolution remote desktop
previously always got shrunk to fit the window, making text
illegible. Adds a per-tab "Display Mode" choice (tab-bar right-click)
between the existing scale-to-fit behavior and a new actual-size mode
that renders the framebuffer at its native pixel size inside a
QScrollArea. Reuses VncDisplayWidget's existing scale-to-fit render
math unchanged -- it degenerates to an exact 1:1 mapping once the
widget is fixed to the remote's own size. Persisted like the terminal
theme preference.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Implements RFB (RFC 6143) directly against QTcpSocket. 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. This is
from-scratch protocol code instead, threaded like SshSessionBackend (a
QObject on its own QThread driven by Qt's own async socket signals)
rather than RdpSessionBackend's manual worker-thread/blocking-loop
pattern, since QTcpSocket is already async.
Scope, matching SessionTab's existing SSH/RDP dispatch pattern
(session_backend_factory.cpp, session_tab.cpp's widget construction and
signal wiring) and VncDisplayWidget mirroring RdpDisplayWidget's
scale-to-fit rendering:
- Protocol handshake: RFB 3.3/3.7/3.8 negotiated explicitly (the
SecurityResult message only exists in 3.8; pre-3.8 servers signal auth
failure by closing the socket, which the disconnect handler accounts
for)
- VNC Authentication (DES challenge-response, via OpenSSL's classic DES
API) and no-auth security types
- Raw + CopyRect framebuffer decoding into a persistent QImage, requesting
a fixed 32bpp format whose byte layout matches QImage::Format_RGB32
directly (same zero-conversion trick RdpSessionBackend uses for
FreeRDP's GDI buffer)
- Keyboard (Qt key -> X11 keysym, including the Unicode-beyond-Latin-1
keysym convention) and mouse/wheel input forwarding
Explicit non-goals for this pass (see docs/PROGRESS.md for the full
list): Apple's Screen Sharing auth (so this can't yet reach macOS's
built-in VNC server), compression encodings beyond Raw/CopyRect, dynamic
resize, remote cursor shape sync, clipboard sync.
19 unit tests (tests/test_vnc_session_backend.cpp): pure-function
coverage (DES key prep verified against an independently documented test
vector for password "COW", X11 keysym mapping, socket-error mapping) plus
state-machine coverage against a scripted in-process fake RFB server
covering all three protocol-version handshake shapes, auth success/
failure, unsupported security types, and pixel-accurate Raw decoding.
That harness caught a real re-entrancy bug: QAbstractSocket::abort()
synchronously re-emits disconnected() before returning, so
failConnection() calling it was silently letting a second, generic
disconnected-socket handler overwrite an already-correct, specific error
message.
Also 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.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds docs/USER_GUIDE.md, a 10-section end-user guide (getting
started, managing/organizing profiles, SSH and RDP connections,
session management, settings, troubleshooting). It's embedded into
the app binary via a Qt resource file and rendered by a new
Help -> User Guide window: a topic sidebar plus content pane, not a
single scrolling document, with cross-reference links between
sections routed to sidebar selection rather than relying on Qt's
Markdown importer's lack of heading anchors.
A separate, non-shipped tool (tools/user-guide-pdf/) renders the
same source to a standalone PDF via QTextDocument + QPrinter,
wrapped by packaging/docs/build-user-guide-pdf.sh. Kept fully
outside the main CMake target so Qt6::PrintSupport never becomes a
runtime dependency of the shipped app (confirmed via ldd). The PDF
itself isn't committed -- generated per release like the platform
installers.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
License files were installing to share/licenses/orbithub instead of
the path Flathub's own docs specify for this app
(share/licenses/org.darksingularity.OrbitHub, i.e. $FLATPAK_ID).
Also installs FreeRDP's and KodoTerm's bundled LICENSE files there
alongside OrbitHub's own, since previously only the latter was
installed at all.
docs/FLATHUB.md now documents two things found by checking Flathub's
current requirements directly rather than assuming prior packaging
work was sufficient: the vendored libvterm copy has no LICENSE file
at all (needs to come from upstream, not fabricated here), and
Flathub's Generative AI disclosure policy is a real, reviewer-
discretion acceptance risk for this project given its development
history — not something further packaging work resolves.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Runs Flathub's own flatpak-builder-lint against both manifests and
the metainfo, and fixes what it found:
- only-arches doesn't belong in the manifest itself (linter: manifest
unknown property); it belongs in a separate flathub.json, which is
what Flathub's own build infrastructure actually reads it from.
- Bumps the KDE runtime from 6.10 to 6.11 per the linter's outdated-
runtime warning; verified the app still builds and launches against
it before committing to the bump.
The one remaining linter finding, finish-args-ssh-filesystem-access,
is a deliberate policy flag rather than a bug — Flathub requires a
written justification for any ~/.ssh access in the submission PR,
which docs/FLATHUB.md now documents with precedent from already-
approved apps in the same situation.
Also confirms (via a real interactive test with xdotool) that the
private-key Browse button correctly triggers the desktop portal
file chooser inside the sandbox, closing the last open verification
item from the previous commit.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds a production manifest (packaging/flatpak/flathub/) using a
pinned git source instead of the local-dir source the dev manifest
uses, so Flathub's build infrastructure can reproduce the build
independently.
Narrows the dev and production manifests' filesystem permission from
--filesystem=home to --filesystem=~/.ssh (read-write, needed for SSH
known_hosts/config) after confirming in the actual sandbox that: SSH
already works there (provided by the KDE runtime base, no extra
packaging needed), RDP needs no filesystem access at all (its
certificate trust never touched disk even before today's fix), and
QFileDialog's private-key/export pickers route through the desktop
portal rather than needing static filesystem access.
Expands AppStream metainfo with bugtracker/vcs-browser URLs, a
developer block, a releases entry, an OARS content rating, and three
screenshots (profiles view, an active SSH session, an active RDP
session) using real test-system profiles.
Adds docs/FLATHUB.md tracking overall submission readiness.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The reverse-DNS app ID should reflect a domain we actually control.
Renames the desktop file, AppStream metainfo, icon, Flatpak manifest,
and macOS bundle identifier from io.orbithub.OrbitHub to
org.darksingularity.OrbitHub. Also fixes a stale homepage URL in the
metainfo file pointing at an old git host.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Linux, macOS, and Windows have all been validated against the current
feature set (SSH, RDP display/keyboard/clipboard/cursor, Tab forwarding,
key repeat, profile dialog, single-window UI), with the platform-specific
bugs found along the way fixed and tracked individually on the issue
tracker (#8-#16). VNC validation remains out of scope while Milestone 6
stays deferred.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Same root cause as the earlier Windows exe icon fix: add_executable()
never set MACOSX_BUNDLE, so on macOS this produced a bare Mach-O binary
rather than a proper .app bundle -- and without a bundle there's no
Info.plist/.icns mechanism for Finder to show a custom icon at all.
Added packaging/macos/orbithub.icns, rendered at up to 1024px directly
from the same createOrbitHubAppIcon() logic used at runtime (via a
one-off export tool, not committed) so it matches what the app actually
looks like. CMake's MACOSX_BUNDLE keyword and MACOSX_BUNDLE_ICON_FILE
wire it into an auto-generated Info.plist; both keywords are no-ops on
other platforms. The profile database uses QStandardPaths::AppDataLocation,
not the executable's own path, so this doesn't affect where existing
profiles are found.
Build output on macOS changes from build/orbithub to build/orbithub.app
(launch with `open build/orbithub.app`); docs/BUILDING.md updated to match.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>