summaryrefslogtreecommitdiffstats
path: root/x11vnc/gui.c
diff options
context:
space:
mode:
Diffstat (limited to 'x11vnc/gui.c')
-rw-r--r--x11vnc/gui.c638
1 files changed, 638 insertions, 0 deletions
diff --git a/x11vnc/gui.c b/x11vnc/gui.c
new file mode 100644
index 0000000..9e6ac54
--- /dev/null
+++ b/x11vnc/gui.c
@@ -0,0 +1,638 @@
+/* -- gui.c -- */
+
+#include "x11vnc.h"
+#include "xevents.h"
+#include "win_utils.h"
+#include "remote.h"
+#include "cleanup.h"
+
+#include "tkx11vnc.h"
+
+#define SYSTEM_TRAY_REQUEST_DOCK 0
+#define SYSTEM_TRAY_BEGIN_MESSAGE 1
+#define SYSTEM_TRAY_CANCEL_MESSAGE 2
+#define XEMBED_VERSION 0
+#define XEMBED_MAPPED (1 << 0)
+
+int icon_mode = 0; /* hack for -gui tray */
+char *icon_mode_file = NULL;
+FILE *icon_mode_fh = NULL;
+int icon_mode_socks[ICON_MODE_SOCKS];
+int tray_manager_ok = 0;
+Window tray_request = None;
+Window tray_window = None;
+int tray_unembed = 0;
+
+
+char *get_gui_code(void);
+int tray_embed(Window iconwin, int remove);
+void do_gui(char *opts, int sleep);
+
+
+static Window tweak_tk_window_id(Window win);
+static int tray_manager_running(Display *d, Window *manager);
+static void run_gui(char *gui_xdisplay, int connect_to_x11vnc, int start_x11vnc,
+ int simple_gui, pid_t parent, char *gui_opts);
+
+
+char *get_gui_code(void) {
+ return gui_code;
+}
+
+static Window tweak_tk_window_id(Window win) {
+ char *name = NULL;
+ Window parent, new;
+
+ /* hack for tk, does not report outermost window */
+ new = win;
+ parent = parent_window(win, &name);
+ if (parent && name != NULL) {
+ lowercase(name);
+ if (strstr(name, "wish") || strstr(name, "x11vnc")) {
+ new = parent;
+ rfbLog("tray_embed: using parent: %s\n", name);
+ }
+ }
+ if (name != NULL) {
+ XFree(name);
+ }
+ return new;
+}
+
+int tray_embed(Window iconwin, int remove) {
+ XEvent ev;
+ XErrorHandler old_handler;
+ Window manager;
+ Atom xembed_info;
+ Atom tatom;
+ XWindowAttributes attr;
+ long info[2] = {XEMBED_VERSION, XEMBED_MAPPED};
+ long data = 0;
+
+ if (remove) {
+ if (!valid_window(iconwin, &attr, 1)) {
+ return 0;
+ }
+ iconwin = tweak_tk_window_id(iconwin);
+ trapped_xerror = 0;
+ old_handler = XSetErrorHandler(trap_xerror);
+
+ /*
+ * unfortunately no desktops seem to obey this
+ * part of the XEMBED spec yet...
+ */
+ XReparentWindow(dpy, iconwin, rootwin, 0, 0);
+
+ XSetErrorHandler(old_handler);
+ if (trapped_xerror) {
+ trapped_xerror = 0;
+ return 0;
+ }
+ trapped_xerror = 0;
+ return 1;
+ }
+
+ xembed_info = XInternAtom(dpy, "_XEMBED_INFO", False);
+ if (xembed_info == None) {
+ return 0;
+ }
+
+ if (!tray_manager_running(dpy, &manager)) {
+ return 0;
+ }
+
+ memset(&ev, 0, sizeof(ev));
+ ev.xclient.type = ClientMessage;
+ ev.xclient.window = manager;
+ ev.xclient.message_type = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE",
+ False);
+ ev.xclient.format = 32;
+ ev.xclient.data.l[0] = CurrentTime;
+ ev.xclient.data.l[1] = SYSTEM_TRAY_REQUEST_DOCK;
+ ev.xclient.data.l[2] = iconwin;
+ ev.xclient.data.l[3] = 0;
+ ev.xclient.data.l[4] = 0;
+
+ if (!valid_window(iconwin, &attr, 1)) {
+ return 0;
+ }
+
+ iconwin = tweak_tk_window_id(iconwin);
+ ev.xclient.data.l[2] = iconwin;
+
+ XUnmapWindow(dpy, iconwin);
+
+ trapped_xerror = 0;
+ old_handler = XSetErrorHandler(trap_xerror);
+
+ XSendEvent(dpy, manager, False, NoEventMask, &ev);
+ XSync(dpy, False);
+
+ if (trapped_xerror) {
+ XSetErrorHandler(old_handler);
+ trapped_xerror = 0;
+ return 0;
+ }
+
+ XChangeProperty(dpy, iconwin, xembed_info, xembed_info, 32,
+ PropModeReplace, (unsigned char *)&info, 2);
+
+ /* kludge for KDE evidently needed... */
+ tatom = XInternAtom(dpy, "KWM_DOCKWINDOW", False);
+ XChangeProperty(dpy, iconwin, tatom, tatom, 32, PropModeReplace,
+ (unsigned char *)&data, 1);
+ tatom = XInternAtom(dpy, "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR", False);
+ XChangeProperty(dpy, iconwin, tatom, XA_WINDOW, 32, PropModeReplace,
+ (unsigned char *)&data, 1);
+
+ XSetErrorHandler(old_handler);
+ trapped_xerror = 0;
+ return 1;
+}
+
+static int tray_manager_running(Display *d, Window *manager) {
+ char tray_string[100];
+ Atom tray_manager;
+ Window tray_win;
+
+ if (manager) {
+ *manager = None;
+ }
+ sprintf(tray_string, "_NET_SYSTEM_TRAY_S%d", scr);
+
+ tray_manager = XInternAtom(d, tray_string, True);
+ if (tray_manager == None) {
+ return 0;
+ }
+
+ tray_win = XGetSelectionOwner(d, tray_manager);
+ if (manager) {
+ *manager = tray_win;
+ }
+
+ if (tray_win == None) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+static char *gui_geometry = NULL;
+static int icon_in_tray = 0;
+static char *icon_mode_embed_id = NULL;
+static char *icon_mode_font = NULL;
+static char *icon_mode_params = NULL;
+
+static void run_gui(char *gui_xdisplay, int connect_to_x11vnc, int start_x11vnc,
+ int simple_gui, pid_t parent, char *gui_opts) {
+ char *x11vnc_xdisplay = NULL;
+ char extra_path[] = ":/usr/local/bin:/usr/bin/X11:/usr/sfw/bin"
+ ":/usr/X11R6/bin:/usr/openwin/bin:/usr/dt/bin";
+ char cmd[100];
+ char *wish = NULL, *orig_path, *full_path, *tpath, *p;
+ char *old_xauth = NULL;
+ int try_max = 4, sleep = 300;
+ pid_t mypid = getpid();
+ FILE *pipe, *tmpf;
+
+ if (*gui_code == '\0') {
+ rfbLog("gui: gui not compiled into this program.\n");
+ exit(0);
+ }
+ if (getenv("DISPLAY") != NULL) {
+ /* worst case */
+ x11vnc_xdisplay = strdup(getenv("DISPLAY"));
+ }
+ if (use_dpy) {
+ /* better */
+ x11vnc_xdisplay = strdup(use_dpy);
+ }
+ if (connect_to_x11vnc) {
+ int rc, i;
+ rfbLogEnable(1);
+ if (! client_connect_file) {
+ if (getenv("XAUTHORITY") != NULL) {
+ old_xauth = strdup(getenv("XAUTHORITY"));
+ } else {
+ old_xauth = strdup("");
+ }
+ dpy = XOpenDisplay(x11vnc_xdisplay);
+ if (! dpy && auth_file) {
+ set_env("XAUTHORITY", auth_file);
+ dpy = XOpenDisplay(x11vnc_xdisplay);
+ }
+ if (! dpy && ! x11vnc_xdisplay) {
+ /* worstest case */
+ x11vnc_xdisplay = strdup(":0");
+ dpy = XOpenDisplay(x11vnc_xdisplay);
+ }
+ if (! dpy) {
+ rfbLog("gui: could not open x11vnc "
+ "display: %s\n", NONUL(x11vnc_xdisplay));
+ exit(1);
+ }
+ scr = DefaultScreen(dpy);
+ rootwin = RootWindow(dpy, scr);
+ initialize_vnc_connect_prop();
+ }
+ usleep(2200*1000);
+ fprintf(stderr, "\n");
+ if (!quiet) {
+ rfbLog("gui: trying to contact a x11vnc server at X"
+ " display %s ...\n", NONUL(x11vnc_xdisplay));
+ }
+ for (i=0; i<try_max; i++) {
+ usleep(sleep*1000);
+ if (!quiet) {
+ rfbLog("gui: pinging %s try=%d ...\n",
+ NONUL(x11vnc_xdisplay), i+1);
+ }
+ rc = send_remote_cmd("qry=ping", 1, 1);
+ if (rc == 0) {
+ break;
+ }
+ if (parent && mypid != parent && kill(parent, 0) != 0) {
+ rfbLog("gui: parent process %d has gone"
+ " away: bailing out.\n", parent);
+ rc = 1;
+ break;
+ }
+ }
+ set_env("X11VNC_XDISPLAY", x11vnc_xdisplay);
+ if (getenv("XAUTHORITY") != NULL) {
+ set_env("X11VNC_AUTH_FILE", getenv("XAUTHORITY"));
+ }
+ if (rc == 0) {
+ rfbLog("gui: ping succeeded.\n");
+ set_env("X11VNC_CONNECT", "1");
+ } else {
+ rfbLog("gui: could not connect to: '%s', try"
+ " again manually.\n", x11vnc_xdisplay);
+ }
+ if (client_connect_file) {
+ set_env("X11VNC_CONNECT_FILE", client_connect_file);
+ }
+ if (dpy) {
+ XCloseDisplay(dpy);
+ dpy = NULL;
+ }
+ if (old_xauth) {
+ if (*old_xauth == '\0') {
+ /* wasn't set, hack it out if it is now */
+ char *xauth = getenv("XAUTHORITY");
+ if (xauth) {
+ *(xauth-2) = '_'; /* yow */
+ }
+ } else {
+ set_env("XAUTHORITY", old_xauth);
+ }
+ free(old_xauth);
+ }
+ rfbLogEnable(0);
+ }
+
+ orig_path = getenv("PATH");
+ if (! orig_path) {
+ orig_path = strdup("/bin:/usr/bin:/usr/bin/X11");
+ }
+ full_path = (char *) malloc(strlen(orig_path)+strlen(extra_path)+1);
+ strcpy(full_path, orig_path);
+ strcat(full_path, extra_path);
+
+ tpath = strdup(full_path);
+ p = strtok(tpath, ":");
+
+ while (p) {
+ char *try;
+ struct stat sbuf;
+ char *wishes[] = {"wish", "wish8.3", "wish8.4", "wish8.5",
+ "wish8.0"};
+ int nwishes = 3, i;
+
+ try = (char *) malloc(strlen(p) + 1 + strlen("wish8.4") + 1);
+ for (i=0; i<nwishes; i++) {
+ sprintf(try, "%s/%s", p, wishes[i]);
+ if (stat(try, &sbuf) == 0) {
+ /* assume executable, should check mode */
+ wish = wishes[i];
+ }
+ }
+ free(try);
+ if (wish) {
+ break;
+ }
+ p = strtok(NULL, ":");
+ }
+ free(tpath);
+ if (!wish) {
+ wish = strdup("wish");
+ }
+ set_env("PATH", full_path);
+ set_env("DISPLAY", gui_xdisplay);
+ set_env("X11VNC_PROG", program_name);
+ set_env("X11VNC_CMDLINE", program_cmdline);
+ set_env("X11VNC_WISHCMD", wish);
+ if (simple_gui) {
+ set_env("X11VNC_SIMPLE_GUI", "1");
+ }
+ if (gui_opts) {
+ set_env("X11VNC_GUI_PARAMS", gui_opts);
+ }
+ if (gui_geometry) {
+ set_env("X11VNC_GUI_GEOM", gui_geometry);
+ }
+ if (start_x11vnc) {
+ set_env("X11VNC_STARTED", "1");
+ }
+ if (icon_mode) {
+ set_env("X11VNC_ICON_MODE", "1");
+ if (icon_mode_file) {
+ set_env("X11VNC_CLIENT_FILE", icon_mode_file);
+ }
+ if (icon_in_tray) {
+ if (tray_manager_ok) {
+ set_env("X11VNC_ICON_MODE", "TRAY:RUNNING");
+ } else {
+ set_env("X11VNC_ICON_MODE", "TRAY");
+ }
+ } else {
+ set_env("X11VNC_ICON_MODE", "ICON");
+ }
+ if (icon_mode_params) {
+ char *p, *str = strdup(icon_mode_params);
+ p = strtok(str, ":-/,.+");
+ while (p) {
+ if(strstr(p, "setp") == p) {
+ set_env("X11VNC_ICON_SETPASS", "1");
+ } else if(strstr(p, "noadvanced") == p) {
+ set_env("X11VNC_ICON_NOADVANCED", "1");
+ } else if(strstr(p, "minimal") == p) {
+ set_env("X11VNC_ICON_MINIMAL", "1");
+ } else if (strstr(p, "0x") == p) {
+ set_env("X11VNC_ICON_EMBED_ID", p);
+ icon_mode_embed_id = strdup(p);
+ }
+ p = strtok(NULL, ":-/,.+");
+ }
+ free(str);
+ }
+ }
+ if (icon_mode_font) {
+ set_env("X11VNC_ICON_FONT", icon_mode_font);
+ }
+
+ if (no_external_cmds) {
+ fprintf(stderr, "cannot run external commands in -nocmds "
+ "mode:\n");
+ fprintf(stderr, " \"%s\"\n", "gui + wish");
+ fprintf(stderr, " exiting.\n");
+ fflush(stderr);
+ exit(1);
+ }
+
+ tmpf = tmpfile();
+ if (tmpf == NULL) {
+ /* if no tmpfile, use a pipe */
+ if (icon_mode_embed_id) {
+ if (strlen(icon_mode_embed_id) < 20) {
+ strcat(cmd, " -use ");
+ strcat(cmd, icon_mode_embed_id);
+ }
+ }
+ pipe = popen(cmd, "w");
+ if (! pipe) {
+ fprintf(stderr, "could not run: %s\n", cmd);
+ perror("popen");
+ }
+ fprintf(pipe, "%s", gui_code);
+ pclose(pipe);
+ } else {
+ /*
+ * we prefer a tmpfile since then this x11vnc process
+ * will then be gone, otherwise the x11vnc program text
+ * will still be in use.
+ */
+ int n = fileno(tmpf);
+ fprintf(tmpf, "%s", gui_code);
+ fflush(tmpf);
+ rewind(tmpf);
+ dup2(n, 0);
+ close(n);
+ if (icon_mode_embed_id) {
+ execlp(wish, wish, "-", "-use", icon_mode_embed_id,
+ (char *) NULL);
+ } else {
+ execlp(wish, wish, "-", (char *) NULL);
+ }
+ fprintf(stderr, "could not exec wish: %s -\n", wish);
+ perror("execlp");
+ }
+ exit(0);
+}
+
+void do_gui(char *opts, int sleep) {
+ char *s, *p;
+ char *old_xauth = NULL;
+ char *gui_xdisplay = NULL;
+ int got_gui_xdisplay = 0;
+ int start_x11vnc = 1;
+ int connect_to_x11vnc = 0;
+ int simple_gui = 0, none_gui = 0;
+ Display *test_dpy;
+
+ if (opts) {
+ s = strdup(opts);
+ } else {
+ s = strdup("");
+ }
+
+ if (use_dpy) {
+ /* worst case */
+ gui_xdisplay = strdup(use_dpy);
+
+ }
+ if (getenv("DISPLAY") != NULL) {
+ /* better */
+ gui_xdisplay = strdup(getenv("DISPLAY"));
+ }
+
+ p = strtok(s, ",");
+
+ while(p) {
+ if (*p == '\0') {
+ ;
+ } else if (strchr(p, ':') != NULL) {
+ /* best */
+ if (gui_xdisplay) {
+ free(gui_xdisplay);
+ }
+ gui_xdisplay = strdup(p);
+ got_gui_xdisplay = 1;
+ } else if (!strcmp(p, "wait")) {
+ start_x11vnc = 0;
+ connect_to_x11vnc = 0;
+ } else if (!strcmp(p, "none")) {
+ none_gui = 1;
+ } else if (!strcmp(p, "conn") || !strcmp(p, "connect")) {
+ start_x11vnc = 0;
+ connect_to_x11vnc = 1;
+ } else if (!strcmp(p, "ez") || !strcmp(p, "simple")) {
+ simple_gui = 1;
+ } else if (strstr(p, "iconfont") == p) {
+ char *q;
+ if ((q = strchr(p, '=')) != NULL) {
+ icon_mode_font = strdup(q+1);
+ }
+ } else if (!strcmp(p, "full")) {
+ ;
+ } else if (strstr(p, "tray") == p || strstr(p, "icon") == p) {
+ char *q;
+ icon_mode = 1;
+ if ((q = strchr(p, '=')) != NULL) {
+ icon_mode_params = strdup(q+1);
+ if (strstr(icon_mode_params, "setp")) {
+ deny_all = 1;
+ }
+ }
+ if (strstr(p, "tray") == p) {
+ icon_in_tray = 1;
+ }
+ } else if (strstr(p, "geom") == p) {
+ char *q;
+ if ((q = strchr(p, '=')) != NULL) {
+ gui_geometry = strdup(q+1);
+ }
+ } else {
+ fprintf(stderr, "unrecognized gui opt: %s\n", p);
+ }
+
+ p = strtok(NULL, ",");
+ }
+ free(s);
+
+ if (none_gui) {
+ if (!start_x11vnc) {
+ exit(0);
+ }
+ return;
+ }
+ if (start_x11vnc) {
+ connect_to_x11vnc = 1;
+ }
+
+ if (icon_mode && !got_gui_xdisplay) {
+ /* for tray mode, prefer the polled DISPLAY */
+ if (use_dpy) {
+ if (gui_xdisplay) {
+ free(gui_xdisplay);
+ }
+ gui_xdisplay = strdup(use_dpy);
+ }
+ }
+
+ if (! gui_xdisplay) {
+ fprintf(stderr, "error: cannot determine X DISPLAY for gui"
+ " to display on.\n");
+ exit(1);
+ }
+ if (!quiet) {
+ fprintf(stderr, "starting gui, trying display: %s\n",
+ gui_xdisplay);
+ }
+ test_dpy = XOpenDisplay(gui_xdisplay);
+ if (! test_dpy && auth_file) {
+ if (getenv("XAUTHORITY") != NULL) {
+ old_xauth = strdup(getenv("XAUTHORITY"));
+ }
+ set_env("XAUTHORITY", auth_file);
+ test_dpy = XOpenDisplay(gui_xdisplay);
+ }
+ if (! test_dpy) {
+ if (! old_xauth && getenv("XAUTHORITY") != NULL) {
+ old_xauth = strdup(getenv("XAUTHORITY"));
+ }
+ set_env("XAUTHORITY", "");
+ test_dpy = XOpenDisplay(gui_xdisplay);
+ }
+ if (! test_dpy) {
+ fprintf(stderr, "error: cannot connect to gui X DISPLAY: %s\n",
+ gui_xdisplay);
+ exit(1);
+ }
+ if (icon_mode && icon_in_tray) {
+ if (tray_manager_running(test_dpy, NULL)) {
+ tray_manager_ok = 1;
+ } else {
+ tray_manager_ok = 0;
+ }
+ }
+ XCloseDisplay(test_dpy);
+
+ if (start_x11vnc) {
+
+#if LIBVNCSERVER_HAVE_FORK
+ /* fork into the background now */
+ int p;
+ pid_t parent = getpid();
+
+ if (icon_mode) {
+ char tf[100];
+ double dn = dnow();
+ struct stat sbuf;
+ /* FIXME */
+ dn = dn - ((int) dn);
+ sprintf(tf, "/tmp/x11vnc.tray%d%d", (int) (1000000*dn),
+ (int) getpid());
+ unlink(tf);
+ /* race begins.. */
+ if (stat(tf, &sbuf) == 0) {
+ icon_mode = 0;
+ } else {
+ icon_mode_fh = fopen(tf, "w");
+ if (! icon_mode_fh) {
+ icon_mode = 0;
+ } else {
+ chmod(tf, 0400);
+ icon_mode_file = strdup(tf);
+ fprintf(icon_mode_fh, "none\n");
+ fprintf(icon_mode_fh, "none\n");
+ fflush(icon_mode_fh);
+ if (! got_connect_once) {
+ /* want -forever for tray */
+ connect_once = 0;
+ }
+ }
+ }
+ }
+
+ if ((p = fork()) > 0) {
+ ; /* parent */
+ } else if (p == -1) {
+ fprintf(stderr, "could not fork\n");
+ perror("fork");
+ clean_up_exit(1);
+ } else {
+ if (sleep > 0) {
+ usleep(sleep * 1000 * 1000);
+ }
+ run_gui(gui_xdisplay, connect_to_x11vnc, start_x11vnc,
+ simple_gui, parent, opts);
+ exit(1);
+ }
+#else
+ fprintf(stderr, "system does not support fork: start "
+ "x11vnc in the gui.\n");
+ start_x11vnc = 0;
+#endif
+ }
+ if (!start_x11vnc) {
+ run_gui(gui_xdisplay, connect_to_x11vnc, start_x11vnc,
+ simple_gui, 0, opts);
+ exit(1);
+ }
+ if (old_xauth) {
+ set_env("XAUTHORITY", old_xauth);
+ }
+}
+
+