blob: e707ffb9b9bda9f06486268a1017a5e9f00b1b73 [file] [log] [blame]
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +00001/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved.
2 *
3 * This is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This software is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this software; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
16 * USA.
17 */
18//
19// VNC server configuration utility
20//
21
22#include <string.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <sys/time.h>
26#include <sys/types.h>
27#include <unistd.h>
28#include <errno.h>
29
30#include <signal.h>
31#include <X11/X.h>
32#include <X11/Xlib.h>
33#include <X11/Xatom.h>
34#include <X11/Xutil.h>
35#include <X11/keysym.h>
36#include "vncExt.h"
37#include <rdr/Exception.h>
38#include <rfb/Configuration.h>
39#include <rfb/Logger_stdio.h>
40#include <rfb/LogWriter.h>
41#include "TXWindow.h"
42#include "TXCheckbox.h"
43
44using namespace rfb;
45
46LogWriter vlog("vncconfig");
47
48StringParameter displayname("display", "The X display", "");
49BoolParameter noWindow("nowin", "Don't display a window", 0);
50BoolParameter iconic("iconic", "Start with window iconified", 0);
51
52#define ACCEPT_CUT_TEXT "AcceptCutText"
53#define SEND_CUT_TEXT "SendCutText"
54
55char* programName = 0;
56Display* dpy;
57int vncExtEventBase, vncExtErrorBase;
58
59static bool getBoolParam(Display* dpy, const char* param) {
60 char* data;
61 int len;
62 if (XVncExtGetParam(dpy, param, &data, &len)) {
63 if (strcmp(data,"1") == 0) return true;
64 }
65 return false;
66}
67
68class VncConfigWindow : public TXWindow, public TXEventHandler,
69 public TXDeleteWindowCallback,
70 public TXCheckboxCallback {
71public:
72 VncConfigWindow(Display* dpy)
73 : TXWindow(dpy, 300, 100), clientCutText(0), clientCutTextLen(0),
74 acceptClipboard(dpy, "Accept clipboard from viewers", this, false, this),
75 sendClipboard(dpy, "Send clipboard to viewers", this, false, this),
76 sendPrimary(dpy, "Send primary selection to viewers", this, false, this)
77 {
78 int y = yPad;
79 acceptClipboard.move(xPad, y);
80 acceptClipboard.checked(getBoolParam(dpy, ACCEPT_CUT_TEXT));
81 y += acceptClipboard.height();
82 sendClipboard.move(xPad, y);
83 sendClipboard.checked(getBoolParam(dpy, SEND_CUT_TEXT));
84 y += sendClipboard.height();
85 sendPrimary.move(xPad, y);
86 sendPrimary.checked(true);
87 sendPrimary.disabled(!sendClipboard.checked());
88 y += sendPrimary.height();
89 setEventHandler(this);
90 toplevel("VNC config", this, 0, 0, 0, iconic);
91 XVncExtSelectInput(dpy, win(),
92 VncExtClientCutTextMask|VncExtSelectionChangeMask);
93 }
94
95 // handleEvent(). If we get a ClientCutTextNotify event from Xvnc, set the
96 // primary and clipboard selections to the clientCutText. If we get a
97 // SelectionChangeNotify event from Xvnc, set the serverCutText to the value
98 // of the new selection.
99
100 virtual void handleEvent(TXWindow* w, XEvent* ev) {
101 if (acceptClipboard.checked()) {
102 if (ev->type == vncExtEventBase + VncExtClientCutTextNotify) {
103 XVncExtClientCutTextEvent* cutEv = (XVncExtClientCutTextEvent*)ev;
104 if (clientCutText)
105 XFree(clientCutText);
106 clientCutText = 0;
107 if (XVncExtGetClientCutText(dpy, &clientCutText, &clientCutTextLen)) {
108 vlog.debug("Got client cut text");
109 XStoreBytes(dpy, clientCutText, clientCutTextLen);
110 ownSelection(XA_PRIMARY, cutEv->time);
111 ownSelection(xaCLIPBOARD, cutEv->time);
112 }
113 }
114 }
115 if (sendClipboard.checked()) {
116 if (ev->type == vncExtEventBase + VncExtSelectionChangeNotify) {
117 XVncExtSelectionChangeEvent* selEv = (XVncExtSelectionChangeEvent*)ev;
118 if (selEv->selection == xaCLIPBOARD ||
119 (selEv->selection == XA_PRIMARY && sendPrimary.checked())) {
120 if (!selectionOwner(selEv->selection))
121 XConvertSelection(dpy, selEv->selection, XA_STRING,
122 selEv->selection, win(), CurrentTime);
123 }
124 }
125 }
126 }
127
128
129 // selectionRequest() is called when we are the selection owner and another X
130 // client has requested the selection. We simply put the server's cut text
131 // into the requested property. TXWindow will handle the rest.
132 bool selectionRequest(Window requestor, Atom selection, Atom property)
133 {
134 if (clientCutText)
135 XChangeProperty(dpy, requestor, property, XA_STRING, 8,
136 PropModeReplace, (unsigned char*)clientCutText,
137 clientCutTextLen);
138 return clientCutText;
139 }
140
141 // selectionNotify() is called when we have requested the selection from the
142 // selection owner.
143 void selectionNotify(XSelectionEvent* ev, Atom type, int format,
144 int nitems, void* data)
145 {
146 if (ev->requestor != win() || ev->target != XA_STRING)
147 return;
148
149 if (data && format == 8) {
150 vlog.debug("setting selection as server cut text");
151 XVncExtSetServerCutText(dpy, (char*)data, nitems);
152 }
153 }
154
155 // TXDeleteWindowCallback method
156 virtual void deleteWindow(TXWindow* w) {
157 exit(1);
158 }
159
160 // TXCheckboxCallback method
161 virtual void checkboxSelect(TXCheckbox* checkbox) {
162 if (checkbox == &acceptClipboard) {
163 XVncExtSetParam(dpy, (acceptClipboard.checked()
164 ? ACCEPT_CUT_TEXT "=1" : ACCEPT_CUT_TEXT "=0"));
165 } else if (checkbox == &sendClipboard) {
166 XVncExtSetParam(dpy, (sendClipboard.checked()
167 ? SEND_CUT_TEXT "=1" : SEND_CUT_TEXT "=0"));
168 sendPrimary.disabled(!sendClipboard.checked());
169 }
170 }
171
172private:
173 char* clientCutText;
174 int clientCutTextLen;
175 TXCheckbox acceptClipboard, sendClipboard, sendPrimary;
176};
177
178static void usage()
179{
180 fprintf(stderr,"usage: %s [-display <display>] [-nowin] [-iconic]\n",
181 programName);
182 fprintf(stderr," %s [-display <display>] -connect <host>[:<port>]\n",
183 programName);
184 fprintf(stderr," %s [-display <display>] -disconnect\n", programName);
185 fprintf(stderr," %s [-display <display>] [-set] <param>=<value> ...\n",
186 programName);
187 fprintf(stderr," %s [-display <display>] -list\n", programName);
188 fprintf(stderr," %s [-display <display>] -get <param>\n", programName);
189 fprintf(stderr," %s [-display <display>] -desc <param>\n",programName);
190 exit(1);
191}
192
193void removeArgs(int* argc, char** argv, int first, int n)
194{
195 if (first + n > *argc) return;
196 for (int i = first + n; i < *argc; i++)
197 argv[i-n] = argv[i];
198 *argc -= n;
199}
200
201int main(int argc, char** argv)
202{
203 programName = argv[0];
204 rfb::initStdIOLoggers();
205 rfb::LogWriter::setLogParams("*:stderr:30");
206
207 // Process vncconfig's own parameters first, then we process the
208 // other arguments when we have the X display.
209 int i;
210 for (i = 1; i < argc; i++) {
211 if (Configuration::setParam(argv[i]))
212 continue;
213
214 if (argv[i][0] == '-' && i+1 < argc &&
215 Configuration::setParam(&argv[i][1], argv[i+1])) {
216 i++;
217 continue;
218 }
219 break;
220 }
221
222 CharArray displaynameStr(displayname.getData());
223 if (!(dpy = XOpenDisplay(displaynameStr.buf))) {
224 fprintf(stderr,"%s: unable to open display \"%s\"\n",
225 programName, XDisplayName(displaynameStr.buf));
226 exit(1);
227 }
228
229 if (!XVncExtQueryExtension(dpy, &vncExtEventBase, &vncExtErrorBase)) {
230 fprintf(stderr,"No VNC extension on display %s\n",
231 XDisplayName(displaynameStr.buf));
232 exit(1);
233 }
234
235 if (i < argc) {
236 for (; i < argc; i++) {
237 if (strcmp(argv[i], "-connect") == 0) {
238 i++;
239 if (i >= argc) usage();
240 if (!XVncExtConnect(dpy, argv[i])) {
241 fprintf(stderr,"connecting to %s failed\n",argv[i]);
242 }
243 } else if (strcmp(argv[i], "-disconnect") == 0) {
244 if (!XVncExtConnect(dpy, "")) {
245 fprintf(stderr,"disconnecting all clients failed\n");
246 }
247 } else if (strcmp(argv[i], "-get") == 0) {
248 i++;
249 if (i >= argc) usage();
250 char* data;
251 int len;
252 if (XVncExtGetParam(dpy, argv[i], &data, &len)) {
253 printf("%.*s\n",len,data);
254 } else {
255 fprintf(stderr,"getting param %s failed\n",argv[i]);
256 }
257 XFree(data);
258 } else if (strcmp(argv[i], "-desc") == 0) {
259 i++;
260 if (i >= argc) usage();
261 char* desc = XVncExtGetParamDesc(dpy, argv[i]);
262 if (desc) {
263 printf("%s\n",desc);
264 } else {
265 fprintf(stderr,"getting description for param %s failed\n",argv[i]);
266 }
267 XFree(desc);
268 } else if (strcmp(argv[i], "-list") == 0) {
269 int nParams;
270 char** list = XVncExtListParams(dpy, &nParams);
271 for (int i = 0; i < nParams; i++) {
272 printf("%s\n",list[i]);
273 }
274 XVncExtFreeParamList(list);
275 } else if (strcmp(argv[i], "-set") == 0) {
276 i++;
277 if (i >= argc) usage();
278 if (!XVncExtSetParam(dpy, argv[i])) {
279 fprintf(stderr,"setting param %s failed\n",argv[i]);
280 }
281 } else if (XVncExtSetParam(dpy, argv[i])) {
282 fprintf(stderr,"set parameter %s\n",argv[i]);
283 } else {
284 usage();
285 }
286 }
287
288 return 0;
289 }
290
291 try {
292 TXWindow::init(dpy,"Vncconfig");
293
294 VncConfigWindow w(dpy);
295 if (!noWindow) w.map();
296
297 while (true) {
298 TXWindow::handleXEvents(dpy);
299 fd_set rfds;
300 FD_ZERO(&rfds);
301 FD_SET(ConnectionNumber(dpy), &rfds);
302 int n = select(FD_SETSIZE, &rfds, 0, 0, 0);
303 if (n < 0) throw rdr::SystemException("select",errno);
304 }
305
306 XCloseDisplay(dpy);
307
308 } catch (rdr::Exception &e) {
309 vlog.error(e.str());
310 }
311
312 return 0;
313}