1 /*
2 Copyright (C) 2002-2010 Karl J. Runge <runge@karlrunge.com>
3 All rights reserved.
4
5 This file is part of x11vnc.
6
7 x11vnc is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or (at
10 your option) any later version.
11
12 x11vnc is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with x11vnc; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
20 or see <http://www.gnu.org/licenses/>.
21
22 In addition, as a special exception, Karl J. Runge
23 gives permission to link the code of its release of x11vnc with the
24 OpenSSL project's "OpenSSL" library (or with modified versions of it
25 that use the same license as the "OpenSSL" library), and distribute
26 the linked executables. You must obey the GNU General Public License
27 in all respects for all of the code used other than "OpenSSL". If you
28 modify this file, you may extend this exception to your version of the
29 file, but you are not obligated to do so. If you do not wish to do
30 so, delete this exception statement from your version.
31 */
32
33 /* -- appshare.c -- */
34
35 #include "x11vnc.h"
36
37 extern int pick_windowid(unsigned long *num);
38 extern char *get_xprop(char *prop, Window win);
39 extern int set_xprop(char *prop, Window win, char *value);
40 extern void set_env(char *name, char *value);
41 extern double dnow(void);
42
43 static char *usage =
44 "\n"
45 " x11vnc -appshare: an experiment in application sharing via x11vnc.\n"
46 "\n"
47 #if !SMALL_FOOTPRINT
48 " Usage: x11vnc -appshare -id windowid -connect viewer_host:0\n"
49 " x11vnc -appshare -id pick -connect viewer_host:0\n"
50 "\n"
51 " Both the -connect option and the -id (or -sid) option are required.\n"
52 " (However see the -control option below that can replace -connect.)\n"
53 "\n"
54 " The VNC viewer at viewer_host MUST be in 'listen' mode. This is because\n"
55 " a new VNC connection (and viewer window) is established for each new\n"
56 " toplevel window that the application creates. For example:\n"
57 "\n"
58 " vncviewer -listen 0\n"
59 "\n"
60 " The '-connect viewer_host:0' indicates the listening viewer to connect to.\n"
61 "\n"
62 " No password should be used, otherwise it will need to be typed for each\n"
63 " new window (or one could use vncviewer -passwd file if the viewer supports\n"
64 " that.) For security an SSH tunnel can be used:\n"
65 "\n"
66 " ssh -R 5500:localhost:5500 user@server_host\n"
67 "\n"
68 " (then use -connect localhost:0)\n"
69 "\n"
70 " The -id/-sid option is as in x11vnc(1). It is either a numerical window\n"
71 " id or the string 'pick' which will ask the user to click on an app window.\n"
72 " To track more than one application at the same time, list their window ids\n"
73 " separated by commas (see also the 'add_app' command below.)\n"
74 "\n"
75 " Additional options:\n"
76 "\n"
77 " -h, -help Print this help.\n"
78 " -debug Print debugging output (same as X11VNC_APPSHARE_DEBUG=1)\n"
79 " -showmenus Create a new viewer window even if a new window is\n"
80 " completely inside of an existing one. Default is to\n"
81 " try to not show them in a new viewer window.\n"
82 " -noexit Do not exit if the main app (windowid/pick) window\n"
83 " goes away. Default is to exit.\n"
84 " -display dpy X DISPLAY to use.\n"
85 " -trackdir dir Set tracking directory to 'dir'. x11vnc -appshare does\n"
86 " better if it can communicate with the x11vnc's via a\n"
87 " file channel. By default a dir in /tmp is used, -trackdir\n"
88 " specifies another directory, or use 'none' to disable.\n"
89 " -args 'string' Pass options 'string' to x11vnc (e.g. -scale 3/4,\n"
90 " -viewonly, -wait, -once, etc.)\n"
91 " -env VAR=VAL Set environment variables on cmdline as in x11vnc.\n"
92 "\n"
93 " -control file This is a file that one edits to manage the appshare\n"
94 " mode. It replaces -connect. Lines beginning with '#'\n"
95 " are ignored. Initially start off with all of the\n"
96 " desired clients in the file, one per line. If you add\n"
97 " a new client-line, that client is connected to. If you\n"
98 " delete (or comment out) a client-line, that client is\n"
99 " disconnected (for this to work, do not disable trackdir.)\n"
100 "\n"
101 " You can also put cmd= lines in the control file to perform\n"
102 " different actions. These are supported:\n"
103 "\n"
104 " cmd=quit Disconnect all clients and exit.\n"
105 " cmd=restart Restart all of the x11vnc's.\n"
106 " cmd=noop Do nothing (e.g. ping)\n"
107 " cmd=x11vnc Run ps(1) looking for x11vnc's\n"
108 " cmd=help Print out help text.\n"
109 " cmd=add_window:win Add a window to be watched.\n"
110 " cmd=del_window:win Delete a window.\n"
111 " cmd=add_app:win Add an application to be watched.\n"
112 " cmd=del_app:win Delete an application.\n"
113 " cmd=add_client:host Add client ('internal' mode only)\n"
114 " cmd=del_client:host Del client ('internal' mode only)\n"
115 " cmd=list_windows List all tracked windows.\n"
116 " cmd=list_apps List all tracked applications.\n"
117 " cmd=list_clients List all connected clients.\n"
118 " cmd=list_all List all three.\n"
119 " cmd=print_logs Print out the x11vnc logfiles.\n"
120 " cmd=debug:n Set -debug to n (0 or 1).\n"
121 " cmd=showmenus:n Set -showmenus to n (0 or 1).\n"
122 " cmd=noexit:n Set -noexit to n (0 or 1).\n"
123 "\n"
124 " See the '-command internal' mode described below for a way\n"
125 " that tracks connected clients internally (not in a file.)\n"
126 "\n"
127 " In '-shell' mode (see below) you can type in the above\n"
128 " without the leading 'cmd='.\n"
129 "\n"
130 " For 'add_window' and 'del_window' the 'win' can be a\n"
131 " numerical window id or 'pick'. Same for 'add_app'. Be\n"
132 " sure to remove or comment out the add/del line quickly\n"
133 " (e.g. before picking) or it will be re-run the next time\n"
134 " the file is processed.\n"
135 "\n"
136 " If a file with the same name as the control file but\n"
137 " ending with suffix '.cmd' is found, then commands in it\n"
138 " (cmd=...) are processed and then the file is truncated.\n"
139 " This allows 'one time' command actions to be run. Any\n"
140 " client hostnames in the '.cmd' file are ignored. Also\n"
141 " see below for the X11VNC_APPSHARE_COMMAND X property\n"
142 " which is similar to '.cmd'\n"
143 "\n"
144 " -control internal Manage connected clients internally, see below.\n"
145 " -control shell Same as: -shell -control internal\n"
146 "\n"
147 " -delay secs Maximum timeout delay before re-checking the control file.\n"
148 " It can be a fraction, e.g. -delay 0.25 Default 0.5\n"
149 "\n"
150 " -shell Simple command line for '-control internal' mode (see the\n"
151 " details of this mode below.) Enter '?' for command list.\n"
152 "\n"
153 " To stop x11vnc -appshare press Ctrl-C, or (if -noexit not supplied) delete\n"
154 " the initial app window or exit the application. Or cmd=quit in -control mode.\n"
155 "\n"
156 #if 0
157 " If you want your setup to survive periods of time where there are no clients\n"
158 " connected you will need to supply -args '-forever' otherwise the x11vnc's\n"
159 " will exit when the last client disconnects. Howerver, _starting_ with no\n"
160 " clients (e.g. empty control file) will work without -args '-forever'.\n"
161 "\n"
162 #endif
163 " In addition to the '.cmd' file channel, for faster response you can set\n"
164 " X11VNC_APPSHARE_COMMAND X property on the root window to the string that\n"
165 " would go into the '.cmd' file. For example:\n"
166 "\n"
167 " xprop -root -f X11VNC_APPSHARE_COMMAND 8s -set X11VNC_APPSHARE_COMMAND cmd=quit\n"
168 "\n"
169 " The property value will be set to 'DONE' after the command(s) is processed.\n"
170 "\n"
171 " If -control file is specified as 'internal' then no control file is used\n"
172 " and client tracking is done internally. You must add and delete clients\n"
173 " with the cmd=add_client:<client> and cmd=del_client:<client> commands.\n"
174 " Note that '-control internal' is required for '-shell' mode. Using\n"
175 " '-control shell' implies internal mode and -shell.\n"
176 "\n"
177 " Limitations:\n"
178 "\n"
179 " This is a quick lash-up, many things will not work properly.\n"
180 "\n"
181 " The main idea is to provide simple application sharing for two or more\n"
182 " parties to collaborate without needing to share the entire desktop. It\n"
183 " provides an improvement over -id/-sid that only shows a single window.\n"
184 "\n"
185 " Only reverse connections can be done. (Note: one can specify multiple\n"
186 " viewing hosts via: -connect host1,host2,host3 or add/remove them\n"
187 " dynamically as described above.)\n"
188 "\n"
189 " If a new window obscures an old one, you will see some or all of the\n"
190 " new window in the old one. The hope is this is a popup dialog or menu\n"
191 " that will go away soon. Otherwise a user at the physical display will\n"
192 " need to move it. (See also the SSVNC viewer features described below.) \n"
193 "\n"
194 " The viewer side cannot resize or make windows move on the physical\n"
195 " display. Again, a user at the physical display may need to help, or\n"
196 " use the SSVNC viewer (see Tip below.)\n"
197 "\n"
198 " Tip: If the application has its own 'resize corner', then dragging\n"
199 " it may successfully resize the application window.\n"
200 " Tip: Some desktop environments enable moving a window via, say,\n"
201 " Alt+Left-Button-Drag. One may be able to move a window this way.\n"
202 " Also, e.g., Alt+Right-Button-Drag may resize a window.\n"
203 " Tip: Clicking on part of an obscured window may raise it to the top.\n"
204 " Also, e.g., Alt+Middle-Button may toggle Raise/Lower.\n"
205 "\n"
206 " Tip: The SSVNC 1.0.25 unix and macosx vncviewer has 'EscapeKeys' hot\n"
207 " keys that will move, resize, raise, and lower the window via the\n"
208 " x11vnc -remote_prefix X11VNC_APPSHARE_CMD: feature. So in the\n"
209 " viewer while holding down Shift_L+Super_L+Alt_L the arrow keys\n"
210 " move the window, PageUp/PageDn/Home/End resize it, and - and +\n"
211 " raise and lower it. Key 'M' or Button1 moves the remote window\n"
212 " to the +X+Y of the viewer window. Key 'D' or Button3 deletes\n"
213 " the remote window.\n"
214 "\n"
215 " You can run the SSVNC vncviewer with options '-escape default',\n"
216 " '-multilisten' and '-env VNCVIEWER_MIN_TITLE=1'; or just run\n"
217 " with option '-appshare' to enable these and automatic placement.\n"
218 "\n"
219 " If any part of a window goes off of the display screen, then x11vnc\n"
220 " may be unable to poll it (without crashing), and so the window will\n"
221 " stop updating until the window is completely on-screen again.\n"
222 "\n"
223 " The (stock) vnc viewer does not know where to best position each new\n"
224 " viewer window; it likely centers each one (including when resized.)\n"
225 " Note: The SSVNC viewer in '-appshare' mode places them correctly.\n"
226 "\n"
227 " Deleting a viewer window does not delete the real window.\n"
228 " Note: The SSVNC viewer Shift+EscapeKeys+Button3 deletes it.\n"
229 "\n"
230 " Sometimes new window detection fails.\n"
231 "\n"
232 " Sometimes menu/popup detection fails.\n"
233 "\n"
234 " Sometimes the contents of a menu/popup window have blacked-out regions.\n"
235 " Try -sid or -showmenus as a workaround.\n"
236 "\n"
237 " If the application starts up a new application (a different process)\n"
238 " that new application will not be tracked (but, unfortunately, it may\n"
239 " cover up existing windows that are being tracked.) See cmd=add_window\n"
240 " and cmd=add_app described above.\n"
241 "\n"
242 #endif
243 ;
244
245 #include <stdio.h>
246 #include <stdlib.h>
247 #include <string.h>
248
249 #define WMAX 192
250 #define CMAX 128
251 #define AMAX 32
252
253 static Window root = None;
254 static Window watch[WMAX];
255 static Window apps[WMAX];
256 static int state[WMAX];
257 static char *clients[CMAX];
258 static XWindowAttributes attr;
259 static char *ticker_atom_str = "X11VNC_APPSHARE_TICKER";
260 static Atom ticker_atom = None;
261 static char *cmd_atom_str = "X11VNC_APPSHARE_COMMAND";
262 static Atom cmd_atom = None;
263 static char *connect_to = NULL;
264 static char *x11vnc_args = "";
265 static char *id_opt = "-id";
266 static int skip_menus = 1;
267 static int exit_no_app_win = 1;
268 static int shell = 0;
269 static int tree_depth = 3;
270 static char *prompt = "appshare> ";
271 static char *x11vnc = "x11vnc";
272 static char *control = NULL;
273 static char *trackdir = "unset";
274 static char *trackpre = "/tmp/x11vnc-appshare-trackdir-tmp";
275 static char *tracktmp = NULL;
276 static char unique_tag[100];
277 static int use_forever = 1;
278 static int last_event_type = 0;
279 static pid_t helper_pid = 0;
280 static pid_t parent_pid = 0;
281 static double helper_delay = 0.5;
282 static int appshare_debug = 0;
283 static double start_time = 0.0;
284
285 static void get_wm_name(Window win, char **name);
286 static int win_attr(Window win);
287 static int get_xy(Window win, int *x, int *y);
288 static Window check_inside(Window win);
289 static int ours(Window win);
290 static void destroy_win(Window win);
291 static int same_app(Window win, Window app);
292
ff(void)293 static void ff(void) {
294 fflush(stdout);
295 fflush(stderr);
296 }
297
find_win(Window win)298 static int find_win(Window win) {
299 int i;
300 for (i=0; i < WMAX; i++) {
301 if (watch[i] == win) {
302 return i;
303 }
304 }
305 return -1;
306 }
307
find_app(Window app)308 static int find_app(Window app) {
309 int i;
310 for (i=0; i < AMAX; i++) {
311 if (apps[i] == app) {
312 return i;
313 }
314 }
315 return -1;
316 }
317
find_client(char * cl)318 static int find_client(char *cl) {
319 int i;
320 for (i=0; i < CMAX; i++) {
321 if (cl == NULL) {
322 if (clients[i] == NULL) {
323 return i;
324 }
325 continue;
326 }
327 if (clients[i] == NULL) {
328 continue;
329 }
330 if (!strcmp(clients[i], cl)) {
331 return i;
332 }
333 }
334 return -1;
335 }
336
trackdir_pid(Window win)337 static int trackdir_pid(Window win) {
338 FILE *f;
339 int ln = 0, pid = 0;
340 char line[1024];
341
342 if (!trackdir) {
343 return 0;
344 }
345 sprintf(tracktmp, "%s/0x%lx.log", trackdir, win);
346 f = fopen(tracktmp, "r");
347 if (!f) {
348 return 0;
349 }
350 while (fgets(line, sizeof(line), f) != NULL) {
351 if (ln++ > 30) {
352 break;
353 }
354 if (strstr(line, "x11vnc version:")) {
355 char *q = strstr(line, "pid:");
356 if (q) {
357 int p;
358 if (sscanf(q, "pid: %d", &p) == 1) {
359 if (p > 0) {
360 pid = p;
361 break;
362 }
363 }
364 }
365 }
366 }
367 fclose(f);
368 return pid;
369 }
370
trackdir_cleanup(Window win)371 static void trackdir_cleanup(Window win) {
372 char *suffix[] = {"log", "connect", NULL};
373 int i=0;
374 if (!trackdir) {
375 return;
376 }
377 while (suffix[i] != NULL) {
378 sprintf(tracktmp, "%s/0x%lx.%s", trackdir, win, suffix[i]);
379 if (appshare_debug && !strcmp(suffix[i], "log")) {
380 fprintf(stderr, "keeping: %s\n", tracktmp);
381 ff();
382 } else {
383 if (appshare_debug) {
384 fprintf(stderr, "removing: %s\n", tracktmp);
385 ff();
386 }
387 unlink(tracktmp);
388 }
389 i++;
390 }
391 }
392
launch(Window win)393 static void launch(Window win) {
394 char *cmd, *tmp, *connto, *name;
395 int len, timeo = 30, uf = use_forever;
396 int w = 0, h = 0, x = 0, y = 0;
397
398 if (win_attr(win)) {
399 /* maybe switch to debug only. */
400 w = attr.width;
401 h = attr.height;
402 get_xy(win, &x, &y);
403 }
404
405 get_wm_name(win, &name);
406
407 if (strstr(x11vnc_args, "-once")) {
408 uf = 0;
409 }
410
411 if (control) {
412 int i = 0;
413 len = 0;
414 for (i=0; i < CMAX; i++) {
415 if (clients[i] != NULL) {
416 len += strlen(clients[i]) + 2;
417 }
418 }
419 connto = (char *) calloc(len, 1);
420 for (i=0; i < CMAX; i++) {
421 if (clients[i] != NULL) {
422 if (connto[0] != '\0') {
423 strcat(connto, ",");
424 }
425 strcat(connto, clients[i]);
426 }
427 }
428 } else {
429 connto = strdup(connect_to);
430 }
431 if (!strcmp(connto, "")) {
432 timeo = 0;
433 }
434 if (uf) {
435 timeo = 0;
436 }
437
438 len = 1000 + strlen(x11vnc) + strlen(connto) + strlen(x11vnc_args)
439 + 3 * (trackdir ? strlen(trackdir) : 100);
440
441 cmd = (char *) calloc(len, 1);
442 tmp = (char *) calloc(len, 1);
443
444 sprintf(cmd, "%s %s 0x%lx -bg -quiet %s -nopw -rfbport 0 "
445 "-timeout %d -noxdamage -noxinerama -norc -repeat -speeds dsl "
446 "-env X11VNC_AVOID_WINDOWS=never -env X11VNC_APPSHARE_ACTIVE=1 "
447 "-env X11VNC_NO_CHECK_PM=1 -env %s -novncconnect -shared -nonap "
448 "-remote_prefix X11VNC_APPSHARE_CMD:",
449 x11vnc, id_opt, win, use_forever ? "-forever" : "-once", timeo, unique_tag);
450
451 if (trackdir) {
452 FILE *f;
453 sprintf(tracktmp, " -noquiet -o %s/0x%lx.log", trackdir, win);
454 strcat(cmd, tracktmp);
455 sprintf(tracktmp, "%s/0x%lx.connect", trackdir, win);
456 f = fopen(tracktmp, "w");
457 if (f) {
458 fprintf(f, "%s", connto);
459 fclose(f);
460 sprintf(tmp, " -connect_or_exit '%s'", tracktmp);
461 strcat(cmd, tmp);
462 } else {
463 sprintf(tmp, " -connect_or_exit '%s'", connto);
464 strcat(cmd, tmp);
465 }
466 } else {
467 if (!strcmp(connto, "")) {
468 sprintf(tmp, " -connect '%s'", connto);
469 } else {
470 sprintf(tmp, " -connect_or_exit '%s'", connto);
471 }
472 strcat(cmd, tmp);
473 }
474 if (uf) {
475 char *q = strstr(cmd, "-connect_or_exit");
476 if (q) q = strstr(q, "_or_exit");
477 if (q) {
478 unsigned int i;
479 for (i=0; i < strlen("_or_exit"); i++) {
480 *q = ' ';
481 q++;
482 }
483 }
484 }
485
486 strcat(cmd, " ");
487 strcat(cmd, x11vnc_args);
488
489 fprintf(stdout, "launching: x11vnc for window 0x%08lx %dx%d+%d+%d \"%s\"\n",
490 win, w, h, x, y, name);
491
492 if (appshare_debug) {
493 fprintf(stderr, "\nrunning: %s\n\n", cmd);
494 }
495 ff();
496
497 system(cmd);
498
499 free(cmd);
500 free(tmp);
501 free(connto);
502 free(name);
503 }
504
stop(Window win)505 static void stop(Window win) {
506 char *cmd;
507 int pid = -1;
508 int f = find_win(win);
509 if (f < 0 || win == None) {
510 return;
511 }
512 if (state[f] == 0) {
513 return;
514 }
515 if (trackdir) {
516 pid = trackdir_pid(win);
517 if (pid > 0) {
518 if (appshare_debug) {fprintf(stderr,
519 "sending SIGTERM to: %d\n", pid); ff();}
520 kill((pid_t) pid, SIGTERM);
521 }
522 }
523
524 cmd = (char *) malloc(1000 + strlen(x11vnc));
525 sprintf(cmd, "pkill -TERM -f '%s %s 0x%lx -bg'", x11vnc, id_opt, win);
526 if (appshare_debug) {
527 fprintf(stdout, "stopping: 0x%08lx - %s\n", win, cmd);
528 } else {
529 fprintf(stdout, "stopping: x11vnc for window 0x%08lx "
530 "(pid: %d)\n", win, pid);
531 }
532 ff();
533 system(cmd);
534
535 sprintf(cmd, "(sleep 0.25 2>/dev/null || sleep 1; pkill -KILL -f '%s "
536 "%s 0x%lx -bg') &", x11vnc, id_opt, win);
537 system(cmd);
538
539 if (trackdir) {
540 trackdir_cleanup(win);
541 }
542
543 free(cmd);
544 }
545
kill_helper_pid(void)546 static void kill_helper_pid(void) {
547 int status;
548 if (helper_pid <= 0) {
549 return;
550 }
551 fprintf(stderr, "stopping: helper_pid: %d\n", (int) helper_pid);
552 kill(helper_pid, SIGTERM);
553 usleep(50 * 1000);
554 kill(helper_pid, SIGKILL);
555 usleep(25 * 1000);
556 #if LIBVNCSERVER_HAVE_SYS_WAIT_H && LIBVNCSERVER_HAVE_WAITPID
557 waitpid(helper_pid, &status, WNOHANG);
558 #endif
559 }
560
be_helper_pid(char * dpy_str)561 static void be_helper_pid(char *dpy_str) {
562 int cnt = 0;
563 int ms = (int) (1000 * helper_delay);
564 double last_check = 0.0;
565
566 if (ms < 50) ms = 50;
567
568 #if NO_X11
569 fprintf(stderr, "be_helper_pid: not compiled with X11.\n");
570 #else
571 dpy = XOpenDisplay(dpy_str);
572 ticker_atom = XInternAtom(dpy, ticker_atom_str, False);
573
574 while (1) {
575 char tmp[32];
576 sprintf(tmp, "HELPER_CNT_%08d", cnt++);
577 XChangeProperty(dpy, DefaultRootWindow(dpy), ticker_atom, XA_STRING, 8,
578 PropModeReplace, (unsigned char *) tmp, strlen(tmp));
579 XFlush(dpy);
580 usleep(ms*1000);
581 if (parent_pid > 0) {
582 if(dnow() > last_check + 1.0) {
583 last_check = dnow();
584 if (kill(parent_pid, 0) != 0) {
585 fprintf(stderr, "be_helper_pid: parent %d is gone.\n", (int) parent_pid);
586 break;
587 }
588 }
589 }
590 }
591 #endif
592 exit(0);
593 }
594
print_logs(void)595 static void print_logs(void) {
596 if (trackdir) {
597 DIR *dir = opendir(trackdir);
598 if (dir) {
599 struct dirent *dp;
600 while ( (dp = readdir(dir)) != NULL) {
601 FILE *f;
602 char *name = dp->d_name;
603 if (!strcmp(name, ".") || !strcmp(name, "..")) {
604 continue;
605 }
606 if (strstr(name, "0x") != name) {
607 continue;
608 }
609 if (strstr(name, ".log") == NULL) {
610 continue;
611 }
612 sprintf(tracktmp, "%s/%s", trackdir, name);
613 f = fopen(tracktmp, "r");
614 if (f) {
615 char line[1024];
616 fprintf(stderr, "===== x11vnc log %s =====\n", tracktmp);
617 while (fgets(line, sizeof(line), f) != NULL) {
618 fprintf(stderr, "%s", line);
619 }
620 fprintf(stderr, "\n");
621 ff();
622 fclose(f);
623 }
624 }
625 closedir(dir);
626 }
627 }
628 }
629
appshare_cleanup(int s)630 static void appshare_cleanup(int s) {
631 int i;
632 if (s) {}
633
634 if (use_forever) {
635 /* launch this backup in case they kill -9 us before we terminate everything */
636 char cmd[1000];
637 sprintf(cmd, "(sleep 3; pkill -TERM -f '%s') &", unique_tag);
638 if (appshare_debug) fprintf(stderr, "%s\n", cmd);
639 system(cmd);
640 }
641
642 for (i=0; i < WMAX; i++) {
643 if (watch[i] != None) {
644 stop(watch[i]);
645 }
646 }
647
648 if (trackdir) {
649 DIR *dir = opendir(trackdir);
650 if (dir) {
651 struct dirent *dp;
652 while ( (dp = readdir(dir)) != NULL) {
653 char *name = dp->d_name;
654 if (!strcmp(name, ".") || !strcmp(name, "..")) {
655 continue;
656 }
657 if (strstr(name, "0x") != name) {
658 fprintf(stderr, "skipping: %s\n", name);
659 continue;
660 }
661 if (!appshare_debug) {
662 fprintf(stderr, "removing: %s\n", name);
663 sprintf(tracktmp, "%s/%s", trackdir, name);
664 unlink(tracktmp);
665 } else {
666 if (appshare_debug) fprintf(stderr, "keeping: %s\n", name);
667 }
668 }
669 closedir(dir);
670 }
671 if (!appshare_debug) {
672 if (strstr(trackdir, trackpre) == trackdir) {
673 if (appshare_debug) fprintf(stderr, "removing: %s\n", trackdir);
674 rmdir(trackdir);
675 }
676 }
677 ff();
678 }
679
680 kill_helper_pid();
681
682 #if !NO_X11
683 XCloseDisplay(dpy);
684 #endif
685 fprintf(stdout, "done.\n");
686 ff();
687 exit(0);
688 }
689
trap_xerror(Display * d,XErrorEvent * error)690 static int trap_xerror(Display *d, XErrorEvent *error) {
691 if (d || error) {}
692 return 0;
693 }
694
695 #if 0
696 typedef struct {
697 int x, y; /* location of window */
698 int width, height; /* width and height of window */
699 int border_width; /* border width of window */
700 int depth; /* depth of window */
701 Visual *visual; /* the associated visual structure */
702 Window root; /* root of screen containing window */
703 int class; /* InputOutput, InputOnly*/
704 int bit_gravity; /* one of bit gravity values */
705 int win_gravity; /* one of the window gravity values */
706 int backing_store; /* NotUseful, WhenMapped, Always */
707 unsigned long backing_planes;/* planes to be preserved if possible */
708 unsigned long backing_pixel;/* value to be used when restoring planes */
709 Bool save_under; /* boolean, should bits under be saved? */
710 Colormap colormap; /* color map to be associated with window */
711 Bool map_installed; /* boolean, is color map currently installed*/
712 int map_state; /* IsUnmapped, IsUnviewable, IsViewable */
713 long all_event_masks; /* set of events all people have interest in*/
714 long your_event_mask; /* my event mask */
715 long do_not_propagate_mask; /* set of events that should not propagate */
716 Bool override_redirect; /* boolean value for override-redirect */
717 Screen *screen; /* back pointer to correct screen */
718 } XWindowAttributes;
719 #endif
720
get_wm_name(Window win,char ** name)721 static void get_wm_name(Window win, char **name) {
722 int ok;
723
724 #if !NO_X11
725 XErrorHandler old_handler = XSetErrorHandler(trap_xerror);
726 ok = XFetchName(dpy, win, name);
727 XSetErrorHandler(old_handler);
728 #endif
729
730 if (!ok || *name == NULL) {
731 *name = strdup("unknown");
732 }
733 }
734
win_attr(Window win)735 static int win_attr(Window win) {
736 int ok = 0;
737 #if !NO_X11
738 XErrorHandler old_handler = XSetErrorHandler(trap_xerror);
739 ok = XGetWindowAttributes(dpy, win, &attr);
740 XSetErrorHandler(old_handler);
741 #endif
742
743 if (ok) {
744 return 1;
745 } else {
746 return 0;
747 }
748 }
749
win_select(Window win,int ignore)750 static void win_select(Window win, int ignore) {
751 #if !NO_X11
752 XErrorHandler old_handler = XSetErrorHandler(trap_xerror);
753 if (ignore) {
754 XSelectInput(dpy, win, 0);
755 } else {
756 XSelectInput(dpy, win, SubstructureNotifyMask);
757 }
758 XSync(dpy, False);
759 XSetErrorHandler(old_handler);
760 #endif
761 }
762
get_parent(Window win)763 static Window get_parent(Window win) {
764 int ok;
765 Window r, parent = None, *list = NULL;
766 unsigned int nchild;
767
768 #if !NO_X11
769 XErrorHandler old_handler = XSetErrorHandler(trap_xerror);
770 ok = XQueryTree(dpy, win, &r, &parent, &list, &nchild);
771 XSetErrorHandler(old_handler);
772
773 if (!ok) {
774 return None;
775 }
776 if (list) {
777 XFree(list);
778 }
779 #endif
780 return parent;
781 }
782
get_xy(Window win,int * x,int * y)783 static int get_xy(Window win, int *x, int *y) {
784 Window cr;
785 Bool rc = False;
786 #if !NO_X11
787 XErrorHandler old_handler = XSetErrorHandler(trap_xerror);
788
789 rc = XTranslateCoordinates(dpy, win, root, 0, 0, x, y, &cr);
790 XSetErrorHandler(old_handler);
791 #endif
792
793 if (!rc) {
794 return 0;
795 } else {
796 return 1;
797 }
798 }
799
check_inside(Window win)800 static Window check_inside(Window win) {
801 int i, nwin = 0;
802 int w, h, x, y;
803 int Ws[WMAX], Hs[WMAX], Xs[WMAX], Ys[WMAX];
804 Window wins[WMAX];
805
806 if (!win_attr(win)) {
807 return None;
808 }
809
810 /* store them first to give the win app more time to settle. */
811 for (i=0; i < WMAX; i++) {
812 int X, Y;
813 Window wchk = watch[i];
814 if (wchk == None) {
815 continue;
816 }
817 if (state[i] == 0) {
818 continue;
819 }
820 if (!win_attr(wchk)) {
821 continue;
822 }
823 if (!get_xy(wchk, &X, &Y)) {
824 continue;
825 }
826
827 Xs[nwin] = X;
828 Ys[nwin] = Y;
829 Ws[nwin] = attr.width;
830 Hs[nwin] = attr.height;
831 wins[nwin] = wchk;
832 nwin++;
833 }
834
835 if (nwin == 0) {
836 return None;
837 }
838
839 if (!win_attr(win)) {
840 return None;
841 }
842 w = attr.width;
843 h = attr.height;
844
845 get_xy(win, &x, &y);
846 if (!get_xy(win, &x, &y)) {
847 return None;
848 }
849
850 for (i=0; i < nwin; i++) {
851 int X, Y, W, H;
852 Window wchk = wins[i];
853 X = Xs[i];
854 Y = Ys[i];
855 W = Ws[i];
856 H = Hs[i];
857
858 if (appshare_debug) fprintf(stderr, "check inside: 0x%lx %dx%d+%d+%d %dx%d+%d+%d\n", wchk, w, h, x, y, W, H, X, Y);
859
860 if (X <= x && Y <= y) {
861 if (x + w <= X + W && y + h < Y + H) {
862 return wchk;
863 }
864 }
865 }
866
867 return None;
868 }
869
add_win(Window win)870 static void add_win(Window win) {
871 int idx = find_win(win);
872 int free = find_win(None);
873 if (idx >= 0) {
874 if (appshare_debug) {fprintf(stderr, "already watching window: 0x%lx\n", win); ff();}
875 return;
876 }
877 if (free < 0) {
878 fprintf(stderr, "ran out of slots for window: 0x%lx\n", win); ff();
879 return;
880 }
881
882 if (appshare_debug) {fprintf(stderr, "watching: 0x%lx at %d\n", win, free); ff();}
883
884 watch[free] = win;
885 state[free] = 0;
886
887 win_select(win, 0);
888 }
889
delete_win(Window win)890 static void delete_win(Window win) {
891 int i;
892 for (i=0; i < WMAX; i++) {
893 if (watch[i] == win) {
894 watch[i] = None;
895 state[i] = 0;
896 if (appshare_debug) {fprintf(stderr, "deleting: 0x%lx at %d\n", win, i); ff();}
897 }
898 }
899 }
900
recurse_search(int level,int level_max,Window top,Window app,int * nw)901 static void recurse_search(int level, int level_max, Window top, Window app, int *nw) {
902 Window w, r, parent, *list = NULL;
903 unsigned int nchild;
904 int ok = 0;
905
906 if (appshare_debug > 1) {
907 fprintf(stderr, "level: %d level_max: %d top: 0x%lx app: 0x%lx\n", level, level_max, top, app);
908 }
909 if (level >= level_max) {
910 return;
911 }
912
913 #if !NO_X11
914 ok = XQueryTree(dpy, top, &r, &parent, &list, &nchild);
915 if (ok) {
916 int i;
917 for (i=0; i < (int) nchild; i++) {
918 w = list[i];
919 if (w == None || find_win(w) >= 0) {
920 continue;
921 }
922 if (ours(w) && w != app) {
923 if (appshare_debug) fprintf(stderr, "add level %d 0x%lx %d/%d\n",
924 level, w, i, nchild);
925 add_win(w);
926 (*nw)++;
927 }
928 }
929 for (i=0; i < (int) nchild; i++) {
930 w = list[i];
931 if (w == None || ours(w)) {
932 continue;
933 }
934 recurse_search(level+1, level_max, w, app, nw);
935 }
936 }
937 if (list) {
938 XFree(list);
939 }
940 #endif
941 }
942
add_app(Window app)943 static void add_app(Window app) {
944 int i, nw = 0, free = -1;
945 XErrorHandler old_handler;
946
947 #if !NO_X11
948 i = find_app(app);
949 if (i >= 0) {
950 fprintf(stderr, "already tracking app: 0x%lx\n", app);
951 return;
952 }
953 for (i=0; i < AMAX; i++) {
954 if (same_app(apps[i], app)) {
955 fprintf(stderr, "already tracking app: 0x%lx via 0x%lx\n", app, apps[i]);
956 return;
957 }
958 }
959 free = find_app(None);
960 if (free < 0) {
961 fprintf(stderr, "ran out of app slots.\n");
962 return;
963 }
964 apps[free] = app;
965
966 add_win(app);
967
968 old_handler = XSetErrorHandler(trap_xerror);
969 recurse_search(0, tree_depth, root, app, &nw);
970 XSetErrorHandler(old_handler);
971 #endif
972 fprintf(stderr, "tracking %d windows related to app window 0x%lx\n", nw, app);
973 }
974
del_app(Window app)975 static void del_app(Window app) {
976 int i;
977 for (i=0; i < WMAX; i++) {
978 Window win = watch[i];
979 if (win != None) {
980 if (same_app(app, win)) {
981 destroy_win(win);
982 }
983 }
984 }
985 for (i=0; i < AMAX; i++) {
986 Window app2 = apps[i];
987 if (app2 != None) {
988 if (same_app(app, app2)) {
989 apps[i] = None;
990 }
991 }
992 }
993 }
994
wait_until_empty(char * file)995 static void wait_until_empty(char *file) {
996 double t = 0.0, dt = 0.05;
997 while (t < 1.0) {
998 struct stat sb;
999 if (stat(file, &sb) != 0) {
1000 return;
1001 }
1002 if (sb.st_size == 0) {
1003 return;
1004 }
1005 t += dt;
1006 usleep( (int) (dt * 1000 * 1000) );
1007 }
1008 }
1009
client(char * client,int add)1010 static void client(char *client, int add) {
1011 DIR *dir;
1012 struct dirent *dp;
1013
1014 if (!client) {
1015 return;
1016 }
1017 if (!trackdir) {
1018 fprintf(stderr, "no trackdir, cannot %s client: %s\n",
1019 add ? "add" : "disconnect", client);
1020 ff();
1021 return;
1022 }
1023 fprintf(stdout, "%s client: %s\n", add ? "adding " : "deleting", client);
1024
1025 dir = opendir(trackdir);
1026 if (!dir) {
1027 fprintf(stderr, "could not opendir trackdir: %s\n", trackdir);
1028 return;
1029 }
1030 while ( (dp = readdir(dir)) != NULL) {
1031 char *name = dp->d_name;
1032 if (!strcmp(name, ".") || !strcmp(name, "..")) {
1033 continue;
1034 }
1035 if (strstr(name, "0x") != name) {
1036 continue;
1037 }
1038 if (strstr(name, ".connect")) {
1039 FILE *f;
1040 char *tmp;
1041 Window twin;
1042
1043 if (scan_hexdec(name, &twin)) {
1044 int f = find_win(twin);
1045 if (appshare_debug) {
1046 fprintf(stderr, "twin: 0x%lx name=%s f=%d\n", twin, name, f);
1047 ff();
1048 }
1049 if (f < 0) {
1050 continue;
1051 }
1052 }
1053
1054 tmp = (char *) calloc(100 + strlen(client), 1);
1055 sprintf(tracktmp, "%s/%s", trackdir, name);
1056 if (add) {
1057 sprintf(tmp, "%s\n", client);
1058 } else {
1059 sprintf(tmp, "cmd=close:%s\n", client);
1060 }
1061 wait_until_empty(tracktmp);
1062 f = fopen(tracktmp, "w");
1063 if (f) {
1064 if (appshare_debug) {
1065 fprintf(stderr, "%s client: %s + %s",
1066 add ? "add" : "disconnect", tracktmp, tmp);
1067 ff();
1068 }
1069 fprintf(f, "%s", tmp);
1070 fclose(f);
1071 }
1072 free(tmp);
1073 }
1074 }
1075 closedir(dir);
1076 }
1077
mapped(Window win)1078 static void mapped(Window win) {
1079 int f;
1080 if (win == None) {
1081 return;
1082 }
1083 f = find_win(win);
1084 if (f < 0) {
1085 if (win_attr(win)) {
1086 if (get_parent(win) == root) {
1087 /* XXX more cases? */
1088 add_win(win);
1089 }
1090 }
1091 }
1092 }
1093
unmapped(Window win)1094 static void unmapped(Window win) {
1095 int f = find_win(win);
1096 if (f < 0 || win == None) {
1097 return;
1098 }
1099 stop(win);
1100 state[f] = 0;
1101 }
1102
destroy_win(Window win)1103 static void destroy_win(Window win) {
1104 stop(win);
1105 delete_win(win);
1106 }
1107
parse_win(char * str)1108 static Window parse_win(char *str) {
1109 Window win = None;
1110 if (!str) {
1111 return None;
1112 }
1113 if (!strcmp(str, "pick") || !strcmp(str, "p")) {
1114 static double last_pick = 0.0;
1115 if (dnow() < start_time + 15) {
1116 ;
1117 } else if (dnow() < last_pick + 2) {
1118 return None;
1119 } else {
1120 last_pick = dnow();
1121 }
1122 if (!pick_windowid(&win)) {
1123 fprintf(stderr, "parse_win: bad window pick.\n");
1124 win = None;
1125 }
1126 if (win == root) {
1127 fprintf(stderr, "parse_win: ignoring pick of rootwin 0x%lx.\n", win);
1128 win = None;
1129 }
1130 ff();
1131 } else if (!scan_hexdec(str, &win)) {
1132 win = None;
1133 }
1134 return win;
1135 }
1136
add_or_del_app(char * str,int add)1137 static void add_or_del_app(char *str, int add) {
1138 Window win = parse_win(str);
1139
1140 if (win != None) {
1141 if (add) {
1142 add_app(win);
1143 } else {
1144 del_app(win);
1145 }
1146 } else if (!strcmp(str, "all")) {
1147 if (!add) {
1148 int i;
1149 for (i=0; i < AMAX; i++) {
1150 if (apps[i] != None) {
1151 del_app(apps[i]);
1152 }
1153 }
1154 }
1155 }
1156 }
1157
add_or_del_win(char * str,int add)1158 static void add_or_del_win(char *str, int add) {
1159 Window win = parse_win(str);
1160
1161 if (win != None) {
1162 int f = find_win(win);
1163 if (add) {
1164 if (f < 0 && win_attr(win)) {
1165 add_win(win);
1166 }
1167 } else {
1168 if (f >= 0) {
1169 destroy_win(win);
1170 }
1171 }
1172 } else if (!strcmp(str, "all")) {
1173 if (!add) {
1174 int i;
1175 for (i=0; i < WMAX; i++) {
1176 if (watch[i] != None) {
1177 destroy_win(watch[i]);
1178 }
1179 }
1180 }
1181 }
1182 }
1183
add_or_del_client(char * str,int add)1184 static void add_or_del_client(char *str, int add) {
1185 int i;
1186
1187 if (!str) {
1188 return;
1189 }
1190 if (strcmp(control, "internal")) {
1191 return;
1192 }
1193 if (add) {
1194 int idx = find_client(str);
1195 int free = find_client(NULL);
1196
1197 if (idx >=0) {
1198 fprintf(stderr, "already tracking client: %s in slot %d\n", str, idx);
1199 ff();
1200 return;
1201 }
1202 if (free < 0) {
1203 static int cnt = 0;
1204 if (cnt++ < 10) {
1205 fprintf(stderr, "ran out of client slots.\n");
1206 ff();
1207 }
1208 return;
1209 }
1210 clients[free] = strdup(str);
1211 client(str, 1);
1212 } else {
1213 if (str[0] == '#' || str[0] == '%') {
1214 if (sscanf(str+1, "%d", &i) == 1) {
1215 i--;
1216 if (0 <= i && i < CMAX) {
1217 if (clients[i] != NULL) {
1218 client(clients[i], 0);
1219 free(clients[i]);
1220 clients[i] = NULL;
1221 return;
1222 }
1223 }
1224 }
1225 } else if (!strcmp(str, "all")) {
1226 for (i=0; i < CMAX; i++) {
1227 if (clients[i] == NULL) {
1228 continue;
1229 }
1230 client(clients[i], 0);
1231 free(clients[i]);
1232 clients[i] = NULL;
1233 }
1234 return;
1235 }
1236
1237 i = find_client(str);
1238 if (i >= 0) {
1239 free(clients[i]);
1240 clients[i] = NULL;
1241 client(str, 0);
1242 }
1243 }
1244 }
1245
restart_x11vnc(void)1246 static void restart_x11vnc(void) {
1247 int i, n = 0;
1248 Window win, active[WMAX];
1249 for (i=0; i < WMAX; i++) {
1250 win = watch[i];
1251 if (win == None) {
1252 continue;
1253 }
1254 if (state[i]) {
1255 active[n++] = win;
1256 stop(win);
1257 }
1258 }
1259 if (n) {
1260 usleep(1500 * 1000);
1261 }
1262 for (i=0; i < n; i++) {
1263 win = active[i];
1264 launch(win);
1265 }
1266 }
1267
1268 static unsigned long cmask = 0x3fc00000; /* 00111111110000000000000000000000 */
1269
init_cmask(void)1270 static void init_cmask(void) {
1271 /* dependent on the X server implementation; XmuClientWindow better? */
1272 /* xc/programs/Xserver/include/resource.h */
1273 int didit = 0, res_cnt = 29, client_bits = 8;
1274
1275 if (getenv("X11VNC_APPSHARE_CLIENT_MASK")) {
1276 unsigned long cr;
1277 if (sscanf(getenv("X11VNC_APPSHARE_CLIENT_MASK"), "0x%lx", &cr) == 1) {
1278 cmask = cr;
1279 didit = 1;
1280 }
1281 } else if (getenv("X11VNC_APPSHARE_CLIENT_BITS")) {
1282 int cr = atoi(getenv("X11VNC_APPSHARE_CLIENT_BITS"));
1283 if (cr > 0) {
1284 client_bits = cr;
1285 }
1286 }
1287 if (!didit) {
1288 cmask = (((1 << client_bits) - 1) << (res_cnt-client_bits));
1289 }
1290 fprintf(stderr, "client_mask: 0x%08lx\n", cmask);
1291 }
1292
same_app(Window win,Window app)1293 static int same_app(Window win, Window app) {
1294 if ( (win & cmask) == (app & cmask) ) {
1295 return 1;
1296 } else {
1297 return 0;
1298 }
1299 }
1300
ours(Window win)1301 static int ours(Window win) {
1302 int i;
1303 for (i=0; i < AMAX; i++) {
1304 if (apps[i] != None) {
1305 if (same_app(win, apps[i])) {
1306 return 1;
1307 }
1308 }
1309 }
1310 return 0;
1311 }
1312
list_clients(void)1313 static void list_clients(void) {
1314 int i, n = 0;
1315 for (i=0; i < CMAX; i++) {
1316 if (clients[i] == NULL) {
1317 continue;
1318 }
1319 fprintf(stdout, "client[%02d] %s\n", ++n, clients[i]);
1320 }
1321 fprintf(stdout, "total clients: %d\n", n);
1322 ff();
1323 }
1324
list_windows(void)1325 static void list_windows(void) {
1326 int i, n = 0;
1327 for (i=0; i < WMAX; i++) {
1328 char *name;
1329 Window win = watch[i];
1330 if (win == None) {
1331 continue;
1332 }
1333 get_wm_name(win, &name);
1334 fprintf(stdout, "window[%02d] 0x%08lx state: %d slot: %03d \"%s\"\n",
1335 ++n, win, state[i], i, name);
1336 free(name);
1337 }
1338 fprintf(stdout, "total windows: %d\n", n);
1339 ff();
1340 }
1341
list_apps(void)1342 static void list_apps(void) {
1343 int i, n = 0;
1344 for (i=0; i < AMAX; i++) {
1345 char *name;
1346 Window win = apps[i];
1347 if (win == None) {
1348 continue;
1349 }
1350 get_wm_name(win, &name);
1351 fprintf(stdout, "app[%02d] 0x%08lx state: %d slot: %03d \"%s\"\n",
1352 ++n, win, state[i], i, name);
1353 free(name);
1354 }
1355 fprintf(stdout, "total apps: %d\n", n);
1356 ff();
1357 }
1358
process_control(char * file,int check_clients)1359 static int process_control(char *file, int check_clients) {
1360 int i, nnew = 0, seen[CMAX];
1361 char line[1024], *newctl[CMAX];
1362 FILE *f;
1363
1364 f = fopen(file, "r");
1365 if (!f) {
1366 return 1;
1367 }
1368 if (check_clients) {
1369 for (i=0; i < CMAX; i++) {
1370 seen[i] = 0;
1371 }
1372 }
1373 while (fgets(line, sizeof(line), f) != NULL) {
1374 char *q = strchr(line, '\n');
1375 if (q) *q = '\0';
1376
1377 if (appshare_debug) {
1378 fprintf(stderr, "check_control: %s\n", line);
1379 ff();
1380 }
1381
1382 q = lblanks(line);
1383 if (q[0] == '#') {
1384 continue;
1385 }
1386 if (!strcmp(q, "")) {
1387 continue;
1388 }
1389 if (strstr(q, "cmd=") == q) {
1390 char *cmd = q + strlen("cmd=");
1391 if (!strcmp(cmd, "quit")) {
1392 if (strcmp(control, file) && strstr(file, ".cmd")) {
1393 FILE *f2 = fopen(file, "w");
1394 if (f2) fclose(f2);
1395 }
1396 appshare_cleanup(0);
1397 } else if (!strcmp(cmd, "wait")) {
1398 return 0;
1399 } else if (strstr(cmd, "bcast:") == cmd) {
1400 ;
1401 } else if (strstr(cmd, "del_window:") == cmd) {
1402 add_or_del_win(cmd + strlen("del_window:"), 0);
1403 } else if (strstr(cmd, "add_window:") == cmd) {
1404 add_or_del_win(cmd + strlen("add_window:"), 1);
1405 } else if (strstr(cmd, "del:") == cmd) {
1406 add_or_del_win(cmd + strlen("del:"), 0);
1407 } else if (strstr(cmd, "add:") == cmd) {
1408 add_or_del_win(cmd + strlen("add:"), 1);
1409 } else if (strstr(cmd, "del_client:") == cmd) {
1410 add_or_del_client(cmd + strlen("del_client:"), 0);
1411 } else if (strstr(cmd, "add_client:") == cmd) {
1412 add_or_del_client(cmd + strlen("add_client:"), 1);
1413 } else if (strstr(cmd, "-") == cmd) {
1414 add_or_del_client(cmd + strlen("-"), 0);
1415 } else if (strstr(cmd, "+") == cmd) {
1416 add_or_del_client(cmd + strlen("+"), 1);
1417 } else if (strstr(cmd, "del_app:") == cmd) {
1418 add_or_del_app(cmd + strlen("del_app:"), 0);
1419 } else if (strstr(cmd, "add_app:") == cmd) {
1420 add_or_del_app(cmd + strlen("add_app:"), 1);
1421 } else if (strstr(cmd, "debug:") == cmd) {
1422 appshare_debug = atoi(cmd + strlen("debug:"));
1423 } else if (strstr(cmd, "showmenus:") == cmd) {
1424 skip_menus = atoi(cmd + strlen("showmenus:"));
1425 skip_menus = !(skip_menus);
1426 } else if (strstr(cmd, "noexit:") == cmd) {
1427 exit_no_app_win = atoi(cmd + strlen("noexit:"));
1428 exit_no_app_win = !(exit_no_app_win);
1429 } else if (strstr(cmd, "use_forever:") == cmd) {
1430 use_forever = atoi(cmd + strlen("use_forever:"));
1431 } else if (strstr(cmd, "tree_depth:") == cmd) {
1432 tree_depth = atoi(cmd + strlen("tree_depth:"));
1433 } else if (strstr(cmd, "x11vnc_args:") == cmd) {
1434 x11vnc_args = strdup(cmd + strlen("x11vnc_args:"));
1435 } else if (strstr(cmd, "env:") == cmd) {
1436 putenv(cmd + strlen("env:"));
1437 } else if (strstr(cmd, "noop") == cmd) {
1438 ;
1439 } else if (!strcmp(cmd, "restart")) {
1440 restart_x11vnc();
1441 } else if (!strcmp(cmd, "list_clients") || !strcmp(cmd, "lc")) {
1442 list_clients();
1443 } else if (!strcmp(cmd, "list_windows") || !strcmp(cmd, "lw")) {
1444 list_windows();
1445 } else if (!strcmp(cmd, "list_apps") || !strcmp(cmd, "la")) {
1446 list_apps();
1447 } else if (!strcmp(cmd, "list_all") || !strcmp(cmd, "ls")) {
1448 list_windows();
1449 fprintf(stderr, "\n");
1450 list_apps();
1451 fprintf(stderr, "\n");
1452 list_clients();
1453 } else if (!strcmp(cmd, "print_logs") || !strcmp(cmd, "pl")) {
1454 print_logs();
1455 } else if (!strcmp(cmd, "?") || !strcmp(cmd, "h") || !strcmp(cmd, "help")) {
1456 fprintf(stderr, "available commands:\n");
1457 fprintf(stderr, "\n");
1458 fprintf(stderr, " quit restart noop x11vnc help ? ! !!\n");
1459 fprintf(stderr, "\n");
1460 fprintf(stderr, " add_window:win (add:win, add:pick)\n");
1461 fprintf(stderr, " del_window:win (del:win, del:pick, del:all)\n");
1462 fprintf(stderr, " add_app:win (add_app:pick)\n");
1463 fprintf(stderr, " del_app:win (del_app:pick, del_app:all)\n");
1464 fprintf(stderr, " add_client:host (+host)\n");
1465 fprintf(stderr, " del_client:host (-host, -all)\n");
1466 fprintf(stderr, "\n");
1467 fprintf(stderr, " list_windows (lw)\n");
1468 fprintf(stderr, " list_apps (la)\n");
1469 fprintf(stderr, " list_clients (lc)\n");
1470 fprintf(stderr, " list_all (ls)\n");
1471 fprintf(stderr, " print_logs (pl)\n");
1472 fprintf(stderr, "\n");
1473 fprintf(stderr, " debug:n showmenus:n noexit:n\n");
1474 } else {
1475 fprintf(stderr, "unrecognized %s\n", q);
1476 }
1477 continue;
1478 }
1479 if (check_clients) {
1480 int idx = find_client(q);
1481 if (idx >= 0) {
1482 seen[idx] = 1;
1483 } else {
1484 newctl[nnew++] = strdup(q);
1485 }
1486 }
1487 }
1488 fclose(f);
1489
1490 if (check_clients) {
1491 for (i=0; i < CMAX; i++) {
1492 if (clients[i] == NULL) {
1493 continue;
1494 }
1495 if (!seen[i]) {
1496 client(clients[i], 0);
1497 free(clients[i]);
1498 clients[i] = NULL;
1499 }
1500 }
1501 for (i=0; i < nnew; i++) {
1502 int free = find_client(NULL);
1503 if (free < 0) {
1504 static int cnt = 0;
1505 if (cnt++ < 10) {
1506 fprintf(stderr, "ran out of client slots.\n");
1507 ff();
1508 break;
1509 }
1510 continue;
1511 }
1512 clients[free] = newctl[i];
1513 client(newctl[i], 1);
1514 }
1515 }
1516 return 1;
1517 }
1518
check_control(void)1519 static int check_control(void) {
1520 static int last_size = -1;
1521 static time_t last_mtime = 0;
1522 struct stat sb;
1523 char *control_cmd;
1524
1525 if (!control) {
1526 return 1;
1527 }
1528
1529 if (!strcmp(control, "internal")) {
1530 return 1;
1531 }
1532
1533 control_cmd = (char *)malloc(strlen(control) + strlen(".cmd") + 1);
1534 sprintf(control_cmd, "%s.cmd", control);
1535 if (stat(control_cmd, &sb) == 0) {
1536 FILE *f;
1537 if (sb.st_size > 0) {
1538 process_control(control_cmd, 0);
1539 }
1540 f = fopen(control_cmd, "w");
1541 if (f) {
1542 fclose(f);
1543 }
1544 }
1545 free(control_cmd);
1546
1547 if (stat(control, &sb) != 0) {
1548 return 1;
1549 }
1550 if (last_size == (int) sb.st_size && last_mtime == sb.st_mtime) {
1551 return 1;
1552 }
1553 last_size = (int) sb.st_size;
1554 last_mtime = sb.st_mtime;
1555
1556 return process_control(control, 1);
1557 }
1558
update(void)1559 static void update(void) {
1560 int i, app_ok = 0;
1561 if (last_event_type != PropertyNotify) {
1562 if (appshare_debug) fprintf(stderr, "\nupdate ...\n");
1563 } else if (appshare_debug > 1) {
1564 fprintf(stderr, "update ... propertynotify\n");
1565 }
1566 if (!check_control()) {
1567 return;
1568 }
1569 for (i=0; i < WMAX; i++) {
1570 Window win = watch[i];
1571 if (win == None) {
1572 continue;
1573 }
1574 if (!win_attr(win)) {
1575 destroy_win(win);
1576 continue;
1577 }
1578 if (find_app(win) >= 0) {
1579 app_ok++;
1580 }
1581 if (state[i] == 0) {
1582 if (attr.map_state == IsViewable) {
1583 if (skip_menus) {
1584 Window inside = check_inside(win);
1585 if (inside != None) {
1586 if (appshare_debug) {fprintf(stderr, "skip_menus: window 0x%lx is inside of 0x%lx, not tracking it.\n", win, inside); ff();}
1587 delete_win(win);
1588 continue;
1589 }
1590 }
1591 launch(win);
1592 state[i] = 1;
1593 }
1594 } else if (state[i] == 1) {
1595 if (attr.map_state != IsViewable) {
1596 stop(win);
1597 state[i] = 0;
1598 }
1599 }
1600 }
1601 if (exit_no_app_win && !app_ok) {
1602 for (i=0; i < AMAX; i++) {
1603 if (apps[i] != None) {
1604 fprintf(stdout, "main application window is gone: 0x%lx\n", apps[i]);
1605 }
1606 }
1607 ff();
1608 appshare_cleanup(0);
1609 }
1610 if (last_event_type != PropertyNotify) {
1611 if (appshare_debug) {fprintf(stderr, "update done.\n"); ff();}
1612 }
1613 }
1614
exiter(char * msg,int rc)1615 static void exiter(char *msg, int rc) {
1616 fprintf(stderr, "%s", msg);
1617 ff();
1618 kill_helper_pid();
1619 exit(rc);
1620 }
1621
set_trackdir(void)1622 static void set_trackdir(void) {
1623 char tmp[256];
1624 struct stat sb;
1625 if (!strcmp(trackdir, "none")) {
1626 trackdir = NULL;
1627 return;
1628 }
1629 if (!strcmp(trackdir, "unset")) {
1630 int fd;
1631 sprintf(tmp, "%s.XXXXXX", trackpre);
1632 fd = mkstemp(tmp);
1633 if (fd < 0) {
1634 strcat(tmp, ": failed to create file.\n");
1635 exiter(tmp, 1);
1636 }
1637 /* XXX race */
1638 close(fd);
1639 unlink(tmp);
1640 if (mkdir(tmp, 0700) != 0) {
1641 strcat(tmp, ": failed to create dir.\n");
1642 exiter(tmp, 1);
1643 }
1644 trackdir = strdup(tmp);
1645 }
1646 if (stat(trackdir, &sb) != 0) {
1647 if (mkdir(trackdir, 0700) != 0) {
1648 exiter("could not make trackdir.\n", 1);
1649 }
1650 } else if (! S_ISDIR(sb.st_mode)) {
1651 exiter("trackdir not a directory.\n", 1);
1652 }
1653 tracktmp = (char *) calloc(1000 + strlen(trackdir), 1);
1654 }
1655
process_string(char * str)1656 static void process_string(char *str) {
1657 FILE *f;
1658 char *file;
1659 if (trackdir) {
1660 sprintf(tracktmp, "%s/0xprop.cmd", trackdir);
1661 file = strdup(tracktmp);
1662 } else {
1663 char tmp[] = "/tmp/x11vnc-appshare.cmd.XXXXXX";
1664 int fd = mkstemp(tmp);
1665 if (fd < 0) {
1666 return;
1667 }
1668 file = strdup(tmp);
1669 close(fd);
1670 }
1671 f = fopen(file, "w");
1672 if (f) {
1673 fprintf(f, "%s", str);
1674 fclose(f);
1675 process_control(file, 0);
1676 }
1677 unlink(file);
1678 free(file);
1679 }
1680
handle_shell(void)1681 static void handle_shell(void) {
1682 struct timeval tv;
1683 static char lastline[1000];
1684 static int first = 1;
1685 fd_set rfds;
1686 int fd0 = fileno(stdin);
1687
1688 if (first) {
1689 memset(lastline, 0, sizeof(lastline));
1690 first = 0;
1691 }
1692
1693 FD_ZERO(&rfds);
1694 FD_SET(fd0, &rfds);
1695 tv.tv_sec = 0;
1696 tv.tv_usec = 0;
1697 select(fd0+1, &rfds, NULL, NULL, &tv);
1698 if (FD_ISSET(fd0, &rfds)) {
1699 char line[1000], line2[1010];
1700 if (fgets(line, sizeof(line), stdin) != NULL) {
1701 char *str = lblanks(line);
1702 char *q = strrchr(str, '\n');
1703 if (q) *q = '\0';
1704 if (strcmp(str, "")) {
1705 if (!strcmp(str, "!!")) {
1706 sprintf(line, "%s", lastline);
1707 fprintf(stderr, "%s\n", line);
1708 str = line;
1709 }
1710 if (strstr(str, "!") == str) {
1711 system(str+1);
1712 } else if (!strcmp(str, "x11vnc") || !strcmp(str, "ps")) {
1713 char *cmd = "ps -elf | egrep 'PID|x11vnc' | grep -v egrep";
1714 fprintf(stderr, "%s\n", cmd);
1715 system(cmd);
1716 } else {
1717 sprintf(line2, "cmd=%s", str);
1718 process_string(line2);
1719 }
1720 sprintf(lastline, "%s", str);
1721 }
1722 }
1723 fprintf(stderr, "\n%s", prompt); ff();
1724 }
1725 }
1726
handle_prop_cmd(void)1727 static void handle_prop_cmd(void) {
1728 char *value, *str, *done = "DONE";
1729
1730 if (cmd_atom == None) {
1731 return;
1732 }
1733
1734 value = get_xprop(cmd_atom_str, root);
1735 if (value == NULL) {
1736 return;
1737 }
1738
1739 str = lblanks(value);
1740 if (!strcmp(str, done)) {
1741 free(value);
1742 return;
1743 }
1744 if (strstr(str, "cmd=quit") == str || strstr(str, "\ncmd=quit")) {
1745 set_xprop(cmd_atom_str, root, done);
1746 appshare_cleanup(0);
1747 }
1748
1749 process_string(str);
1750
1751 free(value);
1752 set_xprop(cmd_atom_str, root, done);
1753 }
1754
1755 #define PREFIX if(appshare_debug) fprintf(stderr, " %8.2f 0x%08lx : ", dnow() - start, ev.xany.window);
1756
monitor(void)1757 static void monitor(void) {
1758 #if !NO_X11
1759 XEvent ev;
1760 double start = dnow();
1761 int got_prop_cmd = 0;
1762
1763 if (shell) {
1764 update();
1765 fprintf(stderr, "\n\n");
1766 process_string("cmd=help");
1767 fprintf(stderr, "\n%s", prompt); ff();
1768 }
1769
1770 while (1) {
1771 int t;
1772
1773 if (XEventsQueued(dpy, QueuedAlready) == 0) {
1774 update();
1775 if (got_prop_cmd) {
1776 handle_prop_cmd();
1777 }
1778 got_prop_cmd = 0;
1779 if (shell) {
1780 handle_shell();
1781 }
1782 }
1783
1784 XNextEvent(dpy, &ev);
1785
1786 last_event_type = ev.type;
1787
1788 switch (ev.type) {
1789 case Expose:
1790 PREFIX
1791 if(appshare_debug) fprintf(stderr, "Expose %04dx%04d+%04d+%04d\n", ev.xexpose.width, ev.xexpose.height, ev.xexpose.x, ev.xexpose.y);
1792 break;
1793 case ConfigureNotify:
1794 #if 0
1795 PREFIX
1796 if(appshare_debug) fprintf(stderr, "ConfigureNotify %04dx%04d+%04d+%04d above: 0x%lx\n", ev.xconfigure.width, ev.xconfigure.height, ev.xconfigure.x, ev.xconfigure.y, ev.xconfigure.above);
1797 #endif
1798 break;
1799 case VisibilityNotify:
1800 PREFIX
1801 if (appshare_debug) {
1802 fprintf(stderr, "VisibilityNotify: ");
1803 t = ev.xvisibility.state;
1804 if (t == VisibilityFullyObscured) fprintf(stderr, "VisibilityFullyObscured\n");
1805 if (t == VisibilityPartiallyObscured) fprintf(stderr, "VisibilityPartiallyObscured\n");
1806 if (t == VisibilityUnobscured) fprintf(stderr, "VisibilityUnobscured\n");
1807 }
1808 break;
1809 case MapNotify:
1810 PREFIX
1811 if(appshare_debug) fprintf(stderr, "MapNotify win: 0x%lx\n", ev.xmap.window);
1812 if (ours(ev.xmap.window)) {
1813 mapped(ev.xmap.window);
1814 }
1815 break;
1816 case UnmapNotify:
1817 PREFIX
1818 if(appshare_debug) fprintf(stderr, "UnmapNotify win: 0x%lx\n", ev.xmap.window);
1819 if (ours(ev.xmap.window)) {
1820 unmapped(ev.xmap.window);
1821 }
1822 break;
1823 case MapRequest:
1824 PREFIX
1825 if(appshare_debug) fprintf(stderr, "MapRequest\n");
1826 break;
1827 case CreateNotify:
1828 PREFIX
1829 if(appshare_debug) fprintf(stderr, "CreateNotify parent: 0x%lx win: 0x%lx\n", ev.xcreatewindow.parent, ev.xcreatewindow.window);
1830 if (ev.xcreatewindow.parent == root && ours(ev.xcreatewindow.window)) {
1831 if (find_win(ev.xcreatewindow.window) >= 0) {
1832 destroy_win(ev.xcreatewindow.window);
1833 }
1834 add_win(ev.xcreatewindow.window);
1835 }
1836 break;
1837 case DestroyNotify:
1838 PREFIX
1839 if(appshare_debug) fprintf(stderr, "DestroyNotify win: 0x%lx\n", ev.xdestroywindow.window);
1840 if (ours(ev.xdestroywindow.window)) {
1841 destroy_win(ev.xdestroywindow.window);
1842 }
1843 break;
1844 case ConfigureRequest:
1845 PREFIX
1846 if(appshare_debug) fprintf(stderr, "ConfigureRequest\n");
1847 break;
1848 case CirculateRequest:
1849 #if 0
1850 PREFIX
1851 if(appshare_debug) fprintf(stderr, "CirculateRequest parent: 0x%lx win: 0x%lx\n", ev.xcirculaterequest.parent, ev.xcirculaterequest.window);
1852 #endif
1853 break;
1854 case CirculateNotify:
1855 #if 0
1856 PREFIX
1857 if(appshare_debug) fprintf(stderr, "CirculateNotify\n");
1858 #endif
1859 break;
1860 case PropertyNotify:
1861 #if 0
1862 PREFIX
1863 if(appshare_debug) fprintf(stderr, "PropertyNotify\n");
1864 #endif
1865 if (cmd_atom != None && ev.xproperty.atom == cmd_atom) {
1866 got_prop_cmd++;
1867 }
1868 break;
1869 case ReparentNotify:
1870 PREFIX
1871 if(appshare_debug) fprintf(stderr, "ReparentNotify parent: 0x%lx win: 0x%lx\n", ev.xreparent.parent, ev.xreparent.window);
1872 if (ours(ev.xreparent.window)) {
1873 if (ours(ev.xreparent.parent)) {
1874 destroy_win(ev.xreparent.window);
1875 } else if (ev.xreparent.parent == root) {
1876 /* ??? */
1877 }
1878 }
1879 break;
1880 default:
1881 PREFIX
1882 if(appshare_debug) fprintf(stderr, "Unknown: %d\n", ev.type);
1883 break;
1884 }
1885 }
1886 #endif
1887 }
1888
appshare_main(int argc,char * argv[])1889 int appshare_main(int argc, char *argv[]) {
1890 int i;
1891 char *app_str = NULL;
1892 char *dpy_str = NULL;
1893 long xselectinput = 0;
1894 #if NO_X11
1895 exiter("not compiled with X11\n", 1);
1896 #else
1897 for (i=0; i < WMAX; i++) {
1898 watch[i] = None;
1899 state[i] = 0;
1900 }
1901 for (i=0; i < AMAX; i++) {
1902 apps[i] = None;
1903 }
1904 for (i=0; i < CMAX; i++) {
1905 clients[i] = NULL;
1906 }
1907
1908 x11vnc = strdup(argv[0]);
1909
1910 for (i=1; i < argc; i++) {
1911 int end = (i == argc-1) ? 1 : 0;
1912 char *s = argv[i];
1913 if (strstr(s, "--") == s) {
1914 s++;
1915 }
1916
1917 if (!strcmp(s, "-h") || !strcmp(s, "-help")) {
1918 fprintf(stdout, "%s", usage);
1919 exit(0);
1920 } else if (!strcmp(s, "-id")) {
1921 id_opt = "-id";
1922 if (end) exiter("no -id value supplied\n", 1);
1923 app_str = strdup(argv[++i]);
1924 } else if (!strcmp(s, "-sid")) {
1925 id_opt = "-sid";
1926 if (end) exiter("no -sid value supplied\n", 1);
1927 app_str = strdup(argv[++i]);
1928 } else if (!strcmp(s, "-connect") || !strcmp(s, "-connect_or_exit") || !strcmp(s, "-coe")) {
1929 if (end) exiter("no -connect value supplied\n", 1);
1930 connect_to = strdup(argv[++i]);
1931 } else if (!strcmp(s, "-control")) {
1932 if (end) exiter("no -control value supplied\n", 1);
1933 control = strdup(argv[++i]);
1934 if (!strcmp(control, "shell")) {
1935 free(control);
1936 control = strdup("internal");
1937 shell = 1;
1938 }
1939 } else if (!strcmp(s, "-trackdir")) {
1940 if (end) exiter("no -trackdir value supplied\n", 1);
1941 trackdir = strdup(argv[++i]);
1942 } else if (!strcmp(s, "-display")) {
1943 if (end) exiter("no -display value supplied\n", 1);
1944 dpy_str = strdup(argv[++i]);
1945 set_env("DISPLAY", dpy_str);
1946 } else if (!strcmp(s, "-delay")) {
1947 if (end) exiter("no -delay value supplied\n", 1);
1948 helper_delay = atof(argv[++i]);
1949 } else if (!strcmp(s, "-args")) {
1950 if (end) exiter("no -args value supplied\n", 1);
1951 x11vnc_args = strdup(argv[++i]);
1952 } else if (!strcmp(s, "-env")) {
1953 if (end) exiter("no -env value supplied\n", 1);
1954 putenv(argv[++i]);
1955 } else if (!strcmp(s, "-debug")) {
1956 appshare_debug++;
1957 } else if (!strcmp(s, "-showmenus")) {
1958 skip_menus = 0;
1959 } else if (!strcmp(s, "-noexit")) {
1960 exit_no_app_win = 0;
1961 } else if (!strcmp(s, "-shell")) {
1962 shell = 1;
1963 } else if (!strcmp(s, "-nocmds") || !strcmp(s, "-safer")) {
1964 fprintf(stderr, "ignoring %s in -appshare mode.\n", s);
1965 } else if (!strcmp(s, "-appshare")) {
1966 ;
1967 } else {
1968 fprintf(stderr, "unrecognized 'x11vnc -appshare' option: %s\n", s);
1969 exiter("", 1);
1970 }
1971 }
1972
1973 if (getenv("X11VNC_APPSHARE_DEBUG")) {
1974 appshare_debug = atoi(getenv("X11VNC_APPSHARE_DEBUG"));
1975 }
1976
1977 /* let user override name for multiple instances: */
1978 if (getenv("X11VNC_APPSHARE_COMMAND_PROPNAME")) {
1979 cmd_atom_str = strdup(getenv("X11VNC_APPSHARE_COMMAND_PROPNAME"));
1980 }
1981 if (getenv("X11VNC_APPSHARE_TICKER_PROPNAME")) {
1982 ticker_atom_str = strdup(getenv("X11VNC_APPSHARE_TICKER_PROPNAME"));
1983 }
1984
1985 if (shell) {
1986 if (!control || strcmp(control, "internal")) {
1987 exiter("mode -shell requires '-control internal'\n", 1);
1988 }
1989 }
1990
1991 if (connect_to == NULL && control != NULL) {
1992 struct stat sb;
1993 if (stat(control, &sb) == 0) {
1994 int len = 100 + sb.st_size;
1995 FILE *f = fopen(control, "r");
1996
1997 if (f) {
1998 char *line = (char *) malloc(len);
1999 connect_to = (char *) calloc(2 * len, 1);
2000 while (fgets(line, len, f) != NULL) {
2001 char *q = strchr(line, '\n');
2002 if (q) *q = '\0';
2003 q = lblanks(line);
2004 if (q[0] == '#') {
2005 continue;
2006 }
2007 if (connect_to[0] != '\0') {
2008 strcat(connect_to, ",");
2009 }
2010 strcat(connect_to, q);
2011 }
2012 fclose(f);
2013 }
2014 fprintf(stderr, "set -connect to: %s\n", connect_to);
2015 }
2016 }
2017 if (0 && connect_to == NULL && control == NULL) {
2018 exiter("no -connect host or -control file specified.\n", 1);
2019 }
2020
2021 if (control) {
2022 pid_t pid;
2023 parent_pid = getpid();
2024 pid = fork();
2025 if (pid == (pid_t) -1) {
2026 ;
2027 } else if (pid == 0) {
2028 be_helper_pid(dpy_str);
2029 exit(0);
2030 } else {
2031 helper_pid = pid;
2032 }
2033 }
2034
2035 dpy = XOpenDisplay(dpy_str);
2036 if (!dpy) {
2037 exiter("cannot open display\n", 1);
2038 }
2039
2040 root = DefaultRootWindow(dpy);
2041
2042 xselectinput = SubstructureNotifyMask;
2043 if (helper_pid > 0) {
2044 ticker_atom = XInternAtom(dpy, ticker_atom_str, False);
2045 xselectinput |= PropertyChangeMask;
2046 }
2047 XSelectInput(dpy, root, xselectinput);
2048
2049 cmd_atom = XInternAtom(dpy, cmd_atom_str, False);
2050
2051 init_cmask();
2052
2053 sprintf(unique_tag, "X11VNC_APPSHARE_TAG=%d-tag", getpid());
2054
2055 start_time = dnow();
2056
2057 if (app_str == NULL) {
2058 exiter("no -id/-sid window specified.\n", 1);
2059 } else {
2060 char *p, *str = strdup(app_str);
2061 char *alist[AMAX];
2062 int i, n = 0;
2063
2064 p = strtok(str, ",");
2065 while (p) {
2066 if (n >= AMAX) {
2067 fprintf(stderr, "ran out of app slots: %s\n", app_str);
2068 exiter("", 1);
2069 }
2070 alist[n++] = strdup(p);
2071 p = strtok(NULL, ",");
2072 }
2073 free(str);
2074
2075 for (i=0; i < n; i++) {
2076 Window app = None;
2077 p = alist[i];
2078 app = parse_win(p);
2079 free(p);
2080
2081 if (app != None) {
2082 if (!ours(app)) {
2083 add_app(app);
2084 }
2085 }
2086 }
2087 }
2088
2089 set_trackdir();
2090
2091 signal(SIGINT, appshare_cleanup);
2092 signal(SIGTERM, appshare_cleanup);
2093
2094 rfbLogEnable(0);
2095
2096 if (connect_to) {
2097 char *p, *str = strdup(connect_to);
2098 int n = 0;
2099 p = strtok(str, ",");
2100 while (p) {
2101 clients[n++] = strdup(p);
2102 p = strtok(NULL, ",");
2103 }
2104 free(str);
2105 } else {
2106 connect_to = strdup("");
2107 }
2108
2109 for (i=0; i < AMAX; i++) {
2110 if (apps[i] == None) {
2111 continue;
2112 }
2113 fprintf(stdout, "Using app win: 0x%08lx root: 0x%08lx\n", apps[i], root);
2114 }
2115 fprintf(stdout, "\n");
2116
2117 monitor();
2118
2119 appshare_cleanup(0);
2120
2121 #endif
2122 return 0;
2123 }
2124
2125