-/* Copyright (C) 2002-2003 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
#include <stdio.h>
#define NEED_REPLIES
+#include <X11/Xlib.h>
#include <X11/Xlibint.h>
#define _VNCEXT_PROTO_
#include "vncExt.h"
xEvent* w);
static Bool XVncExtSelectionChangeNotifyWireToEvent(Display* dpy, XEvent* e,
xEvent* w);
+static Bool XVncExtQueryConnectNotifyWireToEvent(Display* dpy, XEvent* e,
+ xEvent* w);
static Bool extensionInited = False;
static XExtCodes* codes = 0;
XVncExtClientCutTextNotifyWireToEvent);
XESetWireToEvent(dpy, codes->first_event + VncExtSelectionChangeNotify,
XVncExtSelectionChangeNotifyWireToEvent);
+ XESetWireToEvent(dpy, codes->first_event + VncExtQueryConnectNotify,
+ XVncExtQueryConnectNotifyWireToEvent);
}
return codes != 0;
}
return rep.success;
}
+Bool XVncExtGetQueryConnect(Display* dpy, char** addr, char** user,
+ int* timeout, void** opaqueId)
+{
+ xVncExtGetQueryConnectReq* req;
+ xVncExtGetQueryConnectReply rep;
+
+ if (!checkExtension(dpy)) return False;
+
+ LockDisplay(dpy);
+ GetReq(VncExtGetQueryConnect, req);
+ req->reqType = codes->major_opcode;
+ req->vncExtReqType = X_VncExtGetQueryConnect;
+ if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
+ UnlockDisplay(dpy);
+ SyncHandle();
+ return False;
+ }
+ UnlockDisplay(dpy);
+ SyncHandle();
+
+ *addr = Xmalloc(rep.addrLen+1);
+ _XReadPad(dpy, *addr, rep.addrLen);
+ (*addr)[rep.addrLen] = 0;
+ *user = Xmalloc(rep.userLen+1);
+ _XReadPad(dpy, *user, rep.userLen);
+ (*user)[rep.userLen] = 0;
+ *timeout = rep.timeout;
+ *opaqueId = (void*)rep.opaqueId;
+ return True;
+}
+
+Bool XVncExtApproveConnect(Display* dpy, void* opaqueId, int approve)
+{
+ xVncExtApproveConnectReq* req;
+
+ if (!checkExtension(dpy)) return False;
+
+ LockDisplay(dpy);
+ GetReq(VncExtApproveConnect, req);
+ req->reqType = codes->major_opcode;
+ req->vncExtReqType = X_VncExtApproveConnect;
+ req->approve = approve;
+ req->opaqueId = (CARD32)opaqueId;
+ UnlockDisplay(dpy);
+ SyncHandle();
+ return True;
+}
+
+
static Bool XVncExtClientCutTextNotifyWireToEvent(Display* dpy, XEvent* e,
xEvent* w)
{
ev->selection = wire->selection;
return True;
}
+
+static Bool XVncExtQueryConnectNotifyWireToEvent(Display* dpy, XEvent* e,
+ xEvent* w)
+{
+ XVncExtQueryConnectEvent* ev = (XVncExtQueryConnectEvent*)e;
+ xVncExtQueryConnectNotifyEvent* wire
+ = (xVncExtQueryConnectNotifyEvent*)w;
+ ev->type = wire->type & 0x7f;
+ ev->serial = _XSetLastRequestRead(dpy,(xGenericReply*)wire);
+ ev->send_event = (wire->type & 0x80) != 0;
+ ev->display = dpy;
+ ev->window = wire->window;
+ return True;
+}
-/* Copyright (C) 2002-2003 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
#define X_VncExtGetClientCutText 5
#define X_VncExtSelectInput 6
#define X_VncExtConnect 7
+#define X_VncExtGetQueryConnect 8
+#define X_VncExtApproveConnect 9
#define VncExtClientCutTextNotify 0
#define VncExtSelectionChangeNotify 1
+#define VncExtQueryConnectNotify 2
#define VncExtClientCutTextMask (1 << VncExtClientCutTextNotify)
#define VncExtSelectionChangeMask (1 << VncExtSelectionChangeNotify)
+#define VncExtQueryConnectMask (1 << VncExtQueryConnectNotify)
-#define VncExtNumberEvents 2
+#define VncExtNumberEvents 3
#define VncExtNumberErrors 0
#ifndef _VNCEXT_SERVER_
Bool XVncExtGetClientCutText(Display* dpy, char** str, int* len);
Bool XVncExtSelectInput(Display* dpy, Window w, int mask);
Bool XVncExtConnect(Display* dpy, char* hostAndPort);
+Bool XVncExtGetQueryConnect(Display* dpy, char** addr,
+ char** user, int* timeout, void** opaqueId);
+Bool XVncExtApproveConnect(Display* dpy, void* opaqueId, int approve);
+
typedef struct {
int type;
Atom selection;
} XVncExtSelectionChangeEvent;
+typedef struct {
+ int type;
+ unsigned long serial;
+ Bool send_event;
+ Display *display;
+ Window window;
+} XVncExtQueryConnectEvent;
+
#endif
#ifdef _VNCEXT_PROTO_
#define sz_xVncExtConnectReply 32
+typedef struct {
+ CARD8 reqType; /* always VncExtReqCode */
+ CARD8 vncExtReqType; /* always VncExtGetQueryConnect */
+ CARD16 length B16;
+} xVncExtGetQueryConnectReq;
+#define sz_xVncExtGetQueryConnectReq 4
+
+typedef struct {
+ BYTE type; /* X_Reply */
+ BYTE pad0;
+ CARD16 sequenceNumber B16;
+ CARD32 length B32;
+ CARD32 addrLen B32;
+ CARD32 userLen B32;
+ CARD32 timeout B32;
+ CARD32 opaqueId B32;
+ CARD32 pad4 B32;
+ CARD32 pad5 B32;
+} xVncExtGetQueryConnectReply;
+#define sz_xVncExtGetQueryConnectReply 32
+
+typedef struct {
+ CARD8 reqType; /* always VncExtReqCode */
+ CARD8 vncExtReqType; /* always VncExtApproveConnect */
+ CARD16 length B16;
+ CARD8 approve;
+ CARD8 pad0;
+ CARD16 pad1;
+ CARD32 opaqueId B32;
+} xVncExtApproveConnectReq;
+#define sz_xVncExtApproveConnectReq 12
+
+
+
typedef struct {
BYTE type; /* always eventBase + VncExtClientCutTextNotify */
BYTE pad0;
} xVncExtSelectionChangeNotifyEvent;
#define sz_xVncExtSelectionChangeNotifyEvent 32
+typedef struct {
+ BYTE type; /* always eventBase + VncExtQueryConnectNotify */
+ BYTE pad0;
+ CARD16 sequenceNumber B16;
+ CARD32 window B32;
+ CARD32 pad6 B32;
+ CARD32 pad1 B32;
+ CARD32 pad2 B32;
+ CARD32 pad3 B32;
+ CARD32 pad4 B32;
+ CARD32 pad5 B32;
+} xVncExtQueryConnectNotifyEvent;
+#define sz_xVncExtQueryConnectNotifyEvent 32
+
#endif
#ifdef __cplusplus
-/* 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
#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;
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"
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));
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
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);
+ }
+ }
}
// 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
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);
}
}
} 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);
}
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);
}