Add VNC clipboard sync

RFB's ServerCutText/ClientCutText messages are much simpler than RDP's
CLIPRDR channel (no format-list/format-request negotiation -- text is
just sent directly in both directions), so ServerCutText now emits
remoteClipboardTextChanged instead of being discarded, and
VncSessionBackend overrides setClipboardText to send ClientCutText
immediately. Extends session_tab.cpp's clipboard-sync gate to cover
VNC alongside RDP; the QClipboard wiring itself was already
protocol-agnostic. Latin-1 only, per RFB's wire format -- no Unicode
clipboard extension is in scope.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-15 20:31:03 -06:00
co-authored by Claude Sonnet 5
parent 35d4daec7f
commit 3dd894407a
4 changed files with 134 additions and 5 deletions
+28 -2
View File
@@ -263,6 +263,16 @@ void VncSessionBackend::sendMouseWheelEvent(int x, int y, int deltaX, int deltaY
}
}
void VncSessionBackend::setClipboardText(const QString& text)
{
if (m_socket->state() != QAbstractSocket::ConnectedState) {
return;
}
// Unlike RDP's CLIPRDR (list -> server request -> response), RFB's
// ClientCutText has no negotiation: send it immediately.
sendClientCutText(text);
}
void VncSessionBackend::onSocketConnected()
{
emit eventLogged(QStringLiteral("TCP connection established; waiting for VNC handshake."));
@@ -465,6 +475,19 @@ void VncSessionBackend::sendWheelClick(quint8 wheelBit)
m_socket->write(release);
}
void VncSessionBackend::sendClientCutText(const QString& text)
{
// Latin-1 only, matching ServerCutText's decode above -- RFB has no
// Unicode clipboard extension in scope here.
const QByteArray latin1 = text.toLatin1();
QByteArray msg;
msg.append(kMsgClientCutText);
msg.append(3, char(0)); // padding
appendU32BE(msg, static_cast<quint32>(latin1.size()));
msg.append(latin1);
m_socket->write(msg);
}
void VncSessionBackend::finishHandshakeIntoRunningState()
{
emit remoteDesktopSizeChanged(m_framebuffer.width(), m_framebuffer.height());
@@ -895,10 +918,13 @@ void VncSessionBackend::processReceiveBuffer()
if (!haveBytes(static_cast<int>(m_pendingLength))) {
return;
}
// Clipboard sync is out of scope for this pass; consume and
// discard so the byte stream stays in sync.
// RFB's ServerCutText is Latin-1 only (RFC 6143 SS7.5.4) -- no
// Unicode extension is implemented, unlike RDP's CLIPRDR.
const QString text =
QString::fromLatin1(m_recvBuffer.left(static_cast<int>(m_pendingLength)));
m_recvBuffer.remove(0, static_cast<int>(m_pendingLength));
m_rfbState = RfbState::WaitingServerMessageType;
emit remoteClipboardTextChanged(text);
break;
}
}