2
Copyright (C) 2002-2010 Karl J. Runge <runge@karlrunge.com>
5
This file is part of x11vnc.
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.
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.
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/>.
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.
33
/* -- appshare.c -- */
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);
45
" x11vnc -appshare: an experiment in application sharing via x11vnc.\n"
48
" Usage: x11vnc -appshare -id windowid -connect viewer_host:0\n"
49
" x11vnc -appshare -id pick -connect viewer_host:0\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"
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"
58
" vncviewer -listen 0\n"
60
" The '-connect viewer_host:0' indicates the listening viewer to connect to.\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"
66
" ssh -R 5500:localhost:5500 user@server_host\n"
68
" (then use -connect localhost:0)\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"
75
" Additional options:\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"
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"
101
" You can also put cmd= lines in the control file to perform\n"
102
" different actions. These are supported:\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"
124
" See the '-command internal' mode described below for a way\n"
125
" that tracks connected clients internally (not in a file.)\n"
127
" In '-shell' mode (see below) you can type in the above\n"
128
" without the leading 'cmd='.\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"
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"
144
" -control internal Manage connected clients internally, see below.\n"
145
" -control shell Same as: -shell -control internal\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"
150
" -shell Simple command line for '-control internal' mode (see the\n"
151
" details of this mode below.) Enter '?' for command list.\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"
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"
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"
167
" xprop -root -f X11VNC_APPSHARE_COMMAND 8s -set X11VNC_APPSHARE_COMMAND cmd=quit\n"
169
" The property value will be set to 'DONE' after the command(s) is processed.\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"
179
" This is a quick lash-up, many things will not work properly.\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"
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"
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"
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"
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"
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"
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"
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"
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"
227
" Deleting a viewer window does not delete the real window.\n"
228
" Note: The SSVNC viewer Shift+EscapeKeys+Button3 deletes it.\n"
230
" Sometimes new window detection fails.\n"
232
" Sometimes menu/popup detection fails.\n"
234
" Sometimes the contents of a menu/popup window have blacked-out regions.\n"
235
" Try -sid or -showmenus as a workaround.\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"
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;
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);
293
static void ff(void) {
298
static int find_win(Window win) {
300
for (i=0; i < WMAX; i++) {
301
if (watch[i] == win) {
308
static int find_app(Window app) {
310
for (i=0; i < AMAX; i++) {
311
if (apps[i] == app) {
318
static int find_client(char *cl) {
320
for (i=0; i < CMAX; i++) {
322
if (clients[i] == NULL) {
327
if (clients[i] == NULL) {
330
if (!strcmp(clients[i], cl)) {
337
static int trackdir_pid(Window win) {
345
sprintf(tracktmp, "%s/0x%lx.log", trackdir, win);
346
f = fopen(tracktmp, "r");
350
while (fgets(line, sizeof(line), f) != NULL) {
354
if (strstr(line, "x11vnc version:")) {
355
char *q = strstr(line, "pid:");
358
if (sscanf(q, "pid: %d", &p) == 1) {
371
static void trackdir_cleanup(Window win) {
372
char *suffix[] = {"log", "connect", NULL};
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);
383
if (appshare_debug) {
384
fprintf(stderr, "removing: %s\n", tracktmp);
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;
399
/* maybe switch to debug only. */
405
get_wm_name(win, &name);
407
if (strstr(x11vnc_args, "-once")) {
414
for (i=0; i < CMAX; i++) {
415
if (clients[i] != NULL) {
416
len += strlen(clients[i]) + 2;
419
connto = (char *) calloc(len, 1);
420
for (i=0; i < CMAX; i++) {
421
if (clients[i] != NULL) {
422
if (connto[0] != '\0') {
425
strcat(connto, clients[i]);
429
connto = strdup(connect_to);
431
if (!strcmp(connto, "")) {
438
len = 1000 + strlen(x11vnc) + strlen(connto) + strlen(x11vnc_args)
439
+ 3 * (trackdir ? strlen(trackdir) : 100);
441
cmd = (char *) calloc(len, 1);
442
tmp = (char *) calloc(len, 1);
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);
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");
458
fprintf(f, "%s", connto);
460
sprintf(tmp, " -connect_or_exit '%s'", tracktmp);
463
sprintf(tmp, " -connect_or_exit '%s'", connto);
467
if (!strcmp(connto, "")) {
468
sprintf(tmp, " -connect '%s'", connto);
470
sprintf(tmp, " -connect_or_exit '%s'", connto);
475
char *q = strstr(cmd, "-connect_or_exit");
476
if (q) q = strstr(q, "_or_exit");
479
for (i=0; i < strlen("_or_exit"); i++) {
487
strcat(cmd, x11vnc_args);
489
fprintf(stdout, "launching: x11vnc for window 0x%08lx %dx%d+%d+%d \"%s\"\n",
490
win, w, h, x, y, name);
492
if (appshare_debug) {
493
fprintf(stderr, "\nrunning: %s\n\n", cmd);
505
static void stop(Window win) {
508
int f = find_win(win);
509
if (f < 0 || win == None) {
516
pid = trackdir_pid(win);
518
if (appshare_debug) {fprintf(stderr,
519
"sending SIGTERM to: %d\n", pid); ff();}
520
kill((pid_t) pid, SIGTERM);
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);
529
fprintf(stdout, "stopping: x11vnc for window 0x%08lx "
530
"(pid: %d)\n", win, pid);
535
sprintf(cmd, "(sleep 0.25 2>/dev/null || sleep 1; pkill -KILL -f '%s "
536
"%s 0x%lx -bg') &", x11vnc, id_opt, win);
540
trackdir_cleanup(win);
546
static void kill_helper_pid(void) {
548
if (helper_pid <= 0) {
551
fprintf(stderr, "stopping: helper_pid: %d\n", (int) helper_pid);
552
kill(helper_pid, SIGTERM);
554
kill(helper_pid, SIGKILL);
556
#if LIBVNCSERVER_HAVE_SYS_WAIT_H && LIBVNCSERVER_HAVE_WAITPID
557
waitpid(helper_pid, &status, WNOHANG);
561
static void be_helper_pid(char *dpy_str) {
563
int ms = (int) (1000 * helper_delay);
564
double last_check = 0.0;
566
if (ms < 50) ms = 50;
569
fprintf(stderr, "be_helper_pid: not compiled with X11.\n");
571
dpy = XOpenDisplay(dpy_str);
572
ticker_atom = XInternAtom(dpy, ticker_atom_str, False);
576
sprintf(tmp, "HELPER_CNT_%08d", cnt++);
577
XChangeProperty(dpy, DefaultRootWindow(dpy), ticker_atom, XA_STRING, 8,
578
PropModeReplace, (unsigned char *) tmp, strlen(tmp));
581
if (parent_pid > 0) {
582
if(dnow() > last_check + 1.0) {
584
if (kill(parent_pid, 0) != 0) {
585
fprintf(stderr, "be_helper_pid: parent %d is gone.\n", (int) parent_pid);
595
static void print_logs(void) {
597
DIR *dir = opendir(trackdir);
600
while ( (dp = readdir(dir)) != NULL) {
602
char *name = dp->d_name;
603
if (!strcmp(name, ".") || !strcmp(name, "..")) {
606
if (strstr(name, "0x") != name) {
609
if (strstr(name, ".log") == NULL) {
612
sprintf(tracktmp, "%s/%s", trackdir, name);
613
f = fopen(tracktmp, "r");
616
fprintf(stderr, "===== x11vnc log %s =====\n", tracktmp);
617
while (fgets(line, sizeof(line), f) != NULL) {
618
fprintf(stderr, "%s", line);
620
fprintf(stderr, "\n");
630
static void appshare_cleanup(int s) {
635
/* launch this backup in case they kill -9 us before we terminate everything */
637
sprintf(cmd, "(sleep 3; pkill -TERM -f '%s') &", unique_tag);
638
if (appshare_debug) fprintf(stderr, "%s\n", cmd);
642
for (i=0; i < WMAX; i++) {
643
if (watch[i] != None) {
649
DIR *dir = opendir(trackdir);
652
while ( (dp = readdir(dir)) != NULL) {
653
char *name = dp->d_name;
654
if (!strcmp(name, ".") || !strcmp(name, "..")) {
657
if (strstr(name, "0x") != name) {
658
fprintf(stderr, "skipping: %s\n", name);
661
if (!appshare_debug) {
662
fprintf(stderr, "removing: %s\n", name);
663
sprintf(tracktmp, "%s/%s", trackdir, name);
666
if (appshare_debug) fprintf(stderr, "keeping: %s\n", name);
671
if (!appshare_debug) {
672
if (strstr(trackdir, trackpre) == trackdir) {
673
if (appshare_debug) fprintf(stderr, "removing: %s\n", trackdir);
685
fprintf(stdout, "done.\n");
690
static int trap_xerror(Display *d, XErrorEvent *error) {
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 */
721
static void get_wm_name(Window win, char **name) {
725
XErrorHandler old_handler = XSetErrorHandler(trap_xerror);
726
ok = XFetchName(dpy, win, name);
727
XSetErrorHandler(old_handler);
730
if (!ok || *name == NULL) {
731
*name = strdup("unknown");
735
static int win_attr(Window win) {
738
XErrorHandler old_handler = XSetErrorHandler(trap_xerror);
739
ok = XGetWindowAttributes(dpy, win, &attr);
740
XSetErrorHandler(old_handler);
750
static void win_select(Window win, int ignore) {
752
XErrorHandler old_handler = XSetErrorHandler(trap_xerror);
754
XSelectInput(dpy, win, 0);
756
XSelectInput(dpy, win, SubstructureNotifyMask);
759
XSetErrorHandler(old_handler);
763
static Window get_parent(Window win) {
765
Window r, parent = None, *list = NULL;
769
XErrorHandler old_handler = XSetErrorHandler(trap_xerror);
770
ok = XQueryTree(dpy, win, &r, &parent, &list, &nchild);
771
XSetErrorHandler(old_handler);
783
static int get_xy(Window win, int *x, int *y) {
787
XErrorHandler old_handler = XSetErrorHandler(trap_xerror);
789
rc = XTranslateCoordinates(dpy, win, root, 0, 0, x, y, &cr);
790
XSetErrorHandler(old_handler);
800
static Window check_inside(Window win) {
803
int Ws[WMAX], Hs[WMAX], Xs[WMAX], Ys[WMAX];
806
if (!win_attr(win)) {
810
/* store them first to give the win app more time to settle. */
811
for (i=0; i < WMAX; i++) {
813
Window wchk = watch[i];
820
if (!win_attr(wchk)) {
823
if (!get_xy(wchk, &X, &Y)) {
829
Ws[nwin] = attr.width;
830
Hs[nwin] = attr.height;
839
if (!win_attr(win)) {
846
if (!get_xy(win, &x, &y)) {
850
for (i=0; i < nwin; i++) {
852
Window wchk = wins[i];
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);
860
if (X <= x && Y <= y) {
861
if (x + w <= X + W && y + h < Y + H) {
870
static void add_win(Window win) {
871
int idx = find_win(win);
872
int free = find_win(None);
874
if (appshare_debug) {fprintf(stderr, "already watching window: 0x%lx\n", win); ff();}
878
fprintf(stderr, "ran out of slots for window: 0x%lx\n", win); ff();
882
if (appshare_debug) {fprintf(stderr, "watching: 0x%lx at %d\n", win, free); ff();}
890
static void delete_win(Window win) {
892
for (i=0; i < WMAX; i++) {
893
if (watch[i] == win) {
896
if (appshare_debug) {fprintf(stderr, "deleting: 0x%lx at %d\n", win, i); ff();}
901
static void recurse_search(int level, int level_max, Window top, Window app, int *nw) {
902
Window w, r, parent, *list = NULL;
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);
909
if (level >= level_max) {
914
ok = XQueryTree(dpy, top, &r, &parent, &list, &nchild);
917
for (i=0; i < (int) nchild; i++) {
919
if (w == None || find_win(w) >= 0) {
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);
929
for (i=0; i < (int) nchild; i++) {
931
if (w == None || ours(w)) {
934
recurse_search(level+1, level_max, w, app, nw);
943
static void add_app(Window app) {
944
int i, nw = 0, free = -1;
945
XErrorHandler old_handler;
950
fprintf(stderr, "already tracking app: 0x%lx\n", app);
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]);
959
free = find_app(None);
961
fprintf(stderr, "ran out of app slots.\n");
968
old_handler = XSetErrorHandler(trap_xerror);
969
recurse_search(0, tree_depth, root, app, &nw);
970
XSetErrorHandler(old_handler);
972
fprintf(stderr, "tracking %d windows related to app window 0x%lx\n", nw, app);
975
static void del_app(Window app) {
977
for (i=0; i < WMAX; i++) {
978
Window win = watch[i];
980
if (same_app(app, win)) {
985
for (i=0; i < AMAX; i++) {
986
Window app2 = apps[i];
988
if (same_app(app, app2)) {
995
static void wait_until_empty(char *file) {
996
double t = 0.0, dt = 0.05;
999
if (stat(file, &sb) != 0) {
1002
if (sb.st_size == 0) {
1006
usleep( (int) (dt * 1000 * 1000) );
1010
static void client(char *client, int add) {
1018
fprintf(stderr, "no trackdir, cannot %s client: %s\n",
1019
add ? "add" : "disconnect", client);
1023
fprintf(stdout, "%s client: %s\n", add ? "adding " : "deleting", client);
1025
dir = opendir(trackdir);
1027
fprintf(stderr, "could not opendir trackdir: %s\n", trackdir);
1030
while ( (dp = readdir(dir)) != NULL) {
1031
char *name = dp->d_name;
1032
if (!strcmp(name, ".") || !strcmp(name, "..")) {
1035
if (strstr(name, "0x") != name) {
1038
if (strstr(name, ".connect")) {
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);
1054
tmp = (char *) calloc(100 + strlen(client), 1);
1055
sprintf(tracktmp, "%s/%s", trackdir, name);
1057
sprintf(tmp, "%s\n", client);
1059
sprintf(tmp, "cmd=close:%s\n", client);
1061
wait_until_empty(tracktmp);
1062
f = fopen(tracktmp, "w");
1064
if (appshare_debug) {
1065
fprintf(stderr, "%s client: %s + %s",
1066
add ? "add" : "disconnect", tracktmp, tmp);
1069
fprintf(f, "%s", tmp);
1078
static void mapped(Window win) {
1085
if (win_attr(win)) {
1086
if (get_parent(win) == root) {
1087
/* XXX more cases? */
1094
static void unmapped(Window win) {
1095
int f = find_win(win);
1096
if (f < 0 || win == None) {
1103
static void destroy_win(Window win) {
1108
static Window parse_win(char *str) {
1113
if (!strcmp(str, "pick") || !strcmp(str, "p")) {
1114
static double last_pick = 0.0;
1115
if (dnow() < start_time + 15) {
1117
} else if (dnow() < last_pick + 2) {
1122
if (!pick_windowid(&win)) {
1123
fprintf(stderr, "parse_win: bad window pick.\n");
1127
fprintf(stderr, "parse_win: ignoring pick of rootwin 0x%lx.\n", win);
1131
} else if (!scan_hexdec(str, &win)) {
1137
static void add_or_del_app(char *str, int add) {
1138
Window win = parse_win(str);
1146
} else if (!strcmp(str, "all")) {
1149
for (i=0; i < AMAX; i++) {
1150
if (apps[i] != None) {
1158
static void add_or_del_win(char *str, int add) {
1159
Window win = parse_win(str);
1162
int f = find_win(win);
1164
if (f < 0 && win_attr(win)) {
1172
} else if (!strcmp(str, "all")) {
1175
for (i=0; i < WMAX; i++) {
1176
if (watch[i] != None) {
1177
destroy_win(watch[i]);
1184
static void add_or_del_client(char *str, int add) {
1190
if (strcmp(control, "internal")) {
1194
int idx = find_client(str);
1195
int free = find_client(NULL);
1198
fprintf(stderr, "already tracking client: %s in slot %d\n", str, idx);
1205
fprintf(stderr, "ran out of client slots.\n");
1210
clients[free] = strdup(str);
1213
if (str[0] == '#' || str[0] == '%') {
1214
if (sscanf(str+1, "%d", &i) == 1) {
1216
if (0 <= i && i < CMAX) {
1217
if (clients[i] != NULL) {
1218
client(clients[i], 0);
1225
} else if (!strcmp(str, "all")) {
1226
for (i=0; i < CMAX; i++) {
1227
if (clients[i] == NULL) {
1230
client(clients[i], 0);
1237
i = find_client(str);
1246
static void restart_x11vnc(void) {
1248
Window win, active[WMAX];
1249
for (i=0; i < WMAX; i++) {
1260
usleep(1500 * 1000);
1262
for (i=0; i < n; i++) {
1268
static unsigned long cmask = 0x3fc00000; /* 00111111110000000000000000000000 */
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;
1275
if (getenv("X11VNC_APPSHARE_CLIENT_MASK")) {
1277
if (sscanf(getenv("X11VNC_APPSHARE_CLIENT_MASK"), "0x%lx", &cr) == 1) {
1281
} else if (getenv("X11VNC_APPSHARE_CLIENT_BITS")) {
1282
int cr = atoi(getenv("X11VNC_APPSHARE_CLIENT_BITS"));
1288
cmask = (((1 << client_bits) - 1) << (res_cnt-client_bits));
1290
fprintf(stderr, "client_mask: 0x%08lx\n", cmask);
1293
static int same_app(Window win, Window app) {
1294
if ( (win & cmask) == (app & cmask) ) {
1301
static int ours(Window win) {
1303
for (i=0; i < AMAX; i++) {
1304
if (apps[i] != None) {
1305
if (same_app(win, apps[i])) {
1313
static void list_clients(void) {
1315
for (i=0; i < CMAX; i++) {
1316
if (clients[i] == NULL) {
1319
fprintf(stdout, "client[%02d] %s\n", ++n, clients[i]);
1321
fprintf(stdout, "total clients: %d\n", n);
1325
static void list_windows(void) {
1327
for (i=0; i < WMAX; i++) {
1329
Window win = watch[i];
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);
1338
fprintf(stdout, "total windows: %d\n", n);
1342
static void list_apps(void) {
1344
for (i=0; i < AMAX; i++) {
1346
Window win = apps[i];
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);
1355
fprintf(stdout, "total apps: %d\n", n);
1359
static int process_control(char *file, int check_clients) {
1360
int i, nnew = 0, seen[CMAX];
1361
char line[1024], *new[CMAX];
1364
f = fopen(file, "r");
1368
if (check_clients) {
1369
for (i=0; i < CMAX; i++) {
1373
while (fgets(line, sizeof(line), f) != NULL) {
1374
char *q = strchr(line, '\n');
1377
if (appshare_debug) {
1378
fprintf(stderr, "check_control: %s\n", line);
1386
if (!strcmp(q, "")) {
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");
1396
appshare_cleanup(0);
1397
} else if (!strcmp(cmd, "wait")) {
1399
} else if (strstr(cmd, "bcast:") == cmd) {
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) {
1439
} else if (!strcmp(cmd, "restart")) {
1441
} else if (!strcmp(cmd, "list_clients") || !strcmp(cmd, "lc")) {
1443
} else if (!strcmp(cmd, "list_windows") || !strcmp(cmd, "lw")) {
1445
} else if (!strcmp(cmd, "list_apps") || !strcmp(cmd, "la")) {
1447
} else if (!strcmp(cmd, "list_all") || !strcmp(cmd, "ls")) {
1449
fprintf(stderr, "\n");
1451
fprintf(stderr, "\n");
1453
} else if (!strcmp(cmd, "print_logs") || !strcmp(cmd, "pl")) {
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");
1475
fprintf(stderr, "unrecognized %s\n", q);
1479
if (check_clients) {
1480
int idx = find_client(q);
1484
new[nnew++] = strdup(q);
1490
if (check_clients) {
1491
for (i=0; i < CMAX; i++) {
1492
if (clients[i] == NULL) {
1496
client(clients[i], 0);
1501
for (i=0; i < nnew; i++) {
1502
int free = find_client(NULL);
1506
fprintf(stderr, "ran out of client slots.\n");
1512
clients[free] = new[i];
1519
static int check_control(void) {
1520
static int last_size = -1;
1521
static time_t last_mtime = 0;
1529
if (!strcmp(control, "internal")) {
1533
control_cmd = (char *)malloc(strlen(control) + strlen(".cmd") + 1);
1534
sprintf(control_cmd, "%s.cmd", control);
1535
if (stat(control_cmd, &sb) == 0) {
1537
if (sb.st_size > 0) {
1538
process_control(control_cmd, 0);
1540
f = fopen(control_cmd, "w");
1547
if (stat(control, &sb) != 0) {
1550
if (last_size == (int) sb.st_size && last_mtime == sb.st_mtime) {
1553
last_size = (int) sb.st_size;
1554
last_mtime = sb.st_mtime;
1556
return process_control(control, 1);
1559
static void update(void) {
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");
1566
if (!check_control()) {
1569
for (i=0; i < WMAX; i++) {
1570
Window win = watch[i];
1574
if (!win_attr(win)) {
1578
if (find_app(win) >= 0) {
1581
if (state[i] == 0) {
1582
if (attr.map_state == IsViewable) {
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();}
1594
} else if (state[i] == 1) {
1595
if (attr.map_state != IsViewable) {
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]);
1608
appshare_cleanup(0);
1610
if (last_event_type != PropertyNotify) {
1611
if (appshare_debug) {fprintf(stderr, "update done.\n"); ff();}
1615
static void exiter(char *msg, int rc) {
1616
fprintf(stderr, "%s", msg);
1622
static void set_trackdir(void) {
1625
if (!strcmp(trackdir, "none")) {
1629
if (!strcmp(trackdir, "unset")) {
1631
sprintf(tmp, "%s.XXXXXX", trackpre);
1634
strcat(tmp, ": failed to create file.\n");
1640
if (mkdir(tmp, 0700) != 0) {
1641
strcat(tmp, ": failed to create dir.\n");
1644
trackdir = strdup(tmp);
1646
if (stat(trackdir, &sb) != 0) {
1647
if (mkdir(trackdir, 0700) != 0) {
1648
exiter("could not make trackdir.\n", 1);
1650
} else if (! S_ISDIR(sb.st_mode)) {
1651
exiter("trackdir not a directory.\n", 1);
1653
tracktmp = (char *) calloc(1000 + strlen(trackdir), 1);
1656
static void process_string(char *str) {
1660
sprintf(tracktmp, "%s/0xprop.cmd", trackdir);
1661
file = strdup(tracktmp);
1663
char tmp[] = "/tmp/x11vnc-appshare.cmd.XXXXXX";
1664
int fd = mkstemp(tmp);
1671
f = fopen(file, "w");
1673
fprintf(f, "%s", str);
1675
process_control(file, 0);
1681
static void handle_shell(void) {
1683
static char lastline[1000];
1684
static int first = 1;
1686
int fd0 = fileno(stdin);
1689
memset(lastline, 0, sizeof(lastline));
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');
1704
if (strcmp(str, "")) {
1705
if (!strcmp(str, "!!")) {
1706
sprintf(line, "%s", lastline);
1707
fprintf(stderr, "%s\n", line);
1710
if (strstr(str, "!") == str) {
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);
1717
sprintf(line2, "cmd=%s", str);
1718
process_string(line2);
1720
sprintf(lastline, "%s", str);
1723
fprintf(stderr, "\n%s", prompt); ff();
1727
static void handle_prop_cmd(void) {
1728
char *value, *str, *done = "DONE";
1730
if (cmd_atom == None) {
1734
value = get_xprop(cmd_atom_str, root);
1735
if (value == NULL) {
1739
str = lblanks(value);
1740
if (!strcmp(str, done)) {
1744
if (strstr(str, "cmd=quit") == str || strstr(str, "\ncmd=quit")) {
1745
set_xprop(cmd_atom_str, root, done);
1746
appshare_cleanup(0);
1749
process_string(str);
1752
set_xprop(cmd_atom_str, root, done);
1755
#define PREFIX if(appshare_debug) fprintf(stderr, " %8.2f 0x%08lx : ", dnow() - start, ev.xany.window);
1757
static void monitor(void) {
1760
double start = dnow();
1761
int got_prop_cmd = 0;
1765
fprintf(stderr, "\n\n");
1766
process_string("cmd=help");
1767
fprintf(stderr, "\n%s", prompt); ff();
1773
if (XEventsQueued(dpy, QueuedAlready) == 0) {
1784
XNextEvent(dpy, &ev);
1786
last_event_type = ev.type;
1791
if(appshare_debug) fprintf(stderr, "Expose %04dx%04d+%04d+%04d\n", ev.xexpose.width, ev.xexpose.height, ev.xexpose.x, ev.xexpose.y);
1793
case ConfigureNotify:
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);
1799
case VisibilityNotify:
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");
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);
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);
1825
if(appshare_debug) fprintf(stderr, "MapRequest\n");
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);
1834
add_win(ev.xcreatewindow.window);
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);
1844
case ConfigureRequest:
1846
if(appshare_debug) fprintf(stderr, "ConfigureRequest\n");
1848
case CirculateRequest:
1851
if(appshare_debug) fprintf(stderr, "CirculateRequest parent: 0x%lx win: 0x%lx\n", ev.xcirculaterequest.parent, ev.xcirculaterequest.window);
1854
case CirculateNotify:
1857
if(appshare_debug) fprintf(stderr, "CirculateNotify\n");
1860
case PropertyNotify:
1863
if(appshare_debug) fprintf(stderr, "PropertyNotify\n");
1865
if (cmd_atom != None && ev.xproperty.atom == cmd_atom) {
1869
case ReparentNotify:
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) {
1882
if(appshare_debug) fprintf(stderr, "Unknown: %d\n", ev.type);
1889
int appshare_main(int argc, char *argv[]) {
1891
char *app_str = NULL;
1892
char *dpy_str = NULL;
1893
long xselectinput = 0;
1895
exiter("not compiled with X11\n", 1);
1897
for (i=0; i < WMAX; i++) {
1901
for (i=0; i < AMAX; i++) {
1904
for (i=0; i < CMAX; i++) {
1908
x11vnc = strdup(argv[0]);
1910
for (i=1; i < argc; i++) {
1911
int end = (i == argc-1) ? 1 : 0;
1913
if (strstr(s, "--") == s) {
1917
if (!strcmp(s, "-h") || !strcmp(s, "-help")) {
1918
fprintf(stdout, "%s", usage);
1920
} else if (!strcmp(s, "-id")) {
1922
if (end) exiter("no -id value supplied\n", 1);
1923
app_str = strdup(argv[++i]);
1924
} else if (!strcmp(s, "-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")) {
1936
control = strdup("internal");
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);
1955
} else if (!strcmp(s, "-debug")) {
1957
} else if (!strcmp(s, "-showmenus")) {
1959
} else if (!strcmp(s, "-noexit")) {
1960
exit_no_app_win = 0;
1961
} else if (!strcmp(s, "-shell")) {
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")) {
1968
fprintf(stderr, "unrecognized 'x11vnc -appshare' option: %s\n", s);
1973
if (getenv("X11VNC_APPSHARE_DEBUG")) {
1974
appshare_debug = atoi(getenv("X11VNC_APPSHARE_DEBUG"));
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"));
1981
if (getenv("X11VNC_APPSHARE_TICKER_PROPNAME")) {
1982
ticker_atom_str = strdup(getenv("X11VNC_APPSHARE_TICKER_PROPNAME"));
1986
if (!control || strcmp(control, "internal")) {
1987
exiter("mode -shell requires '-control internal'\n", 1);
1991
if (connect_to == NULL && control != NULL) {
1993
if (stat(control, &sb) == 0) {
1994
int len = 100 + sb.st_size;
1995
FILE *f = fopen(control, "r");
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');
2007
if (connect_to[0] != '\0') {
2008
strcat(connect_to, ",");
2010
strcat(connect_to, q);
2014
fprintf(stderr, "set -connect to: %s\n", connect_to);
2017
if (0 && connect_to == NULL && control == NULL) {
2018
exiter("no -connect host or -control file specified.\n", 1);
2023
parent_pid = getpid();
2025
if (pid == (pid_t) -1) {
2027
} else if (pid == 0) {
2028
be_helper_pid(dpy_str);
2035
dpy = XOpenDisplay(dpy_str);
2037
exiter("cannot open display\n", 1);
2040
root = DefaultRootWindow(dpy);
2042
xselectinput = SubstructureNotifyMask;
2043
if (helper_pid > 0) {
2044
ticker_atom = XInternAtom(dpy, ticker_atom_str, False);
2045
xselectinput |= PropertyChangeMask;
2047
XSelectInput(dpy, root, xselectinput);
2049
cmd_atom = XInternAtom(dpy, cmd_atom_str, False);
2053
sprintf(unique_tag, "X11VNC_APPSHARE_TAG=%d-tag", getpid());
2055
start_time = dnow();
2057
if (app_str == NULL) {
2058
exiter("no -id/-sid window specified.\n", 1);
2060
char *p, *str = strdup(app_str);
2064
p = strtok(str, ",");
2067
fprintf(stderr, "ran out of app slots: %s\n", app_str);
2070
alist[n++] = strdup(p);
2071
p = strtok(NULL, ",");
2075
for (i=0; i < n; i++) {
2091
signal(SIGINT, appshare_cleanup);
2092
signal(SIGTERM, appshare_cleanup);
2097
char *p, *str = strdup(connect_to);
2099
p = strtok(str, ",");
2101
clients[n++] = strdup(p);
2102
p = strtok(NULL, ",");
2106
connect_to = strdup("");
2109
for (i=0; i < AMAX; i++) {
2110
if (apps[i] == None) {
2113
fprintf(stdout, "Using app win: 0x%08lx root: 0x%08lx\n", apps[i], root);
2115
fprintf(stdout, "\n");
2119
appshare_cleanup(0);