1/*
2   Copyright (C) 2002-2010 Karl J. Runge <runge@karlrunge.com>
3   All rights reserved.
4
5This file is part of x11vnc.
6
7x11vnc is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 2 of the License, or (at
10your option) any later version.
11
12x11vnc is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with x11vnc; if not, write to the Free Software
19Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
20or see <http://www.gnu.org/licenses/>.
21
22In addition, as a special exception, Karl J. Runge
23gives permission to link the code of its release of x11vnc with the
24OpenSSL project's "OpenSSL" library (or with modified versions of it
25that use the same license as the "OpenSSL" library), and distribute
26the linked executables.  You must obey the GNU General Public License
27in all respects for all of the code used other than "OpenSSL".  If you
28modify this file, you may extend this exception to your version of the
29file, but you are not obligated to do so.  If you do not wish to do
30so, 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 */
56int grab_buster = 0;
57int grab_kbd = 0;
58int grab_ptr = 0;
59int grab_always = 0;
60int ungrab_both = 0;
61int grab_local = 0;
62int sync_tod_delay = 20;
63
64void initialize_vnc_connect_prop(void);
65void initialize_x11vnc_remote_prop(void);
66void initialize_clipboard_atom(void);
67void spawn_grab_buster(void);
68void sync_tod_with_servertime(void);
69void check_keycode_state(void);
70void check_autorepeat(void);
71void set_prop_atom(Atom atom);
72void check_xevents(int reset);
73void xcut_receive(char *text, int len, rfbClientPtr cl);
74
75void kbd_release_all_keys(rfbClientPtr cl);
76void set_single_window(rfbClientPtr cl, int x, int y);
77void set_server_input(rfbClientPtr cl, int s);
78void set_text_chat(rfbClientPtr cl, int l, char *t);
79int get_keyboard_led_state_hook(rfbScreenInfoPtr s);
80int get_file_transfer_permitted(rfbClientPtr cl);
81void get_prop(char *str, int len, Atom prop, Window w);
82int guess_dm_gone(int t1, int t2);
83
84static void initialize_xevents(int reset);
85static void print_xevent_bases(void);
86static void bust_grab(int reset);
87static int process_watch(char *str, int parent, int db);
88static void grab_buster_watch(int parent, char *dstr);
89
90
91void 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
104void 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
117void 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
157static 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
179static 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
241static 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
286int 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
337static 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
436static 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
448void 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
496static 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
592typedef struct _grabwatch {
593	int pid;
594	int tick;
595	unsigned long time;
596	time_t change;
597} grabwatch_t;
598#define GRABWATCH 16
599
600static int grab_buster_delay = 20;
601static pid_t grab_buster_pid = 0;
602
603static int grab_npids = 1;
604
605static 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
757static 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
837void 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
875void 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
950void 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
984void 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
1067void 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
1191void 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 */
1203void 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
1677extern int rawfb_vnc_reflect;
1678/*
1679 * hook called when a VNC client sends us some "XCut" text (rfbClientCutText).
1680 */
1681void 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
1848void 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
1875void 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}
1923void 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
1962static int wsock_timeout_sock = -1;
1963
1964static 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
1972static 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
2083void 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
2143int 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}
2151int 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	}
2158if (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