diff options
author | Constantin Kaplinsky <const@tightvnc.com> | 2006-05-25 05:04:46 +0000 |
---|---|---|
committer | Constantin Kaplinsky <const@tightvnc.com> | 2006-05-25 05:04:46 +0000 |
commit | b30ae7facbdf8273f34f5d67d3d2e9c81db75576 (patch) | |
tree | 5091b0a7b991672b19c17b86b263e5ff4f173a3e /unix/vncconfig/vncconfig.cxx | |
parent | a2adc8d4cfdf7336ce9192414c5e775224742a97 (diff) | |
download | tigervnc-b30ae7facbdf8273f34f5d67d3d2e9c81db75576.tar.gz tigervnc-b30ae7facbdf8273f34f5d67d3d2e9c81db75576.zip |
Migrating to new directory structure adopted from the RealVNC's source tree. More changes will follow.
git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@590 3789f03b-4d11-0410-bbf8-ca57d06f2519
Diffstat (limited to 'unix/vncconfig/vncconfig.cxx')
-rw-r--r-- | unix/vncconfig/vncconfig.cxx | 438 |
1 files changed, 438 insertions, 0 deletions
diff --git a/unix/vncconfig/vncconfig.cxx b/unix/vncconfig/vncconfig.cxx new file mode 100644 index 00000000..c901d193 --- /dev/null +++ b/unix/vncconfig/vncconfig.cxx @@ -0,0 +1,438 @@ +/* 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 + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +// +// VNC server configuration utility +// + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> +#include <errno.h> + +#include <signal.h> +#include <X11/X.h> +#include <X11/Xlib.h> +#include <X11/Xatom.h> +#include <X11/Xutil.h> +#include <X11/keysym.h> +#include "vncExt.h" +#include <rdr/Exception.h> +#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; + +LogWriter vlog("vncconfig"); + +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" + +char* programName = 0; +Display* dpy; +int vncExtEventBase, vncExtErrorBase; + +static bool getBoolParam(Display* dpy, const char* param) { + char* data; + int len; + if (XVncExtGetParam(dpy, param, &data, &len)) { + if (strcmp(data,"1") == 0) return true; + } + return false; +} + +class VncConfigWindow : public TXWindow, public TXEventHandler, + public TXDeleteWindowCallback, + public TXCheckboxCallback, + public rfb::Timer::Callback, + public QueryResultCallback { +public: + VncConfigWindow(Display* dpy) + : 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), + 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)); + y += acceptClipboard.height(); + sendClipboard.move(xPad, y); + sendClipboard.checked(getBoolParam(dpy, SEND_CUT_TEXT)); + y += sendClipboard.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| + 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 + // primary and clipboard selections to the clientCutText. If we get a + // SelectionChangeNotify event from Xvnc, set the serverCutText to the value + // of the new selection. + + virtual void handleEvent(TXWindow* w, XEvent* ev) { + if (acceptClipboard.checked()) { + if (ev->type == vncExtEventBase + VncExtClientCutTextNotify) { + XVncExtClientCutTextEvent* cutEv = (XVncExtClientCutTextEvent*)ev; + 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 && 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); + } + } + } + + + // selectionRequest() is called when we are the selection owner and another X + // client has requested the selection. We simply put the server's cut text + // into the requested property. TXWindow will handle the rest. + bool selectionRequest(Window requestor, Atom selection, Atom property) + { + if (cutText) + XChangeProperty(dpy, requestor, property, XA_STRING, 8, + PropModeReplace, (unsigned char*)cutText, + cutTextLen); + return cutText; + } + + // selectionNotify() is called when we have requested the selection from the + // selection owner. + void selectionNotify(XSelectionEvent* ev, Atom type, int format, + int nitems, void* data) + { + if (ev->requestor != win() || ev->target != XA_STRING) + return; + + if (data && format == 8) { + 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); + } + } + + // TXDeleteWindowCallback method + virtual void deleteWindow(TXWindow* w) { + exit(1); + } + + // TXCheckboxCallback method + virtual void checkboxSelect(TXCheckbox* checkbox) { + if (checkbox == &acceptClipboard) { + XVncExtSetParam(dpy, (acceptClipboard.checked() + ? ACCEPT_CUT_TEXT "=1" : ACCEPT_CUT_TEXT "=0")); + } else if (checkbox == &sendClipboard) { + XVncExtSetParam(dpy, (sendClipboard.checked() + ? SEND_CUT_TEXT "=1" : SEND_CUT_TEXT "=0")); + 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* 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 [parameters]\n", + programName); + fprintf(stderr," %s [parameters] -connect <host>[:<port>]\n", + programName); + fprintf(stderr," %s [parameters] -disconnect\n", programName); + fprintf(stderr," %s [parameters] [-set] <Xvnc-param>=<value> ...\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); +} + +void removeArgs(int* argc, char** argv, int first, int n) +{ + if (first + n > *argc) return; + for (int i = first + n; i < *argc; i++) + argv[i-n] = argv[i]; + *argc -= n; +} + +int main(int argc, char** argv) +{ + programName = argv[0]; + rfb::initStdIOLoggers(); + rfb::LogWriter::setLogParams("*:stderr:30"); + + // Process vncconfig's own parameters first, then we process the + // other arguments when we have the X display. + int i; + for (i = 1; i < argc; i++) { + if (Configuration::setParam(argv[i])) + continue; + + if (argv[i][0] == '-' && i+1 < argc && + Configuration::setParam(&argv[i][1], argv[i+1])) { + i++; + continue; + } + break; + } + + CharArray displaynameStr(displayname.getData()); + if (!(dpy = XOpenDisplay(displaynameStr.buf))) { + fprintf(stderr,"%s: unable to open display \"%s\"\n", + programName, XDisplayName(displaynameStr.buf)); + exit(1); + } + + if (!XVncExtQueryExtension(dpy, &vncExtEventBase, &vncExtErrorBase)) { + fprintf(stderr,"No VNC extension on display %s\n", + XDisplayName(displaynameStr.buf)); + exit(1); + } + + if (i < argc) { + for (; i < argc; i++) { + if (strcmp(argv[i], "-connect") == 0) { + i++; + if (i >= argc) usage(); + if (!XVncExtConnect(dpy, argv[i])) { + fprintf(stderr,"connecting to %s failed\n",argv[i]); + } + } else if (strcmp(argv[i], "-disconnect") == 0) { + if (!XVncExtConnect(dpy, "")) { + fprintf(stderr,"disconnecting all clients failed\n"); + } + } else if (strcmp(argv[i], "-get") == 0) { + i++; + if (i >= argc) usage(); + char* data; + int len; + if (XVncExtGetParam(dpy, argv[i], &data, &len)) { + printf("%.*s\n",len,data); + } else { + fprintf(stderr,"getting param %s failed\n",argv[i]); + } + XFree(data); + } else if (strcmp(argv[i], "-desc") == 0) { + i++; + if (i >= argc) usage(); + char* desc = XVncExtGetParamDesc(dpy, argv[i]); + if (desc) { + printf("%s\n",desc); + } else { + fprintf(stderr,"getting description for param %s failed\n",argv[i]); + } + XFree(desc); + } else if (strcmp(argv[i], "-list") == 0) { + int nParams; + char** list = XVncExtListParams(dpy, &nParams); + for (int i = 0; i < nParams; i++) { + printf("%s\n",list[i]); + } + XVncExtFreeParamList(list); + } else if (strcmp(argv[i], "-set") == 0) { + i++; + if (i >= argc) usage(); + if (!XVncExtSetParam(dpy, argv[i])) { + fprintf(stderr,"setting param %s failed\n",argv[i]); + } + } else if (XVncExtSetParam(dpy, argv[i])) { + fprintf(stderr,"set parameter %s\n",argv[i]); + } else { + usage(); + } + } + + return 0; + } + + try { + TXWindow::init(dpy,"Vncconfig"); + + VncConfigWindow w(dpy); + 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, tvp); + if (n < 0) throw rdr::SystemException("select",errno); + } + + XCloseDisplay(dpy); + + } catch (rdr::Exception &e) { + vlog.error(e.str()); + } + + return 0; +} |