Commit 00a9a0ea authored by runge's avatar runge

x11vnc: -appshare mode for sharing an application windows instead of the

entire desktop. map port + 5500 in reverse connect.  Add id_cmd remote
control functions for id (and other) windows.  Allow zero port in SSL
reverse connections.  Adjust delays between multiple reverse connections;
X11VNC_REVERSE_SLEEP_MAX env var.  Add some missing mutex locks; add
INPUT_LOCK and threads_drop_input.  More safety in -threads mode for
new framebuffer change.  Fix some stderr leaking in -inetd mode.
parent f40b0111
2009-12-02 Karl Runge <runge@karlrunge.com>
* x11vnc: -appshare mode for sharing an application windows intead
of the entire desktop. map port + 5500 in reverse connect.
Add id_cmd remote control functions for id (and other) windows.
Allow zero port in SSL reverse connections. Adjust delays
between multiple reverse connections; X11VNC_REVERSE_SLEEP_MAX
env var. Add some missing mutex locks; add INPUT_LOCK and
threads_drop_input. More safety in -threads mode for new
framebuffer change. Fix some stderr leaking in -inetd mode.
2009-11-18 Karl Runge <runge@karlrunge.com>
* x11vnc: use -timeout setting for reverse connections too.
Delay calling xfixes at the beginning of 1st connection to avoid
......
......@@ -23,7 +23,7 @@ LD_CYGIPC=-lcygipc
endif
bin_PROGRAMS=x11vnc
x11vnc_SOURCES = 8to24.c avahi.c cleanup.c connections.c cursor.c gui.c help.c inet.c keyboard.c linuxfb.c macosx.c macosxCG.c macosxCGP.c macosxCGS.c options.c pm.c pointer.c rates.c remote.c scan.c screen.c selection.c solid.c sslcmds.c sslhelper.c uinput.c unixpw.c user.c userinput.c util.c v4l.c win_utils.c x11vnc.c x11vnc_defs.c xdamage.c xevents.c xinerama.c xkb_bell.c xrandr.c xrecord.c xwrappers.c 8to24.h allowed_input_t.h avahi.h blackout_t.h cleanup.h connections.h cursor.h enc.h enums.h gui.h help.h inet.h keyboard.h linuxfb.h macosx.h macosxCG.h macosxCGP.h macosxCGS.h nox11.h nox11_funcs.h options.h params.h pm.h pointer.h rates.h remote.h scan.h screen.h scrollevent_t.h selection.h solid.h sslcmds.h sslhelper.h ssltools.h tkx11vnc.h uinput.h unixpw.h user.h userinput.h util.h v4l.h win_utils.h winattr_t.h x11vnc.h xdamage.h xevents.h xinerama.h xkb_bell.h xrandr.h xrecord.h xwrappers.h
x11vnc_SOURCES = 8to24.c appshare.c avahi.c cleanup.c connections.c cursor.c gui.c help.c inet.c keyboard.c linuxfb.c macosx.c macosxCG.c macosxCGP.c macosxCGS.c options.c pm.c pointer.c rates.c remote.c scan.c screen.c selection.c solid.c sslcmds.c sslhelper.c uinput.c unixpw.c user.c userinput.c util.c v4l.c win_utils.c x11vnc.c x11vnc_defs.c xdamage.c xevents.c xinerama.c xkb_bell.c xrandr.c xrecord.c xwrappers.c 8to24.h allowed_input_t.h avahi.h blackout_t.h cleanup.h connections.h cursor.h enc.h enums.h gui.h help.h inet.h keyboard.h linuxfb.h macosx.h macosxCG.h macosxCGP.h macosxCGS.h nox11.h nox11_funcs.h options.h params.h pm.h pointer.h rates.h remote.h scan.h screen.h scrollevent_t.h selection.h solid.h sslcmds.h sslhelper.h ssltools.h tkx11vnc.h uinput.h unixpw.h user.h userinput.h util.h v4l.h win_utils.h winattr_t.h x11vnc.h xdamage.h xevents.h xinerama.h xkb_bell.h xrandr.h xrecord.h xwrappers.h
if HAVE_SYSTEM_LIBVNCSERVER
INCLUDES_LIBVNCSERVER = @SYSTEM_LIBVNCSERVER_CFLAGS@
......
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
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;
}
......@@ -53,6 +53,7 @@ so, delete this exception statement from your version.
#include "macosxCG.h"
#include "userinput.h"
#include "pointer.h"
#include "xrandr.h"
/*
* routines for handling incoming, outgoing, etc connections
......@@ -257,10 +258,10 @@ static rfbClientPtr *client_match(char *str) {
i = 0;
iter = rfbGetClientIterator(screen);
while( (cl = rfbClientIteratorNext(iter)) ) {
ClientData *cd = (ClientData *) cl->clientData;
if (strstr(str, "0x") == str) {
unsigned int in;
int id;
ClientData *cd = (ClientData *) cl->clientData;
if (! cd) {
continue;
}
......@@ -277,25 +278,52 @@ static rfbClientPtr *client_match(char *str) {
cl_list[i++] = cl;
}
} else {
char *rstr = str;
int port = -1;
char *rstr = strdup(str);
char *q = strrchr(rstr, ':');
if (q) {
port = atoi(q+1);
*q = '\0';
if (port == 0 && q[1] != '0') {
port = -1;
} else if (port < 0) {
port = -port;
} else if (port < 200) {
port = 5500 + port;
}
}
if (! dotted_ip(str)) {
rstr = host2ip(str);
char *orig = rstr;
rstr = host2ip(rstr);
free(orig);
if (rstr == NULL || *rstr == '\0') {
if (host_warn++) {
continue;
}
rfbLog("skipping bad lookup: \"%s\"\n",
str);
rfbLog("skipping bad lookup: \"%s\"\n", str);
continue;
}
rfbLog("lookup: %s -> %s\n", str, rstr);
rfbLog("lookup: %s -> %s port=%d\n", str, rstr, port);
}
if (!strcmp(rstr, cl->host)) {
cl_list[i++] = cl;
}
if (rstr != str) {
free(rstr);
int ok = 1;
if (port > 0) {
if (cd != NULL && cd->client_port > 0) {
if (cd->client_port != port) {
ok = 0;
}
} else {
int cport = get_remote_port(cl->sock);
if (cport != port) {
ok = 0;
}
}
}
if (ok) {
cl_list[i++] = cl;
}
}
free(rstr);
}
if (i >= n - 1) {
break;
......@@ -766,6 +794,7 @@ void client_gone(rfbClientPtr client) {
if (unixpw_in_progress && unixpw_client) {
if (client == unixpw_client) {
unixpw_in_progress = 0;
/* mutex */
screen->permitFileTransfer = unixpw_file_xfer_save;
if ((tightfilexfer = unixpw_tightvnc_xfer_save)) {
#ifdef LIBVNCSERVER_WITH_TIGHTVNC_FILETRANSFER
......@@ -1670,14 +1699,20 @@ static void check_connect_file(char *file) {
FILE *in;
char line[VNC_CONNECT_MAX], host[VNC_CONNECT_MAX];
static int first_warn = 1, truncate_ok = 1;
static time_t last_time = 0;
time_t now = time(NULL);
if (last_time == 0) {
last_time = now;
static double last_time = 0.0, delay = 0.5;
double now = dnow();
struct stat sbuf;
if (last_time == 0.0) {
if (!getenv("X11VNC_APPSHARE_ACTIVE")) {
/* skip first */
last_time = now;
} else {
delay = 0.25;
}
}
if (now - last_time < 1) {
/* check only once a second */
if (now - last_time < delay) {
/* check only about once a second */
return;
}
last_time = now;
......@@ -1691,6 +1726,13 @@ static void check_connect_file(char *file) {
}
}
if (stat(file, &sbuf) == 0) {
/* skip empty file directly */
if (sbuf.st_size == 0) {
return;
}
}
in = fopen(file, "r");
if (in == NULL) {
if (first_warn) {
......@@ -2567,6 +2609,12 @@ void reverse_connect(char *str) {
int nclients0 = client_count;
int lcnt, j;
char **list;
int do_appshare = 0;
if (!getenv("X11VNC_REVERSE_USE_OLD_SLEEP")) {
sleep_min = 500;
sleep_max = 2500;
}
if (unixpw_in_progress) return;
......@@ -2582,19 +2630,56 @@ void reverse_connect(char *str) {
}
free(tmp);
if (subwin && getenv("X11VNC_APPSHARE_ACTIVE")) {
do_appshare = 1;
sleep_between_host = 0; /* too agressive??? */
}
if (getenv("X11VNC_REVERSE_SLEEP_BETWEEN_HOST")) {
sleep_between_host = atoi(getenv("X11VNC_REVERSE_SLEEP_BETWEEN_HOST"));
}
if (do_appshare) {
if (screen && dpy) {
char *s = choose_title(DisplayString(dpy));
/* mutex */
screen->desktopName = s;
if (rfb_desktop_name) {
free(rfb_desktop_name);
}
rfb_desktop_name = strdup(s);
}
}
for (j = 0; j < lcnt; j++) {
p = list[j];
if ((n = do_reverse_connect(p)) != 0) {
int i;
progress_client();
rfbPE(-1);
for (i=0; i < 3; i++) {
rfbPE(-1);
}
}
cnt += n;
if (list[j+1] != NULL) {
t = 0;
while (t < sleep_between_host) {
double t1, t2;
int i;
t1 = dnow();
for (i=0; i < 8; i++) {
rfbPE(-1);
if (do_appshare && t == 0) {
rfbPE(-1);
}
}
t2 = dnow();
t += (int) (1000 * (t2 - t1));
if (t >= sleep_between_host) {
break;
}
usleep(dt * 1000);
rfbPE(-1);
t += dt;
}
}
......@@ -2616,6 +2701,9 @@ void reverse_connect(char *str) {
}
clean_up_exit(0);
}
if (xrandr || xrandr_maybe) {
check_xrandr_event("reverse_connect1");
}
return;
}
......@@ -2623,6 +2711,8 @@ void reverse_connect(char *str) {
* XXX: we need to process some of the initial handshaking
* events, otherwise the client can get messed up (why??)
* so we send rfbProcessEvents() all over the place.
*
* How much is this still needed?
*/
n = cnt;
......@@ -2632,17 +2722,42 @@ void reverse_connect(char *str) {
t = sleep_max - sleep_min;
tot = sleep_min + ((n-1) * t) / (n_max-1);
if (do_appshare) {
tot /= 3;
if (tot < dt) {
tot = dt;
}
tot = 0; /* too agressive??? */
}
if (getenv("X11VNC_REVERSE_SLEEP_MAX")) {
tot = atoi(getenv("X11VNC_REVERSE_SLEEP_MAX"));
}
t = 0;
while (t < tot) {
rfbPE(-1);
rfbPE(-1);
int i;
double t1, t2;
t1 = dnow();
for (i=0; i < 8; i++) {
rfbPE(-1);
if (t == 0) rfbPE(-1);
}
t2 = dnow();
t += (int) (1000 * (t2 - t1));
if (t >= tot) {
break;
}
usleep(dt * 1000);
t += dt;
}
if (connect_or_exit) {
if (client_count <= nclients0) {
for (t = 0; t < 10; t++) {
rfbPE(-1);
int i;
for (i=0; i < 3; i++) {
rfbPE(-1);
}
usleep(100 * 1000);
}
}
......@@ -2656,6 +2771,9 @@ void reverse_connect(char *str) {
clean_up_exit(0);
}
}
if (xrandr || xrandr_maybe) {
check_xrandr_event("reverse_connect2");
}
}
/*
......@@ -2857,7 +2975,9 @@ static void pmove(int x, int y) {
}
rfbLog("pmove: x y: %d %d\n", x, y);
pointer(0, x, y, NULL);
X_LOCK;
XFlush_wr(dpy);
X_UNLOCK;
}
......@@ -3472,6 +3592,7 @@ static void turn_off_truecolor_ad(rfbClientPtr client) {
if (client) {}
if (turn_off_truecolor) {
rfbLog("turning off truecolor advertising.\n");
/* mutex */
screen->serverFormat.trueColour = FALSE;
screen->displayHook = NULL;
screen->serverFormat.redShift = 0;
......@@ -3639,6 +3760,7 @@ enum rfbNewClientAction new_client(rfbClientPtr client) {
}
} else if (strstr(passwdfile, "custom:") == passwdfile) {
if (screen) {
/* mutex */
screen->passwordCheck = custom_passwd_check;
}
}
......@@ -3683,8 +3805,9 @@ enum rfbNewClientAction new_client(rfbClientPtr client) {
cd->cmp_bytes_sent = 0;
cd->raw_bytes_sent = 0;
rfbLog("incr accepted_client for %s:%d sock=%d\n", client->host, get_remote_port(client->sock), client->sock);
accepted_client++;
rfbLog("incr accepted_client=%d for %s:%d sock=%d\n", accepted_client,
client->host, get_remote_port(client->sock), client->sock);
last_client = time(NULL);
if (ncache) {
......@@ -3720,6 +3843,7 @@ enum rfbNewClientAction new_client(rfbClientPtr client) {
rfbSetTranslateFunction(client);
/* mutex */
screen->serverFormat.trueColour = TRUE;
screen->serverFormat.redShift = rs;
screen->serverFormat.greenShift = gs;
......
......@@ -576,7 +576,9 @@ void first_cursor(void) {
return;
}
if (! show_cursor) {
LOCK(screen->cursorMutex);
screen->cursor = NULL;
UNLOCK(screen->cursorMutex);
} else {
got_xfixes_cursor_notify++;
set_rfb_cursor(get_which_cursor());
......@@ -591,7 +593,7 @@ static void setup_cursors(void) {
int w_in = 0, h_in = 0;
static int first = 1;
if (verbose) {
if (verbose || use_threads) {
rfbLog("setting up %d cursors...\n", CURS_MAX);
}
......@@ -603,8 +605,8 @@ static void setup_cursors(void) {
first = 0;
if (screen) {
screen->cursor = NULL;
LOCK(screen->cursorMutex);
screen->cursor = NULL;
}
for (i=0; i<CURS_MAX; i++) {
......@@ -985,7 +987,6 @@ static void tree_descend_cursor(int *depth, Window *w, win_str_info_t *winfo) {
void initialize_xfixes(void) {
#if LIBVNCSERVER_HAVE_LIBXFIXES
if (xfixes_present) {
xfixes_first_initialized = 1;
X_LOCK;
if (use_xfixes) {
XFixesSelectCursorInput(dpy, rootwin,
......@@ -994,6 +995,7 @@ void initialize_xfixes(void) {
XFixesSelectCursorInput(dpy, rootwin, 0);
}
X_UNLOCK;
xfixes_first_initialized = 1;
}
#endif
}
......@@ -1325,24 +1327,23 @@ static int get_exact_cursor(int init) {
return which;
}
X_LOCK;
if (! got_xfixes_cursor_notify && xfixes_base_event_type) {
/* try again for XFixesCursorNotify event */
XEvent xev;
X_LOCK;
if (XCheckTypedEvent(dpy, xfixes_base_event_type +
XFixesCursorNotify, &xev)) {
got_xfixes_cursor_notify++;
}
X_UNLOCK;
}
if (! got_xfixes_cursor_notify) {
/* evidently no cursor change, just return last one */
X_UNLOCK;
return which;
}
got_xfixes_cursor_notify = 0;
/* retrieve the cursor info + pixels from server: */
X_LOCK;
xfc = XFixesGetCursorImage(dpy);
X_UNLOCK;
if (! xfc) {
......@@ -1512,7 +1513,9 @@ void initialize_cursors_mode(void) {
}
} else {
if (screen) {
LOCK(screen->cursorMutex);
screen->cursor = NULL;
UNLOCK(screen->cursorMutex);
set_cursor_was_changed(screen);
}
}
......@@ -1656,9 +1659,11 @@ static void set_cursor_was_changed(rfbScreenInfoPtr s) {
return;
}
iter = rfbGetClientIterator(s);
LOCK(screen->cursorMutex);
while( (cl = rfbClientIteratorNext(iter)) ) {
cl->cursorWasChanged = TRUE;
}
UNLOCK(screen->cursorMutex);
rfbReleaseClientIterator(iter);
}
......
......@@ -176,6 +176,21 @@ void print_help(int mode) {
" shifts a root view to it: this shows SaveUnders menus,\n"
" etc, although they will be clipped if they extend beyond\n"
" the window.\n"
"\n"
"-appshare Simple application sharing based on the -id/-sid\n"
" mechanism. Every new toplevel window that the\n"
" application creates induces a new viewer window via\n"
" a reverse connection. The -id/-sid and -connect\n"
" options are required. Run 'x11vnc -appshare -help'\n"
" for more info.\n"
"\n"
#if 0
"-freeze_when_obscured Probably only of use in -appshare mode: if the -id/-sid\n"
" window is partially or fully obscured by other windows,\n"
" stop checking for framebuffer updates. Mouse and\n"
" keyboard events are still processed and injected.\n"
"\n"
#endif
"-clip WxH+X+Y Only show the sub-region of the full display that\n"
" corresponds to the rectangle geometry with size WxH and\n"
" offset +X+Y. The VNC display has size WxH (i.e. smaller\n"
......@@ -2779,7 +2794,7 @@ void print_help(int mode) {
" \"debug crash shell\" when fatal errors are trapped.\n"
"\n"
"-q, -quiet Be quiet by printing less informational output to\n"
" stderr.\n"
" stderr. (use -noquiet to undo an earlier -quiet.)\n"
"-v, -verbose Print out more information to stderr.\n"
"\n"
"-bg Go into the background after screen setup. Messages to\n"
......@@ -3175,7 +3190,7 @@ void print_help(int mode) {
" (an integer) times that of the full display is allocated\n"
" below the actual framebuffer to cache screen contents\n"
" for rapid retrieval. So a W x H frambuffer is expanded\n"
" to a W x (n+1)*H one. Use 0 to disable. Default: XXX.\n"
" to a W x (n+1)*H one. Use 0 to disable.\n"
"\n"
" The \"n\" is actually optional, the default is 10.\n"
"\n"
......@@ -3183,13 +3198,17 @@ void print_help(int mode) {
" abbreviate \"-ncache\" with \"-nc\". Also, \"-nonc\"\n"
" is the same as \"-ncache 0\"\n"
"\n"
" This is an experimental option, currently implemented\n"
" in an awkward way in that in the VNC Viewer you can\n"
" see the cache contents if you scroll down, etc. So you\n"
" This is an experimental option, currently implemented in\n"
" an awkward way in that in the VNC Viewer you can see the\n"
" pixel cache contents if you scroll down, etc. So you\n"
" will have to set things up so you can't see that region.\n"
" If this method is successful, the changes required for\n"
" clients to do this less awkwardly will be investigated.\n"
"\n"
" The SSVNC viewer does a good job at automatically hiding\n"
" the pixel cache region. Or use SSVNC's -ycrop option\n"
" to explicitly hide the region.\n"
"\n"
" Note that this mode consumes a huge amount of memory,\n"
" both on the x11vnc server side and on the VNC Viewer\n"
" side. If n=2 then the amount of RAM used is roughly\n"
......@@ -3896,10 +3915,7 @@ void print_help(int mode) {
" for output) are created to handle each new client.\n"
" Default: %s.\n"
"\n"
" NOTE: The -threads mode may be disabled due to its\n"
" unstable behavior. If it is disabled, a warning is\n"
" printed out. Stability has been improved in version\n"
" 0.9.8 and so the feature has been re-enabled.\n"
" Thread stability is much improved in version 0.9.8.\n"
"\n"
" Multiple clients in threaded mode should be stable\n"
" for the ZRLE encoding on all platforms. The Tight and\n"
......@@ -3907,9 +3923,14 @@ void print_help(int mode) {
" multiple clients. Compile with -DTLS=__thread if your\n"
" OS and compiler and linker support it.\n"
"\n"
" For resizes (randr, etc.) set this env. var. to the number\n"
" of milliseconds to sleep: X11VNC_THREADS_NEW_FB_SLEEP\n"
" at various places in the do_new_fb() action. This is to\n"
" let various activities settle. Default is about 500ms.\n"
"\n"
" Multiple clients in threaded mode could yield better\n"
" performance for 'class-room' broadcasting usage.\n"
" See also the -reflect option.\n"
" performance for 'class-room' broadcasting usage; also in\n"
" -appshare broadcast mode. See also the -reflect option.\n"
"\n"
"-fs f If the fraction of changed tiles in a poll is greater\n"
" than f, the whole screen is updated. Default: %.2f\n"
......@@ -4599,6 +4620,12 @@ void print_help(int mode) {
" id:windowid set -id window to \"windowid\". empty\n"
" or \"root\" to go back to root window\n"
" sid:windowid set -sid window to \"windowid\"\n"
" id_cmd:cmd cmds: raise, lower, map, unmap, iconify,\n"
" move:dXdY, resize:dWdH, geom:WxH+X+Y. dX\n"
" dY, dW, and dH must have a leading \"+\"\n"
" or \"-\" e.g.: move:-30+10 resize:+20+35\n"
" also: wm_delete, wm_name:string and\n"
" icon_name:string. Also id_cmd:win=N:cmd\n"
" waitmapped wait until subwin is mapped.\n"
" nowaitmapped do not wait until subwin is mapped.\n"
" clip:WxH+X+Y set -clip mode to \"WxH+X+Y\"\n"
......@@ -5081,8 +5108,8 @@ void print_help(int mode) {
" file use \"qry=...\" instead of \"cmd=...\"\n"
"\n"
" ans= stop quit exit shutdown ping resend_cutbuffer\n"
" resend_clipboard resend_primary blacken zero\n"
" refresh reset close disconnect id sid waitmapped\n"
" resend_clipboard resend_primary blacken zero refresh\n"
" reset close disconnect id_cmd id sid waitmapped\n"
" nowaitmapped clip flashcmap noflashcmap shiftcmap\n"
" truecolor notruecolor overlay nooverlay overlay_cursor\n"
" overlay_yescursor nooverlay_nocursor nooverlay_cursor\n"
......@@ -5224,6 +5251,10 @@ void print_help(int mode) {
" The returned output is also prefixed with \"str\".\n"
" Example: -remote_prefix DO_THIS:\n"
"\n"
" Note that enabling -remote_prefix allows the remote\n"
" VNC viewers to run x11vnc -remote commands. Do not\n"
" use this option if they are not to be trusted.\n"
"\n"
"-noremote Do not process any remote control commands or queries.\n"
"-yesremote Do process remote control commands or queries.\n"
" Default: %s\n"
......
......@@ -3056,6 +3056,10 @@ void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) {
static double max_keyrepeat_last_time = 0.0;
static double max_keyrepeat_always = -1.0;
if (threads_drop_input) {
return;
}
dtime0(&tnow);
got_keyboard_calls++;
......@@ -3125,6 +3129,8 @@ void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) {
}
}
INPUT_LOCK;
last_down = down;
last_keysym = keysym;
last_keyboard_time = tnow;
......@@ -3215,6 +3221,7 @@ void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) {
if (db) rfbLog("--- scroll keyrate skipping 0x%lx %s "
"%.4f %.4f\n", keysym, down ? "down":"up ",
tnow - x11vnc_start, tnow - max_keyrepeat_last_time);
INPUT_UNLOCK;
return;
}
}
......@@ -3237,6 +3244,7 @@ void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) {
tnow - x11vnc_start, tnow - max_keyrepeat_last_time);
max_keyrepeat_last_keysym = keysym;
skipped_last_down = 1;
INPUT_UNLOCK;
return;
} else {
if (db) rfbLog("--- scroll keyrate KEEPING 0x%lx %s "
......@@ -3267,15 +3275,18 @@ void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) {
got_user_input++;
got_keyboard_input++;
}
INPUT_UNLOCK;
return;
}
}
if (view_only) {
INPUT_UNLOCK;
return;
}
get_allowed_input(client, &input);
if (! input.keystroke) {
INPUT_UNLOCK;
return;
}
......@@ -3327,6 +3338,7 @@ void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) {
char *b, bstr[32];
if (! down) {
INPUT_UNLOCK;
return; /* nothing to send */
}
if (debug_keyboard) {
......@@ -3360,6 +3372,7 @@ void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) {
}
XFlush_wr(dpy);
X_UNLOCK;
INPUT_UNLOCK;
return;
}
......@@ -3368,6 +3381,7 @@ void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) {
X_LOCK;
XFlush_wr(dpy);
X_UNLOCK;
INPUT_UNLOCK;
return;
}
......@@ -3394,6 +3408,7 @@ void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) {
}
X_UNLOCK;
INPUT_UNLOCK;
}
......@@ -206,6 +206,8 @@ int macosx_icon_anim_time = 450;
unsigned long subwin = 0x0; /* -id, -sid */
int subwin_wait_mapped = 0;
int freeze_when_obscured = 0;
int subwin_obscured = 0;
int debug_xevents = 0; /* -R debug_xevents:1 */
int debug_xdamage = 0; /* -R debug_xdamage:1 or 2 ... */
......@@ -472,6 +474,7 @@ int verbose = 0;
/* threaded vs. non-threaded (default) */
int use_threads = 0;
int started_rfbRunEventLoop = 0;
int threads_drop_input = 0;
/* info about command line opts */
int got_noxwarppointer = 0;
......
......@@ -178,6 +178,8 @@ extern int macosx_icon_anim_time;
extern unsigned long subwin;
extern int subwin_wait_mapped;
extern int freeze_when_obscured;
extern int subwin_obscured;
extern int debug_xevents;
extern int debug_xdamage;
......@@ -367,6 +369,7 @@ extern int verbose;
extern int use_threads;
extern int started_rfbRunEventLoop;
extern int threads_drop_input;
extern int got_noxwarppointer;
extern int got_rfbport;
......
......@@ -49,6 +49,16 @@ static void check_dpms(void);
#endif
void check_pm(void) {
static int skip = -1;
if (skip < 0) {
skip = 0;
if (getenv("X11VNC_NO_CHECK_PM")) {
skip = 1;
}
}
if (skip) {
return;
}
check_fbpm();
check_dpms();
/* someday dpms activities? */
......
......@@ -76,9 +76,6 @@ typedef struct ptrremap {
int up;
} prtremap_t;
#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD
MUTEX(pointerMutex);
#endif
#define MAX_BUTTON_EVENTS 50
static prtremap_t pointer_map[MAX_BUTTONS+1][MAX_BUTTON_EVENTS];
......@@ -674,6 +671,10 @@ void pointer(int mask, int x, int y, rfbClientPtr client) {
int sent = 0, buffer_it = 0;
double now;
if (threads_drop_input) {
return;
}
if (mask >= 0) {
got_pointer_calls++;
}
......@@ -721,6 +722,8 @@ void pointer(int mask, int x, int y, rfbClientPtr client) {
y = nfix(y, dpy_y);
}
INPUT_LOCK;
if ((pipeinput_fh != NULL || pipeinput_int) && mask >= 0) {
pipe_pointer(mask, x, y, client); /* MACOSX here. */
if (! pipeinput_tee) {
......@@ -739,11 +742,13 @@ void pointer(int mask, int x, int y, rfbClientPtr client) {
if (!view_only && (input.motion || input.button)) {
last_rfb_ptr_injected = dnow();
}
INPUT_UNLOCK;
return;
}
}
if (view_only) {
INPUT_UNLOCK;
return;
}
......@@ -755,6 +760,7 @@ void pointer(int mask, int x, int y, rfbClientPtr client) {
* to flush the event queue; there is no real pointer event.
*/
if (! input.motion && ! input.button) {
INPUT_UNLOCK;
return;
}
......@@ -786,6 +792,7 @@ void pointer(int mask, int x, int y, rfbClientPtr client) {
blackr[b].x1, blackr[b].y1,
blackr[b].x2, blackr[b].y2);
}
INPUT_UNLOCK;
return;
}
}
......@@ -800,18 +807,12 @@ void pointer(int mask, int x, int y, rfbClientPtr client) {
if ((use_threads && pointer_mode != 1) || pointer_flush_delay > 0.0) {
# define NEV 32
/* storage for the event queue */
static int mutex_init = 0;
static int nevents = 0;
static int ev[NEV][3];
int i;
/* timer things */
static double dt = 0.0, tmr = 0.0, maxwait = 0.4;
if (! mutex_init) {
INIT_MUTEX(pointerMutex);
mutex_init = 1;
}
if (pointer_flush_delay > 0.0) {
maxwait = pointer_flush_delay;
}
......@@ -821,7 +822,7 @@ void pointer(int mask, int x, int y, rfbClientPtr client) {
}
}
LOCK(pointerMutex);
POINTER_LOCK;
/*
* If the framebuffer is being copied in another thread
......@@ -856,11 +857,12 @@ void pointer(int mask, int x, int y, rfbClientPtr client) {
ev[i][1] = -1;
ev[i][2] = -1;
}
UNLOCK(pointerMutex);
if (debug_pointer) {
rfbLog("pointer(): deferring event %d"
" %.4f\n", i, tmr - x11vnc_start);
}
POINTER_UNLOCK;
INPUT_UNLOCK;
return;
}
}
......@@ -912,13 +914,14 @@ void pointer(int mask, int x, int y, rfbClientPtr client) {
dt = 0.0;
dtime0(&tmr);
UNLOCK(pointerMutex);
POINTER_UNLOCK;
}
if (mask < 0) { /* -1 just means flush the event queue */
if (debug_pointer) {
rfbLog("pointer(): flush only. %.4f\n",
dnowx());
}
INPUT_UNLOCK;
return;
}
......@@ -955,6 +958,7 @@ void pointer(int mask, int x, int y, rfbClientPtr client) {
XFlush_wr(dpy);
X_UNLOCK;
}
INPUT_UNLOCK;
}
void initialize_pipeinput(void) {
......
......@@ -484,6 +484,7 @@ void http_connections(int on) {
if (screen->httpPort == 0) {
int port = find_free_port(5800, 5850);
if (port) {
/* mutex */
screen->httpPort = port;
}
}
......@@ -517,6 +518,7 @@ static void reset_httpport(int old, int new) {
rfbLog("reset_httpport: cannot set httpport: %d"
" in inetd.\n", hp);
} else if (screen) {
/* mutex */
screen->httpPort = hp;
screen->httpInitDone = FALSE;
if (screen->httpListenSock > -1) {
......@@ -544,6 +546,7 @@ static void reset_rfbport(int old, int new) {
rfbClientIteratorPtr iter;
rfbClientPtr cl;
int maxfd;
/* mutex */
if (rp == 0) {
screen->autoPort = TRUE;
} else {
......@@ -655,7 +658,9 @@ int remote_control_access_ok(void) {
}
}
X_LOCK;
xha = XListHosts(dpy, &n, &enabled);
X_UNLOCK;
if (! enabled) {
rfbLog("X access control is disabled, X clients can\n");
rfbLog(" connect from any host. Run 'xhost -'\n");
......@@ -1055,6 +1060,13 @@ char *process_remote_cmd(char *cmd, int stringonly) {
close_clients(p);
goto done;
}
if (strstr(p, "id_cmd") == p) {
NOTAPP
COLON_CHECK("id_cmd:")
p += strlen("id_cmd:");
id_cmd(p);
goto done;
}
if (strstr(p, "id") == p) {
int ok = 0;
Window twin;
......@@ -1465,6 +1477,7 @@ char *process_remote_cmd(char *cmd, int stringonly) {
rfbLog("remote_cmd: enable sharing.\n");
shared = 1;
if (screen) {
/* mutex */
screen->alwaysShared = TRUE;
screen->neverShared = FALSE;
}
......@@ -1477,6 +1490,7 @@ char *process_remote_cmd(char *cmd, int stringonly) {
rfbLog("remote_cmd: disable sharing.\n");
shared = 0;
if (screen) {
/* mutex */
screen->alwaysShared = FALSE;
screen->neverShared = TRUE;
}
......@@ -1562,6 +1576,7 @@ char *process_remote_cmd(char *cmd, int stringonly) {
}
if (! screen->permitFileTransfer) {
rfbLog("remote_cmd: enabling -ultrafilexfer for clients.\n");
/* mutex */
screen->permitFileTransfer = TRUE;
}
goto done;
......@@ -1577,6 +1592,7 @@ char *process_remote_cmd(char *cmd, int stringonly) {
}
if (screen->permitFileTransfer) {
rfbLog("remote_cmd: disabling -ultrafilexfer for clients.\n");
/* mutex */
screen->permitFileTransfer = FALSE;
}
goto done;
......@@ -1595,6 +1611,7 @@ char *process_remote_cmd(char *cmd, int stringonly) {
p += strlen("rfbversion:");
if (sscanf(p, "%d.%d", &maj, &min) == 2) {
/* mutex */
screen->protocolMajorVersion = maj;
screen->protocolMinorVersion = min;
rfbLog("remote_cmd: set rfbversion to: %d.%d\n", maj, min);
......@@ -1766,6 +1783,7 @@ char *process_remote_cmd(char *cmd, int stringonly) {
}
listen_str = strdup("localhost");
/* mutex */
screen->listenInterface = htonl(INADDR_LOOPBACK);
rfbLog("listening on loopback network only.\n");
rfbLog("allow list is: '%s'\n", NONUL(allow_list));
......@@ -1813,6 +1831,7 @@ char *process_remote_cmd(char *cmd, int stringonly) {
}
listen_str = NULL;
/* mutex */
screen->listenInterface = htonl(INADDR_ANY);
rfbLog("listening on ALL network interfaces.\n");
rfbLog("allow list is: '%s'\n", NONUL(allow_list));
......@@ -1853,6 +1872,7 @@ char *process_remote_cmd(char *cmd, int stringonly) {
}
ok = 1;
/* mutex */
if (listen_str == NULL || *listen_str == '\0' ||
!strcmp(listen_str, "any")) {
screen->listenInterface = htonl(INADDR_ANY);
......@@ -4134,6 +4154,7 @@ char *process_remote_cmd(char *cmd, int stringonly) {
}
#endif
} else {
X_LOCK;
if (down == -1) {
XTestFakeKeyEvent_wr(dpy, kc, 1, CurrentTime);
usleep(50*1000);
......@@ -4141,6 +4162,7 @@ char *process_remote_cmd(char *cmd, int stringonly) {
} else {
XTestFakeKeyEvent_wr(dpy, kc, down, CurrentTime);
}
X_UNLOCK;
}
goto done;
}
......@@ -4340,8 +4362,9 @@ char *process_remote_cmd(char *cmd, int stringonly) {
rfbLog("bcx_xattach: failed grab check for '%s': %s. Retrying[%d]...\n", p, res, try);
free(res);
pointer(0, dpy_x/2 + try, dpy_y/2 + try, NULL);
XFlush_wr(dpy);
#if !NO_X11
X_LOCK;
XFlush_wr(dpy);
if (dpy) {
if (try == 2) {
XSync(dpy, False);
......@@ -4349,6 +4372,7 @@ char *process_remote_cmd(char *cmd, int stringonly) {
XSync(dpy, True);
}
}
X_UNLOCK;
#endif
if (try == 1) {
usleep(250*1000);
......@@ -4382,6 +4406,7 @@ char *process_remote_cmd(char *cmd, int stringonly) {
if (d < 0) d = 0;
rfbLog("remote_cmd: setting defer to %d ms.\n", d);
defer_update = d;
/* mutex */
screen->deferUpdateTime = d;
got_defer = 1;
goto done;
......@@ -4403,6 +4428,7 @@ char *process_remote_cmd(char *cmd, int stringonly) {
if (d < 0) d = 0;
rfbLog("remote_cmd: setting defer to %d ms.\n", d);
defer_update = d;
/* mutex */
screen->deferUpdateTime = d;
got_defer = 1;
goto done;
......@@ -4907,6 +4933,7 @@ char *process_remote_cmd(char *cmd, int stringonly) {
if (f < 0) f = 0;
rfbLog("remote_cmd: setting progressive %d -> %d.\n",
screen->progressiveSliceHeight, f);
/* mutex */
screen->progressiveSliceHeight = f;
goto done;
}
......@@ -4995,6 +5022,7 @@ char *process_remote_cmd(char *cmd, int stringonly) {
goto qry;
}
rfbLog("turning on enablehttpproxy.\n");
/* mutex */
screen->httpEnableProxyConnect = 1;
goto done;
}
......@@ -5081,6 +5109,7 @@ char *process_remote_cmd(char *cmd, int stringonly) {
free(rfb_desktop_name);
}
rfb_desktop_name = strdup(p);
/* mutex */
screen->desktopName = rfb_desktop_name;
rfbLog("remote_cmd: setting desktop name to %s\n",
rfb_desktop_name);
......@@ -5449,6 +5478,7 @@ char *process_remote_cmd(char *cmd, int stringonly) {
passwds_new[0] = strdup(p);
/* mutex */
if (screen->authPasswdData &&
screen->passwordCheck == rfbCheckPasswordByList) {
passwds_new[1] = passwds_old[1];
......@@ -5984,8 +6014,10 @@ char *process_remote_cmd(char *cmd, int stringonly) {
}
} else {
if (dpy) { /* raw_fb hack */
X_LOCK;
set_x11vnc_remote_prop(buf);
XFlush_wr(dpy);
X_UNLOCK;
}
}
#endif /* REMOTE_CONTROL */
......
......@@ -125,6 +125,7 @@ void set_greyscale_colormap(void) {
if (! screen) {
return;
}
/* mutex */
if (screen->colourMap.data.shorts) {
free(screen->colourMap.data.shorts);
screen->colourMap.data.shorts = NULL;
......@@ -154,6 +155,7 @@ void set_hi240_colormap(void) {
if (! screen) {
return;
}
/* mutex */
if (0) fprintf(stderr, "set_hi240_colormap: %s\n", raw_fb_pixfmt);
if (screen->colourMap.data.shorts) {
free(screen->colourMap.data.shorts);
......@@ -211,6 +213,7 @@ void set_colormap(int reset) {
if (reset) {
init = 1;
ncolor = 0;
/* mutex */
if (screen->colourMap.data.shorts) {
free(screen->colourMap.data.shorts);
screen->colourMap.data.shorts = NULL;
......@@ -233,6 +236,7 @@ void set_colormap(int reset) {
} else {
ncolor = NCOLOR;
}
/* mutex */
screen->colourMap.count = ncolor;
screen->serverFormat.trueColour = FALSE;
screen->colourMap.is16 = TRUE;
......@@ -777,6 +781,7 @@ static void nofb_hook(rfbClientPtr cl) {
}
main_fb = fb->data;
rfb_fb = main_fb;
/* mutex */
screen->frameBuffer = rfb_fb;
screen->displayHook = NULL;
}
......@@ -813,13 +818,214 @@ void free_old_fb(void) {
}
}
static char _lcs_tmp[128];
static int _bytes0_size = 128, _bytes0[128];
static char *lcs(rfbClientPtr cl) {
sprintf(_lcs_tmp, "%d/%d/%d/%d/%d-%d/%d/%d",
!!(cl->newFBSizePending),
!!(cl->cursorWasChanged),
!!(cl->cursorWasMoved),
!!(cl->reverseConnection),
cl->state,
cl->modifiedRegion ? !!(sraRgnEmpty(cl->modifiedRegion)) : 2,
cl->requestedRegion ? !!(sraRgnEmpty(cl->requestedRegion)) : 2,
cl->copyRegion ? !!(sraRgnEmpty(cl->copyRegion)) : 2
);
return _lcs_tmp;
}
static int lock_client_sends(int lock) {
static rfbClientPtr *cls = NULL;
static int cls_len = 0;
static int blocked = 0;
static int state = 0;
rfbClientIteratorPtr iter;
rfbClientPtr cl;
char *s;
if (!use_threads || !screen) {
return 0;
}
if (lock < 0) {
return state;
}
state = lock;
if (lock) {
if (cls_len < client_count + 128) {
if (cls != NULL) {
free(cls);
}
cls_len = client_count + 256;
cls = (rfbClientPtr *) calloc(cls_len * sizeof(rfbClientPtr), 1);
}
iter = rfbGetClientIterator(screen);
blocked = 0;
while ((cl = rfbClientIteratorNext(iter)) != NULL) {
s = lcs(cl);
SEND_LOCK(cl);
rfbLog("locked client: %p %.6f %s\n", cl, dnowx(), s);
cls[blocked++] = cl;
}
rfbReleaseClientIterator(iter);
} else {
int i;
for (i=0; i < blocked; i++) {
cl = cls[i];
if (cl != NULL) {
s = lcs(cl);
SEND_UNLOCK(cl)
rfbLog("unlocked client: %p %.6f %s\n", cl, dnowx(), s);
}
cls[i] = NULL;
}
blocked = 0;
}
return state;
}
static void settle_clients(int init) {
rfbClientIteratorPtr iter;
rfbClientPtr cl;
int fb_pend, i, ms = 1000;
char *s;
if (!use_threads || !screen) {
return;
}
if (init) {
iter = rfbGetClientIterator(screen);
i = 0;
while ((cl = rfbClientIteratorNext(iter)) != NULL) {
if (i < _bytes0_size) {
_bytes0[i] = rfbStatGetSentBytesIfRaw(cl);
}
i++;
}
rfbReleaseClientIterator(iter);
if (getenv("X11VNC_THREADS_NEW_FB_SLEEP")) {
ms = atoi(getenv("X11VNC_THREADS_NEW_FB_SLEEP"));
} else if (subwin) {
ms = 250;
} else {
ms = 500;
}
usleep(ms * 1000);
return;
}
if (getenv("X11VNC_THREADS_NEW_FB_SLEEP")) {
ms = atoi(getenv("X11VNC_THREADS_NEW_FB_SLEEP"));
} else if (subwin) {
ms = 500;
} else {
ms = 1000;
}
usleep(ms * 1000);
for (i=0; i < 5; i++) {
fb_pend = 0;
iter = rfbGetClientIterator(screen);
while ((cl = rfbClientIteratorNext(iter)) != NULL) {
s = lcs(cl);
if (cl->newFBSizePending) {
fb_pend++;
rfbLog("pending fb size: %p %.6f %s\n", cl, dnowx(), s);
}
}
rfbReleaseClientIterator(iter);
if (fb_pend > 0) {
rfbLog("do_new_fb: newFBSizePending extra -threads sleep (%d)\n", i+1);
usleep(ms * 1000);
} else {
break;
}
}
for (i=0; i < 5; i++) {
int stuck = 0, tot = 0, j = 0;
iter = rfbGetClientIterator(screen);
while ((cl = rfbClientIteratorNext(iter)) != NULL) {
if (j < _bytes0_size) {
int db = rfbStatGetSentBytesIfRaw(cl) - _bytes0[j];
int Bpp = cl->format.bitsPerPixel / 8;
s = lcs(cl);
rfbLog("addl bytes sent: %p %.6f %s %d %d\n",
cl, dnowx(), s, db, _bytes0[j]);
if (i==0) {
if (db < Bpp * dpy_x * dpy_y) {
stuck++;
}
} else if (i==1) {
if (db < 0.5 * Bpp * dpy_x * dpy_y) {
stuck++;
}
} else {
if (db <= 0) {
stuck++;
}
}
}
tot++;
j++;
}
rfbReleaseClientIterator(iter);
if (stuck > 0) {
rfbLog("clients stuck: %d/%d sleep(%d)\n", stuck, tot, i);
usleep(2 * ms * 1000);
} else {
break;
}
}
}
static void prep_clients_for_new_fb(void) {
rfbClientIteratorPtr iter;
rfbClientPtr cl;
if (!use_threads || !screen) {
return;
}
iter = rfbGetClientIterator(screen);
while ((cl = rfbClientIteratorNext(iter)) != NULL) {
if (!cl->newFBSizePending) {
rfbLog("** set_new_fb_size_pending client: %p\n", cl);
cl->newFBSizePending = TRUE;
}
cl->cursorWasChanged = FALSE;
cl->cursorWasMoved = FALSE;
}
rfbReleaseClientIterator(iter);
}
void do_new_fb(int reset_mem) {
XImage *fb;
/* for threaded we really should lock libvncserver out. */
if (use_threads) {
rfbLog("warning: changing framebuffers while threaded may\n");
rfbLog(" not work, do not use -threads if problems arise.\n");
int ms = 1000;
if (getenv("X11VNC_THREADS_NEW_FB_SLEEP")) {
ms = atoi(getenv("X11VNC_THREADS_NEW_FB_SLEEP"));
} else if (subwin) {
ms = 500;
} else {
ms = 1000;
}
rfbLog("Warning: changing framebuffers in threaded mode may be unstable.\n");
threads_drop_input = 1;
usleep(ms * 1000);
}
INPUT_LOCK;
lock_client_sends(1);
if (use_threads) {
settle_clients(1);
}
if (reset_mem == 1) {
......@@ -842,6 +1048,16 @@ void do_new_fb(int reset_mem) {
if (ncache) {
check_ncache(1, 0);
}
prep_clients_for_new_fb();
lock_client_sends(0);
INPUT_UNLOCK;
if (use_threads) {
/* need to let things settle... */
settle_clients(0);
threads_drop_input = 0;
}
}
static void remove_fake_fb(void) {
......@@ -859,51 +1075,6 @@ static void remove_fake_fb(void) {
fake_fb = NULL;
}
static void lock_client_sends(int lock) {
static rfbClientPtr *cls = NULL;
static int cls_len = 0;
static int blocked = 0;
rfbClientIteratorPtr iter;
rfbClientPtr cl;
if (!use_threads) {
return;
}
if (!screen) {
return;
}
if (lock) {
if (cls_len < client_count + 128) {
if (cls != NULL) {
free(cls);
}
cls_len = client_count + 128;
cls = (rfbClientPtr *) calloc(cls_len * sizeof(rfbClientPtr), 1);
}
iter = rfbGetClientIterator(screen);
blocked = 0;
while ((cl = rfbClientIteratorNext(iter)) != NULL) {
SEND_LOCK(cl);
rfbLog("locked client: %p\n", cl);
cls[blocked++] = cl;
}
rfbReleaseClientIterator(iter);
} else {
int i;
for (i=0; i < blocked; i++) {
cl = cls[i];
if (cl != NULL) {
SEND_UNLOCK(cl)
rfbLog("unlocked client: %p\n", cl);
}
cls[i] = NULL;
}
blocked = 0;
}
}
static void rfb_new_framebuffer(rfbScreenInfoPtr rfbScreen, char *framebuffer,
int width,int height, int bitsPerSample,int samplesPerPixel,
int bytesPerPixel) {
......@@ -918,12 +1089,14 @@ static void install_fake_fb(int w, int h, int bpp) {
if (! screen) {
return;
}
lock_client_sends(1);
if (fake_fb) {
free(fake_fb);
}
fake_fb = (char *) calloc(w*h*bpp/8, 1);
if (! fake_fb) {
rfbLog("could not create fake fb: %dx%d %d\n", w, h, bpp);
lock_client_sends(0);
return;
}
bpc = guess_bits_per_color(bpp);
......@@ -931,7 +1104,6 @@ static void install_fake_fb(int w, int h, int bpp) {
rfbLog("rfbNewFramebuffer(0x%x, 0x%x, %d, %d, %d, %d, %d)\n",
screen, fake_fb, w, h, bpc, 1, bpp/8);
lock_client_sends(1);
rfb_new_framebuffer(screen, fake_fb, w, h, bpc, 1, bpp/8);
lock_client_sends(0);
}
......@@ -2275,6 +2447,8 @@ if (0) fprintf(stderr, "vis_str %s\n", vis_str ? vis_str : "notset");
/* set up parameters for subwin or non-subwin cases: */
again:
if (! subwin) {
/* full screen */
window = rootwin;
......@@ -2368,7 +2542,6 @@ if (0) fprintf(stderr, "DefaultDepth: %d visial_id: %d\n", depth, (int) visual_
(int) XVisualIDFromVisual(default_visual));
}
again:
if (subwin) {
int shift = 0, resize = 0;
int subwin_x, subwin_y;
......@@ -2435,7 +2608,9 @@ if (0) fprintf(stderr, "DefaultDepth: %d visial_id: %d\n", depth, (int) visual_
*/
fb = XCreateImage_wr(dpy, default_visual, depth, ZPixmap,
0, NULL, dpy_x, dpy_y, BitmapPad(dpy), 0);
fb->data = (char *) malloc(fb->bytes_per_line * fb->height);
if (fb) {
fb->data = (char *) malloc(fb->bytes_per_line * fb->height);
}
} else {
fb = XGetImage_wr(dpy, window, 0, 0, dpy_x, dpy_y, AllPlanes,
......@@ -2448,7 +2623,7 @@ if (0) fprintf(stderr, "DefaultDepth: %d visial_id: %d\n", depth, (int) visual_
if (subwin) {
XSetErrorHandler(old_handler);
if (trapped_xerror) {
if (trapped_xerror || fb == NULL) {
rfbLog("trapped GetImage at SUBWIN creation.\n");
if (try < subwin_tries) {
usleep(250 * 1000);
......@@ -2464,10 +2639,51 @@ if (0) fprintf(stderr, "DefaultDepth: %d visial_id: %d\n", depth, (int) visual_
}
trapped_xerror = 0;
} else if (! fb && try == 1) {
/* try once more */
usleep(250 * 1000);
goto again;
} else if (fb == NULL) {
XEvent xev;
rfbLog("initialize_xdisplay_fb: *** fb creation failed: 0x%x try: %d\n", fb, try);
#if LIBVNCSERVER_HAVE_LIBXRANDR
if (xrandr_present && xrandr_base_event_type) {
int cnt = 0;
while (XCheckTypedEvent(dpy, xrandr_base_event_type + RRScreenChangeNotify, &xev)) {
XRRScreenChangeNotifyEvent *rev;
rev = (XRRScreenChangeNotifyEvent *) &xev;
rfbLog("initialize_xdisplay_fb: XRANDR event while redoing fb[%d]:\n", cnt++);
rfbLog(" serial: %d\n", (int) rev->serial);
rfbLog(" timestamp: %d\n", (int) rev->timestamp);
rfbLog(" cfg_timestamp: %d\n", (int) rev->config_timestamp);
rfbLog(" size_id: %d\n", (int) rev->size_index);
rfbLog(" sub_pixel: %d\n", (int) rev->subpixel_order);
rfbLog(" rotation: %d\n", (int) rev->rotation);
rfbLog(" width: %d\n", (int) rev->width);
rfbLog(" height: %d\n", (int) rev->height);
rfbLog(" mwidth: %d mm\n", (int) rev->mwidth);
rfbLog(" mheight: %d mm\n", (int) rev->mheight);
rfbLog("\n");
rfbLog("previous WxH: %dx%d\n", wdpy_x, wdpy_y);
xrandr_width = rev->width;
xrandr_height = rev->height;
xrandr_timestamp = rev->timestamp;
xrandr_cfg_time = rev->config_timestamp;
xrandr_rotation = (int) rev->rotation;
rfbLog("initialize_xdisplay_fb: updating XRANDR config...\n");
XRRUpdateConfiguration(&xev);
}
}
#endif
if (try < 5) {
XFlush_wr(dpy);
usleep(250 * 1000);
if (try < 3) {
XSync(dpy, False);
} else if (try >= 3) {
XSync(dpy, True);
}
goto again;
}
}
if (use_snapfb) {
initialize_snap_fb();
......@@ -2734,9 +2950,11 @@ static rfbBool set_xlate_wrapper(rfbClientPtr cl) {
} else if (ncache) {
int save = ncache_xrootpmap;
rfbLog("set_xlate_wrapper: clearing -ncache for new pixel format.\n");
INPUT_LOCK;
ncache_xrootpmap = 0;
check_ncache(1, 0);
ncache_xrootpmap = save;
INPUT_UNLOCK;
}
return rfbSetTranslateFunction(cl);
}
......@@ -2751,7 +2969,8 @@ void initialize_screen(int *argc, char **argv, XImage *fb) {
int create_screen = screen ? 0 : 1;
int bits_per_color;
int fb_bpp, fb_Bpl, fb_depth;
int locked_sends = 0;
bpp = fb->bits_per_pixel;
fb_bpp = (int) fb->bits_per_pixel;
......@@ -2860,7 +3079,10 @@ void initialize_screen(int *argc, char **argv, XImage *fb) {
*/
bits_per_color = guess_bits_per_color(fb_bpp);
lock_client_sends(1);
if (lock_client_sends(-1) == 0) {
lock_client_sends(1);
locked_sends = 1;
}
/* n.b. samplesPerPixel (set = 1 here) seems to be unused. */
if (create_screen) {
......@@ -3274,7 +3496,6 @@ void initialize_screen(int *argc, char **argv, XImage *fb) {
/* may need, bpp, main_red_max, etc. */
parse_wireframe();
parse_scroll_copyrect();
setup_cursors_and_push();
if (scaling || rotating || cmap8to24) {
......@@ -3297,10 +3518,13 @@ void initialize_screen(int *argc, char **argv, XImage *fb) {
}
rfbReleaseClientIterator(iter);
if (!quiet) rfbLog(" done.\n");
do_copy_screen = 1;
/* done for framebuffer change case */
lock_client_sends(0);
if (locked_sends) {
lock_client_sends(0);
}
do_copy_screen = 1;
return;
}
......@@ -3376,7 +3600,10 @@ void initialize_screen(int *argc, char **argv, XImage *fb) {
install_passwds();
lock_client_sends(0);
if (locked_sends) {
lock_client_sends(0);
}
return;
}
#define DO_AVAHI \
......@@ -3981,6 +4208,7 @@ void watch_loop(void) {
while (1) {
char msg[] = "new client: %s taking unixpw client off hold.\n";
int skip_scan_for_updates = 0;
got_user_input = 0;
got_pointer_input = 0;
......@@ -4187,8 +4415,20 @@ void watch_loop(void) {
if (x11vnc_current < last_new_client + 0.5 && !all_clients_initialized()) {
continue;
}
if (subwin && freeze_when_obscured) {
/* XXX not working */
X_LOCK;
XFlush_wr(dpy);
X_UNLOCK;
check_xevents(0);
if (subwin_obscured) {
skip_scan_for_updates = 1;
}
}
if (button_mask && (!show_dragging || pointer_mode == 0)) {
if (skip_scan_for_updates) {
;
} else if (button_mask && (!show_dragging || pointer_mode == 0)) {
/*
* if any button is pressed in this mode do
* not update rfb screen, but do flush the
......@@ -4216,6 +4456,7 @@ void watch_loop(void) {
if (rawfb_vnc_reflect) {
vnc_reflect_process_client();
}
dtime0(&tm);
#if !NO_X11
......@@ -4235,7 +4476,9 @@ void watch_loop(void) {
}
X_UNLOCK;
}
X_LOCK;
check_xrandr_event("before-scan");
X_UNLOCK;
}
#endif
if (use_snapfb) {
......
......@@ -2666,8 +2666,8 @@ void check_https(void) {
}
void openssl_port(void) {
int sock, shutdown = 0;
static int port = 0;
int sock = -1, shutdown = 0;
static int port = -1;
static in_addr_t iface = INADDR_ANY;
int db = 0;
......@@ -2683,6 +2683,8 @@ void openssl_port(void) {
if (screen->listenSock > -1 && screen->port > 0) {
port = screen->port;
shutdown = 1;
} else if (screen->port == 0) {
port = screen->port;
}
if (screen->listenInterface) {
iface = screen->listenInterface;
......@@ -2696,14 +2698,18 @@ void openssl_port(void) {
#endif
}
if (port <= 0) {
if (port < 0) {
rfbLog("openssl_port: could not obtain listening port %d\n", port);
clean_up_exit(1);
}
sock = rfbListenOnTCPPort(port, iface);
if (sock < 0) {
rfbLog("openssl_port: could not reopen port %d\n", port);
clean_up_exit(1);
} else if (port == 0) {
/* no listen case, i.e. -connect */
sock = -1;
} else {
sock = rfbListenOnTCPPort(port, iface);
if (sock < 0) {
rfbLog("openssl_port: could not reopen port %d\n", port);
clean_up_exit(1);
}
}
rfbLog("openssl_port: listen on port/sock %d/%d\n", port, sock);
if (!quiet) {
......@@ -3852,6 +3858,7 @@ void accept_openssl(int mode, int presock) {
if (screen->port == 0) {
int fd = fileno(stdin);
if (getenv("X11VNC_INETD_PORT")) {
/* mutex */
screen->port = atoi(getenv(
"X11VNC_INETD_PORT"));
} else {
......
......@@ -176,6 +176,7 @@ Screen
=GAL SubWindow::
id:
sid:
=RA id_cmd:
=GAL LOFF
=GAL ResizeRotate::
= xrandr
......
......@@ -187,6 +187,7 @@ char gui_code[] = "";
" =GAL SubWindow::\n"
" id:\n"
" sid:\n"
" =RA id_cmd:\n"
" =GAL LOFF\n"
" =GAL ResizeRotate::\n"
" = xrandr\n"
......
......@@ -2033,6 +2033,7 @@ void unixpw_accept(char *user) {
unixpw_client->viewOnly = TRUE;
}
unixpw_in_progress = 0;
/* mutex */
screen->permitFileTransfer = unixpw_file_xfer_save;
if ((tightfilexfer = unixpw_tightvnc_xfer_save)) {
/* this doesn't work: the current client is never registered! */
......@@ -2078,6 +2079,7 @@ void unixpw_deny(void) {
}
unixpw_in_progress = 0;
/* mutex */
screen->permitFileTransfer = unixpw_file_xfer_save;
if ((tightfilexfer = unixpw_tightvnc_xfer_save)) {
#ifdef LIBVNCSERVER_WITH_TIGHTVNC_FILETRANSFER
......
......@@ -1085,6 +1085,7 @@ void install_passwds(void) {
passwds_new[0] = passwds_old[0];
passwds_new[1] = viewonly_passwd;
passwds_new[2] = NULL;
/* mutex */
screen->authPasswdData = (void*) passwds_new;
} else if (passwd_list) {
int i = 0;
......@@ -1094,6 +1095,7 @@ void install_passwds(void) {
if (begin_viewonly < 0) {
begin_viewonly = i+1;
}
/* mutex */
screen->authPasswdData = (void*) passwd_list;
screen->authPasswdFirstViewOnly = begin_viewonly;
}
......@@ -1167,6 +1169,7 @@ static void handle_one_http_request(void) {
if (inetd || screen->httpPort == 0) {
int port = find_free_port(5800, 5860);
if (port) {
/* mutex */
screen->httpPort = port;
} else {
rfbLog("handle_one_http_request: no http port.\n");
......@@ -1703,6 +1706,7 @@ static void vnc_redirect_loop(char *vnc_redirect_test, int *vnc_redirect_cnt) {
#if LIBVNCSERVER_HAVE_FORK
if ((pid = fork()) > 0) {
close(screen->httpListenSock);
/* mutex */
screen->httpListenSock = -2;
usleep(500 * 1000);
} else {
......
......@@ -373,6 +373,7 @@ static void parse_wireframe_str(char *wf) {
Colormap cmap;
if (dpy && (bpp == 32 || bpp == 16)) {
#if !NO_X11
X_LOCK;
cmap = DefaultColormap (dpy, scr);
if (XParseColor(dpy, cmap, str, &cdef) &&
XAllocColor(dpy, cmap, &cdef)) {
......@@ -389,6 +390,7 @@ static void parse_wireframe_str(char *wf) {
wireframe_shade = n;
ok = 1;
}
X_UNLOCK;
#else
r = g = b = 0;
cmap = 0;
......
......@@ -471,6 +471,7 @@ double rfac(void) {
void check_allinput_rate(void) {
static double last_all_input_check = 0.0;
static int set = 0, verb = -1;
if (use_threads) {
return;
}
......@@ -703,7 +704,7 @@ double rect_overlap(int x1, int y1, int x2, int y2, int X1, int Y1,
char *choose_title(char *display) {
static char title[(MAXN+10)];
memset(title, 0, MAXN+10);
memset(title, 0, sizeof(title));
strcpy(title, "x11vnc");
if (display == NULL) {
......@@ -724,13 +725,33 @@ char *choose_title(char *display) {
if (subwin && dpy && valid_window(subwin, NULL, 0)) {
#if !NO_X11
char *name = NULL;
int do_appshare = getenv("X11VNC_APPSHARE_ACTIVE") ? 1 : 0;
if (0 && do_appshare) {
title[0] = '\0';
}
if (XFetchName(dpy, subwin, &name)) {
if (name) {
strncat(title, " ", MAXN - strlen(title));
if (title[0] != '\0') {
strncat(title, " ", MAXN - strlen(title));
}
strncat(title, name, MAXN - strlen(title));
free(name);
}
}
if (do_appshare) {
Window c;
int x, y;
if (xtranslate(subwin, rootwin, 0, 0, &x, &y, &c, 1)) {
char tmp[32];
if (scaling) {
x *= scale_fac_x;
y *= scale_fac_y;
}
sprintf(tmp, " XY=%d,%d", x, y);
strncat(title, tmp, MAXN - strlen(title));
}
rfbLog("appshare title: %s\n", title);
}
#endif /* NO_X11 */
}
X_UNLOCK;
......
......@@ -72,6 +72,11 @@ extern char *choose_title(char *display);
#define NONUL(x) ((x) ? (x) : "")
/*
Put this in usleep2() for debug printout.
fprintf(stderr, "_mysleep: %08d %10.6f %s:%d\n", (x), dnow() - x11vnc_start, __FILE__, __LINE__); \
*/
/* XXX usleep(3) is not thread safe on some older systems... */
extern struct timeval _mysleep;
#define usleep2(x) \
......@@ -96,6 +101,10 @@ extern struct timeval _mysleep;
*/
#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD
extern MUTEX(x11Mutex);
extern MUTEX(scrollMutex);
MUTEX(clientMutex);
MUTEX(inputMutex);
MUTEX(pointerMutex);
#endif
#define X_INIT INIT_MUTEX(x11Mutex)
......@@ -105,26 +114,33 @@ extern MUTEX(x11Mutex);
#define X_UNLOCK UNLOCK(x11Mutex)
#else
extern int hxl;
#define X_LOCK fprintf(stderr, "*** X_LOCK**[%05d] %d%s\n", \
__LINE__, hxl, hxl ? " BAD-PRE-LOCK":""); LOCK(x11Mutex); hxl = 1;
#define X_UNLOCK fprintf(stderr, " x_unlock[%05d] %d%s\n", \
__LINE__, hxl, !hxl ? " BAD-PRE-UNLOCK":""); UNLOCK(x11Mutex); hxl = 0;
#define X_LOCK fprintf(stderr, "*** X_LOCK** %d%s %s:%d\n", \
hxl, hxl ? " BAD-PRE-LOCK":"", __FILE__, __LINE__); LOCK(x11Mutex); hxl = 1;
#define X_UNLOCK fprintf(stderr, " x_unlock %d%s %s:%d\n", \
hxl, !hxl ? " BAD-PRE-UNLOCK":"", __FILE__, __LINE__); UNLOCK(x11Mutex); hxl = 0;
#endif
#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD
extern MUTEX(scrollMutex);
#endif
#define SCR_LOCK if (use_threads) {LOCK(scrollMutex);}
#define SCR_UNLOCK if (use_threads) {UNLOCK(scrollMutex);}
#define SCR_INIT INIT_MUTEX(scrollMutex)
#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD
MUTEX(clientMutex);
#endif
#define CLIENT_LOCK if (use_threads) {LOCK(clientMutex);}
#define CLIENT_UNLOCK if (use_threads) {UNLOCK(clientMutex);}
#define CLIENT_INIT INIT_MUTEX(clientMutex)
#if 1
#define INPUT_LOCK if (use_threads) {LOCK(inputMutex);}
#define INPUT_UNLOCK if (use_threads) {UNLOCK(inputMutex);}
#else
#define INPUT_LOCK
#define INPUT_UNLOCK
#endif
#define INPUT_INIT INIT_MUTEX(inputMutex)
#define POINTER_LOCK if (use_threads) {LOCK(pointerMutex);}
#define POINTER_UNLOCK if (use_threads) {UNLOCK(pointerMutex);}
#define POINTER_INIT INIT_MUTEX(pointerMutex)
/*
* The sendMutex member was added to libvncserver 0.9.8
* rfb/rfb.h sets LIBVNCSERVER_SEND_MUTEX if present.
......
......@@ -38,6 +38,7 @@ so, delete this exception statement from your version.
#include "cleanup.h"
#include "xwrappers.h"
#include "connections.h"
#include "xrandr.h"
#include "macosx.h"
winattr_t *stack_list = NULL;
......@@ -49,7 +50,7 @@ Window parent_window(Window win, char **name);
int valid_window(Window win, XWindowAttributes *attr_ret, int bequiet);
Bool xtranslate(Window src, Window dst, int src_x, int src_y, int *dst_x,
int *dst_y, Window *child, int bequiet);
int get_window_size(Window win, int *x, int *y);
int get_window_size(Window win, int *w, int *h);
void snapshot_stack_list(int free_only, double allowed_age);
int get_boff(void);
int get_bwin(void);
......@@ -58,6 +59,7 @@ Window query_pointer(Window start);
unsigned int mask_state(void);
int pick_windowid(unsigned long *num);
Window descend_pointer(int depth, Window start, char *name_info, int len);
void id_cmd(char *cmd);
Window parent_window(Window win, char **name) {
......@@ -184,12 +186,12 @@ Bool xtranslate(Window src, Window dst, int src_x, int src_y, int *dst_x,
#endif /* NO_X11 */
}
int get_window_size(Window win, int *x, int *y) {
int get_window_size(Window win, int *w, int *h) {
XWindowAttributes attr;
/* valid_window? */
if (valid_window(win, &attr, 1)) {
*x = attr.width;
*y = attr.height;
*w = attr.width;
*h = attr.height;
return 1;
} else {
return 0;
......@@ -609,3 +611,161 @@ Window descend_pointer(int depth, Window start, char *name_info, int len) {
#endif /* NO_X11 */
}
void id_cmd(char *cmd) {
int rc, dx = 0, dy = 0, dw = 0, dh = 0;
int x0, y0, w0, h0;
int x, y, w, h, do_move = 0, do_resize = 0;
int disp_x = DisplayWidth(dpy, scr);
int disp_y = DisplayHeight(dpy, scr);
Window win = subwin;
XWindowAttributes attr;
XErrorHandler old_handler = NULL;
Window twin;
if (!cmd || !strcmp(cmd, "")) {
return;
}
if (strstr(cmd, "win=") == cmd) {
if (! scan_hexdec(cmd + strlen("win="), &win)) {
rfbLog("id_cmd: incorrect win= hex/dec number: %s\n", cmd);
return;
} else {
char *q = strchr(cmd, ':');
if (!q) {
rfbLog("id_cmd: incorrect win=...: hex/dec number: %s\n", cmd);
return;
}
rfbLog("id_cmd:%s set window id to 0x%lx\n", cmd, win);
cmd = q+1;
}
}
if (!win) {
rfbLog("id_cmd:%s not in sub-window mode or no win=0xNNNN.\n", cmd);
return;
}
#if !NO_X11
X_LOCK;
if (!valid_window(win, &attr, 1)) {
X_UNLOCK;
return;
}
w0 = w = attr.width;
h0 = h = attr.height;
old_handler = XSetErrorHandler(trap_xerror);
trapped_xerror = 0;
XTranslateCoordinates(dpy, win, rootwin, 0, 0, &x, &y, &twin);
x0 = x;
y0 = y;
if (strstr(cmd, "move:") == cmd) {
if (sscanf(cmd, "move:%d%d", &dx, &dy) == 2) {
x = x + dx;
y = y + dy;
do_move = 1;
}
} else if (strstr(cmd, "resize:") == cmd) {
if (sscanf(cmd, "resize:%d%d", &dw, &dh) == 2) {
w = w + dw;
h = h + dh;
do_move = 1;
do_resize = 1;
}
} else if (strstr(cmd, "geom:") == cmd) {
if (parse_geom(cmd+strlen("geom:"), &w, &h, &x, &y, disp_x, disp_y)) {
do_move = 1;
do_resize = 1;
if (w <= 0) {
w = w0;
}
if (h <= 0) {
h = h0;
}
if (scaling && getenv("X11VNC_APPSHARE_ACTIVE")) {
x /= scale_fac_x;
y /= scale_fac_y;
}
}
} else if (!strcmp(cmd, "raise")) {
rc = XRaiseWindow(dpy, win);
rfbLog("id_cmd:%s rc=%d\n", cmd, rc);
} else if (!strcmp(cmd, "lower")) {
rc = XLowerWindow(dpy, win);
rfbLog("id_cmd:%s rc=%d\n", cmd, rc);
} else if (!strcmp(cmd, "map")) {
rc= XMapRaised(dpy, win);
rfbLog("id_cmd:%s rc=%d\n", cmd, rc);
} else if (!strcmp(cmd, "unmap")) {
rc= XUnmapWindow(dpy, win);
rfbLog("id_cmd:%s rc=%d\n", cmd, rc);
} else if (!strcmp(cmd, "iconify")) {
rc= XIconifyWindow(dpy, win, scr);
rfbLog("id_cmd:%s rc=%d\n", cmd, rc);
} else if (strstr(cmd, "wm_name:") == cmd) {
rc= XStoreName(dpy, win, cmd+strlen("wm_name:"));
rfbLog("id_cmd:%s rc=%d\n", cmd, rc);
} else if (strstr(cmd, "icon_name:") == cmd) {
rc= XSetIconName(dpy, win, cmd+strlen("icon_name:"));
rfbLog("id_cmd:%s rc=%d\n", cmd, rc);
} else if (!strcmp(cmd, "wm_delete")) {
XClientMessageEvent ev;
memset(&ev, 0, sizeof(ev));
ev.type = ClientMessage;
ev.send_event = True;
ev.display = dpy;
ev.window = win;
ev.message_type = XInternAtom(dpy, "WM_PROTOCOLS", False);
ev.format = 32;
ev.data.l[0] = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
rc = XSendEvent(dpy, win, False, 0, (XEvent *) &ev);
rfbLog("id_cmd:%s rc=%d\n", cmd, rc);
} else {
rfbLog("id_cmd:%s unrecognized command.\n", cmd);
}
if (do_move || do_resize) {
if (w >= disp_x) {
w = disp_x - 4;
}
if (h >= disp_y) {
h = disp_y - 4;
}
if (w < 1) {
w = 1;
}
if (h < 1) {
h = 1;
}
if (x + w > disp_x) {
x = disp_x - w - 1;
}
if (y + h > disp_y) {
y = disp_y - h - 1;
}
if (x < 0) {
x = 1;
}
if (y < 0) {
y = 1;
}
rc = 0;
rc += XMoveWindow(dpy, win, x, y);
off_x = x;
off_y = y;
rc += XResizeWindow(dpy, win, w, h);
rfbLog("id_cmd:%s rc=%d dx=%d dy=%d dw=%d dh=%d %dx%d+%d+%d -> %dx%d+%d+%d\n",
cmd, rc, dx, dy, dw, dh, w0, h0, x0, y0, w, h, x, h);
}
XSync(dpy, False);
XSetErrorHandler(old_handler);
if (trapped_xerror) {
rfbLog("id_cmd:%s trapped_xerror.\n", cmd);
}
trapped_xerror = 0;
if (do_resize) {
rfbLog("id_cmd:%s calling check_xrandr_event.\n", cmd);
check_xrandr_event("id_cmd");
}
X_UNLOCK;
#endif
}
......@@ -45,7 +45,7 @@ extern Window parent_window(Window win, char **name);
extern int valid_window(Window win, XWindowAttributes *attr_ret, int bequiet);
extern Bool xtranslate(Window src, Window dst, int src_x, int src_y, int *dst_x,
int *dst_y, Window *child, int bequiet);
extern int get_window_size(Window win, int *x, int *y);
extern int get_window_size(Window win, int *w, int *h);
extern void snapshot_stack_list(int free_only, double allowed_age);
extern int get_boff(void);
extern int get_bwin(void);
......@@ -54,5 +54,6 @@ extern Window query_pointer(Window start);
extern unsigned int mask_state(void);
extern int pick_windowid(unsigned long *num);
extern Window descend_pointer(int depth, Window start, char *name_info, int len);
extern void id_cmd(char *cmd);
#endif /* _X11VNC_WIN_UTILS_H */
.\" This file was automatically generated from x11vnc -help output.
.TH X11VNC "1" "November 2009" "x11vnc " "User Commands"
.TH X11VNC "1" "December 2009" "x11vnc " "User Commands"
.SH NAME
x11vnc - allow VNC connections to real X11 displays
version: 0.9.9, lastmod: 2009-11-18
version: 0.9.9, lastmod: 2009-12-02
.SH SYNOPSIS
.B x11vnc
[OPTION]...
......@@ -158,6 +158,15 @@ shifts a root view to it: this shows SaveUnders menus,
etc, although they will be clipped if they extend beyond
the window.
.PP
\fB-appshare\fR
.IP
Simple application sharing based on the \fB-id/-sid\fR
mechanism. Every new toplevel window that the
application creates induces a new viewer window via
a reverse connection. The \fB-id/-sid\fR and \fB-connect\fR
options are required. Run 'x11vnc \fB-appshare\fR \fB-help'\fR
for more info.
.PP
\fB-clip\fR \fIWxH+X+Y\fR
.IP
Only show the sub-region of the full display that
......@@ -3110,7 +3119,7 @@ Instead of exiting after cleaning up, run a simple
\fB-q,\fR \fB-quiet\fR
.IP
Be quiet by printing less informational output to
stderr.
stderr. (use \fB-noquiet\fR to undo an earlier \fB-quiet.)\fR
.PP
\fB-v,\fR \fB-verbose\fR
.IP
......@@ -3611,7 +3620,7 @@ Client-side caching scheme. Framebuffer memory \fIn\fR
(an integer) times that of the full display is allocated
below the actual framebuffer to cache screen contents
for rapid retrieval. So a W x H frambuffer is expanded
to a W x (n+1)*H one. Use 0 to disable. Default: XXX.
to a W x (n+1)*H one. Use 0 to disable.
.IP
The \fIn\fR is actually optional, the default is 10.
.IP
......@@ -3619,13 +3628,17 @@ For this and the other \fB-ncache*\fR options below you can
abbreviate "\fB-ncache\fR" with "\fB-nc\fR". Also, "\fB-nonc\fR"
is the same as "\fB-ncache\fR \fI0\fR"
.IP
This is an experimental option, currently implemented
in an awkward way in that in the VNC Viewer you can
see the cache contents if you scroll down, etc. So you
This is an experimental option, currently implemented in
an awkward way in that in the VNC Viewer you can see the
pixel cache contents if you scroll down, etc. So you
will have to set things up so you can't see that region.
If this method is successful, the changes required for
clients to do this less awkwardly will be investigated.
.IP
The SSVNC viewer does a good job at automatically hiding
the pixel cache region. Or use SSVNC's \fB-ycrop\fR option
to explicitly hide the region.
.IP
Note that this mode consumes a huge amount of memory,
both on the x11vnc server side and on the VNC Viewer
side. If n=2 then the amount of RAM used is roughly
......@@ -4465,10 +4478,7 @@ In this mode new threads (one for input and one
for output) are created to handle each new client.
Default: \fB-nothreads.\fR
.IP
NOTE: The \fB-threads\fR mode may be disabled due to its
unstable behavior. If it is disabled, a warning is
printed out. Stability has been improved in version
0.9.8 and so the feature has been re-enabled.
Thread stability is much improved in version 0.9.8.
.IP
Multiple clients in threaded mode should be stable
for the ZRLE encoding on all platforms. The Tight and
......@@ -4476,9 +4486,14 @@ Zlib encodings are currently only stable on Linux for
multiple clients. Compile with \fB-DTLS=__thread\fR if your
OS and compiler and linker support it.
.IP
For resizes (randr, etc.) set this env. var. to the number
of milliseconds to sleep: X11VNC_THREADS_NEW_FB_SLEEP
at various places in the do_new_fb() action. This is to
let various activities settle. Default is about 500ms.
.IP
Multiple clients in threaded mode could yield better
performance for 'class-room' broadcasting usage.
See also the \fB-reflect\fR option.
performance for 'class-room' broadcasting usage; also in
\fB-appshare\fR broadcast mode. See also the \fB-reflect\fR option.
.PP
\fB-fs\fR \fIf\fR
.IP
......@@ -5252,6 +5267,13 @@ or "root" to go back to root window
.IP
sid:windowid set \fB-sid\fR window to "windowid"
.IP
id_cmd:cmd cmds: raise, lower, map, unmap, iconify,
move:dXdY, resize:dWdH, geom:WxH+X+Y. dX
dY, dW, and dH must have a leading "+"
or "-" e.g.: move:-30+10 resize:+20+35
also: wm_delete, wm_name:string and
icon_name:string. Also id_cmd:win=N:cmd
.IP
waitmapped wait until subwin is mapped.
.IP
nowaitmapped do not wait until subwin is mapped.
......@@ -6035,8 +6057,8 @@ query straight to the X11VNC_REMOTE property or connect
file use "qry=..." instead of "cmd=..."
.IP
ans= stop quit exit shutdown ping resend_cutbuffer
resend_clipboard resend_primary blacken zero
refresh reset close disconnect id sid waitmapped
resend_clipboard resend_primary blacken zero refresh
reset close disconnect id_cmd id sid waitmapped
nowaitmapped clip flashcmap noflashcmap shiftcmap
truecolor notruecolor overlay nooverlay overlay_cursor
overlay_yescursor nooverlay_nocursor nooverlay_cursor
......@@ -6185,6 +6207,10 @@ Any corresponding output text for that remote control
command is sent back to all client as rfbCutText.
The returned output is also prefixed with \fIstr\fR.
Example: \fB-remote_prefix\fR DO_THIS:
.IP
Note that enabling \fB-remote_prefix\fR allows the remote
VNC viewers to run x11vnc \fB-remote\fR commands. Do not
use this option if they are not to be trusted.
.PP
\fB-noremote,\fR \fB-yesremote\fR
.IP
......
......@@ -172,6 +172,7 @@ static void check_rcfile(int argc, char **argv);
static void immediate_switch_user(int argc, char* argv[]);
static void print_settings(int try_http, int bg, char *gui_str);
static void check_loop_mode(int argc, char* argv[], int force);
static void check_appshare_mode(int argc, char* argv[]);
static int tsdo_timeout_flag;
......@@ -1702,6 +1703,24 @@ static void check_loop_mode(int argc, char* argv[], int force) {
#endif
}
}
extern int appshare_main(int argc, char* argv[]);
static void check_appshare_mode(int argc, char* argv[]) {
int i;
for (i=1; i < argc; i++) {
char *p = argv[i];
if (strstr(p, "--") == p) {
p++;
}
if (strstr(p, "-appshare") == p) {
appshare_main(argc, argv);
exit(0);
}
}
}
static void store_homedir_passwd(char *file) {
char str1[32], str2[32], *p, *h, *f;
struct stat sbuf;
......@@ -1992,21 +2011,32 @@ int main(int argc, char* argv[]) {
char *got_rfbport_str = NULL;
int got_rfbport_pos = -1;
int got_tls = 0;
int got_inetd = 0;
int got_noxrandr = 0;
/* used to pass args we do not know about to rfbGetScreen(): */
int argc_vnc_max = 1024;
int argc_vnc = 1; char *argv_vnc[2048];
/* check for -loop mode: */
check_loop_mode(argc, argv, 0);
/* check for -appshare mode: */
check_appshare_mode(argc, argv);
dtime0(&x11vnc_start);
for (i=1; i < argc; i++) {
if (!strcmp(argv[i], "-inetd")) {
got_inetd = 1;
}
}
if (!getuid() || !geteuid()) {
started_as_root = 1;
rfbLog("getuid: %d geteuid: %d\n", getuid(), geteuid());
if (0 && !got_inetd) {
rfbLog("getuid: %d geteuid: %d\n", getuid(), geteuid());
}
/* check for '-users =bob' */
immediate_switch_user(argc, argv);
......@@ -2363,6 +2393,14 @@ int main(int argc, char* argv[]) {
; /* handled above */
continue;
}
if (strstr(arg, "-appshare") == arg) {
; /* handled above */
continue;
}
if (strstr(arg, "-freeze_when_obscured") == arg) {
freeze_when_obscured = 1;
continue;
}
if (!strcmp(arg, "-timeout")) {
CHECK_ARGC
first_conn_timeout = atoi(argv[++i]);
......@@ -2955,6 +2993,7 @@ int main(int argc, char* argv[]) {
if (!strcmp(arg, "-noxrandr")) {
xrandr = 0;
xrandr_maybe = 0;
got_noxrandr = 1;
continue;
}
if (!strcmp(arg, "-rotate")) {
......@@ -3040,6 +3079,10 @@ int main(int argc, char* argv[]) {
quiet = 1;
continue;
}
if (!strcmp(arg, "-noquiet")) {
quiet = 0;
continue;
}
if (!strcmp(arg, "-v") || !strcmp(arg, "-verbose")) {
verbose = 1;
continue;
......@@ -3049,7 +3092,9 @@ int main(int argc, char* argv[]) {
bg = 1;
opts_bg = bg;
#else
fprintf(stderr, "warning: -bg mode not supported.\n");
if (!got_inetd) {
fprintf(stderr, "warning: -bg mode not supported.\n");
}
#endif
continue;
}
......@@ -3446,8 +3491,10 @@ int main(int argc, char* argv[]) {
*p = '\0';
}
if (atoi(s) < 1 || atoi(s) > pointer_mode_max) {
rfbLog("pointer_mode out of range 1-%d: %d\n",
pointer_mode_max, atoi(s));
if (!got_inetd) {
rfbLog("pointer_mode out of range 1-%d: %d\n",
pointer_mode_max, atoi(s));
}
} else {
pointer_mode = atoi(s);
got_pointer_mode = pointer_mode;
......@@ -3591,7 +3638,9 @@ int main(int argc, char* argv[]) {
if (!strcmp(arg, "-chatwindow")) {
chat_window = 1;
if (argc_vnc + 1 < argc_vnc_max) {
rfbLog("setting '-rfbversion 3.6' for -chatwindow.\n");
if (!got_inetd) {
rfbLog("setting '-rfbversion 3.6' for -chatwindow.\n");
}
argv_vnc[argc_vnc++] = strdup("-rfbversion");
argv_vnc[argc_vnc++] = strdup("3.6");
}
......@@ -3645,14 +3694,16 @@ int main(int argc, char* argv[]) {
/* we re-enable it due to threaded mode bugfixes. */
use_threads = 1;
} else {
rfbLog("\n");
rfbLog("The -threads mode is unstable and not tested or maintained.\n");
rfbLog("It is disabled in the source code. If you really need\n");
rfbLog("the feature you can reenable it at build time by setting\n");
rfbLog("-DX11VNC_THREADED in CPPFLAGS. Or set X11VNC_THREADED=1\n");
rfbLog("in your runtime environment.\n");
rfbLog("\n");
usleep(500*1000);
if (!got_inetd) {
rfbLog("\n");
rfbLog("The -threads mode is unstable and not tested or maintained.\n");
rfbLog("It is disabled in the source code. If you really need\n");
rfbLog("the feature you can reenable it at build time by setting\n");
rfbLog("-DX11VNC_THREADED in CPPFLAGS. Or set X11VNC_THREADED=1\n");
rfbLog("in your runtime environment.\n");
rfbLog("\n");
usleep(500*1000);
}
}
#endif
continue;
......@@ -3931,9 +3982,11 @@ int main(int argc, char* argv[]) {
if (!strcasecmp(argv[i+1], "prompt")) {
;
} else if (!is_decimal(argv[i+1])) {
rfbLog("Invalid -rfbport value: '%s'\n", argv[i+1]);
rfbLog("setting it to '-1' to induce failure.\n");
argv[i+1] = strdup("-1");
if (!got_inetd) {
rfbLog("Invalid -rfbport value: '%s'\n", argv[i+1]);
rfbLog("setting it to '-1' to induce failure.\n");
argv[i+1] = strdup("-1");
}
}
got_rfbport_str = strdup(argv[i+1]);
got_rfbport_pos = argc_vnc+1;
......@@ -4032,7 +4085,9 @@ int main(int argc, char* argv[]) {
client_connect_file = str;
}
if (client_connect_file) {
rfbLog("MacOS X: set -connect file to %s\n", client_connect_file);
if (!got_inetd) {
rfbLog("MacOS X: set -connect file to %s\n", client_connect_file);
}
}
}
}
......@@ -4053,7 +4108,9 @@ int main(int argc, char* argv[]) {
rfbLog("Port prompt indicated cancel.\n");
clean_up_exit(1);
}
rfbLog("Port prompt selected: %d\n", got_rfbport_val);
if (!got_inetd) {
rfbLog("Port prompt selected: %d\n", got_rfbport_val);
}
sprintf(tport, "%d", got_rfbport_val);
argv_vnc[got_rfbport_pos] = strdup(tport);
free(opts);
......@@ -4117,8 +4174,9 @@ int main(int argc, char* argv[]) {
q = t + strlen(pstr);
}
logfile = new;
if (!quiet) {
if (!quiet && !got_inetd) {
rfbLog("Expanded logfile to '%s'\n", new);
}
free(s);
}
......@@ -4152,7 +4210,7 @@ int main(int argc, char* argv[]) {
q = t + strlen(pstr);
}
logfile = new;
if (!quiet) {
if (!quiet && !got_inetd) {
rfbLog("Expanded logfile to '%s'\n", new);
}
free(s);
......@@ -4389,7 +4447,7 @@ int main(int argc, char* argv[]) {
if (1) {
/* mix things up a little bit */
unsigned char buf[CHALLENGESIZE];
int k, kmax = (int) (500 * rfac()) + 100;
int k, kmax = (int) (50 * rfac()) + 10;
for (k=0; k < kmax; k++) {
rfbRandomBytes(buf);
}
......@@ -4550,6 +4608,13 @@ int main(int argc, char* argv[]) {
exit(1);
}
if (use_threads && !got_noxrandr) {
xrandr = 1;
if (! quiet) {
rfbLog("enabling -xrandr in -threads mode.\n");
}
}
/* fixup settings that do not make sense */
if (use_threads && nofb && cursor_pos_updates) {
......@@ -4626,6 +4691,7 @@ int main(int argc, char* argv[]) {
/* increase rfbwait if threaded */
if (use_threads && ! got_rfbwait) {
/* ??? lower this ??? */
rfbMaxClientWait = 604800000;
}
......@@ -4712,6 +4778,8 @@ int main(int argc, char* argv[]) {
X_INIT;
SCR_INIT;
CLIENT_INIT;
INPUT_INIT;
POINTER_INIT;
/* open the X display: */
......@@ -5331,9 +5399,7 @@ int main(int argc, char* argv[]) {
}
#endif
if (!getenv("X11VNC_NO_CHECK_PM")) {
check_pm();
}
check_pm();
if (! quiet && ! raw_fb_str) {
rfbLog("--------------------------------------------------------\n");
......@@ -5644,4 +5710,3 @@ int main(int argc, char* argv[]) {
}
......@@ -47,7 +47,7 @@ int xtrap_base_event_type = 0;
int xdamage_base_event_type = 0;
/* date +'lastmod: %Y-%m-%d' */
char lastmod[] = "0.9.9 lastmod: 2009-11-18";
char lastmod[] = "0.9.9 lastmod: 2009-12-02";
/* X display info */
......
......@@ -340,6 +340,10 @@ static void initialize_xevents(int reset) {
X_LOCK;
xselectinput_rootwin |= PropertyChangeMask;
XSelectInput_wr(dpy, rootwin, xselectinput_rootwin);
if (subwin && freeze_when_obscured) {
XSelectInput_wr(dpy, subwin, VisibilityChangeMask);
}
X_UNLOCK;
did_xselect_input = 1;
}
......@@ -1284,6 +1288,22 @@ void check_xevents(int reset) {
last_call = now;
}
if (freeze_when_obscured) {
if (XCheckTypedEvent(dpy, VisibilityNotify, &xev)) {
if (xev.type == VisibilityNotify && xev.xany.window == subwin) {
int prev = subwin_obscured;
if (xev.xvisibility.state == VisibilityUnobscured) {
subwin_obscured = 0;
} else if (xev.xvisibility.state == VisibilityPartiallyObscured) {
subwin_obscured = 1;
} else {
subwin_obscured = 2;
}
rfbLog("subwin_obscured: %d -> %d\n", prev, subwin_obscured);
}
}
}
/* check for CUT_BUFFER0, VNC_CONNECT, X11VNC_REMOTE changes: */
if (XCheckTypedEvent(dpy, PropertyNotify, &xev)) {
int got_cutbuffer = 0;
......@@ -1622,6 +1642,10 @@ extern int rawfb_vnc_reflect;
void xcut_receive(char *text, int len, rfbClientPtr cl) {
allowed_input_t input;
if (threads_drop_input) {
return;
}
if (unixpw_in_progress) {
rfbLog("xcut_receive: unixpw_in_progress, skipping.\n");
return;
......@@ -1640,6 +1664,7 @@ void xcut_receive(char *text, int len, rfbClientPtr cl) {
if (!input.clipboard) {
return;
}
INPUT_LOCK;
if (remote_prefix != NULL && strstr(text, remote_prefix) == text) {
char *result, *rcmd = text + strlen(remote_prefix);
......@@ -1649,9 +1674,29 @@ void xcut_receive(char *text, int len, rfbClientPtr cl) {
strcat(tmp, "qry=");
}
strncat(tmp, rcmd, len - strlen(remote_prefix));
rfbLog("remote_prefix command: '%s'\n", tmp);
if (use_threads) {
if (client_connect_file) {
FILE *f = fopen(client_connect_file, "w");
if (f) {
fprintf(f, "%s\n", tmp);
fclose(f);
free(tmp);
INPUT_UNLOCK;
return;
}
}
if (vnc_connect) {
sprintf(x11vnc_remote_str, "%s", tmp);
free(tmp);
INPUT_UNLOCK;
return;
}
}
INPUT_UNLOCK;
result = process_remote_cmd(tmp, 1);
if (result == NULL ) {
result = strdup("null");
......@@ -1675,24 +1720,28 @@ void xcut_receive(char *text, int len, rfbClientPtr cl) {
}
if (! check_sel_direction("recv", "xcut_receive", text, len)) {
INPUT_UNLOCK;
return;
}
#ifdef MACOSX
if (macosx_console) {
macosx_set_sel(text, len);
INPUT_UNLOCK;
return;
}
#endif
if (rawfb_vnc_reflect) {
vnc_reflect_send_cuttext(text, len);
INPUT_UNLOCK;
return;
}
RAWFB_RET_VOID
#if NO_X11
INPUT_UNLOCK;
return;
#else
......@@ -1751,6 +1800,7 @@ void xcut_receive(char *text, int len, rfbClientPtr cl) {
XFlush_wr(dpy);
X_UNLOCK;
INPUT_UNLOCK;
set_cutbuffer = 1;
#endif /* NO_X11 */
......@@ -1956,6 +2006,7 @@ static void try_local_chat_window(void) {
return;
}
/* mutex */
new_save = screen->newClientHook;
screen->newClientHook = new_client_chat_helper;
......
......@@ -344,12 +344,14 @@ static void initialize_xinerama (void) {
RAWFB_RET_VOID
X_LOCK;
if (! XineramaQueryExtension(dpy, &ev, &er)) {
if (verbose) {
rfbLog("Xinerama: disabling: display does not support it.\n");
}
xinerama = 0;
xinerama_present = 0;
X_UNLOCK;
return;
}
if (! XineramaIsActive(dpy)) {
......@@ -359,6 +361,7 @@ static void initialize_xinerama (void) {
}
xinerama = 0;
xinerama_present = 0;
X_UNLOCK;
return;
}
xinerama_present = 1;
......@@ -385,6 +388,7 @@ static void initialize_xinerama (void) {
rfbLog("\n");
}
XFree_wr(xineramas);
X_UNLOCK;
return; /* must be OK w/o change */
}
......@@ -406,6 +410,7 @@ static void initialize_xinerama (void) {
sc++;
}
XFree_wr(xineramas);
X_UNLOCK;
if (sraRgnEmpty(black_region)) {
......
......@@ -143,6 +143,8 @@ static void handle_xrandr_change(int new_x, int new_y) {
RAWFB_RET_VOID
/* assumes no X_LOCK */
/* sanity check xrandr_mode */
if (! xrandr_mode) {
xrandr_mode = strdup("default");
......@@ -184,6 +186,8 @@ int check_xrandr_event(char *msg) {
RAWFB_RET(0)
/* it is assumed that X_LOCK is on at this point. */
if (subwin) {
return handle_subwin_resize(msg);
}
......@@ -235,8 +239,13 @@ int check_xrandr_event(char *msg) {
if (wdpy_x == rev->width && wdpy_y == rev->height &&
xrandr_rotation == (int) rev->rotation) {
rfbLog("check_xrandr_event: no change detected.\n");
rfbLog("check_xrandr_event: no change detected.\n");
do_change = 0;
if (! xrandr) {
rfbLog("check_xrandr_event: "
"enabling full XRANDR trapping anyway.\n");
xrandr = 1;
}
} else {
do_change = 1;
if (! xrandr) {
......@@ -256,6 +265,7 @@ int check_xrandr_event(char *msg) {
XRRUpdateConfiguration(&xev);
if (do_change) {
/* under do_change caller normally returns before its X_UNLOCK */
X_UNLOCK;
handle_xrandr_change(rev->width, rev->height);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment