You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2117 lines
54 KiB
2117 lines
54 KiB
15 years ago
|
/*
|
||
|
Copyright (C) 2002-2009 Karl J. Runge <runge@karlrunge.com>
|
||
|
All rights reserved.
|
||
|
|
||
|
This file is part of x11vnc.
|
||
|
|
||
|
x11vnc is free software; you can redistribute it and/or modify
|
||
|
it under the terms of the GNU General Public License as published by
|
||
|
the Free Software Foundation; either version 2 of the License, or (at
|
||
|
your option) any later version.
|
||
|
|
||
|
x11vnc is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
GNU General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License
|
||
|
along with x11vnc; if not, write to the Free Software
|
||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
|
||
|
or see <http://www.gnu.org/licenses/>.
|
||
|
|
||
|
In addition, as a special exception, Karl J. Runge
|
||
|
gives permission to link the code of its release of x11vnc with the
|
||
|
OpenSSL project's "OpenSSL" library (or with modified versions of it
|
||
|
that use the same license as the "OpenSSL" library), and distribute
|
||
|
the linked executables. You must obey the GNU General Public License
|
||
|
in all respects for all of the code used other than "OpenSSL". If you
|
||
|
modify this file, you may extend this exception to your version of the
|
||
|
file, but you are not obligated to do so. If you do not wish to do
|
||
|
so, delete this exception statement from your version.
|
||
|
*/
|
||
|
|
||
|
/* -- appshare.c -- */
|
||
|
|
||
|
#include "x11vnc.h"
|
||
|
|
||
|
extern int pick_windowid(unsigned long *num);
|
||
|
extern char *get_xprop(char *prop, Window win);
|
||
|
extern int set_xprop(char *prop, Window win, char *value);
|
||
|
extern void set_env(char *name, char *value);
|
||
|
extern double dnow(void);
|
||
|
|
||
|
static char *usage =
|
||
|
"\n"
|
||
|
" x11vnc -appshare: an experiment in application sharing via x11vnc.\n"
|
||
|
"\n"
|
||
|
" Usage: x11vnc -appshare -id windowid -connect viewer_host:0\n"
|
||
|
" x11vnc -appshare -id pick -connect viewer_host:0\n"
|
||
|
"\n"
|
||
|
" Both the -connect option and the -id (or -sid) option are required.\n"
|
||
|
" (However see the -control option below that can replace -connect.)\n"
|
||
|
"\n"
|
||
|
" The VNC viewer at viewer_host MUST be in 'listen' mode. This is because\n"
|
||
|
" a new VNC connection (and viewer window) is established for each new\n"
|
||
|
" toplevel window that the application creates. For example:\n"
|
||
|
"\n"
|
||
|
" vncviewer -listen 0\n"
|
||
|
"\n"
|
||
|
" The '-connect viewer_host:0' indicates the listening viewer to connect to.\n"
|
||
|
"\n"
|
||
|
" No password should be used, otherwise it will need to be typed for each\n"
|
||
|
" new window (or one could use vncviewer -passwd file if the viewer supports\n"
|
||
|
" that.) For security an SSH tunnel can be used:\n"
|
||
|
"\n"
|
||
|
" ssh -R 5500:localhost:5500 user@server_host\n"
|
||
|
"\n"
|
||
|
" (then use -connect localhost:0)\n"
|
||
|
"\n"
|
||
|
" The -id/-sid option is as in x11vnc(1). It is either a numerical window\n"
|
||
|
" id or the string 'pick' which will ask the user to click on an app window.\n"
|
||
|
" To track more than one application at the same time, list their window ids\n"
|
||
|
" separated by commas (see also the 'add_app' command below.)\n"
|
||
|
"\n"
|
||
|
" Additional options:\n"
|
||
|
"\n"
|
||
|
" -h, -help Print this help.\n"
|
||
|
" -debug Print debugging output (same as X11VNC_APPSHARE_DEBUG=1)\n"
|
||
|
" -showmenus Create a new viewer window even if a new window is\n"
|
||
|
" completely inside of an existing one. Default is to\n"
|
||
|
" try to not show them in a new viewer window.\n"
|
||
|
" -noexit Do not exit if the main app (windowid/pick) window\n"
|
||
|
" goes away. Default is to exit.\n"
|
||
|
" -display dpy X DISPLAY to use.\n"
|
||
|
" -trackdir dir Set tracking directory to 'dir'. x11vnc -appshare does\n"
|
||
|
" better if it can communicate with the x11vnc's via a\n"
|
||
|
" file channel. By default a dir in /tmp is used, -trackdir\n"
|
||
|
" specifies another directory, or use 'none' to disable.\n"
|
||
|
" -args 'string' Pass options 'string' to x11vnc (e.g. -scale 3/4,\n"
|
||
|
" -viewonly, -wait, -once, etc.)\n"
|
||
|
" -env VAR=VAL Set environment variables on cmdline as in x11vnc.\n"
|
||
|
"\n"
|
||
|
" -control file This is a file that one edits to manage the appshare\n"
|
||
|
" mode. It replaces -connect. Lines beginning with '#'\n"
|
||
|
" are ignored. Initially start off with all of the\n"
|
||
|
" desired clients in the file, one per line. If you add\n"
|
||
|
" a new client-line, that client is connected to. If you\n"
|
||
|
" delete (or comment out) a client-line, that client is\n"
|
||
|
" disconnected (for this to work, do not disable trackdir.)\n"
|
||
|
"\n"
|
||
|
" You can also put cmd= lines in the control file to perform\n"
|
||
|
" different actions. These are supported:\n"
|
||
|
"\n"
|
||
|
" cmd=quit Disconnect all clients and exit.\n"
|
||
|
" cmd=restart Restart all of the x11vnc's.\n"
|
||
|
" cmd=noop Do nothing (e.g. ping)\n"
|
||
|
" cmd=x11vnc Run ps(1) looking for x11vnc's\n"
|
||
|
" cmd=help Print out help text.\n"
|
||
|
" cmd=add_window:win Add a window to be watched.\n"
|
||
|
" cmd=del_window:win Delete a window.\n"
|
||
|
" cmd=add_app:win Add an application to be watched.\n"
|
||
|
" cmd=del_app:win Delete an application.\n"
|
||
|
" cmd=add_client:host Add client ('internal' mode only)\n"
|
||
|
" cmd=del_client:host Del client ('internal' mode only)\n"
|
||
|
" cmd=list_windows List all tracked windows.\n"
|
||
|
" cmd=list_apps List all tracked applications.\n"
|
||
|
" cmd=list_clients List all connected clients.\n"
|
||
|
" cmd=list_all List all three.\n"
|
||
|
" cmd=print_logs Print out the x11vnc logfiles.\n"
|
||
|
" cmd=debug:n Set -debug to n (0 or 1).\n"
|
||
|
" cmd=showmenus:n Set -showmenus to n (0 or 1).\n"
|
||
|
" cmd=noexit:n Set -noexit to n (0 or 1).\n"
|
||
|
"\n"
|
||
|
" See the '-command internal' mode described below for a way\n"
|
||
|
" that tracks connected clients internally (not in a file.)\n"
|
||
|
"\n"
|
||
|
" In '-shell' mode (see below) you can type in the above\n"
|
||
|
" without the leading 'cmd='.\n"
|
||
|
"\n"
|
||
|
" For 'add_window' and 'del_window' the 'win' can be a\n"
|
||
|
" numerical window id or 'pick'. Same for 'add_app'. Be\n"
|
||
|
" sure to remove or comment out the add/del line quickly\n"
|
||
|
" (e.g. before picking) or it will be re-run the next time\n"
|
||
|
" the file is processed.\n"
|
||
|
"\n"
|
||
|
" If a file with the same name as the control file but\n"
|
||
|
" ending with suffix '.cmd' is found, then commands in it\n"
|
||
|
" (cmd=...) are processed and then the file is truncated.\n"
|
||
|
" This allows 'one time' command actions to be run. Any\n"
|
||
|
" client hostnames in the '.cmd' file are ignored. Also\n"
|
||
|
" see below for the X11VNC_APPSHARE_COMMAND X property\n"
|
||
|
" which is similar to '.cmd'\n"
|
||
|
"\n"
|
||
|
" -control internal Manage connected clients internally, see below.\n"
|
||
|
" -control shell Same as: -shell -control internal\n"
|
||
|
"\n"
|
||
|
" -delay secs Maximum timeout delay before re-checking the control file.\n"
|
||
|
" It can be a fraction, e.g. -delay 0.25 Default 0.5\n"
|
||
|
"\n"
|
||
|
" -shell Simple command line for '-control internal' mode (see the\n"
|
||
|
" details of this mode below.) Enter '?' for command list.\n"
|
||
|
"\n"
|
||
|
" To stop x11vnc -appshare press Ctrl-C, or (if -noexit not supplied) delete\n"
|
||
|
" the initial app window or exit the application. Or cmd=quit in -control mode.\n"
|
||
|
"\n"
|
||
|
#if 0
|
||
|
" If you want your setup to survive periods of time where there are no clients\n"
|
||
|
" connected you will need to supply -args '-forever' otherwise the x11vnc's\n"
|
||
|
" will exit when the last client disconnects. Howerver, _starting_ with no\n"
|
||
|
" clients (e.g. empty control file) will work without -args '-forever'.\n"
|
||
|
"\n"
|
||
|
#endif
|
||
|
" In addition to the '.cmd' file channel, for faster response you can set\n"
|
||
|
" X11VNC_APPSHARE_COMMAND X property on the root window to the string that\n"
|
||
|
" would go into the '.cmd' file. For example:\n"
|
||
|
"\n"
|
||
|
" xprop -root -f X11VNC_APPSHARE_COMMAND 8s -set X11VNC_APPSHARE_COMMAND cmd=quit\n"
|
||
|
"\n"
|
||
|
" The property value will be set to 'DONE' after the command(s) is processed.\n"
|
||
|
"\n"
|
||
|
" If -control file is specified as 'internal' then no control file is used\n"
|
||
|
" and client tracking is done internally. You must add and delete clients\n"
|
||
|
" with the cmd=add_client:<client> and cmd=del_client:<client> commands.\n"
|
||
|
" Note that '-control internal' is required for '-shell' mode. Using\n"
|
||
|
" '-control shell' implies internal mode and -shell.\n"
|
||
|
"\n"
|
||
|
" Limitations:\n"
|
||
|
"\n"
|
||
|
" This is a quick lash-up, many things will not work properly.\n"
|
||
|
"\n"
|
||
|
" The main idea is to provide simple application sharing for two or more\n"
|
||
|
" parties to collaborate without needing to share the entire desktop. It\n"
|
||
|
" provides an improvement over -id/-sid that only shows a single window.\n"
|
||
|
"\n"
|
||
|
" Only reverse connections can be done. (Note: one can specify multiple\n"
|
||
|
" viewing hosts via: -connect host1,host2,host3 or add/remove them\n"
|
||
|
" dynamically as described above.)\n"
|
||
|
"\n"
|
||
|
" If a new window obscures an old one, you will see some or all of the\n"
|
||
|
" new window in the old one. The hope is this is a popup dialog or menu\n"
|
||
|
" that will go away soon. Otherwise a user at the physical display will\n"
|
||
|
" need to move it. (See also the SSVNC viewer features described below.) \n"
|
||
|
"\n"
|
||
|
" The viewer side cannot resize or make windows move on the physical\n"
|
||
|
" display. Again, a user at the physical display may need to help, or\n"
|
||
|
" use the SSVNC viewer (see Tip below.)\n"
|
||
|
"\n"
|
||
|
" Tip: If the application has its own 'resize corner', then dragging\n"
|
||
|
" it may successfully resize the application window.\n"
|
||
|
" Tip: Some desktop environments enable moving a window via, say,\n"
|
||
|
" Alt+Left-Button-Drag. One may be able to move a window this way.\n"
|
||
|
" Also, e.g., Alt+Right-Button-Drag may resize a window.\n"
|
||
|
" Tip: Clicking on part of an obscured window may raise it to the top.\n"
|
||
|
" Also, e.g., Alt+Middle-Button may toggle Raise/Lower.\n"
|
||
|
"\n"
|
||
|
" Tip: The SSVNC 1.0.25 unix and macosx vncviewer has 'EscapeKeys' hot\n"
|
||
|
" keys that will move, resize, raise, and lower the window via the\n"
|
||
|
" x11vnc -remote_prefix X11VNC_APPSHARE_CMD: feature. So in the\n"
|
||
|
" viewer while holding down Shift_L+Super_L+Alt_L the arrow keys\n"
|
||
|
" move the window, PageUp/PageDn/Home/End resize it, and - and +\n"
|
||
|
" raise and lower it. Key 'M' or Button1 moves the remote window\n"
|
||
|
" to the +X+Y of the viewer window. Key 'D' or Button3 deletes\n"
|
||
|
" the remote window.\n"
|
||
|
"\n"
|
||
|
" You can run the SSVNC vncviewer with options '-escape default',\n"
|
||
|
" '-multilisten' and '-env VNCVIEWER_MIN_TITLE=1'; or just run\n"
|
||
|
" with option '-appshare' to enable these and automatic placement.\n"
|
||
|
"\n"
|
||
|
" If any part of a window goes off of the display screen, then x11vnc\n"
|
||
|
" may be unable to poll it (without crashing), and so the window will\n"
|
||
|
" stop updating until the window is completely on-screen again.\n"
|
||
|
"\n"
|
||
|
" The (stock) vnc viewer does not know where to best position each new\n"
|
||
|
" viewer window; it likely centers each one (including when resized.)\n"
|
||
|
" Note: The SSVNC viewer in '-appshare' mode places them correctly.\n"
|
||
|
"\n"
|
||
|
" Deleting a viewer window does not delete the real window.\n"
|
||
|
" Note: The SSVNC viewer Shift+EscapeKeys+Button3 deletes it.\n"
|
||
|
"\n"
|
||
|
" Sometimes new window detection fails.\n"
|
||
|
"\n"
|
||
|
" Sometimes menu/popup detection fails.\n"
|
||
|
"\n"
|
||
|
" Sometimes the contents of a menu/popup window have blacked-out regions.\n"
|
||
|
" Try -sid or -showmenus as a workaround.\n"
|
||
|
"\n"
|
||
|
" If the application starts up a new application (a different process)\n"
|
||
|
" that new application will not be tracked (but, unfortunately, it may\n"
|
||
|
" cover up existing windows that are being tracked.) See cmd=add_window\n"
|
||
|
" and cmd=add_app described above.\n"
|
||
|
"\n"
|
||
|
;
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#define WMAX 192
|
||
|
#define CMAX 128
|
||
|
#define AMAX 32
|
||
|
|
||
|
static Window root = None;
|
||
|
static Window watch[WMAX];
|
||
|
static Window apps[WMAX];
|
||
|
static int state[WMAX];
|
||
|
static char *clients[CMAX];
|
||
|
static XWindowAttributes attr;
|
||
|
static char *ticker_atom_str = "X11VNC_APPSHARE_TICKER";
|
||
|
static Atom ticker_atom = None;
|
||
|
static char *cmd_atom_str = "X11VNC_APPSHARE_COMMAND";
|
||
|
static Atom cmd_atom = None;
|
||
|
static char *connect_to = NULL;
|
||
|
static char *x11vnc_args = "";
|
||
|
static char *id_opt = "-id";
|
||
|
static int skip_menus = 1;
|
||
|
static int exit_no_app_win = 1;
|
||
|
static int shell = 0;
|
||
|
static int tree_depth = 3;
|
||
|
static char *prompt = "appshare> ";
|
||
|
static char *x11vnc = "x11vnc";
|
||
|
static char *control = NULL;
|
||
|
static char *trackdir = "unset";
|
||
|
static char *trackpre = "/tmp/x11vnc-appshare-trackdir-tmp";
|
||
|
static char *tracktmp = NULL;
|
||
|
static char unique_tag[100];
|
||
|
static int use_forever = 1;
|
||
|
static int last_event_type = 0;
|
||
|
static pid_t helper_pid = 0;
|
||
|
static pid_t parent_pid = 0;
|
||
|
static double helper_delay = 0.5;
|
||
|
static int appshare_debug = 0;
|
||
|
static double start_time = 0.0;
|
||
|
|
||
|
static void get_wm_name(Window win, char **name);
|
||
|
static int win_attr(Window win);
|
||
|
static int get_xy(Window win, int *x, int *y);
|
||
|
static Window check_inside(Window win);
|
||
|
static int ours(Window win);
|
||
|
static void destroy_win(Window win);
|
||
|
static int same_app(Window win, Window app);
|
||
|
|
||
|
static void ff(void) {
|
||
|
fflush(stdout);
|
||
|
fflush(stderr);
|
||
|
}
|
||
|
|
||
|
static int find_win(Window win) {
|
||
|
int i;
|
||
|
for (i=0; i < WMAX; i++) {
|
||
|
if (watch[i] == win) {
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
static int find_app(Window app) {
|
||
|
int i;
|
||
|
for (i=0; i < AMAX; i++) {
|
||
|
if (apps[i] == app) {
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
static int find_client(char *cl) {
|
||
|
int i;
|
||
|
for (i=0; i < CMAX; i++) {
|
||
|
if (cl == NULL) {
|
||
|
if (clients[i] == NULL) {
|
||
|
return i;
|
||
|
}
|
||
|
continue;
|
||
|
}
|
||
|
if (clients[i] == NULL) {
|
||
|
continue;
|
||
|
}
|
||
|
if (!strcmp(clients[i], cl)) {
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
static int trackdir_pid(Window win) {
|
||
|
FILE *f;
|
||
|
int ln = 0, pid = 0;
|
||
|
char line[1024];
|
||
|
|
||
|
if (!trackdir) {
|
||
|
return 0;
|
||
|
}
|
||
|
sprintf(tracktmp, "%s/0x%lx.log", trackdir, win);
|
||
|
f = fopen(tracktmp, "r");
|
||
|
if (!f) {
|
||
|
return 0;
|
||
|
}
|
||
|
while (fgets(line, sizeof(line), f) != NULL) {
|
||
|
if (ln++ > 30) {
|
||
|
break;
|
||
|
}
|
||
|
if (strstr(line, "x11vnc version:")) {
|
||
|
char *q = strstr(line, "pid:");
|
||
|
if (q) {
|
||
|
int p;
|
||
|
if (sscanf(q, "pid: %d", &p) == 1) {
|
||
|
if (p > 0) {
|
||
|
pid = p;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
fclose(f);
|
||
|
return pid;
|
||
|
}
|
||
|
|
||
|
static void trackdir_cleanup(Window win) {
|
||
|
char *suffix[] = {"log", "connect", NULL};
|
||
|
int i=0;
|
||
|
if (!trackdir) {
|
||
|
return;
|
||
|
}
|
||
|
while (suffix[i] != NULL) {
|
||
|
sprintf(tracktmp, "%s/0x%lx.%s", trackdir, win, suffix[i]);
|
||
|
if (appshare_debug && !strcmp(suffix[i], "log")) {
|
||
|
fprintf(stderr, "keeping: %s\n", tracktmp);
|
||
|
ff();
|
||
|
} else {
|
||
|
if (appshare_debug) {
|
||
|
fprintf(stderr, "removing: %s\n", tracktmp);
|
||
|
ff();
|
||
|
}
|
||
|
unlink(tracktmp);
|
||
|
}
|
||
|
i++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void launch(Window win) {
|
||
|
char *cmd, *tmp, *connto, *name;
|
||
|
int len, timeo = 30, uf = use_forever;
|
||
|
int w = 0, h = 0, x = 0, y = 0;
|
||
|
|
||
|
if (win_attr(win)) {
|
||
|
/* maybe switch to debug only. */
|
||
|
w = attr.width;
|
||
|
h = attr.height;
|
||
|
get_xy(win, &x, &y);
|
||
|
}
|
||
|
|
||
|
get_wm_name(win, &name);
|
||
|
|
||
|
if (strstr(x11vnc_args, "-once")) {
|
||
|
uf = 0;
|
||
|
}
|
||
|
|
||
|
if (control) {
|
||
|
int i = 0;
|
||
|
len = 0;
|
||
|
for (i=0; i < CMAX; i++) {
|
||
|
if (clients[i] != NULL) {
|
||
|
len += strlen(clients[i]) + 2;
|
||
|
}
|
||
|
}
|
||
|
connto = (char *) calloc(len, 1);
|
||
|
for (i=0; i < CMAX; i++) {
|
||
|
if (clients[i] != NULL) {
|
||
|
if (connto[0] != '\0') {
|
||
|
strcat(connto, ",");
|
||
|
}
|
||
|
strcat(connto, clients[i]);
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
connto = strdup(connect_to);
|
||
|
}
|
||
|
if (!strcmp(connto, "")) {
|
||
|
timeo = 0;
|
||
|
}
|
||
|
if (uf) {
|
||
|
timeo = 0;
|
||
|
}
|
||
|
|
||
|
len = 1000 + strlen(x11vnc) + strlen(connto) + strlen(x11vnc_args)
|
||
|
+ 3 * (trackdir ? strlen(trackdir) : 100);
|
||
|
|
||
|
cmd = (char *) calloc(len, 1);
|
||
|
tmp = (char *) calloc(len, 1);
|
||
|
|
||
|
sprintf(cmd, "%s %s 0x%lx -bg -quiet %s -nopw -rfbport 0 "
|
||
|
"-timeout %d -noxdamage -noxinerama -norc -repeat -speeds dsl "
|
||
|
"-env X11VNC_AVOID_WINDOWS=never -env X11VNC_APPSHARE_ACTIVE=1 "
|
||
|
"-env X11VNC_NO_CHECK_PM=1 -env %s -novncconnect -shared -nonap "
|
||
|
"-remote_prefix X11VNC_APPSHARE_CMD:",
|
||
|
x11vnc, id_opt, win, use_forever ? "-forever" : "-once", timeo, unique_tag);
|
||
|
|
||
|
if (trackdir) {
|
||
|
FILE *f;
|
||
|
sprintf(tracktmp, " -noquiet -o %s/0x%lx.log", trackdir, win);
|
||
|
strcat(cmd, tracktmp);
|
||
|
sprintf(tracktmp, "%s/0x%lx.connect", trackdir, win);
|
||
|
f = fopen(tracktmp, "w");
|
||
|
if (f) {
|
||
|
fprintf(f, "%s", connto);
|
||
|
fclose(f);
|
||
|
sprintf(tmp, " -connect_or_exit '%s'", tracktmp);
|
||
|
strcat(cmd, tmp);
|
||
|
} else {
|
||
|
sprintf(tmp, " -connect_or_exit '%s'", connto);
|
||
|
strcat(cmd, tmp);
|
||
|
}
|
||
|
} else {
|
||
|
if (!strcmp(connto, "")) {
|
||
|
sprintf(tmp, " -connect '%s'", connto);
|
||
|
} else {
|
||
|
sprintf(tmp, " -connect_or_exit '%s'", connto);
|
||
|
}
|
||
|
strcat(cmd, tmp);
|
||
|
}
|
||
|
if (uf) {
|
||
|
char *q = strstr(cmd, "-connect_or_exit");
|
||
|
if (q) q = strstr(q, "_or_exit");
|
||
|
if (q) {
|
||
|
int i;
|
||
|
for (i=0; i < strlen("_or_exit"); i++) {
|
||
|
*q = ' ';
|
||
|
q++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
strcat(cmd, " ");
|
||
|
strcat(cmd, x11vnc_args);
|
||
|
|
||
|
fprintf(stdout, "launching: x11vnc for window 0x%08lx %dx%d+%d+%d \"%s\"\n",
|
||
|
win, w, h, x, y, name);
|
||
|
|
||
|
if (appshare_debug) {
|
||
|
fprintf(stderr, "\nrunning: %s\n\n", cmd);
|
||
|
}
|
||
|
ff();
|
||
|
|
||
|
system(cmd);
|
||
|
|
||
|
free(cmd);
|
||
|
free(tmp);
|
||
|
free(connto);
|
||
|
free(name);
|
||
|
}
|
||
|
|
||
|
static void stop(Window win) {
|
||
|
char *cmd;
|
||
|
int pid = -1;
|
||
|
int f = find_win(win);
|
||
|
if (f < 0 || win == None) {
|
||
|
return;
|
||
|
}
|
||
|
if (state[f] == 0) {
|
||
|
return;
|
||
|
}
|
||
|
if (trackdir) {
|
||
|
pid = trackdir_pid(win);
|
||
|
if (pid > 0) {
|
||
|
if (appshare_debug) {fprintf(stderr,
|
||
|
"sending SIGTERM to: %d\n", pid); ff();}
|
||
|
kill((pid_t) pid, SIGTERM);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
cmd = (char *) malloc(1000 + strlen(x11vnc));
|
||
|
sprintf(cmd, "pkill -TERM -f '%s %s 0x%lx -bg'", x11vnc, id_opt, win);
|
||
|
if (appshare_debug) {
|
||
|
fprintf(stdout, "stopping: 0x%08lx - %s\n", win, cmd);
|
||
|
} else {
|
||
|
fprintf(stdout, "stopping: x11vnc for window 0x%08lx "
|
||
|
"(pid: %d)\n", win, pid);
|
||
|
}
|
||
|
ff();
|
||
|
system(cmd);
|
||
|
|
||
|
sprintf(cmd, "(sleep 0.25 2>/dev/null || sleep 1; pkill -KILL -f '%s "
|
||
|
"%s 0x%lx -bg') &", x11vnc, id_opt, win);
|
||
|
system(cmd);
|
||
|
|
||
|
if (trackdir) {
|
||
|
trackdir_cleanup(win);
|
||
|
}
|
||
|
|
||
|
free(cmd);
|
||
|
}
|
||
|
|
||
|
static void kill_helper_pid(void) {
|
||
|
int status;
|
||
|
if (helper_pid <= 0) {
|
||
|
return;
|
||
|
}
|
||
|
fprintf(stderr, "stopping: helper_pid: %d\n", (int) helper_pid);
|
||
|
kill(helper_pid, SIGTERM);
|
||
|
usleep(50 * 1000);
|
||
|
kill(helper_pid, SIGKILL);
|
||
|
usleep(25 * 1000);
|
||
|
#if LIBVNCSERVER_HAVE_SYS_WAIT_H && LIBVNCSERVER_HAVE_WAITPID
|
||
|
waitpid(helper_pid, &status, WNOHANG);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static void be_helper_pid(char *dpy_str) {
|
||
|
int cnt = 0;
|
||
|
int ms = (int) (1000 * helper_delay);
|
||
|
double last_check = 0.0;
|
||
|
|
||
|
if (ms < 50) ms = 50;
|
||
|
|
||
|
dpy = XOpenDisplay(dpy_str);
|
||
|
ticker_atom = XInternAtom(dpy, ticker_atom_str, False);
|
||
|
|
||
|
while (1) {
|
||
|
char tmp[32];
|
||
|
sprintf(tmp, "HELPER_CNT_%08d", cnt++);
|
||
|
XChangeProperty(dpy, DefaultRootWindow(dpy), ticker_atom, XA_STRING, 8,
|
||
|
PropModeReplace, (unsigned char *) tmp, strlen(tmp));
|
||
|
XFlush(dpy);
|
||
|
usleep(ms*1000);
|
||
|
if (parent_pid > 0) {
|
||
|
if(dnow() > last_check + 1.0) {
|
||
|
last_check = dnow();
|
||
|
if (kill(parent_pid, 0) != 0) {
|
||
|
fprintf(stderr, "be_helper_pid: parent %d is gone.\n", (int) parent_pid);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
exit(0);
|
||
|
}
|
||
|
|
||
|
static void print_logs(void) {
|
||
|
if (trackdir) {
|
||
|
DIR *dir = opendir(trackdir);
|
||
|
if (dir) {
|
||
|
struct dirent *dp;
|
||
|
while ( (dp = readdir(dir)) != NULL) {
|
||
|
FILE *f;
|
||
|
char *name = dp->d_name;
|
||
|
if (!strcmp(name, ".") || !strcmp(name, "..")) {
|
||
|
continue;
|
||
|
}
|
||
|
if (strstr(name, "0x") != name) {
|
||
|
continue;
|
||
|
}
|
||
|
if (strstr(name, ".log") == NULL) {
|
||
|
continue;
|
||
|
}
|
||
|
sprintf(tracktmp, "%s/%s", trackdir, name);
|
||
|
f = fopen(tracktmp, "r");
|
||
|
if (f) {
|
||
|
char line[1024];
|
||
|
fprintf(stderr, "===== x11vnc log %s =====\n", tracktmp);
|
||
|
while (fgets(line, sizeof(line), f) != NULL) {
|
||
|
fprintf(stderr, "%s", line);
|
||
|
}
|
||
|
fprintf(stderr, "\n");
|
||
|
ff();
|
||
|
fclose(f);
|
||
|
}
|
||
|
}
|
||
|
closedir(dir);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void appshare_cleanup(int s) {
|
||
|
int i;
|
||
|
if (s) {}
|
||
|
|
||
|
if (use_forever) {
|
||
|
/* launch this backup in case they kill -9 us before we terminate everything */
|
||
|
char cmd[1000];
|
||
|
sprintf(cmd, "(sleep 3; pkill -TERM -f '%s') &", unique_tag);
|
||
|
if (appshare_debug) fprintf(stderr, "%s\n", cmd);
|
||
|
system(cmd);
|
||
|
}
|
||
|
|
||
|
for (i=0; i < WMAX; i++) {
|
||
|
if (watch[i] != None) {
|
||
|
stop(watch[i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (trackdir) {
|
||
|
DIR *dir = opendir(trackdir);
|
||
|
if (dir) {
|
||
|
struct dirent *dp;
|
||
|
while ( (dp = readdir(dir)) != NULL) {
|
||
|
char *name = dp->d_name;
|
||
|
if (!strcmp(name, ".") || !strcmp(name, "..")) {
|
||
|
continue;
|
||
|
}
|
||
|
if (strstr(name, "0x") != name) {
|
||
|
fprintf(stderr, "skipping: %s\n", name);
|
||
|
continue;
|
||
|
}
|
||
|
if (!appshare_debug) {
|
||
|
fprintf(stderr, "removing: %s\n", name);
|
||
|
sprintf(tracktmp, "%s/%s", trackdir, name);
|
||
|
unlink(tracktmp);
|
||
|
} else {
|
||
|
if (appshare_debug) fprintf(stderr, "keeping: %s\n", name);
|
||
|
}
|
||
|
}
|
||
|
closedir(dir);
|
||
|
}
|
||
|
if (!appshare_debug) {
|
||
|
if (strstr(trackdir, trackpre) == trackdir) {
|
||
|
if (appshare_debug) fprintf(stderr, "removing: %s\n", trackdir);
|
||
|
rmdir(trackdir);
|
||
|
}
|
||
|
}
|
||
|
ff();
|
||
|
}
|
||
|
|
||
|
kill_helper_pid();
|
||
|
|
||
|
#if !NO_X11
|
||
|
XCloseDisplay(dpy);
|
||
|
#endif
|
||
|
fprintf(stdout, "done.\n");
|
||
|
ff();
|
||
|
exit(0);
|
||
|
}
|
||
|
|
||
|
static int trap_xerror(Display *d, XErrorEvent *error) {
|
||
|
if (d || error) {}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#if 0
|
||
|
typedef struct {
|
||
|
int x, y; /* location of window */
|
||
|
int width, height; /* width and height of window */
|
||
|
int border_width; /* border width of window */
|
||
|
int depth; /* depth of window */
|
||
|
Visual *visual; /* the associated visual structure */
|
||
|
Window root; /* root of screen containing window */
|
||
|
int class; /* InputOutput, InputOnly*/
|
||
|
int bit_gravity; /* one of bit gravity values */
|
||
|
int win_gravity; /* one of the window gravity values */
|
||
|
int backing_store; /* NotUseful, WhenMapped, Always */
|
||
|
unsigned long backing_planes;/* planes to be preserved if possible */
|
||
|
unsigned long backing_pixel;/* value to be used when restoring planes */
|
||
|
Bool save_under; /* boolean, should bits under be saved? */
|
||
|
Colormap colormap; /* color map to be associated with window */
|
||
|
Bool map_installed; /* boolean, is color map currently installed*/
|
||
|
int map_state; /* IsUnmapped, IsUnviewable, IsViewable */
|
||
|
long all_event_masks; /* set of events all people have interest in*/
|
||
|
long your_event_mask; /* my event mask */
|
||
|
long do_not_propagate_mask; /* set of events that should not propagate */
|
||
|
Bool override_redirect; /* boolean value for override-redirect */
|
||
|
Screen *screen; /* back pointer to correct screen */
|
||
|
} XWindowAttributes;
|
||
|
#endif
|
||
|
|
||
|
static void get_wm_name(Window win, char **name) {
|
||
|
int ok;
|
||
|
|
||
|
#if !NO_X11
|
||
|
XErrorHandler old_handler = XSetErrorHandler(trap_xerror);
|
||
|
ok = XFetchName(dpy, win, name);
|
||
|
XSetErrorHandler(old_handler);
|
||
|
#endif
|
||
|
|
||
|
if (!ok || *name == NULL) {
|
||
|
*name = strdup("unknown");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int win_attr(Window win) {
|
||
|
int ok = 0;
|
||
|
#if !NO_X11
|
||
|
XErrorHandler old_handler = XSetErrorHandler(trap_xerror);
|
||
|
ok = XGetWindowAttributes(dpy, win, &attr);
|
||
|
XSetErrorHandler(old_handler);
|
||
|
#endif
|
||
|
|
||
|
if (ok) {
|
||
|
return 1;
|
||
|
} else {
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void win_select(Window win, int ignore) {
|
||
|
#if !NO_X11
|
||
|
XErrorHandler old_handler = XSetErrorHandler(trap_xerror);
|
||
|
if (ignore) {
|
||
|
XSelectInput(dpy, win, 0);
|
||
|
} else {
|
||
|
XSelectInput(dpy, win, SubstructureNotifyMask);
|
||
|
}
|
||
|
XSync(dpy, False);
|
||
|
XSetErrorHandler(old_handler);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static Window get_parent(Window win) {
|
||
|
int ok;
|
||
|
Window r, parent = None, *list = NULL;
|
||
|
unsigned int nchild;
|
||
|
|
||
|
#if !NO_X11
|
||
|
XErrorHandler old_handler = XSetErrorHandler(trap_xerror);
|
||
|
ok = XQueryTree(dpy, win, &r, &parent, &list, &nchild);
|
||
|
XSetErrorHandler(old_handler);
|
||
|
|
||
|
if (!ok) {
|
||
|
return None;
|
||
|
}
|
||
|
if (list) {
|
||
|
XFree(list);
|
||
|
}
|
||
|
#endif
|
||
|
return parent;
|
||
|
}
|
||
|
|
||
|
static int get_xy(Window win, int *x, int *y) {
|
||
|
Window cr;
|
||
|
Bool rc = False;
|
||
|
#if !NO_X11
|
||
|
XErrorHandler old_handler = XSetErrorHandler(trap_xerror);
|
||
|
|
||
|
rc = XTranslateCoordinates(dpy, win, root, 0, 0, x, y, &cr);
|
||
|
XSetErrorHandler(old_handler);
|
||
|
#endif
|
||
|
|
||
|
if (!rc) {
|
||
|
return 0;
|
||
|
} else {
|
||
|
return 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static Window check_inside(Window win) {
|
||
|
int i, nwin = 0;
|
||
|
int w, h, x, y;
|
||
|
int Ws[WMAX], Hs[WMAX], Xs[WMAX], Ys[WMAX];
|
||
|
Window wins[WMAX];
|
||
|
|
||
|
if (!win_attr(win)) {
|
||
|
return None;
|
||
|
}
|
||
|
|
||
|
/* store them first to give the win app more time to settle. */
|
||
|
for (i=0; i < WMAX; i++) {
|
||
|
int X, Y;
|
||
|
Window wchk = watch[i];
|
||
|
if (wchk == None) {
|
||
|
continue;
|
||
|
}
|
||
|
if (state[i] == 0) {
|
||
|
continue;
|
||
|
}
|
||
|
if (!win_attr(wchk)) {
|
||
|
continue;
|
||
|
}
|
||
|
if (!get_xy(wchk, &X, &Y)) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
Xs[nwin] = X;
|
||
|
Ys[nwin] = Y;
|
||
|
Ws[nwin] = attr.width;
|
||
|
Hs[nwin] = attr.height;
|
||
|
wins[nwin] = wchk;
|
||
|
nwin++;
|
||
|
}
|
||
|
|
||
|
if (nwin == 0) {
|
||
|
return None;
|
||
|
}
|
||
|
|
||
|
if (!win_attr(win)) {
|
||
|
return None;
|
||
|
}
|
||
|
w = attr.width;
|
||
|
h = attr.height;
|
||
|
|
||
|
get_xy(win, &x, &y);
|
||
|
if (!get_xy(win, &x, &y)) {
|
||
|
return None;
|
||
|
}
|
||
|
|
||
|
for (i=0; i < nwin; i++) {
|
||
|
int X, Y, W, H;
|
||
|
Window wchk = wins[i];
|
||
|
X = Xs[i];
|
||
|
Y = Ys[i];
|
||
|
W = Ws[i];
|
||
|
H = Hs[i];
|
||
|
|
||
|
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);
|
||
|
|
||
|
if (X <= x && Y <= y) {
|
||
|
if (x + w <= X + W && y + h < Y + H) {
|
||
|
return wchk;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return None;
|
||
|
}
|
||
|
|
||
|
static void add_win(Window win) {
|
||
|
int idx = find_win(win);
|
||
|
int free = find_win(None);
|
||
|
if (idx >= 0) {
|
||
|
if (appshare_debug) {fprintf(stderr, "already watching window: 0x%lx\n", win); ff();}
|
||
|
return;
|
||
|
}
|
||
|
if (free < 0) {
|
||
|
fprintf(stderr, "ran out of slots for window: 0x%lx\n", win); ff();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (appshare_debug) {fprintf(stderr, "watching: 0x%lx at %d\n", win, free); ff();}
|
||
|
|
||
|
watch[free] = win;
|
||
|
state[free] = 0;
|
||
|
|
||
|
win_select(win, 0);
|
||
|
}
|
||
|
|
||
|
static void delete_win(Window win) {
|
||
|
int i;
|
||
|
for (i=0; i < WMAX; i++) {
|
||
|
if (watch[i] == win) {
|
||
|
watch[i] = None;
|
||
|
state[i] = 0;
|
||
|
if (appshare_debug) {fprintf(stderr, "deleting: 0x%lx at %d\n", win, i); ff();}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void recurse_search(int level, int level_max, Window top, Window app, int *nw) {
|
||
|
Window w, r, parent, *list = NULL;
|
||
|
unsigned int nchild;
|
||
|
int ok;
|
||
|
|
||
|
if (appshare_debug > 1) {
|
||
|
fprintf(stderr, "level: %d level_max: %d top: 0x%lx app: 0x%lx\n", level, level_max, top, app);
|
||
|
}
|
||
|
if (level >= level_max) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
ok = XQueryTree(dpy, top, &r, &parent, &list, &nchild);
|
||
|
if (ok) {
|
||
|
int i;
|
||
|
for (i=0; i < nchild; i++) {
|
||
|
w = list[i];
|
||
|
if (w == None || find_win(w) >= 0) {
|
||
|
continue;
|
||
|
}
|
||
|
if (ours(w) && w != app) {
|
||
|
if (appshare_debug) fprintf(stderr, "add level %d 0x%lx %d/%d\n",
|
||
|
level, w, i, nchild);
|
||
|
add_win(w);
|
||
|
(*nw)++;
|
||
|
}
|
||
|
}
|
||
|
for (i=0; i < nchild; i++) {
|
||
|
w = list[i];
|
||
|
if (w == None || ours(w)) {
|
||
|
continue;
|
||
|
}
|
||
|
recurse_search(level+1, level_max, w, app, nw);
|
||
|
}
|
||
|
}
|
||
|
if (list) {
|
||
|
XFree(list);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void add_app(Window app) {
|
||
|
int i, nw = 0, free = -1;
|
||
|
XErrorHandler old_handler;
|
||
|
|
||
|
#if !NO_X11
|
||
|
i = find_app(app);
|
||
|
if (i >= 0) {
|
||
|
fprintf(stderr, "already tracking app: 0x%lx\n", app);
|
||
|
return;
|
||
|
}
|
||
|
for (i=0; i < AMAX; i++) {
|
||
|
if (same_app(apps[i], app)) {
|
||
|
fprintf(stderr, "already tracking app: 0x%lx via 0x%lx\n", app, apps[i]);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
free = find_app(None);
|
||
|
if (free < 0) {
|
||
|
fprintf(stderr, "ran out of app slots.\n");
|
||
|
return;
|
||
|
}
|
||
|
apps[free] = app;
|
||
|
|
||
|
add_win(app);
|
||
|
|
||
|
old_handler = XSetErrorHandler(trap_xerror);
|
||
|
recurse_search(0, tree_depth, root, app, &nw);
|
||
|
XSetErrorHandler(old_handler);
|
||
|
#endif
|
||
|
fprintf(stderr, "tracking %d windows related to app window 0x%lx\n", nw, app);
|
||
|
}
|
||
|
|
||
|
static void del_app(Window app) {
|
||
|
int i;
|
||
|
for (i=0; i < WMAX; i++) {
|
||
|
Window win = watch[i];
|
||
|
if (win != None) {
|
||
|
if (same_app(app, win)) {
|
||
|
destroy_win(win);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
for (i=0; i < AMAX; i++) {
|
||
|
Window app2 = apps[i];
|
||
|
if (app2 != None) {
|
||
|
if (same_app(app, app2)) {
|
||
|
apps[i] = None;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void wait_until_empty(char *file) {
|
||
|
double t = 0.0, dt = 0.05;
|
||
|
while (t < 1.0) {
|
||
|
struct stat sb;
|
||
|
if (stat(file, &sb) != 0) {
|
||
|
return;
|
||
|
}
|
||
|
if (sb.st_size == 0) {
|
||
|
return;
|
||
|
}
|
||
|
t += dt;
|
||
|
usleep( (int) (dt * 1000 * 1000) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void client(char *client, int add) {
|
||
|
DIR *dir;
|
||
|
struct dirent *dp;
|
||
|
|
||
|
if (!client) {
|
||
|
return;
|
||
|
}
|
||
|
if (!trackdir) {
|
||
|
fprintf(stderr, "no trackdir, cannot %s client: %s\n",
|
||
|
add ? "add" : "disconnect", client);
|
||
|
ff();
|
||
|
return;
|
||
|
}
|
||
|
fprintf(stdout, "%s client: %s\n", add ? "adding " : "deleting", client);
|
||
|
|
||
|
dir = opendir(trackdir);
|
||
|
if (!dir) {
|
||
|
fprintf(stderr, "could not opendir trackdir: %s\n", trackdir);
|
||
|
return;
|
||
|
}
|
||
|
while ( (dp = readdir(dir)) != NULL) {
|
||
|
char *name = dp->d_name;
|
||
|
if (!strcmp(name, ".") || !strcmp(name, "..")) {
|
||
|
continue;
|
||
|
}
|
||
|
if (strstr(name, "0x") != name) {
|
||
|
continue;
|
||
|
}
|
||
|
if (strstr(name, ".connect")) {
|
||
|
FILE *f;
|
||
|
char *tmp;
|
||
|
Window twin;
|
||
|
|
||
|
if (scan_hexdec(name, &twin)) {
|
||
|
int f = find_win(twin);
|
||
|
if (appshare_debug) {
|
||
|
fprintf(stderr, "twin: 0x%lx name=%s f=%d\n", twin, name, f);
|
||
|
ff();
|
||
|
}
|
||
|
if (f < 0) {
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
tmp = (char *) calloc(100 + strlen(client), 1);
|
||
|
sprintf(tracktmp, "%s/%s", trackdir, name);
|
||
|
if (add) {
|
||
|
sprintf(tmp, "%s\n", client);
|
||
|
} else {
|
||
|
sprintf(tmp, "cmd=close:%s\n", client);
|
||
|
}
|
||
|
wait_until_empty(tracktmp);
|
||
|
f = fopen(tracktmp, "w");
|
||
|
if (f) {
|
||
|
if (appshare_debug) {
|
||
|
fprintf(stderr, "%s client: %s + %s",
|
||
|
add ? "add" : "disconnect", tracktmp, tmp);
|
||
|
ff();
|
||
|
}
|
||
|
fprintf(f, "%s", tmp);
|
||
|
fclose(f);
|
||
|
}
|
||
|
free(tmp);
|
||
|
}
|
||
|
}
|
||
|
closedir(dir);
|
||
|
}
|
||
|
|
||
|
static void mapped(Window win) {
|
||
|
int f;
|
||
|
if (win == None) {
|
||
|
return;
|
||
|
}
|
||
|
f = find_win(win);
|
||
|
if (f < 0) {
|
||
|
if (win_attr(win)) {
|
||
|
if (get_parent(win) == root) {
|
||
|
/* XXX more cases? */
|
||
|
add_win(win);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void unmapped(Window win) {
|
||
|
int f = find_win(win);
|
||
|
if (f < 0 || win == None) {
|
||
|
return;
|
||
|
}
|
||
|
stop(win);
|
||
|
state[f] = 0;
|
||
|
}
|
||
|
|
||
|
static void destroy_win(Window win) {
|
||
|
stop(win);
|
||
|
delete_win(win);
|
||
|
}
|
||
|
|
||
|
static Window parse_win(char *str) {
|
||
|
Window win = None;
|
||
|
if (!str) {
|
||
|
return None;
|
||
|
}
|
||
|
if (!strcmp(str, "pick") || !strcmp(str, "p")) {
|
||
|
static double last_pick = 0.0;
|
||
|
if (dnow() < start_time + 15) {
|
||
|
;
|
||
|
} else if (dnow() < last_pick + 2) {
|
||
|
return None;
|
||
|
} else {
|
||
|
last_pick = dnow();
|
||
|
}
|
||
|
if (!pick_windowid(&win)) {
|
||
|
fprintf(stderr, "parse_win: bad window pick.\n");
|
||
|
win = None;
|
||
|
}
|
||
|
if (win == root) {
|
||
|
fprintf(stderr, "parse_win: ignoring pick of rootwin 0x%lx.\n", win);
|
||
|
win = None;
|
||
|
}
|
||
|
ff();
|
||
|
} else if (!scan_hexdec(str, &win)) {
|
||
|
win = None;
|
||
|
}
|
||
|
return win;
|
||
|
}
|
||
|
|
||
|
static void add_or_del_app(char *str, int add) {
|
||
|
Window win = parse_win(str);
|
||
|
|
||
|
if (win != None) {
|
||
|
if (add) {
|
||
|
add_app(win);
|
||
|
} else {
|
||
|
del_app(win);
|
||
|
}
|
||
|
} else if (!strcmp(str, "all")) {
|
||
|
if (!add) {
|
||
|
int i;
|
||
|
for (i=0; i < AMAX; i++) {
|
||
|
if (apps[i] != None) {
|
||
|
del_app(apps[i]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void add_or_del_win(char *str, int add) {
|
||
|
Window win = parse_win(str);
|
||
|
|
||
|
if (win != None) {
|
||
|
int f = find_win(win);
|
||
|
if (add) {
|
||
|
if (f < 0 && win_attr(win)) {
|
||
|
add_win(win);
|
||
|
}
|
||
|
} else {
|
||
|
if (f >= 0) {
|
||
|
destroy_win(win);
|
||
|
}
|
||
|
}
|
||
|
} else if (!strcmp(str, "all")) {
|
||
|
if (!add) {
|
||
|
int i;
|
||
|
for (i=0; i < WMAX; i++) {
|
||
|
if (watch[i] != None) {
|
||
|
destroy_win(watch[i]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void add_or_del_client(char *str, int add) {
|
||
|
int i;
|
||
|
|
||
|
if (!str) {
|
||
|
return;
|
||
|
}
|
||
|
if (strcmp(control, "internal")) {
|
||
|
return;
|
||
|
}
|
||
|
if (add) {
|
||
|
int idx = find_client(str);
|
||
|
int free = find_client(NULL);
|
||
|
|
||
|
if (idx >=0) {
|
||
|
fprintf(stderr, "already tracking client: %s in slot %d\n", str, idx);
|
||
|
ff();
|
||
|
return;
|
||
|
}
|
||
|
if (free < 0) {
|
||
|
static int cnt = 0;
|
||
|
if (cnt++ < 10) {
|
||
|
fprintf(stderr, "ran out of client slots.\n");
|
||
|
ff();
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
clients[free] = strdup(str);
|
||
|
client(str, 1);
|
||
|
} else {
|
||
|
if (str[0] == '#' || str[0] == '%') {
|
||
|
if (sscanf(str+1, "%d", &i) == 1) {
|
||
|
i--;
|
||
|
if (0 <= i && i < CMAX) {
|
||
|
if (clients[i] != NULL) {
|
||
|
client(clients[i], 0);
|
||
|
free(clients[i]);
|
||
|
clients[i] = NULL;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} else if (!strcmp(str, "all")) {
|
||
|
for (i=0; i < CMAX; i++) {
|
||
|
if (clients[i] == NULL) {
|
||
|
continue;
|
||
|
}
|
||
|
client(clients[i], 0);
|
||
|
free(clients[i]);
|
||
|
clients[i] = NULL;
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
i = find_client(str);
|
||
|
if (i >= 0) {
|
||
|
free(clients[i]);
|
||
|
clients[i] = NULL;
|
||
|
client(str, 0);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void restart_x11vnc(void) {
|
||
|
int i, n = 0;
|
||
|
Window win, active[WMAX];
|
||
|
for (i=0; i < WMAX; i++) {
|
||
|
win = watch[i];
|
||
|
if (win == None) {
|
||
|
continue;
|
||
|
}
|
||
|
if (state[i]) {
|
||
|
active[n++] = win;
|
||
|
stop(win);
|
||
|
}
|
||
|
}
|
||
|
if (n) {
|
||
|
usleep(1500 * 1000);
|
||
|
}
|
||
|
for (i=0; i < n; i++) {
|
||
|
win = active[i];
|
||
|
launch(win);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static unsigned long cmask = 0x3fc00000; /* 00111111110000000000000000000000 */
|
||
|
|
||
|
static void init_cmask(void) {
|
||
|
/* dependent on the X server implementation; XmuClientWindow better? */
|
||
|
/* xc/programs/Xserver/include/resource.h */
|
||
|
int didit = 0, res_cnt = 29, client_bits = 8;
|
||
|
|
||
|
if (getenv("X11VNC_APPSHARE_CLIENT_MASK")) {
|
||
|
unsigned long cr;
|
||
|
if (sscanf(getenv("X11VNC_APPSHARE_CLIENT_MASK"), "0x%lx", &cr) == 1) {
|
||
|
cmask = cr;
|
||
|
didit = 1;
|
||
|
}
|
||
|
} else if (getenv("X11VNC_APPSHARE_CLIENT_BITS")) {
|
||
|
int cr = atoi(getenv("X11VNC_APPSHARE_CLIENT_BITS"));
|
||
|
if (cr > 0) {
|
||
|
client_bits = cr;
|
||
|
}
|
||
|
}
|
||
|
if (!didit) {
|
||
|
cmask = (((1 << client_bits) - 1) << (res_cnt-client_bits));
|
||
|
}
|
||
|
fprintf(stderr, "client_mask: 0x%08lx\n", cmask);
|
||
|
}
|
||
|
|
||
|
static int same_app(Window win, Window app) {
|
||
|
if ( (win & cmask) == (app & cmask) ) {
|
||
|
return 1;
|
||
|
} else {
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int ours(Window win) {
|
||
|
int i;
|
||
|
for (i=0; i < AMAX; i++) {
|
||
|
if (apps[i] != None) {
|
||
|
if (same_app(win, apps[i])) {
|
||
|
return 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void list_clients(void) {
|
||
|
int i, n = 0;
|
||
|
for (i=0; i < CMAX; i++) {
|
||
|
if (clients[i] == NULL) {
|
||
|
continue;
|
||
|
}
|
||
|
fprintf(stdout, "client[%02d] %s\n", ++n, clients[i]);
|
||
|
}
|
||
|
fprintf(stdout, "total clients: %d\n", n);
|
||
|
ff();
|
||
|
}
|
||
|
|
||
|
static void list_windows(void) {
|
||
|
int i, n = 0;
|
||
|
for (i=0; i < WMAX; i++) {
|
||
|
char *name;
|
||
|
Window win = watch[i];
|
||
|
if (win == None) {
|
||
|
continue;
|
||
|
}
|
||
|
get_wm_name(win, &name);
|
||
|
fprintf(stdout, "window[%02d] 0x%08lx state: %d slot: %03d \"%s\"\n",
|
||
|
++n, win, state[i], i, name);
|
||
|
free(name);
|
||
|
}
|
||
|
fprintf(stdout, "total windows: %d\n", n);
|
||
|
ff();
|
||
|
}
|
||
|
|
||
|
static void list_apps(void) {
|
||
|
int i, n = 0;
|
||
|
for (i=0; i < AMAX; i++) {
|
||
|
char *name;
|
||
|
Window win = apps[i];
|
||
|
if (win == None) {
|
||
|
continue;
|
||
|
}
|
||
|
get_wm_name(win, &name);
|
||
|
fprintf(stdout, "app[%02d] 0x%08lx state: %d slot: %03d \"%s\"\n",
|
||
|
++n, win, state[i], i, name);
|
||
|
free(name);
|
||
|
}
|
||
|
fprintf(stdout, "total apps: %d\n", n);
|
||
|
ff();
|
||
|
}
|
||
|
|
||
|
static int process_control(char *file, int check_clients) {
|
||
|
int i, nnew = 0, seen[CMAX];
|
||
|
char line[1024], *new[CMAX];
|
||
|
FILE *f;
|
||
|
|
||
|
f = fopen(file, "r");
|
||
|
if (!f) {
|
||
|
return 1;
|
||
|
}
|
||
|
if (check_clients) {
|
||
|
for (i=0; i < CMAX; i++) {
|
||
|
seen[i] = 0;
|
||
|
}
|
||
|
}
|
||
|
while (fgets(line, sizeof(line), f) != NULL) {
|
||
|
char *q = strchr(line, '\n');
|
||
|
if (q) *q = '\0';
|
||
|
|
||
|
if (appshare_debug) {
|
||
|
fprintf(stderr, "check_control: %s\n", line);
|
||
|
ff();
|
||
|
}
|
||
|
|
||
|
q = lblanks(line);
|
||
|
if (q[0] == '#') {
|
||
|
continue;
|
||
|
}
|
||
|
if (!strcmp(q, "")) {
|
||
|
continue;
|
||
|
}
|
||
|
if (strstr(q, "cmd=") == q) {
|
||
|
char *cmd = q + strlen("cmd=");
|
||
|
if (!strcmp(cmd, "quit")) {
|
||
|
if (strcmp(control, file) && strstr(file, ".cmd")) {
|
||
|
FILE *f2 = fopen(file, "w");
|
||
|
if (f2) fclose(f2);
|
||
|
}
|
||
|
appshare_cleanup(0);
|
||
|
} else if (!strcmp(cmd, "wait")) {
|
||
|
return 0;
|
||
|
} else if (strstr(cmd, "bcast:") == cmd) {
|
||
|
;
|
||
|
} else if (strstr(cmd, "del_window:") == cmd) {
|
||
|
add_or_del_win(cmd + strlen("del_window:"), 0);
|
||
|
} else if (strstr(cmd, "add_window:") == cmd) {
|
||
|
add_or_del_win(cmd + strlen("add_window:"), 1);
|
||
|
} else if (strstr(cmd, "del:") == cmd) {
|
||
|
add_or_del_win(cmd + strlen("del:"), 0);
|
||
|
} else if (strstr(cmd, "add:") == cmd) {
|
||
|
add_or_del_win(cmd + strlen("add:"), 1);
|
||
|
} else if (strstr(cmd, "del_client:") == cmd) {
|
||
|
add_or_del_client(cmd + strlen("del_client:"), 0);
|
||
|
} else if (strstr(cmd, "add_client:") == cmd) {
|
||
|
add_or_del_client(cmd + strlen("add_client:"), 1);
|
||
|
} else if (strstr(cmd, "-") == cmd) {
|
||
|
add_or_del_client(cmd + strlen("-"), 0);
|
||
|
} else if (strstr(cmd, "+") == cmd) {
|
||
|
add_or_del_client(cmd + strlen("+"), 1);
|
||
|
} else if (strstr(cmd, "del_app:") == cmd) {
|
||
|
add_or_del_app(cmd + strlen("del_app:"), 0);
|
||
|
} else if (strstr(cmd, "add_app:") == cmd) {
|
||
|
add_or_del_app(cmd + strlen("add_app:"), 1);
|
||
|
} else if (strstr(cmd, "debug:") == cmd) {
|
||
|
appshare_debug = atoi(cmd + strlen("debug:"));
|
||
|
} else if (strstr(cmd, "showmenus:") == cmd) {
|
||
|
skip_menus = atoi(cmd + strlen("showmenus:"));
|
||
|
skip_menus = !(skip_menus);
|
||
|
} else if (strstr(cmd, "noexit:") == cmd) {
|
||
|
exit_no_app_win = atoi(cmd + strlen("noexit:"));
|
||
|
exit_no_app_win = !(exit_no_app_win);
|
||
|
} else if (strstr(cmd, "use_forever:") == cmd) {
|
||
|
use_forever = atoi(cmd + strlen("use_forever:"));
|
||
|
} else if (strstr(cmd, "tree_depth:") == cmd) {
|
||
|
tree_depth = atoi(cmd + strlen("tree_depth:"));
|
||
|
} else if (strstr(cmd, "x11vnc_args:") == cmd) {
|
||
|
x11vnc_args = strdup(cmd + strlen("x11vnc_args:"));
|
||
|
} else if (strstr(cmd, "env:") == cmd) {
|
||
|
putenv(cmd + strlen("env:"));
|
||
|
} else if (strstr(cmd, "noop") == cmd) {
|
||
|
;
|
||
|
} else if (!strcmp(cmd, "restart")) {
|
||
|
restart_x11vnc();
|
||
|
} else if (!strcmp(cmd, "list_clients") || !strcmp(cmd, "lc")) {
|
||
|
list_clients();
|
||
|
} else if (!strcmp(cmd, "list_windows") || !strcmp(cmd, "lw")) {
|
||
|
list_windows();
|
||
|
} else if (!strcmp(cmd, "list_apps") || !strcmp(cmd, "la")) {
|
||
|
list_apps();
|
||
|
} else if (!strcmp(cmd, "list_all") || !strcmp(cmd, "ls")) {
|
||
|
list_windows();
|
||
|
fprintf(stderr, "\n");
|
||
|
list_apps();
|
||
|
fprintf(stderr, "\n");
|
||
|
list_clients();
|
||
|
} else if (!strcmp(cmd, "print_logs") || !strcmp(cmd, "pl")) {
|
||
|
print_logs();
|
||
|
} else if (!strcmp(cmd, "?") || !strcmp(cmd, "h") || !strcmp(cmd, "help")) {
|
||
|
fprintf(stderr, "available commands:\n");
|
||
|
fprintf(stderr, "\n");
|
||
|
fprintf(stderr, " quit restart noop x11vnc help ? ! !!\n");
|
||
|
fprintf(stderr, "\n");
|
||
|
fprintf(stderr, " add_window:win (add:win, add:pick)\n");
|
||
|
fprintf(stderr, " del_window:win (del:win, del:pick, del:all)\n");
|
||
|
fprintf(stderr, " add_app:win (add_app:pick)\n");
|
||
|
fprintf(stderr, " del_app:win (del_app:pick, del_app:all)\n");
|
||
|
fprintf(stderr, " add_client:host (+host)\n");
|
||
|
fprintf(stderr, " del_client:host (-host, -all)\n");
|
||
|
fprintf(stderr, "\n");
|
||
|
fprintf(stderr, " list_windows (lw)\n");
|
||
|
fprintf(stderr, " list_apps (la)\n");
|
||
|
fprintf(stderr, " list_clients (lc)\n");
|
||
|
fprintf(stderr, " list_all (ls)\n");
|
||
|
fprintf(stderr, " print_logs (pl)\n");
|
||
|
fprintf(stderr, "\n");
|
||
|
fprintf(stderr, " debug:n showmenus:n noexit:n\n");
|
||
|
} else {
|
||
|
fprintf(stderr, "unrecognized %s\n", q);
|
||
|
}
|
||
|
continue;
|
||
|
}
|
||
|
if (check_clients) {
|
||
|
int idx = find_client(q);
|
||
|
if (idx >= 0) {
|
||
|
seen[idx] = 1;
|
||
|
} else {
|
||
|
new[nnew++] = strdup(q);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
fclose(f);
|
||
|
|
||
|
if (check_clients) {
|
||
|
for (i=0; i < CMAX; i++) {
|
||
|
if (clients[i] == NULL) {
|
||
|
continue;
|
||
|
}
|
||
|
if (!seen[i]) {
|
||
|
client(clients[i], 0);
|
||
|
free(clients[i]);
|
||
|
clients[i] = NULL;
|
||
|
}
|
||
|
}
|
||
|
for (i=0; i < nnew; i++) {
|
||
|
int free = find_client(NULL);
|
||
|
if (free < 0) {
|
||
|
static int cnt = 0;
|
||
|
if (cnt++ < 10) {
|
||
|
fprintf(stderr, "ran out of client slots.\n");
|
||
|
ff();
|
||
|
break;
|
||
|
}
|
||
|
continue;
|
||
|
}
|
||
|
clients[free] = new[i];
|
||
|
client(new[i], 1);
|
||
|
}
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static int check_control(void) {
|
||
|
static int last_size = -1;
|
||
|
static time_t last_mtime = 0;
|
||
|
struct stat sb;
|
||
|
char *control_cmd;
|
||
|
|
||
|
if (!control) {
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
if (!strcmp(control, "internal")) {
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
control_cmd = (char *)malloc(strlen(control) + strlen(".cmd") + 1);
|
||
|
sprintf(control_cmd, "%s.cmd", control);
|
||
|
if (stat(control_cmd, &sb) == 0) {
|
||
|
FILE *f;
|
||
|
if (sb.st_size > 0) {
|
||
|
process_control(control_cmd, 0);
|
||
|
}
|
||
|
f = fopen(control_cmd, "w");
|
||
|
if (f) {
|
||
|
fclose(f);
|
||
|
}
|
||
|
}
|
||
|
free(control_cmd);
|
||
|
|
||
|
if (stat(control, &sb) != 0) {
|
||
|
return 1;
|
||
|
}
|
||
|
if (last_size == (int) sb.st_size && last_mtime == sb.st_mtime) {
|
||
|
return 1;
|
||
|
}
|
||
|
last_size = (int) sb.st_size;
|
||
|
last_mtime = sb.st_mtime;
|
||
|
|
||
|
return process_control(control, 1);
|
||
|
}
|
||
|
|
||
|
static void update(void) {
|
||
|
int i, app_ok = 0;
|
||
|
if (last_event_type != PropertyNotify) {
|
||
|
if (appshare_debug) fprintf(stderr, "\nupdate ...\n");
|
||
|
} else if (appshare_debug > 1) {
|
||
|
fprintf(stderr, "update ... propertynotify\n");
|
||
|
}
|
||
|
if (!check_control()) {
|
||
|
return;
|
||
|
}
|
||
|
for (i=0; i < WMAX; i++) {
|
||
|
Window win = watch[i];
|
||
|
if (win == None) {
|
||
|
continue;
|
||
|
}
|
||
|
if (!win_attr(win)) {
|
||
|
destroy_win(win);
|
||
|
continue;
|
||
|
}
|
||
|
if (find_app(win) >= 0) {
|
||
|
app_ok++;
|
||
|
}
|
||
|
if (state[i] == 0) {
|
||
|
if (attr.map_state == IsViewable) {
|
||
|
if (skip_menus) {
|
||
|
Window inside = check_inside(win);
|
||
|
if (inside != None) {
|
||
|
if (appshare_debug) {fprintf(stderr, "skip_menus: window 0x%lx is inside of 0x%lx, not tracking it.\n", win, inside); ff();}
|
||
|
delete_win(win);
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
launch(win);
|
||
|
state[i] = 1;
|
||
|
}
|
||
|
} else if (state[i] == 1) {
|
||
|
if (attr.map_state != IsViewable) {
|
||
|
stop(win);
|
||
|
state[i] = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (exit_no_app_win && !app_ok) {
|
||
|
for (i=0; i < AMAX; i++) {
|
||
|
if (apps[i] != None) {
|
||
|
fprintf(stdout, "main application window is gone: 0x%lx\n", apps[i]);
|
||
|
}
|
||
|
}
|
||
|
ff();
|
||
|
appshare_cleanup(0);
|
||
|
}
|
||
|
if (last_event_type != PropertyNotify) {
|
||
|
if (appshare_debug) {fprintf(stderr, "update done.\n"); ff();}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void exiter(char *msg, int rc) {
|
||
|
fprintf(stderr, "%s", msg);
|
||
|
ff();
|
||
|
kill_helper_pid();
|
||
|
exit(rc);
|
||
|
}
|
||
|
|
||
|
static void set_trackdir(void) {
|
||
|
char tmp[256];
|
||
|
struct stat sb;
|
||
|
if (!strcmp(trackdir, "none")) {
|
||
|
trackdir = NULL;
|
||
|
return;
|
||
|
}
|
||
|
if (!strcmp(trackdir, "unset")) {
|
||
|
int fd;
|
||
|
sprintf(tmp, "%s.XXXXXX", trackpre);
|
||
|
fd = mkstemp(tmp);
|
||
|
if (fd < 0) {
|
||
|
strcat(tmp, ": failed to create file.\n");
|
||
|
exiter(tmp, 1);
|
||
|
}
|
||
|
/* XXX race */
|
||
|
close(fd);
|
||
|
unlink(tmp);
|
||
|
if (mkdir(tmp, 0700) != 0) {
|
||
|
strcat(tmp, ": failed to create dir.\n");
|
||
|
exiter(tmp, 1);
|
||
|
}
|
||
|
trackdir = strdup(tmp);
|
||
|
}
|
||
|
if (stat(trackdir, &sb) != 0) {
|
||
|
if (mkdir(trackdir, 0700) != 0) {
|
||
|
exiter("could not make trackdir.\n", 1);
|
||
|
}
|
||
|
} else if (! S_ISDIR(sb.st_mode)) {
|
||
|
exiter("trackdir not a directory.\n", 1);
|
||
|
}
|
||
|
tracktmp = (char *) calloc(1000 + strlen(trackdir), 1);
|
||
|
}
|
||
|
|
||
|
static void process_string(char *str) {
|
||
|
FILE *f;
|
||
|
char *file;
|
||
|
if (trackdir) {
|
||
|
sprintf(tracktmp, "%s/0xprop.cmd", trackdir);
|
||
|
file = strdup(tracktmp);
|
||
|
} else {
|
||
|
char tmp[] = "/tmp/x11vnc-appshare.cmd.XXXXXX";
|
||
|
int fd = mkstemp(tmp);
|
||
|
if (fd < 0) {
|
||
|
return;
|
||
|
}
|
||
|
file = strdup(tmp);
|
||
|
close(fd);
|
||
|
}
|
||
|
f = fopen(file, "w");
|
||
|
if (f) {
|
||
|
fprintf(f, "%s", str);
|
||
|
fclose(f);
|
||
|
process_control(file, 0);
|
||
|
}
|
||
|
unlink(file);
|
||
|
free(file);
|
||
|
}
|
||
|
|
||
|
static void handle_shell(void) {
|
||
|
struct timeval tv;
|
||
|
static char lastline[1000];
|
||
|
static int first = 1;
|
||
|
fd_set rfds;
|
||
|
int fd0 = fileno(stdin);
|
||
|
|
||
|
if (first) {
|
||
|
memset(lastline, 0, sizeof(lastline));
|
||
|
first = 0;
|
||
|
}
|
||
|
|
||
|
FD_ZERO(&rfds);
|
||
|
FD_SET(fd0, &rfds);
|
||
|
tv.tv_sec = 0;
|
||
|
tv.tv_usec = 0;
|
||
|
select(fd0+1, &rfds, NULL, NULL, &tv);
|
||
|
if (FD_ISSET(fd0, &rfds)) {
|
||
|
char line[1000], line2[1010];
|
||
|
if (fgets(line, sizeof(line), stdin) != NULL) {
|
||
|
char *str = lblanks(line);
|
||
|
char *q = strrchr(str, '\n');
|
||
|
if (q) *q = '\0';
|
||
|
if (strcmp(str, "")) {
|
||
|
if (!strcmp(str, "!!")) {
|
||
|
sprintf(line, "%s", lastline);
|
||
|
fprintf(stderr, "%s\n", line);
|
||
|
str = line;
|
||
|
}
|
||
|
if (strstr(str, "!") == str) {
|
||
|
system(str+1);
|
||
|
} else if (!strcmp(str, "x11vnc") || !strcmp(str, "ps")) {
|
||
|
char *cmd = "ps -elf | egrep 'PID|x11vnc' | grep -v egrep";
|
||
|
fprintf(stderr, "%s\n", cmd);
|
||
|
system(cmd);
|
||
|
} else {
|
||
|
sprintf(line2, "cmd=%s", str);
|
||
|
process_string(line2);
|
||
|
}
|
||
|
sprintf(lastline, "%s", str);
|
||
|
}
|
||
|
}
|
||
|
fprintf(stderr, "\n%s", prompt); ff();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void handle_prop_cmd(void) {
|
||
|
char *value, *str, *done = "DONE";
|
||
|
|
||
|
if (cmd_atom == None) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
value = get_xprop(cmd_atom_str, root);
|
||
|
if (value == NULL) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
str = lblanks(value);
|
||
|
if (!strcmp(str, done)) {
|
||
|
free(value);
|
||
|
return;
|
||
|
}
|
||
|
if (strstr(str, "cmd=quit") == str || strstr(str, "\ncmd=quit")) {
|
||
|
set_xprop(cmd_atom_str, root, done);
|
||
|
appshare_cleanup(0);
|
||
|
}
|
||
|
|
||
|
process_string(str);
|
||
|
|
||
|
free(value);
|
||
|
set_xprop(cmd_atom_str, root, done);
|
||
|
}
|
||
|
|
||
|
#define PREFIX if(appshare_debug) fprintf(stderr, " %8.2f 0x%08lx : ", dnow() - start, ev.xany.window);
|
||
|
|
||
|
static void monitor(void) {
|
||
|
#if !NO_X11
|
||
|
XEvent ev;
|
||
|
double start = dnow();
|
||
|
int got_prop_cmd = 0;
|
||
|
|
||
|
if (shell) {
|
||
|
update();
|
||
|
fprintf(stderr, "\n\n");
|
||
|
process_string("cmd=help");
|
||
|
fprintf(stderr, "\n%s", prompt); ff();
|
||
|
}
|
||
|
|
||
|
while (1) {
|
||
|
int t;
|
||
|
|
||
|
if (XEventsQueued(dpy, QueuedAlready) == 0) {
|
||
|
update();
|
||
|
if (got_prop_cmd) {
|
||
|
handle_prop_cmd();
|
||
|
}
|
||
|
got_prop_cmd = 0;
|
||
|
if (shell) {
|
||
|
handle_shell();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
XNextEvent(dpy, &ev);
|
||
|
|
||
|
last_event_type = ev.type;
|
||
|
|
||
|
switch (ev.type) {
|
||
|
case Expose:
|
||
|
PREFIX
|
||
|
if(appshare_debug) fprintf(stderr, "Expose %04dx%04d+%04d+%04d\n", ev.xexpose.width, ev.xexpose.height, ev.xexpose.x, ev.xexpose.y);
|
||
|
break;
|
||
|
case ConfigureNotify:
|
||
|
#if 0
|
||
|
PREFIX
|
||
|
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);
|
||
|
#endif
|
||
|
break;
|
||
|
case VisibilityNotify:
|
||
|
PREFIX
|
||
|
if (appshare_debug) {
|
||
|
fprintf(stderr, "VisibilityNotify: ");
|
||
|
t = ev.xvisibility.state;
|
||
|
if (t == VisibilityFullyObscured) fprintf(stderr, "VisibilityFullyObscured\n");
|
||
|
if (t == VisibilityPartiallyObscured) fprintf(stderr, "VisibilityPartiallyObscured\n");
|
||
|
if (t == VisibilityUnobscured) fprintf(stderr, "VisibilityUnobscured\n");
|
||
|
}
|
||
|
break;
|
||
|
case MapNotify:
|
||
|
PREFIX
|
||
|
if(appshare_debug) fprintf(stderr, "MapNotify win: 0x%lx\n", ev.xmap.window);
|
||
|
if (ours(ev.xmap.window)) {
|
||
|
mapped(ev.xmap.window);
|
||
|
}
|
||
|
break;
|
||
|
case UnmapNotify:
|
||
|
PREFIX
|
||
|
if(appshare_debug) fprintf(stderr, "UnmapNotify win: 0x%lx\n", ev.xmap.window);
|
||
|
if (ours(ev.xmap.window)) {
|
||
|
unmapped(ev.xmap.window);
|
||
|
}
|
||
|
break;
|
||
|
case MapRequest:
|
||
|
PREFIX
|
||
|
if(appshare_debug) fprintf(stderr, "MapRequest\n");
|
||
|
break;
|
||
|
case CreateNotify:
|
||
|
PREFIX
|
||
|
if(appshare_debug) fprintf(stderr, "CreateNotify parent: 0x%lx win: 0x%lx\n", ev.xcreatewindow.parent, ev.xcreatewindow.window);
|
||
|
if (ev.xcreatewindow.parent == root && ours(ev.xcreatewindow.window)) {
|
||
|
if (find_win(ev.xcreatewindow.window) >= 0) {
|
||
|
destroy_win(ev.xcreatewindow.window);
|
||
|
}
|
||
|
add_win(ev.xcreatewindow.window);
|
||
|
}
|
||
|
break;
|
||
|
case DestroyNotify:
|
||
|
PREFIX
|
||
|
if(appshare_debug) fprintf(stderr, "DestroyNotify win: 0x%lx\n", ev.xdestroywindow.window);
|
||
|
if (ours(ev.xdestroywindow.window)) {
|
||
|
destroy_win(ev.xdestroywindow.window);
|
||
|
}
|
||
|
break;
|
||
|
case ConfigureRequest:
|
||
|
PREFIX
|
||
|
if(appshare_debug) fprintf(stderr, "ConfigureRequest\n");
|
||
|
break;
|
||
|
case CirculateRequest:
|
||
|
#if 0
|
||
|
PREFIX
|
||
|
if(appshare_debug) fprintf(stderr, "CirculateRequest parent: 0x%lx win: 0x%lx\n", ev.xcirculaterequest.parent, ev.xcirculaterequest.window);
|
||
|
#endif
|
||
|
break;
|
||
|
case CirculateNotify:
|
||
|
#if 0
|
||
|
PREFIX
|
||
|
if(appshare_debug) fprintf(stderr, "CirculateNotify\n");
|
||
|
#endif
|
||
|
break;
|
||
|
case PropertyNotify:
|
||
|
#if 0
|
||
|
PREFIX
|
||
|
if(appshare_debug) fprintf(stderr, "PropertyNotify\n");
|
||
|
#endif
|
||
|
if (cmd_atom != None && ev.xproperty.atom == cmd_atom) {
|
||
|
got_prop_cmd++;
|
||
|
}
|
||
|
break;
|
||
|
case ReparentNotify:
|
||
|
PREFIX
|
||
|
if(appshare_debug) fprintf(stderr, "ReparentNotify parent: 0x%lx win: 0x%lx\n", ev.xreparent.parent, ev.xreparent.window);
|
||
|
if (ours(ev.xreparent.window)) {
|
||
|
if (ours(ev.xreparent.parent)) {
|
||
|
destroy_win(ev.xreparent.window);
|
||
|
} else if (ev.xreparent.parent == root) {
|
||
|
/* ??? */
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
PREFIX
|
||
|
if(appshare_debug) fprintf(stderr, "Unknown: %d\n", ev.type);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
int appshare_main(int argc, char *argv[]) {
|
||
|
int i;
|
||
|
char *app_str = NULL;
|
||
|
char *dpy_str = NULL;
|
||
|
long xselectinput = 0;
|
||
|
#if NO_X11
|
||
|
exiter("not compiled with X11\n", 1);
|
||
|
#else
|
||
|
for (i=0; i < WMAX; i++) {
|
||
|
watch[i] = None;
|
||
|
state[i] = 0;
|
||
|
}
|
||
|
for (i=0; i < AMAX; i++) {
|
||
|
apps[i] = None;
|
||
|
}
|
||
|
for (i=0; i < CMAX; i++) {
|
||
|
clients[i] = NULL;
|
||
|
}
|
||
|
|
||
|
x11vnc = strdup(argv[0]);
|
||
|
|
||
|
for (i=1; i < argc; i++) {
|
||
|
int end = (i == argc-1) ? 1 : 0;
|
||
|
char *s = argv[i];
|
||
|
if (strstr(s, "--") == s) {
|
||
|
s++;
|
||
|
}
|
||
|
|
||
|
if (!strcmp(s, "-h") || !strcmp(s, "-help")) {
|
||
|
fprintf(stdout, "%s", usage);
|
||
|
exit(0);
|
||
|
} else if (!strcmp(s, "-id")) {
|
||
|
id_opt = "-id";
|
||
|
if (end) exiter("no -id value supplied\n", 1);
|
||
|
app_str = strdup(argv[++i]);
|
||
|
} else if (!strcmp(s, "-sid")) {
|
||
|
id_opt = "-sid";
|
||
|
if (end) exiter("no -sid value supplied\n", 1);
|
||
|
app_str = strdup(argv[++i]);
|
||
|
} else if (!strcmp(s, "-connect") || !strcmp(s, "-connect_or_exit")) {
|
||
|
if (end) exiter("no -connect value supplied\n", 1);
|
||
|
connect_to = strdup(argv[++i]);
|
||
|
} else if (!strcmp(s, "-control")) {
|
||
|
if (end) exiter("no -control value supplied\n", 1);
|
||
|
control = strdup(argv[++i]);
|
||
|
if (!strcmp(control, "shell")) {
|
||
|
free(control);
|
||
|
control = strdup("internal");
|
||
|
shell = 1;
|
||
|
}
|
||
|
} else if (!strcmp(s, "-trackdir")) {
|
||
|
if (end) exiter("no -trackdir value supplied\n", 1);
|
||
|
trackdir = strdup(argv[++i]);
|
||
|
} else if (!strcmp(s, "-display")) {
|
||
|
if (end) exiter("no -display value supplied\n", 1);
|
||
|
dpy_str = strdup(argv[++i]);
|
||
|
set_env("DISPLAY", dpy_str);
|
||
|
} else if (!strcmp(s, "-delay")) {
|
||
|
if (end) exiter("no -delay value supplied\n", 1);
|
||
|
helper_delay = atof(argv[++i]);
|
||
|
} else if (!strcmp(s, "-args")) {
|
||
|
if (end) exiter("no -args value supplied\n", 1);
|
||
|
x11vnc_args = strdup(argv[++i]);
|
||
|
} else if (!strcmp(s, "-env")) {
|
||
|
if (end) exiter("no -env value supplied\n", 1);
|
||
|
putenv(argv[++i]);
|
||
|
} else if (!strcmp(s, "-debug")) {
|
||
|
appshare_debug++;
|
||
|
} else if (!strcmp(s, "-showmenus")) {
|
||
|
skip_menus = 0;
|
||
|
} else if (!strcmp(s, "-noexit")) {
|
||
|
exit_no_app_win = 0;
|
||
|
} else if (!strcmp(s, "-shell")) {
|
||
|
shell = 1;
|
||
|
} else if (!strcmp(s, "-nocmds") || !strcmp(s, "-safer")) {
|
||
|
fprintf(stderr, "ignoring %s in -appshare mode.\n", s);
|
||
|
} else if (!strcmp(s, "-appshare")) {
|
||
|
;
|
||
|
} else {
|
||
|
fprintf(stderr, "unrecognized 'x11vnc -appshare' option: %s\n", s);
|
||
|
exiter("", 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (getenv("X11VNC_APPSHARE_DEBUG")) {
|
||
|
appshare_debug = atoi(getenv("X11VNC_APPSHARE_DEBUG"));
|
||
|
}
|
||
|
|
||
|
/* let user override name for multiple instances: */
|
||
|
if (getenv("X11VNC_APPSHARE_COMMAND_PROPNAME")) {
|
||
|
cmd_atom_str = strdup(getenv("X11VNC_APPSHARE_COMMAND_PROPNAME"));
|
||
|
}
|
||
|
if (getenv("X11VNC_APPSHARE_TICKER_PROPNAME")) {
|
||
|
ticker_atom_str = strdup(getenv("X11VNC_APPSHARE_TICKER_PROPNAME"));
|
||
|
}
|
||
|
|
||
|
if (shell) {
|
||
|
if (!control || strcmp(control, "internal")) {
|
||
|
exiter("mode -shell requires '-control internal'\n", 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (connect_to == NULL && control != NULL) {
|
||
|
struct stat sb;
|
||
|
if (stat(control, &sb) == 0) {
|
||
|
int len = 100 + sb.st_size;
|
||
|
FILE *f = fopen(control, "r");
|
||
|
|
||
|
if (f) {
|
||
|
char *line = (char *) malloc(len);
|
||
|
connect_to = (char *) calloc(2 * len, 1);
|
||
|
while (fgets(line, len, f) != NULL) {
|
||
|
char *q = strchr(line, '\n');
|
||
|
if (q) *q = '\0';
|
||
|
q = lblanks(line);
|
||
|
if (q[0] == '#') {
|
||
|
continue;
|
||
|
}
|
||
|
if (connect_to[0] != '\0') {
|
||
|
strcat(connect_to, ",");
|
||
|
}
|
||
|
strcat(connect_to, q);
|
||
|
}
|
||
|
fclose(f);
|
||
|
}
|
||
|
fprintf(stderr, "set -connect to: %s\n", connect_to);
|
||
|
}
|
||
|
}
|
||
|
if (0 && connect_to == NULL && control == NULL) {
|
||
|
exiter("no -connect host or -control file specified.\n", 1);
|
||
|
}
|
||
|
|
||
|
if (control) {
|
||
|
pid_t pid;
|
||
|
parent_pid = getpid();
|
||
|
pid = fork();
|
||
|
if (pid == (pid_t) -1) {
|
||
|
;
|
||
|
} else if (pid == 0) {
|
||
|
be_helper_pid(dpy_str);
|
||
|
exit(0);
|
||
|
} else {
|
||
|
helper_pid = pid;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
dpy = XOpenDisplay(dpy_str);
|
||
|
if (!dpy) {
|
||
|
exiter("cannot open display\n", 1);
|
||
|
}
|
||
|
|
||
|
root = DefaultRootWindow(dpy);
|
||
|
|
||
|
xselectinput = SubstructureNotifyMask;
|
||
|
if (helper_pid > 0) {
|
||
|
ticker_atom = XInternAtom(dpy, ticker_atom_str, False);
|
||
|
xselectinput |= PropertyChangeMask;
|
||
|
}
|
||
|
XSelectInput(dpy, root, xselectinput);
|
||
|
|
||
|
cmd_atom = XInternAtom(dpy, cmd_atom_str, False);
|
||
|
|
||
|
init_cmask();
|
||
|
|
||
|
sprintf(unique_tag, "X11VNC_APPSHARE_TAG=%d-tag", getpid());
|
||
|
|
||
|
start_time = dnow();
|
||
|
|
||
|
if (app_str == NULL) {
|
||
|
exiter("no -id/-sid window specified.\n", 1);
|
||
|
} else {
|
||
|
char *p, *str = strdup(app_str);
|
||
|
char *alist[AMAX];
|
||
|
int i, n = 0;
|
||
|
|
||
|
p = strtok(str, ",");
|
||
|
while (p) {
|
||
|
if (n >= AMAX) {
|
||
|
fprintf(stderr, "ran out of app slots: %s\n", app_str);
|
||
|
exiter("", 1);
|
||
|
}
|
||
|
alist[n++] = strdup(p);
|
||
|
p = strtok(NULL, ",");
|
||
|
}
|
||
|
free(str);
|
||
|
|
||
|
for (i=0; i < n; i++) {
|
||
|
Window app = None;
|
||
|
p = alist[i];
|
||
|
app = parse_win(p);
|
||
|
free(p);
|
||
|
|
||
|
if (app != None) {
|
||
|
if (!ours(app)) {
|
||
|
add_app(app);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
set_trackdir();
|
||
|
|
||
|
signal(SIGINT, appshare_cleanup);
|
||
|
signal(SIGTERM, appshare_cleanup);
|
||
|
|
||
|
rfbLogEnable(0);
|
||
|
|
||
|
if (connect_to) {
|
||
|
char *p, *str = strdup(connect_to);
|
||
|
int n = 0;
|
||
|
p = strtok(str, ",");
|
||
|
while (p) {
|
||
|
clients[n++] = strdup(p);
|
||
|
p = strtok(NULL, ",");
|
||
|
}
|
||
|
free(str);
|
||
|
} else {
|
||
|
connect_to = strdup("");
|
||
|
}
|
||
|
|
||
|
for (i=0; i < AMAX; i++) {
|
||
|
if (apps[i] == None) {
|
||
|
continue;
|
||
|
}
|
||
|
fprintf(stdout, "Using app win: 0x%08lx root: 0x%08lx\n", apps[i], root);
|
||
|
}
|
||
|
fprintf(stdout, "\n");
|
||
|
|
||
|
monitor();
|
||
|
|
||
|
appshare_cleanup(0);
|
||
|
|
||
|
#endif
|
||
|
return 0;
|
||
|
}
|
||
|
|