mirror of
https://github.com/kasmtech/KasmVNC.git
synced 2025-02-16 10:29:19 +01:00
Sync utf8 clipboard support
This commit is contained in:
parent
04461b9d4c
commit
a1cf454f06
@ -20,6 +20,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <rfb/Exception.h>
|
||||
#include <rfb/clipboardTypes.h>
|
||||
#include <rfb/fenceTypes.h>
|
||||
#include <rfb/CMsgReader.h>
|
||||
#include <rfb/CMsgWriter.h>
|
||||
@ -42,7 +43,8 @@ CConnection::CConnection()
|
||||
: csecurity(0), is(0), os(0), reader_(0), writer_(0),
|
||||
shared(false),
|
||||
state_(RFBSTATE_UNINITIALISED), useProtocol3_3(false),
|
||||
framebuffer(NULL), decoder(this)
|
||||
framebuffer(NULL), decoder(this),
|
||||
serverClipboard(NULL), hasLocalClipboard(false)
|
||||
{
|
||||
}
|
||||
|
||||
@ -54,6 +56,7 @@ CConnection::~CConnection()
|
||||
reader_ = 0;
|
||||
delete writer_;
|
||||
writer_ = 0;
|
||||
strFree(serverClipboard);
|
||||
}
|
||||
|
||||
void CConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
|
||||
@ -342,6 +345,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 (cp.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()
|
||||
{
|
||||
}
|
||||
@ -364,3 +440,53 @@ void CConnection::fence(rdr::U32 flags, unsigned len, const char data[])
|
||||
|
||||
writer()->writeFence(flags, len, data);
|
||||
}
|
||||
|
||||
void CConnection::handleClipboardRequest()
|
||||
{
|
||||
}
|
||||
|
||||
void CConnection::handleClipboardAnnounce(bool available)
|
||||
{
|
||||
}
|
||||
|
||||
void CConnection::handleClipboardData(const char* data)
|
||||
{
|
||||
}
|
||||
|
||||
void CConnection::requestClipboard()
|
||||
{
|
||||
if (serverClipboard != NULL) {
|
||||
handleClipboardData(serverClipboard);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cp.clipboardFlags() & rfb::clipboardRequest)
|
||||
writer()->writeClipboardRequest(rfb::clipboardUTF8);
|
||||
}
|
||||
|
||||
void CConnection::announceClipboard(bool available)
|
||||
{
|
||||
hasLocalClipboard = available;
|
||||
|
||||
if (cp.clipboardFlags() & rfb::clipboardNotify)
|
||||
writer()->writeClipboardNotify(available ? rfb::clipboardUTF8 : 0);
|
||||
else {
|
||||
if (available)
|
||||
handleClipboardRequest();
|
||||
}
|
||||
}
|
||||
|
||||
void CConnection::sendClipboardData(const char* data)
|
||||
{
|
||||
if (cp.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, strlen(latin1.buf));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -107,6 +107,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
|
||||
|
||||
@ -121,9 +132,43 @@ namespace rfb {
|
||||
// derived class must call on to CConnection::serverInit().
|
||||
virtual void serverInit();
|
||||
|
||||
// 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);
|
||||
|
||||
|
||||
CMsgReader* reader() { return reader_; }
|
||||
CMsgWriter* writer() { return writer_; }
|
||||
|
||||
@ -190,6 +235,9 @@ namespace rfb {
|
||||
|
||||
ModifiablePixelBuffer* framebuffer;
|
||||
DecodeManager decoder;
|
||||
|
||||
char* serverClipboard;
|
||||
bool hasLocalClipboard;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
@ -92,3 +92,26 @@ void CMsgHandler::setLEDState(unsigned int state)
|
||||
{
|
||||
cp.setLEDState(state);
|
||||
}
|
||||
|
||||
void CMsgHandler::handleClipboardCaps(rdr::U32 flags, const rdr::U32* lengths)
|
||||
{
|
||||
cp.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)
|
||||
{
|
||||
}
|
||||
|
@ -72,6 +72,15 @@ namespace rfb {
|
||||
|
||||
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);
|
||||
|
||||
ConnParams cp;
|
||||
};
|
||||
}
|
||||
|
@ -18,10 +18,14 @@
|
||||
*/
|
||||
#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/encodings.h>
|
||||
#include <rfb/qemuTypes.h>
|
||||
#include <rfb/clipboardTypes.h>
|
||||
#include <rfb/Exception.h>
|
||||
#include <rfb/PixelFormat.h>
|
||||
#include <rfb/Rect.h>
|
||||
@ -265,6 +269,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 (!(cp->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 (!(cp->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 (!(cp->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 (!(cp->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 (!(cp->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);
|
||||
|
@ -56,6 +56,13 @@ namespace rfb {
|
||||
void writePointerEvent(const Point& pos, int buttonMask);
|
||||
void writeClientCutText(const char* str, rdr::U32 len);
|
||||
|
||||
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);
|
||||
void endMsg();
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <rfb/Exception.h>
|
||||
#include <rfb/encodings.h>
|
||||
#include <rfb/ledStates.h>
|
||||
#include <rfb/clipboardTypes.h>
|
||||
#include <rfb/ConnParams.h>
|
||||
#include <rfb/ServerCore.h>
|
||||
#include <rfb/SMsgHandler.h>
|
||||
@ -42,7 +43,7 @@ ConnParams::ConnParams()
|
||||
supportsLEDState(false), supportsQEMUKeyEvent(false),
|
||||
supportsWEBP(false),
|
||||
supportsSetDesktopSize(false), supportsFence(false),
|
||||
supportsContinuousUpdates(false),
|
||||
supportsContinuousUpdates(false), supportsExtendedClipboard(false),
|
||||
compressLevel(2), qualityLevel(-1), fineQualityLevel(-1),
|
||||
subsampling(subsampleUndefined), name_(0), cursorPos_(0, 0), verStrPos(0),
|
||||
ledState_(ledUnknown), shandler(NULL)
|
||||
@ -50,6 +51,11 @@ ConnParams::ConnParams()
|
||||
memset(kasmPassed, 0, KASM_NUM_SETTINGS);
|
||||
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;
|
||||
}
|
||||
|
||||
ConnParams::~ConnParams()
|
||||
@ -177,6 +183,9 @@ void ConnParams::setEncodings(int nEncodings, const rdr::S32* encodings)
|
||||
case pseudoEncodingContinuousUpdates:
|
||||
supportsContinuousUpdates = true;
|
||||
break;
|
||||
case pseudoEncodingExtendedClipboard:
|
||||
supportsExtendedClipboard = true;
|
||||
break;
|
||||
case pseudoEncodingSubsamp1X:
|
||||
subsampling = subsampleNone;
|
||||
break;
|
||||
@ -268,3 +277,17 @@ void ConnParams::setLEDState(unsigned int state)
|
||||
{
|
||||
ledState_ = state;
|
||||
}
|
||||
|
||||
void ConnParams::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++];
|
||||
}
|
||||
}
|
||||
|
@ -94,6 +94,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 useCopyRect;
|
||||
|
||||
bool supportsLocalCursor;
|
||||
@ -111,6 +114,7 @@ namespace rfb {
|
||||
bool supportsSetDesktopSize;
|
||||
bool supportsFence;
|
||||
bool supportsContinuousUpdates;
|
||||
bool supportsExtendedClipboard;
|
||||
|
||||
int compressLevel;
|
||||
int qualityLevel;
|
||||
@ -146,6 +150,8 @@ namespace rfb {
|
||||
int verStrPos;
|
||||
unsigned int ledState_;
|
||||
SMsgHandler *shandler;
|
||||
rdr::U32 clipFlags;
|
||||
rdr::U32 clipSizes[16];
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
@ -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;
|
||||
@ -69,6 +71,7 @@ SConnection::~SConnection()
|
||||
reader_ = 0;
|
||||
delete writer_;
|
||||
writer_ = 0;
|
||||
strFree(clientClipboard);
|
||||
}
|
||||
|
||||
void SConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
|
||||
@ -281,6 +284,69 @@ void SConnection::setEncodings(int nEncodings, const rdr::S32* encodings)
|
||||
}
|
||||
|
||||
SMsgHandler::setEncodings(nEncodings, encodings);
|
||||
|
||||
if (cp.supportsExtendedClipboard) {
|
||||
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 (cp.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, strlen(clientClipboard));
|
||||
}
|
||||
|
||||
void SConnection::supportsQEMUKeyEvent()
|
||||
@ -375,6 +441,58 @@ void SConnection::enableContinuousUpdates(bool enable,
|
||||
{
|
||||
}
|
||||
|
||||
void SConnection::handleClipboardRequest()
|
||||
{
|
||||
}
|
||||
|
||||
void SConnection::handleClipboardAnnounce(bool available)
|
||||
{
|
||||
}
|
||||
|
||||
void SConnection::handleClipboardData(const char* data, int len)
|
||||
{
|
||||
}
|
||||
|
||||
void SConnection::requestClipboard()
|
||||
{
|
||||
if (clientClipboard != NULL) {
|
||||
handleClipboardData(clientClipboard, strlen(clientClipboard));
|
||||
return;
|
||||
}
|
||||
|
||||
if (cp.supportsExtendedClipboard &&
|
||||
(cp.clipboardFlags() & rfb::clipboardRequest))
|
||||
writer()->writeClipboardRequest(rfb::clipboardUTF8);
|
||||
}
|
||||
|
||||
void SConnection::announceClipboard(bool available)
|
||||
{
|
||||
hasLocalClipboard = available;
|
||||
|
||||
if (cp.supportsExtendedClipboard &&
|
||||
(cp.clipboardFlags() & rfb::clipboardNotify))
|
||||
writer()->writeClipboardNotify(available ? rfb::clipboardUTF8 : 0);
|
||||
else {
|
||||
if (available)
|
||||
handleClipboardRequest();
|
||||
}
|
||||
}
|
||||
|
||||
void SConnection::sendClipboardData(const char* data, int len)
|
||||
{
|
||||
if (cp.supportsExtendedClipboard &&
|
||||
(cp.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, strlen(latin1.buf));
|
||||
}
|
||||
}
|
||||
|
||||
void SConnection::writeFakeColourMap(void)
|
||||
{
|
||||
int i;
|
||||
|
@ -73,6 +73,15 @@ 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
|
||||
@ -118,6 +127,25 @@ 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, int len);
|
||||
|
||||
|
||||
virtual void add_changed_all() {}
|
||||
|
||||
// setAccessRights() allows a security package to limit the access rights
|
||||
@ -138,6 +166,22 @@ namespace rfb {
|
||||
|
||||
// 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, int len);
|
||||
|
||||
|
||||
// authenticated() returns true if the client has authenticated
|
||||
// successfully.
|
||||
bool authenticated() { return (state_ == RFBSTATE_INITIALISATION ||
|
||||
@ -203,6 +247,9 @@ namespace rfb {
|
||||
SSecurity* ssecurity;
|
||||
stateEnum state_;
|
||||
rdr::S32 preferredEncoding;
|
||||
|
||||
char* clientClipboard;
|
||||
bool hasLocalClipboard;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
@ -77,6 +77,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, int len __unused_attr) {}
|
||||
|
||||
protected:
|
||||
virtual ~SDesktop() {}
|
||||
};
|
||||
|
@ -64,6 +64,29 @@ void SMsgHandler::setEncodings(int nEncodings, const rdr::S32* encodings)
|
||||
supportsQEMUKeyEvent();
|
||||
}
|
||||
|
||||
void SMsgHandler::handleClipboardCaps(rdr::U32 flags, const rdr::U32* lengths)
|
||||
{
|
||||
cp.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()
|
||||
{
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
virtual void sendStats() = 0;
|
||||
|
||||
virtual bool canChangeKasmSettings() const = 0;
|
||||
|
@ -18,8 +18,11 @@
|
||||
*/
|
||||
#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>
|
||||
@ -224,11 +227,15 @@ 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;
|
||||
@ -239,6 +246,100 @@ void SMsgReader::readClientCutText()
|
||||
handler->clientCutText(ca.buf, len);
|
||||
}
|
||||
|
||||
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.flushUnderlying();
|
||||
zis.setUnderlying(NULL, 0);
|
||||
|
||||
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::readRequestStats()
|
||||
{
|
||||
is->skip(3);
|
||||
|
@ -55,6 +55,7 @@ namespace rfb {
|
||||
void readKeyEvent();
|
||||
void readPointerEvent();
|
||||
void readClientCutText();
|
||||
void readExtendedClipboard(rdr::S32 len);
|
||||
void readRequestStats();
|
||||
|
||||
void readQEMUMessage();
|
||||
|
@ -19,8 +19,12 @@
|
||||
*/
|
||||
#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/ConnParams.h>
|
||||
#include <rfb/UpdateTracker.h>
|
||||
@ -89,6 +93,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 (!cp->supportsExtendedClipboard)
|
||||
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 (!cp->supportsExtendedClipboard)
|
||||
throw Exception("Client does not support extended clipboard");
|
||||
if (!(cp->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 (!cp->supportsExtendedClipboard)
|
||||
throw Exception("Client does not support extended clipboard");
|
||||
if (!(cp->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 (!cp->supportsExtendedClipboard)
|
||||
throw Exception("Client does not support extended clipboard");
|
||||
if (!(cp->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 (!cp->supportsExtendedClipboard)
|
||||
throw Exception("Client does not support extended clipboard");
|
||||
if (!(cp->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::writeStats(const char* str, int len)
|
||||
{
|
||||
startMsg(msgTypeStats);
|
||||
|
@ -55,6 +55,14 @@ namespace rfb {
|
||||
// writeBell() and writeServerCutText() do the obvious thing.
|
||||
void writeBell();
|
||||
void writeServerCutText(const char* str, int len);
|
||||
|
||||
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);
|
||||
|
||||
void writeStats(const char* str, int len);
|
||||
|
||||
// writeFence() sends a new fence request or response to the client.
|
||||
|
@ -403,7 +403,31 @@ static void keylog(unsigned keysym, const char *client) {
|
||||
flushKeylog(client);
|
||||
}
|
||||
|
||||
void VNCSConnectionST::serverCutTextOrClose(const char *str, int len)
|
||||
void VNCSConnectionST::requestClipboardOrClose()
|
||||
{
|
||||
try {
|
||||
if (!(accessRights & AccessCutText)) return;
|
||||
if (!rfb::Server::acceptCutText) return;
|
||||
if (state() != RFBSTATE_NORMAL) return;
|
||||
requestClipboard();
|
||||
} catch(rdr::Exception& e) {
|
||||
close(e.str());
|
||||
}
|
||||
}
|
||||
|
||||
void VNCSConnectionST::announceClipboardOrClose(bool available)
|
||||
{
|
||||
try {
|
||||
if (!(accessRights & 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 (!(accessRights & AccessCutText)) return;
|
||||
@ -413,19 +437,19 @@ void VNCSConnectionST::serverCutTextOrClose(const char *str, int len)
|
||||
sock->getPeerAddress());
|
||||
return;
|
||||
}
|
||||
int len = strlen(data);
|
||||
const int origlen = len;
|
||||
if (rfb::Server::DLP_ClipSendMax && len > rfb::Server::DLP_ClipSendMax)
|
||||
len = rfb::Server::DLP_ClipSendMax;
|
||||
cliplog(str, len, origlen, "sent", sock->getPeerAddress());
|
||||
if (state() == RFBSTATE_NORMAL)
|
||||
writer()->writeServerCutText(str, len);
|
||||
cliplog(data, len, origlen, "sent", sock->getPeerAddress());
|
||||
if (state() != RFBSTATE_NORMAL) return;
|
||||
sendClipboardData(data, len);
|
||||
gettimeofday(&lastClipboardOp, NULL);
|
||||
} catch(rdr::Exception& e) {
|
||||
close(e.str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void VNCSConnectionST::setDesktopNameOrClose(const char *name)
|
||||
{
|
||||
try {
|
||||
@ -846,24 +870,6 @@ void VNCSConnectionST::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down) {
|
||||
server->desktop->keyEvent(keysym, keycode, down);
|
||||
}
|
||||
|
||||
void VNCSConnectionST::clientCutText(const char* str, int len)
|
||||
{
|
||||
if (!(accessRights & AccessCutText)) return;
|
||||
if (!rfb::Server::acceptCutText) return;
|
||||
if (msSince(&lastClipboardOp) < (unsigned) rfb::Server::DLP_ClipDelay) {
|
||||
vlog.info("DLP: client %s: refused to receive clipboard, too soon",
|
||||
sock->getPeerAddress());
|
||||
return;
|
||||
}
|
||||
const int origlen = len;
|
||||
if (rfb::Server::DLP_ClipAcceptMax && len > rfb::Server::DLP_ClipAcceptMax)
|
||||
len = rfb::Server::DLP_ClipAcceptMax;
|
||||
cliplog(str, len, origlen, "received", sock->getPeerAddress());
|
||||
|
||||
gettimeofday(&lastClipboardOp, NULL);
|
||||
server->desktop->clientCutText(str, len);
|
||||
}
|
||||
|
||||
void VNCSConnectionST::framebufferUpdateRequest(const Rect& r,bool incremental)
|
||||
{
|
||||
Rect safeRect;
|
||||
@ -997,6 +1003,39 @@ void VNCSConnectionST::enableContinuousUpdates(bool enable,
|
||||
}
|
||||
}
|
||||
|
||||
void VNCSConnectionST::handleClipboardRequest()
|
||||
{
|
||||
if (!(accessRights & AccessCutText)) return;
|
||||
server->handleClipboardRequest(this);
|
||||
}
|
||||
|
||||
void VNCSConnectionST::handleClipboardAnnounce(bool available)
|
||||
{
|
||||
if (!(accessRights & AccessCutText)) return;
|
||||
if (!rfb::Server::acceptCutText) return;
|
||||
server->handleClipboardAnnounce(this, available);
|
||||
}
|
||||
|
||||
void VNCSConnectionST::handleClipboardData(const char* data)
|
||||
{
|
||||
if (!(accessRights & AccessCutText)) return;
|
||||
if (!rfb::Server::acceptCutText) return;
|
||||
if (msSince(&lastClipboardOp) < (unsigned) rfb::Server::DLP_ClipDelay) {
|
||||
vlog.info("DLP: client %s: refused to receive clipboard, too soon",
|
||||
sock->getPeerAddress());
|
||||
return;
|
||||
}
|
||||
int len = strlen(data);
|
||||
const int origlen = len;
|
||||
if (rfb::Server::DLP_ClipAcceptMax && len > rfb::Server::DLP_ClipAcceptMax)
|
||||
len = rfb::Server::DLP_ClipAcceptMax;
|
||||
cliplog(data, len, origlen, "received", sock->getPeerAddress());
|
||||
|
||||
gettimeofday(&lastClipboardOp, NULL);
|
||||
server->handleClipboardData(this, data, len);
|
||||
}
|
||||
|
||||
|
||||
// supportsLocalCursor() is called whenever the status of
|
||||
// cp.supportsLocalCursor has changed. If the client does now support local
|
||||
// cursor, we make sure that the old server-side rendered cursor is cleaned up
|
||||
|
@ -75,9 +75,11 @@ 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 requestClipboardOrClose();
|
||||
void announceClipboardOrClose(bool available);
|
||||
void sendClipboardDataOrClose(const char* data);
|
||||
|
||||
// checkIdleTimeout() returns the number of milliseconds left until the
|
||||
// idle timeout expires. If it has expired, the connection is closed and
|
||||
@ -175,13 +177,15 @@ namespace rfb {
|
||||
virtual void setPixelFormat(const PixelFormat& pf);
|
||||
virtual void pointerEvent(const Point& pos, int buttonMask, const bool skipClick, const bool skipRelease);
|
||||
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();
|
||||
|
@ -52,9 +52,21 @@ namespace rfb {
|
||||
// getPixelBuffer() returns a pointer to the PixelBuffer object.
|
||||
virtual 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;
|
||||
|
@ -123,8 +123,8 @@ static void parseRegionPart(const bool percents, rdr::U16 &pcdest, int &dest,
|
||||
VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_)
|
||||
: blHosts(&blacklist), desktop(desktop_), desktopStarted(false),
|
||||
blockCounter(0), pb(0), blackedpb(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),
|
||||
queryConnectionHandler(0), keyRemapper(&KeyRemapper::defInstance),
|
||||
lastConnectionTime(0), disableclients(false),
|
||||
@ -502,6 +502,45 @@ void VNCServerST::setScreenLayout(const ScreenSet& layout)
|
||||
}
|
||||
}
|
||||
|
||||
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)->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, strlen(data));
|
||||
}
|
||||
|
||||
clipboardRequestors.clear();
|
||||
}
|
||||
|
||||
void VNCServerST::bell()
|
||||
{
|
||||
std::list<VNCSConnectionST*>::iterator ci, ci_next;
|
||||
@ -511,15 +550,6 @@ void VNCServerST::bell()
|
||||
}
|
||||
}
|
||||
|
||||
void VNCServerST::serverCutText(const char* str, int len)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void VNCServerST::setName(const char* name_)
|
||||
{
|
||||
name.replaceBuf(strDup(name_));
|
||||
@ -1052,3 +1082,32 @@ bool VNCServerST::getComparerState()
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void VNCServerST::handleClipboardRequest(VNCSConnectionST* client)
|
||||
{
|
||||
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, int len)
|
||||
{
|
||||
if (client != clipboardClient)
|
||||
return;
|
||||
desktop->handleClipboardData(data, len);
|
||||
}
|
||||
|
||||
|
@ -96,7 +96,9 @@ namespace rfb {
|
||||
virtual void setPixelBuffer(PixelBuffer* pb);
|
||||
virtual void setScreenLayout(const ScreenSet& layout);
|
||||
virtual PixelBuffer* getPixelBuffer() const { if (DLPRegion.enabled && blackedpb) return blackedpb; else 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 add_changed(const Region ®ion);
|
||||
virtual void add_copied(const Region &dest, const Point &delta);
|
||||
virtual void setCursor(int width, int height, const Point& hotspot,
|
||||
@ -189,6 +191,10 @@ namespace rfb {
|
||||
|
||||
void setAPIMessager(network::GetAPIMessager *msgr) { apimessager = msgr; }
|
||||
|
||||
void handleClipboardRequest(VNCSConnectionST* client);
|
||||
void handleClipboardAnnounce(VNCSConnectionST* client, bool available);
|
||||
void handleClipboardData(VNCSConnectionST* client, const char* data, int len);
|
||||
|
||||
protected:
|
||||
|
||||
friend class VNCSConnectionST;
|
||||
@ -217,6 +223,8 @@ namespace rfb {
|
||||
|
||||
std::list<VNCSConnectionST*> clients;
|
||||
VNCSConnectionST* pointerClient;
|
||||
VNCSConnectionST* clipboardClient;
|
||||
std::list<VNCSConnectionST*> clipboardRequestors;
|
||||
std::list<network::Socket*> closingSockets;
|
||||
|
||||
static EncCache encCache;
|
||||
|
41
common/rfb/clipboardTypes.h
Normal file
41
common/rfb/clipboardTypes.h
Normal file
@ -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
|
@ -88,6 +88,9 @@ namespace rfb {
|
||||
// VMware-specific
|
||||
const int pseudoEncodingVMwareCursorPosition = 0x574d5666;
|
||||
|
||||
// UltraVNC-specific
|
||||
const int pseudoEncodingExtendedClipboard = 0xC0A1E5CE;
|
||||
|
||||
int encodingNum(const char* name);
|
||||
const char* encodingName(int num);
|
||||
}
|
||||
|
@ -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,444 @@ 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_len > 0) && (*in != '\0')) {
|
||||
if (*in != '\r') {
|
||||
sz++;
|
||||
in++;
|
||||
in_len--;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((in_len < 2) || (*(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_len > 0) && (*in != '\0')) {
|
||||
if (*in != '\r') {
|
||||
*out++ = *in++;
|
||||
in_len--;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((in_len < 2) || (*(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_len > 0) && (*in != '\0')) {
|
||||
sz++;
|
||||
|
||||
if (*in == '\r') {
|
||||
if ((in_len < 2) || (*(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_len > 0) && (*in != '\0')) {
|
||||
if (*in == '\n') {
|
||||
if ((in == src) || (*(in-1) != '\r'))
|
||||
*out++ = '\r';
|
||||
}
|
||||
|
||||
*out = *in;
|
||||
|
||||
if (*in == '\r') {
|
||||
if ((in_len < 2) || (*(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--) {
|
||||
consumed++;
|
||||
|
||||
// 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 >= 0x10000) && (src < 0x110000)) {
|
||||
src -= 0x10000;
|
||||
*dst++ = 0xd800 | ((src >> 10) & 0x03ff);
|
||||
*dst++ = 0xdc00 | (src & 0x03ff);
|
||||
*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_len > 0) && (*in != '\0')) {
|
||||
char buf[5];
|
||||
sz += ucs4ToUTF8(*(const unsigned char*)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_len > 0) && (*in != '\0')) {
|
||||
out += ucs4ToUTF8(*(const unsigned char*)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_len > 0) && (*in != '\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_len > 0) && (*in != '\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_len > 0) && (*in != '\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_len > 0) && (*in != '\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_len > 0) && (*in != '\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 * sizeof(wchar_t));
|
||||
|
||||
// And convert
|
||||
out = buffer;
|
||||
in = src;
|
||||
in_len = bytes;
|
||||
while ((in_len > 0) && (*in != '\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)
|
||||
{
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
@ -226,3 +226,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);
|
||||
}
|
||||
|
@ -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
|
||||
@ -50,6 +50,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
|
||||
|
@ -176,6 +176,33 @@ XserverDesktop::queryConnection(network::Socket* sock,
|
||||
return rfb::VNCServerST::PENDING;
|
||||
}
|
||||
|
||||
void XserverDesktop::requestClipboard()
|
||||
{
|
||||
try {
|
||||
server->requestClipboard();
|
||||
} catch (rdr::Exception& e) {
|
||||
vlog.error("XserverDesktop::requestClipboard: %s",e.str());
|
||||
}
|
||||
}
|
||||
|
||||
void XserverDesktop::announceClipboard(bool available)
|
||||
{
|
||||
try {
|
||||
server->announceClipboard(available);
|
||||
} catch (rdr::Exception& e) {
|
||||
vlog.error("XserverDesktop::announceClipboard: %s",e.str());
|
||||
}
|
||||
}
|
||||
|
||||
void XserverDesktop::sendClipboardData(const char* data)
|
||||
{
|
||||
try {
|
||||
server->sendClipboardData(data);
|
||||
} catch (rdr::Exception& e) {
|
||||
vlog.error("XserverDesktop::sendClipboardData: %s",e.str());
|
||||
}
|
||||
}
|
||||
|
||||
void XserverDesktop::bell()
|
||||
{
|
||||
server->bell();
|
||||
@ -186,15 +213,6 @@ void XserverDesktop::setLEDState(unsigned int state)
|
||||
server->setLEDState(state);
|
||||
}
|
||||
|
||||
void XserverDesktop::serverCutText(const char* str, int len)
|
||||
{
|
||||
try {
|
||||
server->serverCutText(str, len);
|
||||
} catch (rdr::Exception& e) {
|
||||
vlog.error("XserverDesktop::serverCutText: %s",e.str());
|
||||
}
|
||||
}
|
||||
|
||||
void XserverDesktop::setDesktopName(const char* name)
|
||||
{
|
||||
try {
|
||||
@ -435,11 +453,6 @@ void XserverDesktop::pointerEvent(const Point& pos, int buttonMask,
|
||||
vncPointerButtonAction(buttonMask, skipClick, skipRelease);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
@ -453,6 +466,21 @@ unsigned int XserverDesktop::setScreenLayout(int fb_width, int fb_height,
|
||||
return ::setScreenLayout(fb_width, fb_height, layout, &outputIdMap);
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -60,9 +60,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);
|
||||
@ -90,9 +92,11 @@ public:
|
||||
virtual void pointerEvent(const rfb::Point& pos, int buttonMask,
|
||||
const bool skipClick, const bool skipRelease);
|
||||
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);
|
||||
|
@ -315,10 +315,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)
|
||||
|
@ -60,7 +60,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);
|
||||
|
||||
|
@ -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");
|
||||
|
||||
if (vncGetSetPrimary()) {
|
||||
rc = vncOwnSelection(xaPRIMARY);
|
||||
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");
|
||||
}
|
||||
|
||||
rc = vncOwnSelection(xaCLIPBOARD);
|
||||
if (rc != Success)
|
||||
LOG_ERROR("Could not set PRIMARY selection");
|
||||
}
|
||||
LOG_ERROR("Could not set CLIPBOARD selection");
|
||||
} else {
|
||||
struct VncDataTarget* next;
|
||||
|
||||
vncOwnSelection(xaCLIPBOARD);
|
||||
if (rc != Success)
|
||||
LOG_ERROR("Could not set CLIPBOARD selection");
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
} else {
|
||||
if (data == NULL) {
|
||||
struct VncDataTarget* vdt;
|
||||
|
||||
const unsigned char* in;
|
||||
size_t in_len;
|
||||
if ((target != xaSTRING) && (target != xaTEXT) &&
|
||||
(target != xaUTF8_STRING))
|
||||
return BadMatch;
|
||||
|
||||
buffer = malloc(clientCutTextLen*2);
|
||||
if (buffer == NULL)
|
||||
return BadAlloc;
|
||||
vdt = calloc(1, sizeof(struct VncDataTarget));
|
||||
if (vdt == 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--;
|
||||
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;
|
||||
|
||||
const unsigned char* in;
|
||||
size_t in_len;
|
||||
utf8 = vncLatin1ToUTF8(filtered, (size_t)-1);
|
||||
vncStrFree(filtered);
|
||||
if (utf8 == NULL)
|
||||
return;
|
||||
|
||||
LOG_DEBUG("Sending clipboard to clients (%d bytes)",
|
||||
(int)strlen(utf8));
|
||||
|
||||
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)
|
||||
@ -527,5 +623,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user