Add VNC remote cursor shape sync

Implements RFB's Cursor pseudo-encoding (RFC 6143 SS7.8.2, type -239):
a FramebufferUpdate rectangle carrying a cursor shape instead of
screen content (x/y are the hotspot, not position; width/height are
the cursor image size), decoded into an ARGB32 QImage using the
rectangle's RGB pixel data plus its opacity bitmask, then never
painted into the framebuffer. A 0x0 rectangle means "hide the
cursor" per spec.

VncDisplayWidget gains RdpDisplayWidget's setCursorImage/Hidden/
Default() + applyCursor() shape, reusing its own renderRect()/
effectiveRemoteSize() so cursor scaling works correctly in both the
scale-to-fit and actual-size display modes with no special-casing.
VNC never emits cursorReset() (RFB's Cursor pseudo-encoding has no
"reset to system default" signal, unlike RDP's SetDefault callback) --
setCursorDefault() exists for symmetry but is unused today.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
ksmithandClaude Sonnet 5 committed 2026-09-15 20:34:20 -06:00
1 parent 3dd894407a
commit 9dd1af21d6
6 files changed
+286 -6

No files matched your search

+21 -2
View File
@@ -2,6 +2,7 @@
#define ORBITHUB_VNC_DISPLAY_WIDGET_H
#include <QImage>
#include <QPoint>
#include <QWidget>
class QKeyEvent;
@@ -12,8 +13,7 @@ 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).
// same shape as RdpDisplayWidget, including remote cursor-shape sync.
class VncDisplayWidget : public QWidget
{
Q_OBJECT
@@ -38,6 +38,15 @@ public:
return m_scaleToFit;
}
// Mirrors RdpDisplayWidget's cursor handling. VNC's Cursor pseudo-
// encoding never signals "reset to default" the way RDP's SetDefault
// callback does -- it only ever supplies a shape or hides the cursor --
// so setCursorDefault() exists for symmetry/future use but VNC sessions
// never call it today.
void setCursorImage(const QImage& image, const QPoint& hotspot);
void setCursorHidden();
void setCursorDefault();
signals:
void keyInput(int key, quint32 nativeScanCode, const QString& text, bool pressed, int modifiers);
void mouseMoveInput(int x, int y);
@@ -59,10 +68,19 @@ protected:
bool focusNextPrevChild(bool next) override;
private:
enum class CursorMode {
Default,
Hidden,
Custom,
};
QImage m_frame;
QSize m_remoteSize;
QTimer* m_resizeDebounceTimer;
bool m_scaleToFit;
QImage m_cursorImage;
QPoint m_cursorHotspot;
CursorMode m_cursorMode;
QRectF renderRect() const;
QPoint mapToRemote(const QPointF& pos) const;
@@ -70,6 +88,7 @@ private:
void emitViewportGeometry();
void scheduleViewportGeometryEmit();
void applySizeConstraint();
void applyCursor();
};
#endif