Internal
Public Access
Add VNC support (Milestone 6, issue #3)
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>
This commit is contained in:
1 parent
dbcc20d155
commit
6da9dc6ca5
12 files changed
+2097
-21
No files matched your search
@@ -0,0 +1,60 @@
|
||||
#ifndef ORBITHUB_VNC_DISPLAY_WIDGET_H
|
||||
#define ORBITHUB_VNC_DISPLAY_WIDGET_H
|
||||
|
||||
#include <QImage>
|
||||
#include <QWidget>
|
||||
|
||||
class QKeyEvent;
|
||||
class QMouseEvent;
|
||||
class QPaintEvent;
|
||||
class QResizeEvent;
|
||||
class QTimer;
|
||||
class QWheelEvent;
|
||||
|
||||
// Renders a VNC framebuffer and forwards local input, scaled-to-fit --
|
||||
// same shape as RdpDisplayWidget, minus cursor-shape sync (out of scope for
|
||||
// the initial VNC implementation; the system cursor is left alone).
|
||||
class VncDisplayWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit VncDisplayWidget(QWidget* parent = nullptr);
|
||||
|
||||
void setFrame(const QImage& frame);
|
||||
void setRemoteDesktopSize(int width, int height);
|
||||
void clearFrame();
|
||||
|
||||
signals:
|
||||
void keyInput(int key, quint32 nativeScanCode, const QString& text, bool pressed, int modifiers);
|
||||
void mouseMoveInput(int x, int y);
|
||||
void mouseButtonInput(int x, int y, int button, bool pressed);
|
||||
void mouseWheelInput(int x, int y, int deltaX, int deltaY);
|
||||
void viewportSizeChanged(int width, int height);
|
||||
void displayScaleChanged(qreal devicePixelRatio);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent* event) override;
|
||||
void resizeEvent(QResizeEvent* event) override;
|
||||
bool event(QEvent* event) override;
|
||||
void keyPressEvent(QKeyEvent* event) override;
|
||||
void keyReleaseEvent(QKeyEvent* event) override;
|
||||
void mousePressEvent(QMouseEvent* event) override;
|
||||
void mouseReleaseEvent(QMouseEvent* event) override;
|
||||
void mouseMoveEvent(QMouseEvent* event) override;
|
||||
void wheelEvent(QWheelEvent* event) override;
|
||||
bool focusNextPrevChild(bool next) override;
|
||||
|
||||
private:
|
||||
QImage m_frame;
|
||||
QSize m_remoteSize;
|
||||
QTimer* m_resizeDebounceTimer;
|
||||
|
||||
QRectF renderRect() const;
|
||||
QPoint mapToRemote(const QPointF& pos) const;
|
||||
QSize effectiveRemoteSize() const;
|
||||
void emitViewportGeometry();
|
||||
void scheduleViewportGeometryEmit();
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in new issue
Block a user