Browse Source

Improved clipboard API

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
Pierre Ossman 5 years ago
parent
commit
615d16bd5b

+ 45
- 2
common/rfb/CConnection.cxx View File

@@ -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;

+ 38
- 1
common/rfb/CConnection.h View File

@@ -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

+ 45
- 2
common/rfb/SConnection.cxx View File

@@ -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;

+ 40
- 1
common/rfb/SConnection.h View File

@@ -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

+ 20
- 0
common/rfb/SDesktop.h View File

@@ -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() {}
};

+ 55
- 23
common/rfb/VNCSConnectionST.cxx View File

@@ -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 {

+ 7
- 3
common/rfb/VNCSConnectionST.h View File

@@ -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();

+ 16
- 3
common/rfb/VNCServer.h View File

@@ -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;

+ 65
- 11
common/rfb/VNCServerST.cxx View File

@@ -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,

+ 11
- 3
common/rfb/VNCServerST.h View File

@@ -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;

+ 41
- 13
unix/xserver/hw/vnc/XserverDesktop.cc View File

@@ -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)

+ 7
- 3
unix/xserver/hw/vnc/XserverDesktop.h View File

@@ -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);

+ 15
- 3
unix/xserver/hw/vnc/vncExtInit.cc View File

@@ -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)

+ 4
- 2
unix/xserver/hw/vnc/vncExtInit.h View File

@@ -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);


+ 213
- 48
unix/xserver/hw/vnc/vncSelection.c View File

@@ -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;
}
}
}

+ 3
- 1
unix/xserver/hw/vnc/vncSelection.h View File

@@ -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
}

+ 15
- 5
vncviewer/CConn.cxx View File

@@ -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 //////////////////////


+ 4
- 2
vncviewer/CConn.h View File

@@ -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();

+ 16
- 6
vncviewer/DesktopWindow.cxx View File

@@ -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;

+ 5
- 3
vncviewer/DesktopWindow.h View File

@@ -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);

+ 81
- 62
vncviewer/Viewport.cxx View File

@@ -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;
}



+ 9
- 6
vncviewer/Viewport.h View File

@@ -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;

+ 38
- 26
win/rfb_win32/Clipboard.cxx View File

@@ -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;

+ 4
- 1
win/rfb_win32/Clipboard.h View File

@@ -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);


+ 18
- 6
win/rfb_win32/SDisplay.cxx View File

@@ -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);
}



+ 5
- 3
win/rfb_win32/SDisplay.h View File

@@ -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

Loading…
Cancel
Save