Change the internal clipboard API to use a request based model in order to be prepared for more advanced clipboard transfers.tags/v1.9.90
@@ -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 | |||
@@ -52,7 +52,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) | |||
{ | |||
} | |||
@@ -65,6 +66,7 @@ CConnection::~CConnection() | |||
reader_ = 0; | |||
delete writer_; | |||
writer_ = 0; | |||
strFree(serverClipboard); | |||
} | |||
void CConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_) | |||
@@ -463,6 +465,16 @@ void CConnection::dataRect(const Rect& r, int encoding) | |||
decoder.decodeRect(r, encoding, framebuffer); | |||
} | |||
void CConnection::serverCutText(const char* str) | |||
{ | |||
strFree(serverClipboard); | |||
serverClipboard = NULL; | |||
serverClipboard = strDup(str); | |||
handleClipboardAnnounce(true); | |||
} | |||
void CConnection::authSuccess() | |||
{ | |||
} | |||
@@ -476,6 +488,37 @@ 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; | |||
} | |||
} | |||
void CConnection::announceClipboard(bool available) | |||
{ | |||
if (available) | |||
handleClipboardRequest(); | |||
} | |||
void CConnection::sendClipboardData(const char* data) | |||
{ | |||
writer()->writeClientCutText(data); | |||
} | |||
void CConnection::refreshFramebuffer() | |||
{ | |||
forceNonincremental = true; |
@@ -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,8 @@ namespace rfb { | |||
virtual void framebufferUpdateEnd(); | |||
virtual void dataRect(const Rect& r, int encoding); | |||
virtual void serverCutText(const char* str); | |||
// Methods to be overridden in a derived class | |||
@@ -128,9 +130,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 +275,8 @@ namespace rfb { | |||
ModifiablePixelBuffer* framebuffer; | |||
DecodeManager decoder; | |||
char* serverClipboard; | |||
}; | |||
} | |||
#endif |
@@ -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 | |||
@@ -52,7 +52,8 @@ SConnection::SConnection() | |||
: readyForSetColourMapEntries(false), | |||
is(0), os(0), reader_(0), writer_(0), | |||
ssecurity(0), state_(RFBSTATE_UNINITIALISED), | |||
preferredEncoding(encodingRaw) | |||
preferredEncoding(encodingRaw), | |||
clientClipboard(NULL) | |||
{ | |||
defaultMajorVersion = 3; | |||
defaultMinorVersion = 8; | |||
@@ -70,6 +71,7 @@ SConnection::~SConnection() | |||
reader_ = 0; | |||
delete writer_; | |||
writer_ = 0; | |||
strFree(clientClipboard); | |||
} | |||
void SConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_) | |||
@@ -299,6 +301,16 @@ void SConnection::setEncodings(int nEncodings, const rdr::S32* encodings) | |||
SMsgHandler::setEncodings(nEncodings, encodings); | |||
} | |||
void SConnection::clientCutText(const char* str) | |||
{ | |||
strFree(clientClipboard); | |||
clientClipboard = NULL; | |||
clientClipboard = strDup(str); | |||
handleClipboardAnnounce(true); | |||
} | |||
void SConnection::supportsQEMUKeyEvent() | |||
{ | |||
writer()->writeQEMUKeyEvent(); | |||
@@ -410,6 +422,37 @@ 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; | |||
} | |||
} | |||
void SConnection::announceClipboard(bool available) | |||
{ | |||
if (available) | |||
handleClipboardRequest(); | |||
} | |||
void SConnection::sendClipboardData(const char* data) | |||
{ | |||
writer()->writeServerCutText(data); | |||
} | |||
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,11 @@ namespace rfb { | |||
virtual void setEncodings(int nEncodings, const rdr::S32* encodings); | |||
virtual void clientCutText(const char* str); | |||
virtual void supportsQEMUKeyEvent(); | |||
// Methods to be overridden in a derived class | |||
// versionReceived() indicates that the version number has just been read | |||
@@ -129,8 +132,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 +245,8 @@ namespace rfb { | |||
stateEnum state_; | |||
rdr::S32 preferredEncoding; | |||
AccessRights accessRights; | |||
char* clientClipboard; | |||
}; | |||
} | |||
#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-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) | |||
void VNCSConnectionST::setDesktopNameOrClose(const char *name) | |||
{ | |||
try { | |||
if (!accessCheck(AccessCutText)) return; | |||
if (!rfb::Server::sendCutText) return; | |||
if (state() == RFBSTATE_NORMAL) | |||
writer()->writeServerCutText(str); | |||
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) | |||
{ | |||
if (!accessCheck(AccessCutText)) return; | |||
if (!rfb::Server::acceptCutText) return; | |||
server->clientCutText(str); | |||
} | |||
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); | |||
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); | |||
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) = 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,23 +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::serverCutText(const char* str) | |||
void VNCServerST::sendClipboardData(const char* data) | |||
{ | |||
if (strchr(str, '\r') != NULL) | |||
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::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); | |||
(*ci)->bellOrClose(); | |||
} | |||
} | |||
@@ -461,9 +492,32 @@ void VNCServerST::pointerEvent(VNCSConnectionST* client, | |||
desktop->pointerEvent(pos, buttonMask); | |||
} | |||
void VNCServerST::clientCutText(const char* str) | |||
void VNCServerST::handleClipboardRequest(VNCSConnectionST* client) | |||
{ | |||
desktop->clientCutText(str); | |||
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); | |||
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); | |||
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; |
@@ -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) | |||
void XserverDesktop::sendClipboardData(const char* data) | |||
{ | |||
try { | |||
server->serverCutText(str); | |||
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) | |||
{ | |||
vncClientCutText(str); | |||
} | |||
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); | |||
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); | |||
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) | |||
void vncRequestClipboard(void) | |||
{ | |||
for (int scr = 0; scr < vncGetScreenCount(); scr++) | |||
desktop[scr]->serverCutText(text); | |||
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); | |||
void vncRequestClipboard(void); | |||
void vncAnnounceClipboard(int available); | |||
void vncSendClipboardData(const char* data); | |||
int vncConnectClient(const char *addr); | |||
@@ -47,14 +47,34 @@ static Atom xaTARGETS, xaTIMESTAMP, xaSTRING, xaTEXT, xaUTF8_STRING; | |||
static WindowPtr pWindow; | |||
static Window wid; | |||
static char* clientCutText; | |||
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); | |||
@@ -79,31 +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) | |||
void vncHandleClipboardRequest(void) | |||
{ | |||
int rc; | |||
if (clientCutText != NULL) | |||
free(clientCutText); | |||
clientCutText = strdup(str); | |||
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; | |||
} | |||
if (vncGetSetPrimary()) { | |||
rc = vncOwnSelection(xaPRIMARY); | |||
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"); | |||
} | |||
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) | |||
@@ -191,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; | |||
@@ -201,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) | |||
@@ -239,28 +333,58 @@ 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, | |||
strlen(clientCutText), clientCutText, | |||
TRUE); | |||
if (rc != Success) | |||
return rc; | |||
} else if (target == xaUTF8_STRING) { | |||
char* buffer; | |||
buffer = vncLatin1ToUTF8(clientCutText, (size_t)-1); | |||
if (buffer == NULL) | |||
return BadAlloc; | |||
rc = dixChangeWindowProperty(serverClient, pWin, realProperty, | |||
xaUTF8_STRING, 8, PropModeReplace, | |||
strlen(buffer), buffer, TRUE); | |||
vncStrFree(buffer); | |||
if (rc != Success) | |||
return rc; | |||
} else { | |||
return BadMatch; | |||
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)) { | |||
rc = dixChangeWindowProperty(serverClient, pWin, realProperty, | |||
XA_STRING, 8, PropModeReplace, | |||
strlen(data), (char*)data, | |||
TRUE); | |||
if (rc != Success) | |||
return rc; | |||
} else if (target == xaUTF8_STRING) { | |||
char* buffer; | |||
buffer = vncLatin1ToUTF8(data, (size_t)-1); | |||
if (buffer == NULL) | |||
return BadAlloc; | |||
rc = dixChangeWindowProperty(serverClient, pWin, realProperty, | |||
xaUTF8_STRING, 8, PropModeReplace, | |||
strlen(buffer), buffer, TRUE); | |||
vncStrFree(buffer); | |||
if (rc != Success) | |||
return rc; | |||
} else { | |||
return BadMatch; | |||
} | |||
} | |||
} | |||
event.u.u.type = SelectionNotify; | |||
@@ -299,7 +423,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; | |||
@@ -383,10 +507,19 @@ 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(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); | |||
} | |||
} else if (target == xaSTRING) { | |||
char* filtered; | |||
@@ -399,7 +532,10 @@ static void vncHandleSelection(Atom selection, Atom target, | |||
if (filtered == NULL) | |||
return; | |||
vncServerCutText(filtered); | |||
LOG_DEBUG("Sending clipboard to clients (%d bytes)", | |||
(int)strlen(filtered)); | |||
vncSendClipboardData(filtered); | |||
vncStrFree(filtered); | |||
} else if (target == xaUTF8_STRING) { | |||
@@ -420,7 +556,10 @@ static void vncHandleSelection(Atom selection, Atom target, | |||
if (filtered == NULL) | |||
return; | |||
vncServerCutText(filtered); | |||
LOG_DEBUG("Sending clipboard to clients (%d bytes)", | |||
(int)strlen(filtered)); | |||
vncSendClipboardData(filtered); | |||
vncStrFree(filtered); | |||
} | |||
@@ -454,6 +593,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) | |||
@@ -470,5 +615,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; | |||
} | |||
} | |||
} |
@@ -24,7 +24,9 @@ extern "C" { | |||
void vncSelectionInit(void); | |||
void vncClientCutText(const char* str); | |||
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) | |||
{ | |||
desktop->serverCutText(str); | |||
} | |||
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); | |||
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) | |||
{ | |||
viewport->serverCutText(str); | |||
} | |||
void DesktopWindow::setCursor(int width, int height, | |||
const rfb::Point& hotspot, | |||
const rdr::U8* data) | |||
@@ -420,6 +414,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); | |||
// 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 | |||
void draw(); | |||
void resize(int x, int y, int w, int h); |
@@ -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,37 +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) | |||
{ | |||
char *buffer; | |||
size_t len; | |||
clearPendingClipboard(); | |||
if (!acceptClipboard) | |||
return; | |||
buffer = latin1ToUTF8(str); | |||
len = strlen(buffer); | |||
vlog.debug("Got clipboard data (%d bytes)", (int)len); | |||
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, len, 0); | |||
#endif | |||
Fl::copy(buffer, len, 1); | |||
strFree(buffer); | |||
} | |||
static const char * dotcursor_xpm[] = { | |||
"5 5 2 1", | |||
". c #000000", | |||
@@ -311,6 +278,59 @@ 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) | |||
{ | |||
char* buffer; | |||
size_t len; | |||
if (!hasFocus()) | |||
return; | |||
buffer = latin1ToUTF8(data); | |||
len = strlen(buffer); | |||
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(buffer, len, 0); | |||
#endif | |||
Fl::copy(buffer, len, 1); | |||
strFree(buffer); | |||
} | |||
void Viewport::setLEDState(unsigned int state) | |||
{ | |||
@@ -547,21 +567,14 @@ int Viewport::handle(int event) | |||
switch (event) { | |||
case FL_PASTE: | |||
clearPendingClipboard(); | |||
buffer = utf8ToLatin1(Fl::event_text(), Fl::event_length()); | |||
filtered = convertLF(buffer); | |||
strFree(buffer); | |||
if (!hasFocus()) { | |||
pendingClientCutText = filtered; | |||
return 1; | |||
} | |||
vlog.debug("Sending clipboard data (%d bytes)", (int)strlen(filtered)); | |||
try { | |||
cc->writer()->writeClientCutText(filtered); | |||
cc->sendClipboardData(filtered); | |||
} catch (rdr::Exception& e) { | |||
vlog.error("%s", e.str()); | |||
exit_vncviewer(e.str()); | |||
@@ -725,41 +738,47 @@ void Viewport::handleClipboardChange(int source, void *data) | |||
return; | |||
#endif | |||
Fl::paste(*self, source); | |||
} | |||
self->clipboardSource = source; | |||
self->pendingServerClipboard = false; | |||
void Viewport::clearPendingClipboard() | |||
{ | |||
strFree(pendingServerCutText); | |||
pendingServerCutText = NULL; | |||
strFree(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); | |||
cc->announceClipboard(true); | |||
} catch (rdr::Exception& e) { | |||
vlog.error("%s", e.str()); | |||
exit_vncviewer(e.str()); | |||
} | |||
} | |||
clearPendingClipboard(); | |||
pendingServerClipboard = false; | |||
pendingClientClipboard = false; | |||
} | |||
@@ -45,9 +45,6 @@ public: | |||
// Flush updates to screen | |||
void updateWindow(); | |||
// Incoming clipboard from server | |||
void serverCutText(const char* str); | |||
// 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; | |||
char* pendingServerCutText; | |||
char* pendingClientCutText; | |||
bool pendingServerClipboard; | |||
bool pendingClientClipboard; | |||
int clipboardSource; | |||
rdr::U32 menuKeySym; | |||
int menuKeyCode, menuKeyFLTK; |
@@ -103,32 +103,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); | |||
} else { | |||
CharArray unix_text(convertLF(clipdata, strlen(clipdata))); | |||
removeNonISOLatin1Chars(unix_text.buf); | |||
notifier->notifyClipboardChanged(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_TEXT)); | |||
} | |||
} | |||
if (next_window) | |||
@@ -139,6 +117,40 @@ Clipboard::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) { | |||
return MsgWindow::processMessage(msg, wParam, lParam); | |||
}; | |||
char* | |||
Clipboard::getClipText() { | |||
HGLOBAL cliphandle; | |||
char* clipdata; | |||
char* filtered; | |||
// Open the clipboard | |||
if (!OpenClipboard(getHandle())) | |||
return NULL; | |||
// Get the clipboard data | |||
cliphandle = GetClipboardData(CF_TEXT); | |||
if (!cliphandle) { | |||
CloseClipboard(); | |||
return NULL; | |||
} | |||
clipdata = (char*) GlobalLock(cliphandle); | |||
if (!clipdata) { | |||
CloseClipboard(); | |||
return NULL; | |||
} | |||
// Filter out anything unwanted | |||
filtered = convertLF(clipdata, strlen(clipdata)); | |||
removeNonISOLatin1Chars(filtered); | |||
// Release the buffer and close the clipboard | |||
GlobalUnlock(cliphandle); | |||
CloseClipboard(); | |||
return filtered; | |||
} | |||
void | |||
Clipboard::setClipText(const char* text) { | |||
HANDLE clip_handle = 0; |
@@ -39,7 +39,7 @@ namespace rfb { | |||
// -=- Abstract base class for callback recipients | |||
class Notifier { | |||
public: | |||
virtual void notifyClipboardChanged(const char* text) = 0; | |||
virtual void notifyClipboardChanged(bool available) = 0; | |||
virtual ~Notifier() {}; | |||
}; | |||
@@ -49,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); | |||
@@ -292,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); | |||
@@ -329,16 +345,12 @@ bool SDisplay::checkLedState() { | |||
return false; | |||
} | |||
void SDisplay::clientCutText(const char* text) { | |||
clipboard->setClipText(text); | |||
} | |||
void | |||
SDisplay::notifyClipboardChanged(const char* text) { | |||
SDisplay::notifyClipboardChanged(bool available) { | |||
vlog.debug("clipboard text changed"); | |||
if (server) | |||
server->serverCutText(text); | |||
server->announceClipboard(available); | |||
} | |||
@@ -76,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); | |||
// -=- Clipboard | |||
// -=- Clipboard events | |||
virtual void notifyClipboardChanged(const char* text); | |||
virtual void notifyClipboardChanged(bool available); | |||
// -=- Display events | |||