blob: 2bb299f02b4c31599cc796b3ee0bd7f8ebd780f0 [file] [log] [blame]
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
Pierre Ossman296630f2016-07-08 14:24:27 +02002 * Copyright 2012-2016 Pierre Ossman for Cendio AB
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +00003 *
4 * This is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This software is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this software; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17 * USA.
18 */
19//
20// VNC server configuration utility
21//
22
23#include <string.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <sys/time.h>
27#include <sys/types.h>
28#include <unistd.h>
29#include <errno.h>
30
31#include <signal.h>
32#include <X11/X.h>
33#include <X11/Xlib.h>
34#include <X11/Xatom.h>
35#include <X11/Xutil.h>
36#include <X11/keysym.h>
37#include "vncExt.h"
38#include <rdr/Exception.h>
39#include <rfb/Configuration.h>
40#include <rfb/Logger_stdio.h>
41#include <rfb/LogWriter.h>
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000042#include "TXWindow.h"
43#include "TXCheckbox.h"
44#include "TXLabel.h"
45#include "QueryConnectDialog.h"
46
47using namespace rfb;
48
49LogWriter vlog("vncconfig");
50
51StringParameter displayname("display", "The X display", "");
52BoolParameter noWindow("nowin", "Don't display a window", 0);
53BoolParameter iconic("iconic", "Start with window iconified", 0);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000054
55#define ACCEPT_CUT_TEXT "AcceptCutText"
56#define SEND_CUT_TEXT "SendCutText"
57
Pierre Ossman296630f2016-07-08 14:24:27 +020058#define SET_PRIMARY "SetPrimary"
59#define SEND_PRIMARY "SendPrimary"
60
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000061char* programName = 0;
62Display* dpy;
63int vncExtEventBase, vncExtErrorBase;
64
65static bool getBoolParam(Display* dpy, const char* param) {
66 char* data;
67 int len;
68 if (XVncExtGetParam(dpy, param, &data, &len)) {
69 if (strcmp(data,"1") == 0) return true;
70 }
71 return false;
72}
73
74class VncConfigWindow : public TXWindow, public TXEventHandler,
75 public TXDeleteWindowCallback,
76 public TXCheckboxCallback,
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000077 public QueryResultCallback {
78public:
79 VncConfigWindow(Display* dpy)
Pierre Ossmanbfd567a2016-01-12 18:57:37 +010080 : TXWindow(dpy, 300, 100),
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000081 acceptClipboard(dpy, "Accept clipboard from viewers", this, false, this),
Pierre Ossman296630f2016-07-08 14:24:27 +020082 setPrimaryCB(dpy, "Also set primary selection", this, false, this),
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000083 sendClipboard(dpy, "Send clipboard to viewers", this, false, this),
Pierre Ossman296630f2016-07-08 14:24:27 +020084 sendPrimaryCB(dpy, "Send primary selection to viewers", this,false,this),
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000085 queryConnectDialog(0)
86 {
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000087 int y = yPad;
88 acceptClipboard.move(xPad, y);
89 acceptClipboard.checked(getBoolParam(dpy, ACCEPT_CUT_TEXT));
90 y += acceptClipboard.height();
Pierre Ossman296630f2016-07-08 14:24:27 +020091 setPrimaryCB.move(xPad + 10, y);
92 setPrimaryCB.checked(getBoolParam(dpy, SET_PRIMARY));
93 setPrimaryCB.disabled(!acceptClipboard.checked());
94 y += setPrimaryCB.height();
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000095 sendClipboard.move(xPad, y);
96 sendClipboard.checked(getBoolParam(dpy, SEND_CUT_TEXT));
97 y += sendClipboard.height();
Pierre Ossman296630f2016-07-08 14:24:27 +020098 sendPrimaryCB.move(xPad + 10, y);
99 sendPrimaryCB.checked(getBoolParam(dpy, SEND_PRIMARY));
100 sendPrimaryCB.disabled(!sendClipboard.checked());
101 y += sendPrimaryCB.height();
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000102 setEventHandler(this);
103 toplevel("VNC config", this, 0, 0, 0, iconic);
Pierre Ossmanbfd567a2016-01-12 18:57:37 +0100104 XVncExtSelectInput(dpy, win(), VncExtQueryConnectMask);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000105 }
106
Pierre Ossmanbfd567a2016-01-12 18:57:37 +0100107 // handleEvent()
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000108
109 virtual void handleEvent(TXWindow* w, XEvent* ev) {
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000110 if (ev->type == vncExtEventBase + VncExtQueryConnectNotify) {
111 vlog.debug("query connection event");
112 if (queryConnectDialog)
113 delete queryConnectDialog;
114 queryConnectDialog = 0;
115 char* qcAddress;
116 char* qcUser;
117 int qcTimeout;
118 if (XVncExtGetQueryConnect(dpy, &qcAddress, &qcUser,
119 &qcTimeout, &queryConnectId)) {
120 if (qcTimeout)
121 queryConnectDialog = new QueryConnectDialog(dpy, qcAddress,
122 qcUser, qcTimeout,
123 this);
124 if (queryConnectDialog)
125 queryConnectDialog->map();
126 XFree(qcAddress);
127 XFree(qcUser);
128 }
129 }
130 }
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000131
132 // TXDeleteWindowCallback method
133 virtual void deleteWindow(TXWindow* w) {
134 exit(1);
135 }
136
137 // TXCheckboxCallback method
138 virtual void checkboxSelect(TXCheckbox* checkbox) {
139 if (checkbox == &acceptClipboard) {
140 XVncExtSetParam(dpy, (acceptClipboard.checked()
141 ? ACCEPT_CUT_TEXT "=1" : ACCEPT_CUT_TEXT "=0"));
Pierre Ossman296630f2016-07-08 14:24:27 +0200142 setPrimaryCB.disabled(!acceptClipboard.checked());
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000143 } else if (checkbox == &sendClipboard) {
144 XVncExtSetParam(dpy, (sendClipboard.checked()
145 ? SEND_CUT_TEXT "=1" : SEND_CUT_TEXT "=0"));
Pierre Ossman296630f2016-07-08 14:24:27 +0200146 sendPrimaryCB.disabled(!sendClipboard.checked());
147 } else if (checkbox == &setPrimaryCB) {
148 XVncExtSetParam(dpy, (setPrimaryCB.checked()
149 ? SET_PRIMARY "=1" : SET_PRIMARY "=0"));
150 } else if (checkbox == &sendPrimaryCB) {
151 XVncExtSetParam(dpy, (sendPrimaryCB.checked()
152 ? SEND_PRIMARY "=1" : SEND_PRIMARY "=0"));
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000153 }
154 }
155
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000156 // QueryResultCallback interface
157 virtual void queryApproved() {
158 XVncExtApproveConnect(dpy, queryConnectId, 1);
159 }
160 virtual void queryRejected() {
161 XVncExtApproveConnect(dpy, queryConnectId, 0);
162 }
163
164private:
Pierre Ossman296630f2016-07-08 14:24:27 +0200165 TXCheckbox acceptClipboard, setPrimaryCB;
166 TXCheckbox sendClipboard, sendPrimaryCB;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000167
168 QueryConnectDialog* queryConnectDialog;
169 void* queryConnectId;
170};
171
172static void usage()
173{
174 fprintf(stderr,"usage: %s [parameters]\n",
175 programName);
176 fprintf(stderr," %s [parameters] -connect <host>[:<port>]\n",
177 programName);
178 fprintf(stderr," %s [parameters] -disconnect\n", programName);
179 fprintf(stderr," %s [parameters] [-set] <Xvnc-param>=<value> ...\n",
180 programName);
181 fprintf(stderr," %s [parameters] -list\n", programName);
182 fprintf(stderr," %s [parameters] -get <param>\n", programName);
183 fprintf(stderr," %s [parameters] -desc <param>\n",programName);
184 fprintf(stderr,"\n"
185 "Parameters can be turned on with -<param> or off with -<param>=0\n"
186 "Parameters which take a value can be specified as "
187 "-<param> <value>\n"
188 "Other valid forms are <param>=<value> -<param>=<value> "
189 "--<param>=<value>\n"
190 "Parameter names are case-insensitive. The parameters are:\n\n");
191 Configuration::listParams(79, 14);
192 exit(1);
193}
194
195void removeArgs(int* argc, char** argv, int first, int n)
196{
197 if (first + n > *argc) return;
198 for (int i = first + n; i < *argc; i++)
199 argv[i-n] = argv[i];
200 *argc -= n;
201}
202
203int main(int argc, char** argv)
204{
205 programName = argv[0];
206 rfb::initStdIOLoggers();
207 rfb::LogWriter::setLogParams("*:stderr:30");
208
209 // Process vncconfig's own parameters first, then we process the
210 // other arguments when we have the X display.
211 int i;
212 for (i = 1; i < argc; i++) {
213 if (Configuration::setParam(argv[i]))
214 continue;
215
216 if (argv[i][0] == '-' && i+1 < argc &&
217 Configuration::setParam(&argv[i][1], argv[i+1])) {
218 i++;
219 continue;
220 }
221 break;
222 }
223
224 CharArray displaynameStr(displayname.getData());
225 if (!(dpy = XOpenDisplay(displaynameStr.buf))) {
226 fprintf(stderr,"%s: unable to open display \"%s\"\n",
227 programName, XDisplayName(displaynameStr.buf));
228 exit(1);
229 }
230
231 if (!XVncExtQueryExtension(dpy, &vncExtEventBase, &vncExtErrorBase)) {
232 fprintf(stderr,"No VNC extension on display %s\n",
233 XDisplayName(displaynameStr.buf));
234 exit(1);
235 }
236
237 if (i < argc) {
238 for (; i < argc; i++) {
239 if (strcmp(argv[i], "-connect") == 0) {
240 i++;
241 if (i >= argc) usage();
242 if (!XVncExtConnect(dpy, argv[i])) {
243 fprintf(stderr,"connecting to %s failed\n",argv[i]);
244 }
245 } else if (strcmp(argv[i], "-disconnect") == 0) {
246 if (!XVncExtConnect(dpy, "")) {
247 fprintf(stderr,"disconnecting all clients failed\n");
248 }
249 } else if (strcmp(argv[i], "-get") == 0) {
250 i++;
251 if (i >= argc) usage();
252 char* data;
253 int len;
254 if (XVncExtGetParam(dpy, argv[i], &data, &len)) {
255 printf("%.*s\n",len,data);
256 } else {
257 fprintf(stderr,"getting param %s failed\n",argv[i]);
258 }
259 XFree(data);
260 } else if (strcmp(argv[i], "-desc") == 0) {
261 i++;
262 if (i >= argc) usage();
263 char* desc = XVncExtGetParamDesc(dpy, argv[i]);
264 if (desc) {
265 printf("%s\n",desc);
266 } else {
267 fprintf(stderr,"getting description for param %s failed\n",argv[i]);
268 }
269 XFree(desc);
270 } else if (strcmp(argv[i], "-list") == 0) {
271 int nParams;
272 char** list = XVncExtListParams(dpy, &nParams);
273 for (int i = 0; i < nParams; i++) {
274 printf("%s\n",list[i]);
275 }
276 XVncExtFreeParamList(list);
277 } else if (strcmp(argv[i], "-set") == 0) {
278 i++;
279 if (i >= argc) usage();
280 if (!XVncExtSetParam(dpy, argv[i])) {
281 fprintf(stderr,"setting param %s failed\n",argv[i]);
282 }
283 } else if (XVncExtSetParam(dpy, argv[i])) {
284 fprintf(stderr,"set parameter %s\n",argv[i]);
285 } else {
286 usage();
287 }
288 }
289
290 return 0;
291 }
292
293 try {
294 TXWindow::init(dpy,"Vncconfig");
295
296 VncConfigWindow w(dpy);
297 if (!noWindow) w.map();
298
299 while (true) {
300 struct timeval tv;
301 struct timeval* tvp = 0;
302
303 // Process any incoming X events
304 TXWindow::handleXEvents(dpy);
305
306 // Process expired timers and get the time until the next one
307 int timeoutMs = Timer::checkTimeouts();
308 if (timeoutMs) {
309 tv.tv_sec = timeoutMs / 1000;
310 tv.tv_usec = (timeoutMs % 1000) * 1000;
311 tvp = &tv;
312 }
313
314 // If there are X requests pending then poll, don't wait!
315 if (XPending(dpy)) {
316 tv.tv_usec = tv.tv_sec = 0;
317 tvp = &tv;
318 }
319
320 // Wait for X events, VNC traffic, or the next timer expiry
321 fd_set rfds;
322 FD_ZERO(&rfds);
323 FD_SET(ConnectionNumber(dpy), &rfds);
324 int n = select(FD_SETSIZE, &rfds, 0, 0, tvp);
325 if (n < 0) throw rdr::SystemException("select",errno);
326 }
327
328 XCloseDisplay(dpy);
329
330 } catch (rdr::Exception &e) {
Pierre Ossmanad8609a2012-04-26 09:04:14 +0000331 vlog.error("%s", e.str());
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000332 }
333
334 return 0;
335}