]> source.dussan.org Git - tigervnc.git/commitdiff
Improved clipboard API
authorPierre Ossman <ossman@cendio.se>
Fri, 3 May 2019 08:53:06 +0000 (10:53 +0200)
committerPierre Ossman <ossman@cendio.se>
Mon, 1 Jul 2019 09:18:27 +0000 (11:18 +0200)
Change the internal clipboard API to use a request based model in
order to be prepared for more advanced clipboard transfers.

26 files changed:
common/rfb/CConnection.cxx
common/rfb/CConnection.h
common/rfb/SConnection.cxx
common/rfb/SConnection.h
common/rfb/SDesktop.h
common/rfb/VNCSConnectionST.cxx
common/rfb/VNCSConnectionST.h
common/rfb/VNCServer.h
common/rfb/VNCServerST.cxx
common/rfb/VNCServerST.h
unix/xserver/hw/vnc/XserverDesktop.cc
unix/xserver/hw/vnc/XserverDesktop.h
unix/xserver/hw/vnc/vncExtInit.cc
unix/xserver/hw/vnc/vncExtInit.h
unix/xserver/hw/vnc/vncSelection.c
unix/xserver/hw/vnc/vncSelection.h
vncviewer/CConn.cxx
vncviewer/CConn.h
vncviewer/DesktopWindow.cxx
vncviewer/DesktopWindow.h
vncviewer/Viewport.cxx
vncviewer/Viewport.h
win/rfb_win32/Clipboard.cxx
win/rfb_win32/Clipboard.h
win/rfb_win32/SDisplay.cxx
win/rfb_win32/SDisplay.h

index d6960dd291d29dea84bdb1e761b75b205f94f200..ce2741e418f62d382e587da67ff8eca55ed55c70 100644 (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;
index 66a71a2439f499915ff9af0c8cdf54f33b29b680..4106a1e6ea1d7a1af374ad7f3efdd8a88ec10cc9 100644 (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
index 4e224aa1ae60871a58970080e8d86d8085626703..1cc330d8f8a99519857202458202d925d0be3e29 100644 (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;
index 31d1cb2e1b66dfe4f696e339060f9f4e9243f835..6c80569db09122f4df1f5668e82103b559c34552 100644 (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
index 0060aa2300c7b33400233f2397f7a87866571607..b4104ea749e7e7621f4ef1f44d1853cdd13d8ec2 100644 (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() {}
   };
index 002ae92502cd29fa677337858890611ebfca53cd..cdd87b135db4ad46047102dc0866861961634b46 100644 (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 {
index 54266e3c7f58cc7e11bc74e3c22269180286b56e..c8f4c24f07a52508b60158b48ca58acabd1ffed4 100644 (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();
index 5398e9fd4248000cd00a64b545d1ca511ef9a012..5d04da53eb2a3fe34099ea5faa6861bdc4d4d0e8 100644 (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;
index 21af0dac360e964f2e024eba3615427309115340..a3655bca4b99bbd77627d19a8466d89613abef62 100644 (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,
index 5231977d262ab968282267e95d80dccfb71ebe64..fd20cc37b18f2df375f9ee1c5542cfc4c16f7e95 100644 (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;
index cd9b1f9b9463601f926acbfb6c0ad258ae2ab9eb..4edffec7fac19b5f3597fe5c1a8356f55efc26c1 100644 (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)
index c6c4eb10c27e894bc4b5eac0e35964977eeffbe6..6c67068933b0035f618074cecf21f5f97fec7aba 100644 (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);
index 10143f6f9491a004e481ab0c67030827518bdfac..6ab306b142dcf73b40eb701002359169031b73b6 100644 (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)
index bdcce83a7e4c94c4f7baa685e7dceed373cd82b4..1fb87c1954c47e99d253941b28585a412c2ea0d7 100644 (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);
 
index 3438ac8640679dc4ba53301fd81072313625616b..4fac50e28dbcf44cca24f56ba178036c904fcc4a 100644 (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;
+    }
+  }
+}
index e11d4a49fdc28b7eb0ee32698eb351de7d532504..ea52bf23ad8cde0e3e14d730969ce4a6b848ac8d 100644 (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
 }
index 2cc1fe4c514a2e84c52be79174d8bf6d8fb04a05..6ba3276bea499c11896323bde318aac8ccf24e1f 100644 (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 //////////////////////
 
index 38e09c2529469fb9980933037499150ed23e89e6..4d935c96c378d4be596a72999cefbc341463015f 100644 (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();
index 4429e77531f92ea74448bf355ab7bcc1f46322db..4860b9280e0783aae3c7fd40198c758110fed4c2 100644 (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;
index fe938d9b1c758b9afb54dc42ee5023dd73751d9b..6b03325cb8f6066c08e486b34c41f62c38ecee5c 100644 (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);
index 151ecb47b009319f0c9397edc46fa4f78bcd88e9..713d36486fdcba2ae9ef270b34308cc36a2dc8ed 100644 (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;
 }
 
 
index 8b9b469b6b981a632642d2be7da4bc86c21adfc8..1fb93c668b3cbbdd8855d7d97325e84ac22a8c2a 100644 (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;
index 4952695681fd12457a2e65f78fe5d9182516f1fd..306cfbadcb1241cca3ba5c17c573aabf97d726e6 100644 (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;
index c69e981febec07cc6387bdcfc3dceb334c88b5dd..1dead82e5767abc4227e637110f0744ba66e39b8 100644 (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);
 
index 2c91e3f4c5ef1c15616802503a289a399c148176..be33ff159a7d1f735d154340ca1cffb7bf8715fd 100644 (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);
 }
 
 
index 1773b785eddbcc9e246cd443ec1ba8f745bef2a0..8e38edb326766189e422efaa047d93beee70844b 100644 (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