blob: 165441fed576325db826faa4023f8023cf05340b [file] [log] [blame]
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
Constantin Kaplinsky2c019832008-05-30 11:02:04 +00002 * Copyright (C) 2004-2008 Constantin Kaplinsky. All Rights Reserved.
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// FIXME: Check cases when screen width/height is not a multiply of 32.
21// e.g. 800x600.
22
23#include <strings.h>
24#include <sys/types.h>
25#include <sys/stat.h>
26#include <unistd.h>
27#include <errno.h>
28#include <rfb/Logger_stdio.h>
29#include <rfb/LogWriter.h>
30#include <rfb/VNCServerST.h>
31#include <rfb/Configuration.h>
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000032#include <rfb/Timer.h>
33#include <network/TcpSocket.h>
34#include <tx/TXWindow.h>
35
Constantin Kaplinskya3b60c42006-06-02 04:07:49 +000036#include <vncconfig/QueryConnectDialog.h>
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000037
38#include <signal.h>
39#include <X11/X.h>
40#include <X11/Xlib.h>
41#include <X11/Xutil.h>
42#ifdef HAVE_XTEST
43#include <X11/extensions/XTest.h>
44#endif
Pierre Ossmana7b728a2014-06-13 10:56:59 +000045#ifdef HAVE_XDAMAGE
46#include <X11/extensions/Xdamage.h>
47#endif
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000048
49#include <x0vncserver/Geometry.h>
50#include <x0vncserver/Image.h>
Constantin Kaplinsky614c7b52007-12-26 18:17:09 +000051#include <x0vncserver/XPixelBuffer.h>
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000052#include <x0vncserver/PollingScheduler.h>
53
54// XXX Lynx/OS 2.3: protos for select(), bzero()
55#ifdef Lynx
56#include <sys/proto.h>
57#endif
58
Constantin Kaplinskyb9632702006-12-01 10:54:55 +000059extern char buildtime[];
60
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000061using namespace rfb;
62using namespace network;
63
64static LogWriter vlog("Main");
65
66IntParameter pollingCycle("PollingCycle", "Milliseconds per one polling "
67 "cycle; actual interval may be dynamically "
68 "adjusted to satisfy MaxProcessorUsage setting", 30);
69IntParameter maxProcessorUsage("MaxProcessorUsage", "Maximum percentage of "
70 "CPU time to be consumed", 35);
71BoolParameter useShm("UseSHM", "Use MIT-SHM extension if available", true);
72BoolParameter useOverlay("OverlayMode", "Use overlay mode under "
73 "IRIX or Solaris", true);
74StringParameter displayname("display", "The X display", "");
75IntParameter rfbport("rfbport", "TCP port to listen for RFB protocol",5900);
76IntParameter queryConnectTimeout("QueryConnectTimeout",
77 "Number of seconds to show the Accept Connection dialog before "
78 "rejecting the connection",
79 10);
80StringParameter hostsFile("HostsFile", "File with IP access control rules", "");
81
82//
83// Allow the main loop terminate itself gracefully on receiving a signal.
84//
85
86static bool caughtSignal = false;
87
88static void CleanupSignalHandler(int sig)
89{
90 caughtSignal = true;
91}
92
93
94class QueryConnHandler : public VNCServerST::QueryConnectionHandler,
95 public QueryResultCallback {
96public:
97 QueryConnHandler(Display* dpy, VNCServerST* vs)
98 : display(dpy), server(vs), queryConnectDialog(0), queryConnectSock(0) {}
99 ~QueryConnHandler() { delete queryConnectDialog; }
100
101 // -=- VNCServerST::QueryConnectionHandler interface
102 virtual VNCServerST::queryResult queryConnection(network::Socket* sock,
103 const char* userName,
104 char** reason) {
105 if (queryConnectSock) {
Adam Tkacd36b6262009-09-04 10:57:20 +0000106 *reason = strDup("Another connection is currently being queried.");
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000107 return VNCServerST::REJECT;
108 }
109 if (!userName) userName = "(anonymous)";
110 queryConnectSock = sock;
111 CharArray address(sock->getPeerAddress());
112 delete queryConnectDialog;
113 queryConnectDialog = new QueryConnectDialog(display, address.buf,
114 userName, queryConnectTimeout,
115 this);
116 queryConnectDialog->map();
117 return VNCServerST::PENDING;
118 }
119
120 // -=- QueryResultCallback interface
121 virtual void queryApproved() {
122 server->approveConnection(queryConnectSock, true, 0);
123 queryConnectSock = 0;
124 }
125 virtual void queryRejected() {
126 server->approveConnection(queryConnectSock, false,
127 "Connection rejected by local user");
128 queryConnectSock = 0;
129 }
130private:
131 Display* display;
132 VNCServerST* server;
133 QueryConnectDialog* queryConnectDialog;
134 network::Socket* queryConnectSock;
135};
136
137
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000138class XDesktop : public SDesktop, public ColourMap, public TXGlobalEventHandler
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000139{
140public:
141 XDesktop(Display* dpy_, Geometry *geometry_)
Constantin Kaplinsky303433a2008-06-04 05:57:06 +0000142 : dpy(dpy_), geometry(geometry_), pb(0), server(0),
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000143 oldButtonMask(0), haveXtest(false), haveDamage(false),
144 maxButtons(0), running(false)
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000145 {
146#ifdef HAVE_XTEST
147 int xtestEventBase;
148 int xtestErrorBase;
149 int major, minor;
150
151 if (XTestQueryExtension(dpy, &xtestEventBase,
152 &xtestErrorBase, &major, &minor)) {
153 XTestGrabControl(dpy, True);
154 vlog.info("XTest extension present - version %d.%d",major,minor);
155 haveXtest = true;
156 } else {
157#endif
158 vlog.info("XTest extension not present");
159 vlog.info("Unable to inject events or display while server is grabbed");
160#ifdef HAVE_XTEST
161 }
162#endif
163
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000164#ifdef HAVE_XDAMAGE
165 int xdamageErrorBase;
166
167 if (XDamageQueryExtension(dpy, &xdamageEventBase, &xdamageErrorBase)) {
168 TXWindow::setGlobalEventHandler(this);
169 haveDamage = true;
170 } else {
171#endif
172 vlog.info("DAMAGE extension not present");
173 vlog.info("Will have to poll screen for changes");
174#ifdef HAVE_XDAMAGE
175 }
176#endif
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000177 }
178 virtual ~XDesktop() {
179 stop();
180 }
181
Constantin Kaplinskydc873dc2008-06-04 09:46:57 +0000182 inline void poll() {
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000183 if (pb and not haveDamage)
Constantin Kaplinskydc873dc2008-06-04 09:46:57 +0000184 pb->poll(server);
185 }
186
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000187 // -=- SDesktop interface
188
189 virtual void start(VNCServer* vs) {
190
191 // Determine actual number of buttons of the X pointer device.
192 unsigned char btnMap[8];
193 int numButtons = XGetPointerMapping(dpy, btnMap, 8);
194 maxButtons = (numButtons > 8) ? 8 : numButtons;
195 vlog.info("Enabling %d button%s of X pointer device",
196 maxButtons, (maxButtons != 1) ? "s" : "");
197
Constantin Kaplinskye0c80c52008-06-04 03:10:05 +0000198 // Create an ImageFactory instance for producing Image objects.
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000199 ImageFactory factory((bool)useShm, (bool)useOverlay);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000200
Constantin Kaplinskydc873dc2008-06-04 09:46:57 +0000201 // Create pixel buffer and provide it to the server object.
Constantin Kaplinskyf773a8e2008-06-04 04:30:10 +0000202 pb = new XPixelBuffer(dpy, factory, geometry->getRect(), this);
Constantin Kaplinskye0c80c52008-06-04 03:10:05 +0000203 vlog.info("Allocated %s", pb->getImage()->classDesc());
204
Constantin Kaplinskyc341ac62008-08-21 03:35:08 +0000205 server = (VNCServerST *)vs;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000206 server->setPixelBuffer(pb);
207
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000208#ifdef HAVE_XDAMAGE
209 if (haveDamage) {
210 damage = XDamageCreate(dpy, DefaultRootWindow(dpy),
211 XDamageReportRawRectangles);
212 }
213#endif
214
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000215 running = true;
216 }
217
218 virtual void stop() {
219 running = false;
220
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000221#ifdef HAVE_XDAMAGE
222 if (haveDamage)
223 XDamageDestroy(dpy, damage);
224#endif
225
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000226 delete pb;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000227 pb = 0;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000228 }
229
230 inline bool isRunning() {
231 return running;
232 }
233
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000234 virtual void pointerEvent(const Point& pos, int buttonMask) {
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000235#ifdef HAVE_XTEST
236 if (!haveXtest) return;
237 XTestFakeMotionEvent(dpy, DefaultScreen(dpy),
238 geometry->offsetLeft() + pos.x,
239 geometry->offsetTop() + pos.y,
240 CurrentTime);
241 if (buttonMask != oldButtonMask) {
242 for (int i = 0; i < maxButtons; i++) {
243 if ((buttonMask ^ oldButtonMask) & (1<<i)) {
244 if (buttonMask & (1<<i)) {
245 XTestFakeButtonEvent(dpy, i+1, True, CurrentTime);
246 } else {
247 XTestFakeButtonEvent(dpy, i+1, False, CurrentTime);
248 }
249 }
250 }
251 }
252 oldButtonMask = buttonMask;
253#endif
254 }
255
256 virtual void keyEvent(rdr::U32 key, bool down) {
257#ifdef HAVE_XTEST
258 if (!haveXtest) return;
259 int keycode = XKeysymToKeycode(dpy, key);
260 if (keycode)
261 XTestFakeKeyEvent(dpy, keycode, down, CurrentTime);
262#endif
263 }
264
265 virtual void clientCutText(const char* str, int len) {
266 }
267
268 virtual Point getFbSize() {
269 return Point(pb->width(), pb->height());
270 }
271
Pierre Ossman20547672014-06-13 11:26:21 +0000272 // -=- ColourMap callbacks
273 virtual void lookup(int index, int* r, int* g, int* b) {
274 XColor xc;
275 xc.pixel = index;
276 if (index < DisplayCells(dpy,DefaultScreen(dpy))) {
277 XQueryColor(dpy, DefaultColormap(dpy,DefaultScreen(dpy)), &xc);
278 } else {
279 xc.red = xc.green = xc.blue = 0;
280 }
281 *r = xc.red;
282 *g = xc.green;
283 *b = xc.blue;
284 }
285
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000286 // -=- TXGlobalEventHandler interface
287
288 virtual bool handleGlobalEvent(XEvent* ev) {
289#ifdef HAVE_XDAMAGE
290 XDamageNotifyEvent* dev;
291 Rect rect;
292
293 if (ev->type != xdamageEventBase)
294 return false;
295
296 if (!running)
297 return true;
298
299 dev = (XDamageNotifyEvent*)ev;
300 rect.setXYWH(dev->area.x, dev->area.y, dev->area.width, dev->area.height);
301 server->add_changed(rect);
302
303 return true;
304#endif
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000305 }
306
307protected:
308 Display* dpy;
309 Geometry* geometry;
Constantin Kaplinsky2c019832008-05-30 11:02:04 +0000310 XPixelBuffer* pb;
Constantin Kaplinskyc341ac62008-08-21 03:35:08 +0000311 VNCServerST* server;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000312 int oldButtonMask;
313 bool haveXtest;
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000314 bool haveDamage;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000315 int maxButtons;
316 bool running;
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000317#ifdef HAVE_XDAMAGE
318 Damage damage;
319 int xdamageEventBase;
320#endif
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000321};
322
323
324class FileTcpFilter : public TcpFilter
325{
326
327public:
328
329 FileTcpFilter(const char *fname)
330 : TcpFilter("-"), fileName(NULL), lastModTime(0)
331 {
332 if (fname != NULL)
333 fileName = strdup((char *)fname);
334 }
335
336 virtual ~FileTcpFilter()
337 {
338 if (fileName != NULL)
339 free(fileName);
340 }
341
342 virtual bool verifyConnection(Socket* s)
343 {
344 if (!reloadRules()) {
345 vlog.error("Could not read IP filtering rules: rejecting all clients");
346 filter.clear();
347 filter.push_back(parsePattern("-"));
348 return false;
349 }
350
351 return TcpFilter::verifyConnection(s);
352 }
353
354protected:
355
356 bool reloadRules()
357 {
358 if (fileName == NULL)
359 return true;
360
361 struct stat st;
362 if (stat(fileName, &st) != 0)
363 return false;
364
365 if (st.st_mtime != lastModTime) {
366 // Actually reload only if the file was modified
367 FILE *fp = fopen(fileName, "r");
368 if (fp == NULL)
369 return false;
370
371 // Remove all the rules from the parent class
372 filter.clear();
373
374 // Parse the file contents adding rules to the parent class
375 char buf[32];
376 while (readLine(buf, 32, fp)) {
377 if (buf[0] && strchr("+-?", buf[0])) {
378 filter.push_back(parsePattern(buf));
379 }
380 }
381
382 fclose(fp);
383 lastModTime = st.st_mtime;
384 }
385 return true;
386 }
387
388protected:
389
390 char *fileName;
391 time_t lastModTime;
392
393private:
394
395 //
396 // NOTE: we silently truncate long lines in this function.
397 //
398
399 bool readLine(char *buf, int bufSize, FILE *fp)
400 {
401 if (fp == NULL || buf == NULL || bufSize == 0)
402 return false;
403
404 if (fgets(buf, bufSize, fp) == NULL)
405 return false;
406
407 char *ptr = strchr(buf, '\n');
408 if (ptr != NULL) {
409 *ptr = '\0'; // remove newline at the end
410 } else {
411 if (!feof(fp)) {
412 int c;
413 do { // skip the rest of a long line
414 c = getc(fp);
415 } while (c != '\n' && c != EOF);
416 }
417 }
418 return true;
419 }
420
421};
422
423char* programName;
424
Constantin Kaplinsky2039d7b2008-06-04 10:43:10 +0000425static void printVersion(FILE *fp)
426{
Peter Ã…strand4eacc022009-02-27 10:12:14 +0000427 fprintf(fp, "TigerVNC Server version %s, built %s\n",
Constantin Kaplinskyea7b6502008-09-28 05:08:48 +0000428 PACKAGE_VERSION, buildtime);
Constantin Kaplinsky2039d7b2008-06-04 10:43:10 +0000429}
430
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000431static void usage()
432{
Constantin Kaplinsky2039d7b2008-06-04 10:43:10 +0000433 printVersion(stderr);
434 fprintf(stderr, "\nUsage: %s [<parameters>]\n", programName);
435 fprintf(stderr, " %s --version\n", programName);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000436 fprintf(stderr,"\n"
437 "Parameters can be turned on with -<param> or off with -<param>=0\n"
438 "Parameters which take a value can be specified as "
439 "-<param> <value>\n"
440 "Other valid forms are <param>=<value> -<param>=<value> "
441 "--<param>=<value>\n"
442 "Parameter names are case-insensitive. The parameters are:\n\n");
443 Configuration::listParams(79, 14);
444 exit(1);
445}
446
447int main(int argc, char** argv)
448{
449 initStdIOLoggers();
450 LogWriter::setLogParams("*:stderr:30");
451
452 programName = argv[0];
453 Display* dpy;
454
Adam Tkacc58b3d12010-04-23 13:55:10 +0000455 Configuration::enableServerParams();
456
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000457 for (int i = 1; i < argc; i++) {
458 if (Configuration::setParam(argv[i]))
459 continue;
460
461 if (argv[i][0] == '-') {
462 if (i+1 < argc) {
463 if (Configuration::setParam(&argv[i][1], argv[i+1])) {
464 i++;
465 continue;
466 }
467 }
Constantin Kaplinsky2039d7b2008-06-04 10:43:10 +0000468 if (strcmp(argv[i], "-v") == 0 ||
469 strcmp(argv[i], "-version") == 0 ||
470 strcmp(argv[i], "--version") == 0) {
471 printVersion(stdout);
472 return 0;
473 }
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000474 usage();
475 }
476
477 usage();
478 }
479
480 CharArray dpyStr(displayname.getData());
481 if (!(dpy = XOpenDisplay(dpyStr.buf[0] ? dpyStr.buf : 0))) {
Constantin Kaplinsky7bdccd72008-08-20 06:22:28 +0000482 // FIXME: Why not vlog.error(...)?
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000483 fprintf(stderr,"%s: unable to open display \"%s\"\r\n",
Constantin Kaplinsky7bdccd72008-08-20 06:22:28 +0000484 programName, XDisplayName(dpyStr.buf));
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000485 exit(1);
486 }
487
488 signal(SIGHUP, CleanupSignalHandler);
489 signal(SIGINT, CleanupSignalHandler);
490 signal(SIGTERM, CleanupSignalHandler);
491
492 try {
493 TXWindow::init(dpy,"x0vncserver");
494 Geometry geo(DisplayWidth(dpy, DefaultScreen(dpy)),
495 DisplayHeight(dpy, DefaultScreen(dpy)));
Constantin Kaplinsky23c60222008-06-04 03:58:07 +0000496 if (geo.getRect().is_empty()) {
497 vlog.error("Exiting with error");
498 return 1;
499 }
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000500 XDesktop desktop(dpy, &geo);
Constantin Kaplinsky82328312008-04-24 08:44:24 +0000501
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000502 VNCServerST server("x0vncserver", &desktop);
503 QueryConnHandler qcHandler(dpy, &server);
504 server.setQueryConnectionHandler(&qcHandler);
505
Adam Tkac93ff5db2010-02-05 15:54:10 +0000506 TcpListener listener(NULL, (int)rfbport);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000507 vlog.info("Listening on port %d", (int)rfbport);
508
Constantin Kaplinsky7bdccd72008-08-20 06:22:28 +0000509 const char *hostsData = hostsFile.getData();
510 FileTcpFilter fileTcpFilter(hostsData);
511 if (strlen(hostsData) != 0)
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000512 listener.setFilter(&fileTcpFilter);
Constantin Kaplinsky7bdccd72008-08-20 06:22:28 +0000513 delete[] hostsData;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000514
515 PollingScheduler sched((int)pollingCycle, (int)maxProcessorUsage);
516
517 while (!caughtSignal) {
518 struct timeval tv;
519 fd_set rfds;
520 std::list<Socket*> sockets;
521 std::list<Socket*>::iterator i;
522
523 // Process any incoming X events
524 TXWindow::handleXEvents(dpy);
525
526 FD_ZERO(&rfds);
Pierre Ossmana7b728a2014-06-13 10:56:59 +0000527 FD_SET(ConnectionNumber(dpy), &rfds);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000528 FD_SET(listener.getFd(), &rfds);
529 server.getSockets(&sockets);
530 int clients_connected = 0;
531 for (i = sockets.begin(); i != sockets.end(); i++) {
532 if ((*i)->isShutdown()) {
533 server.removeSocket(*i);
534 delete (*i);
535 } else {
536 FD_SET((*i)->getFd(), &rfds);
537 clients_connected++;
538 }
539 }
540
Constantin Kaplinsky813dbb42006-12-05 08:03:18 +0000541 if (!clients_connected)
542 sched.reset();
543
544 if (sched.isRunning()) {
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000545 int wait_ms = sched.millisRemaining();
546 if (wait_ms > 500) {
547 wait_ms = 500;
548 }
549 tv.tv_usec = wait_ms * 1000;
550#ifdef DEBUG
551 // fprintf(stderr, "[%d]\t", wait_ms);
552#endif
553 } else {
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000554 tv.tv_usec = 100000;
555 }
556 tv.tv_sec = 0;
557
558 // Do the wait...
559 sched.sleepStarted();
560 int n = select(FD_SETSIZE, &rfds, 0, 0, &tv);
561 sched.sleepFinished();
562
563 if (n < 0) {
564 if (errno == EINTR) {
565 vlog.debug("Interrupted select() system call");
566 continue;
567 } else {
568 throw rdr::SystemException("select", errno);
569 }
570 }
571
572 // Accept new VNC connections
573 if (FD_ISSET(listener.getFd(), &rfds)) {
574 Socket* sock = listener.accept();
575 if (sock) {
576 server.addSocket(sock);
577 } else {
578 vlog.status("Client connection rejected");
579 }
580 }
581
582 Timer::checkTimeouts();
583 server.checkTimeouts();
584
585 // Client list could have been changed.
586 server.getSockets(&sockets);
587
588 // Nothing more to do if there are no client connections.
589 if (sockets.empty())
590 continue;
591
592 // Process events on existing VNC connections
593 for (i = sockets.begin(); i != sockets.end(); i++) {
594 if (FD_ISSET((*i)->getFd(), &rfds))
595 server.processSocketEvent(*i);
596 }
597
598 if (desktop.isRunning() && sched.goodTimeToPoll()) {
599 sched.newPass();
600 desktop.poll();
601 }
602 }
603
604 } catch (rdr::Exception &e) {
Pierre Ossmanad8609a2012-04-26 09:04:14 +0000605 vlog.error("%s", e.str());
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000606 return 1;
607 }
608
Constantin Kaplinsky0c4306c2008-09-05 07:13:55 +0000609 TXWindow::handleXEvents(dpy);
610
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000611 vlog.info("Terminated");
612 return 0;
613}