summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/rfb/CConnection.cxx130
-rw-r--r--common/rfb/CConnection.h49
-rw-r--r--common/rfb/CMsgHandler.cxx25
-rw-r--r--common/rfb/CMsgHandler.h18
-rw-r--r--common/rfb/CMsgReader.cxx118
-rw-r--r--common/rfb/CMsgReader.h3
-rw-r--r--common/rfb/CMsgWriter.cxx113
-rw-r--r--common/rfb/CMsgWriter.h12
-rw-r--r--common/rfb/ClientParams.cxx23
-rw-r--r--common/rfb/ClientParams.h7
-rw-r--r--common/rfb/InputHandler.h3
-rw-r--r--common/rfb/SConnection.cxx122
-rw-r--r--common/rfb/SConnection.h49
-rw-r--r--common/rfb/SDesktop.h20
-rw-r--r--common/rfb/SMsgHandler.cxx25
-rw-r--r--common/rfb/SMsgHandler.h15
-rw-r--r--common/rfb/SMsgReader.cxx118
-rw-r--r--common/rfb/SMsgReader.h3
-rw-r--r--common/rfb/SMsgWriter.cxx121
-rw-r--r--common/rfb/SMsgWriter.h14
-rw-r--r--common/rfb/ServerParams.cxx20
-rw-r--r--common/rfb/ServerParams.h7
-rw-r--r--common/rfb/VNCSConnectionST.cxx78
-rw-r--r--common/rfb/VNCSConnectionST.h10
-rw-r--r--common/rfb/VNCServer.h19
-rw-r--r--common/rfb/VNCServerST.cxx76
-rw-r--r--common/rfb/VNCServerST.h14
-rw-r--r--common/rfb/clipboardTypes.h41
-rw-r--r--common/rfb/encodings.h3
-rw-r--r--common/rfb/util.cxx440
-rw-r--r--common/rfb/util.h21
-rw-r--r--tests/decperf.cxx4
-rw-r--r--tests/encperf.cxx4
-rw-r--r--unix/x0vncserver/XDesktop.cxx2
-rw-r--r--unix/x0vncserver/XDesktop.h2
-rw-r--r--unix/xserver/hw/vnc/RFBGlue.cc34
-rw-r--r--unix/xserver/hw/vnc/RFBGlue.h9
-rw-r--r--unix/xserver/hw/vnc/XserverDesktop.cc54
-rw-r--r--unix/xserver/hw/vnc/XserverDesktop.h10
-rw-r--r--unix/xserver/hw/vnc/vncExtInit.cc18
-rw-r--r--unix/xserver/hw/vnc/vncExtInit.h6
-rw-r--r--unix/xserver/hw/vnc/vncSelection.c342
-rw-r--r--unix/xserver/hw/vnc/vncSelection.h6
-rw-r--r--vncviewer/CConn.cxx20
-rw-r--r--vncviewer/CConn.h6
-rw-r--r--vncviewer/DesktopWindow.cxx22
-rw-r--r--vncviewer/DesktopWindow.h8
-rw-r--r--vncviewer/Viewport.cxx163
-rw-r--r--vncviewer/Viewport.h17
-rw-r--r--vncviewer/vncviewer.man5
-rw-r--r--win/rfb_win32/Clipboard.cxx133
-rw-r--r--win/rfb_win32/Clipboard.h6
-rw-r--r--win/rfb_win32/SDisplay.cxx28
-rw-r--r--win/rfb_win32/SDisplay.h9
54 files changed, 2192 insertions, 433 deletions
diff --git a/common/rfb/CConnection.cxx b/common/rfb/CConnection.cxx
index d6960dd2..bdde3253 100644
--- a/common/rfb/CConnection.cxx
+++ b/common/rfb/CConnection.cxx
@@ -1,5 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2011-2017 Pierre Ossman for Cendio AB
+ * Copyright 2011-2019 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -21,6 +21,7 @@
#include <string.h>
#include <rfb/Exception.h>
+#include <rfb/clipboardTypes.h>
#include <rfb/fenceTypes.h>
#include <rfb/CMsgReader.h>
#include <rfb/CMsgWriter.h>
@@ -52,7 +53,8 @@ CConnection::CConnection()
formatChange(false), encodingChange(false),
firstUpdate(true), pendingUpdate(false), continuousUpdates(false),
forceNonincremental(true),
- framebuffer(NULL), decoder(this)
+ framebuffer(NULL), decoder(this),
+ serverClipboard(NULL), hasLocalClipboard(false)
{
}
@@ -65,6 +67,7 @@ CConnection::~CConnection()
reader_ = 0;
delete writer_;
writer_ = 0;
+ strFree(serverClipboard);
}
void CConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
@@ -463,6 +466,79 @@ void CConnection::dataRect(const Rect& r, int encoding)
decoder.decodeRect(r, encoding, framebuffer);
}
+void CConnection::serverCutText(const char* str)
+{
+ hasLocalClipboard = false;
+
+ strFree(serverClipboard);
+ serverClipboard = NULL;
+
+ serverClipboard = latin1ToUTF8(str);
+
+ handleClipboardAnnounce(true);
+}
+
+void CConnection::handleClipboardCaps(rdr::U32 flags,
+ const rdr::U32* lengths)
+{
+ rdr::U32 sizes[] = { 0 };
+
+ CMsgHandler::handleClipboardCaps(flags, lengths);
+
+ writer()->writeClipboardCaps(rfb::clipboardUTF8 |
+ rfb::clipboardRequest |
+ rfb::clipboardPeek |
+ rfb::clipboardNotify |
+ rfb::clipboardProvide,
+ sizes);
+}
+
+void CConnection::handleClipboardRequest(rdr::U32 flags)
+{
+ if (!(flags & rfb::clipboardUTF8))
+ return;
+ if (!hasLocalClipboard)
+ return;
+ handleClipboardRequest();
+}
+
+void CConnection::handleClipboardPeek(rdr::U32 flags)
+{
+ if (!hasLocalClipboard)
+ return;
+ if (server.clipboardFlags() & rfb::clipboardNotify)
+ writer()->writeClipboardNotify(rfb::clipboardUTF8);
+}
+
+void CConnection::handleClipboardNotify(rdr::U32 flags)
+{
+ strFree(serverClipboard);
+ serverClipboard = NULL;
+
+ if (flags & rfb::clipboardUTF8) {
+ hasLocalClipboard = false;
+ handleClipboardAnnounce(true);
+ } else {
+ handleClipboardAnnounce(false);
+ }
+}
+
+void CConnection::handleClipboardProvide(rdr::U32 flags,
+ const size_t* lengths,
+ const rdr::U8* const* data)
+{
+ if (!(flags & rfb::clipboardUTF8))
+ return;
+
+ strFree(serverClipboard);
+ serverClipboard = NULL;
+
+ serverClipboard = convertLF((const char*)data[0], lengths[0]);
+
+ // FIXME: Should probably verify that this data was actually requested
+ handleClipboardData(serverClipboard);
+}
+
void CConnection::authSuccess()
{
}
@@ -476,6 +552,55 @@ void CConnection::resizeFramebuffer()
assert(false);
}
+void CConnection::handleClipboardRequest()
+{
+}
+
+void CConnection::handleClipboardAnnounce(bool available)
+{
+}
+
+void CConnection::handleClipboardData(const char* data)
+{
+}
+
+void CConnection::requestClipboard()
+{
+ if (serverClipboard != NULL) {
+ handleClipboardData(serverClipboard);
+ return;
+ }
+
+ if (server.clipboardFlags() & rfb::clipboardRequest)
+ writer()->writeClipboardRequest(rfb::clipboardUTF8);
+}
+
+void CConnection::announceClipboard(bool available)
+{
+ hasLocalClipboard = available;
+
+ if (server.clipboardFlags() & rfb::clipboardNotify)
+ writer()->writeClipboardNotify(available ? rfb::clipboardUTF8 : 0);
+ else {
+ if (available)
+ handleClipboardRequest();
+ }
+}
+
+void CConnection::sendClipboardData(const char* data)
+{
+ if (server.clipboardFlags() & rfb::clipboardProvide) {
+ CharArray filtered(convertCRLF(data));
+ size_t sizes[1] = { strlen(filtered.buf) + 1 };
+ const rdr::U8* data[1] = { (const rdr::U8*)filtered.buf };
+ writer()->writeClipboardProvide(rfb::clipboardUTF8, sizes, data);
+ } else {
+ CharArray latin1(utf8ToLatin1(data));
+
+ writer()->writeClientCutText(latin1.buf);
+ }
+}
+
void CConnection::refreshFramebuffer()
{
forceNonincremental = true;
@@ -611,6 +736,7 @@ void CConnection::updateEncodings()
encodings.push_back(pseudoEncodingDesktopName);
encodings.push_back(pseudoEncodingLastRect);
+ encodings.push_back(pseudoEncodingExtendedClipboard);
encodings.push_back(pseudoEncodingContinuousUpdates);
encodings.push_back(pseudoEncodingFence);
encodings.push_back(pseudoEncodingQEMUKeyEvent);
diff --git a/common/rfb/CConnection.h b/common/rfb/CConnection.h
index 66a71a24..f01d5d36 100644
--- a/common/rfb/CConnection.h
+++ b/common/rfb/CConnection.h
@@ -1,5 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2011-2017 Pierre Ossman for Cendio AB
+ * Copyright 2011-2019 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -109,6 +109,17 @@ namespace rfb {
virtual void framebufferUpdateEnd();
virtual void dataRect(const Rect& r, int encoding);
+ virtual void serverCutText(const char* str);
+
+ virtual void handleClipboardCaps(rdr::U32 flags,
+ const rdr::U32* lengths);
+ virtual void handleClipboardRequest(rdr::U32 flags);
+ virtual void handleClipboardPeek(rdr::U32 flags);
+ virtual void handleClipboardNotify(rdr::U32 flags);
+ virtual void handleClipboardProvide(rdr::U32 flags,
+ const size_t* lengths,
+ const rdr::U8* const* data);
+
// Methods to be overridden in a derived class
@@ -128,9 +139,42 @@ namespace rfb {
// sure the pixel buffer has been updated once this call returns.
virtual void resizeFramebuffer();
+ // handleClipboardRequest() is called whenever the server requests
+ // the client to send over its clipboard data. It will only be
+ // called after the client has first announced a clipboard change
+ // via announceClipboard().
+ virtual void handleClipboardRequest();
+
+ // handleClipboardAnnounce() is called to indicate a change in the
+ // clipboard on the server. Call requestClipboard() to access the
+ // actual data.
+ virtual void handleClipboardAnnounce(bool available);
+
+ // handleClipboardData() is called when the server has sent over
+ // the clipboard data as a result of a previous call to
+ // requestClipboard(). Note that this function might never be
+ // called if the clipboard data was no longer available when the
+ // server received the request.
+ virtual void handleClipboardData(const char* data);
+
// Other methods
+ // requestClipboard() will result in a request to the server to
+ // transfer its clipboard data. A call to handleClipboardData()
+ // will be made once the data is available.
+ virtual void requestClipboard();
+
+ // announceClipboard() informs the server of changes to the
+ // clipboard on the client. The server may later request the
+ // clipboard data via handleClipboardRequest().
+ virtual void announceClipboard(bool available);
+
+ // sendClipboardData() transfers the clipboard data to the server
+ // and should be called whenever the server has requested the
+ // clipboard via handleClipboardRequest().
+ virtual void sendClipboardData(const char* data);
+
// refreshFramebuffer() forces a complete refresh of the entire
// framebuffer
void refreshFramebuffer();
@@ -240,6 +284,9 @@ namespace rfb {
ModifiablePixelBuffer* framebuffer;
DecodeManager decoder;
+
+ char* serverClipboard;
+ bool hasLocalClipboard;
};
}
#endif
diff --git a/common/rfb/CMsgHandler.cxx b/common/rfb/CMsgHandler.cxx
index c009067a..9dab5d94 100644
--- a/common/rfb/CMsgHandler.cxx
+++ b/common/rfb/CMsgHandler.cxx
@@ -1,5 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2009-2011 Pierre Ossman for Cendio AB
+ * Copyright 2009-2019 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -98,3 +98,26 @@ void CMsgHandler::setLEDState(unsigned int state)
{
server.setLEDState(state);
}
+
+void CMsgHandler::handleClipboardCaps(rdr::U32 flags, const rdr::U32* lengths)
+{
+ server.setClipboardCaps(flags, lengths);
+}
+
+void CMsgHandler::handleClipboardRequest(rdr::U32 flags)
+{
+}
+
+void CMsgHandler::handleClipboardPeek(rdr::U32 flags)
+{
+}
+
+void CMsgHandler::handleClipboardNotify(rdr::U32 flags)
+{
+}
+
+void CMsgHandler::handleClipboardProvide(rdr::U32 flags,
+ const size_t* lengths,
+ const rdr::U8* const* data)
+{
+}
diff --git a/common/rfb/CMsgHandler.h b/common/rfb/CMsgHandler.h
index effdaabf..84dd115c 100644
--- a/common/rfb/CMsgHandler.h
+++ b/common/rfb/CMsgHandler.h
@@ -1,5 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2009-2011 Pierre Ossman for Cendio AB
+ * Copyright 2009-2019 Pierre Ossman for Cendio AB
* Copyright (C) 2011 D. R. Commander. All Rights Reserved.
*
* This is free software; you can redistribute it and/or modify
@@ -42,8 +42,9 @@ namespace rfb {
// The following methods are called as corresponding messages are read. A
// derived class should override these methods as desired. Note that for
// the setDesktopSize(), setExtendedDesktopSize(), setPixelFormat(),
- // setName() and serverInit() methods, a derived class should call on to
- // CMsgHandler's methods to set the members of "server" appropriately.
+ // setName(), serverInit() and clipboardCaps methods, a derived class
+ // should call on to CMsgHandler's methods to set the members of "server"
+ // appropriately.
virtual void setDesktopSize(int w, int h);
virtual void setExtendedDesktopSize(unsigned reason, unsigned result,
@@ -70,10 +71,19 @@ namespace rfb {
virtual void setColourMapEntries(int firstColour, int nColours,
rdr::U16* rgbs) = 0;
virtual void bell() = 0;
- virtual void serverCutText(const char* str, rdr::U32 len) = 0;
+ virtual void serverCutText(const char* str) = 0;
virtual void setLEDState(unsigned int state);
+ virtual void handleClipboardCaps(rdr::U32 flags,
+ const rdr::U32* lengths);
+ virtual void handleClipboardRequest(rdr::U32 flags);
+ virtual void handleClipboardPeek(rdr::U32 flags);
+ virtual void handleClipboardNotify(rdr::U32 flags);
+ virtual void handleClipboardProvide(rdr::U32 flags,
+ const size_t* lengths,
+ const rdr::U8* const* data);
+
ServerParams server;
};
}
diff --git a/common/rfb/CMsgReader.cxx b/common/rfb/CMsgReader.cxx
index 2b5b9fbf..a9e12d70 100644
--- a/common/rfb/CMsgReader.cxx
+++ b/common/rfb/CMsgReader.cxx
@@ -1,5 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2009-2017 Pierre Ossman for Cendio AB
+ * Copyright 2009-2019 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,8 +20,11 @@
#include <assert.h>
#include <stdio.h>
-#include <rfb/msgTypes.h>
#include <rdr/InStream.h>
+#include <rdr/ZlibInStream.h>
+
+#include <rfb/msgTypes.h>
+#include <rfb/clipboardTypes.h>
#include <rfb/Exception.h>
#include <rfb/LogWriter.h>
#include <rfb/util.h>
@@ -30,6 +33,8 @@
static rfb::LogWriter vlog("CMsgReader");
+static rfb::IntParameter maxCutText("MaxCutText", "Maximum permitted length of an incoming clipboard update", 256*1024);
+
using namespace rfb;
CMsgReader::CMsgReader(CMsgHandler* handler_, rdr::InStream* is_)
@@ -152,15 +157,116 @@ void CMsgReader::readServerCutText()
{
is->skip(3);
rdr::U32 len = is->readU32();
- if (len > 256*1024) {
+
+ if (len & 0x80000000) {
+ rdr::S32 slen = len;
+ slen = -slen;
+ readExtendedClipboard(slen);
+ return;
+ }
+
+ if (len > (size_t)maxCutText) {
is->skip(len);
vlog.error("cut text too long (%d bytes) - ignoring",len);
return;
}
- CharArray ca(len+1);
- ca.buf[len] = 0;
+ CharArray ca(len);
is->readBytes(ca.buf, len);
- handler->serverCutText(ca.buf, len);
+ CharArray filtered(convertLF(ca.buf, len));
+ handler->serverCutText(filtered.buf);
+}
+
+void CMsgReader::readExtendedClipboard(rdr::S32 len)
+{
+ rdr::U32 flags;
+ rdr::U32 action;
+
+ if (len < 4)
+ throw Exception("Invalid extended clipboard message");
+ if (len > maxCutText) {
+ vlog.error("Extended clipboard message too long (%d bytes) - ignoring", len);
+ is->skip(len);
+ return;
+ }
+
+ flags = is->readU32();
+ action = flags & clipboardActionMask;
+
+ if (action & clipboardCaps) {
+ int i;
+ size_t num;
+ rdr::U32 lengths[16];
+
+ num = 0;
+ for (i = 0;i < 16;i++) {
+ if (flags & (1 << i))
+ num++;
+ }
+
+ if (len < (rdr::S32)(4 + 4*num))
+ throw Exception("Invalid extended clipboard message");
+
+ num = 0;
+ for (i = 0;i < 16;i++) {
+ if (flags & (1 << i))
+ lengths[num++] = is->readU32();
+ }
+
+ handler->handleClipboardCaps(flags, lengths);
+ } else if (action == clipboardProvide) {
+ rdr::ZlibInStream zis;
+
+ int i;
+ size_t num;
+ size_t lengths[16];
+ rdr::U8* buffers[16];
+
+ zis.setUnderlying(is, len - 4);
+
+ num = 0;
+ for (i = 0;i < 16;i++) {
+ if (!(flags & 1 << i))
+ continue;
+
+ lengths[num] = zis.readU32();
+ if (lengths[num] > (size_t)maxCutText) {
+ vlog.error("Extended clipboard data too long (%d bytes) - ignoring",
+ (unsigned)lengths[num]);
+ zis.skip(lengths[num]);
+ flags &= ~(1 << i);
+ continue;
+ }
+
+ buffers[num] = new rdr::U8[lengths[num]];
+ zis.readBytes(buffers[num], lengths[num]);
+ num++;
+ }
+
+ zis.removeUnderlying();
+
+ handler->handleClipboardProvide(flags, lengths, buffers);
+
+ num = 0;
+ for (i = 0;i < 16;i++) {
+ if (!(flags & 1 << i))
+ continue;
+ delete [] buffers[num++];
+ }
+ } else {
+ switch (action) {
+ case clipboardRequest:
+ handler->handleClipboardRequest(flags);
+ break;
+ case clipboardPeek:
+ handler->handleClipboardPeek(flags);
+ break;
+ case clipboardNotify:
+ handler->handleClipboardNotify(flags);
+ break;
+ default:
+ throw Exception("Invalid extended clipboard action");
+ }
+ }
}
void CMsgReader::readFence()
diff --git a/common/rfb/CMsgReader.h b/common/rfb/CMsgReader.h
index 03f3d8d2..050990a9 100644
--- a/common/rfb/CMsgReader.h
+++ b/common/rfb/CMsgReader.h
@@ -1,5 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2009-2014 Pierre Ossman for Cendio AB
+ * Copyright 2009-2019 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -53,6 +53,7 @@ namespace rfb {
void readSetColourMapEntries();
void readBell();
void readServerCutText();
+ void readExtendedClipboard(rdr::S32 len);
void readFence();
void readEndOfContinuousUpdates();
diff --git a/common/rfb/CMsgWriter.cxx b/common/rfb/CMsgWriter.cxx
index d357c976..3180391b 100644
--- a/common/rfb/CMsgWriter.cxx
+++ b/common/rfb/CMsgWriter.cxx
@@ -1,5 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2009-2014 Pierre Ossman for Cendio AB
+ * Copyright 2009-2019 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,10 +17,15 @@
* USA.
*/
#include <stdio.h>
+
#include <rdr/OutStream.h>
+#include <rdr/MemOutStream.h>
+#include <rdr/ZlibOutStream.h>
+
#include <rfb/msgTypes.h>
#include <rfb/fenceTypes.h>
#include <rfb/qemuTypes.h>
+#include <rfb/clipboardTypes.h>
#include <rfb/Exception.h>
#include <rfb/PixelFormat.h>
#include <rfb/Rect.h>
@@ -179,8 +184,14 @@ void CMsgWriter::writePointerEvent(const Point& pos, int buttonMask)
}
-void CMsgWriter::writeClientCutText(const char* str, rdr::U32 len)
+void CMsgWriter::writeClientCutText(const char* str)
{
+ size_t len;
+
+ if (strchr(str, '\r') != NULL)
+ throw Exception("Invalid carriage return in clipboard data");
+
+ len = strlen(str);
startMsg(msgTypeClientCutText);
os->pad(3);
os->writeU32(len);
@@ -188,6 +199,104 @@ void CMsgWriter::writeClientCutText(const char* str, rdr::U32 len)
endMsg();
}
+void CMsgWriter::writeClipboardCaps(rdr::U32 caps,
+ const rdr::U32* lengths)
+{
+ size_t i, count;
+
+ if (!(server->clipboardFlags() & clipboardCaps))
+ throw Exception("Server does not support clipboard \"caps\" action");
+
+ count = 0;
+ for (i = 0;i < 16;i++) {
+ if (caps & (1 << i))
+ count++;
+ }
+
+ startMsg(msgTypeClientCutText);
+ os->pad(3);
+ os->writeS32(-(4 + 4 * count));
+
+ os->writeU32(caps | clipboardCaps);
+
+ count = 0;
+ for (i = 0;i < 16;i++) {
+ if (caps & (1 << i))
+ os->writeU32(lengths[count++]);
+ }
+
+ endMsg();
+}
+
+void CMsgWriter::writeClipboardRequest(rdr::U32 flags)
+{
+ if (!(server->clipboardFlags() & clipboardRequest))
+ throw Exception("Server does not support clipboard \"request\" action");
+
+ startMsg(msgTypeClientCutText);
+ os->pad(3);
+ os->writeS32(-4);
+ os->writeU32(flags | clipboardRequest);
+ endMsg();
+}
+
+void CMsgWriter::writeClipboardPeek(rdr::U32 flags)
+{
+ if (!(server->clipboardFlags() & clipboardPeek))
+ throw Exception("Server does not support clipboard \"peek\" action");
+
+ startMsg(msgTypeClientCutText);
+ os->pad(3);
+ os->writeS32(-4);
+ os->writeU32(flags | clipboardPeek);
+ endMsg();
+}
+
+void CMsgWriter::writeClipboardNotify(rdr::U32 flags)
+{
+ if (!(server->clipboardFlags() & clipboardNotify))
+ throw Exception("Server does not support clipboard \"notify\" action");
+
+ startMsg(msgTypeClientCutText);
+ os->pad(3);
+ os->writeS32(-4);
+ os->writeU32(flags | clipboardNotify);
+ endMsg();
+}
+
+void CMsgWriter::writeClipboardProvide(rdr::U32 flags,
+ const size_t* lengths,
+ const rdr::U8* const* data)
+{
+ rdr::MemOutStream mos;
+ rdr::ZlibOutStream zos;
+
+ int i, count;
+
+ if (!(server->clipboardFlags() & clipboardProvide))
+ throw Exception("Server does not support clipboard \"provide\" action");
+
+ zos.setUnderlying(&mos);
+
+ count = 0;
+ for (i = 0;i < 16;i++) {
+ if (!(flags & (1 << i)))
+ continue;
+ zos.writeU32(lengths[count]);
+ zos.writeBytes(data[count], lengths[count]);
+ count++;
+ }
+
+ zos.flush();
+
+ startMsg(msgTypeClientCutText);
+ os->pad(3);
+ os->writeS32(-(4 + mos.length()));
+ os->writeU32(flags | clipboardProvide);
+ os->writeBytes(mos.data(), mos.length());
+ endMsg();
+}
+
void CMsgWriter::startMsg(int type)
{
os->writeU8(type);
diff --git a/common/rfb/CMsgWriter.h b/common/rfb/CMsgWriter.h
index 4d533d42..7b839393 100644
--- a/common/rfb/CMsgWriter.h
+++ b/common/rfb/CMsgWriter.h
@@ -1,5 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2009-2014 Pierre Ossman for Cendio AB
+ * Copyright 2009-2019 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -55,7 +55,15 @@ namespace rfb {
void writeKeyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down);
void writePointerEvent(const Point& pos, int buttonMask);
- void writeClientCutText(const char* str, rdr::U32 len);
+
+ void writeClientCutText(const char* str);
+
+ void writeClipboardCaps(rdr::U32 caps, const rdr::U32* lengths);
+ void writeClipboardRequest(rdr::U32 flags);
+ void writeClipboardPeek(rdr::U32 flags);
+ void writeClipboardNotify(rdr::U32 flags);
+ void writeClipboardProvide(rdr::U32 flags, const size_t* lengths,
+ const rdr::U8* const* data);
protected:
void startMsg(int type);
diff --git a/common/rfb/ClientParams.cxx b/common/rfb/ClientParams.cxx
index e42d494b..987abe32 100644
--- a/common/rfb/ClientParams.cxx
+++ b/common/rfb/ClientParams.cxx
@@ -1,6 +1,6 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright (C) 2011 D. R. Commander. All Rights Reserved.
- * Copyright 2014-2018 Pierre Ossman for Cendio AB
+ * Copyright 2014-2019 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,6 +20,7 @@
#include <rfb/Exception.h>
#include <rfb/encodings.h>
#include <rfb/ledStates.h>
+#include <rfb/clipboardTypes.h>
#include <rfb/ClientParams.h>
using namespace rfb;
@@ -32,7 +33,13 @@ ClientParams::ClientParams()
ledState_(ledUnknown)
{
setName("");
+
cursor_ = new Cursor(0, 0, Point(), NULL);
+
+ clipFlags = clipboardUTF8 | clipboardRTF | clipboardHTML |
+ clipboardRequest | clipboardNotify | clipboardProvide;
+ memset(clipSizes, 0, sizeof(clipSizes));
+ clipSizes[0] = 20 * 1024 * 1024;
}
ClientParams::~ClientParams()
@@ -136,6 +143,20 @@ void ClientParams::setLEDState(unsigned int state)
ledState_ = state;
}
+void ClientParams::setClipboardCaps(rdr::U32 flags, const rdr::U32* lengths)
+{
+ int i, num;
+
+ clipFlags = flags;
+
+ num = 0;
+ for (i = 0;i < 16;i++) {
+ if (!(flags & (1 << i)))
+ continue;
+ clipSizes[i] = lengths[num++];
+ }
+}
+
bool ClientParams::supportsLocalCursor() const
{
if (supportsEncoding(pseudoEncodingCursorWithAlpha))
diff --git a/common/rfb/ClientParams.h b/common/rfb/ClientParams.h
index f7a7044b..aab3d644 100644
--- a/common/rfb/ClientParams.h
+++ b/common/rfb/ClientParams.h
@@ -1,5 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2014-2018 Pierre Ossman for Cendio AB
+ * Copyright 2014-2019 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -84,6 +84,9 @@ namespace rfb {
unsigned int ledState() { return ledState_; }
void setLEDState(unsigned int state);
+ rdr::U32 clipboardFlags() const { return clipFlags; }
+ void setClipboardCaps(rdr::U32 flags, const rdr::U32* lengths);
+
// Wrappers to check for functionality rather than specific
// encodings
bool supportsLocalCursor() const;
@@ -108,6 +111,8 @@ namespace rfb {
Cursor* cursor_;
std::set<rdr::S32> encodings_;
unsigned int ledState_;
+ rdr::U32 clipFlags;
+ rdr::U32 clipSizes[16];
};
}
#endif
diff --git a/common/rfb/InputHandler.h b/common/rfb/InputHandler.h
index 6c072849..b91f0e42 100644
--- a/common/rfb/InputHandler.h
+++ b/common/rfb/InputHandler.h
@@ -37,8 +37,7 @@ namespace rfb {
bool __unused_attr down) { }
virtual void pointerEvent(const Point& __unused_attr pos,
int __unused_attr buttonMask) { }
- virtual void clientCutText(const char* __unused_attr str,
- int __unused_attr len) { }
+ virtual void clientCutText(const char* __unused_attr str) { }
};
}
diff --git a/common/rfb/SConnection.cxx b/common/rfb/SConnection.cxx
index 4e224aa1..4869199a 100644
--- a/common/rfb/SConnection.cxx
+++ b/common/rfb/SConnection.cxx
@@ -1,5 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2011 Pierre Ossman for Cendio AB
+ * Copyright 2011-2019 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,6 +20,7 @@
#include <string.h>
#include <rfb/Exception.h>
#include <rfb/Security.h>
+#include <rfb/clipboardTypes.h>
#include <rfb/msgTypes.h>
#include <rfb/fenceTypes.h>
#include <rfb/SMsgReader.h>
@@ -52,7 +53,8 @@ SConnection::SConnection()
: readyForSetColourMapEntries(false),
is(0), os(0), reader_(0), writer_(0),
ssecurity(0), state_(RFBSTATE_UNINITIALISED),
- preferredEncoding(encodingRaw)
+ preferredEncoding(encodingRaw),
+ clientClipboard(NULL), hasLocalClipboard(false)
{
defaultMajorVersion = 3;
defaultMinorVersion = 8;
@@ -70,6 +72,7 @@ SConnection::~SConnection()
reader_ = 0;
delete writer_;
writer_ = 0;
+ strFree(clientClipboard);
}
void SConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
@@ -297,6 +300,69 @@ void SConnection::setEncodings(int nEncodings, const rdr::S32* encodings)
}
SMsgHandler::setEncodings(nEncodings, encodings);
+
+ if (client.supportsEncoding(pseudoEncodingExtendedClipboard)) {
+ rdr::U32 sizes[] = { 0 };
+ writer()->writeClipboardCaps(rfb::clipboardUTF8 |
+ rfb::clipboardRequest |
+ rfb::clipboardPeek |
+ rfb::clipboardNotify |
+ rfb::clipboardProvide,
+ sizes);
+ }
+}
+
+void SConnection::clientCutText(const char* str)
+{
+ strFree(clientClipboard);
+ clientClipboard = NULL;
+
+ clientClipboard = latin1ToUTF8(str);
+
+ handleClipboardAnnounce(true);
+}
+
+void SConnection::handleClipboardRequest(rdr::U32 flags)
+{
+ if (!(flags & rfb::clipboardUTF8))
+ return;
+ if (!hasLocalClipboard)
+ return;
+ handleClipboardRequest();
+}
+
+void SConnection::handleClipboardPeek(rdr::U32 flags)
+{
+ if (!hasLocalClipboard)
+ return;
+ if (client.clipboardFlags() & rfb::clipboardNotify)
+ writer()->writeClipboardNotify(rfb::clipboardUTF8);
+}
+
+void SConnection::handleClipboardNotify(rdr::U32 flags)
+{
+ strFree(clientClipboard);
+ clientClipboard = NULL;
+
+ if (flags & rfb::clipboardUTF8)
+ handleClipboardAnnounce(true);
+ else
+ handleClipboardAnnounce(false);
+}
+
+void SConnection::handleClipboardProvide(rdr::U32 flags,
+ const size_t* lengths,
+ const rdr::U8* const* data)
+{
+ if (!(flags & rfb::clipboardUTF8))
+ return;
+
+ strFree(clientClipboard);
+ clientClipboard = NULL;
+
+ clientClipboard = convertLF((const char*)data[0], lengths[0]);
+
+ handleClipboardData(clientClipboard);
}
void SConnection::supportsQEMUKeyEvent()
@@ -410,6 +476,58 @@ void SConnection::enableContinuousUpdates(bool enable,
{
}
+void SConnection::handleClipboardRequest()
+{
+}
+
+void SConnection::handleClipboardAnnounce(bool available)
+{
+}
+
+void SConnection::handleClipboardData(const char* data)
+{
+}
+
+void SConnection::requestClipboard()
+{
+ if (clientClipboard != NULL) {
+ handleClipboardData(clientClipboard);
+ return;
+ }
+
+ if (client.supportsEncoding(pseudoEncodingExtendedClipboard) &&
+ (client.clipboardFlags() & rfb::clipboardRequest))
+ writer()->writeClipboardRequest(rfb::clipboardUTF8);
+}
+
+void SConnection::announceClipboard(bool available)
+{
+ hasLocalClipboard = available;
+
+ if (client.supportsEncoding(pseudoEncodingExtendedClipboard) &&
+ (client.clipboardFlags() & rfb::clipboardNotify))
+ writer()->writeClipboardNotify(available ? rfb::clipboardUTF8 : 0);
+ else {
+ if (available)
+ handleClipboardRequest();
+ }
+}
+
+void SConnection::sendClipboardData(const char* data)
+{
+ if (client.supportsEncoding(pseudoEncodingExtendedClipboard) &&
+ (client.clipboardFlags() & rfb::clipboardProvide)) {
+ CharArray filtered(convertCRLF(data));
+ size_t sizes[1] = { strlen(filtered.buf) + 1 };
+ const rdr::U8* data[1] = { (const rdr::U8*)filtered.buf };
+ writer()->writeClipboardProvide(rfb::clipboardUTF8, sizes, data);
+ } else {
+ CharArray latin1(utf8ToLatin1(data));
+
+ writer()->writeServerCutText(latin1.buf);
+ }
+}
+
void SConnection::writeFakeColourMap(void)
{
int i;
diff --git a/common/rfb/SConnection.h b/common/rfb/SConnection.h
index 31d1cb2e..db3ab08c 100644
--- a/common/rfb/SConnection.h
+++ b/common/rfb/SConnection.h
@@ -1,5 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2011 Pierre Ossman for Cendio AB
+ * Copyright 2011-2019 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -80,8 +80,18 @@ namespace rfb {
virtual void setEncodings(int nEncodings, const rdr::S32* encodings);
+ virtual void clientCutText(const char* str);
+
+ virtual void handleClipboardRequest(rdr::U32 flags);
+ virtual void handleClipboardPeek(rdr::U32 flags);
+ virtual void handleClipboardNotify(rdr::U32 flags);
+ virtual void handleClipboardProvide(rdr::U32 flags,
+ const size_t* lengths,
+ const rdr::U8* const* data);
+
virtual void supportsQEMUKeyEvent();
+
// Methods to be overridden in a derived class
// versionReceived() indicates that the version number has just been read
@@ -129,8 +139,42 @@ namespace rfb {
virtual void enableContinuousUpdates(bool enable,
int x, int y, int w, int h);
+ // handleClipboardRequest() is called whenever the client requests
+ // the server to send over its clipboard data. It will only be
+ // called after the server has first announced a clipboard change
+ // via announceClipboard().
+ virtual void handleClipboardRequest();
+
+ // handleClipboardAnnounce() is called to indicate a change in the
+ // clipboard on the client. Call requestClipboard() to access the
+ // actual data.
+ virtual void handleClipboardAnnounce(bool available);
+
+ // handleClipboardData() is called when the client has sent over
+ // the clipboard data as a result of a previous call to
+ // requestClipboard(). Note that this function might never be
+ // called if the clipboard data was no longer available when the
+ // client received the request.
+ virtual void handleClipboardData(const char* data);
+
+
// Other methods
+ // requestClipboard() will result in a request to the client to
+ // transfer its clipboard data. A call to handleClipboardData()
+ // will be made once the data is available.
+ virtual void requestClipboard();
+
+ // announceClipboard() informs the client of changes to the
+ // clipboard on the server. The client may later request the
+ // clipboard data via handleClipboardRequest().
+ virtual void announceClipboard(bool available);
+
+ // sendClipboardData() transfers the clipboard data to the client
+ // and should be called whenever the client has requested the
+ // clipboard via handleClipboardRequest().
+ virtual void sendClipboardData(const char* data);
+
// setAccessRights() allows a security package to limit the access rights
// of a SConnection to the server. How the access rights are treated
// is up to the derived class.
@@ -208,6 +252,9 @@ namespace rfb {
stateEnum state_;
rdr::S32 preferredEncoding;
AccessRights accessRights;
+
+ char* clientClipboard;
+ bool hasLocalClipboard;
};
}
#endif
diff --git a/common/rfb/SDesktop.h b/common/rfb/SDesktop.h
index 0060aa23..b4104ea7 100644
--- a/common/rfb/SDesktop.h
+++ b/common/rfb/SDesktop.h
@@ -1,4 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ * Copyright 2009-2019 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -93,6 +94,25 @@ namespace rfb {
// pointerEvent(), keyEvent() and clientCutText() are called in response to
// the relevant RFB protocol messages from clients.
// See InputHandler for method signatures.
+
+ // handleClipboardRequest() is called whenever a client requests
+ // the server to send over its clipboard data. It will only be
+ // called after the server has first announced a clipboard change
+ // via VNCServer::announceClipboard().
+ virtual void handleClipboardRequest() {}
+
+ // handleClipboardAnnounce() is called to indicate a change in the
+ // clipboard on a client. Call VNCServer::requestClipboard() to
+ // access the actual data.
+ virtual void handleClipboardAnnounce(bool __unused_attr available) {}
+
+ // handleClipboardData() is called when a client has sent over
+ // the clipboard data as a result of a previous call to
+ // VNCServer::requestClipboard(). Note that this function might
+ // never be called if the clipboard data was no longer available
+ // when the client received the request.
+ virtual void handleClipboardData(const char* __unused_attr data) {}
+
protected:
virtual ~SDesktop() {}
};
diff --git a/common/rfb/SMsgHandler.cxx b/common/rfb/SMsgHandler.cxx
index f952ec2b..32b561e7 100644
--- a/common/rfb/SMsgHandler.cxx
+++ b/common/rfb/SMsgHandler.cxx
@@ -1,5 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2009-2011 Pierre Ossman for Cendio AB
+ * Copyright 2009-2019 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -64,6 +64,29 @@ void SMsgHandler::setEncodings(int nEncodings, const rdr::S32* encodings)
supportsQEMUKeyEvent();
}
+void SMsgHandler::handleClipboardCaps(rdr::U32 flags, const rdr::U32* lengths)
+{
+ client.setClipboardCaps(flags, lengths);
+}
+
+void SMsgHandler::handleClipboardRequest(rdr::U32 flags)
+{
+}
+
+void SMsgHandler::handleClipboardPeek(rdr::U32 flags)
+{
+}
+
+void SMsgHandler::handleClipboardNotify(rdr::U32 flags)
+{
+}
+
+void SMsgHandler::handleClipboardProvide(rdr::U32 flags,
+ const size_t* lengths,
+ const rdr::U8* const* data)
+{
+}
+
void SMsgHandler::supportsLocalCursor()
{
}
diff --git a/common/rfb/SMsgHandler.h b/common/rfb/SMsgHandler.h
index 8548d911..b290f194 100644
--- a/common/rfb/SMsgHandler.h
+++ b/common/rfb/SMsgHandler.h
@@ -1,5 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2009-2011 Pierre Ossman for Cendio AB
+ * Copyright 2009-2019 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -40,8 +40,8 @@ namespace rfb {
// The following methods are called as corresponding messages are read. A
// derived class should override these methods as desired. Note that for
- // the setPixelFormat(), and setEncodings() methods, a derived class must
- // call on to SMsgHandler's methods.
+ // the setPixelFormat(), setEncodings() and clipboardCaps() methods, a
+ // derived class must call on to SMsgHandler's methods.
virtual void clientInit(bool shared);
@@ -54,6 +54,15 @@ namespace rfb {
virtual void enableContinuousUpdates(bool enable,
int x, int y, int w, int h) = 0;
+ virtual void handleClipboardCaps(rdr::U32 flags,
+ const rdr::U32* lengths);
+ virtual void handleClipboardRequest(rdr::U32 flags);
+ virtual void handleClipboardPeek(rdr::U32 flags);
+ virtual void handleClipboardNotify(rdr::U32 flags);
+ virtual void handleClipboardProvide(rdr::U32 flags,
+ const size_t* lengths,
+ const rdr::U8* const* data);
+
// InputHandler interface
// The InputHandler methods will be called for the corresponding messages.
diff --git a/common/rfb/SMsgReader.cxx b/common/rfb/SMsgReader.cxx
index 200350c1..ab42e59a 100644
--- a/common/rfb/SMsgReader.cxx
+++ b/common/rfb/SMsgReader.cxx
@@ -1,5 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2009-2014 Pierre Ossman for Cendio AB
+ * Copyright 2009-2019 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,9 +17,13 @@
* USA.
*/
#include <stdio.h>
+
#include <rdr/InStream.h>
+#include <rdr/ZlibInStream.h>
+
#include <rfb/msgTypes.h>
#include <rfb/qemuTypes.h>
+#include <rfb/clipboardTypes.h>
#include <rfb/Exception.h>
#include <rfb/util.h>
#include <rfb/SMsgHandler.h>
@@ -203,19 +207,117 @@ void SMsgReader::readPointerEvent()
void SMsgReader::readClientCutText()
{
is->skip(3);
- int len = is->readU32();
- if (len < 0) {
- throw Exception("Cut text too long.");
+ rdr::U32 len = is->readU32();
+
+ if (len & 0x80000000) {
+ rdr::S32 slen = len;
+ slen = -slen;
+ readExtendedClipboard(slen);
+ return;
}
- if (len > maxCutText) {
+
+ if (len > (size_t)maxCutText) {
is->skip(len);
vlog.error("Cut text too long (%d bytes) - ignoring", len);
return;
}
- CharArray ca(len+1);
- ca.buf[len] = 0;
+ CharArray ca(len);
is->readBytes(ca.buf, len);
- handler->clientCutText(ca.buf, len);
+ CharArray filtered(convertLF(ca.buf, len));
+ handler->clientCutText(filtered.buf);
+}
+
+void SMsgReader::readExtendedClipboard(rdr::S32 len)
+{
+ rdr::U32 flags;
+ rdr::U32 action;
+
+ if (len < 4)
+ throw Exception("Invalid extended clipboard message");
+ if (len > maxCutText) {
+ vlog.error("Extended clipboard message too long (%d bytes) - ignoring", len);
+ is->skip(len);
+ return;
+ }
+
+ flags = is->readU32();
+ action = flags & clipboardActionMask;
+
+ if (action & clipboardCaps) {
+ int i;
+ size_t num;
+ rdr::U32 lengths[16];
+
+ num = 0;
+ for (i = 0;i < 16;i++) {
+ if (flags & (1 << i))
+ num++;
+ }
+
+ if (len < (rdr::S32)(4 + 4*num))
+ throw Exception("Invalid extended clipboard message");
+
+ num = 0;
+ for (i = 0;i < 16;i++) {
+ if (flags & (1 << i))
+ lengths[num++] = is->readU32();
+ }
+
+ handler->handleClipboardCaps(flags, lengths);
+ } else if (action == clipboardProvide) {
+ rdr::ZlibInStream zis;
+
+ int i;
+ size_t num;
+ size_t lengths[16];
+ rdr::U8* buffers[16];
+
+ zis.setUnderlying(is, len - 4);
+
+ num = 0;
+ for (i = 0;i < 16;i++) {
+ if (!(flags & 1 << i))
+ continue;
+
+ lengths[num] = zis.readU32();
+ if (lengths[num] > (size_t)maxCutText) {
+ vlog.error("Extended clipboard data too long (%d bytes) - ignoring",
+ (unsigned)lengths[num]);
+ zis.skip(lengths[num]);
+ flags &= ~(1 << i);
+ continue;
+ }
+
+ buffers[num] = new rdr::U8[lengths[num]];
+ zis.readBytes(buffers[num], lengths[num]);
+ num++;
+ }
+
+ zis.removeUnderlying();
+
+ handler->handleClipboardProvide(flags, lengths, buffers);
+
+ num = 0;
+ for (i = 0;i < 16;i++) {
+ if (!(flags & 1 << i))
+ continue;
+ delete [] buffers[num++];
+ }
+ } else {
+ switch (action) {
+ case clipboardRequest:
+ handler->handleClipboardRequest(flags);
+ break;
+ case clipboardPeek:
+ handler->handleClipboardPeek(flags);
+ break;
+ case clipboardNotify:
+ handler->handleClipboardNotify(flags);
+ break;
+ default:
+ throw Exception("Invalid extended clipboard action");
+ }
+ }
}
void SMsgReader::readQEMUMessage()
diff --git a/common/rfb/SMsgReader.h b/common/rfb/SMsgReader.h
index 146b29f8..4991fd38 100644
--- a/common/rfb/SMsgReader.h
+++ b/common/rfb/SMsgReader.h
@@ -1,5 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2009-2014 Pierre Ossman for Cendio AB
+ * Copyright 2009-2019 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -54,6 +54,7 @@ namespace rfb {
void readKeyEvent();
void readPointerEvent();
void readClientCutText();
+ void readExtendedClipboard(rdr::S32 len);
void readQEMUMessage();
void readQEMUKeyEvent();
diff --git a/common/rfb/SMsgWriter.cxx b/common/rfb/SMsgWriter.cxx
index 6a2c2ba0..becf6e70 100644
--- a/common/rfb/SMsgWriter.cxx
+++ b/common/rfb/SMsgWriter.cxx
@@ -1,6 +1,6 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright (C) 2011 D. R. Commander. All Rights Reserved.
- * Copyright 2009-2017 Pierre Ossman for Cendio AB
+ * Copyright 2009-2019 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,9 +18,14 @@
* USA.
*/
#include <stdio.h>
+
#include <rdr/OutStream.h>
+#include <rdr/MemOutStream.h>
+#include <rdr/ZlibOutStream.h>
+
#include <rfb/msgTypes.h>
#include <rfb/fenceTypes.h>
+#include <rfb/clipboardTypes.h>
#include <rfb/Exception.h>
#include <rfb/ClientParams.h>
#include <rfb/UpdateTracker.h>
@@ -78,8 +83,14 @@ void SMsgWriter::writeBell()
endMsg();
}
-void SMsgWriter::writeServerCutText(const char* str, int len)
+void SMsgWriter::writeServerCutText(const char* str)
{
+ size_t len;
+
+ if (strchr(str, '\r') != NULL)
+ throw Exception("Invalid carriage return in clipboard data");
+
+ len = strlen(str);
startMsg(msgTypeServerCutText);
os->pad(3);
os->writeU32(len);
@@ -87,6 +98,112 @@ void SMsgWriter::writeServerCutText(const char* str, int len)
endMsg();
}
+void SMsgWriter::writeClipboardCaps(rdr::U32 caps,
+ const rdr::U32* lengths)
+{
+ size_t i, count;
+
+ if (!client->supportsEncoding(pseudoEncodingExtendedClipboard))
+ throw Exception("Client does not support extended clipboard");
+
+ count = 0;
+ for (i = 0;i < 16;i++) {
+ if (caps & (1 << i))
+ count++;
+ }
+
+ startMsg(msgTypeServerCutText);
+ os->pad(3);
+ os->writeS32(-(4 + 4 * count));
+
+ os->writeU32(caps | clipboardCaps);
+
+ count = 0;
+ for (i = 0;i < 16;i++) {
+ if (caps & (1 << i))
+ os->writeU32(lengths[count++]);
+ }
+
+ endMsg();
+}
+
+void SMsgWriter::writeClipboardRequest(rdr::U32 flags)
+{
+ if (!client->supportsEncoding(pseudoEncodingExtendedClipboard))
+ throw Exception("Client does not support extended clipboard");
+ if (!(client->clipboardFlags() & clipboardRequest))
+ throw Exception("Client does not support clipboard \"request\" action");
+
+ startMsg(msgTypeServerCutText);
+ os->pad(3);
+ os->writeS32(-4);
+ os->writeU32(flags | clipboardRequest);
+ endMsg();
+}
+
+void SMsgWriter::writeClipboardPeek(rdr::U32 flags)
+{
+ if (!client->supportsEncoding(pseudoEncodingExtendedClipboard))
+ throw Exception("Client does not support extended clipboard");
+ if (!(client->clipboardFlags() & clipboardPeek))
+ throw Exception("Client does not support clipboard \"peek\" action");
+
+ startMsg(msgTypeServerCutText);
+ os->pad(3);
+ os->writeS32(-4);
+ os->writeU32(flags | clipboardPeek);
+ endMsg();
+}
+
+void SMsgWriter::writeClipboardNotify(rdr::U32 flags)
+{
+ if (!client->supportsEncoding(pseudoEncodingExtendedClipboard))
+ throw Exception("Client does not support extended clipboard");
+ if (!(client->clipboardFlags() & clipboardNotify))
+ throw Exception("Client does not support clipboard \"notify\" action");
+
+ startMsg(msgTypeServerCutText);
+ os->pad(3);
+ os->writeS32(-4);
+ os->writeU32(flags | clipboardNotify);
+ endMsg();
+}
+
+void SMsgWriter::writeClipboardProvide(rdr::U32 flags,
+ const size_t* lengths,
+ const rdr::U8* const* data)
+{
+ rdr::MemOutStream mos;
+ rdr::ZlibOutStream zos;
+
+ int i, count;
+
+ if (!client->supportsEncoding(pseudoEncodingExtendedClipboard))
+ throw Exception("Client does not support extended clipboard");
+ if (!(client->clipboardFlags() & clipboardProvide))
+ throw Exception("Client does not support clipboard \"provide\" action");
+
+ zos.setUnderlying(&mos);
+
+ count = 0;
+ for (i = 0;i < 16;i++) {
+ if (!(flags & (1 << i)))
+ continue;
+ zos.writeU32(lengths[count]);
+ zos.writeBytes(data[count], lengths[count]);
+ count++;
+ }
+
+ zos.flush();
+
+ startMsg(msgTypeServerCutText);
+ os->pad(3);
+ os->writeS32(-(4 + mos.length()));
+ os->writeU32(flags | clipboardProvide);
+ os->writeBytes(mos.data(), mos.length());
+ endMsg();
+}
+
void SMsgWriter::writeFence(rdr::U32 flags, unsigned len, const char data[])
{
if (!client->supportsEncoding(pseudoEncodingFence))
diff --git a/common/rfb/SMsgWriter.h b/common/rfb/SMsgWriter.h
index 4f4c9cc0..2cea44d1 100644
--- a/common/rfb/SMsgWriter.h
+++ b/common/rfb/SMsgWriter.h
@@ -1,5 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2009-2014 Pierre Ossman for Cendio AB
+ * Copyright 2009-2019 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -54,9 +54,17 @@ namespace rfb {
const rdr::U16 green[],
const rdr::U16 blue[]);
- // writeBell() and writeServerCutText() do the obvious thing.
+ // writeBell() does the obvious thing.
void writeBell();
- void writeServerCutText(const char* str, int len);
+
+ void writeServerCutText(const char* str);
+
+ void writeClipboardCaps(rdr::U32 caps, const rdr::U32* lengths);
+ void writeClipboardRequest(rdr::U32 flags);
+ void writeClipboardPeek(rdr::U32 flags);
+ void writeClipboardNotify(rdr::U32 flags);
+ void writeClipboardProvide(rdr::U32 flags, const size_t* lengths,
+ const rdr::U8* const* data);
// writeFence() sends a new fence request or response to the client.
void writeFence(rdr::U32 flags, unsigned len, const char data[]);
diff --git a/common/rfb/ServerParams.cxx b/common/rfb/ServerParams.cxx
index bfeb80d6..a2e3aa83 100644
--- a/common/rfb/ServerParams.cxx
+++ b/common/rfb/ServerParams.cxx
@@ -1,6 +1,6 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright (C) 2011 D. R. Commander. All Rights Reserved.
- * Copyright 2014-2018 Pierre Ossman for Cendio AB
+ * Copyright 2014-2019 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -32,7 +32,11 @@ ServerParams::ServerParams()
ledState_(ledUnknown)
{
setName("");
+
cursor_ = new Cursor(0, 0, Point(), NULL);
+
+ clipFlags = 0;
+ memset(clipSizes, 0, sizeof(clipSizes));
}
ServerParams::~ServerParams()
@@ -82,3 +86,17 @@ void ServerParams::setLEDState(unsigned int state)
{
ledState_ = state;
}
+
+void ServerParams::setClipboardCaps(rdr::U32 flags, const rdr::U32* lengths)
+{
+ int i, num;
+
+ clipFlags = flags;
+
+ num = 0;
+ for (i = 0;i < 16;i++) {
+ if (!(flags & (1 << i)))
+ continue;
+ clipSizes[i] = lengths[num++];
+ }
+}
diff --git a/common/rfb/ServerParams.h b/common/rfb/ServerParams.h
index 7a58ea37..c84f6252 100644
--- a/common/rfb/ServerParams.h
+++ b/common/rfb/ServerParams.h
@@ -1,5 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2014-2018 Pierre Ossman for Cendio AB
+ * Copyright 2014-2019 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -69,6 +69,9 @@ namespace rfb {
unsigned int ledState() { return ledState_; }
void setLEDState(unsigned int state);
+ rdr::U32 clipboardFlags() const { return clipFlags; }
+ void setClipboardCaps(rdr::U32 flags, const rdr::U32* lengths);
+
bool supportsQEMUKeyEvent;
bool supportsSetDesktopSize;
bool supportsFence;
@@ -84,6 +87,8 @@ namespace rfb {
char* name_;
Cursor* cursor_;
unsigned int ledState_;
+ rdr::U32 clipFlags;
+ rdr::U32 clipSizes[16];
};
}
#endif
diff --git a/common/rfb/VNCSConnectionST.cxx b/common/rfb/VNCSConnectionST.cxx
index fe00dab6..cdd87b13 100644
--- a/common/rfb/VNCSConnectionST.cxx
+++ b/common/rfb/VNCSConnectionST.cxx
@@ -1,5 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2009-2018 Pierre Ossman for Cendio AB
+ * Copyright 2009-2019 Pierre Ossman for Cendio AB
* Copyright 2018 Peter Astrand for Cendio AB
*
* This is free software; you can redistribute it and/or modify
@@ -282,51 +282,71 @@ void VNCSConnectionST::bellOrClose()
}
}
-void VNCSConnectionST::serverCutTextOrClose(const char *str, int len)
+void VNCSConnectionST::setDesktopNameOrClose(const char *name)
{
try {
- if (!accessCheck(AccessCutText)) return;
- if (!rfb::Server::sendCutText) return;
- if (state() == RFBSTATE_NORMAL)
- writer()->writeServerCutText(str, len);
+ setDesktopName(name);
+ writeFramebufferUpdate();
} catch(rdr::Exception& e) {
close(e.str());
}
}
-
-void VNCSConnectionST::setDesktopNameOrClose(const char *name)
+void VNCSConnectionST::setCursorOrClose()
{
try {
- setDesktopName(name);
+ setCursor();
writeFramebufferUpdate();
} catch(rdr::Exception& e) {
close(e.str());
}
}
-
-void VNCSConnectionST::setCursorOrClose()
+void VNCSConnectionST::setLEDStateOrClose(unsigned int state)
{
try {
- setCursor();
+ setLEDState(state);
writeFramebufferUpdate();
} catch(rdr::Exception& e) {
close(e.str());
}
}
+void VNCSConnectionST::requestClipboardOrClose()
+{
+ try {
+ if (!accessCheck(AccessCutText)) return;
+ if (!rfb::Server::acceptCutText) return;
+ if (state() != RFBSTATE_NORMAL) return;
+ requestClipboard();
+ } catch(rdr::Exception& e) {
+ close(e.str());
+ }
+}
-void VNCSConnectionST::setLEDStateOrClose(unsigned int state)
+void VNCSConnectionST::announceClipboardOrClose(bool available)
{
try {
- setLEDState(state);
- writeFramebufferUpdate();
+ if (!accessCheck(AccessCutText)) return;
+ if (!rfb::Server::sendCutText) return;
+ if (state() != RFBSTATE_NORMAL) return;
+ announceClipboard(available);
} catch(rdr::Exception& e) {
close(e.str());
}
}
+void VNCSConnectionST::sendClipboardDataOrClose(const char* data)
+{
+ try {
+ if (!accessCheck(AccessCutText)) return;
+ if (!rfb::Server::sendCutText) return;
+ if (state() != RFBSTATE_NORMAL) return;
+ sendClipboardData(data);
+ } catch(rdr::Exception& e) {
+ close(e.str());
+ }
+}
bool VNCSConnectionST::getComparerState()
{
@@ -596,13 +616,6 @@ void VNCSConnectionST::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down) {
server->keyEvent(keysym, keycode, down);
}
-void VNCSConnectionST::clientCutText(const char* str, int len)
-{
- if (!accessCheck(AccessCutText)) return;
- if (!rfb::Server::acceptCutText) return;
- server->clientCutText(str, len);
-}
-
void VNCSConnectionST::framebufferUpdateRequest(const Rect& r,bool incremental)
{
Rect safeRect;
@@ -719,6 +732,26 @@ void VNCSConnectionST::enableContinuousUpdates(bool enable,
}
}
+void VNCSConnectionST::handleClipboardRequest()
+{
+ if (!accessCheck(AccessCutText)) return;
+ server->handleClipboardRequest(this);
+}
+
+void VNCSConnectionST::handleClipboardAnnounce(bool available)
+{
+ if (!accessCheck(AccessCutText)) return;
+ if (!rfb::Server::acceptCutText) return;
+ server->handleClipboardAnnounce(this, available);
+}
+
+void VNCSConnectionST::handleClipboardData(const char* data)
+{
+ if (!accessCheck(AccessCutText)) return;
+ if (!rfb::Server::acceptCutText) return;
+ server->handleClipboardData(this, data);
+}
+
// supportsLocalCursor() is called whenever the status of
// client.supportsLocalCursor() has changed. If the client does now support local
// cursor, we make sure that the old server-side rendered cursor is cleaned up
@@ -756,7 +789,6 @@ void VNCSConnectionST::supportsLEDState()
writer()->writeLEDState();
}
-
bool VNCSConnectionST::handleTimeout(Timer* t)
{
try {
diff --git a/common/rfb/VNCSConnectionST.h b/common/rfb/VNCSConnectionST.h
index a9a8d3a4..c8f4c24f 100644
--- a/common/rfb/VNCSConnectionST.h
+++ b/common/rfb/VNCSConnectionST.h
@@ -1,5 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2009-2016 Pierre Ossman for Cendio AB
+ * Copyright 2009-2019 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -72,10 +72,12 @@ namespace rfb {
void screenLayoutChangeOrClose(rdr::U16 reason);
void setCursorOrClose();
void bellOrClose();
- void serverCutTextOrClose(const char *str, int len);
void setDesktopNameOrClose(const char *name);
void setLEDStateOrClose(unsigned int state);
void approveConnectionOrClose(bool accept, const char* reason);
+ void requestClipboardOrClose();
+ void announceClipboardOrClose(bool available);
+ void sendClipboardDataOrClose(const char* data);
// The following methods never throw exceptions
@@ -115,13 +117,15 @@ namespace rfb {
virtual void setPixelFormat(const PixelFormat& pf);
virtual void pointerEvent(const Point& pos, int buttonMask);
virtual void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down);
- virtual void clientCutText(const char* str, int len);
virtual void framebufferUpdateRequest(const Rect& r, bool incremental);
virtual void setDesktopSize(int fb_width, int fb_height,
const ScreenSet& layout);
virtual void fence(rdr::U32 flags, unsigned len, const char data[]);
virtual void enableContinuousUpdates(bool enable,
int x, int y, int w, int h);
+ virtual void handleClipboardRequest();
+ virtual void handleClipboardAnnounce(bool available);
+ virtual void handleClipboardData(const char* data);
virtual void supportsLocalCursor();
virtual void supportsFence();
virtual void supportsContinuousUpdates();
diff --git a/common/rfb/VNCServer.h b/common/rfb/VNCServer.h
index 298326f5..5d04da53 100644
--- a/common/rfb/VNCServer.h
+++ b/common/rfb/VNCServer.h
@@ -1,4 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ * Copyright 2009-2019 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -55,9 +56,21 @@ namespace rfb {
// getPixelBuffer() returns a pointer to the PixelBuffer object.
virtual const PixelBuffer* getPixelBuffer() const = 0;
- // serverCutText() tells the server that the cut text has changed. This
- // will normally be sent to all clients.
- virtual void serverCutText(const char* str, int len) = 0;
+ // requestClipboard() will result in a request to a client to
+ // transfer its clipboard data. A call to
+ // SDesktop::handleClipboardData() will be made once the data is
+ // available.
+ virtual void requestClipboard() = 0;
+
+ // announceClipboard() informs all clients of changes to the
+ // clipboard on the server. A client may later request the
+ // clipboard data via SDesktop::handleClipboardRequest().
+ virtual void announceClipboard(bool available) = 0;
+
+ // sendClipboardData() transfers the clipboard data to a client
+ // and should be called whenever a client has requested the
+ // clipboard via SDesktop::handleClipboardRequest().
+ virtual void sendClipboardData(const char* data) = 0;
// bell() tells the server that it should make all clients make a bell sound.
virtual void bell() = 0;
diff --git a/common/rfb/VNCServerST.cxx b/common/rfb/VNCServerST.cxx
index c95c14f0..a3655bca 100644
--- a/common/rfb/VNCServerST.cxx
+++ b/common/rfb/VNCServerST.cxx
@@ -1,5 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2009-2018 Pierre Ossman for Cendio AB
+ * Copyright 2009-2019 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -77,8 +77,8 @@ static LogWriter connectionsLog("Connections");
VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_)
: blHosts(&blacklist), desktop(desktop_), desktopStarted(false),
blockCounter(0), pb(0), ledState(ledUnknown),
- name(strDup(name_)), pointerClient(0), comparer(0),
- cursor(new Cursor(0, 0, Point(), NULL)),
+ name(strDup(name_)), pointerClient(0), clipboardClient(0),
+ comparer(0), cursor(new Cursor(0, 0, Point(), NULL)),
renderedCursorInvalid(false),
keyRemapper(&KeyRemapper::defInstance),
idleTimer(this), disconnectTimer(this), connectTimer(this),
@@ -167,9 +167,12 @@ void VNCServerST::removeSocket(network::Socket* sock) {
if ((*ci)->getSock() == sock) {
clients.remove(*ci);
- // - Release the cursor if this client owns it
+ // - Remove any references to it
if (pointerClient == *ci)
pointerClient = NULL;
+ if (clipboardClient == *ci)
+ clipboardClient = NULL;
+ clipboardRequestors.remove(*ci);
// Adjust the exit timers
connectTimer.stop();
@@ -331,21 +334,51 @@ void VNCServerST::setScreenLayout(const ScreenSet& layout)
}
}
-void VNCServerST::bell()
+void VNCServerST::requestClipboard()
+{
+ if (clipboardClient == NULL)
+ return;
+
+ clipboardClient->requestClipboard();
+}
+
+void VNCServerST::announceClipboard(bool available)
{
std::list<VNCSConnectionST*>::iterator ci, ci_next;
+
+ if (available)
+ clipboardClient = NULL;
+
+ clipboardRequestors.clear();
+
for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
ci_next = ci; ci_next++;
- (*ci)->bellOrClose();
+ (*ci)->announceClipboard(available);
+ }
+}
+
+void VNCServerST::sendClipboardData(const char* data)
+{
+ std::list<VNCSConnectionST*>::iterator ci, ci_next;
+
+ if (strchr(data, '\r') != NULL)
+ throw Exception("Invalid carriage return in clipboard data");
+
+ for (ci = clipboardRequestors.begin();
+ ci != clipboardRequestors.end(); ci = ci_next) {
+ ci_next = ci; ci_next++;
+ (*ci)->sendClipboardData(data);
}
+
+ clipboardRequestors.clear();
}
-void VNCServerST::serverCutText(const char* str, int len)
+void VNCServerST::bell()
{
std::list<VNCSConnectionST*>::iterator ci, ci_next;
for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
ci_next = ci; ci_next++;
- (*ci)->serverCutTextOrClose(str, len);
+ (*ci)->bellOrClose();
}
}
@@ -459,9 +492,32 @@ void VNCServerST::pointerEvent(VNCSConnectionST* client,
desktop->pointerEvent(pos, buttonMask);
}
-void VNCServerST::clientCutText(const char* str, int len)
+void VNCServerST::handleClipboardRequest(VNCSConnectionST* client)
{
- desktop->clientCutText(str, len);
+ clipboardRequestors.push_back(client);
+ if (clipboardRequestors.size() == 1)
+ desktop->handleClipboardRequest();
+}
+
+void VNCServerST::handleClipboardAnnounce(VNCSConnectionST* client,
+ bool available)
+{
+ if (available)
+ clipboardClient = client;
+ else {
+ if (client != clipboardClient)
+ return;
+ clipboardClient = NULL;
+ }
+ desktop->handleClipboardAnnounce(available);
+}
+
+void VNCServerST::handleClipboardData(VNCSConnectionST* client,
+ const char* data)
+{
+ if (client != clipboardClient)
+ return;
+ desktop->handleClipboardData(data);
}
unsigned int VNCServerST::setDesktopSize(VNCSConnectionST* requester,
diff --git a/common/rfb/VNCServerST.h b/common/rfb/VNCServerST.h
index 43a3bb95..fd20cc37 100644
--- a/common/rfb/VNCServerST.h
+++ b/common/rfb/VNCServerST.h
@@ -1,5 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2009-2016 Pierre Ossman for Cendio AB
+ * Copyright 2009-2019 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -85,7 +85,10 @@ namespace rfb {
virtual void setPixelBuffer(PixelBuffer* pb);
virtual void setScreenLayout(const ScreenSet& layout);
virtual const PixelBuffer* getPixelBuffer() const { return pb; }
- virtual void serverCutText(const char* str, int len);
+
+ virtual void requestClipboard();
+ virtual void announceClipboard(bool available);
+ virtual void sendClipboardData(const char* data);
virtual void approveConnection(network::Socket* sock, bool accept,
const char* reason);
@@ -115,7 +118,10 @@ namespace rfb {
// Event handlers
void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down);
void pointerEvent(VNCSConnectionST* client, const Point& pos, int buttonMask);
- void clientCutText(const char* str, int len);
+
+ void handleClipboardRequest(VNCSConnectionST* client);
+ void handleClipboardAnnounce(VNCSConnectionST* client, bool available);
+ void handleClipboardData(VNCSConnectionST* client, const char* data);
unsigned int setDesktopSize(VNCSConnectionST* requester,
int fb_width, int fb_height,
@@ -181,6 +187,8 @@ namespace rfb {
std::list<VNCSConnectionST*> clients;
VNCSConnectionST* pointerClient;
+ VNCSConnectionST* clipboardClient;
+ std::list<VNCSConnectionST*> clipboardRequestors;
std::list<network::Socket*> closingSockets;
ComparingUpdateTracker* comparer;
diff --git a/common/rfb/clipboardTypes.h b/common/rfb/clipboardTypes.h
new file mode 100644
index 00000000..bd3fa03d
--- /dev/null
+++ b/common/rfb/clipboardTypes.h
@@ -0,0 +1,41 @@
+/* Copyright 2019 Pierre Ossman for Cendio AB
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+#ifndef __RFB_CLIPBOARDTYPES_H__
+#define __RFB_CLIPBOARDTYPES_H__
+
+namespace rfb {
+
+ // Formats
+ const unsigned int clipboardUTF8 = 1 << 0;
+ const unsigned int clipboardRTF = 1 << 1;
+ const unsigned int clipboardHTML = 1 << 2;
+ const unsigned int clipboardDIB = 1 << 3;
+ const unsigned int clipboardFiles = 1 << 4;
+
+ const unsigned int clipboardFormatMask = 0x0000ffff;
+
+ // Actions
+ const unsigned int clipboardCaps = 1 << 24;
+ const unsigned int clipboardRequest = 1 << 25;
+ const unsigned int clipboardPeek = 1 << 26;
+ const unsigned int clipboardNotify = 1 << 27;
+ const unsigned int clipboardProvide = 1 << 28;
+
+ const unsigned int clipboardActionMask = 0xff000000;
+}
+#endif
diff --git a/common/rfb/encodings.h b/common/rfb/encodings.h
index acb86ecc..cf0c8572 100644
--- a/common/rfb/encodings.h
+++ b/common/rfb/encodings.h
@@ -63,6 +63,9 @@ namespace rfb {
const int pseudoEncodingVMwareCursor = 0x574d5664;
const int pseudoEncodingVMwareLEDState = 0x574d5668;
+ // UltraVNC-specific
+ const int pseudoEncodingExtendedClipboard = 0xC0A1E5CE;
+
int encodingNum(const char* name);
const char* encodingName(int num);
}
diff --git a/common/rfb/util.cxx b/common/rfb/util.cxx
index f52213b3..fc4f4ca4 100644
--- a/common/rfb/util.cxx
+++ b/common/rfb/util.cxx
@@ -1,4 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ * Copyright 2011-2019 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -63,6 +64,10 @@ namespace rfb {
delete [] s;
}
+ void strFree(wchar_t* s) {
+ delete [] s;
+ }
+
bool strSplit(const char* src, const char limiter, char** out1, char** out2, bool fromEnd) {
CharArray out1old, out2old;
@@ -107,6 +112,441 @@ namespace rfb {
dest[src ? destlen-1 : 0] = 0;
}
+ char* convertLF(const char* src, size_t bytes)
+ {
+ char* buffer;
+ size_t sz;
+
+ char* out;
+ const char* in;
+ size_t in_len;
+
+ // Always include space for a NULL
+ sz = 1;
+
+ // Compute output size
+ in = src;
+ in_len = bytes;
+ while ((*in != '\0') && (in_len > 0)) {
+ if (*in != '\r') {
+ sz++;
+ in++;
+ in_len--;
+ continue;
+ }
+
+ if ((in_len == 0) || (*(in+1) != '\n'))
+ sz++;
+
+ in++;
+ in_len--;
+ }
+
+ // Alloc
+ buffer = new char[sz];
+ memset(buffer, 0, sz);
+
+ // And convert
+ out = buffer;
+ in = src;
+ in_len = bytes;
+ while ((*in != '\0') && (in_len > 0)) {
+ if (*in != '\r') {
+ *out++ = *in++;
+ in_len--;
+ continue;
+ }
+
+ if ((in_len == 0) || (*(in+1) != '\n'))
+ *out++ = '\n';
+
+ in++;
+ in_len--;
+ }
+
+ return buffer;
+ }
+
+ char* convertCRLF(const char* src, size_t bytes)
+ {
+ char* buffer;
+ size_t sz;
+
+ char* out;
+ const char* in;
+ size_t in_len;
+
+ // Always include space for a NULL
+ sz = 1;
+
+ // Compute output size
+ in = src;
+ in_len = bytes;
+ while ((*in != '\0') && (in_len > 0)) {
+ sz++;
+
+ if (*in == '\r') {
+ if ((in_len == 0) || (*(in+1) != '\n'))
+ sz++;
+ } else if (*in == '\n') {
+ if ((in == src) || (*(in-1) != '\r'))
+ sz++;
+ }
+
+ in++;
+ in_len--;
+ }
+
+ // Alloc
+ buffer = new char[sz];
+ memset(buffer, 0, sz);
+
+ // And convert
+ out = buffer;
+ in = src;
+ in_len = bytes;
+ while ((*in != '\0') && (in_len > 0)) {
+ if (*in == '\n') {
+ if ((in == src) || (*(in-1) != '\r'))
+ *out++ = '\r';
+ }
+
+ *out = *in;
+
+ if (*in == '\r') {
+ if ((in_len == 0) || (*(in+1) != '\n')) {
+ out++;
+ *out = '\n';
+ }
+ }
+
+ out++;
+ in++;
+ in_len--;
+ }
+
+ return buffer;
+ }
+
+ size_t ucs4ToUTF8(unsigned src, char* dst) {
+ if (src < 0x80) {
+ *dst++ = src;
+ *dst++ = '\0';
+ return 1;
+ } else if (src < 0x800) {
+ *dst++ = 0xc0 | (src >> 6);
+ *dst++ = 0x80 | (src & 0x3f);
+ *dst++ = '\0';
+ return 2;
+ } else if (src < 0x10000) {
+ *dst++ = 0xe0 | (src >> 12);
+ *dst++ = 0x80 | ((src >> 6) & 0x3f);
+ *dst++ = 0x80 | (src & 0x3f);
+ *dst++ = '\0';
+ return 3;
+ } else if (src < 0x110000) {
+ *dst++ = 0xf0 | (src >> 18);
+ *dst++ = 0x80 | ((src >> 12) & 0x3f);
+ *dst++ = 0x80 | ((src >> 6) & 0x3f);
+ *dst++ = 0x80 | (src & 0x3f);
+ *dst++ = '\0';
+ return 4;
+ } else {
+ return ucs4ToUTF8(0xfffd, dst);
+ }
+ }
+
+ size_t utf8ToUCS4(const char* src, size_t max, unsigned* dst) {
+ size_t count, consumed;
+
+ *dst = 0xfffd;
+
+ if (max == 0)
+ return 0;
+
+ consumed = 1;
+
+ if ((*src & 0x80) == 0) {
+ *dst = *src;
+ count = 0;
+ } else if ((*src & 0xe0) == 0xc0) {
+ *dst = *src & 0x1f;
+ count = 1;
+ } else if ((*src & 0xf0) == 0xe0) {
+ *dst = *src & 0x0f;
+ count = 2;
+ } else if ((*src & 0xf8) == 0xf0) {
+ *dst = *src & 0x07;
+ count = 3;
+ } else {
+ // Invalid sequence, consume all continuation characters
+ src++;
+ max--;
+ while ((max-- > 0) && ((*src++ & 0xc0) == 0x80))
+ consumed++;
+ return consumed;
+ }
+
+ src++;
+ max--;
+
+ while (count--) {
+ // Invalid or truncated sequence?
+ if ((max == 0) || ((*src & 0xc0) != 0x80)) {
+ *dst = 0xfffd;
+ return consumed;
+ }
+
+ *dst <<= 6;
+ *dst |= *src & 0x3f;
+
+ src++;
+ max--;
+ }
+
+ return consumed;
+ }
+
+ size_t ucs4ToUTF16(unsigned src, wchar_t* dst) {
+ if ((src < 0xd800) || ((src >= 0xe000) && (src < 0x10000))) {
+ *dst++ = src;
+ *dst++ = L'\0';
+ return 1;
+ } else if (src < 0x110000) {
+ *dst++ = 0xd800 | ((src >> 10) & 0x07ff);
+ *dst++ = 0xdc00 | (src & 0x07ff);
+ *dst++ = L'\0';
+ return 2;
+ } else {
+ return ucs4ToUTF16(0xfffd, dst);
+ }
+ }
+
+ size_t utf16ToUCS4(const wchar_t* src, size_t max, unsigned* dst) {
+ *dst = 0xfffd;
+
+ if (max == 0)
+ return 0;
+
+ if ((*src < 0xd800) || (*src >= 0xe000)) {
+ *dst = *src;
+ return 1;
+ }
+
+ if (*src & 0x0400) {
+ size_t consumed;
+
+ // Invalid sequence, consume all continuation characters
+ consumed = 0;
+ while ((max > 0) && (*src & 0x0400)) {
+ src++;
+ max--;
+ consumed++;
+ }
+
+ return consumed;
+ }
+
+ *dst = *src++;
+ max--;
+
+ // Invalid or truncated sequence?
+ if ((max == 0) || ((*src & 0xfc00) != 0xdc00)) {
+ *dst = 0xfffd;
+ return 1;
+ }
+
+ *dst = 0x10000 | ((*dst & 0x03ff) << 10);
+ *dst |= *src & 0x3ff;
+
+ return 2;
+ }
+
+ char* latin1ToUTF8(const char* src, size_t bytes) {
+ char* buffer;
+ size_t sz;
+
+ char* out;
+ const char* in;
+ size_t in_len;
+
+ // Always include space for a NULL
+ sz = 1;
+
+ // Compute output size
+ in = src;
+ in_len = bytes;
+ while ((*in != '\0') && (in_len > 0)) {
+ char buf[5];
+ sz += ucs4ToUTF8(*in, buf);
+ in++;
+ in_len--;
+ }
+
+ // Alloc
+ buffer = new char[sz];
+ memset(buffer, 0, sz);
+
+ // And convert
+ out = buffer;
+ in = src;
+ in_len = bytes;
+ while ((*in != '\0') && (in_len > 0)) {
+ out += ucs4ToUTF8(*in, out);
+ in++;
+ in_len--;
+ }
+
+ return buffer;
+ }
+
+ char* utf8ToLatin1(const char* src, size_t bytes) {
+ char* buffer;
+ size_t sz;
+
+ char* out;
+ const char* in;
+ size_t in_len;
+
+ // Always include space for a NULL
+ sz = 1;
+
+ // Compute output size
+ in = src;
+ in_len = bytes;
+ while ((*in != '\0') && (in_len > 0)) {
+ size_t len;
+ unsigned ucs;
+
+ len = utf8ToUCS4(in, in_len, &ucs);
+ in += len;
+ in_len -= len;
+ sz++;
+ }
+
+ // Alloc
+ buffer = new char[sz];
+ memset(buffer, 0, sz);
+
+ // And convert
+ out = buffer;
+ in = src;
+ in_len = bytes;
+ while ((*in != '\0') && (in_len > 0)) {
+ size_t len;
+ unsigned ucs;
+
+ len = utf8ToUCS4(in, in_len, &ucs);
+ in += len;
+ in_len -= len;
+
+ if (ucs > 0xff)
+ *out++ = '?';
+ else
+ *out++ = (unsigned char)ucs;
+ }
+
+ return buffer;
+ }
+
+ char* utf16ToUTF8(const wchar_t* src, size_t units)
+ {
+ char* buffer;
+ size_t sz;
+
+ char* out;
+ const wchar_t* in;
+ size_t in_len;
+
+ // Always include space for a NULL
+ sz = 1;
+
+ // Compute output size
+ in = src;
+ in_len = units;
+ while ((*in != '\0') && (in_len > 0)) {
+ size_t len;
+ unsigned ucs;
+ char buf[5];
+
+ len = utf16ToUCS4(in, in_len, &ucs);
+ in += len;
+ in_len -= len;
+
+ sz += ucs4ToUTF8(ucs, buf);
+ }
+
+ // Alloc
+ buffer = new char[sz];
+ memset(buffer, 0, sz);
+
+ // And convert
+ out = buffer;
+ in = src;
+ in_len = units;
+ while ((*in != '\0') && (in_len > 0)) {
+ size_t len;
+ unsigned ucs;
+
+ len = utf16ToUCS4(in, in_len, &ucs);
+ in += len;
+ in_len -= len;
+
+ out += ucs4ToUTF8(ucs, out);
+ }
+
+ return buffer;
+ }
+
+ wchar_t* utf8ToUTF16(const char* src, size_t bytes)
+ {
+ wchar_t* buffer;
+ size_t sz;
+
+ wchar_t* out;
+ const char* in;
+ size_t in_len;
+
+ // Always include space for a NULL
+ sz = 1;
+
+ // Compute output size
+ in = src;
+ in_len = bytes;
+ while ((*in != '\0') && (in_len > 0)) {
+ size_t len;
+ unsigned ucs;
+ wchar_t buf[3];
+
+ len = utf8ToUCS4(in, in_len, &ucs);
+ in += len;
+ in_len -= len;
+
+ sz += ucs4ToUTF16(ucs, buf);
+ }
+
+ // Alloc
+ buffer = new wchar_t[sz];
+ memset(buffer, 0, sz);
+
+ // And convert
+ out = buffer;
+ in = src;
+ in_len = bytes;
+ while ((*in != '\0') && (in_len > 0)) {
+ size_t len;
+ unsigned ucs;
+
+ len = utf8ToUCS4(in, in_len, &ucs);
+ in += len;
+ in_len -= len;
+
+ out += ucs4ToUTF16(ucs, out);
+ }
+
+ return buffer;
+ }
+
unsigned msBetween(const struct timeval *first,
const struct timeval *second)
{
diff --git a/common/rfb/util.h b/common/rfb/util.h
index 9e59bd37..8503519d 100644
--- a/common/rfb/util.h
+++ b/common/rfb/util.h
@@ -1,4 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ * Copyright 2011-2019 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -67,6 +68,7 @@ namespace rfb {
char* strDup(const char* s);
void strFree(char* s);
+ void strFree(wchar_t* s);
// Returns true if split successful. Returns false otherwise.
// ALWAYS *copies* first part of string to out1 buffer.
@@ -83,6 +85,25 @@ namespace rfb {
// Copies src to dest, up to specified length-1, and guarantees termination
void strCopy(char* dest, const char* src, int destlen);
+ // Makes sure line endings are in a certain format
+
+ char* convertLF(const char* src, size_t bytes = (size_t)-1);
+ char* convertCRLF(const char* src, size_t bytes = (size_t)-1);
+
+ // Convertions between various Unicode formats. The returned strings are
+ // always null terminated and must be freed using strFree().
+
+ size_t ucs4ToUTF8(unsigned src, char* dst);
+ size_t utf8ToUCS4(const char* src, size_t max, unsigned* dst);
+
+ size_t ucs4ToUTF16(unsigned src, wchar_t* dst);
+ size_t utf16ToUCS4(const wchar_t* src, size_t max, unsigned* dst);
+
+ char* latin1ToUTF8(const char* src, size_t bytes = (size_t)-1);
+ char* utf8ToLatin1(const char* src, size_t bytes = (size_t)-1);
+
+ char* utf16ToUTF8(const wchar_t* src, size_t units = (size_t)-1);
+ wchar_t* utf8ToUTF16(const char* src, size_t bytes = (size_t)-1);
// HELPER functions for timeout handling
diff --git a/tests/decperf.cxx b/tests/decperf.cxx
index 301e45e0..df5214f2 100644
--- a/tests/decperf.cxx
+++ b/tests/decperf.cxx
@@ -54,7 +54,7 @@ public:
virtual void framebufferUpdateEnd();
virtual void setColourMapEntries(int, int, rdr::U16*);
virtual void bell();
- virtual void serverCutText(const char*, rdr::U32);
+ virtual void serverCutText(const char*);
public:
double cpuTime;
@@ -122,7 +122,7 @@ void CConn::bell()
{
}
-void CConn::serverCutText(const char*, rdr::U32)
+void CConn::serverCutText(const char*)
{
}
diff --git a/tests/encperf.cxx b/tests/encperf.cxx
index 6523eb72..e461197e 100644
--- a/tests/encperf.cxx
+++ b/tests/encperf.cxx
@@ -96,7 +96,7 @@ public:
virtual void dataRect(const rfb::Rect&, int);
virtual void setColourMapEntries(int, int, rdr::U16*);
virtual void bell();
- virtual void serverCutText(const char*, rdr::U32);
+ virtual void serverCutText(const char*);
public:
double decodeTime;
@@ -254,7 +254,7 @@ void CConn::bell()
{
}
-void CConn::serverCutText(const char*, rdr::U32)
+void CConn::serverCutText(const char*)
{
}
diff --git a/unix/x0vncserver/XDesktop.cxx b/unix/x0vncserver/XDesktop.cxx
index 564b2d51..8be9aa3d 100644
--- a/unix/x0vncserver/XDesktop.cxx
+++ b/unix/x0vncserver/XDesktop.cxx
@@ -406,7 +406,7 @@ void XDesktop::keyEvent(rdr::U32 keysym, rdr::U32 xtcode, bool down) {
#endif
}
-void XDesktop::clientCutText(const char* str, int len) {
+void XDesktop::clientCutText(const char* str) {
}
ScreenSet XDesktop::computeScreenLayout()
diff --git a/unix/x0vncserver/XDesktop.h b/unix/x0vncserver/XDesktop.h
index 3e85aac3..840d4331 100644
--- a/unix/x0vncserver/XDesktop.h
+++ b/unix/x0vncserver/XDesktop.h
@@ -56,7 +56,7 @@ public:
virtual void pointerEvent(const rfb::Point& pos, int buttonMask);
KeyCode XkbKeysymToKeycode(Display* dpy, KeySym keysym);
virtual void keyEvent(rdr::U32 keysym, rdr::U32 xtcode, bool down);
- virtual void clientCutText(const char* str, int len);
+ virtual void clientCutText(const char* str);
virtual unsigned int setScreenLayout(int fb_width, int fb_height,
const rfb::ScreenSet& layout);
diff --git a/unix/xserver/hw/vnc/RFBGlue.cc b/unix/xserver/hw/vnc/RFBGlue.cc
index 160177bd..f108fae4 100644
--- a/unix/xserver/hw/vnc/RFBGlue.cc
+++ b/unix/xserver/hw/vnc/RFBGlue.cc
@@ -1,5 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2011-2015 Pierre Ossman for Cendio AB
+ * Copyright 2011-2019 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -210,3 +210,35 @@ int vncIsTCPPortUsed(int port)
}
return 0;
}
+
+char* vncConvertLF(const char* src, size_t bytes)
+{
+ try {
+ return convertLF(src, bytes);
+ } catch (...) {
+ return NULL;
+ }
+}
+
+char* vncLatin1ToUTF8(const char* src, size_t bytes)
+{
+ try {
+ return latin1ToUTF8(src, bytes);
+ } catch (...) {
+ return NULL;
+ }
+}
+
+char* vncUTF8ToLatin1(const char* src, size_t bytes)
+{
+ try {
+ return utf8ToLatin1(src, bytes);
+ } catch (...) {
+ return NULL;
+ }
+}
+
+void vncStrFree(char* str)
+{
+ strFree(str);
+}
diff --git a/unix/xserver/hw/vnc/RFBGlue.h b/unix/xserver/hw/vnc/RFBGlue.h
index a63afd07..112405b8 100644
--- a/unix/xserver/hw/vnc/RFBGlue.h
+++ b/unix/xserver/hw/vnc/RFBGlue.h
@@ -1,5 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2011-2015 Pierre Ossman for Cendio AB
+ * Copyright 2011-2019 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -49,6 +49,13 @@ void vncListParams(int width, int nameWidth);
int vncGetSocketPort(int fd);
int vncIsTCPPortUsed(int port);
+char* vncConvertLF(const char* src, size_t bytes);
+
+char* vncLatin1ToUTF8(const char* src, size_t bytes);
+char* vncUTF8ToLatin1(const char* src, size_t bytes);
+
+void vncStrFree(char* str);
+
#ifdef __cplusplus
}
#endif
diff --git a/unix/xserver/hw/vnc/XserverDesktop.cc b/unix/xserver/hw/vnc/XserverDesktop.cc
index d8b3a4d4..4edffec7 100644
--- a/unix/xserver/hw/vnc/XserverDesktop.cc
+++ b/unix/xserver/hw/vnc/XserverDesktop.cc
@@ -1,5 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2009-2017 Pierre Ossman for Cendio AB
+ * Copyright 2009-2019 Pierre Ossman for Cendio AB
* Copyright 2014 Brian P. Hinz
*
* This is free software; you can redistribute it and/or modify
@@ -182,25 +182,43 @@ void XserverDesktop::queryConnection(network::Socket* sock,
queryConnectTimer.start(queryConnectTimeout * 1000);
}
-void XserverDesktop::bell()
+void XserverDesktop::requestClipboard()
{
- server->bell();
+ try {
+ server->requestClipboard();
+ } catch (rdr::Exception& e) {
+ vlog.error("XserverDesktop::requestClipboard: %s",e.str());
+ }
}
-void XserverDesktop::setLEDState(unsigned int state)
+void XserverDesktop::announceClipboard(bool available)
{
- server->setLEDState(state);
+ try {
+ server->announceClipboard(available);
+ } catch (rdr::Exception& e) {
+ vlog.error("XserverDesktop::announceClipboard: %s",e.str());
+ }
}
-void XserverDesktop::serverCutText(const char* str, int len)
+void XserverDesktop::sendClipboardData(const char* data)
{
try {
- server->serverCutText(str, len);
+ server->sendClipboardData(data);
} catch (rdr::Exception& e) {
- vlog.error("XserverDesktop::serverCutText: %s",e.str());
+ vlog.error("XserverDesktop::sendClipboardData: %s",e.str());
}
}
+void XserverDesktop::bell()
+{
+ server->bell();
+}
+
+void XserverDesktop::setLEDState(unsigned int state)
+{
+ server->setLEDState(state);
+}
+
void XserverDesktop::setDesktopName(const char* name)
{
try {
@@ -436,11 +454,6 @@ void XserverDesktop::pointerEvent(const Point& pos, int buttonMask)
vncPointerButtonAction(buttonMask);
}
-void XserverDesktop::clientCutText(const char* str, int len)
-{
- vncClientCutText(str, len);
-}
-
unsigned int XserverDesktop::setScreenLayout(int fb_width, int fb_height,
const rfb::ScreenSet& layout)
{
@@ -462,6 +475,21 @@ unsigned int XserverDesktop::setScreenLayout(int fb_width, int fb_height,
return result;
}
+void XserverDesktop::handleClipboardRequest()
+{
+ vncHandleClipboardRequest();
+}
+
+void XserverDesktop::handleClipboardAnnounce(bool available)
+{
+ vncHandleClipboardAnnounce(available);
+}
+
+void XserverDesktop::handleClipboardData(const char* data_)
+{
+ vncHandleClipboardData(data_);
+}
+
void XserverDesktop::grabRegion(const rfb::Region& region)
{
if (directFbptr)
diff --git a/unix/xserver/hw/vnc/XserverDesktop.h b/unix/xserver/hw/vnc/XserverDesktop.h
index 1253935f..6c670689 100644
--- a/unix/xserver/hw/vnc/XserverDesktop.h
+++ b/unix/xserver/hw/vnc/XserverDesktop.h
@@ -1,5 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2009-2015 Pierre Ossman for Cendio AB
+ * Copyright 2009-2019 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -59,9 +59,11 @@ public:
void unblockUpdates();
void setFramebuffer(int w, int h, void* fbptr, int stride);
void refreshScreenLayout();
+ void requestClipboard();
+ void announceClipboard(bool available);
+ void sendClipboardData(const char* data);
void bell();
void setLEDState(unsigned int state);
- void serverCutText(const char* str, int len);
void setDesktopName(const char* name);
void setCursor(int width, int height, int hotX, int hotY,
const unsigned char *rgbaData);
@@ -92,9 +94,11 @@ public:
const char* userName);
virtual void pointerEvent(const rfb::Point& pos, int buttonMask);
virtual void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down);
- virtual void clientCutText(const char* str, int len);
virtual unsigned int setScreenLayout(int fb_width, int fb_height,
const rfb::ScreenSet& layout);
+ virtual void handleClipboardRequest();
+ virtual void handleClipboardAnnounce(bool available);
+ virtual void handleClipboardData(const char* data);
// rfb::PixelBuffer callbacks
virtual void grabRegion(const rfb::Region& r);
diff --git a/unix/xserver/hw/vnc/vncExtInit.cc b/unix/xserver/hw/vnc/vncExtInit.cc
index 20072f48..6ab306b1 100644
--- a/unix/xserver/hw/vnc/vncExtInit.cc
+++ b/unix/xserver/hw/vnc/vncExtInit.cc
@@ -1,5 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2011-2015 Pierre Ossman for Cendio AB
+ * Copyright 2011-2019 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -285,10 +285,22 @@ void vncUpdateDesktopName(void)
desktop[scr]->setDesktopName(desktopName);
}
-void vncServerCutText(const char *text, size_t len)
+void vncRequestClipboard(void)
{
for (int scr = 0; scr < vncGetScreenCount(); scr++)
- desktop[scr]->serverCutText(text, len);
+ desktop[scr]->requestClipboard();
+}
+
+void vncAnnounceClipboard(int available)
+{
+ for (int scr = 0; scr < vncGetScreenCount(); scr++)
+ desktop[scr]->announceClipboard(available);
+}
+
+void vncSendClipboardData(const char* data)
+{
+ for (int scr = 0; scr < vncGetScreenCount(); scr++)
+ desktop[scr]->sendClipboardData(data);
}
int vncConnectClient(const char *addr)
diff --git a/unix/xserver/hw/vnc/vncExtInit.h b/unix/xserver/hw/vnc/vncExtInit.h
index 5f97f96d..1fb87c19 100644
--- a/unix/xserver/hw/vnc/vncExtInit.h
+++ b/unix/xserver/hw/vnc/vncExtInit.h
@@ -1,5 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2011-2015 Pierre Ossman for Cendio AB
+ * Copyright 2011-2019 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -53,7 +53,9 @@ int vncGetSendPrimary(void);
void vncUpdateDesktopName(void);
-void vncServerCutText(const char *text, size_t len);
+void vncRequestClipboard(void);
+void vncAnnounceClipboard(int available);
+void vncSendClipboardData(const char* data);
int vncConnectClient(const char *addr);
diff --git a/unix/xserver/hw/vnc/vncSelection.c b/unix/xserver/hw/vnc/vncSelection.c
index 4f3538d4..5191bb94 100644
--- a/unix/xserver/hw/vnc/vncSelection.c
+++ b/unix/xserver/hw/vnc/vncSelection.c
@@ -1,4 +1,4 @@
-/* Copyright 2016 Pierre Ossman for Cendio AB
+/* Copyright 2016-2019 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -47,15 +47,34 @@ static Atom xaTARGETS, xaTIMESTAMP, xaSTRING, xaTEXT, xaUTF8_STRING;
static WindowPtr pWindow;
static Window wid;
-static char* clientCutText;
-static int clientCutTextLen;
+static Bool probing;
+static Atom activeSelection = None;
+
+struct VncDataTarget {
+ ClientPtr client;
+ Atom selection;
+ Atom target;
+ Atom property;
+ Window requestor;
+ CARD32 time;
+ struct VncDataTarget* next;
+};
+
+static struct VncDataTarget* vncDataTargetHead;
static int vncCreateSelectionWindow(void);
static int vncOwnSelection(Atom selection);
+static int vncConvertSelection(ClientPtr client, Atom selection,
+ Atom target, Atom property,
+ Window requestor, CARD32 time,
+ const char* data);
static int vncProcConvertSelection(ClientPtr client);
+static void vncSelectionRequest(Atom selection, Atom target);
static int vncProcSendEvent(ClientPtr client);
static void vncSelectionCallback(CallbackListPtr *callbacks,
void * data, void * args);
+static void vncClientStateCallback(CallbackListPtr * l,
+ void * d, void * p);
static int (*origProcConvertSelection)(ClientPtr);
static int (*origProcSendEvent)(ClientPtr);
@@ -80,34 +99,99 @@ void vncSelectionInit(void)
if (!AddCallback(&SelectionCallback, vncSelectionCallback, 0))
FatalError("Add VNC SelectionCallback failed\n");
+ if (!AddCallback(&ClientStateCallback, vncClientStateCallback, 0))
+ FatalError("Add VNC ClientStateCallback failed\n");
}
-void vncClientCutText(const char* str, int len)
+void vncHandleClipboardRequest(void)
{
- int rc;
-
- if (clientCutText != NULL)
- free(clientCutText);
-
- clientCutText = malloc(len);
- if (clientCutText == NULL) {
- LOG_ERROR("Could not allocate clipboard buffer");
- DeleteWindowFromAnySelections(pWindow);
+ if (activeSelection == None) {
+ LOG_DEBUG("Got request for local clipboard although no clipboard is active");
return;
}
- memcpy(clientCutText, str, len);
- clientCutTextLen = len;
+ LOG_DEBUG("Got request for local clipboard, re-probing formats");
+
+ probing = FALSE;
+ vncSelectionRequest(activeSelection, xaTARGETS);
+}
+
+void vncHandleClipboardAnnounce(int available)
+{
+ if (available) {
+ int rc;
+
+ LOG_DEBUG("Remote clipboard announced, grabbing local ownership");
+
+ if (vncGetSetPrimary()) {
+ rc = vncOwnSelection(xaPRIMARY);
+ if (rc != Success)
+ LOG_ERROR("Could not set PRIMARY selection");
+ }
- if (vncGetSetPrimary()) {
- rc = vncOwnSelection(xaPRIMARY);
+ rc = vncOwnSelection(xaCLIPBOARD);
if (rc != Success)
- LOG_ERROR("Could not set PRIMARY selection");
+ LOG_ERROR("Could not set CLIPBOARD selection");
+ } else {
+ struct VncDataTarget* next;
+
+ if (pWindow == NULL)
+ return;
+
+ LOG_DEBUG("Remote clipboard lost, removing local ownership");
+
+ DeleteWindowFromAnySelections(pWindow);
+
+ /* Abort any pending transfer */
+ while (vncDataTargetHead != NULL) {
+ xEvent event;
+
+ event.u.u.type = SelectionNotify;
+ event.u.selectionNotify.time = vncDataTargetHead->time;
+ event.u.selectionNotify.requestor = vncDataTargetHead->requestor;
+ event.u.selectionNotify.selection = vncDataTargetHead->selection;
+ event.u.selectionNotify.target = vncDataTargetHead->target;
+ event.u.selectionNotify.property = None;
+ WriteEventsToClient(vncDataTargetHead->client, 1, &event);
+
+ next = vncDataTargetHead->next;
+ free(vncDataTargetHead);
+ vncDataTargetHead = next;
+ }
}
+}
- rc = vncOwnSelection(xaCLIPBOARD);
- if (rc != Success)
- LOG_ERROR("Could not set CLIPBOARD selection");
+void vncHandleClipboardData(const char* data)
+{
+ struct VncDataTarget* next;
+
+ LOG_DEBUG("Got remote clipboard data, sending to X11 clients");
+
+ while (vncDataTargetHead != NULL) {
+ int rc;
+ xEvent event;
+
+ rc = vncConvertSelection(vncDataTargetHead->client,
+ vncDataTargetHead->selection,
+ vncDataTargetHead->target,
+ vncDataTargetHead->property,
+ vncDataTargetHead->requestor,
+ vncDataTargetHead->time,
+ data);
+ if (rc != Success) {
+ event.u.u.type = SelectionNotify;
+ event.u.selectionNotify.time = vncDataTargetHead->time;
+ event.u.selectionNotify.requestor = vncDataTargetHead->requestor;
+ event.u.selectionNotify.selection = vncDataTargetHead->selection;
+ event.u.selectionNotify.target = vncDataTargetHead->target;
+ event.u.selectionNotify.property = None;
+ WriteEventsToClient(vncDataTargetHead->client, 1, &event);
+ }
+
+ next = vncDataTargetHead->next;
+ free(vncDataTargetHead);
+ vncDataTargetHead = next;
+ }
}
static int vncCreateSelectionWindow(void)
@@ -195,7 +279,8 @@ static int vncOwnSelection(Atom selection)
static int vncConvertSelection(ClientPtr client, Atom selection,
Atom target, Atom property,
- Window requestor, CARD32 time)
+ Window requestor, CARD32 time,
+ const char* data)
{
Selection *pSel;
WindowPtr pWin;
@@ -205,8 +290,13 @@ static int vncConvertSelection(ClientPtr client, Atom selection,
xEvent event;
- LOG_DEBUG("Selection request for %s (type %s)",
- NameForAtom(selection), NameForAtom(target));
+ if (data == NULL) {
+ LOG_DEBUG("Selection request for %s (type %s)",
+ NameForAtom(selection), NameForAtom(target));
+ } else {
+ LOG_DEBUG("Sending data for selection request for %s (type %s)",
+ NameForAtom(selection), NameForAtom(target));
+ }
rc = dixLookupSelection(&pSel, selection, client, DixGetAttrAccess);
if (rc != Success)
@@ -243,51 +333,59 @@ static int vncConvertSelection(ClientPtr client, Atom selection,
TRUE);
if (rc != Success)
return rc;
- } else if ((target == xaSTRING) || (target == xaTEXT)) {
- rc = dixChangeWindowProperty(serverClient, pWin, realProperty,
- XA_STRING, 8, PropModeReplace,
- clientCutTextLen, clientCutText,
- TRUE);
- if (rc != Success)
- return rc;
- } else if (target == xaUTF8_STRING) {
- unsigned char* buffer;
- unsigned char* out;
- size_t len;
-
- const unsigned char* in;
- size_t in_len;
-
- buffer = malloc(clientCutTextLen*2);
- if (buffer == NULL)
- return BadAlloc;
-
- out = buffer;
- len = 0;
- in = clientCutText;
- in_len = clientCutTextLen;
- while (in_len > 0) {
- if (*in & 0x80) {
- *out++ = 0xc0 | (*in >> 6);
- *out++ = 0x80 | (*in & 0x3f);
- len += 2;
- in++;
- in_len--;
+ } else {
+ if (data == NULL) {
+ struct VncDataTarget* vdt;
+
+ if ((target != xaSTRING) && (target != xaTEXT) &&
+ (target != xaUTF8_STRING))
+ return BadMatch;
+
+ vdt = calloc(1, sizeof(struct VncDataTarget));
+ if (vdt == NULL)
+ return BadAlloc;
+
+ vdt->client = client;
+ vdt->selection = selection;
+ vdt->target = target;
+ vdt->property = property;
+ vdt->requestor = requestor;
+ vdt->time = time;
+
+ vdt->next = vncDataTargetHead;
+ vncDataTargetHead = vdt;
+
+ LOG_DEBUG("Requesting clipboard data from client");
+
+ vncRequestClipboard();
+
+ return Success;
+ } else {
+ if ((target == xaSTRING) || (target == xaTEXT)) {
+ char* latin1;
+
+ latin1 = vncUTF8ToLatin1(data, (size_t)-1);
+ if (latin1 == NULL)
+ return BadAlloc;
+
+ rc = dixChangeWindowProperty(serverClient, pWin, realProperty,
+ XA_STRING, 8, PropModeReplace,
+ strlen(latin1), latin1, TRUE);
+
+ vncStrFree(latin1);
+
+ if (rc != Success)
+ return rc;
+ } else if (target == xaUTF8_STRING) {
+ rc = dixChangeWindowProperty(serverClient, pWin, realProperty,
+ xaUTF8_STRING, 8, PropModeReplace,
+ strlen(data), data, TRUE);
+ if (rc != Success)
+ return rc;
} else {
- *out++ = *in++;
- len++;
- in_len--;
+ return BadMatch;
}
}
-
- rc = dixChangeWindowProperty(serverClient, pWin, realProperty,
- xaUTF8_STRING, 8, PropModeReplace,
- len, buffer, TRUE);
- free(buffer);
- if (rc != Success)
- return rc;
- } else {
- return BadMatch;
}
event.u.u.type = SelectionNotify;
@@ -326,7 +424,7 @@ static int vncProcConvertSelection(ClientPtr client)
pSel->window == wid) {
rc = vncConvertSelection(client, stuff->selection,
stuff->target, stuff->property,
- stuff->requestor, stuff->time);
+ stuff->requestor, stuff->time, NULL);
if (rc != Success) {
xEvent event;
@@ -410,69 +508,61 @@ static void vncHandleSelection(Atom selection, Atom target,
if (prop->type != XA_ATOM)
return;
- if (vncHasAtom(xaSTRING, (const Atom*)prop->data, prop->size))
- vncSelectionRequest(selection, xaSTRING);
- else if (vncHasAtom(xaUTF8_STRING, (const Atom*)prop->data, prop->size))
- vncSelectionRequest(selection, xaUTF8_STRING);
+ if (probing) {
+ if (vncHasAtom(xaSTRING, (const Atom*)prop->data, prop->size) ||
+ vncHasAtom(xaUTF8_STRING, (const Atom*)prop->data, prop->size)) {
+ LOG_DEBUG("Compatible format found, notifying clients");
+ activeSelection = selection;
+ vncAnnounceClipboard(TRUE);
+ }
+ } else {
+ if (vncHasAtom(xaUTF8_STRING, (const Atom*)prop->data, prop->size))
+ vncSelectionRequest(selection, xaUTF8_STRING);
+ else if (vncHasAtom(xaSTRING, (const Atom*)prop->data, prop->size))
+ vncSelectionRequest(selection, xaSTRING);
+ }
} else if (target == xaSTRING) {
+ char* filtered;
+ char* utf8;
+
if (prop->format != 8)
return;
if (prop->type != xaSTRING)
return;
- vncServerCutText(prop->data, prop->size);
- } else if (target == xaUTF8_STRING) {
- unsigned char* buffer;
- unsigned char* out;
- size_t len;
+ filtered = vncConvertLF(prop->data, prop->size);
+ if (filtered == NULL)
+ return;
+
+ utf8 = vncLatin1ToUTF8(filtered, (size_t)-1);
+ vncStrFree(filtered);
+ if (utf8 == NULL)
+ return;
+
+ LOG_DEBUG("Sending clipboard to clients (%d bytes)",
+ (int)strlen(utf8));
- const unsigned char* in;
- size_t in_len;
+ vncSendClipboardData(utf8);
+
+ vncStrFree(utf8);
+ } else if (target == xaUTF8_STRING) {
+ char *filtered;
if (prop->format != 8)
return;
if (prop->type != xaUTF8_STRING)
return;
- buffer = malloc(prop->size);
- if (buffer == NULL)
+ filtered = vncConvertLF(prop->data, prop->size);
+ if (filtered == NULL)
return;
- out = buffer;
- len = 0;
- in = prop->data;
- in_len = prop->size;
- while (in_len > 0) {
- if ((*in & 0x80) == 0x00) {
- *out++ = *in++;
- len++;
- in_len--;
- } else if ((*in & 0xe0) == 0xc0) {
- unsigned ucs;
- ucs = (*in++ & 0x1f) << 6;
- in_len--;
- if (in_len > 0) {
- ucs |= (*in++ & 0x3f);
- in_len--;
- }
- if (ucs <= 0xff)
- *out++ = ucs;
- else
- *out++ = '?';
- len++;
- } else {
- *out++ = '?';
- len++;
- do {
- in++;
- in_len--;
- } while ((in_len > 0) && ((*in & 0xc0) == 0x80));
- }
- }
+ LOG_DEBUG("Sending clipboard to clients (%d bytes)",
+ (int)strlen(filtered));
- vncServerCutText((const char*)buffer, len);
+ vncSendClipboardData(filtered);
- free(buffer);
+ vncStrFree(filtered);
}
}
@@ -504,6 +594,12 @@ static void vncSelectionCallback(CallbackListPtr *callbacks,
{
SelectionInfoRec *info = (SelectionInfoRec *) args;
+ if (info->selection->selection == activeSelection) {
+ LOG_DEBUG("Local clipboard lost, notifying clients");
+ activeSelection = None;
+ vncAnnounceClipboard(FALSE);
+ }
+
if (info->kind != SelectionSetOwner)
return;
if (info->client == serverClient)
@@ -520,5 +616,25 @@ static void vncSelectionCallback(CallbackListPtr *callbacks,
!vncGetSendPrimary())
return;
+ LOG_DEBUG("Got clipboard notification, probing for formats");
+
+ probing = TRUE;
vncSelectionRequest(info->selection->selection, xaTARGETS);
}
+
+static void vncClientStateCallback(CallbackListPtr * l,
+ void * d, void * p)
+{
+ ClientPtr client = ((NewClientInfoRec*)p)->client;
+ if (client->clientState == ClientStateGone) {
+ struct VncDataTarget** nextPtr = &vncDataTargetHead;
+ for (struct VncDataTarget* cur = vncDataTargetHead; cur; cur = *nextPtr) {
+ if (cur->client == client) {
+ *nextPtr = cur->next;
+ free(cur);
+ continue;
+ }
+ nextPtr = &cur->next;
+ }
+ }
+}
diff --git a/unix/xserver/hw/vnc/vncSelection.h b/unix/xserver/hw/vnc/vncSelection.h
index 969f8958..ea52bf23 100644
--- a/unix/xserver/hw/vnc/vncSelection.h
+++ b/unix/xserver/hw/vnc/vncSelection.h
@@ -1,4 +1,4 @@
-/* Copyright 2016 Pierre Ossman for Cendio AB
+/* Copyright 2016-2019 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -24,7 +24,9 @@ extern "C" {
void vncSelectionInit(void);
-void vncClientCutText(const char* str, int len);
+void vncHandleClipboardRequest(void);
+void vncHandleClipboardAnnounce(int available);
+void vncHandleClipboardData(const char* data);
#ifdef __cplusplus
}
diff --git a/vncviewer/CConn.cxx b/vncviewer/CConn.cxx
index b4610e6a..6ba3276b 100644
--- a/vncviewer/CConn.cxx
+++ b/vncviewer/CConn.cxx
@@ -377,11 +377,6 @@ void CConn::bell()
fl_beep();
}
-void CConn::serverCutText(const char* str, rdr::U32 len)
-{
- desktop->serverCutText(str, len);
-}
-
void CConn::dataRect(const Rect& r, int encoding)
{
sock->inStream().startTiming();
@@ -422,6 +417,21 @@ void CConn::setLEDState(unsigned int state)
desktop->setLEDState(state);
}
+void CConn::handleClipboardRequest()
+{
+ desktop->handleClipboardRequest();
+}
+
+void CConn::handleClipboardAnnounce(bool available)
+{
+ desktop->handleClipboardAnnounce(available);
+}
+
+void CConn::handleClipboardData(const char* data)
+{
+ desktop->handleClipboardData(data);
+}
+
////////////////////// Internal methods //////////////////////
diff --git a/vncviewer/CConn.h b/vncviewer/CConn.h
index 2e3362ce..4d935c96 100644
--- a/vncviewer/CConn.h
+++ b/vncviewer/CConn.h
@@ -61,8 +61,6 @@ public:
void bell();
- void serverCutText(const char* str, rdr::U32 len);
-
void framebufferUpdateStart();
void framebufferUpdateEnd();
void dataRect(const rfb::Rect& r, int encoding);
@@ -74,6 +72,10 @@ public:
void setLEDState(unsigned int state);
+ virtual void handleClipboardRequest();
+ virtual void handleClipboardAnnounce(bool available);
+ virtual void handleClipboardData(const char* data);
+
private:
void resizeFramebuffer();
diff --git a/vncviewer/DesktopWindow.cxx b/vncviewer/DesktopWindow.cxx
index c52a9150..1b666f94 100644
--- a/vncviewer/DesktopWindow.cxx
+++ b/vncviewer/DesktopWindow.cxx
@@ -276,12 +276,6 @@ void DesktopWindow::resizeFramebuffer(int new_w, int new_h)
}
-void DesktopWindow::serverCutText(const char* str, rdr::U32 len)
-{
- viewport->serverCutText(str, len);
-}
-
-
void DesktopWindow::setCursor(int width, int height,
const rfb::Point& hotspot,
const rdr::U8* data)
@@ -442,6 +436,22 @@ void DesktopWindow::setLEDState(unsigned int state)
}
+void DesktopWindow::handleClipboardRequest()
+{
+ viewport->handleClipboardRequest();
+}
+
+void DesktopWindow::handleClipboardAnnounce(bool available)
+{
+ viewport->handleClipboardAnnounce(available);
+}
+
+void DesktopWindow::handleClipboardData(const char* data)
+{
+ viewport->handleClipboardData(data);
+}
+
+
void DesktopWindow::resize(int x, int y, int w, int h)
{
bool resizing;
diff --git a/vncviewer/DesktopWindow.h b/vncviewer/DesktopWindow.h
index 97a8178d..ef3dbb08 100644
--- a/vncviewer/DesktopWindow.h
+++ b/vncviewer/DesktopWindow.h
@@ -62,9 +62,6 @@ public:
// Resize the current framebuffer, but retain the contents
void resizeFramebuffer(int new_w, int new_h);
- // Incoming clipboard from server
- void serverCutText(const char* str, rdr::U32 len);
-
// New image for the locally rendered cursor
void setCursor(int width, int height, const rfb::Point& hotspot,
const rdr::U8* data);
@@ -72,6 +69,11 @@ public:
// Change client LED state
void setLEDState(unsigned int state);
+ // Clipboard events
+ void handleClipboardRequest();
+ void handleClipboardAnnounce(bool available);
+ void handleClipboardData(const char* data);
+
// Fl_Window callback methods
virtual void show();
virtual void draw();
diff --git a/vncviewer/Viewport.cxx b/vncviewer/Viewport.cxx
index 5df5c796..cd613279 100644
--- a/vncviewer/Viewport.cxx
+++ b/vncviewer/Viewport.cxx
@@ -1,5 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2011-2014 Pierre Ossman for Cendio AB
+ * Copyright 2011-2019 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -119,7 +119,7 @@ Viewport::Viewport(int w, int h, const rfb::PixelFormat& serverPF, CConn* cc_)
altGrArmed(false),
#endif
firstLEDState(true),
- pendingServerCutText(NULL), pendingClientCutText(NULL),
+ pendingServerClipboard(false), pendingClientClipboard(false),
menuCtrlKey(false), menuAltKey(false), cursor(NULL)
{
#if !defined(WIN32) && !defined(__APPLE__)
@@ -208,8 +208,6 @@ Viewport::~Viewport()
delete cursor;
}
- clearPendingClipboard();
-
// FLTK automatically deletes all child widgets, so we shouldn't touch
// them ourselves here
}
@@ -232,45 +230,6 @@ void Viewport::updateWindow()
damage(FL_DAMAGE_USER1, r.tl.x + x(), r.tl.y + y(), r.width(), r.height());
}
-void Viewport::serverCutText(const char* str, rdr::U32 len)
-{
- char *buffer;
- int size, ret;
-
- clearPendingClipboard();
-
- if (!acceptClipboard)
- return;
-
- size = fl_utf8froma(NULL, 0, str, len);
- if (size <= 0)
- return;
-
- size++;
-
- buffer = new char[size];
-
- ret = fl_utf8froma(buffer, size, str, len);
- assert(ret < size);
-
- vlog.debug("Got clipboard data (%d bytes)", (int)strlen(buffer));
-
- if (!hasFocus()) {
- pendingServerCutText = buffer;
- return;
- }
-
- // RFB doesn't have separate selection and clipboard concepts, so we
- // dump the data into both variants.
-#if !defined(WIN32) && !defined(__APPLE__)
- if (setPrimary)
- Fl::copy(buffer, ret, 0);
-#endif
- Fl::copy(buffer, ret, 1);
-
- delete [] buffer;
-}
-
static const char * dotcursor_xpm[] = {
"5 5 2 1",
". c #000000",
@@ -319,6 +278,55 @@ void Viewport::setCursor(int width, int height, const Point& hotspot,
window()->cursor(cursor, cursorHotspot.x, cursorHotspot.y);
}
+void Viewport::handleClipboardRequest()
+{
+ Fl::paste(*this, clipboardSource);
+}
+
+void Viewport::handleClipboardAnnounce(bool available)
+{
+ if (!acceptClipboard)
+ return;
+
+ if (available)
+ vlog.debug("Got notification of new clipboard on server");
+ else
+ vlog.debug("Clipboard is no longer available on server");
+
+ if (!available) {
+ pendingServerClipboard = false;
+ return;
+ }
+
+ pendingClientClipboard = false;
+
+ if (!hasFocus()) {
+ pendingServerClipboard = true;
+ return;
+ }
+
+ cc->requestClipboard();
+}
+
+void Viewport::handleClipboardData(const char* data)
+{
+ size_t len;
+
+ if (!hasFocus())
+ return;
+
+ len = strlen(data);
+
+ vlog.debug("Got clipboard data (%d bytes)", (int)len);
+
+ // RFB doesn't have separate selection and clipboard concepts, so we
+ // dump the data into both variants.
+#if !defined(WIN32) && !defined(__APPLE__)
+ if (setPrimary)
+ Fl::copy(data, len, 0);
+#endif
+ Fl::copy(data, len, 1);
+}
void Viewport::setLEDState(unsigned int state)
{
@@ -549,37 +557,24 @@ void Viewport::resize(int x, int y, int w, int h)
int Viewport::handle(int event)
{
- char *buffer;
- int ret;
+ char *filtered;
int buttonMask, wheelMask;
DownMap::const_iterator iter;
switch (event) {
case FL_PASTE:
- buffer = new char[Fl::event_length() + 1];
-
- clearPendingClipboard();
-
- // This is documented as to ASCII, but actually does to 8859-1
- ret = fl_utf8toa(Fl::event_text(), Fl::event_length(), buffer,
- Fl::event_length() + 1);
- assert(ret < (Fl::event_length() + 1));
+ filtered = convertLF(Fl::event_text(), Fl::event_length());
- if (!hasFocus()) {
- pendingClientCutText = buffer;
- return 1;
- }
-
- vlog.debug("Sending clipboard data (%d bytes)", (int)strlen(buffer));
+ vlog.debug("Sending clipboard data (%d bytes)", (int)strlen(filtered));
try {
- cc->writer()->writeClientCutText(buffer, ret);
+ cc->sendClipboardData(filtered);
} catch (rdr::Exception& e) {
vlog.error("%s", e.str());
exit_vncviewer(e.str());
}
- delete [] buffer;
+ strFree(filtered);
return 1;
@@ -737,41 +732,47 @@ void Viewport::handleClipboardChange(int source, void *data)
return;
#endif
- Fl::paste(*self, source);
-}
+ self->clipboardSource = source;
+ self->pendingServerClipboard = false;
-void Viewport::clearPendingClipboard()
-{
- delete [] pendingServerCutText;
- pendingServerCutText = NULL;
- delete [] pendingClientCutText;
- pendingClientCutText = NULL;
+ if (!self->hasFocus()) {
+ self->pendingClientClipboard = true;
+ // Clear any older client clipboard from the server
+ self->cc->announceClipboard(false);
+ return;
+ }
+
+ try {
+ self->cc->announceClipboard(true);
+ } catch (rdr::Exception& e) {
+ vlog.error("%s", e.str());
+ exit_vncviewer(e.str());
+ }
}
void Viewport::flushPendingClipboard()
{
- if (pendingServerCutText) {
- size_t len = strlen(pendingServerCutText);
-#if !defined(WIN32) && !defined(__APPLE__)
- if (setPrimary)
- Fl::copy(pendingServerCutText, len, 0);
-#endif
- Fl::copy(pendingServerCutText, len, 1);
+ if (pendingServerClipboard) {
+ try {
+ cc->requestClipboard();
+ } catch (rdr::Exception& e) {
+ vlog.error("%s", e.str());
+ exit_vncviewer(e.str());
+ }
}
- if (pendingClientCutText) {
- size_t len = strlen(pendingClientCutText);
- vlog.debug("Sending pending clipboard data (%d bytes)", (int)len);
+ if (pendingClientClipboard) {
try {
- cc->writer()->writeClientCutText(pendingClientCutText, len);
+ cc->announceClipboard(true);
} catch (rdr::Exception& e) {
vlog.error("%s", e.str());
exit_vncviewer(e.str());
}
}
- clearPendingClipboard();
+ pendingServerClipboard = false;
+ pendingClientClipboard = false;
}
diff --git a/vncviewer/Viewport.h b/vncviewer/Viewport.h
index be2192b8..1fb93c66 100644
--- a/vncviewer/Viewport.h
+++ b/vncviewer/Viewport.h
@@ -1,5 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2011 Pierre Ossman <ossman@cendio.se> for Cendio AB
+ * Copyright 2011-2019 Pierre Ossman <ossman@cendio.se> for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -45,9 +45,6 @@ public:
// Flush updates to screen
void updateWindow();
- // Incoming clipboard from server
- void serverCutText(const char* str, rdr::U32 len);
-
// New image for the locally rendered cursor
void setCursor(int width, int height, const rfb::Point& hotspot,
const rdr::U8* data);
@@ -57,6 +54,11 @@ public:
void draw(Surface* dst);
+ // Clipboard events
+ void handleClipboardRequest();
+ void handleClipboardAnnounce(bool available);
+ void handleClipboardData(const char* data);
+
// Fl_Widget callback methods
void draw();
@@ -72,7 +74,6 @@ private:
static void handleClipboardChange(int source, void *data);
- void clearPendingClipboard();
void flushPendingClipboard();
void handlePointerEvent(const rfb::Point& pos, int buttonMask);
@@ -114,8 +115,10 @@ private:
bool firstLEDState;
- const char* pendingServerCutText;
- const char* pendingClientCutText;
+ bool pendingServerClipboard;
+ bool pendingClientClipboard;
+
+ int clipboardSource;
rdr::U32 menuKeySym;
int menuKeyCode, menuKeyFLTK;
diff --git a/vncviewer/vncviewer.man b/vncviewer/vncviewer.man
index ebfe7725..f93e096a 100644
--- a/vncviewer/vncviewer.man
+++ b/vncviewer/vncviewer.man
@@ -182,6 +182,11 @@ Set the primary selection as well as the clipboard selection.
Default is on.
.
.TP
+.B \-MaxCutText \fIbytes\fP
+The maximum size of a clipboard update that will be accepted from a server.
+Default is \fB262144\fP.
+.
+.TP
.B \-SendClipboard
Send clipboard changes to the server. Default is on.
.
diff --git a/win/rfb_win32/Clipboard.cxx b/win/rfb_win32/Clipboard.cxx
index 0e290623..11963675 100644
--- a/win/rfb_win32/Clipboard.cxx
+++ b/win/rfb_win32/Clipboard.cxx
@@ -1,4 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ * Copyright 2012-2019 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -29,53 +30,6 @@ using namespace rfb::win32;
static LogWriter vlog("Clipboard");
-
-//
-// -=- CR/LF handlers
-//
-
-char*
-dos2unix(const char* text) {
- int len = strlen(text)+1;
- char* unix = new char[strlen(text)+1];
- int i, j=0;
- for (i=0; i<len; i++) {
- if (text[i] != '\x0d')
- unix[j++] = text[i];
- }
- return unix;
-}
-
-char*
-unix2dos(const char* text) {
- int len = strlen(text)+1;
- char* dos = new char[strlen(text)*2+1];
- int i, j=0;
- for (i=0; i<len; i++) {
- if (text[i] == '\x0a')
- dos[j++] = '\x0d';
- dos[j++] = text[i];
- }
- return dos;
-}
-
-
-//
-// -=- ISO-8859-1 (Latin 1) filter (in-place)
-//
-
-void
-removeNonISOLatin1Chars(char* text) {
- int len = strlen(text);
- int i=0, j=0;
- for (; i<len; i++) {
- if (((text[i] >= 1) && (text[i] <= 127)) ||
- ((text[i] >= 160) && (text[i] <= 255)))
- text[j++] = text[i];
- }
- text[j] = 0;
-}
-
//
// -=- Clipboard object
//
@@ -114,33 +68,10 @@ Clipboard::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
} else {
vlog.debug("local clipboard changed by %p", owner);
- // Open the clipboard
- if (OpenClipboard(getHandle())) {
- // Get the clipboard data
- HGLOBAL cliphandle = GetClipboardData(CF_TEXT);
- if (cliphandle) {
- char* clipdata = (char*) GlobalLock(cliphandle);
-
- // Notify clients
- if (notifier) {
- if (!clipdata) {
- notifier->notifyClipboardChanged(0, 0);
- } else {
- CharArray unix_text;
- unix_text.buf = dos2unix(clipdata);
- removeNonISOLatin1Chars(unix_text.buf);
- notifier->notifyClipboardChanged(unix_text.buf, strlen(unix_text.buf));
- }
- } else {
- vlog.debug("no clipboard notifier registered");
- }
-
- // Release the buffer and close the clipboard
- GlobalUnlock(cliphandle);
- }
-
- CloseClipboard();
- }
+ if (notifier == NULL)
+ vlog.debug("no clipboard notifier registered");
+ else
+ notifier->notifyClipboardChanged(IsClipboardFormatAvailable(CF_UNICODETEXT));
}
}
if (next_window)
@@ -151,6 +82,39 @@ Clipboard::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
return MsgWindow::processMessage(msg, wParam, lParam);
};
+char*
+Clipboard::getClipText() {
+ HGLOBAL cliphandle;
+ wchar_t* clipdata;
+ CharArray utf8;
+
+ // Open the clipboard
+ if (!OpenClipboard(getHandle()))
+ return NULL;
+
+ // Get the clipboard data
+ cliphandle = GetClipboardData(CF_UNICODETEXT);
+ if (!cliphandle) {
+ CloseClipboard();
+ return NULL;
+ }
+
+ clipdata = (wchar_t*) GlobalLock(cliphandle);
+ if (!clipdata) {
+ CloseClipboard();
+ return NULL;
+ }
+
+ // Convert it to UTF-8
+ utf8.replaceBuf(utf16ToUTF8(clipdata));
+
+ // Release the buffer and close the clipboard
+ GlobalUnlock(cliphandle);
+ CloseClipboard();
+
+ return convertLF(utf8.buf);
+}
+
void
Clipboard::setClipText(const char* text) {
HANDLE clip_handle = 0;
@@ -161,26 +125,27 @@ Clipboard::setClipText(const char* text) {
if (!OpenClipboard(getHandle()))
throw rdr::SystemException("unable to open Win32 clipboard", GetLastError());
- // - Pre-process the supplied clipboard text into DOS format
- CharArray dos_text;
- dos_text.buf = unix2dos(text);
- removeNonISOLatin1Chars(dos_text.buf);
- int dos_text_len = strlen(dos_text.buf);
+ // - Convert the supplied clipboard text into UTF-16 format with CRLF
+ CharArray filtered(convertCRLF(text));
+ wchar_t* utf16;
+
+ utf16 = utf8ToUTF16(filtered.buf);
// - Allocate global memory for the data
- clip_handle = ::GlobalAlloc(GMEM_MOVEABLE, dos_text_len+1);
+ clip_handle = ::GlobalAlloc(GMEM_MOVEABLE, (wcslen(utf16) + 1) * 2);
- char* data = (char*) GlobalLock(clip_handle);
- memcpy(data, dos_text.buf, dos_text_len+1);
- data[dos_text_len] = 0;
+ wchar_t* data = (wchar_t*) GlobalLock(clip_handle);
+ wcscpy(data, utf16);
GlobalUnlock(clip_handle);
+ strFree(utf16);
+
// - Next, we must clear out any existing data
if (!EmptyClipboard())
throw rdr::SystemException("unable to empty Win32 clipboard", GetLastError());
// - Set the new clipboard data
- if (!SetClipboardData(CF_TEXT, clip_handle))
+ if (!SetClipboardData(CF_UNICODETEXT, clip_handle))
throw rdr::SystemException("unable to set Win32 clipboard", GetLastError());
clip_handle = 0;
diff --git a/win/rfb_win32/Clipboard.h b/win/rfb_win32/Clipboard.h
index 3da7bfca..1dead82e 100644
--- a/win/rfb_win32/Clipboard.h
+++ b/win/rfb_win32/Clipboard.h
@@ -1,4 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ * Copyright 2016-2019 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -38,7 +39,7 @@ namespace rfb {
// -=- Abstract base class for callback recipients
class Notifier {
public:
- virtual void notifyClipboardChanged(const char* text, int len) = 0;
+ virtual void notifyClipboardChanged(bool available) = 0;
virtual ~Notifier() {};
};
@@ -48,6 +49,9 @@ namespace rfb {
// - Set the notifier to use
void setNotifier(Notifier* cbn) {notifier = cbn;}
+ // - Get the clipboard contents
+ char* getClipText();
+
// - Set the clipboard contents
void setClipText(const char* text);
diff --git a/win/rfb_win32/SDisplay.cxx b/win/rfb_win32/SDisplay.cxx
index 2cedc4a8..be33ff15 100644
--- a/win/rfb_win32/SDisplay.cxx
+++ b/win/rfb_win32/SDisplay.cxx
@@ -1,4 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ * Copyright 2011-2019 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -291,6 +292,22 @@ void SDisplay::restartCore() {
}
+void SDisplay::handleClipboardRequest() {
+ CharArray data(clipboard->getClipText());
+ server->sendClipboardData(data.buf);
+}
+
+void SDisplay::handleClipboardAnnounce(bool available) {
+ // FIXME: Wait for an application to actually request it
+ if (available)
+ server->requestClipboard();
+}
+
+void SDisplay::handleClipboardData(const char* data) {
+ clipboard->setClipText(data);
+}
+
+
void SDisplay::pointerEvent(const Point& pos, int buttonmask) {
if (pb->getRect().contains(pos)) {
Point screenPos = pos.translate(screenRect.tl);
@@ -328,19 +345,12 @@ bool SDisplay::checkLedState() {
return false;
}
-void SDisplay::clientCutText(const char* text, int len) {
- CharArray clip_sz(len+1);
- memcpy(clip_sz.buf, text, len);
- clip_sz.buf[len] = 0;
- clipboard->setClipText(clip_sz.buf);
-}
-
void
-SDisplay::notifyClipboardChanged(const char* text, int len) {
+SDisplay::notifyClipboardChanged(bool available) {
vlog.debug("clipboard text changed");
if (server)
- server->serverCutText(text, len);
+ server->announceClipboard(available);
}
diff --git a/win/rfb_win32/SDisplay.h b/win/rfb_win32/SDisplay.h
index 6dbfabbc..8e38edb3 100644
--- a/win/rfb_win32/SDisplay.h
+++ b/win/rfb_win32/SDisplay.h
@@ -1,4 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ * Copyright 2011-2019 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -75,13 +76,15 @@ namespace rfb {
virtual void terminate();
virtual void queryConnection(network::Socket* sock,
const char* userName);
+ virtual void handleClipboardRequest();
+ virtual void handleClipboardAnnounce(bool available);
+ virtual void handleClipboardData(const char* data);
virtual void pointerEvent(const Point& pos, int buttonmask);
virtual void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down);
- virtual void clientCutText(const char* str, int len);
- // -=- Clipboard
+ // -=- Clipboard events
- virtual void notifyClipboardChanged(const char* text, int len);
+ virtual void notifyClipboardChanged(bool available);
// -=- Display events