@@ -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); |
@@ -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 |
@@ -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) | |||
{ | |||
} |
@@ -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; | |||
}; | |||
} |
@@ -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() |
@@ -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(); | |||
@@ -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); |
@@ -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); |
@@ -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)) |
@@ -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 |
@@ -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) { } | |||
}; | |||
} |
@@ -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; |
@@ -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 |
@@ -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() {} | |||
}; |
@@ -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() | |||
{ | |||
} |
@@ -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. | |||
@@ -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() |
@@ -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(); |
@@ -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)) |
@@ -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[]); |
@@ -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++]; | |||
} | |||
} |
@@ -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 |
@@ -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 { |
@@ -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(); |
@@ -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; |
@@ -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, |
@@ -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; |
@@ -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 |
@@ -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); | |||
} |
@@ -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) | |||
{ |
@@ -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 | |||
@@ -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*) | |||
{ | |||
} | |||
@@ -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*) | |||
{ | |||
} | |||
@@ -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() |
@@ -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); | |||
@@ -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); | |||
} |
@@ -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 |
@@ -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) |
@@ -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); |
@@ -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) |
@@ -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); | |||
@@ -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; | |||
} | |||
} | |||
} |
@@ -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 | |||
} |
@@ -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 ////////////////////// | |||
@@ -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(); |
@@ -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; |
@@ -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(); |
@@ -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; | |||
} | |||
@@ -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; |
@@ -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. | |||
. |
@@ -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; | |||
@@ -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); | |||
@@ -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); | |||
} | |||
@@ -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 | |||