/* Copyright (C) 2002-2009 Karl J. Runge 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 . 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. */ /* -- gui.c -- */ #include "x11vnc.h" #include "xevents.h" #include "win_utils.h" #include "remote.h" #include "cleanup.h" #include "xwrappers.h" #include "connections.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/icon */ 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; pid_t run_gui_pid = 0; pid_t gui_pid = 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) { #if NO_X11 if (!win) {} return None; #else char *name = NULL; Window parent, new; if (getenv("NO_TWEAK_TK_WINDOW_ID")) { return win; } /* 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_wr(name); } return new; #endif /* NO_X11 */ } int tray_embed(Window iconwin, int remove) { #if NO_X11 RAWFB_RET(0) if (!iconwin || !remove) {} return 0; #else XEvent ev; XErrorHandler old_handler; Window manager; Atom xembed_info; Atom tatom; XWindowAttributes attr; long info[2] = {XEMBED_VERSION, XEMBED_MAPPED}; long data = 0; RAWFB_RET(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); #if 0 { XSizeHints *xszh = XAllocSizeHints(); xszh->flags = PMinSize; xszh->min_width = 24; xszh->min_height = 24; XSetWMNormalHints(dpy, iconwin, xszh); } #endif /* 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; #endif /* NO_X11 */ } static int tray_manager_running(Display *d, Window *manager) { #if NO_X11 RAWFB_RET(0) if (!d || !manager) {} return 0; #else char tray_string[100]; Atom tray_manager; Window tray_win; RAWFB_RET(0) 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; } #endif /* NO_X11 */ } 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 int got_sigusr1 = 0; static void sigusr1 (int sig) { got_sigusr1 = 1; if (0) sig = 0; } /* Most of the following mess is for wish on Solaris: */ static char *extra_path = ":/usr/local/bin:/usr/bin/X11:/usr/sfw/bin" ":/usr/X11R6/bin:/usr/openwin/bin:/usr/dt/bin:/opt/sfw/bin"; static char *wishes[] = {"wish8.4", "wish", "wish8.3", "wish8.5", "wish8.6", "wish8.7", "wishx", "wish8.0", 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 cmd[100]; char *wish = NULL, *orig_path, *full_path, *tpath, *p; char *old_xauth = NULL; int try_max = 4, sleep = 300, totms, rc = 0; pid_t mypid = getpid(); FILE *pipe, *tmpf; if (0) fprintf(stderr, "run_gui: %s -- %d %d\n", gui_xdisplay, connect_to_x11vnc, (int) parent); 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 i; rfbLogEnable(1); if (! client_connect_file) { if (getenv("XAUTHORITY") != NULL) { old_xauth = strdup(getenv("XAUTHORITY")); } else { old_xauth = strdup(""); } dpy = XOpenDisplay_wr(x11vnc_xdisplay); if (! dpy && auth_file) { set_env("XAUTHORITY", auth_file); dpy = XOpenDisplay_wr(x11vnc_xdisplay); } if (! dpy && ! x11vnc_xdisplay) { /* worstest case */ x11vnc_xdisplay = strdup(":0"); dpy = XOpenDisplay_wr(x11vnc_xdisplay); } if (! dpy) { rfbLog("gui: could not open x11vnc " "display: %s\n", NONUL(x11vnc_xdisplay)); #ifdef MACOSX goto macjump; #endif exit(1); } scr = DefaultScreen(dpy); rootwin = RootWindow(dpy, scr); initialize_vnc_connect_prop(); initialize_x11vnc_remote_prop(); } #ifdef MACOSX macjump: #endif signal(SIGUSR1, sigusr1); got_sigusr1 = 0; totms = 0; while (totms < 3500) { usleep(50*1000); totms += 50; if (got_sigusr1) { fprintf(stderr, "\n"); if (! quiet) rfbLog("gui: got SIGUSR1\n"); break; } if (! start_x11vnc && totms >= 150) { break; } } signal(SIGUSR1, SIG_DFL); if (! got_sigusr1) fprintf(stderr, "\n"); if (!quiet && ! got_sigusr1) { rfbLog("gui: trying to contact a x11vnc server at X" " display %s ...\n", NONUL(x11vnc_xdisplay)); } for (i=0; i %s", p1, wish, tf1, tf2); } else { sprintf(cmd, "env PATH='%s' DISPLAY='%s' %s %s -name x11vnc_port_prompt -portprompt > %s", p1, dstr, wish, tf1, tf2); } if (getenv("X11VNC_DEBUG_PORTPROMPT")) { fprintf(stderr, "cmd=%s\n", cmd); } if (use_openssl) { set_env("X11VNC_SSL_ENABLED", "1"); } if (allow_list && !strcmp(allow_list, "127.0.0.1")) { set_env("X11VNC_LOCALHOST_ENABLED", "1"); } if (got_ultrafilexfer) { set_env("X11VNC_FILETRANSFER_ENABLED", "ultra"); } else if (tightfilexfer) { set_env("X11VNC_FILETRANSFER_ENABLED", "tight"); } system(cmd); free(cmd); free(p1); fp = fopen(tf2, "r"); memset(line, 0, sizeof(line)); if (fp) { fgets(line, 128, fp); fclose(fp); if (line[0] != '\0') { int readport = atoi(line); if (readport > 0) { got_rfbport_val = readport; } } } if (strstr(line, "ssl0")) { if (use_openssl) use_openssl = 0; } else if (strstr(line, "ssl1")) { if (!use_openssl) { use_openssl = 1; openssl_pem = strdup("SAVE_NOPROMPT"); set_env("X11VNC_GOT_SSL", "1"); } } if (strstr(line, "localhost0")) { if (allow_list && !strcmp(allow_list, "127.0.0.1")) { allow_list = NULL; } } else if (strstr(line, "localhost1")) { allow_list = strdup("127.0.0.1"); } if (strstr(line, "ft_ultra")) { got_ultrafilexfer = 1; tightfilexfer = 0; } else if (strstr(line, "ft_tight")) { got_ultrafilexfer = 0; tightfilexfer = 1; } else if (strstr(line, "ft_none")) { got_ultrafilexfer = 0; tightfilexfer = 0; } unlink(tf1); unlink(tf2); if (old_xauth) { set_env("XAUTHORITY", old_xauth); } return; } if (start_x11vnc) { #if LIBVNCSERVER_HAVE_FORK /* fork into the background now */ int p; pid_t parent = getpid(); if (icon_mode) { char tf[] = "/tmp/x11vnc.tray.XXXXXX"; int fd; fd = mkstemp(tf); if (fd < 0) { icon_mode = 0; } else { close(fd); icon_mode_fh = fopen(tf, "w"); if (! icon_mode_fh) { icon_mode = 0; } else { chmod(tf, 0400); icon_mode_file = strdup(tf); rfbLog("icon_mode_file=%s\n", icon_mode_file); fprintf(icon_mode_fh, "none\n"); fprintf(icon_mode_fh, "none\n"); fflush(icon_mode_fh); if (! got_connect_once) { if (!client_connect && !connect_or_exit) { /* 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); } if (connect_to_x11vnc) { run_gui_pid = p; gui_pid = p; } #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); } }