Internal
Public Access
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:
@@ -110,6 +110,8 @@ private slots:
|
||||
void rawFramebufferUpdateProducesExpectedPixels();
|
||||
void copyRectEncodingDoesNotFailConnection();
|
||||
void unannouncedEncodingFailsConnectionWithClearMessage();
|
||||
void serverCutTextEmitsRemoteClipboardTextChanged();
|
||||
void setClipboardTextSendsClientCutText();
|
||||
|
||||
private:
|
||||
std::unique_ptr<FakeVncServer> m_server;
|
||||
@@ -620,5 +622,102 @@ void TestVncSessionBackend::unannouncedEncodingFailsConnectionWithClearMessage()
|
||||
QVERIFY(m_lastErrorDisplay.contains(QStringLiteral("non-compliant")));
|
||||
}
|
||||
|
||||
void TestVncSessionBackend::serverCutTextEmitsRemoteClipboardTextChanged()
|
||||
{
|
||||
QString lastClipboardText;
|
||||
connect(m_backend.get(), &SessionBackend::remoteClipboardTextChanged, this,
|
||||
[&lastClipboardText](const QString& text) { lastClipboardText = text; });
|
||||
|
||||
connect(m_server.get(), &FakeVncServer::clientConnected, this, [this]() {
|
||||
m_server->sendWhenConnected(QByteArray("RFB 003.008\n"));
|
||||
});
|
||||
connect(m_server.get(), &FakeVncServer::dataReceived, this, [this]() {
|
||||
switch (m_server->nextStep()) {
|
||||
case 0: {
|
||||
QByteArray securityTypes;
|
||||
securityTypes.append(char(1));
|
||||
securityTypes.append(char(1));
|
||||
m_server->sendWhenConnected(securityTypes);
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
m_server->sendWhenConnected(QByteArray(4, char(0))); // SecurityResult: OK
|
||||
break;
|
||||
case 2: { // ClientInit -- reply with ServerInit, then a ServerCutText message
|
||||
QByteArray serverInit;
|
||||
serverInit.append(char(0)); serverInit.append(char(1));
|
||||
serverInit.append(char(0)); serverInit.append(char(1));
|
||||
serverInit.append(QByteArray(16, char(0)));
|
||||
serverInit.append(QByteArray(4, char(0)));
|
||||
|
||||
QByteArray cutText;
|
||||
cutText.append(char(3)); // message-type: ServerCutText
|
||||
cutText.append(3, char(0)); // padding
|
||||
const QByteArray text = QByteArray("hello clipboard");
|
||||
cutText.append(char(0)); cutText.append(char(0)); cutText.append(char(0));
|
||||
cutText.append(static_cast<char>(text.size())); // length (BE, fits one byte here)
|
||||
|
||||
m_server->sendWhenConnected(serverInit + cutText + text);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
m_backend->connectSession(makeOptions());
|
||||
QTRY_COMPARE(lastClipboardText, QStringLiteral("hello clipboard"));
|
||||
}
|
||||
|
||||
void TestVncSessionBackend::setClipboardTextSendsClientCutText()
|
||||
{
|
||||
connect(m_server.get(), &FakeVncServer::clientConnected, this, [this]() {
|
||||
m_server->sendWhenConnected(QByteArray("RFB 003.008\n"));
|
||||
});
|
||||
connect(m_server.get(), &FakeVncServer::dataReceived, this, [this]() {
|
||||
switch (m_server->nextStep()) {
|
||||
case 0: {
|
||||
QByteArray securityTypes;
|
||||
securityTypes.append(char(1));
|
||||
securityTypes.append(char(1));
|
||||
m_server->sendWhenConnected(securityTypes);
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
m_server->sendWhenConnected(QByteArray(4, char(0))); // SecurityResult: OK
|
||||
break;
|
||||
case 2: { // ClientInit
|
||||
QByteArray serverInit;
|
||||
serverInit.append(char(0)); serverInit.append(char(1));
|
||||
serverInit.append(char(0)); serverInit.append(char(1));
|
||||
serverInit.append(QByteArray(16, char(0)));
|
||||
serverInit.append(QByteArray(4, char(0)));
|
||||
m_server->sendWhenConnected(serverInit);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
m_backend->connectSession(makeOptions());
|
||||
QTRY_COMPARE(m_lastState, SessionState::Connected);
|
||||
|
||||
m_backend->setClipboardText(QStringLiteral("hi"));
|
||||
|
||||
// The exact ClientCutText bytes must eventually appear at the tail of
|
||||
// whatever the fake server received -- checked with endsWith() rather
|
||||
// than an exact match on the whole buffer, since exactly how the
|
||||
// preceding handshake/SetEncodings/FramebufferUpdateRequest bytes get
|
||||
// chunked into TCP reads isn't guaranteed and doesn't matter here.
|
||||
QByteArray expected;
|
||||
expected.append(char(6)); // message-type: ClientCutText
|
||||
expected.append(3, char(0)); // padding
|
||||
expected.append(char(0)); expected.append(char(0)); expected.append(char(0));
|
||||
expected.append(char(2)); // length = 2
|
||||
expected.append("hi");
|
||||
QTRY_VERIFY(m_server->received.endsWith(expected));
|
||||
}
|
||||
|
||||
QTEST_GUILESS_MAIN(TestVncSessionBackend)
|
||||
#include "test_vnc_session_backend.moc"
|
||||
|
||||
Reference in New Issue
Block a user