blob: 72b651598746c24a58a42beea32a2eba2087e399 [file] [log] [blame]
San Mehata430b2b2014-09-23 08:30:51 -07001/*
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