Improved clipboard API

Change the internal clipboard API to use a request based model in
order to be prepared for more advanced clipboard transfers.
diff --git a/common/rfb/CConnection.cxx b/common/rfb/CConnection.cxx
index d6960dd..ce2741e 100644
--- a/common/rfb/CConnection.cxx
+++ b/common/rfb/CConnection.cxx
@@ -1,5 +1,5 @@
 /* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
- * Copyright 2011-2017 Pierre Ossman for Cendio AB
+ * Copyright 2011-2019 Pierre Ossman for Cendio AB
  * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -52,7 +52,8 @@
     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 @@
   reader_ = 0;
   delete writer_;
   writer_ = 0;
+  strFree(serverClipboard);
 }
 
 void CConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
@@ -463,6 +465,16 @@
   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 @@
   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;
diff --git a/common/rfb/CConnection.h b/common/rfb/CConnection.h
index 66a71a2..4106a1e 100644
--- a/common/rfb/CConnection.h
+++ b/common/rfb/CConnection.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
- * Copyright 2011-2017 Pierre Ossman for Cendio AB
+ * Copyright 2011-2019 Pierre Ossman for Cendio AB
  * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -109,6 +109,8 @@
     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 @@
     // 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 @@
 
     ModifiablePixelBuffer* framebuffer;
     DecodeManager decoder;
+
+    char* serverClipboard;
   };
 }
 #endif
diff --git a/common/rfb/SConnection.cxx b/common/rfb/SConnection.cxx
index 4e224aa..1cc330d 100644
--- a/common/rfb/SConnection.cxx
+++ b/common/rfb/SConnection.cxx
@@ -1,5 +1,5 @@
 /* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
- * Copyright 2011 Pierre Ossman for Cendio AB
+ * Copyright 2011-2019 Pierre Ossman for Cendio AB
  * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -52,7 +52,8 @@
   : 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 @@
   reader_ = 0;
   delete writer_;
   writer_ = 0;
+  strFree(clientClipboard);
 }
 
 void SConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
@@ -299,6 +301,16 @@
   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::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;
diff --git a/common/rfb/SConnection.h b/common/rfb/SConnection.h
index 31d1cb2..6c80569 100644
--- a/common/rfb/SConnection.h
+++ b/common/rfb/SConnection.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
- * Copyright 2011 Pierre Ossman for Cendio AB
+ * Copyright 2011-2019 Pierre Ossman for Cendio AB
  * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -80,8 +80,11 @@
 
     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 @@
     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 @@
     stateEnum state_;
     rdr::S32 preferredEncoding;
     AccessRights accessRights;
+
+    char* clientClipboard;
   };
 }
 #endif
diff --git a/common/rfb/SDesktop.h b/common/rfb/SDesktop.h
index 0060aa2..b4104ea 100644
--- a/common/rfb/SDesktop.h
+++ b/common/rfb/SDesktop.h
@@ -1,4 +1,5 @@
 /* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * Copyright 2009-2019 Pierre Ossman for Cendio AB
  * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -93,6 +94,25 @@
     // pointerEvent(), keyEvent() and clientCutText() are called in response to
     // the relevant RFB protocol messages from clients.
     // See InputHandler for method signatures.
+
+    // handleClipboardRequest() is called whenever a client requests
+    // the server to send over its clipboard data. It will only be
+    // called after the server has first announced a clipboard change
+    // via VNCServer::announceClipboard().
+    virtual void handleClipboardRequest() {}
+
+    // handleClipboardAnnounce() is called to indicate a change in the
+    // clipboard on a client. Call VNCServer::requestClipboard() to
+    // access the actual data.
+    virtual void handleClipboardAnnounce(bool __unused_attr available) {}
+
+    // handleClipboardData() is called when a client has sent over
+    // the clipboard data as a result of a previous call to
+    // VNCServer::requestClipboard(). Note that this function might
+    // never be called if the clipboard data was no longer available
+    // when the client received the request.
+    virtual void handleClipboardData(const char* __unused_attr data) {}
+
   protected:
     virtual ~SDesktop() {}
   };
diff --git a/common/rfb/VNCSConnectionST.cxx b/common/rfb/VNCSConnectionST.cxx
index 002ae92..cdd87b1 100644
--- a/common/rfb/VNCSConnectionST.cxx
+++ b/common/rfb/VNCSConnectionST.cxx
@@ -1,5 +1,5 @@
 /* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
- * Copyright 2009-2018 Pierre Ossman for Cendio AB
+ * Copyright 2009-2019 Pierre Ossman for Cendio AB
  * Copyright 2018 Peter Astrand for Cendio AB
  * 
  * This is free software; you can redistribute it and/or modify
@@ -282,19 +282,6 @@
   }
 }
 
-void VNCSConnectionST::serverCutTextOrClose(const char *str)
-{
-  try {
-    if (!accessCheck(AccessCutText)) return;
-    if (!rfb::Server::sendCutText) return;
-    if (state() == RFBSTATE_NORMAL)
-      writer()->writeServerCutText(str);
-  } catch(rdr::Exception& e) {
-    close(e.str());
-  }
-}
-
-
 void VNCSConnectionST::setDesktopNameOrClose(const char *name)
 {
   try {
@@ -305,7 +292,6 @@
   }
 }
 
-
 void VNCSConnectionST::setCursorOrClose()
 {
   try {
@@ -316,7 +302,6 @@
   }
 }
 
-
 void VNCSConnectionST::setLEDStateOrClose(unsigned int state)
 {
   try {
@@ -327,6 +312,41 @@
   }
 }
 
+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::announceClipboardOrClose(bool available)
+{
+  try {
+    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 @@
   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::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 @@
   writer()->writeLEDState();
 }
 
-
 bool VNCSConnectionST::handleTimeout(Timer* t)
 {
   try {
diff --git a/common/rfb/VNCSConnectionST.h b/common/rfb/VNCSConnectionST.h
index 54266e3..c8f4c24 100644
--- a/common/rfb/VNCSConnectionST.h
+++ b/common/rfb/VNCSConnectionST.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
- * Copyright 2009-2016 Pierre Ossman for Cendio AB
+ * Copyright 2009-2019 Pierre Ossman for Cendio AB
  * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -72,10 +72,12 @@
     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 @@
     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();
diff --git a/common/rfb/VNCServer.h b/common/rfb/VNCServer.h
index 5398e9f..5d04da5 100644
--- a/common/rfb/VNCServer.h
+++ b/common/rfb/VNCServer.h
@@ -1,4 +1,5 @@
 /* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * Copyright 2009-2019 Pierre Ossman for Cendio AB
  * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -55,9 +56,21 @@
     // 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;
diff --git a/common/rfb/VNCServerST.cxx b/common/rfb/VNCServerST.cxx
index 21af0da..a3655bc 100644
--- a/common/rfb/VNCServerST.cxx
+++ b/common/rfb/VNCServerST.cxx
@@ -1,5 +1,5 @@
 /* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
- * Copyright 2009-2018 Pierre Ossman for Cendio AB
+ * Copyright 2009-2019 Pierre Ossman for Cendio AB
  * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -77,8 +77,8 @@
 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 @@
     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,6 +334,45 @@
   }
 }
 
+void VNCServerST::requestClipboard()
+{
+  if (clipboardClient == NULL)
+    return;
+
+  clipboardClient->requestClipboard();
+}
+
+void VNCServerST::announceClipboard(bool available)
+{
+  std::list<VNCSConnectionST*>::iterator ci, ci_next;
+
+  if (available)
+    clipboardClient = NULL;
+
+  clipboardRequestors.clear();
+
+  for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
+    ci_next = ci; ci_next++;
+    (*ci)->announceClipboard(available);
+  }
+}
+
+void VNCServerST::sendClipboardData(const char* data)
+{
+  std::list<VNCSConnectionST*>::iterator ci, ci_next;
+
+  if (strchr(data, '\r') != NULL)
+    throw Exception("Invalid carriage return in clipboard data");
+
+  for (ci = clipboardRequestors.begin();
+       ci != clipboardRequestors.end(); ci = ci_next) {
+    ci_next = ci; ci_next++;
+    (*ci)->sendClipboardData(data);
+  }
+
+  clipboardRequestors.clear();
+}
+
 void VNCServerST::bell()
 {
   std::list<VNCSConnectionST*>::iterator ci, ci_next;
@@ -340,17 +382,6 @@
   }
 }
 
-void VNCServerST::serverCutText(const char* str)
-{
-  if (strchr(str, '\r') != NULL)
-    throw Exception("Invalid carriage return in clipboard data");
-  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);
-  }
-}
-
 void VNCServerST::setName(const char* name_)
 {
   name.replaceBuf(strDup(name_));
@@ -461,9 +492,32 @@
   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,
diff --git a/common/rfb/VNCServerST.h b/common/rfb/VNCServerST.h
index 5231977..fd20cc3 100644
--- a/common/rfb/VNCServerST.h
+++ b/common/rfb/VNCServerST.h
@@ -1,5 +1,5 @@
 /* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
- * Copyright 2009-2016 Pierre Ossman for Cendio AB
+ * Copyright 2009-2019 Pierre Ossman for Cendio AB
  * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -85,7 +85,10 @@
     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 @@
     // 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 @@
 
     std::list<VNCSConnectionST*> clients;
     VNCSConnectionST* pointerClient;
+    VNCSConnectionST* clipboardClient;
+    std::list<VNCSConnectionST*> clipboardRequestors;
     std::list<network::Socket*> closingSockets;
 
     ComparingUpdateTracker* comparer;