/*
   Copyright (C) 2002-2010 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.
*/

/* -- solid.c -- */

#include "x11vnc.h"
#include "win_utils.h"
#include "xwrappers.h"
#include "connections.h"
#include "cleanup.h"
#include "xevents.h"

char *guess_desktop(void);
void solid_bg(int restore);
char *dbus_session(void);


static void usr_bin_path(int restore);
static int dt_cmd(char *cmd);
static char *cmd_output(char *cmd);
XImage *solid_root(char *color);
static void solid_cde(char *color);
static void solid_gnome(char *color);
static void solid_kde(char *color);
static void solid_macosx(int restore);

static void usr_bin_path(int restore) {
	static char *oldpath = NULL;
	char *newpath;
	char addpath[] = "/usr/bin:/bin:";

	if (restore) {
		if (oldpath) {
			set_env("PATH", oldpath);
			free(oldpath);
			oldpath = NULL;
		}
		return;
	}

	if (getenv("PATH")) {
		oldpath = strdup(getenv("PATH"));
	} else {
		oldpath = strdup("/usr/bin");
	}
	newpath = (char *) malloc(strlen(oldpath) + strlen(addpath) + 1);
	newpath[0] = '\0';
	strcat(newpath, addpath);
	strcat(newpath, oldpath);
	set_env("PATH", newpath);
	free(newpath);
}

static int dt_cmd(char *cmd) {
	int rc;

	RAWFB_RET(0)

	if (!cmd || *cmd == '\0') {
		return 0;
	}

	/* dt */
	if (no_external_cmds || !cmd_ok("dt")) {
		rfbLog("cannot run external commands in -nocmds mode:\n");
		rfbLog("   \"%s\"\n", cmd);
		rfbLog("   dt_cmd: returning 1\n");
		return 1;
	}

	if (getenv("DISPLAY") == NULL) {
		set_env("DISPLAY", DisplayString(dpy));
	}

	rfbLog("running command:\n");
	if (!quiet) {
		fprintf(stderr, "\n  %s\n\n", cmd);
	}
	usr_bin_path(0);
	close_exec_fds();
	rc = system(cmd);
	usr_bin_path(1);

	if (rc >= 256) {
		rc = rc/256;
	}
	return rc;
}

static char *cmd_output(char *cmd) {
	FILE *p;
	static char output[50000];
	char line[1024];
	int rc;

	if (!cmd || *cmd == '\0') {
		return "";
	}

	if (no_external_cmds) {
		rfbLog("cannot run external commands in -nocmds mode:\n");
		rfbLog("   \"%s\"\n", cmd);
		rfbLog("   cmd_output: null string.\n");
		return "";
	}

	rfbLog("running pipe:\n");
	if (!quiet) {
		fprintf(stderr, "\n  %s\n\n", cmd);
	}
	usr_bin_path(0);
	close_exec_fds();
	p = popen(cmd, "r");
	usr_bin_path(1);

	output[0] = '\0';

	while (fgets(line, 1024, p) != NULL) {
		if (strlen(output) + strlen(line) + 1 < 50000) {
			strcat(output, line);
		}
	}
	rc = pclose(p);
	return(output);
}

static char *last_color = NULL;

unsigned long get_pixel(char *color) {
#if NO_X11
	return 0;
#else
	XColor cdef;
	Colormap cmap;
	unsigned long pixel = BlackPixel(dpy, scr);
	if (depth > 8 || strcmp(color, solid_default)) {
		cmap = DefaultColormap (dpy, scr);
		if (XParseColor(dpy, cmap, color, &cdef) &&
		    XAllocColor(dpy, cmap, &cdef)) {
			pixel = cdef.pixel;
		} else {
			rfbLog("error parsing/allocing color: %s\n", color);
		}
	}
	return pixel;
#endif
}

XImage *solid_root(char *color) {
#if NO_X11
	RAWFB_RET_VOID
	if (!color) {}
	return NULL;
#else
	Window expose;
	static XImage *image = NULL;
	Pixmap pixmap;
	XGCValues gcv;
	GC gc;
	XSetWindowAttributes swa;
	Visual visual;
	static unsigned long mask, pixel = 0;

	RAWFB_RET(NULL)

	if (subwin || window != rootwin) {
		rfbLog("cannot set subwin to solid color, must be rootwin\n");
		return NULL;
	}

	/* create the "clear" window just for generating exposures */
	swa.override_redirect = True;
	swa.backing_store = NotUseful;
	swa.save_under = False;
	swa.background_pixmap = None;
	visual.visualid = CopyFromParent;
	mask = (CWOverrideRedirect|CWBackingStore|CWSaveUnder|CWBackPixmap);
	expose = XCreateWindow(dpy, window, 0, 0, wdpy_x, wdpy_y, 0, depth,
	    InputOutput, &visual, mask, &swa);

	if (! color) {

		if (! image) {
			/* whoops */
			XDestroyWindow(dpy, expose);
			rfbLog("no root snapshot available.\n");
			return NULL;
		}

		/* restore the root window from the XImage snapshot */
		pixmap = XCreatePixmap(dpy, window, wdpy_x, wdpy_y, depth);
		
		/* draw the image to a pixmap: */
		gcv.function = GXcopy;
		gcv.plane_mask = AllPlanes;
		gc = XCreateGC(dpy, window, GCFunction|GCPlaneMask, &gcv);

		XPutImage(dpy, pixmap, gc, image, 0, 0, 0, 0, wdpy_x, wdpy_y);

		gcv.foreground = gcv.background = BlackPixel(dpy, scr);
		gc = XCreateGC(dpy, window, GCForeground|GCBackground, &gcv);

		rfbLog("restoring root snapshot...\n");
		/* set the pixmap as the bg: */
		XSetWindowBackgroundPixmap(dpy, window, pixmap);
		XFreePixmap(dpy, pixmap);
		XClearWindow(dpy, window);
		XFlush_wr(dpy);
		
		/* generate exposures */
		XMapWindow(dpy, expose);
		XSync(dpy, False);
		XDestroyWindow(dpy, expose);
		return NULL;
	}

	if (! image) {
		/* need to retrieve a snapshot of the root background: */
		Window iwin;
		XSetWindowAttributes iswa;

		/* create image window: */
		iswa.override_redirect = True;
		iswa.backing_store = NotUseful;
		iswa.save_under = False;
		iswa.background_pixmap = ParentRelative;

		iwin = XCreateWindow(dpy, window, 0, 0, wdpy_x, wdpy_y, 0,
		    depth, InputOutput, &visual, mask, &iswa);

		rfbLog("snapshotting background...\n");

		XMapWindow(dpy, iwin);
		XSync(dpy, False);
		image = XGetImage(dpy, iwin, 0, 0, wdpy_x, wdpy_y, AllPlanes,
		    ZPixmap);
		XSync(dpy, False);
		XDestroyWindow(dpy, iwin);
		rfbLog("done.\n");
	}
	if (color == (char *) 0x1) {
		/* caller will XDestroyImage it: */
		XImage *xi = image;
		image = NULL;
		return xi;
	}

	/* use black for low colors or failure */
	pixel = get_pixel(color);

	rfbLog("setting solid background...\n");
	XSetWindowBackground(dpy, window, pixel);
	XMapWindow(dpy, expose);
	XSync(dpy, False);
	XDestroyWindow(dpy, expose);
#endif	/* NO_X11 */
	return NULL;
}

static void solid_cde(char *color) {
#if NO_X11
	RAWFB_RET_VOID
	if (!color) {}
	return;
#else
	int wsmax = 16;
	static XImage *image[16];
	static Window ws_wins[16];
	static int nws = -1;

	Window expose;
	Pixmap pixmap;
	XGCValues gcv;
	GC gc;
	XSetWindowAttributes swa;
	Visual visual;
	unsigned long mask, pixel;
	XColor cdef;
	Colormap cmap;
	int n;

	RAWFB_RET_VOID

	if (subwin || window != rootwin) {
		rfbLog("cannot set subwin to solid color, must be rootwin\n");
		return;
	}

	/* create the "clear" window just for generating exposures */
	swa.override_redirect = True;
	swa.backing_store = NotUseful;
	swa.save_under = False;
	swa.background_pixmap = None;
	visual.visualid = CopyFromParent;
	mask = (CWOverrideRedirect|CWBackingStore|CWSaveUnder|CWBackPixmap);
	expose = XCreateWindow(dpy, window, 0, 0, wdpy_x, wdpy_y, 0, depth,
	    InputOutput, &visual, mask, &swa);

	if (! color) {
		/* restore the backdrop windows from the XImage snapshots */

		for (n=0; n < nws; n++) {
			Window twin;

			if (! image[n]) {
				continue;
			}

			twin = ws_wins[n];
			if (! twin) {
				twin = rootwin;
			}
			if (! valid_window(twin, NULL, 0)) {
				continue;
			}

			pixmap = XCreatePixmap(dpy, twin, wdpy_x, wdpy_y,
			    depth);
			
			/* draw the image to a pixmap: */
			gcv.function = GXcopy;
			gcv.plane_mask = AllPlanes;
			gc = XCreateGC(dpy, twin, GCFunction|GCPlaneMask, &gcv);

			XPutImage(dpy, pixmap, gc, image[n], 0, 0, 0, 0,
			    wdpy_x, wdpy_y);

			gcv.foreground = gcv.background = BlackPixel(dpy, scr);
			gc = XCreateGC(dpy, twin, GCForeground|GCBackground,
			    &gcv);

			rfbLog("restoring CDE ws%d snapshot to 0x%lx\n",
			    n, twin);
			/* set the pixmap as the bg: */
			XSetWindowBackgroundPixmap(dpy, twin, pixmap);
			XFreePixmap(dpy, pixmap);
			XClearWindow(dpy, twin);
			XFlush_wr(dpy);
		}
		
		/* generate exposures */
		XMapWindow(dpy, expose);
		XSync(dpy, False);
		XDestroyWindow(dpy, expose);
		return;
	}

	if (nws < 0) {
		/* need to retrieve snapshots of the ws backgrounds: */
		Window iwin, wm_win;
		XSetWindowAttributes iswa;
		Atom dt_list, wm_info, type;
		int format;
		unsigned long length, after;
		unsigned char *data;
		unsigned long *dp;	/* crash on 64bit with int */

		nws = 0;

		/* extract the hidden wm properties about backdrops: */

		wm_info = XInternAtom(dpy, "_MOTIF_WM_INFO", True);
		if (wm_info == None) {
			return;
		}

		XGetWindowProperty(dpy, rootwin, wm_info, 0L, 10L, False,
		    AnyPropertyType, &type, &format, &length, &after, &data);

		/*
		 * xprop -notype -root _MOTIF_WM_INFO
		 * _MOTIF_WM_INFO = 0x2, 0x580028
		 */

		if (length < 2 || format != 32 || after != 0) {
			return;
		}

		dp = (unsigned long *) data;
		wm_win = (Window) *(dp+1);	/* 2nd item. */


		dt_list = XInternAtom(dpy, "_DT_WORKSPACE_LIST", True);
		if (dt_list == None) {
			return;
		}

		XGetWindowProperty(dpy, wm_win, dt_list, 0L, 10L, False,
		   AnyPropertyType, &type, &format, &length, &after, &data);

		nws = length;

		if (nws > wsmax) {
			nws = wsmax;
		}
		if (nws < 0) {
			nws = 0;
		}

		rfbLog("special CDE win: 0x%lx, %d workspaces\n", wm_win, nws);
		if (nws == 0) {
			return;
		}

		for (n=0; n<nws; n++) {
			Atom ws_atom;
			char tmp[32];
			Window twin;
			XWindowAttributes attr;
			int i, cnt;

			image[n] = NULL;
			ws_wins[n] = 0x0;

			sprintf(tmp, "_DT_WORKSPACE_INFO_ws%d", n);
			ws_atom = XInternAtom(dpy, tmp, False);
			if (ws_atom == None) {
				continue;
			}
			XGetWindowProperty(dpy, wm_win, ws_atom, 0L, 100L,
			   False, AnyPropertyType, &type, &format, &length,
			   &after, &data);

			if (format != 8 || after != 0) {
				continue;
			}
			/*
			 * xprop -notype -id wm_win
			 * _DT_WORKSPACE_INFO_ws0 = "One", "3", "0x2f2f4a",
			 * "0x63639c", "0x103", "1", "0x58044e"
			 */

			cnt = 0;
			twin = 0x0;
			for (i=0; i< (int) length; i++) {
				if (*(data+i) != '\0') {
					continue;
				}
				cnt++;	/* count nulls to indicate field */
				if (cnt == 6) {
					/* one past the null: */
					char *q = (char *) (data+i+1);
					unsigned long in;
					if (sscanf(q, "0x%lx", &in) == 1) {
						twin = (Window) in;
						break;
					}
				}
			}
			ws_wins[n] = twin;

			if (! twin) {
				twin = rootwin;
			}

			XGetWindowAttributes(dpy, twin, &attr);
			if (twin != rootwin) {
				if (attr.map_state != IsViewable) {
					XMapWindow(dpy, twin);
				}
				XRaiseWindow(dpy, twin);
			}
			XSync(dpy, False);
		
			/* create image window: */
			iswa.override_redirect = True;
			iswa.backing_store = NotUseful;
			iswa.save_under = False;
			iswa.background_pixmap = ParentRelative;
			visual.visualid = CopyFromParent;

			iwin = XCreateWindow(dpy, twin, 0, 0, wdpy_x, wdpy_y,
			    0, depth, InputOutput, &visual, mask, &iswa);

			rfbLog("snapshotting CDE backdrop ws%d 0x%lx -> "
			    "0x%lx ...\n", n, twin, iwin);
			XMapWindow(dpy, iwin);
			XSync(dpy, False);

			image[n] = XGetImage(dpy, iwin, 0, 0, wdpy_x, wdpy_y,
			    AllPlanes, ZPixmap);
			XSync(dpy, False);
			XDestroyWindow(dpy, iwin);
			if (twin != rootwin) {
				XLowerWindow(dpy, twin);
				if (attr.map_state != IsViewable) {
					XUnmapWindow(dpy, twin);
				}
			}
		}
	}
	if (nws == 0) {
		return;
	}

	/* use black for low colors or failure */
	pixel = BlackPixel(dpy, scr);
	if (depth > 8 || strcmp(color, solid_default)) {
		cmap = DefaultColormap (dpy, scr);
		if (XParseColor(dpy, cmap, color, &cdef) &&
		    XAllocColor(dpy, cmap, &cdef)) {
			pixel = cdef.pixel;
		} else {
			rfbLog("error parsing/allocing color: %s\n", color);
		}
	}

	rfbLog("setting solid backgrounds...\n");

	for (n=0; n < nws; n++)  {
		Window twin = ws_wins[n];
		if (image[n] == NULL) {
			continue;
		}
		if (! twin)  {
			twin = rootwin;
		}
		XSetWindowBackground(dpy, twin, pixel);
	}
	XMapWindow(dpy, expose);
	XSync(dpy, False);
	XDestroyWindow(dpy, expose);
#endif	/* NO_X11 */
}

static char _dbus_str[1100];

char *dbus_session(void) {
	char *dbus_env = getenv("DBUS_SESSION_BUS_ADDRESS"); 
	char tmp[1000];

	if (dbus_env != NULL && strlen(dbus_env) > 0) {
		return "";
	}
	if (!dpy) {
		return "";
	}
#if NO_X11
	return "";
#else
	{
		Atom dbus_prop, dbus_pid;
		Window r, w, *children;
		int sbest = -1;
		unsigned int ui;
		int rc, i;

		memset(_dbus_str, 0, sizeof(_dbus_str));

		X_LOCK;
		dbus_prop = XInternAtom(dpy, "_DBUS_SESSION_BUS_ADDRESS", True);
		dbus_pid  = XInternAtom(dpy, "_DBUS_SESSION_BUS_PID", True);
		X_UNLOCK;
		if (dbus_prop == None) {
			return "";
		}

		X_LOCK;
		memset(tmp, 0, sizeof(tmp));
		get_prop(tmp, sizeof(tmp)-1, dbus_prop, None);
		X_UNLOCK;
		if (strcmp(tmp, "")) {
			if (!strchr(tmp, '\'')) {
				sprintf(_dbus_str, "env DBUS_SESSION_BUS_ADDRESS='%s'", tmp);
				return _dbus_str;
			}
		}

		X_LOCK;
		rc = XQueryTree_wr(dpy, rootwin, &r, &w, &children, &ui);
		X_UNLOCK;
		if (!rc || children == NULL || ui == 0) {
			return "";
		}
		for (i=0; i < (int) ui; i++) {
			int pid = -1;
	
			X_LOCK;
			memset(tmp, 0, sizeof(tmp));
			get_prop(tmp, sizeof(tmp)-1, dbus_prop, children[i]);
			if (dbus_pid != None) {
				Atom atype;
				int aformat;
				unsigned long nitems, bafter;
				unsigned char *prop;
				if (XGetWindowProperty(dpy, children[i], dbus_pid,
				    0, 1, False, XA_CARDINAL, &atype, &aformat,
				    &nitems, &bafter, &prop) == Success
				    && atype == XA_CARDINAL) {
					pid = *((int *) prop);
					XFree_wr(prop);
				}
			}
			X_UNLOCK;

			if (strcmp(tmp, "")  && !strchr(tmp, '\'')) {
				int score = 0;
				if (1 < pid && pid < 10000000) {
					struct stat sb;
					char procfile[32];

					sprintf(procfile, "/proc/%d", pid);
					if (stat(procfile, &sb) == 0) {
						score += 10000000;
					}
					score += pid;
				}
				if (getenv("X11VNC_DBUS_DEBUG")) fprintf(stderr, "win: 0x%lx  pid: %8d  score: %8d  str: %s\n", children[i], pid, score, tmp);
				if (score > sbest) {
					sprintf(_dbus_str, "env DBUS_SESSION_BUS_ADDRESS='%s'", tmp);
					sbest = score;
				}
			}
		}
		X_LOCK;
		XFree_wr(children);
		X_UNLOCK;

		return _dbus_str;
	}
#endif
}

static void solid_gnome(char *color) {
#if NO_X11
	RAWFB_RET_VOID
	if (!color) {}
	return;
#else
	char get_color[] = "%s gconftool-2 --get "
	    "/desktop/gnome/background/primary_color";
	char set_color[] = "%s gconftool-2 --set --type string "
	    "/desktop/gnome/background/primary_color '%s'";
	char get_option[] = "%s gconftool-2 --get "
	    "/desktop/gnome/background/picture_options";
	char set_option[] = "%s gconftool-2 --set --type string "
	    "/desktop/gnome/background/picture_options '%s'";
#if 0
	char get_shading[] = "%s gconftool-2 --get "
	    "/desktop/gnome/background/color_shading_type";
	char set_shading[] = "%s gconftool-2 --set --type string "
	    "/desktop/gnome/background/color_shading_type '%s'";
	char get_filename[] = "%s gconftool-2 --get "
	    "/desktop/gnome/background/picture_filename";
	char set_filename[] = "%s gconftool-2 --set --type string "
	    "/desktop/gnome/background/picture_filename '%s'";
#endif
	static char *orig_color = NULL;
	static char *orig_option = NULL;
	char *cmd, *dbus = "";

	RAWFB_RET_VOID

	dbus = dbus_session();
	rfbLog("guessed dbus: %s\n", dbus);
	
	if (! color) {
		if (! orig_color) {
			orig_color = strdup("#FFFFFF");
		}
		if (! orig_option) {
			orig_option = strdup("stretched");
		}
		if (strstr(orig_color, "'") != NULL)  {
			rfbLog("invalid color: %s\n", orig_color);
			return;
		}
		if (strstr(orig_option, "'") != NULL)  {
			rfbLog("invalid option: %s\n", orig_option);
			return;
		}
		cmd = (char *) malloc(strlen(set_option) - 2 + strlen(orig_option) + strlen(dbus) + 1);
		sprintf(cmd, set_option, dbus, orig_option);
		dt_cmd(cmd);
		free(cmd);
		cmd = (char *) malloc(strlen(set_color) - 2 + strlen(orig_color) + strlen(dbus) + 1);
		sprintf(cmd, set_color, dbus, orig_color);
		dt_cmd(cmd);
		free(cmd);
		return;
	}

	if (! orig_color) {
		char *q;
		if (cmd_ok("dt")) {
			cmd = (char *) malloc(strlen(get_color) + strlen(dbus) + 1);
			sprintf(cmd, get_color, dbus);
			orig_color = strdup(cmd_output(cmd));
			free(cmd);
		} else {
			orig_color = "";
		}
		if (*orig_color == '\0') {
			orig_color = strdup("#FFFFFF");
		}
		if ((q = strchr(orig_color, '\n')) != NULL) {
			*q = '\0';
		}
	}
	if (! orig_option) {
		char *q;
		if (cmd_ok("dt")) {
			cmd = (char *) malloc(strlen(get_option) + strlen(dbus) + 1);
			sprintf(cmd, get_option, dbus);
			orig_option = strdup(cmd_output(cmd));
			free(cmd);
		} else {
			orig_color = "";
		}
		if (*orig_option == '\0') {
			orig_option = strdup("stretched");
		}
		if ((q = strchr(orig_option, '\n')) != NULL) {
			*q = '\0';
		}
	}
	if (strstr(color, "'") != NULL)  {
		rfbLog("invalid color: %s\n", color);
		return;
	}
	cmd = (char *) malloc(strlen(set_color) + strlen(color) + strlen(dbus) + 1);
	sprintf(cmd, set_color, dbus, color);
	dt_cmd(cmd);
	free(cmd);

	cmd = (char *) malloc(strlen(set_option) + strlen("none") + strlen(dbus) + 1);
	sprintf(cmd, set_option, dbus, "none");
	dt_cmd(cmd);
	free(cmd);

#if 0
	cmd = (char *) malloc(strlen(set_filename) + strlen("none") + 1);
	sprintf(cmd, set_filename, dbus, "none");
	dt_cmd(cmd);
	free(cmd);
#endif

#endif	/* NO_X11 */
}

static void solid_xfce(char *color) {
#if NO_X11
	RAWFB_RET_VOID
	if (!color) {}
	return;
#else
	char get_image_show[]  = "%s xfconf-query -v -c xfce4-desktop -p /backdrop/screen0/monitor0/image-show";
	char set_image_show[]  = "%s xfconf-query -v -c xfce4-desktop -p /backdrop/screen0/monitor0/image-show -s '%s'";
	char get_color_style[] = "%s xfconf-query -v -c xfce4-desktop -p /backdrop/screen0/monitor0/color-style";
	char set_color_style[] = "%s xfconf-query -v -c xfce4-desktop -p /backdrop/screen0/monitor0/color-style -s '%s'";

	static char *orig_image_show = NULL;
	static char *orig_color_style = NULL;
	char *cmd, *dbus = "";

	RAWFB_RET_VOID

	dbus = dbus_session();
	rfbLog("guessed dbus: %s\n", dbus);
	
	if (! color) {
		if (! orig_image_show) {
			orig_image_show = "true";
		}
		if (! orig_color_style) {
			orig_color_style = "0";
		}
		if (strstr(orig_image_show, "'") != NULL)  {
			rfbLog("invalid image show: %s\n", orig_image_show);
			return;
		}
		if (strstr(orig_color_style, "'") != NULL)  {
			rfbLog("invalid color style: %s\n", orig_color_style);
			return;
		}
		if (orig_image_show[0] != '\0') {
			cmd = (char *) malloc(strlen(set_image_show) - 2 + strlen(orig_image_show) + strlen(dbus) + 1);
			sprintf(cmd, set_image_show, dbus, orig_image_show);
			dt_cmd(cmd);
			free(cmd);
		}
		if (orig_color_style[0] != '\0') {
			cmd = (char *) malloc(strlen(set_color_style) - 2 + strlen(orig_color_style) + strlen(dbus) + 1);
			sprintf(cmd, set_color_style, dbus, orig_color_style);
			dt_cmd(cmd);
			free(cmd);
		}
		return;
	}

	if (! orig_image_show) {
		char *q;
		orig_image_show = "";
		if (cmd_ok("dt")) {
			cmd = (char *) malloc(strlen(get_image_show) + strlen(dbus) + 1);
			sprintf(cmd, get_image_show, dbus);
			orig_image_show = strdup(cmd_output(cmd));
			if ((q = strrchr(orig_image_show, '\n')) != NULL) {
				*q = '\0';
			}
			fprintf(stderr, "get_image_show returned: '%s'\n\n", orig_image_show);
			free(cmd);
			if (strcasecmp(orig_image_show, "false") && strcasecmp(orig_image_show, "true")) {
				fprintf(stderr, "unrecognized image_show, disabling.\n");
				free(orig_image_show);
				orig_image_show = "";
			}
		}
	}
	if (! orig_color_style) {
		char *q;
		orig_color_style = "";
		if (cmd_ok("dt")) {
			cmd = (char *) malloc(strlen(get_color_style) + strlen(dbus) + 1);
			sprintf(cmd, get_color_style, dbus);
			orig_color_style = strdup(cmd_output(cmd));
			if ((q = strrchr(orig_color_style, '\n')) != NULL) {
				*q = '\0';
			}
			fprintf(stderr, "get_color_style returned: '%s'\n\n", orig_color_style);
			free(cmd);
			if (strlen(orig_color_style) > 1 || !isdigit((unsigned char) (*orig_color_style))) {
				fprintf(stderr, "unrecognized color_style, disabling.\n");
				free(orig_color_style);
				orig_color_style = "";
			}
		}
	}

	if (strstr(color, "'") != NULL)  {
		rfbLog("invalid color: %s\n", color);
		return;
	}

	cmd = (char *) malloc(strlen(set_color_style) + strlen("0") + strlen(dbus) + 1);
	sprintf(cmd, set_color_style, dbus, "0");
	dt_cmd(cmd);
	free(cmd);

	cmd = (char *) malloc(strlen(set_image_show) + strlen("false") + strlen(dbus) + 1);
	sprintf(cmd, set_image_show, dbus, "false");
	dt_cmd(cmd);
	free(cmd);

#endif	/* NO_X11 */
}


static char *dcop_session(void) {
	char *empty = strdup("");
#if NO_X11
	RAWFB_RET(empty);
	return empty;
#else
	char list_sessions[] = "dcop --user '%s' --list-sessions";
	int len;
	char *cmd, *host, *user = NULL;
	char *out, *p, *ds, *dsn = NULL, *sess = NULL, *sess2 = NULL;
	int db = 0;

	RAWFB_RET(empty);

	if (getenv("SESSION_MANAGER")) {
		return empty;
	}

	user = get_user_name();
	if (strstr(user, "'") != NULL)  {
		rfbLog("invalid user: %s\n", user);
		free(user);
		return empty;
	}

	len = strlen(list_sessions) + strlen(user) + 1;
	cmd = (char *) malloc(len);
	sprintf(cmd, list_sessions, user);

	out = strdup(cmd_output(cmd));
	free(cmd);
	free(user);

	ds = DisplayString(dpy);
	if (!ds || !strcmp(ds, "")) {
		ds = getenv("DISPLAY");
	}
	if (!ds) {
		ds = ":0";
	}
	ds = strdup(ds);
	p = strrchr(ds, '.');
	if (p) *p = '\0';

	dsn = strchr(ds, ':');
	if (dsn) {
		*dsn = '_';
	} else {
		free(ds);
		ds = strdup("_0");
		dsn = ds;
	}
	if (db) fprintf(stderr, "ds:  %s\n", ds);
	if (db) fprintf(stderr, "dsn: %s\n", dsn);

	host = this_host();
	if (host) {
		char *h2 = (char *) malloc(strlen(host) + 2 + 1);
		sprintf(h2, "_%s_", host);
		free(host);
		host = h2;
	} else {
		host = strdup("");
	}
	if (db) fprintf(stderr, "host: %s\n", host);

	p = strtok(out, "\n");
	while (p) {
		char *q = strstr(p, ".DCOP");
		if (db) fprintf(stderr, "p:  %s\n", p);
		if (q == NULL) {
			;
		} else if (host) {
			if (strstr(q, host)) {
				char *r = strstr(p, dsn);
				int n = strlen(dsn);
				if(r && !isalnum((int) *(r+n))) {
					sess = strdup(q);
					break;
				} else {
					if (sess2) {
						free(sess2);
					}
					sess2 = strdup(q);
				}
			}
		} else {
			char *r = strstr(p, dsn);
			int n = strlen(dsn);
			if(r && !isalnum((int) *(r+n))) {
				sess = strdup(q);
				break;
			}
		}
		p = strtok(NULL, "\n");
	}
	free(ds);
	free(out);
	free(host);
	if (!sess && sess2) {
		sess = sess2;
	}
	if (!sess || strchr(sess, '\'')) {
		if (sess) free(sess);
		sess = strdup("--all-sessions");
	} else {
		len = strlen("--session ") + 2 + strlen(sess) + 1;
		cmd = (char *) malloc(len);
		sprintf(cmd, "--session '%s'", sess);
		free(sess);
		sess = cmd;
	}
	return sess;
#endif
}

static void solid_kde(char *color) {
#if NO_X11
	RAWFB_RET_VOID
	if (!color) {}
	return;
#else
	char set_color[] =
	    "dcop --user '%s' %s kdesktop KBackgroundIface setColor '%s' 1";
	char bg_off[] =
	    "dcop --user '%s' %s kdesktop KBackgroundIface setBackgroundEnabled 0";
	char bg_on[] =
	    "dcop --user '%s' %s kdesktop KBackgroundIface setBackgroundEnabled 1";
	char *cmd, *user = NULL, *sess;
	int len;

	RAWFB_RET_VOID

	user = get_user_name();
	if (strstr(user, "'") != NULL)  {
		rfbLog("invalid user: %s\n", user);
		free(user);
		return;
	}

	set_env("DISPLAY", DisplayString(dpy));

	if (! color) {
		sess = dcop_session();
		len = strlen(bg_on) + strlen(user) + strlen(sess) + 1;
		cmd = (char *) malloc(len);
		sprintf(cmd, bg_on, user, sess);

		dt_cmd(cmd);

		free(cmd);
		free(user);
		free(sess);

		return;
	}

	if (strstr(color, "'") != NULL)  {
		rfbLog("invalid color: %s\n", color);
		return;
	}

	sess = dcop_session();

	len = strlen(set_color) + strlen(user) + strlen(sess) + strlen(color) + 1;
	cmd = (char *) malloc(len);
	sprintf(cmd, set_color, user, sess, color);
	dt_cmd(cmd);
	free(cmd);

	len = strlen(bg_off) + strlen(user) + strlen(sess) + 1;
	cmd = (char *) malloc(len);
	sprintf(cmd, bg_off, user, sess);
	dt_cmd(cmd);
	free(cmd);
	free(user);
#endif	/* NO_X11 */
}

void kde_no_animate(int restore) {
#if NO_X11
	if (!restore) {}
	RAWFB_RET_VOID
	return;
#else
	char query_setting[] =
	    "kreadconfig  --file kwinrc --group Windows --key AnimateMinimize";
	char kwinrc_off[] =
	    "kwriteconfig --file kwinrc --group Windows --key AnimateMinimize --type bool false";
	char kwinrc_on[] =
	    "kwriteconfig --file kwinrc --group Windows --key AnimateMinimize --type bool true";
	char kwin_reconfigure[] =
	    "dcop --user '%s' %s kwin KWinInterface reconfigure";
	char *cmd, *cmd2, *out, *user = NULL, *sess;
	int len;
	static int anim_state = 1;

	RAWFB_RET_VOID

	if (ncache_keep_anims) {
		return;
	}

	if (restore) {
		if (anim_state == 1) {
			return;
		}

		user = get_user_name();
		if (strstr(user, "'") != NULL)  {
			rfbLog("invalid user: %s\n", user);
			free(user);
			return;
		}

		sess = dcop_session();

		len = strlen(kwin_reconfigure) + strlen(user) + strlen(sess) + 1;
		cmd = (char *) malloc(len);
		sprintf(cmd, kwin_reconfigure, user, sess);
		rfbLog("\n");
		rfbLog("Restoring KDE kwinrc settings.\n");
		rfbLog("\n");
		dt_cmd(cmd);
		free(cmd);
		free(user);
		free(sess);
		anim_state = 1;
		return;
	} else {
		if (anim_state == 0) {
			return;
		}
		anim_state = 0;
	}

	user = get_user_name();
	if (strstr(user, "'") != NULL)  {
		rfbLog("invalid user: %s\n", user);
		free(user);
		return;
	}
	out = cmd_output(query_setting);


	if (!out || strstr(out, "false")) {
		rfbLog("\n");
		rfbLog("********************************************************\n");
		rfbLog("KDE kwinrc AnimateMinimize is false. Good.\n");
		rfbLog("********************************************************\n");
		rfbLog("\n");
		free(user);
		return;
	}

	rfbLog("\n");
	rfbLog("********************************************************\n");
	rfbLog("To improve the -ncache client-side caching performance\n");
	rfbLog("temporarily setting KDE kwinrc AnimateMinimize to false.\n");
	rfbLog("It will be reset for the next session or when VNC client\n");
	rfbLog("disconnects.  Or you can use the Control Center GUI to\n");
	rfbLog("change it now (toggle its setting a few times):\n");
	rfbLog("   Desktop -> Window Behavior -> Moving\n");
	rfbLog("********************************************************\n");
	rfbLog("\n");

	set_env("DISPLAY", DisplayString(dpy));

	sess = dcop_session();
	len = strlen(kwin_reconfigure) + strlen(user) + strlen(sess) + 1;
	cmd = (char *) malloc(len);
	sprintf(cmd, kwin_reconfigure, user, sess);

	len = 1 + strlen("sleep 10") + 2 + strlen(kwinrc_off) + 2 + strlen(cmd) + 2 + strlen("sleep 5") + 2 + strlen(kwinrc_on) + 3 + 1;
	cmd2 = (char *) malloc(len);

	sprintf(cmd2, "(sleep 10; %s; %s; sleep 5; %s) &", kwinrc_off, cmd, kwinrc_on);

	dt_cmd(cmd2);
	free(cmd);
	free(cmd2);
	free(user);
	free(sess);
#endif	/* NO_X11 */
}

void gnome_no_animate(void) {
	;
}

static pid_t solid_macosx_pid = 0;
extern char macosx_solid_background[];

static void solid_macosx(int restore) {
	char tmp[] = "/tmp/macosx_solid_background.XXXXXX";
	pid_t pid, parent = getpid();

	if (restore) {
		rfbLog("restore pid: %d\n", (int) solid_macosx_pid);
		if (solid_macosx_pid > 0) {
#if 0
			int i, status;
#endif
			rfbLog("kill -TERM macosx_solid_background helper pid: %d\n", (int) solid_macosx_pid);
			kill(solid_macosx_pid, SIGTERM);
#if 0
#if LIBVNCSERVER_HAVE_SYS_WAIT_H
#if LIBVNCSERVER_HAVE_WAITPID
			for (i=0; i < 7; i++) {
				usleep(1000 * 1000);
				waitpid(solid_macosx_pid, &status, WNOHANG); 
				if (kill(solid_macosx_pid, 0) != 0) {
					break;
				}
			}
#endif
#endif
#endif
			solid_macosx_pid = 0;
		}
		return;
	}
	if (no_external_cmds || !cmd_ok("dt")) {
		return;
	}
#if LIBVNCSERVER_HAVE_FORK
	pid = fork();

	if (pid == -1) {
		perror("fork");
		return;
	}
	if (pid == 0) {
		int fd = mkstemp(tmp);
#if LIBVNCSERVER_HAVE_SETSID
		setsid();
#else
		setpgrp();
#endif
		if (fd >= 0) {
			char num[32];
			write(fd, macosx_solid_background, strlen(macosx_solid_background));
			close(fd);
			sprintf(num, "%d", (int) parent);
			set_env("SS_WATCH_PID", num);
			execlp("/bin/sh", "/bin/sh", tmp, (char *) NULL);
		}
		exit(1);
	}
	solid_macosx_pid = pid;
	rfbLog("macosx_solid_background helper pid: %d\n", (int) solid_macosx_pid);
	usleep(2750 * 1000);
	unlink(tmp);
#endif
}

char *guess_desktop(void) {
#if NO_X11
	RAWFB_RET("root")
	return "root";
#else
	Atom prop;

	RAWFB_RET("root")

	if (wmdt_str && *wmdt_str != '\0') {
		char *s = wmdt_str;
		lowercase(s);
		if (strstr(s, "xfce")) {
			return "xfce";
		}
		if (strstr(s, "gnome") || strstr(s, "metacity")) {
			return "gnome";
		}
		if (strstr(s, "kde") || strstr(s, "kwin")) {
			return "kde";
		}
		if (strstr(s, "cde")) {
			return "cde";
		}
		return "root";
	}

	if (! dpy) {
		return "";
	}

	prop = XInternAtom(dpy, "XFCE_DESKTOP_WINDOW", True);
	if (prop != None) return "xfce";

	/* special case windowmaker */
	prop = XInternAtom(dpy, "_WINDOWMAKER_WM_PROTOCOLS", True);
	if (prop != None)  return "root";

	prop = XInternAtom(dpy, "_WINDOWMAKER_COMMAND", True);
	if (prop != None) return "root";

	prop = XInternAtom(dpy, "NAUTILUS_DESKTOP_WINDOW_ID", True);
	if (prop != None) return "gnome";

	prop = XInternAtom(dpy, "KWIN_RUNNING", True);
	if (prop != None) {
		prop = XInternAtom(dpy, "_KDE_RUNNING", True);
		if (prop != None) {
			prop = XInternAtom(dpy, "KDE_DESKTOP_WINDOW", True);
			if (prop != None) return "kde";
		}
	}

	prop = XInternAtom(dpy, "_MOTIF_WM_INFO", True);
	if (prop != None) {
		prop = XInternAtom(dpy, "_DT_WORKSPACE_LIST", True);
		if (prop != None) return "cde";
	}
	return "root";
#endif	/* NO_X11 */
}

XImage *solid_image(char *color) {
#if NO_X11
	RAWFB_RET(NULL)
	return NULL;
#else
	XImage *image = NULL;
	unsigned long pixel = 0;
	int x, y;

	RAWFB_RET(NULL)

	if (!color) {
		color = last_color;
	}

	if (!color) {
		return NULL;
	}

	image = XGetImage(dpy, rootwin, 0, 0, wdpy_x, wdpy_y, AllPlanes,
	    ZPixmap);

	if (!image) {
		return NULL;
	}
	pixel = get_pixel(color);

	for (y=0; y<wdpy_y; y++) {
		for (x=0; x<wdpy_x; x++) {
			XPutPixel(image, x, y, pixel);
		}
	}
	return image;
#endif	/* NO_X11 */
}

void solid_bg(int restore) {
	static int desktop = -1;
	static int solid_on = 0;
	static char *prev_str;
	char *dtname, *color;

	if (started_as_root == 1 && users_list) {
		/* we are still root, don't try. */
		return;
	}

	if (macosx_console) {
		solid_macosx(restore);
		return;
	}

	RAWFB_RET_VOID

	if (restore) {
		if (! solid_on) {
			return;
		}
		if (desktop == 0) {
			solid_root(NULL);
		} else if (desktop == 1) {
			solid_gnome(NULL);
		} else if (desktop == 2) {
			solid_kde(NULL);
		} else if (desktop == 3) {
			solid_cde(NULL);
		} else if (desktop == 4) {
			solid_xfce(NULL);
		}
		solid_on = 0;
		return;
	}
	if (! solid_str) {
		return;
	}
	if (solid_on && !strcmp(prev_str, solid_str)) {
		return;
	}
	if (strstr(solid_str, "guess:") == solid_str
	    || !strchr(solid_str, ':')) {
		dtname = guess_desktop();
		rfbLog("guessed desktop: %s\n", dtname);
	} else {
		if (strstr(solid_str, "gnome:") == solid_str) {
			dtname = "gnome";
		} else if (strstr(solid_str, "kde:") == solid_str) {
			dtname = "kde";
		} else if (strstr(solid_str, "cde:") == solid_str) {
			dtname = "cde";
		} else if (strstr(solid_str, "xfce:") == solid_str) {
			dtname = "xfce";
		} else {
			dtname = "root";
		}
	}

	color = strchr(solid_str, ':');
	if (! color) {
		color = solid_str;
	} else {
		color++;
		if (*color == '\0') {
			color = solid_default;
		}
	}
	if (last_color) {
		free(last_color);
	}
	last_color = strdup(color);

	if (!strcmp(dtname, "gnome")) {
		desktop = 1;
		solid_gnome(color);
	} else if (!strcmp(dtname, "kde")) {
		desktop = 2;
		solid_kde(color);
	} else if (!strcmp(dtname, "cde")) {
		desktop = 3;
		solid_cde(color);
	} else if (!strcmp(dtname, "xfce")) {
		desktop = 4;
		solid_xfce(color);
	} else {
		desktop = 0;
		solid_root(color);
	}
	if (prev_str) {
		free(prev_str);
	}
	prev_str = strdup(solid_str);
	solid_on = 1;
}