vncconfig_unix directory merged with VNC 4.1.1 code.


git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/branches/merge-with-vnc-4.1.1@527 3789f03b-4d11-0410-bbf8-ca57d06f2519
diff --git a/vncconfig_unix/vncconfig.cxx b/vncconfig_unix/vncconfig.cxx
index e707ffb..c901d19 100644
--- a/vncconfig_unix/vncconfig.cxx
+++ b/vncconfig_unix/vncconfig.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -38,8 +38,11 @@
 #include <rfb/Configuration.h>
 #include <rfb/Logger_stdio.h>
 #include <rfb/LogWriter.h>
+#include <rfb/Timer.h>
 #include "TXWindow.h"
 #include "TXCheckbox.h"
+#include "TXLabel.h"
+#include "QueryConnectDialog.h"
 
 using namespace rfb;
 
@@ -48,6 +51,16 @@
 StringParameter displayname("display", "The X display", "");
 BoolParameter noWindow("nowin", "Don't display a window", 0);
 BoolParameter iconic("iconic", "Start with window iconified", 0);
+BoolParameter sendPrimary("SendPrimary", "Send the PRIMARY as well as the "
+                          "CLIPBOARD selection", true);
+IntParameter pollTime("poll",
+                      "How often to poll for clipboard changes in ms", 0);
+
+inline const char* selectionName(Atom sel) {
+  if (sel == xaCLIPBOARD) return "CLIPBOARD";
+  if (sel == XA_PRIMARY) return "PRIMARY";
+  return "unknown";
+}
 
 #define ACCEPT_CUT_TEXT "AcceptCutText"
 #define SEND_CUT_TEXT "SendCutText"
@@ -67,14 +80,20 @@
 
 class VncConfigWindow : public TXWindow, public TXEventHandler,
                         public TXDeleteWindowCallback,
-                        public TXCheckboxCallback {
+                        public TXCheckboxCallback,
+                        public rfb::Timer::Callback,
+                        public QueryResultCallback {
 public:
   VncConfigWindow(Display* dpy)
-    : TXWindow(dpy, 300, 100), clientCutText(0), clientCutTextLen(0),
+    : TXWindow(dpy, 300, 100), cutText(0), cutTextLen(0),
       acceptClipboard(dpy, "Accept clipboard from viewers", this, false, this),
       sendClipboard(dpy, "Send clipboard to viewers", this, false, this),
-      sendPrimary(dpy, "Send primary selection to viewers", this, false, this)
+      sendPrimaryCB(dpy, "Send primary selection to viewers", this,false,this),
+      pollTimer(this),
+      queryConnectDialog(0)
   {
+    selection[0] = selection[1] = 0;
+    selectionLen[0] = selectionLen[1] = 0;
     int y = yPad;
     acceptClipboard.move(xPad, y);
     acceptClipboard.checked(getBoolParam(dpy, ACCEPT_CUT_TEXT));
@@ -82,14 +101,22 @@
     sendClipboard.move(xPad, y);
     sendClipboard.checked(getBoolParam(dpy, SEND_CUT_TEXT));
     y += sendClipboard.height();
-    sendPrimary.move(xPad, y);
-    sendPrimary.checked(true);
-    sendPrimary.disabled(!sendClipboard.checked());
-    y += sendPrimary.height();
+    sendPrimaryCB.move(xPad, y);
+    sendPrimaryCB.checked(sendPrimary);
+    sendPrimaryCB.disabled(!sendClipboard.checked());
+    y += sendPrimaryCB.height();
     setEventHandler(this);
     toplevel("VNC config", this, 0, 0, 0, iconic);
     XVncExtSelectInput(dpy, win(),
-                       VncExtClientCutTextMask|VncExtSelectionChangeMask);
+                       VncExtClientCutTextMask|
+                       VncExtSelectionChangeMask|
+                       VncExtQueryConnectMask);
+    XConvertSelection(dpy, XA_PRIMARY, XA_STRING,
+                      XA_PRIMARY, win(), CurrentTime);
+    XConvertSelection(dpy, xaCLIPBOARD, XA_STRING,
+                      xaCLIPBOARD, win(), CurrentTime);
+    if (pollTime != 0)
+      pollTimer.start(pollTime);
   }
 
   // handleEvent(). If we get a ClientCutTextNotify event from Xvnc, set the
@@ -101,28 +128,55 @@
     if (acceptClipboard.checked()) {
       if (ev->type == vncExtEventBase + VncExtClientCutTextNotify) {
         XVncExtClientCutTextEvent* cutEv = (XVncExtClientCutTextEvent*)ev;
-        if (clientCutText)
-          XFree(clientCutText);
-        clientCutText = 0;
-        if (XVncExtGetClientCutText(dpy, &clientCutText, &clientCutTextLen)) {
-          vlog.debug("Got client cut text");
-          XStoreBytes(dpy, clientCutText, clientCutTextLen);
+        if (cutText)
+          XFree(cutText);
+        cutText = 0;
+        if (XVncExtGetClientCutText(dpy, &cutText, &cutTextLen)) {
+          vlog.debug("Got client cut text: '%.*s%s'",
+                     cutTextLen<9?cutTextLen:8, cutText,
+                     cutTextLen<9?"":"...");
+          XStoreBytes(dpy, cutText, cutTextLen);
           ownSelection(XA_PRIMARY, cutEv->time);
           ownSelection(xaCLIPBOARD, cutEv->time);
+          delete [] selection[0];
+          delete [] selection[1];
+          selection[0] = selection[1] = 0;
+          selectionLen[0] = selectionLen[1] = 0;
         }
       }
     }
     if (sendClipboard.checked()) {
       if (ev->type == vncExtEventBase + VncExtSelectionChangeNotify) {
+        vlog.debug("selection change event");
         XVncExtSelectionChangeEvent* selEv = (XVncExtSelectionChangeEvent*)ev;
         if (selEv->selection == xaCLIPBOARD ||
-            (selEv->selection == XA_PRIMARY && sendPrimary.checked())) {
+            (selEv->selection == XA_PRIMARY && sendPrimaryCB.checked())) {
           if (!selectionOwner(selEv->selection))
             XConvertSelection(dpy, selEv->selection, XA_STRING,
                               selEv->selection, win(), CurrentTime);
         }
       }
     }
+    if (ev->type == vncExtEventBase + VncExtQueryConnectNotify) {
+       vlog.debug("query connection event");
+       if (queryConnectDialog)
+         delete queryConnectDialog;
+       queryConnectDialog = 0;
+       char* qcAddress;
+       char* qcUser;
+       int qcTimeout;
+       if (XVncExtGetQueryConnect(dpy, &qcAddress, &qcUser,
+                                  &qcTimeout, &queryConnectId)) {
+         if (qcTimeout)
+           queryConnectDialog = new QueryConnectDialog(dpy, qcAddress,
+                                                       qcUser, qcTimeout,
+                                                       this);
+         if (queryConnectDialog)
+           queryConnectDialog->map();
+         XFree(qcAddress);
+         XFree(qcUser);
+       }
+    }
   }
   
 
@@ -131,11 +185,11 @@
   // into the requested property.  TXWindow will handle the rest.
   bool selectionRequest(Window requestor, Atom selection, Atom property)
   {
-    if (clientCutText)
+    if (cutText)
       XChangeProperty(dpy, requestor, property, XA_STRING, 8,
-                      PropModeReplace, (unsigned char*)clientCutText,
-                      clientCutTextLen);
-    return clientCutText;
+                      PropModeReplace, (unsigned char*)cutText,
+                      cutTextLen);
+    return cutText;
   }
 
   // selectionNotify() is called when we have requested the selection from the
@@ -147,8 +201,26 @@
       return;
 
     if (data && format == 8) {
-      vlog.debug("setting selection as server cut text");
-      XVncExtSetServerCutText(dpy, (char*)data, nitems);
+      int i = (ev->selection == XA_PRIMARY ? 0 : 1);
+      if (selectionLen[i] == nitems && memcmp(selection[i], data, nitems) == 0)
+        return;
+      delete [] selection[i];
+      selection[i] = new char[nitems];
+      memcpy(selection[i], data, nitems);
+      selectionLen[i] = nitems;
+      if (cutTextLen == nitems && memcmp(cutText, data, nitems) == 0) {
+        vlog.debug("ignoring duplicate cut text");
+        return;
+      }
+      if (cutText)
+        XFree(cutText);
+      cutText = (char*)malloc(nitems); // assuming XFree() same as free()
+      memcpy(cutText, data, nitems);
+      cutTextLen = nitems;
+      vlog.debug("sending %s selection as server cut text: '%.*s%s'",
+                 selectionName(ev->selection),cutTextLen<9?cutTextLen:8,
+                 cutText, cutTextLen<9?"":"...");
+      XVncExtSetServerCutText(dpy, cutText, cutTextLen);
     }
   }
 
@@ -165,28 +237,61 @@
     } else if (checkbox == &sendClipboard) {
       XVncExtSetParam(dpy, (sendClipboard.checked()
                             ? SEND_CUT_TEXT "=1" : SEND_CUT_TEXT "=0"));
-      sendPrimary.disabled(!sendClipboard.checked());
+      sendPrimaryCB.disabled(!sendClipboard.checked());
     }
   }
 
+  // rfb::Timer::Callback interface
+  virtual bool handleTimeout(rfb::Timer* timer) {
+    if (sendPrimaryCB.checked() && !selectionOwner(XA_PRIMARY))
+      XConvertSelection(dpy, XA_PRIMARY, XA_STRING,
+                        XA_PRIMARY, win(), CurrentTime);
+    if (!selectionOwner(xaCLIPBOARD))
+      XConvertSelection(dpy, xaCLIPBOARD, XA_STRING,
+                        xaCLIPBOARD, win(), CurrentTime);
+    return true;
+  }
+
+  // QueryResultCallback interface
+  virtual void queryApproved() {
+    XVncExtApproveConnect(dpy, queryConnectId, 1);
+  }
+  virtual void queryRejected() {
+    XVncExtApproveConnect(dpy, queryConnectId, 0);
+  }
+
 private:
-  char* clientCutText;
-  int clientCutTextLen;
-  TXCheckbox acceptClipboard, sendClipboard, sendPrimary;
+  char* cutText;
+  int cutTextLen;
+  char* selection[2];
+  int selectionLen[2];
+  TXCheckbox acceptClipboard, sendClipboard, sendPrimaryCB;
+  rfb::Timer pollTimer;
+
+  QueryConnectDialog* queryConnectDialog;
+  void* queryConnectId;
 };
 
 static void usage()
 {
-  fprintf(stderr,"usage: %s [-display <display>] [-nowin] [-iconic]\n",
+  fprintf(stderr,"usage: %s [parameters]\n",
           programName);
-  fprintf(stderr,"       %s [-display <display>] -connect <host>[:<port>]\n",
+  fprintf(stderr,"       %s [parameters] -connect <host>[:<port>]\n",
           programName);
-  fprintf(stderr,"       %s [-display <display>] -disconnect\n", programName);
-  fprintf(stderr,"       %s [-display <display>] [-set] <param>=<value> ...\n",
+  fprintf(stderr,"       %s [parameters] -disconnect\n", programName);
+  fprintf(stderr,"       %s [parameters] [-set] <Xvnc-param>=<value> ...\n",
           programName);
-  fprintf(stderr,"       %s [-display <display>] -list\n", programName);
-  fprintf(stderr,"       %s [-display <display>] -get <param>\n", programName);
-  fprintf(stderr,"       %s [-display <display>] -desc <param>\n",programName);
+  fprintf(stderr,"       %s [parameters] -list\n", programName);
+  fprintf(stderr,"       %s [parameters] -get <param>\n", programName);
+  fprintf(stderr,"       %s [parameters] -desc <param>\n",programName);
+  fprintf(stderr,"\n"
+          "Parameters can be turned on with -<param> or off with -<param>=0\n"
+          "Parameters which take a value can be specified as "
+          "-<param> <value>\n"
+          "Other valid forms are <param>=<value> -<param>=<value> "
+          "--<param>=<value>\n"
+          "Parameter names are case-insensitive.  The parameters are:\n\n");
+  Configuration::listParams(79, 14);
   exit(1);
 }
 
@@ -295,11 +400,31 @@
     if (!noWindow) w.map();
 
     while (true) {
+      struct timeval tv;
+      struct timeval* tvp = 0;
+
+      // Process any incoming X events
       TXWindow::handleXEvents(dpy);
+      
+      // Process expired timers and get the time until the next one
+      int timeoutMs = Timer::checkTimeouts();
+      if (timeoutMs) {
+        tv.tv_sec = timeoutMs / 1000;
+        tv.tv_usec = (timeoutMs % 1000) * 1000;
+        tvp = &tv;
+      }
+      
+      // If there are X requests pending then poll, don't wait!
+      if (XPending(dpy)) {
+        tv.tv_usec = tv.tv_sec = 0;
+        tvp = &tv;
+      }
+      
+      // Wait for X events, VNC traffic, or the next timer expiry
       fd_set rfds;
       FD_ZERO(&rfds);
       FD_SET(ConnectionNumber(dpy), &rfds);
-      int n = select(FD_SETSIZE, &rfds, 0, 0, 0);
+      int n = select(FD_SETSIZE, &rfds, 0, 0, tvp);
       if (n < 0) throw rdr::SystemException("select",errno);
     }