Home | History | Annotate | Download | only in x11vnc
      1 /*
      2    Copyright (C) 2002-2010 Karl J. Runge <runge (at) karlrunge.com>
      3    All rights reserved.
      4 
      5 This file is part of x11vnc.
      6 
      7 x11vnc is free software; you can redistribute it and/or modify
      8 it under the terms of the GNU General Public License as published by
      9 the Free Software Foundation; either version 2 of the License, or (at
     10 your option) any later version.
     11 
     12 x11vnc is distributed in the hope that it will be useful,
     13 but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with x11vnc; if not, write to the Free Software
     19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
     20 or see <http://www.gnu.org/licenses/>.
     21 
     22 In addition, as a special exception, Karl J. Runge
     23 gives permission to link the code of its release of x11vnc with the
     24 OpenSSL project's "OpenSSL" library (or with modified versions of it
     25 that use the same license as the "OpenSSL" library), and distribute
     26 the linked executables.  You must obey the GNU General Public License
     27 in all respects for all of the code used other than "OpenSSL".  If you
     28 modify this file, you may extend this exception to your version of the
     29 file, but you are not obligated to do so.  If you do not wish to do
     30 so, delete this exception statement from your version.
     31 */
     32 
     33 /* -- 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 
    293 static void ff(void) {
    294 	fflush(stdout);
    295 	fflush(stderr);
    296 }
    297 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
   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 
   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 
   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 
   1103 static void destroy_win(Window win) {
   1104 	stop(win);
   1105 	delete_win(win);
   1106 }
   1107 
   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 
   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 
   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 
   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 
   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 
   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 
   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 
   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 
   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 
   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 
   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 
   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 
   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 
   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 
   1615 static void exiter(char *msg, int rc) {
   1616 	fprintf(stderr, "%s", msg);
   1617 	ff();
   1618 	kill_helper_pid();
   1619 	exit(rc);
   1620 }
   1621 
   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 
   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 
   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 
   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 
   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 
   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