Avoid clipboard updates when not focused

We don't want to surprise the user with unexpected clipboard changes
when vncviewer is in the background, and it is both wasteful and
possibly insecure to inform the server of every clipboard update
when the user isn't interacting with it.
diff --git a/vncviewer/Viewport.cxx b/vncviewer/Viewport.cxx
index 3b56fce..6c10f35 100644
--- a/vncviewer/Viewport.cxx
+++ b/vncviewer/Viewport.cxx
@@ -118,6 +118,7 @@
 #ifdef WIN32
     altGrArmed(false),
 #endif
+    pendingServerCutText(NULL), pendingClientCutText(NULL),
     menuCtrlKey(false), menuAltKey(false), cursor(NULL)
 {
 #if !defined(WIN32) && !defined(__APPLE__)
@@ -206,6 +207,8 @@
     delete cursor;
   }
 
+  clearPendingClipboard();
+
   // FLTK automatically deletes all child widgets, so we shouldn't touch
   // them ourselves here
 }
@@ -233,6 +236,8 @@
   char *buffer;
   int size, ret;
 
+  clearPendingClipboard();
+
   if (!acceptClipboard)
     return;
 
@@ -249,6 +254,11 @@
 
   vlog.debug("Got clipboard data (%d bytes)", (int)strlen(buffer));
 
+  if (!hasFocus()) {
+    pendingServerCutText = buffer;
+    return;
+  }
+
   // RFB doesn't have separate selection and clipboard concepts, so we
   // dump the data into both variants.
   if (setPrimary)
@@ -534,11 +544,18 @@
   case FL_PASTE:
     buffer = new char[Fl::event_length() + 1];
 
+    clearPendingClipboard();
+
     // This is documented as to ASCII, but actually does to 8859-1
     ret = fl_utf8toa(Fl::event_text(), Fl::event_length(), buffer,
                      Fl::event_length() + 1);
     assert(ret < (Fl::event_length() + 1));
 
+    if (!hasFocus()) {
+      pendingClientCutText = buffer;
+      return 1;
+    }
+
     vlog.debug("Sending clipboard data (%d bytes)", (int)strlen(buffer));
 
     try {
@@ -598,6 +615,8 @@
     Fl::disable_im();
 
     try {
+      flushPendingClipboard();
+
       // We may have gotten our lock keys out of sync with the server
       // whilst we didn't have focus. Try to sort this out.
       pushLEDState();
@@ -704,6 +723,33 @@
 }
 
 
+void Viewport::clearPendingClipboard()
+{
+  delete [] pendingServerCutText;
+  pendingServerCutText = NULL;
+  delete [] pendingClientCutText;
+  pendingClientCutText = NULL;
+}
+
+
+void Viewport::flushPendingClipboard()
+{
+  if (pendingServerCutText) {
+    size_t len = strlen(pendingServerCutText);
+    if (setPrimary)
+      Fl::copy(pendingServerCutText, len, 0);
+    Fl::copy(pendingServerCutText, len, 1);
+  }
+  if (pendingClientCutText) {
+    size_t len = strlen(pendingClientCutText);
+    vlog.debug("Sending pending clipboard data (%d bytes)", (int)len);
+    cc->writer()->clientCutText(pendingClientCutText, len);
+  }
+
+  clearPendingClipboard();
+}
+
+
 void Viewport::handlePointerEvent(const rfb::Point& pos, int buttonMask)
 {
   if (!viewOnly) {
diff --git a/vncviewer/Viewport.h b/vncviewer/Viewport.h
index 170be0d..7bfc418 100644
--- a/vncviewer/Viewport.h
+++ b/vncviewer/Viewport.h
@@ -70,6 +70,9 @@
 
   static void handleClipboardChange(int source, void *data);
 
+  void clearPendingClipboard();
+  void flushPendingClipboard();
+
   void handlePointerEvent(const rfb::Point& pos, int buttonMask);
   static void handlePointerTimeout(void *data);
 
@@ -107,6 +110,9 @@
   unsigned int altGrCtrlTime;
 #endif
 
+  const char* pendingServerCutText;
+  const char* pendingClientCutText;
+
   rdr::U32 menuKeySym;
   int menuKeyCode, menuKeyFLTK;
   Fl_Menu_Button *contextMenu;