Home | History | Annotate | Download | only in x11vnc
      1 /*
      2    Copyright (C) 2002-2010 Karl J. Runge <runge (at) karlrunge.com>
      3    All rights reserved.
      4 
      5 This file is part of x11vnc.
      6 
      7 x11vnc is free software; you can redistribute it and/or modify
      8 it under the terms of the GNU General Public License as published by
      9 the Free Software Foundation; either version 2 of the License, or (at
     10 your option) any later version.
     11 
     12 x11vnc is distributed in the hope that it will be useful,
     13 but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with x11vnc; if not, write to the Free Software
     19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
     20 or see <http://www.gnu.org/licenses/>.
     21 
     22 In addition, as a special exception, Karl J. Runge
     23 gives permission to link the code of its release of x11vnc with the
     24 OpenSSL project's "OpenSSL" library (or with modified versions of it
     25 that use the same license as the "OpenSSL" library), and distribute
     26 the linked executables.  You must obey the GNU General Public License
     27 in all respects for all of the code used other than "OpenSSL".  If you
     28 modify this file, you may extend this exception to your version of the
     29 file, but you are not obligated to do so.  If you do not wish to do
     30 so, delete this exception statement from your version.
     31 */
     32 
     33 /* -- xevents.c -- */
     34 
     35 #include "x11vnc.h"
     36 #include "xwrappers.h"
     37 #include "xkb_bell.h"
     38 #include "xrandr.h"
     39 #include "xdamage.h"
     40 #include "xrecord.h"
     41 #include "selection.h"
     42 #include "keyboard.h"
     43 #include "cursor.h"
     44 #include "gui.h"
     45 #include "connections.h"
     46 #include "unixpw.h"
     47 #include "cleanup.h"
     48 #include "macosx.h"
     49 #include "screen.h"
     50 #include "pm.h"
     51 #include "pointer.h"
     52 #include "remote.h"
     53 #include "inet.h"
     54 
     55 /* XXX CHECK BEFORE RELEASE */
     56 int grab_buster = 0;
     57 int grab_kbd = 0;
     58 int grab_ptr = 0;
     59 int grab_always = 0;
     60 int ungrab_both = 0;
     61 int grab_local = 0;
     62 int sync_tod_delay = 20;
     63 
     64 void initialize_vnc_connect_prop(void);
     65 void initialize_x11vnc_remote_prop(void);
     66 void initialize_clipboard_atom(void);
     67 void spawn_grab_buster(void);
     68 void sync_tod_with_servertime(void);
     69 void check_keycode_state(void);
     70 void check_autorepeat(void);
     71 void set_prop_atom(Atom atom);
     72 void check_xevents(int reset);
     73 void xcut_receive(char *text, int len, rfbClientPtr cl);
     74 
     75 void kbd_release_all_keys(rfbClientPtr cl);
     76 void set_single_window(rfbClientPtr cl, int x, int y);
     77 void set_server_input(rfbClientPtr cl, int s);
     78 void set_text_chat(rfbClientPtr cl, int l, char *t);
     79 int get_keyboard_led_state_hook(rfbScreenInfoPtr s);
     80 int get_file_transfer_permitted(rfbClientPtr cl);
     81 void get_prop(char *str, int len, Atom prop, Window w);
     82 int guess_dm_gone(int t1, int t2);
     83 
     84 static void initialize_xevents(int reset);
     85 static void print_xevent_bases(void);
     86 static void bust_grab(int reset);
     87 static int process_watch(char *str, int parent, int db);
     88 static void grab_buster_watch(int parent, char *dstr);
     89 
     90 
     91 void initialize_vnc_connect_prop(void) {
     92 	char *prop_str;
     93 	vnc_connect_str[0] = '\0';
     94 	RAWFB_RET_VOID
     95 #if !NO_X11
     96 	prop_str = getenv("VNC_CONNECT");
     97 	if (prop_str == NULL) {
     98 		prop_str = "VNC_CONNECT";
     99 	}
    100 	vnc_connect_prop = XInternAtom(dpy, "VNC_CONNECT", False);
    101 #endif
    102 }
    103 
    104 void initialize_x11vnc_remote_prop(void) {
    105 	char *prop_str;
    106 	x11vnc_remote_str[0] = '\0';
    107 	RAWFB_RET_VOID
    108 #if !NO_X11
    109 	prop_str = getenv("X11VNC_REMOTE");
    110 	if (prop_str == NULL) {
    111 		prop_str = "X11VNC_REMOTE";
    112 	}
    113 	x11vnc_remote_prop = XInternAtom(dpy, prop_str, False);
    114 #endif
    115 }
    116 
    117 void initialize_clipboard_atom(void) {
    118 	RAWFB_RET_VOID
    119 #if NO_X11
    120 	return;
    121 #else
    122 	clipboard_atom = XInternAtom(dpy, "CLIPBOARD", False);
    123 	if (clipboard_atom == None) {
    124 		if (! quiet) rfbLog("could not find atom CLIPBOARD\n");
    125 		if (watch_clipboard) {
    126 			watch_clipboard = 0;
    127 		}
    128 		if (set_clipboard) {
    129 			set_clipboard = 0;
    130 		}
    131 	}
    132 #endif	/* NO_X11 */
    133 }
    134 
    135 /*
    136       we observed these strings:
    137 
    138       6 gdm_string: Gnome-power-manager
    139       6 gdm_string: Gnome-session
    140       6 gdm_string: Gnome-settings-daemon
    141       6 gdm_string: Login Window
    142       6 gdm_string: Notify-osd
    143       6 gdm_string: Panel
    144      12 gdm_string: Metacity
    145      12 gdm_string: gnome-power-manager
    146      12 gdm_string: gnome-session
    147      12 gdm_string: gnome-settings-daemon
    148      12 gdm_string: notify-osd
    149      18 gdm_string: Gdm-simple-greeter
    150      24 gdm_string: metacity
    151      36 gdm_string: gdm-simple-greeter
    152 
    153 	kdmgreet
    154 	Kdmgreet
    155  */
    156 
    157 static int dm_string(char *str) {
    158 	char *s = getenv("DEBUG_WM_RUNNING");
    159 	if (str == NULL) {
    160 		return 0;
    161 	}
    162 	if (str[0] == '\0') {
    163 		return 0;
    164 	}
    165 	if (0) fprintf(stderr, "dm_string: %s\n", str);
    166 	if (strstr(str, "gdm-") == str || strstr(str, "Gdm-") == str) {
    167 		if (strstr(str, "-greeter") != NULL) {
    168 			if (s) rfbLog("dm_string: %s\n", str);
    169 			return 1;
    170 		}
    171 	}
    172 	if (!strcmp(str, "kdmgreet") || !strcmp(str, "Kdmgreet")) {
    173 		if (s) rfbLog("dm_string: %s\n", str);
    174 		return 1;
    175 	}
    176 	return 0;
    177 }
    178 
    179 static int dm_still_running(void) {
    180 #if NO_X11
    181 	return 0;
    182 #else
    183 	Window r, parent;
    184 	Window *winlist;
    185 	unsigned int nc;
    186 	int rc, i;
    187 	static XClassHint *classhint = NULL;
    188 	XErrorHandler old_handler;
    189 	int saw_gdm_name = 0;
    190 
    191 	/* some times a window can go away before we get to it */
    192 	trapped_xerror = 0;
    193 	old_handler = XSetErrorHandler(trap_xerror);
    194 
    195 	if (! classhint) {
    196 		classhint = XAllocClassHint();
    197 	}
    198 
    199 	/* we are xlocked. */
    200 	rc = XQueryTree_wr(dpy, DefaultRootWindow(dpy), &r, &parent, &winlist, &nc);
    201 	if (!rc || winlist == NULL || nc == 0) {
    202 		nc = 0;
    203 	}
    204 	for (i=0; i < (int) nc; i++) {
    205 		char *name = NULL;
    206 		Window w = winlist[i];
    207 		if (XFetchName(dpy, w, &name) && name != NULL) {
    208 			saw_gdm_name += dm_string(name);
    209 			XFree_wr(name);
    210 		}
    211 		classhint->res_name = NULL;
    212 		classhint->res_class = NULL;
    213 		if (XGetClassHint(dpy, w, classhint)) {
    214 			name = classhint->res_name;
    215 			if (name != NULL) {
    216 				saw_gdm_name += dm_string(name);
    217 				XFree_wr(name);
    218 			}
    219 			name = classhint->res_class;
    220 			if (name != NULL) {
    221 				saw_gdm_name += dm_string(name);
    222 				XFree_wr(name);
    223 			}
    224 		}
    225 		if (saw_gdm_name > 0) {
    226 			break;
    227 		}
    228 	}
    229 	if (winlist != NULL) {
    230 		XFree_wr(winlist);
    231 	}
    232 
    233 	XSync(dpy, False);
    234 	XSetErrorHandler(old_handler);
    235 	trapped_xerror = 0;
    236 
    237 	return saw_gdm_name;
    238 #endif
    239 }
    240 
    241 static int wm_running(void) {
    242 	char *s = getenv("DEBUG_WM_RUNNING");
    243 	int ret = 0;
    244 	RAWFB_RET(0)
    245 #if NO_X11
    246 	return 0;
    247 #else
    248 	/*
    249 	 * Unfortunately with recent GDM (v2.28), they run gnome-session,
    250 	 * dbus-launch, and metacity for the Login greeter!  So the simple
    251 	 * XInternAtom checks below no longer work.
    252          * We also see a similar thing with KDE.
    253 	 */
    254 	if (dm_still_running()) {
    255 		return 0;
    256 	}
    257 
    258 	/* we are xlocked. */
    259 	if (XInternAtom(dpy, "_NET_SUPPORTED", True) != None) {
    260 		if (s) rfbLog("wm is running (_NET_SUPPORTED).\n");
    261 		ret++;
    262 	}
    263 	if (XInternAtom(dpy, "_WIN_PROTOCOLS", True) != None) {
    264 		if (s) rfbLog("wm is running (_WIN_PROTOCOLS).\n");
    265 		ret++;
    266 	}
    267 	if (XInternAtom(dpy, "_XROOTPMAP_ID", True) != None) {
    268 		if (s) rfbLog("wm is running (_XROOTPMAP_ID).\n");
    269 		ret++;
    270 	}
    271 	if (XInternAtom(dpy, "_MIT_PRIORITY_COLORS", True) != None) {
    272 		if (s) rfbLog("wm is running (_MIT_PRIORITY_COLORS).\n");
    273 		ret++;
    274 	}
    275 	if (!ret) {
    276 		if (s) rfbLog("wm is not running.\n");
    277 		return 0;
    278 	} else {
    279 		if (s) rfbLog("wm is running ret=%d.\n", ret);
    280 		return 1;
    281 	}
    282 #endif	/* NO_X11 */
    283 
    284 }
    285 
    286 int guess_dm_gone(int t1, int t2) {
    287 	int wait = t2;
    288 	char *avoid = getenv("X11VNC_AVOID_WINDOWS");
    289 	time_t tcheck = last_client;
    290 
    291 	if (last_open_xdisplay > last_client) {
    292 		/* better time for -display WAIT:... */
    293 		tcheck = last_open_xdisplay;
    294 	}
    295 
    296 	if (avoid && !strcmp(avoid, "never")) {
    297 		return 1;
    298 	}
    299 	if (!screen || !screen->clientHead) {
    300 		return 0;
    301 	}
    302 	if (avoid) {
    303 		int n = atoi(avoid);
    304 		if (n > 1) {
    305 			wait = n;
    306 		} else {
    307 			wait = 90;
    308 		}
    309 	} else {
    310 		static time_t saw_wm = 0;
    311 
    312 		wait = t2;
    313 
    314 		X_LOCK;
    315 		if (wm_running()) {
    316 			if (saw_wm == 0) {
    317 				saw_wm = time(NULL);
    318 			} else if (time(NULL) <= saw_wm + 2) {
    319 				/* try to wait a few seconds after transition */
    320 				;
    321 			} else {
    322 				wait = t1;
    323 			}
    324 		}
    325 		X_UNLOCK;
    326 	}
    327 	if (getenv("DEBUG_WM_RUNNING")) {
    328 		rfbLog("guess_dm_gone: wait=%d\n", wait);
    329 	}
    330 	/* we assume they've logged in OK after wait seconds... */
    331 	if (time(NULL) <= tcheck + wait)  {
    332 		return 0;
    333 	}
    334 	return 1;
    335 }
    336 
    337 static void initialize_xevents(int reset) {
    338 #if NO_X11
    339 	RAWFB_RET_VOID
    340 	if (!reset) {}
    341 	return;
    342 #else
    343 	static int did_xselect_input = 0;
    344 	static int did_xcreate_simple_window = 0;
    345 	static int did_vnc_connect_prop = 0;
    346 	static int did_x11vnc_remote_prop = 0;
    347 	static int did_clipboard_atom = 0;
    348 	static int did_xfixes = 0;
    349 	static int did_xdamage = 0;
    350 	static int did_xrandr = 0;
    351 
    352 	RAWFB_RET_VOID
    353 
    354 	if (reset) {
    355 		did_xselect_input = 0;
    356 		did_xcreate_simple_window = 0;
    357 		did_vnc_connect_prop = 0;
    358 		did_x11vnc_remote_prop = 0;
    359 		did_clipboard_atom = 0;
    360 		did_xfixes = 0;
    361 		did_xdamage = 0;
    362 		did_xrandr = 0;
    363 	}
    364 
    365 	if ((watch_selection || vnc_connect) && !did_xselect_input) {
    366 		/*
    367 		 * register desired event(s) for notification.
    368 		 * PropertyChangeMask is for CUT_BUFFER0 changes.
    369 		 * XXX: does this cause a flood of other stuff?
    370 		 */
    371 		X_LOCK;
    372 		xselectinput_rootwin |= PropertyChangeMask;
    373 		XSelectInput_wr(dpy, rootwin, xselectinput_rootwin);
    374 
    375 		if (subwin && freeze_when_obscured) {
    376 			XSelectInput_wr(dpy, subwin, VisibilityChangeMask);
    377 		}
    378 		X_UNLOCK;
    379 		did_xselect_input = 1;
    380 	}
    381 	if (watch_selection && !did_xcreate_simple_window) {
    382 		/* create fake window for our selection ownership, etc */
    383 
    384 		/*
    385 		 * We try to delay creating selwin until we are past
    386 		 * any GDM, (or other KillInitClients=true) manager.
    387 		 */
    388 		if (guess_dm_gone(8, 45)) {
    389 			X_LOCK;
    390 			selwin = XCreateSimpleWindow(dpy, rootwin, 3, 2, 1, 1, 0, 0, 0);
    391 			X_UNLOCK;
    392 			did_xcreate_simple_window = 1;
    393 			if (! quiet) rfbLog("created selwin: 0x%lx\n", selwin);
    394 		}
    395 	}
    396 
    397 	if ((xrandr || xrandr_maybe) && !did_xrandr) {
    398 		initialize_xrandr();
    399 		did_xrandr = 1;
    400 	}
    401 	if (vnc_connect && !did_vnc_connect_prop) {
    402 		initialize_vnc_connect_prop();
    403 		did_vnc_connect_prop = 1;
    404 	}
    405 	if (vnc_connect && !did_x11vnc_remote_prop) {
    406 		initialize_x11vnc_remote_prop();
    407 		did_x11vnc_remote_prop = 1;
    408 	}
    409 	if (run_gui_pid > 0) {
    410 		kill(run_gui_pid, SIGUSR1);
    411 		run_gui_pid = 0;
    412 	}
    413 	if (!did_clipboard_atom) {
    414 		initialize_clipboard_atom();
    415 		did_clipboard_atom = 1;
    416 	}
    417 	if (xfixes_present && use_xfixes && !did_xfixes) {
    418 		/*
    419 		 * We try to delay creating initializing xfixes until
    420 		 * we are past the display manager, due to Xorg bug:
    421 		 * http://bugs.freedesktop.org/show_bug.cgi?id=18451
    422 		 */
    423 		if (guess_dm_gone(8, 45)) {
    424 			initialize_xfixes();
    425 			did_xfixes = 1;
    426 			if (! quiet) rfbLog("called initialize_xfixes()\n");
    427 		}
    428 	}
    429 	if (xdamage_present && !did_xdamage) {
    430 		initialize_xdamage();
    431 		did_xdamage = 1;
    432 	}
    433 #endif	/* NO_X11 */
    434 }
    435 
    436 static void print_xevent_bases(void) {
    437 	fprintf(stderr, "X event bases: xkb=%d, xtest=%d, xrandr=%d, "
    438 	    "xfixes=%d, xdamage=%d, xtrap=%d\n", xkb_base_event_type,
    439 	    xtest_base_event_type, xrandr_base_event_type,
    440 	    xfixes_base_event_type, xdamage_base_event_type,
    441 	    xtrap_base_event_type);
    442 	fprintf(stderr, "  MapNotify=%d, ClientMsg=%d PropNotify=%d "
    443 	    "SelNotify=%d, SelRequest=%d\n", MappingNotify, ClientMessage,
    444 	    PropertyNotify, SelectionNotify, SelectionRequest);
    445 	fprintf(stderr, "  SelClear=%d, Expose=%d\n", SelectionClear, Expose);
    446 }
    447 
    448 void get_prop(char *str, int len, Atom prop, Window w) {
    449 	int i;
    450 #if !NO_X11
    451 	Atom type;
    452 	int format, slen, dlen;
    453 	unsigned long nitems = 0, bytes_after = 0;
    454 	unsigned char* data = NULL;
    455 #endif
    456 
    457 	for (i=0; i<len; i++) {
    458 		str[i] = '\0';
    459 	}
    460 	if (prop == None) {
    461 		return;
    462 	}
    463 
    464 	RAWFB_RET_VOID
    465 
    466 #if NO_X11
    467 	return;
    468 #else
    469 
    470 	slen = 0;
    471 	if (w == None) {
    472 		w = DefaultRootWindow(dpy);
    473 	}
    474 
    475 	do {
    476 		if (XGetWindowProperty(dpy, w,
    477 		    prop, nitems/4, len/16, False,
    478 		    AnyPropertyType, &type, &format, &nitems, &bytes_after,
    479 		    &data) == Success) {
    480 
    481 			dlen = nitems * (format/8);
    482 			if (slen + dlen > len) {
    483 				/* too big */
    484 				XFree_wr(data);
    485 				break;
    486 			}
    487 			memcpy(str+slen, data, dlen);
    488 			slen += dlen;
    489 			str[slen] = '\0';
    490 			XFree_wr(data);
    491 		}
    492 	} while (bytes_after > 0);
    493 #endif	/* NO_X11 */
    494 }
    495 
    496 static void bust_grab(int reset) {
    497 #if NO_X11
    498 	if (!reset) {}
    499 	return;
    500 #else
    501 	static int bust_count = 0;
    502 	static time_t last_bust = 0;
    503 	time_t now = time(NULL);
    504 	KeyCode key;
    505 	int button, x, y, nb;
    506 
    507 	if (now > last_bust + 180) {
    508 		bust_count = 0;
    509 	}
    510 	if (reset) {
    511 		bust_count = 0;
    512 		return;
    513 	}
    514 
    515 	x = 0;
    516 	y = 0;
    517 	button = 0;
    518 	key = NoSymbol;
    519 
    520 	nb = 8;
    521 	if (bust_count >= 3 * nb)  {
    522 		fprintf(stderr, "too many bust_grab's %d for me\n", bust_count);
    523 		exit(0);
    524 	}
    525 	if (bust_count % nb == 0) {
    526 		button = 1;
    527 	} else if (bust_count % nb == 1) {
    528 		button = 1;
    529 	} else if (bust_count % nb == 2) {
    530 		key = XKeysymToKeycode(dpy, XK_Escape);
    531 	} else if (bust_count % nb == 3) {
    532 		button = 3;
    533 	} else if (bust_count % nb == 4) {
    534 		key = XKeysymToKeycode(dpy, XK_space);
    535 	} else if (bust_count % nb == 5) {
    536 		x = bust_count * 23;
    537 		y = bust_count * 17;
    538 	} else if (bust_count % nb == 5) {
    539 		button = 2;
    540 	} else if (bust_count % nb == 6) {
    541 		key = XKeysymToKeycode(dpy, XK_a);
    542 	}
    543 
    544 	if (key == NoSymbol) {
    545 		key = XKeysymToKeycode(dpy, XK_a);
    546 		if (key == NoSymbol) {
    547 			button = 1;
    548 		}
    549 	}
    550 
    551 	bust_count++;
    552 
    553 	if (button) {
    554 		/* try button press+release */
    555 		fprintf(stderr, "**bust_grab: button%d  %.4f\n",
    556 		    button, dnowx());
    557 		XTestFakeButtonEvent_wr(dpy, button, True, CurrentTime);
    558 		XFlush_wr(dpy);
    559 		usleep(50 * 1000);
    560 		XTestFakeButtonEvent_wr(dpy, button, False, CurrentTime);
    561 	} else if (x > 0) {
    562 		/* try button motion*/
    563 		int scr = DefaultScreen(dpy);
    564 
    565 		fprintf(stderr, "**bust_grab: x=%d y=%d  %.4f\n", x, y,
    566 		    dnowx());
    567 		XTestFakeMotionEvent_wr(dpy, scr, x, y, CurrentTime);
    568 		XFlush_wr(dpy);
    569 		usleep(50 * 1000);
    570 
    571 		/* followed by button press */
    572 		button = 1;
    573 		fprintf(stderr, "**bust_grab: button%d\n", button);
    574 		XTestFakeButtonEvent_wr(dpy, button, True, CurrentTime);
    575 		XFlush_wr(dpy);
    576 		usleep(50 * 1000);
    577 		XTestFakeButtonEvent_wr(dpy, button, False, CurrentTime);
    578 	} else {
    579 		/* try Escape or Space press+release */
    580 		fprintf(stderr, "**bust_grab: keycode: %d  %.4f\n",
    581 		    (int) key, dnowx());
    582 		XTestFakeKeyEvent_wr(dpy, key, True, CurrentTime);
    583 		XFlush_wr(dpy);
    584 		usleep(50 * 1000);
    585 		XTestFakeKeyEvent_wr(dpy, key, False, CurrentTime);
    586 	}
    587 	XFlush_wr(dpy);
    588 	last_bust = time(NULL);
    589 #endif	/* NO_X11 */
    590 }
    591 
    592 typedef struct _grabwatch {
    593 	int pid;
    594 	int tick;
    595 	unsigned long time;
    596 	time_t change;
    597 } grabwatch_t;
    598 #define GRABWATCH 16
    599 
    600 static int grab_buster_delay = 20;
    601 static pid_t grab_buster_pid = 0;
    602 
    603 static int grab_npids = 1;
    604 
    605 static int process_watch(char *str, int parent, int db) {
    606 	int i, pid, ticker, npids;
    607 	char diff[128];
    608 	unsigned long xtime;
    609 	static grabwatch_t watches[GRABWATCH];
    610 	static int first = 1;
    611 	time_t now = time(NULL);
    612 	static time_t last_bust = 0;
    613 	int too_long, problems = 0;
    614 
    615 	if (first) {
    616 		for (i=0; i < GRABWATCH; i++) {
    617 			watches[i].pid = 0;
    618 			watches[i].tick = 0;
    619 			watches[i].time = 0;
    620 			watches[i].change = 0;
    621 		}
    622 		first = 0;
    623 	}
    624 
    625 	/* record latest value of prop */
    626 	if (str && *str != '\0') {
    627 		if (sscanf(str, "%d/%d/%lu/%s", &pid, &ticker, &xtime, diff)
    628 		    == 4) {
    629 			int got = -1, free = -1;
    630 
    631 			if (db) fprintf(stderr, "grab_buster %d - %d - %lu - %s"
    632 			    "\n", pid, ticker, xtime, diff);
    633 
    634 			if (pid == parent && !strcmp(diff, "QUIT")) {
    635 				/* that's it. */
    636 				return 0;
    637 			}
    638 			if (pid == 0 || ticker == 0 || xtime == 0) {
    639 				/* bad prop read. */
    640 				goto badtickerstr;
    641 			}
    642 			for (i=0; i < GRABWATCH; i++) {
    643 				if (watches[i].pid == pid) {
    644 					got = i;
    645 					break;
    646 				}
    647 				if (free == -1 && watches[i].pid == 0) {
    648 					free = i;
    649 				}
    650 			}
    651 			if (got == -1) {
    652 				if (free == -1) {
    653 					/* bad news */;
    654 					free = GRABWATCH - 1;
    655 				}
    656 				watches[free].pid  = pid;
    657 				watches[free].tick = ticker;
    658 				watches[free].time = xtime;
    659 				watches[free].change = now;
    660 				if (db) fprintf(stderr, "grab_buster free slot: %d\n", free);
    661 			} else {
    662 				if (db) fprintf(stderr, "grab_buster got  slot: %d\n", got);
    663 				if (watches[got].tick != ticker) {
    664 					watches[got].change = now;
    665 				}
    666 				if (watches[got].time != xtime) {
    667 					watches[got].change = now;
    668 				}
    669 				watches[got].tick = ticker;
    670 				watches[got].time = xtime;
    671 			}
    672 		} else {
    673 			if (db) fprintf(stderr, "grab_buster bad prop str: %s\n", str);
    674 		}
    675 	}
    676 
    677 	badtickerstr:
    678 
    679 	too_long = grab_buster_delay;
    680 	if (too_long < 3 * sync_tod_delay) {
    681 		too_long = 3 * sync_tod_delay;
    682 	}
    683 
    684 	npids = 0;
    685 	for (i=0; i < GRABWATCH; i++) {
    686 		if (watches[i].pid) {
    687 			npids++;
    688 		}
    689 	}
    690 	grab_npids = npids;
    691 	if (npids > 4) {
    692 		npids = 4;
    693 	}
    694 
    695 	/* now check everyone we are tracking */
    696 	for (i=0; i < GRABWATCH; i++) {
    697 		int fac = 1;
    698 		if (!watches[i].pid) {
    699 			continue;
    700 		}
    701 		if (watches[i].change == 0) {
    702 			watches[i].change = now;	/* just to be sure */
    703 			continue;
    704 		}
    705 
    706 		pid = watches[i].pid;
    707 
    708 		if (pid != parent) {
    709 			fac = 2;
    710 		}
    711 		if (npids > 0) {
    712 			fac *= npids;
    713 		}
    714 
    715 		if (now > watches[i].change + fac*too_long) {
    716 			int process_alive = 1;
    717 
    718 			fprintf(stderr, "grab_buster: problem with pid: "
    719 			    "%d - %d/%d/%d\n", pid, (int) now,
    720 			    (int) watches[i].change, too_long);
    721 
    722 			if (kill((pid_t) pid, 0) != 0) {
    723 				if (1 || errno == ESRCH) {
    724 					process_alive = 0;
    725 				}
    726 			}
    727 
    728 			if (!process_alive) {
    729 				watches[i].pid = 0;
    730 				watches[i].tick = 0;
    731 				watches[i].time = 0;
    732 				watches[i].change = 0;
    733 				fprintf(stderr, "grab_buster: pid gone: %d\n",
    734 				    pid);
    735 				if (pid == parent) {
    736 					/* that's it */
    737 					return 0;
    738 				}
    739 			} else {
    740 				int sleep = sync_tod_delay * 1000 * 1000;
    741 
    742 				bust_grab(0);
    743 				problems++;
    744 				last_bust = now;
    745 				usleep(1 * sleep);
    746 				break;
    747 			}
    748 		}
    749 	}
    750 
    751 	if (!problems) {
    752 		bust_grab(1);
    753 	}
    754 	return 1;
    755 }
    756 
    757 static void grab_buster_watch(int parent, char *dstr) {
    758 #if NO_X11
    759 	RAWFB_RET_VOID
    760 	if (!parent || !dstr) {}
    761 	return;
    762 #else
    763 	Atom ticker_atom = None;
    764 	int sleep = sync_tod_delay * 921 * 1000;
    765 	char propval[200];
    766 	int ev, er, maj, min;
    767 	int db = 0;
    768 	char *ticker_str = "X11VNC_TICKER";
    769 
    770 	RAWFB_RET_VOID
    771 
    772 	if (grab_buster > 1) {
    773 		db = 1;
    774 	}
    775 
    776 	/* overwrite original dpy, we let orig connection sit unused. */
    777 	dpy = XOpenDisplay_wr(dstr);
    778 	if (!dpy) {
    779 		fprintf(stderr, "grab_buster_watch: could not reopen: %s\n",
    780 		    dstr);
    781 		return;
    782 	}
    783 	rfbLogEnable(0);
    784 
    785 	/* check for XTEST, etc, and then disable grabs for us */
    786 	if (! XTestQueryExtension_wr(dpy, &ev, &er, &maj, &min)) {
    787 		xtest_present = 0;
    788 	} else {
    789 		xtest_present = 1;
    790 	}
    791 	if (! XETrapQueryExtension_wr(dpy, &ev, &er, &maj)) {
    792 		xtrap_present = 0;
    793 	} else {
    794 		xtrap_present = 1;
    795 	}
    796 
    797 	if (! xtest_present && ! xtrap_present) {
    798 		fprintf(stderr, "grab_buster_watch: no grabserver "
    799 		    "protection on display: %s\n", dstr);
    800 		return;
    801 	}
    802 	disable_grabserver(dpy, 0);
    803 
    804 	usleep(3 * sleep);
    805 
    806 	if (getenv("X11VNC_TICKER")) {
    807 		ticker_str = getenv("X11VNC_TICKER");
    808 	}
    809 	ticker_atom = XInternAtom(dpy, ticker_str, False);
    810 	if (! ticker_atom) {
    811 		fprintf(stderr, "grab_buster_watch: no ticker atom\n");
    812 		return;
    813 	}
    814 
    815 	while(1) {
    816 		int slp = sleep;
    817 		if (grab_npids > 1) {
    818 			slp = slp / 8;
    819 		}
    820 		usleep(slp);
    821 		usleep((int) (0.60 * rfac() * slp));
    822 
    823 		if (kill((pid_t) parent, 0) != 0) {
    824 			break;
    825 		}
    826 
    827 		get_prop(propval, 128, ticker_atom, None);
    828 		if (db) fprintf(stderr, "got_prop:   %s\n", propval);
    829 
    830 		if (!process_watch(propval, parent, db)) {
    831 			break;
    832 		}
    833 	}
    834 #endif	/* NO_X11 */
    835 }
    836 
    837 void spawn_grab_buster(void) {
    838 #if LIBVNCSERVER_HAVE_FORK
    839 	pid_t pid;
    840 	int parent = (int) getpid();
    841 	char *dstr = strdup(DisplayString(dpy));
    842 
    843 	RAWFB_RET_VOID
    844 
    845 	XCloseDisplay_wr(dpy);
    846 	dpy = NULL;
    847 
    848 	if ((pid = fork()) > 0) {
    849 		grab_buster_pid = pid;
    850 		if (! quiet) {
    851 			rfbLog("grab_buster pid is: %d\n", (int) pid);
    852 		}
    853 	} else if (pid == -1) {
    854 		fprintf(stderr, "spawn_grab_buster: could not fork\n");
    855 		rfbLogPerror("fork");
    856 	} else {
    857 		signal(SIGHUP,  SIG_DFL);
    858 		signal(SIGINT,  SIG_DFL);
    859 		signal(SIGQUIT, SIG_DFL);
    860 		signal(SIGTERM, SIG_DFL);
    861 
    862 		grab_buster_watch(parent, dstr);
    863 		exit(0);
    864 	}
    865 
    866 	dpy = XOpenDisplay_wr(dstr);
    867 	if (!dpy) {
    868 		rfbLog("failed to reopen display %s in spawn_grab_buster\n",
    869 		    dstr);
    870 		exit(1);
    871 	}
    872 #endif
    873 }
    874 
    875 void sync_tod_with_servertime(void) {
    876 #if NO_X11
    877 	RAWFB_RET_VOID
    878 	return;
    879 #else
    880 	static Atom ticker_atom = None;
    881 	XEvent xev;
    882 	char diff[128];
    883 	static int seq = 0;
    884 	static unsigned long xserver_ticks = 1;
    885 	int i, db = 0;
    886 
    887 	RAWFB_RET_VOID
    888 
    889 	if (atom_NET_ACTIVE_WINDOW == None) {
    890 		atom_NET_ACTIVE_WINDOW = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", True);
    891 	}
    892 	if (atom_NET_CURRENT_DESKTOP == None) {
    893 		atom_NET_CURRENT_DESKTOP = XInternAtom(dpy, "_NET_CURRENT_DESKTOP", True);
    894 	}
    895 	if (atom_NET_CLIENT_LIST_STACKING == None) {
    896 		atom_NET_CLIENT_LIST_STACKING = XInternAtom(dpy, "_NET_CLIENT_LIST_STACKING", True);
    897 	}
    898 	if (atom_XROOTPMAP_ID == None) {
    899 		atom_XROOTPMAP_ID = XInternAtom(dpy, "_XROOTPMAP_ID", True);
    900 	}
    901 
    902 	if (! ticker_atom) {
    903 		char *ticker_str = "X11VNC_TICKER";
    904 		if (getenv("X11VNC_TICKER")) {
    905 			ticker_str = getenv("X11VNC_TICKER");
    906 		}
    907 		ticker_atom = XInternAtom(dpy, ticker_str, False);
    908 	}
    909 	if (! ticker_atom) {
    910 		return;
    911 	}
    912 
    913 	XSync(dpy, False);
    914 	while (XCheckTypedEvent(dpy, PropertyNotify, &xev)) {
    915 		set_prop_atom(xev.xproperty.atom);
    916 	}
    917 
    918 	snprintf(diff, 128, "%d/%08d/%lu/%.6f", (int) getpid(), seq++,
    919 	    xserver_ticks, servertime_diff);
    920 	XChangeProperty(dpy, rootwin, ticker_atom, XA_STRING, 8,
    921 	    PropModeReplace, (unsigned char *) diff, strlen(diff));
    922 	XSync(dpy, False);
    923 
    924 	for (i=0; i < 10; i++) {
    925 		int k, got = 0;
    926 
    927 		for (k=0; k < 5; k++) {
    928 			while (XCheckTypedEvent(dpy, PropertyNotify, &xev)) {
    929 				if (xev.xproperty.atom == ticker_atom) {
    930 					double stime;
    931 
    932 					xserver_ticks = xev.xproperty.time;
    933 					stime = (double) xev.xproperty.time;
    934 					stime = stime/1000.0;
    935 					servertime_diff = dnow() - stime;
    936 					if (db) rfbLog("set servertime_diff: "
    937 					    "%.6f\n", servertime_diff);
    938 					got = 1;
    939 				}
    940 			}
    941 		}
    942 		if (got) {
    943 			break;
    944 		}
    945 		usleep(1000);
    946 	}
    947 #endif	/* NO_X11 */
    948 }
    949 
    950 void check_keycode_state(void) {
    951 	static time_t last_check = 0;
    952 	int delay = 10, noinput = 3;
    953 	time_t now = time(NULL);
    954 
    955 	if (! client_count) {
    956 		return;
    957 	}
    958 	if (unixpw_in_progress) return;
    959 
    960 	RAWFB_RET_VOID
    961 
    962 	/*
    963 	 * periodically update our model of the keycode_state[]
    964 	 * by correlating with the Xserver.  wait for a pause in
    965 	 * keyboard input to be on the safe side.  the idea here
    966 	 * is to remove stale keycode state, not to be perfectly
    967 	 * in sync with the Xserver at every instant of time.
    968 	 */
    969 	if (now > last_check + delay && now > last_keyboard_input + noinput) {
    970 		X_LOCK;
    971 		init_track_keycode_state();
    972 		X_UNLOCK;
    973 		last_check = now;
    974 	}
    975 }
    976 
    977 /*
    978  * To use the experimental -grablocal option configure like this:
    979  * env CPPFLAGS=-DENABLE_GRABLOCAL LDFLAGS=-lXss ./configure
    980  */
    981 #ifdef ENABLE_GRABLOCAL
    982 #include <X11/extensions/scrnsaver.h>
    983 
    984 void check_local_grab(void) {
    985 	static double last_check = 0.0;
    986 	double now;
    987 
    988 	if (grab_local <= 0) {
    989 		return;
    990 	}
    991 	if (! client_count) {
    992 		return;
    993 	}
    994 	if (unixpw_in_progress) return;
    995 
    996 	if (last_rfb_key_injected <= 0.0 && last_rfb_ptr_injected <= 0.0) {
    997 		return;
    998 	}
    999 
   1000 	RAWFB_RET_VOID
   1001 
   1002 	now = dnow();
   1003 
   1004 	if (now > last_check + 0.1)   {
   1005 #if !NO_X11
   1006 		int ret;
   1007 		double idle;
   1008 		XScreenSaverInfo info;
   1009 		static int save_viewonly = -1, local_is_idle = -1, db = -1;
   1010 
   1011 		if (debug_keyboard) db = debug_keyboard;
   1012 		if (debug_pointer ) db = debug_pointer;
   1013 
   1014 		if (db < 0) {
   1015 			if (getenv("LOCAL_GRAB_DEBUG")) {
   1016 				db = atoi(getenv("LOCAL_GRAB_DEBUG"));
   1017 			} else {
   1018 				db = 0;
   1019 			}
   1020 		}
   1021 
   1022 		ret = XScreenSaverQueryInfo(dpy, RootWindowOfScreen(
   1023 		    ScreenOfDisplay(dpy, 0)), &info);
   1024 
   1025 		if (ret) {
   1026 			double tlatest_rfb = 0.0;
   1027 
   1028 			idle = ((double) info.idle)/1000.0;
   1029 			now = dnow();
   1030 
   1031 			if (last_rfb_key_injected > 0.0) {
   1032 				tlatest_rfb = last_rfb_key_injected;
   1033 			}
   1034 			if (last_rfb_ptr_injected > tlatest_rfb) {
   1035 				tlatest_rfb = last_rfb_ptr_injected;
   1036 			}
   1037 			if (db > 1) fprintf(stderr, "idle: %.4f latest: %.4f dt: %.4f\n", idle, now - tlatest_rfb, idle - (now - tlatest_rfb));
   1038 
   1039 			if (now - tlatest_rfb <= idle + 0.005) {
   1040 				/* 0.005 is 5ms tolerance */
   1041 			} else if (idle < grab_local) {
   1042 				if (local_is_idle < 0 || local_is_idle) {
   1043 					save_viewonly = view_only;
   1044 					view_only = 1;
   1045 					if (db) {
   1046 						rfbLog("check_local_grab: set viewonly\n");
   1047 					}
   1048 				}
   1049 
   1050 				local_is_idle = 0;
   1051 			} else {
   1052 				if (!local_is_idle && save_viewonly >= 0) {
   1053 					view_only = save_viewonly;
   1054 					if (db) {
   1055 						rfbLog("check_local_grab: restored viewonly; %d\n", view_only);
   1056 					}
   1057 				}
   1058 				local_is_idle = 1;
   1059 			}
   1060 		}
   1061 #endif
   1062 		last_check = dnow();
   1063 	}
   1064 }
   1065 #endif
   1066 
   1067 void check_autorepeat(void) {
   1068 	static time_t last_check = 0;
   1069 	static int idle_timeout = -300, idle_reset = 0;
   1070 	time_t now = time(NULL);
   1071 	int autorepeat_is_on, autorepeat_initially_on;
   1072 
   1073 	if (! no_autorepeat || ! client_count) {
   1074 		return;
   1075 	}
   1076 	if (now <= last_check + 1) {
   1077 		return;
   1078 	}
   1079 
   1080 	if (unixpw_in_progress) return;
   1081 
   1082 	if (idle_timeout < 0) {
   1083 		if (getenv("X11VNC_IDLE_TIMEOUT")) {
   1084 			idle_timeout = atoi(getenv("X11VNC_IDLE_TIMEOUT"));
   1085 		}
   1086 		if (idle_timeout < 0) {
   1087 			idle_timeout = -idle_timeout;
   1088 		}
   1089 	}
   1090 
   1091 	last_check = now;
   1092 
   1093 	autorepeat_is_on = get_autorepeat_state();
   1094 	autorepeat_initially_on = get_initial_autorepeat_state();
   1095 
   1096 	if (view_only) {
   1097 		if (! autorepeat_is_on) {
   1098 			autorepeat(1, 1);
   1099 		}
   1100 		return;
   1101 	}
   1102 
   1103 	if (now > last_keyboard_input + idle_timeout) {
   1104 		/* autorepeat should be on when idle */
   1105 		if (! autorepeat_is_on && autorepeat_initially_on) {
   1106 			static time_t last_msg = 0;
   1107 			static int cnt = 0;
   1108 			if (now > last_msg + idle_timeout && cnt++ < 10) {
   1109 				rfbLog("idle keyboard:   turning X autorepeat"
   1110 				    " back on.\n");
   1111 				last_msg = now;
   1112 			}
   1113 			autorepeat(1, 1);
   1114 			idle_reset = 1;
   1115 		}
   1116 	} else {
   1117 		if (idle_reset) {
   1118 			int i, state[256], didmsg = 0, pressed = 0;
   1119 			int mwt = 600, mmax = 20;
   1120 			static int msgcnt = 0;
   1121 			static double lastmsg = 0.0;
   1122 
   1123 			for (i=0; i<256; i++) {
   1124 				state[i] = 0;
   1125 			}
   1126 			if (use_threads) {X_LOCK;}
   1127 			get_keystate(state);
   1128 			if (use_threads) {X_UNLOCK;}
   1129 
   1130 			for (i=0; i<256; i++) {
   1131 				if (state[i] != 0) {
   1132 					/* better wait until all keys are up  */
   1133 					pressed++;
   1134 					if (msgcnt < mmax || dnow() > lastmsg + mwt) {
   1135 						char *str = "unset";
   1136 #if !NO_X11
   1137 						if (use_threads) {X_LOCK;}
   1138 						str = XKeysymToString(XKeycodeToKeysym(dpy, i, 0));
   1139 						if (use_threads) {X_UNLOCK;}
   1140 #endif
   1141 						str = str ? str : "nosymbol";
   1142 						didmsg++;
   1143 						rfbLog("active keyboard: waiting until "
   1144 						    "all keys are up. key_down=%d %s.  "
   1145 						    "If the key is inaccessible via keyboard, "
   1146 						    "consider 'x11vnc -R clear_all'\n", i, str);
   1147 					}
   1148 				}
   1149 			}
   1150 			if (didmsg > 0) {
   1151 				msgcnt++;
   1152 				if (msgcnt == mmax) {
   1153 					rfbLog("active keyboard: last such "
   1154 					    "message for %d secs.\n", mwt);
   1155 				}
   1156 				lastmsg = dnow();
   1157 			}
   1158 			if (pressed > 0) {
   1159 				return;
   1160 			}
   1161 		}
   1162 		if (idle_reset) {
   1163 			static time_t last_msg = 0;
   1164 			static int cnt = 0;
   1165 			if (now > last_msg + idle_timeout && cnt++ < 10) {
   1166 				rfbLog("active keyboard: turning X autorepeat"
   1167 				    " off.\n");
   1168 				last_msg = now;
   1169 			}
   1170 			autorepeat(0, 1);
   1171 			idle_reset = 0;
   1172 
   1173 		} else if (no_repeat_countdown && autorepeat_is_on) {
   1174 			int n = no_repeat_countdown - 1;
   1175 			if (n >= 0) {
   1176 				rfbLog("Battling with something for "
   1177 				    "-norepeat!! (%d resets left)\n", n);
   1178 			} else {
   1179 				rfbLog("Battling with something for "
   1180 				    "-norepeat!!\n");
   1181 			}
   1182 			if (no_repeat_countdown > 0) {
   1183 				no_repeat_countdown--;
   1184 			}
   1185 			autorepeat(1, 0);
   1186 			autorepeat(0, 0);
   1187 		}
   1188 	}
   1189 }
   1190 
   1191 void set_prop_atom(Atom atom) {
   1192 	if (atom == None) return;
   1193 	if (atom == atom_NET_ACTIVE_WINDOW) got_NET_ACTIVE_WINDOW = dnow();
   1194 	if (atom == atom_NET_CURRENT_DESKTOP) got_NET_CURRENT_DESKTOP = dnow();
   1195 	if (atom == atom_NET_CLIENT_LIST_STACKING) got_NET_CLIENT_LIST_STACKING = dnow();
   1196 	if (atom == atom_XROOTPMAP_ID) got_XROOTPMAP_ID = dnow();
   1197 }
   1198 
   1199 /*
   1200  * This routine is periodically called to check for selection related
   1201  * and other X11 events and respond to them as needed.
   1202  */
   1203 void check_xevents(int reset) {
   1204 #if NO_X11
   1205 	RAWFB_RET_VOID
   1206 	if (!reset) {}
   1207 	return;
   1208 #else
   1209 	XEvent xev;
   1210 	int tmp, have_clients = 0;
   1211 	static int sent_some_sel = 0;
   1212 	static time_t last_call = 0;
   1213 	static time_t last_bell = 0;
   1214 	static time_t last_init_check = 0;
   1215 	static time_t last_sync = 0;
   1216 	static time_t last_time_sync = 0;
   1217 	time_t now = time(NULL);
   1218 	static double last_request = 0.0;
   1219 	static double last_xrefresh = 0.0;
   1220 	XErrorHandler old_handler;
   1221 
   1222 	if (unixpw_in_progress) return;
   1223 
   1224 	RAWFB_RET_VOID
   1225 
   1226 	if (now > last_init_check+1 || reset) {
   1227 		last_init_check = now;
   1228 		initialize_xevents(reset);
   1229 		if (reset) {
   1230 			return;
   1231 		}
   1232 	}
   1233 
   1234 	if (screen && screen->clientHead) {
   1235 		have_clients = 1;
   1236 	}
   1237 
   1238 	X_LOCK;
   1239 	/*
   1240 	 * There is a bug where we have to wait before sending text to
   1241 	 * the client... so instead of sending right away we wait a
   1242 	 * the few seconds.
   1243 	 */
   1244 
   1245 	if (have_clients && watch_selection && !sent_some_sel
   1246 	    && now > last_client + sel_waittime) {
   1247 		if (XGetSelectionOwner(dpy, XA_PRIMARY) == None) {
   1248 			cutbuffer_send();
   1249 		}
   1250 		sent_some_sel = 1;
   1251 	}
   1252 	if (! have_clients) {
   1253 		/*
   1254 		 * If we don't have clients we can miss the X server
   1255 		 * going away until a client connects.
   1256 		 */
   1257 		static time_t last_X_ping = 0;
   1258 		if (now > last_X_ping + 5) {
   1259 			last_X_ping = now;
   1260 			XGetSelectionOwner(dpy, XA_PRIMARY);
   1261 		}
   1262 	}
   1263 
   1264 	if (have_clients && xrefresh > 0.0 && dnow() > last_xrefresh + xrefresh) {
   1265 		XSetWindowAttributes swa;
   1266 		Visual visual;
   1267 		Window xrf;
   1268 		unsigned long mask;
   1269 
   1270 		swa.override_redirect = True;
   1271 		swa.backing_store = NotUseful;
   1272 		swa.save_under = False;
   1273 		swa.background_pixmap = None;
   1274 		visual.visualid = CopyFromParent;
   1275 		mask = (CWOverrideRedirect|CWBackingStore|CWSaveUnder|CWBackPixmap);
   1276 
   1277 		xrf = XCreateWindow(dpy, window, coff_x, coff_y, dpy_x, dpy_y, 0, CopyFromParent,
   1278 		    InputOutput, &visual, mask, &swa);
   1279 		if (xrf != None) {
   1280 			if (0) fprintf(stderr, "XCreateWindow(%d, %d, %d, %d) 0x%lx\n", coff_x, coff_y, dpy_x, dpy_y, xrf);
   1281 			XMapWindow(dpy, xrf);
   1282 			XFlush_wr(dpy);
   1283 			XDestroyWindow(dpy, xrf);
   1284 			XFlush_wr(dpy);
   1285 		}
   1286 		last_xrefresh = dnow();
   1287 	}
   1288 
   1289 	if (now > last_call+1) {
   1290 		/* we only check these once a second or so. */
   1291 		int n = 0;
   1292 
   1293 		trapped_xerror = 0;
   1294 		old_handler = XSetErrorHandler(trap_xerror);
   1295 
   1296 		while (XCheckTypedEvent(dpy, MappingNotify, &xev)) {
   1297 			XRefreshKeyboardMapping((XMappingEvent *) &xev);
   1298 			n++;
   1299 		}
   1300 		if (n && use_modifier_tweak) {
   1301 			X_UNLOCK;
   1302 			initialize_modtweak();
   1303 			X_LOCK;
   1304 		}
   1305 		if (xtrap_base_event_type) {
   1306 			int base = xtrap_base_event_type;
   1307 			while (XCheckTypedEvent(dpy, base, &xev)) {
   1308 				;
   1309 			}
   1310 		}
   1311 		if (xtest_base_event_type) {
   1312 			int base = xtest_base_event_type;
   1313 			while (XCheckTypedEvent(dpy, base, &xev)) {
   1314 				;
   1315 			}
   1316 		}
   1317 		/*
   1318 		 * we can get ClientMessage from our XSendEvent() call in
   1319 		 * selection_request().
   1320 		 */
   1321 		while (XCheckTypedEvent(dpy, ClientMessage, &xev)) {
   1322 			;
   1323 		}
   1324 
   1325 		XSetErrorHandler(old_handler);
   1326 		trapped_xerror = 0;
   1327 		last_call = now;
   1328 	}
   1329 
   1330 	if (freeze_when_obscured) {
   1331 		if (XCheckTypedEvent(dpy, VisibilityNotify, &xev)) {
   1332 			if (xev.type == VisibilityNotify && xev.xany.window == subwin) {
   1333 				int prev = subwin_obscured;
   1334 				if (xev.xvisibility.state == VisibilityUnobscured) {
   1335 					subwin_obscured = 0;
   1336 				} else if (xev.xvisibility.state == VisibilityPartiallyObscured) {
   1337 					subwin_obscured = 1;
   1338 				} else {
   1339 					subwin_obscured = 2;
   1340 				}
   1341 				rfbLog("subwin_obscured: %d -> %d\n", prev, subwin_obscured);
   1342 			}
   1343 		}
   1344 	}
   1345 
   1346 	/* check for CUT_BUFFER0, VNC_CONNECT, X11VNC_REMOTE changes: */
   1347 	if (XCheckTypedEvent(dpy, PropertyNotify, &xev)) {
   1348 		int got_cutbuffer = 0;
   1349 		int got_vnc_connect = 0;
   1350 		int got_x11vnc_remote = 0;
   1351 		static int prop_dbg = -1;
   1352 
   1353 		/* to avoid piling up between calls, read all PropertyNotify now */
   1354 		do {
   1355 			if (xev.type == PropertyNotify) {
   1356 				if (xev.xproperty.atom == XA_CUT_BUFFER0) {
   1357 					got_cutbuffer++;
   1358 				} else if (vnc_connect && vnc_connect_prop != None
   1359 				    && xev.xproperty.atom == vnc_connect_prop) {
   1360 					got_vnc_connect++;
   1361 				} else if (vnc_connect && x11vnc_remote_prop != None
   1362 				    && xev.xproperty.atom == x11vnc_remote_prop) {
   1363 					got_x11vnc_remote++;
   1364 				}
   1365 				set_prop_atom(xev.xproperty.atom);
   1366 			}
   1367 		} while (XCheckTypedEvent(dpy, PropertyNotify, &xev));
   1368 
   1369 		if (prop_dbg < 0) {
   1370 			prop_dbg = 0;
   1371 			if (getenv("PROP_DBG")) {
   1372 				prop_dbg = 1;
   1373 			}
   1374 		}
   1375 
   1376 		if (prop_dbg && (got_cutbuffer > 1 || got_vnc_connect > 1 || got_x11vnc_remote > 1)) {
   1377 			static double lastmsg = 0.0;
   1378 			static int count = 0;
   1379 			double now = dnow();
   1380 
   1381 			if (1 && now > lastmsg + 300.0) {
   1382 				if (got_cutbuffer > 1) {
   1383 					rfbLog("check_xevents: warning: %d cutbuffer events since last check.\n", got_cutbuffer);
   1384 				}
   1385 				if (got_vnc_connect > 1) {
   1386 					rfbLog("check_xevents: warning: %d vnc_connect events since last check.\n", got_vnc_connect);
   1387 				}
   1388 				if (got_x11vnc_remote > 1) {
   1389 					rfbLog("check_xevents: warning: %d x11vnc_remote events since last check.\n", got_x11vnc_remote);
   1390 				}
   1391 				count++;
   1392 				if (count >= 3) {
   1393 					lastmsg = now;
   1394 					count = 0;
   1395 				}
   1396 			}
   1397 		}
   1398 
   1399 		if (got_cutbuffer)  {
   1400 			/*
   1401 			 * Go retrieve CUT_BUFFER0 and send it.
   1402 			 *
   1403 			 * set_cutbuffer is a flag to try to avoid
   1404 			 * processing our own cutbuffer changes.
   1405 			 */
   1406 			if (have_clients && watch_selection && !set_cutbuffer) {
   1407 				cutbuffer_send();
   1408 				sent_some_sel = 1;
   1409 			}
   1410 			set_cutbuffer = 0;
   1411 		}
   1412 		if (got_vnc_connect) {
   1413 			/*
   1414 			 * Go retrieve VNC_CONNECT string.
   1415 			 */
   1416 			read_vnc_connect_prop(0);
   1417 		}
   1418 		if (got_x11vnc_remote) {
   1419 			/*
   1420 			 * Go retrieve X11VNC_REMOTE string.
   1421 			 */
   1422 			read_x11vnc_remote_prop(0);
   1423 		}
   1424 	}
   1425 
   1426 	/* do this now that we have just cleared PropertyNotify */
   1427 	tmp = 0;
   1428 	if (rfac() < 0.6) {
   1429 		tmp = 1;
   1430 	}
   1431 	if (now > last_time_sync + sync_tod_delay + tmp) {
   1432 		sync_tod_with_servertime();
   1433 		last_time_sync = now;
   1434 	}
   1435 
   1436 #if LIBVNCSERVER_HAVE_LIBXRANDR
   1437 	if (xrandr || xrandr_maybe) {
   1438 		check_xrandr_event("check_xevents");
   1439 	}
   1440 #endif
   1441 #if LIBVNCSERVER_HAVE_LIBXFIXES
   1442 	if (xfixes_present && use_xfixes && xfixes_first_initialized && xfixes_base_event_type) {
   1443 		if (XCheckTypedEvent(dpy, xfixes_base_event_type +
   1444 		    XFixesCursorNotify, &xev)) {
   1445 			got_xfixes_cursor_notify++;
   1446 		}
   1447 	}
   1448 #endif
   1449 
   1450 	/* check for our PRIMARY request notification: */
   1451 	if (watch_primary || watch_clipboard) {
   1452 		int doprimary = 1, doclipboard = 2, which, own = 0;
   1453 		double delay = 1.0;
   1454 		Atom atom;
   1455 		char *req;
   1456 
   1457 		if (XCheckTypedEvent(dpy, SelectionNotify, &xev)) {
   1458 			if (xev.type == SelectionNotify &&
   1459 			    xev.xselection.requestor == selwin &&
   1460 			    xev.xselection.property != None &&
   1461 			    xev.xselection.target == XA_STRING) {
   1462 				Atom s = xev.xselection.selection;
   1463 			        if (s == XA_PRIMARY || s == clipboard_atom) {
   1464 					/* go retrieve it and check it */
   1465 					if (now > last_client + sel_waittime
   1466 					    || sent_some_sel) {
   1467 						selection_send(&xev);
   1468 					}
   1469 				}
   1470 			}
   1471 		}
   1472 		/*
   1473 		 * Every second or so, request PRIMARY or CLIPBOARD,
   1474 		 * unless we already own it or there is no owner or we
   1475 		 * have no clients.
   1476 		 * TODO: even at this low rate we should look into
   1477 		 * and performance problems in odds cases (large text,
   1478 		 * modem, etc.)
   1479 		 */
   1480 		which = 0;
   1481 		if (watch_primary && watch_clipboard && ! own_clipboard &&
   1482 		    ! own_primary) {
   1483 			delay = 0.6;
   1484 		}
   1485 		if (dnow() > last_request + delay) {
   1486 			/*
   1487 			 * It is not a good idea to do both at the same
   1488 			 * time so we must choose one:
   1489 			 */
   1490 			if (watch_primary && watch_clipboard) {
   1491 				static int count = 0;
   1492 				if (own_clipboard) {
   1493 					which = doprimary;
   1494 				} else if (own_primary) {
   1495 					which = doclipboard;
   1496 				} else if (count++ % 3 == 0) {
   1497 					which = doclipboard;
   1498 				} else {
   1499 					which = doprimary;
   1500 				}
   1501 			} else if (watch_primary) {
   1502 				which = doprimary;
   1503 			} else if (watch_clipboard) {
   1504 				which = doclipboard;
   1505 			}
   1506 			last_request = dnow();
   1507 		}
   1508 		atom = None;
   1509 		req = "none";
   1510 		if (which == doprimary) {
   1511 			own = own_primary;
   1512 			atom = XA_PRIMARY;
   1513 			req = "PRIMARY";
   1514 		} else if (which == doclipboard) {
   1515 			own = own_clipboard;
   1516 			atom = clipboard_atom;
   1517 			req = "CLIPBOARD";
   1518 		}
   1519 		if (which != 0 && ! own && have_clients &&
   1520 		    XGetSelectionOwner(dpy, atom) != None && selwin != None) {
   1521 			XConvertSelection(dpy, atom, XA_STRING, XA_STRING,
   1522 			    selwin, CurrentTime);
   1523 			if (debug_sel) {
   1524 				rfbLog("request %s\n", req);
   1525 			}
   1526 		}
   1527 	}
   1528 
   1529 	if (own_primary || own_clipboard) {
   1530 		/* we own PRIMARY or CLIPBOARD, see if someone requested it: */
   1531 		trapped_xerror = 0;
   1532 		old_handler = XSetErrorHandler(trap_xerror);
   1533 
   1534 		if (XCheckTypedEvent(dpy, SelectionRequest, &xev)) {
   1535 			if (own_primary && xev.type == SelectionRequest &&
   1536 			    xev.xselectionrequest.selection == XA_PRIMARY) {
   1537 				selection_request(&xev, "PRIMARY");
   1538 			}
   1539 			if (own_clipboard && xev.type == SelectionRequest &&
   1540 			    xev.xselectionrequest.selection == clipboard_atom) {
   1541 				selection_request(&xev, "CLIPBOARD");
   1542 			}
   1543 		}
   1544 
   1545 		/* we own PRIMARY or CLIPBOARD, see if we no longer own it: */
   1546 		if (XCheckTypedEvent(dpy, SelectionClear, &xev)) {
   1547 			if (own_primary && xev.type == SelectionClear &&
   1548 			    xev.xselectionclear.selection == XA_PRIMARY) {
   1549 				own_primary = 0;
   1550 				if (xcut_str_primary) {
   1551 					free(xcut_str_primary);
   1552 					xcut_str_primary = NULL;
   1553 				}
   1554 				if (debug_sel) {
   1555 					rfbLog("Released PRIMARY.\n");
   1556 				}
   1557 			}
   1558 			if (own_clipboard && xev.type == SelectionClear &&
   1559 			    xev.xselectionclear.selection == clipboard_atom) {
   1560 				own_clipboard = 0;
   1561 				if (xcut_str_clipboard) {
   1562 					free(xcut_str_clipboard);
   1563 					xcut_str_clipboard = NULL;
   1564 				}
   1565 				if (debug_sel) {
   1566 					rfbLog("Released CLIPBOARD.\n");
   1567 				}
   1568 			}
   1569 		}
   1570 
   1571 		XSetErrorHandler(old_handler);
   1572 		trapped_xerror = 0;
   1573 	}
   1574 
   1575 	if (watch_bell || now > last_bell+1) {
   1576 		last_bell = now;
   1577 		check_bell_event();
   1578 	}
   1579 	if (tray_request != None) {
   1580 		static time_t last_tray_request = 0;
   1581 		if (now > last_tray_request + 2) {
   1582 			last_tray_request = now;
   1583 			if (tray_embed(tray_request, tray_unembed)) {
   1584 				tray_window = tray_request;
   1585 				tray_request = None;
   1586 			}
   1587 		}
   1588 	}
   1589 
   1590 #ifndef DEBUG_XEVENTS
   1591 #define DEBUG_XEVENTS 1
   1592 #endif
   1593 #if DEBUG_XEVENTS
   1594 	if (debug_xevents) {
   1595 		static time_t last_check = 0;
   1596 		static time_t reminder = 0;
   1597 		static int freq = 0;
   1598 
   1599 		if (! freq) {
   1600 			if (getenv("X11VNC_REMINDER_RATE")) {
   1601 				freq = atoi(getenv("X11VNC_REMINDER_RATE"));
   1602 			} else {
   1603 				freq = 300;
   1604 			}
   1605 		}
   1606 
   1607 		if (now > last_check + 1) {
   1608 			int ev_type_max = 300, ev_size = 400;
   1609 			XEvent xevs[400];
   1610 			int i, tot = XEventsQueued(dpy, QueuedAlready);
   1611 
   1612 			if (reminder == 0 || (tot && now > reminder + freq)) {
   1613 				print_xevent_bases();
   1614 				reminder = now;
   1615 			}
   1616 			last_check = now;
   1617 
   1618 			if (tot) {
   1619 		    		fprintf(stderr, "Total events queued: %d\n",
   1620 				    tot);
   1621 			}
   1622 			for (i=1; i<ev_type_max; i++) {
   1623 				int k, n = 0;
   1624 				while (XCheckTypedEvent(dpy, i, xevs+n)) {
   1625 					if (++n >= ev_size) {
   1626 						break;
   1627 					}
   1628 				}
   1629 				if (n) {
   1630 					fprintf(stderr, "  %d%s events of type"
   1631 					    " %d queued\n", n,
   1632 					    (n >= ev_size) ? "+" : "", i);
   1633 				}
   1634 				for (k=n-1; k >= 0; k--) {
   1635 					XPutBackEvent(dpy, xevs+k);
   1636 				}
   1637 			}
   1638 		}
   1639 	}
   1640 #endif
   1641 
   1642 	if (now > last_sync + 1200) {
   1643 		/* kludge for any remaining event leaks */
   1644 		int bugout = use_xdamage ? 500 : 50;
   1645 		int qlen, i;
   1646 		if (last_sync != 0) {
   1647 			qlen = XEventsQueued(dpy, QueuedAlready);
   1648 			if (qlen >= bugout) {
   1649 				rfbLog("event leak: %d queued, "
   1650 				    " calling XSync(dpy, True)\n", qlen);
   1651 				rfbLog("  for diagnostics run: 'x11vnc -R"
   1652 				    " debug_xevents:1'\n");
   1653 				XSync(dpy, True);
   1654 			}
   1655 		}
   1656 		last_sync = now;
   1657 
   1658 		/* clear these, we don't want any events on them */
   1659 		if (rdpy_ctrl) {
   1660 			qlen = XEventsQueued(rdpy_ctrl, QueuedAlready);
   1661 			for (i=0; i<qlen; i++) {
   1662 				XNextEvent(rdpy_ctrl, &xev);
   1663 			}
   1664 		}
   1665 		if (gdpy_ctrl) {
   1666 			qlen = XEventsQueued(gdpy_ctrl, QueuedAlready);
   1667 			for (i=0; i<qlen; i++) {
   1668 				XNextEvent(gdpy_ctrl, &xev);
   1669 			}
   1670 		}
   1671 	}
   1672 	X_UNLOCK;
   1673 
   1674 #endif	/* NO_X11 */
   1675 }
   1676 
   1677 extern int rawfb_vnc_reflect;
   1678 /*
   1679  * hook called when a VNC client sends us some "XCut" text (rfbClientCutText).
   1680  */
   1681 void xcut_receive(char *text, int len, rfbClientPtr cl) {
   1682 	allowed_input_t input;
   1683 
   1684 	if (threads_drop_input) {
   1685 		return;
   1686 	}
   1687 
   1688 	if (unixpw_in_progress) {
   1689 		rfbLog("xcut_receive: unixpw_in_progress, skipping.\n");
   1690 		return;
   1691 	}
   1692 
   1693 	if (!watch_selection) {
   1694 		return;
   1695 	}
   1696 	if (view_only) {
   1697 		return;
   1698 	}
   1699 	if (text == NULL || len == 0) {
   1700 		return;
   1701 	}
   1702 	get_allowed_input(cl, &input);
   1703 	if (!input.clipboard) {
   1704 		return;
   1705 	}
   1706 	INPUT_LOCK;
   1707 
   1708 	if (remote_prefix != NULL && strstr(text, remote_prefix) == text) {
   1709 		char *result, *rcmd = text + strlen(remote_prefix);
   1710 		char *tmp = (char *) calloc(len + 8, 1);
   1711 
   1712 		if (strstr(rcmd, "cmd=") != rcmd && strstr(rcmd, "qry=") != rcmd) {
   1713 			strcat(tmp, "qry=");
   1714 		}
   1715 		strncat(tmp, rcmd, len - strlen(remote_prefix));
   1716 		rfbLog("remote_prefix command: '%s'\n", tmp);
   1717 
   1718 		if (use_threads) {
   1719 			if (client_connect_file) {
   1720 				FILE *f = fopen(client_connect_file, "w");
   1721 				if (f) {
   1722 					fprintf(f, "%s\n", tmp);
   1723 					fclose(f);
   1724 					free(tmp);
   1725 					INPUT_UNLOCK;
   1726 					return;
   1727 				}
   1728 			}
   1729 			if (vnc_connect) {
   1730 				sprintf(x11vnc_remote_str, "%s", tmp);
   1731 				free(tmp);
   1732 				INPUT_UNLOCK;
   1733 				return;
   1734 			}
   1735 		}
   1736 		INPUT_UNLOCK;
   1737 
   1738 
   1739 		result = process_remote_cmd(tmp, 1);
   1740 		if (result == NULL ) {
   1741 			result = strdup("null");
   1742 		} else if (!strcmp(result, "")) {
   1743 			free(result);
   1744 			result = strdup("none");
   1745 		}
   1746 		rfbLog("remote_prefix result:  '%s'\n", result);
   1747 
   1748 		free(tmp);
   1749 		tmp = (char *) calloc(strlen(remote_prefix) + strlen(result) + 1, 1);
   1750 
   1751 		strcat(tmp, remote_prefix);
   1752 		strcat(tmp, result);
   1753 		free(result);
   1754 
   1755 		rfbSendServerCutText(screen, tmp, strlen(tmp));
   1756 		free(tmp);
   1757 
   1758 		return;
   1759 	}
   1760 
   1761 	if (! check_sel_direction("recv", "xcut_receive", text, len)) {
   1762 		INPUT_UNLOCK;
   1763 		return;
   1764 	}
   1765 
   1766 #ifdef MACOSX
   1767 	if (macosx_console) {
   1768 		macosx_set_sel(text, len);
   1769 		INPUT_UNLOCK;
   1770 		return;
   1771 	}
   1772 #endif
   1773 
   1774 	if (rawfb_vnc_reflect) {
   1775 		vnc_reflect_send_cuttext(text, len);
   1776 		INPUT_UNLOCK;
   1777 		return;
   1778 	}
   1779 
   1780 	RAWFB_RET_VOID
   1781 
   1782 #if NO_X11
   1783 	INPUT_UNLOCK;
   1784 	return;
   1785 #else
   1786 
   1787 	X_LOCK;
   1788 
   1789 	/* associate this text with PRIMARY (and SECONDARY...) */
   1790 	if (set_primary && ! own_primary && selwin != None) {
   1791 		own_primary = 1;
   1792 		/* we need to grab the PRIMARY selection */
   1793 		XSetSelectionOwner(dpy, XA_PRIMARY, selwin, CurrentTime);
   1794 		XFlush_wr(dpy);
   1795 		if (debug_sel) {
   1796 			rfbLog("Own PRIMARY.\n");
   1797 		}
   1798 	}
   1799 
   1800 	if (set_clipboard && ! own_clipboard && clipboard_atom != None && selwin != None) {
   1801 		own_clipboard = 1;
   1802 		/* we need to grab the CLIPBOARD selection */
   1803 		XSetSelectionOwner(dpy, clipboard_atom, selwin, CurrentTime);
   1804 		XFlush_wr(dpy);
   1805 		if (debug_sel) {
   1806 			rfbLog("Own CLIPBOARD.\n");
   1807 		}
   1808 	}
   1809 
   1810 	/* duplicate the text string for our own use. */
   1811 	if (set_primary) {
   1812 		if (xcut_str_primary != NULL) {
   1813 			free(xcut_str_primary);
   1814 			xcut_str_primary = NULL;
   1815 		}
   1816 		xcut_str_primary = (char *) malloc((size_t) (len+1));
   1817 		strncpy(xcut_str_primary, text, len);
   1818 		xcut_str_primary[len] = '\0';	/* make sure null terminated */
   1819 		if (debug_sel) {
   1820 			rfbLog("Set PRIMARY   '%s'\n", xcut_str_primary);
   1821 		}
   1822 	}
   1823 	if (set_clipboard) {
   1824 		if (xcut_str_clipboard != NULL) {
   1825 			free(xcut_str_clipboard);
   1826 			xcut_str_clipboard = NULL;
   1827 		}
   1828 		xcut_str_clipboard = (char *) malloc((size_t) (len+1));
   1829 		strncpy(xcut_str_clipboard, text, len);
   1830 		xcut_str_clipboard[len] = '\0';	/* make sure null terminated */
   1831 		if (debug_sel) {
   1832 			rfbLog("Set CLIPBOARD '%s'\n", xcut_str_clipboard);
   1833 		}
   1834 	}
   1835 
   1836 	/* copy this text to CUT_BUFFER0 as well: */
   1837 	XChangeProperty(dpy, rootwin, XA_CUT_BUFFER0, XA_STRING, 8,
   1838 	    PropModeReplace, (unsigned char *) text, len);
   1839 	XFlush_wr(dpy);
   1840 
   1841 	X_UNLOCK;
   1842 	INPUT_UNLOCK;
   1843 
   1844 	set_cutbuffer = 1;
   1845 #endif	/* NO_X11 */
   1846 }
   1847 
   1848 void kbd_release_all_keys(rfbClientPtr cl) {
   1849 	if (unixpw_in_progress) {
   1850 		rfbLog("kbd_release_all_keys: unixpw_in_progress, skipping.\n");
   1851 		return;
   1852 	}
   1853 	if (cl->viewOnly) {
   1854 		return;
   1855 	}
   1856 
   1857 	RAWFB_RET_VOID
   1858 
   1859 #if NO_X11
   1860 	return;
   1861 #else
   1862 	if (use_threads) {
   1863 		X_LOCK;
   1864 	}
   1865 
   1866 	clear_keys();
   1867 	clear_modifiers(0);
   1868 
   1869 	if (use_threads) {
   1870 		X_UNLOCK;
   1871 	}
   1872 #endif
   1873 }
   1874 
   1875 void set_single_window(rfbClientPtr cl, int x, int y) {
   1876 	int ok = 0;
   1877 	if (no_ultra_ext) {
   1878 		return;
   1879 	}
   1880 	if (unixpw_in_progress) {
   1881 		rfbLog("set_single_window: unixpw_in_progress, dropping client.\n");
   1882 		rfbCloseClient(cl);
   1883 		return;
   1884 	}
   1885 	if (cl->viewOnly) {
   1886 		return;
   1887 	}
   1888 
   1889 	RAWFB_RET_VOID
   1890 
   1891 #if NO_X11
   1892 	return;
   1893 #else
   1894 	if (x==1 && y==1) {
   1895 		if (subwin) {
   1896 			subwin = 0x0;
   1897 			ok = 1;
   1898 		}
   1899 	} else {
   1900 		Window r, c;
   1901 		int rootx, rooty, wx, wy;
   1902 		unsigned int mask;
   1903 
   1904 		update_x11_pointer_position(x, y);
   1905 		XSync(dpy, False);
   1906 
   1907 		if (XQueryPointer_wr(dpy, rootwin, &r, &c, &rootx, &rooty,
   1908 		    &wx, &wy, &mask)) {
   1909 			if (c != None) {
   1910 				subwin = c;
   1911 				ok = 1;
   1912 			}
   1913 		}
   1914 	}
   1915 
   1916 	if (ok) {
   1917 		check_black_fb();
   1918 		do_new_fb(1);
   1919 	}
   1920 #endif
   1921 
   1922 }
   1923 void set_server_input(rfbClientPtr cl, int grab) {
   1924 	if (no_ultra_ext) {
   1925 		return;
   1926 	}
   1927 	if (unixpw_in_progress) {
   1928 		rfbLog("set_server_input: unixpw_in_progress, dropping client.\n");
   1929 		rfbCloseClient(cl);
   1930 		return;
   1931 	}
   1932 	if (cl->viewOnly) {
   1933 		return;
   1934 	}
   1935 
   1936 	RAWFB_RET_VOID
   1937 
   1938 #if NO_X11
   1939 	return;
   1940 #else
   1941 	if (grab) {
   1942 		if (!no_ultra_dpms) {
   1943 			set_dpms_mode("enable");
   1944 			set_dpms_mode("off");
   1945 			force_dpms = 1;
   1946 		}
   1947 
   1948 		process_remote_cmd("cmd=grabkbd", 0);
   1949 		process_remote_cmd("cmd=grabptr", 0);
   1950 
   1951 	} else {
   1952 		process_remote_cmd("cmd=nograbkbd", 0);
   1953 		process_remote_cmd("cmd=nograbptr", 0);
   1954 
   1955 		if (!no_ultra_dpms) {
   1956 			force_dpms = 0;
   1957 		}
   1958 	}
   1959 #endif
   1960 }
   1961 
   1962 static int wsock_timeout_sock = -1;
   1963 
   1964 static void wsock_timeout (int sig) {
   1965 	rfbLog("sig: %d, wsock_timeout.\n", sig);
   1966 	if (wsock_timeout_sock >= 0) {
   1967 		close(wsock_timeout_sock);
   1968 		wsock_timeout_sock = -1;
   1969 	}
   1970 }
   1971 
   1972 static void try_local_chat_window(void) {
   1973 	int i, port, lsock;
   1974 	char cmd[100];
   1975 	struct sockaddr_in addr;
   1976 	pid_t pid = -1;
   1977 #ifdef __hpux
   1978 	int addrlen = sizeof(addr);
   1979 #else
   1980 	socklen_t addrlen = sizeof(addr);
   1981 #endif
   1982 
   1983 	for (i = 0; i < 90; i++)  {
   1984 		/* find an open port */
   1985 		port = 7300 + i;
   1986 		/* XXX ::1 fallback */
   1987 		lsock = listen_tcp(port, htonl(INADDR_LOOPBACK), 0);
   1988 		if (lsock >= 0) {
   1989 			break;
   1990 		}
   1991 		port = 0;
   1992 	}
   1993 
   1994 	if (port == 0) {
   1995 		return;
   1996 	}
   1997 
   1998 	/* have ssvncvncviewer connect back to us (n.b. sockpair fails) */
   1999 
   2000 	sprintf(cmd, "ssvnc -cmd VNC://localhost:%d -chatonly", port);
   2001 
   2002 #if LIBVNCSERVER_HAVE_FORK
   2003 	pid = fork();
   2004 #endif
   2005 
   2006 	if (pid == -1) {
   2007 		perror("fork");
   2008 		return;
   2009 	} else if (pid == 0) {
   2010 		char *args[4];
   2011 		int d;
   2012 		args[0] = "/bin/sh";
   2013 		args[1] = "-c";
   2014 		/* "ssvnc -cmd VNC://fd=0 -chatonly"; not working */
   2015 		args[2] = cmd;
   2016 		args[3] = NULL;
   2017 
   2018 		set_env("VNCVIEWER_PASSWORD", "moo");
   2019 #if !NO_X11
   2020 		if (dpy != NULL) {
   2021 			set_env("DISPLAY", DisplayString(dpy));
   2022 		}
   2023 #endif
   2024 		for (d = 3; d < 256; d++) {
   2025 			close(d);
   2026 		}
   2027 
   2028 		execvp(args[0], args);
   2029 		perror("exec");
   2030 		exit(1);
   2031 	} else {
   2032 		int i, sock = -1;
   2033 		rfbNewClientHookPtr new_save;
   2034 
   2035 		signal(SIGALRM, wsock_timeout);
   2036 		wsock_timeout_sock = lsock;
   2037 
   2038 		alarm(10);
   2039 		sock = accept(lsock, (struct sockaddr *)&addr, &addrlen);
   2040 		alarm(0);
   2041 
   2042 		signal(SIGALRM, SIG_DFL);
   2043 		close(lsock);
   2044 
   2045 		if (sock < 0) {
   2046 			return;
   2047 		}
   2048 
   2049 		/* mutex */
   2050 		new_save = screen->newClientHook;
   2051 		screen->newClientHook = new_client_chat_helper;
   2052 
   2053 		chat_window_client = create_new_client(sock, 1);
   2054 
   2055 		screen->newClientHook = new_save;
   2056 
   2057 		if (chat_window_client != NULL) {
   2058 			rfbPasswordCheckProcPtr pwchk_save = screen->passwordCheck;
   2059 			rfbBool save_shared1 = screen->alwaysShared;
   2060 			rfbBool save_shared2 = screen->neverShared;
   2061 
   2062 			screen->alwaysShared = TRUE;
   2063 			screen->neverShared  = FALSE;
   2064 
   2065 			screen->passwordCheck = password_check_chat_helper;
   2066 			for (i=0; i<30; i++) {
   2067 				rfbPE(-1);
   2068 				if (!chat_window_client) {
   2069 					break;
   2070 				}
   2071 				if (chat_window_client->state == RFB_NORMAL) {
   2072 					break;
   2073 				}
   2074 			}
   2075 
   2076 			screen->passwordCheck = pwchk_save;
   2077 			screen->alwaysShared  = save_shared1;
   2078 			screen->neverShared   = save_shared2;
   2079 		}
   2080 	}
   2081 }
   2082 
   2083 void set_text_chat(rfbClientPtr cl, int len, char *txt) {
   2084 	int dochat = 1;
   2085 	rfbClientIteratorPtr iter;
   2086 	rfbClientPtr cl2;
   2087 	unsigned int ulen = (unsigned int) len;
   2088 
   2089 	if (no_ultra_ext || ! dochat) {
   2090 		return;
   2091 	}
   2092 
   2093 	if (unixpw_in_progress) {
   2094 		rfbLog("set_text_chat: unixpw_in_progress, dropping client.\n");
   2095 		rfbCloseClient(cl);
   2096 		return;
   2097 	}
   2098 #if LIBVNCSERVER_HAS_TEXTCHAT
   2099 
   2100 	if (chat_window && chat_window_client == NULL && ulen == rfbTextChatOpen) {
   2101 		try_local_chat_window();
   2102 	}
   2103 
   2104 	saw_ultra_chat = 1;
   2105 
   2106 	iter = rfbGetClientIterator(screen);
   2107 	while( (cl2 = rfbClientIteratorNext(iter)) ) {
   2108 		unsigned int ulen = (unsigned int) len;
   2109 		if (cl2 == cl) {
   2110 			continue;
   2111 		}
   2112 		if (cl2->state != RFB_NORMAL) {
   2113 			continue;
   2114 		}
   2115 
   2116 		SEND_LOCK(cl2);
   2117 
   2118 		if (ulen == rfbTextChatOpen) {
   2119 			rfbSendTextChatMessage(cl2, rfbTextChatOpen, "");
   2120 		} else if (ulen == rfbTextChatClose) {
   2121 			rfbSendTextChatMessage(cl2, rfbTextChatClose, "");
   2122 			/* not clear what is going on WRT close and finished... */
   2123 			rfbSendTextChatMessage(cl2, rfbTextChatFinished, "");
   2124 		} else if (ulen == rfbTextChatFinished) {
   2125 			rfbSendTextChatMessage(cl2, rfbTextChatFinished, "");
   2126 		} else if (len <= rfbTextMaxSize) {
   2127 			rfbSendTextChatMessage(cl2, len, txt);
   2128 		}
   2129 
   2130 		SEND_UNLOCK(cl2);
   2131 	}
   2132 	rfbReleaseClientIterator(iter);
   2133 
   2134 	if (ulen == rfbTextChatClose && cl != NULL) {
   2135 		/* not clear what is going on WRT close and finished... */
   2136 		SEND_LOCK(cl);
   2137 		rfbSendTextChatMessage(cl, rfbTextChatFinished, "");
   2138 		SEND_UNLOCK(cl);
   2139 	}
   2140 #endif
   2141 }
   2142 
   2143 int get_keyboard_led_state_hook(rfbScreenInfoPtr s) {
   2144 	if (s) {}
   2145 	if (unixpw_in_progress) {
   2146 		rfbLog("get_keyboard_led_state_hook: unixpw_in_progress, skipping.\n");
   2147 		return 0;
   2148 	}
   2149 	return 0;
   2150 }
   2151 int get_file_transfer_permitted(rfbClientPtr cl) {
   2152 	allowed_input_t input;
   2153 	if (unixpw_in_progress) {
   2154 		rfbLog("get_file_transfer_permitted: unixpw_in_progress, dropping client.\n");
   2155 		rfbCloseClient(cl);
   2156 		return FALSE;
   2157 	}
   2158 if (0) fprintf(stderr, "get_file_transfer_permitted called\n");
   2159 	if (view_only) {
   2160 		return FALSE;
   2161 	}
   2162 	if (cl->viewOnly) {
   2163 		return FALSE;
   2164 	}
   2165 	get_allowed_input(cl, &input);
   2166 	if (!input.files) {
   2167 		return FALSE;
   2168 	}
   2169 	if (screen->permitFileTransfer) {
   2170 		saw_ultra_file = 1;
   2171 	}
   2172 	return screen->permitFileTransfer;
   2173 }
   2174 
   2175 
   2176