1/*
2   Copyright (C) 2002-2010 Karl J. Runge <runge@karlrunge.com>
3   All rights reserved.
4
5This file is part of x11vnc.
6
7x11vnc is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 2 of the License, or (at
10your option) any later version.
11
12x11vnc is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with x11vnc; if not, write to the Free Software
19Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
20or see <http://www.gnu.org/licenses/>.
21
22In addition, as a special exception, Karl J. Runge
23gives permission to link the code of its release of x11vnc with the
24OpenSSL project's "OpenSSL" library (or with modified versions of it
25that use the same license as the "OpenSSL" library), and distribute
26the linked executables.  You must obey the GNU General Public License
27in all respects for all of the code used other than "OpenSSL".  If you
28modify this file, you may extend this exception to your version of the
29file, but you are not obligated to do so.  If you do not wish to do
30so, delete this exception statement from your version.
31*/
32
33/* -- solid.c -- */
34
35#include "x11vnc.h"
36#include "win_utils.h"
37#include "xwrappers.h"
38#include "connections.h"
39#include "cleanup.h"
40#include "xevents.h"
41
42char *guess_desktop(void);
43void solid_bg(int restore);
44char *dbus_session(void);
45
46
47static void usr_bin_path(int restore);
48static int dt_cmd(char *cmd);
49static char *cmd_output(char *cmd);
50XImage *solid_root(char *color);
51static void solid_cde(char *color);
52static void solid_gnome(char *color);
53static void solid_kde(char *color);
54static void solid_macosx(int restore);
55
56static void usr_bin_path(int restore) {
57	static char *oldpath = NULL;
58	char *newpath;
59	char addpath[] = "/usr/bin:/bin:";
60
61	if (restore) {
62		if (oldpath) {
63			set_env("PATH", oldpath);
64			free(oldpath);
65			oldpath = NULL;
66		}
67		return;
68	}
69
70	if (getenv("PATH")) {
71		oldpath = strdup(getenv("PATH"));
72	} else {
73		oldpath = strdup("/usr/bin");
74	}
75	newpath = (char *) malloc(strlen(oldpath) + strlen(addpath) + 1);
76	newpath[0] = '\0';
77	strcat(newpath, addpath);
78	strcat(newpath, oldpath);
79	set_env("PATH", newpath);
80	free(newpath);
81}
82
83static int dt_cmd(char *cmd) {
84	int rc;
85
86	RAWFB_RET(0)
87
88	if (!cmd || *cmd == '\0') {
89		return 0;
90	}
91
92	/* dt */
93	if (no_external_cmds || !cmd_ok("dt")) {
94		rfbLog("cannot run external commands in -nocmds mode:\n");
95		rfbLog("   \"%s\"\n", cmd);
96		rfbLog("   dt_cmd: returning 1\n");
97		return 1;
98	}
99
100	if (getenv("DISPLAY") == NULL) {
101		set_env("DISPLAY", DisplayString(dpy));
102	}
103
104	rfbLog("running command:\n");
105	if (!quiet) {
106		fprintf(stderr, "\n  %s\n\n", cmd);
107	}
108	usr_bin_path(0);
109	close_exec_fds();
110	rc = system(cmd);
111	usr_bin_path(1);
112
113	if (rc >= 256) {
114		rc = rc/256;
115	}
116	return rc;
117}
118
119static char *cmd_output(char *cmd) {
120	FILE *p;
121	static char output[50000];
122	char line[1024];
123	int rc;
124
125	if (!cmd || *cmd == '\0') {
126		return "";
127	}
128
129	if (no_external_cmds) {
130		rfbLog("cannot run external commands in -nocmds mode:\n");
131		rfbLog("   \"%s\"\n", cmd);
132		rfbLog("   cmd_output: null string.\n");
133		return "";
134	}
135
136	rfbLog("running pipe:\n");
137	if (!quiet) {
138		fprintf(stderr, "\n  %s\n\n", cmd);
139	}
140	usr_bin_path(0);
141	close_exec_fds();
142	p = popen(cmd, "r");
143	usr_bin_path(1);
144
145	output[0] = '\0';
146
147	while (fgets(line, 1024, p) != NULL) {
148		if (strlen(output) + strlen(line) + 1 < 50000) {
149			strcat(output, line);
150		}
151	}
152	rc = pclose(p);
153	return(output);
154}
155
156static char *last_color = NULL;
157
158unsigned long get_pixel(char *color) {
159#if NO_X11
160	return 0;
161#else
162	XColor cdef;
163	Colormap cmap;
164	unsigned long pixel = BlackPixel(dpy, scr);
165	if (depth > 8 || strcmp(color, solid_default)) {
166		cmap = DefaultColormap (dpy, scr);
167		if (XParseColor(dpy, cmap, color, &cdef) &&
168		    XAllocColor(dpy, cmap, &cdef)) {
169			pixel = cdef.pixel;
170		} else {
171			rfbLog("error parsing/allocing color: %s\n", color);
172		}
173	}
174	return pixel;
175#endif
176}
177
178XImage *solid_root(char *color) {
179#if NO_X11
180	RAWFB_RET_VOID
181	if (!color) {}
182	return NULL;
183#else
184	Window expose;
185	static XImage *image = NULL;
186	Pixmap pixmap;
187	XGCValues gcv;
188	GC gc;
189	XSetWindowAttributes swa;
190	Visual visual;
191	static unsigned long mask, pixel = 0;
192
193	RAWFB_RET(NULL)
194
195	if (subwin || window != rootwin) {
196		rfbLog("cannot set subwin to solid color, must be rootwin\n");
197		return NULL;
198	}
199
200	/* create the "clear" window just for generating exposures */
201	swa.override_redirect = True;
202	swa.backing_store = NotUseful;
203	swa.save_under = False;
204	swa.background_pixmap = None;
205	visual.visualid = CopyFromParent;
206	mask = (CWOverrideRedirect|CWBackingStore|CWSaveUnder|CWBackPixmap);
207	expose = XCreateWindow(dpy, window, 0, 0, wdpy_x, wdpy_y, 0, depth,
208	    InputOutput, &visual, mask, &swa);
209
210	if (! color) {
211
212		if (! image) {
213			/* whoops */
214			XDestroyWindow(dpy, expose);
215			rfbLog("no root snapshot available.\n");
216			return NULL;
217		}
218
219		/* restore the root window from the XImage snapshot */
220		pixmap = XCreatePixmap(dpy, window, wdpy_x, wdpy_y, depth);
221
222		/* draw the image to a pixmap: */
223		gcv.function = GXcopy;
224		gcv.plane_mask = AllPlanes;
225		gc = XCreateGC(dpy, window, GCFunction|GCPlaneMask, &gcv);
226
227		XPutImage(dpy, pixmap, gc, image, 0, 0, 0, 0, wdpy_x, wdpy_y);
228
229		gcv.foreground = gcv.background = BlackPixel(dpy, scr);
230		gc = XCreateGC(dpy, window, GCForeground|GCBackground, &gcv);
231
232		rfbLog("restoring root snapshot...\n");
233		/* set the pixmap as the bg: */
234		XSetWindowBackgroundPixmap(dpy, window, pixmap);
235		XFreePixmap(dpy, pixmap);
236		XClearWindow(dpy, window);
237		XFlush_wr(dpy);
238
239		/* generate exposures */
240		XMapWindow(dpy, expose);
241		XSync(dpy, False);
242		XDestroyWindow(dpy, expose);
243		return NULL;
244	}
245
246	if (! image) {
247		/* need to retrieve a snapshot of the root background: */
248		Window iwin;
249		XSetWindowAttributes iswa;
250
251		/* create image window: */
252		iswa.override_redirect = True;
253		iswa.backing_store = NotUseful;
254		iswa.save_under = False;
255		iswa.background_pixmap = ParentRelative;
256
257		iwin = XCreateWindow(dpy, window, 0, 0, wdpy_x, wdpy_y, 0,
258		    depth, InputOutput, &visual, mask, &iswa);
259
260		rfbLog("snapshotting background...\n");
261
262		XMapWindow(dpy, iwin);
263		XSync(dpy, False);
264		image = XGetImage(dpy, iwin, 0, 0, wdpy_x, wdpy_y, AllPlanes,
265		    ZPixmap);
266		XSync(dpy, False);
267		XDestroyWindow(dpy, iwin);
268		rfbLog("done.\n");
269	}
270	if (color == (char *) 0x1) {
271		/* caller will XDestroyImage it: */
272		XImage *xi = image;
273		image = NULL;
274		return xi;
275	}
276
277	/* use black for low colors or failure */
278	pixel = get_pixel(color);
279
280	rfbLog("setting solid background...\n");
281	XSetWindowBackground(dpy, window, pixel);
282	XMapWindow(dpy, expose);
283	XSync(dpy, False);
284	XDestroyWindow(dpy, expose);
285#endif	/* NO_X11 */
286	return NULL;
287}
288
289static void solid_cde(char *color) {
290#if NO_X11
291	RAWFB_RET_VOID
292	if (!color) {}
293	return;
294#else
295	int wsmax = 16;
296	static XImage *image[16];
297	static Window ws_wins[16];
298	static int nws = -1;
299
300	Window expose;
301	Pixmap pixmap;
302	XGCValues gcv;
303	GC gc;
304	XSetWindowAttributes swa;
305	Visual visual;
306	unsigned long mask, pixel;
307	XColor cdef;
308	Colormap cmap;
309	int n;
310
311	RAWFB_RET_VOID
312
313	if (subwin || window != rootwin) {
314		rfbLog("cannot set subwin to solid color, must be rootwin\n");
315		return;
316	}
317
318	/* create the "clear" window just for generating exposures */
319	swa.override_redirect = True;
320	swa.backing_store = NotUseful;
321	swa.save_under = False;
322	swa.background_pixmap = None;
323	visual.visualid = CopyFromParent;
324	mask = (CWOverrideRedirect|CWBackingStore|CWSaveUnder|CWBackPixmap);
325	expose = XCreateWindow(dpy, window, 0, 0, wdpy_x, wdpy_y, 0, depth,
326	    InputOutput, &visual, mask, &swa);
327
328	if (! color) {
329		/* restore the backdrop windows from the XImage snapshots */
330
331		for (n=0; n < nws; n++) {
332			Window twin;
333
334			if (! image[n]) {
335				continue;
336			}
337
338			twin = ws_wins[n];
339			if (! twin) {
340				twin = rootwin;
341			}
342			if (! valid_window(twin, NULL, 0)) {
343				continue;
344			}
345
346			pixmap = XCreatePixmap(dpy, twin, wdpy_x, wdpy_y,
347			    depth);
348
349			/* draw the image to a pixmap: */
350			gcv.function = GXcopy;
351			gcv.plane_mask = AllPlanes;
352			gc = XCreateGC(dpy, twin, GCFunction|GCPlaneMask, &gcv);
353
354			XPutImage(dpy, pixmap, gc, image[n], 0, 0, 0, 0,
355			    wdpy_x, wdpy_y);
356
357			gcv.foreground = gcv.background = BlackPixel(dpy, scr);
358			gc = XCreateGC(dpy, twin, GCForeground|GCBackground,
359			    &gcv);
360
361			rfbLog("restoring CDE ws%d snapshot to 0x%lx\n",
362			    n, twin);
363			/* set the pixmap as the bg: */
364			XSetWindowBackgroundPixmap(dpy, twin, pixmap);
365			XFreePixmap(dpy, pixmap);
366			XClearWindow(dpy, twin);
367			XFlush_wr(dpy);
368		}
369
370		/* generate exposures */
371		XMapWindow(dpy, expose);
372		XSync(dpy, False);
373		XDestroyWindow(dpy, expose);
374		return;
375	}
376
377	if (nws < 0) {
378		/* need to retrieve snapshots of the ws backgrounds: */
379		Window iwin, wm_win;
380		XSetWindowAttributes iswa;
381		Atom dt_list, wm_info, type;
382		int format;
383		unsigned long length, after;
384		unsigned char *data;
385		unsigned long *dp;	/* crash on 64bit with int */
386
387		nws = 0;
388
389		/* extract the hidden wm properties about backdrops: */
390
391		wm_info = XInternAtom(dpy, "_MOTIF_WM_INFO", True);
392		if (wm_info == None) {
393			return;
394		}
395
396		XGetWindowProperty(dpy, rootwin, wm_info, 0L, 10L, False,
397		    AnyPropertyType, &type, &format, &length, &after, &data);
398
399		/*
400		 * xprop -notype -root _MOTIF_WM_INFO
401		 * _MOTIF_WM_INFO = 0x2, 0x580028
402		 */
403
404		if (length < 2 || format != 32 || after != 0) {
405			return;
406		}
407
408		dp = (unsigned long *) data;
409		wm_win = (Window) *(dp+1);	/* 2nd item. */
410
411
412		dt_list = XInternAtom(dpy, "_DT_WORKSPACE_LIST", True);
413		if (dt_list == None) {
414			return;
415		}
416
417		XGetWindowProperty(dpy, wm_win, dt_list, 0L, 10L, False,
418		   AnyPropertyType, &type, &format, &length, &after, &data);
419
420		nws = length;
421
422		if (nws > wsmax) {
423			nws = wsmax;
424		}
425		if (nws < 0) {
426			nws = 0;
427		}
428
429		rfbLog("special CDE win: 0x%lx, %d workspaces\n", wm_win, nws);
430		if (nws == 0) {
431			return;
432		}
433
434		for (n=0; n<nws; n++) {
435			Atom ws_atom;
436			char tmp[32];
437			Window twin;
438			XWindowAttributes attr;
439			int i, cnt;
440
441			image[n] = NULL;
442			ws_wins[n] = 0x0;
443
444			sprintf(tmp, "_DT_WORKSPACE_INFO_ws%d", n);
445			ws_atom = XInternAtom(dpy, tmp, False);
446			if (ws_atom == None) {
447				continue;
448			}
449			XGetWindowProperty(dpy, wm_win, ws_atom, 0L, 100L,
450			   False, AnyPropertyType, &type, &format, &length,
451			   &after, &data);
452
453			if (format != 8 || after != 0) {
454				continue;
455			}
456			/*
457			 * xprop -notype -id wm_win
458			 * _DT_WORKSPACE_INFO_ws0 = "One", "3", "0x2f2f4a",
459			 * "0x63639c", "0x103", "1", "0x58044e"
460			 */
461
462			cnt = 0;
463			twin = 0x0;
464			for (i=0; i< (int) length; i++) {
465				if (*(data+i) != '\0') {
466					continue;
467				}
468				cnt++;	/* count nulls to indicate field */
469				if (cnt == 6) {
470					/* one past the null: */
471					char *q = (char *) (data+i+1);
472					unsigned long in;
473					if (sscanf(q, "0x%lx", &in) == 1) {
474						twin = (Window) in;
475						break;
476					}
477				}
478			}
479			ws_wins[n] = twin;
480
481			if (! twin) {
482				twin = rootwin;
483			}
484
485			XGetWindowAttributes(dpy, twin, &attr);
486			if (twin != rootwin) {
487				if (attr.map_state != IsViewable) {
488					XMapWindow(dpy, twin);
489				}
490				XRaiseWindow(dpy, twin);
491			}
492			XSync(dpy, False);
493
494			/* create image window: */
495			iswa.override_redirect = True;
496			iswa.backing_store = NotUseful;
497			iswa.save_under = False;
498			iswa.background_pixmap = ParentRelative;
499			visual.visualid = CopyFromParent;
500
501			iwin = XCreateWindow(dpy, twin, 0, 0, wdpy_x, wdpy_y,
502			    0, depth, InputOutput, &visual, mask, &iswa);
503
504			rfbLog("snapshotting CDE backdrop ws%d 0x%lx -> "
505			    "0x%lx ...\n", n, twin, iwin);
506			XMapWindow(dpy, iwin);
507			XSync(dpy, False);
508
509			image[n] = XGetImage(dpy, iwin, 0, 0, wdpy_x, wdpy_y,
510			    AllPlanes, ZPixmap);
511			XSync(dpy, False);
512			XDestroyWindow(dpy, iwin);
513			if (twin != rootwin) {
514				XLowerWindow(dpy, twin);
515				if (attr.map_state != IsViewable) {
516					XUnmapWindow(dpy, twin);
517				}
518			}
519		}
520	}
521	if (nws == 0) {
522		return;
523	}
524
525	/* use black for low colors or failure */
526	pixel = BlackPixel(dpy, scr);
527	if (depth > 8 || strcmp(color, solid_default)) {
528		cmap = DefaultColormap (dpy, scr);
529		if (XParseColor(dpy, cmap, color, &cdef) &&
530		    XAllocColor(dpy, cmap, &cdef)) {
531			pixel = cdef.pixel;
532		} else {
533			rfbLog("error parsing/allocing color: %s\n", color);
534		}
535	}
536
537	rfbLog("setting solid backgrounds...\n");
538
539	for (n=0; n < nws; n++)  {
540		Window twin = ws_wins[n];
541		if (image[n] == NULL) {
542			continue;
543		}
544		if (! twin)  {
545			twin = rootwin;
546		}
547		XSetWindowBackground(dpy, twin, pixel);
548	}
549	XMapWindow(dpy, expose);
550	XSync(dpy, False);
551	XDestroyWindow(dpy, expose);
552#endif	/* NO_X11 */
553}
554
555static char _dbus_str[1100];
556
557char *dbus_session(void) {
558	char *dbus_env = getenv("DBUS_SESSION_BUS_ADDRESS");
559	char tmp[1000];
560
561	if (dbus_env != NULL && strlen(dbus_env) > 0) {
562		return "";
563	}
564	if (!dpy) {
565		return "";
566	}
567#if NO_X11
568	return "";
569#else
570	{
571		Atom dbus_prop, dbus_pid;
572		Window r, w, *children;
573		int sbest = -1;
574		unsigned int ui;
575		int rc, i;
576
577		memset(_dbus_str, 0, sizeof(_dbus_str));
578
579		X_LOCK;
580		dbus_prop = XInternAtom(dpy, "_DBUS_SESSION_BUS_ADDRESS", True);
581		dbus_pid  = XInternAtom(dpy, "_DBUS_SESSION_BUS_PID", True);
582		X_UNLOCK;
583		if (dbus_prop == None) {
584			return "";
585		}
586
587		X_LOCK;
588		memset(tmp, 0, sizeof(tmp));
589		get_prop(tmp, sizeof(tmp)-1, dbus_prop, None);
590		X_UNLOCK;
591		if (strcmp(tmp, "")) {
592			if (!strchr(tmp, '\'')) {
593				sprintf(_dbus_str, "env DBUS_SESSION_BUS_ADDRESS='%s'", tmp);
594				return _dbus_str;
595			}
596		}
597
598		X_LOCK;
599		rc = XQueryTree_wr(dpy, rootwin, &r, &w, &children, &ui);
600		X_UNLOCK;
601		if (!rc || children == NULL || ui == 0) {
602			return "";
603		}
604		for (i=0; i < (int) ui; i++) {
605			int pid = -1;
606
607			X_LOCK;
608			memset(tmp, 0, sizeof(tmp));
609			get_prop(tmp, sizeof(tmp)-1, dbus_prop, children[i]);
610			if (dbus_pid != None) {
611				Atom atype;
612				int aformat;
613				unsigned long nitems, bafter;
614				unsigned char *prop;
615				if (XGetWindowProperty(dpy, children[i], dbus_pid,
616				    0, 1, False, XA_CARDINAL, &atype, &aformat,
617				    &nitems, &bafter, &prop) == Success
618				    && atype == XA_CARDINAL) {
619					pid = *((int *) prop);
620					XFree_wr(prop);
621				}
622			}
623			X_UNLOCK;
624
625			if (strcmp(tmp, "")  && !strchr(tmp, '\'')) {
626				int score = 0;
627				if (1 < pid && pid < 10000000) {
628					struct stat sb;
629					char procfile[32];
630
631					sprintf(procfile, "/proc/%d", pid);
632					if (stat(procfile, &sb) == 0) {
633						score += 10000000;
634					}
635					score += pid;
636				}
637				if (getenv("X11VNC_DBUS_DEBUG")) fprintf(stderr, "win: 0x%lx  pid: %8d  score: %8d  str: %s\n", children[i], pid, score, tmp);
638				if (score > sbest) {
639					sprintf(_dbus_str, "env DBUS_SESSION_BUS_ADDRESS='%s'", tmp);
640					sbest = score;
641				}
642			}
643		}
644		X_LOCK;
645		XFree_wr(children);
646		X_UNLOCK;
647
648		return _dbus_str;
649	}
650#endif
651}
652
653static void solid_gnome(char *color) {
654#if NO_X11
655	RAWFB_RET_VOID
656	if (!color) {}
657	return;
658#else
659	char get_color[] = "%s gconftool-2 --get "
660	    "/desktop/gnome/background/primary_color";
661	char set_color[] = "%s gconftool-2 --set --type string "
662	    "/desktop/gnome/background/primary_color '%s'";
663	char get_option[] = "%s gconftool-2 --get "
664	    "/desktop/gnome/background/picture_options";
665	char set_option[] = "%s gconftool-2 --set --type string "
666	    "/desktop/gnome/background/picture_options '%s'";
667#if 0
668	char get_shading[] = "%s gconftool-2 --get "
669	    "/desktop/gnome/background/color_shading_type";
670	char set_shading[] = "%s gconftool-2 --set --type string "
671	    "/desktop/gnome/background/color_shading_type '%s'";
672	char get_filename[] = "%s gconftool-2 --get "
673	    "/desktop/gnome/background/picture_filename";
674	char set_filename[] = "%s gconftool-2 --set --type string "
675	    "/desktop/gnome/background/picture_filename '%s'";
676#endif
677	static char *orig_color = NULL;
678	static char *orig_option = NULL;
679	char *cmd, *dbus = "";
680
681	RAWFB_RET_VOID
682
683	dbus = dbus_session();
684	rfbLog("guessed dbus: %s\n", dbus);
685
686	if (! color) {
687		if (! orig_color) {
688			orig_color = strdup("#FFFFFF");
689		}
690		if (! orig_option) {
691			orig_option = strdup("stretched");
692		}
693		if (strstr(orig_color, "'") != NULL)  {
694			rfbLog("invalid color: %s\n", orig_color);
695			return;
696		}
697		if (strstr(orig_option, "'") != NULL)  {
698			rfbLog("invalid option: %s\n", orig_option);
699			return;
700		}
701		cmd = (char *) malloc(strlen(set_option) - 2 + strlen(orig_option) + strlen(dbus) + 1);
702		sprintf(cmd, set_option, dbus, orig_option);
703		dt_cmd(cmd);
704		free(cmd);
705		cmd = (char *) malloc(strlen(set_color) - 2 + strlen(orig_color) + strlen(dbus) + 1);
706		sprintf(cmd, set_color, dbus, orig_color);
707		dt_cmd(cmd);
708		free(cmd);
709		return;
710	}
711
712	if (! orig_color) {
713		char *q;
714		if (cmd_ok("dt")) {
715			cmd = (char *) malloc(strlen(get_color) + strlen(dbus) + 1);
716			sprintf(cmd, get_color, dbus);
717			orig_color = strdup(cmd_output(cmd));
718			free(cmd);
719		} else {
720			orig_color = "";
721		}
722		if (*orig_color == '\0') {
723			orig_color = strdup("#FFFFFF");
724		}
725		if ((q = strchr(orig_color, '\n')) != NULL) {
726			*q = '\0';
727		}
728	}
729	if (! orig_option) {
730		char *q;
731		if (cmd_ok("dt")) {
732			cmd = (char *) malloc(strlen(get_option) + strlen(dbus) + 1);
733			sprintf(cmd, get_option, dbus);
734			orig_option = strdup(cmd_output(cmd));
735			free(cmd);
736		} else {
737			orig_color = "";
738		}
739		if (*orig_option == '\0') {
740			orig_option = strdup("stretched");
741		}
742		if ((q = strchr(orig_option, '\n')) != NULL) {
743			*q = '\0';
744		}
745	}
746	if (strstr(color, "'") != NULL)  {
747		rfbLog("invalid color: %s\n", color);
748		return;
749	}
750	cmd = (char *) malloc(strlen(set_color) + strlen(color) + strlen(dbus) + 1);
751	sprintf(cmd, set_color, dbus, color);
752	dt_cmd(cmd);
753	free(cmd);
754
755	cmd = (char *) malloc(strlen(set_option) + strlen("none") + strlen(dbus) + 1);
756	sprintf(cmd, set_option, dbus, "none");
757	dt_cmd(cmd);
758	free(cmd);
759
760#if 0
761	cmd = (char *) malloc(strlen(set_filename) + strlen("none") + 1);
762	sprintf(cmd, set_filename, dbus, "none");
763	dt_cmd(cmd);
764	free(cmd);
765#endif
766
767#endif	/* NO_X11 */
768}
769
770static void solid_xfce(char *color) {
771#if NO_X11
772	RAWFB_RET_VOID
773	if (!color) {}
774	return;
775#else
776	char get_image_show[]  = "%s xfconf-query -v -c xfce4-desktop -p /backdrop/screen0/monitor0/image-show";
777	char set_image_show[]  = "%s xfconf-query -v -c xfce4-desktop -p /backdrop/screen0/monitor0/image-show -s '%s'";
778	char get_color_style[] = "%s xfconf-query -v -c xfce4-desktop -p /backdrop/screen0/monitor0/color-style";
779	char set_color_style[] = "%s xfconf-query -v -c xfce4-desktop -p /backdrop/screen0/monitor0/color-style -s '%s'";
780
781	static char *orig_image_show = NULL;
782	static char *orig_color_style = NULL;
783	char *cmd, *dbus = "";
784
785	RAWFB_RET_VOID
786
787	dbus = dbus_session();
788	rfbLog("guessed dbus: %s\n", dbus);
789
790	if (! color) {
791		if (! orig_image_show) {
792			orig_image_show = "true";
793		}
794		if (! orig_color_style) {
795			orig_color_style = "0";
796		}
797		if (strstr(orig_image_show, "'") != NULL)  {
798			rfbLog("invalid image show: %s\n", orig_image_show);
799			return;
800		}
801		if (strstr(orig_color_style, "'") != NULL)  {
802			rfbLog("invalid color style: %s\n", orig_color_style);
803			return;
804		}
805		if (orig_image_show[0] != '\0') {
806			cmd = (char *) malloc(strlen(set_image_show) - 2 + strlen(orig_image_show) + strlen(dbus) + 1);
807			sprintf(cmd, set_image_show, dbus, orig_image_show);
808			dt_cmd(cmd);
809			free(cmd);
810		}
811		if (orig_color_style[0] != '\0') {
812			cmd = (char *) malloc(strlen(set_color_style) - 2 + strlen(orig_color_style) + strlen(dbus) + 1);
813			sprintf(cmd, set_color_style, dbus, orig_color_style);
814			dt_cmd(cmd);
815			free(cmd);
816		}
817		return;
818	}
819
820	if (! orig_image_show) {
821		char *q;
822		orig_image_show = "";
823		if (cmd_ok("dt")) {
824			cmd = (char *) malloc(strlen(get_image_show) + strlen(dbus) + 1);
825			sprintf(cmd, get_image_show, dbus);
826			orig_image_show = strdup(cmd_output(cmd));
827			if ((q = strrchr(orig_image_show, '\n')) != NULL) {
828				*q = '\0';
829			}
830			fprintf(stderr, "get_image_show returned: '%s'\n\n", orig_image_show);
831			free(cmd);
832			if (strcasecmp(orig_image_show, "false") && strcasecmp(orig_image_show, "true")) {
833				fprintf(stderr, "unrecognized image_show, disabling.\n");
834				free(orig_image_show);
835				orig_image_show = "";
836			}
837		}
838	}
839	if (! orig_color_style) {
840		char *q;
841		orig_color_style = "";
842		if (cmd_ok("dt")) {
843			cmd = (char *) malloc(strlen(get_color_style) + strlen(dbus) + 1);
844			sprintf(cmd, get_color_style, dbus);
845			orig_color_style = strdup(cmd_output(cmd));
846			if ((q = strrchr(orig_color_style, '\n')) != NULL) {
847				*q = '\0';
848			}
849			fprintf(stderr, "get_color_style returned: '%s'\n\n", orig_color_style);
850			free(cmd);
851			if (strlen(orig_color_style) > 1 || !isdigit((unsigned char) (*orig_color_style))) {
852				fprintf(stderr, "unrecognized color_style, disabling.\n");
853				free(orig_color_style);
854				orig_color_style = "";
855			}
856		}
857	}
858
859	if (strstr(color, "'") != NULL)  {
860		rfbLog("invalid color: %s\n", color);
861		return;
862	}
863
864	cmd = (char *) malloc(strlen(set_color_style) + strlen("0") + strlen(dbus) + 1);
865	sprintf(cmd, set_color_style, dbus, "0");
866	dt_cmd(cmd);
867	free(cmd);
868
869	cmd = (char *) malloc(strlen(set_image_show) + strlen("false") + strlen(dbus) + 1);
870	sprintf(cmd, set_image_show, dbus, "false");
871	dt_cmd(cmd);
872	free(cmd);
873
874#endif	/* NO_X11 */
875}
876
877
878static char *dcop_session(void) {
879	char *empty = strdup("");
880#if NO_X11
881	RAWFB_RET(empty);
882	return empty;
883#else
884	char list_sessions[] = "dcop --user '%s' --list-sessions";
885	int len;
886	char *cmd, *host, *user = NULL;
887	char *out, *p, *ds, *dsn = NULL, *sess = NULL, *sess2 = NULL;
888	int db = 0;
889
890	RAWFB_RET(empty);
891
892	if (getenv("SESSION_MANAGER")) {
893		return empty;
894	}
895
896	user = get_user_name();
897	if (strstr(user, "'") != NULL)  {
898		rfbLog("invalid user: %s\n", user);
899		free(user);
900		return empty;
901	}
902
903	len = strlen(list_sessions) + strlen(user) + 1;
904	cmd = (char *) malloc(len);
905	sprintf(cmd, list_sessions, user);
906
907	out = strdup(cmd_output(cmd));
908	free(cmd);
909	free(user);
910
911	ds = DisplayString(dpy);
912	if (!ds || !strcmp(ds, "")) {
913		ds = getenv("DISPLAY");
914	}
915	if (!ds) {
916		ds = ":0";
917	}
918	ds = strdup(ds);
919	p = strrchr(ds, '.');
920	if (p) *p = '\0';
921
922	dsn = strchr(ds, ':');
923	if (dsn) {
924		*dsn = '_';
925	} else {
926		free(ds);
927		ds = strdup("_0");
928		dsn = ds;
929	}
930	if (db) fprintf(stderr, "ds:  %s\n", ds);
931	if (db) fprintf(stderr, "dsn: %s\n", dsn);
932
933	host = this_host();
934	if (host) {
935		char *h2 = (char *) malloc(strlen(host) + 2 + 1);
936		sprintf(h2, "_%s_", host);
937		free(host);
938		host = h2;
939	} else {
940		host = strdup("");
941	}
942	if (db) fprintf(stderr, "host: %s\n", host);
943
944	p = strtok(out, "\n");
945	while (p) {
946		char *q = strstr(p, ".DCOP");
947		if (db) fprintf(stderr, "p:  %s\n", p);
948		if (q == NULL) {
949			;
950		} else if (host) {
951			if (strstr(q, host)) {
952				char *r = strstr(p, dsn);
953				int n = strlen(dsn);
954				if(r && !isalnum((int) *(r+n))) {
955					sess = strdup(q);
956					break;
957				} else {
958					if (sess2) {
959						free(sess2);
960					}
961					sess2 = strdup(q);
962				}
963			}
964		} else {
965			char *r = strstr(p, dsn);
966			int n = strlen(dsn);
967			if(r && !isalnum((int) *(r+n))) {
968				sess = strdup(q);
969				break;
970			}
971		}
972		p = strtok(NULL, "\n");
973	}
974	free(ds);
975	free(out);
976	free(host);
977	if (!sess && sess2) {
978		sess = sess2;
979	}
980	if (!sess || strchr(sess, '\'')) {
981		if (sess) free(sess);
982		sess = strdup("--all-sessions");
983	} else {
984		len = strlen("--session ") + 2 + strlen(sess) + 1;
985		cmd = (char *) malloc(len);
986		sprintf(cmd, "--session '%s'", sess);
987		free(sess);
988		sess = cmd;
989	}
990	return sess;
991#endif
992}
993
994static void solid_kde(char *color) {
995#if NO_X11
996	RAWFB_RET_VOID
997	if (!color) {}
998	return;
999#else
1000	char set_color[] =
1001	    "dcop --user '%s' %s kdesktop KBackgroundIface setColor '%s' 1";
1002	char bg_off[] =
1003	    "dcop --user '%s' %s kdesktop KBackgroundIface setBackgroundEnabled 0";
1004	char bg_on[] =
1005	    "dcop --user '%s' %s kdesktop KBackgroundIface setBackgroundEnabled 1";
1006	char *cmd, *user = NULL, *sess;
1007	int len;
1008
1009	RAWFB_RET_VOID
1010
1011	user = get_user_name();
1012	if (strstr(user, "'") != NULL)  {
1013		rfbLog("invalid user: %s\n", user);
1014		free(user);
1015		return;
1016	}
1017
1018	set_env("DISPLAY", DisplayString(dpy));
1019
1020	if (! color) {
1021		sess = dcop_session();
1022		len = strlen(bg_on) + strlen(user) + strlen(sess) + 1;
1023		cmd = (char *) malloc(len);
1024		sprintf(cmd, bg_on, user, sess);
1025
1026		dt_cmd(cmd);
1027
1028		free(cmd);
1029		free(user);
1030		free(sess);
1031
1032		return;
1033	}
1034
1035	if (strstr(color, "'") != NULL)  {
1036		rfbLog("invalid color: %s\n", color);
1037		return;
1038	}
1039
1040	sess = dcop_session();
1041
1042	len = strlen(set_color) + strlen(user) + strlen(sess) + strlen(color) + 1;
1043	cmd = (char *) malloc(len);
1044	sprintf(cmd, set_color, user, sess, color);
1045	dt_cmd(cmd);
1046	free(cmd);
1047
1048	len = strlen(bg_off) + strlen(user) + strlen(sess) + 1;
1049	cmd = (char *) malloc(len);
1050	sprintf(cmd, bg_off, user, sess);
1051	dt_cmd(cmd);
1052	free(cmd);
1053	free(user);
1054#endif	/* NO_X11 */
1055}
1056
1057void kde_no_animate(int restore) {
1058#if NO_X11
1059	if (!restore) {}
1060	RAWFB_RET_VOID
1061	return;
1062#else
1063	char query_setting[] =
1064	    "kreadconfig  --file kwinrc --group Windows --key AnimateMinimize";
1065	char kwinrc_off[] =
1066	    "kwriteconfig --file kwinrc --group Windows --key AnimateMinimize --type bool false";
1067	char kwinrc_on[] =
1068	    "kwriteconfig --file kwinrc --group Windows --key AnimateMinimize --type bool true";
1069	char kwin_reconfigure[] =
1070	    "dcop --user '%s' %s kwin KWinInterface reconfigure";
1071	char *cmd, *cmd2, *out, *user = NULL, *sess;
1072	int len;
1073	static int anim_state = 1;
1074
1075	RAWFB_RET_VOID
1076
1077	if (ncache_keep_anims) {
1078		return;
1079	}
1080
1081	if (restore) {
1082		if (anim_state == 1) {
1083			return;
1084		}
1085
1086		user = get_user_name();
1087		if (strstr(user, "'") != NULL)  {
1088			rfbLog("invalid user: %s\n", user);
1089			free(user);
1090			return;
1091		}
1092
1093		sess = dcop_session();
1094
1095		len = strlen(kwin_reconfigure) + strlen(user) + strlen(sess) + 1;
1096		cmd = (char *) malloc(len);
1097		sprintf(cmd, kwin_reconfigure, user, sess);
1098		rfbLog("\n");
1099		rfbLog("Restoring KDE kwinrc settings.\n");
1100		rfbLog("\n");
1101		dt_cmd(cmd);
1102		free(cmd);
1103		free(user);
1104		free(sess);
1105		anim_state = 1;
1106		return;
1107	} else {
1108		if (anim_state == 0) {
1109			return;
1110		}
1111		anim_state = 0;
1112	}
1113
1114	user = get_user_name();
1115	if (strstr(user, "'") != NULL)  {
1116		rfbLog("invalid user: %s\n", user);
1117		free(user);
1118		return;
1119	}
1120	out = cmd_output(query_setting);
1121
1122
1123	if (!out || strstr(out, "false")) {
1124		rfbLog("\n");
1125		rfbLog("********************************************************\n");
1126		rfbLog("KDE kwinrc AnimateMinimize is false. Good.\n");
1127		rfbLog("********************************************************\n");
1128		rfbLog("\n");
1129		free(user);
1130		return;
1131	}
1132
1133	rfbLog("\n");
1134	rfbLog("********************************************************\n");
1135	rfbLog("To improve the -ncache client-side caching performance\n");
1136	rfbLog("temporarily setting KDE kwinrc AnimateMinimize to false.\n");
1137	rfbLog("It will be reset for the next session or when VNC client\n");
1138	rfbLog("disconnects.  Or you can use the Control Center GUI to\n");
1139	rfbLog("change it now (toggle its setting a few times):\n");
1140	rfbLog("   Desktop -> Window Behavior -> Moving\n");
1141	rfbLog("********************************************************\n");
1142	rfbLog("\n");
1143
1144	set_env("DISPLAY", DisplayString(dpy));
1145
1146	sess = dcop_session();
1147	len = strlen(kwin_reconfigure) + strlen(user) + strlen(sess) + 1;
1148	cmd = (char *) malloc(len);
1149	sprintf(cmd, kwin_reconfigure, user, sess);
1150
1151	len = 1 + strlen("sleep 10") + 2 + strlen(kwinrc_off) + 2 + strlen(cmd) + 2 + strlen("sleep 5") + 2 + strlen(kwinrc_on) + 3 + 1;
1152	cmd2 = (char *) malloc(len);
1153
1154	sprintf(cmd2, "(sleep 10; %s; %s; sleep 5; %s) &", kwinrc_off, cmd, kwinrc_on);
1155
1156	dt_cmd(cmd2);
1157	free(cmd);
1158	free(cmd2);
1159	free(user);
1160	free(sess);
1161#endif	/* NO_X11 */
1162}
1163
1164void gnome_no_animate(void) {
1165	;
1166}
1167
1168static pid_t solid_macosx_pid = 0;
1169extern char macosx_solid_background[];
1170
1171static void solid_macosx(int restore) {
1172	char tmp[] = "/tmp/macosx_solid_background.XXXXXX";
1173	pid_t pid, parent = getpid();
1174
1175	if (restore) {
1176		rfbLog("restore pid: %d\n", (int) solid_macosx_pid);
1177		if (solid_macosx_pid > 0) {
1178#if 0
1179			int i, status;
1180#endif
1181			rfbLog("kill -TERM macosx_solid_background helper pid: %d\n", (int) solid_macosx_pid);
1182			kill(solid_macosx_pid, SIGTERM);
1183#if 0
1184#if LIBVNCSERVER_HAVE_SYS_WAIT_H
1185#if LIBVNCSERVER_HAVE_WAITPID
1186			for (i=0; i < 7; i++) {
1187				usleep(1000 * 1000);
1188				waitpid(solid_macosx_pid, &status, WNOHANG);
1189				if (kill(solid_macosx_pid, 0) != 0) {
1190					break;
1191				}
1192			}
1193#endif
1194#endif
1195#endif
1196			solid_macosx_pid = 0;
1197		}
1198		return;
1199	}
1200	if (no_external_cmds || !cmd_ok("dt")) {
1201		return;
1202	}
1203#if LIBVNCSERVER_HAVE_FORK
1204	pid = fork();
1205
1206	if (pid == -1) {
1207		perror("fork");
1208		return;
1209	}
1210	if (pid == 0) {
1211		int fd = mkstemp(tmp);
1212#if LIBVNCSERVER_HAVE_SETSID
1213		setsid();
1214#else
1215		setpgrp();
1216#endif
1217		if (fd >= 0) {
1218			char num[32];
1219			write(fd, macosx_solid_background, strlen(macosx_solid_background));
1220			close(fd);
1221			sprintf(num, "%d", (int) parent);
1222			set_env("SS_WATCH_PID", num);
1223			execlp("/bin/sh", "/bin/sh", tmp, (char *) NULL);
1224		}
1225		exit(1);
1226	}
1227	solid_macosx_pid = pid;
1228	rfbLog("macosx_solid_background helper pid: %d\n", (int) solid_macosx_pid);
1229	usleep(2750 * 1000);
1230	unlink(tmp);
1231#endif
1232}
1233
1234char *guess_desktop(void) {
1235#if NO_X11
1236	RAWFB_RET("root")
1237	return "root";
1238#else
1239	Atom prop;
1240
1241	RAWFB_RET("root")
1242
1243	if (wmdt_str && *wmdt_str != '\0') {
1244		char *s = wmdt_str;
1245		lowercase(s);
1246		if (strstr(s, "xfce")) {
1247			return "xfce";
1248		}
1249		if (strstr(s, "gnome") || strstr(s, "metacity")) {
1250			return "gnome";
1251		}
1252		if (strstr(s, "kde") || strstr(s, "kwin")) {
1253			return "kde";
1254		}
1255		if (strstr(s, "cde")) {
1256			return "cde";
1257		}
1258		return "root";
1259	}
1260
1261	if (! dpy) {
1262		return "";
1263	}
1264
1265	prop = XInternAtom(dpy, "XFCE_DESKTOP_WINDOW", True);
1266	if (prop != None) return "xfce";
1267
1268	/* special case windowmaker */
1269	prop = XInternAtom(dpy, "_WINDOWMAKER_WM_PROTOCOLS", True);
1270	if (prop != None)  return "root";
1271
1272	prop = XInternAtom(dpy, "_WINDOWMAKER_COMMAND", True);
1273	if (prop != None) return "root";
1274
1275	prop = XInternAtom(dpy, "NAUTILUS_DESKTOP_WINDOW_ID", True);
1276	if (prop != None) return "gnome";
1277
1278	prop = XInternAtom(dpy, "KWIN_RUNNING", True);
1279	if (prop != None) {
1280		prop = XInternAtom(dpy, "_KDE_RUNNING", True);
1281		if (prop != None) {
1282			prop = XInternAtom(dpy, "KDE_DESKTOP_WINDOW", True);
1283			if (prop != None) return "kde";
1284		}
1285	}
1286
1287	prop = XInternAtom(dpy, "_MOTIF_WM_INFO", True);
1288	if (prop != None) {
1289		prop = XInternAtom(dpy, "_DT_WORKSPACE_LIST", True);
1290		if (prop != None) return "cde";
1291	}
1292	return "root";
1293#endif	/* NO_X11 */
1294}
1295
1296XImage *solid_image(char *color) {
1297#if NO_X11
1298	RAWFB_RET(NULL)
1299	return NULL;
1300#else
1301	XImage *image = NULL;
1302	unsigned long pixel = 0;
1303	int x, y;
1304
1305	RAWFB_RET(NULL)
1306
1307	if (!color) {
1308		color = last_color;
1309	}
1310
1311	if (!color) {
1312		return NULL;
1313	}
1314
1315	image = XGetImage(dpy, rootwin, 0, 0, wdpy_x, wdpy_y, AllPlanes,
1316	    ZPixmap);
1317
1318	if (!image) {
1319		return NULL;
1320	}
1321	pixel = get_pixel(color);
1322
1323	for (y=0; y<wdpy_y; y++) {
1324		for (x=0; x<wdpy_x; x++) {
1325			XPutPixel(image, x, y, pixel);
1326		}
1327	}
1328	return image;
1329#endif	/* NO_X11 */
1330}
1331
1332void solid_bg(int restore) {
1333	static int desktop = -1;
1334	static int solid_on = 0;
1335	static char *prev_str;
1336	char *dtname, *color;
1337
1338	if (started_as_root == 1 && users_list) {
1339		/* we are still root, don't try. */
1340		return;
1341	}
1342
1343	if (macosx_console) {
1344		solid_macosx(restore);
1345		return;
1346	}
1347
1348	RAWFB_RET_VOID
1349
1350	if (restore) {
1351		if (! solid_on) {
1352			return;
1353		}
1354		if (desktop == 0) {
1355			solid_root(NULL);
1356		} else if (desktop == 1) {
1357			solid_gnome(NULL);
1358		} else if (desktop == 2) {
1359			solid_kde(NULL);
1360		} else if (desktop == 3) {
1361			solid_cde(NULL);
1362		} else if (desktop == 4) {
1363			solid_xfce(NULL);
1364		}
1365		solid_on = 0;
1366		return;
1367	}
1368	if (! solid_str) {
1369		return;
1370	}
1371	if (solid_on && !strcmp(prev_str, solid_str)) {
1372		return;
1373	}
1374	if (strstr(solid_str, "guess:") == solid_str
1375	    || !strchr(solid_str, ':')) {
1376		dtname = guess_desktop();
1377		rfbLog("guessed desktop: %s\n", dtname);
1378	} else {
1379		if (strstr(solid_str, "gnome:") == solid_str) {
1380			dtname = "gnome";
1381		} else if (strstr(solid_str, "kde:") == solid_str) {
1382			dtname = "kde";
1383		} else if (strstr(solid_str, "cde:") == solid_str) {
1384			dtname = "cde";
1385		} else if (strstr(solid_str, "xfce:") == solid_str) {
1386			dtname = "xfce";
1387		} else {
1388			dtname = "root";
1389		}
1390	}
1391
1392	color = strchr(solid_str, ':');
1393	if (! color) {
1394		color = solid_str;
1395	} else {
1396		color++;
1397		if (*color == '\0') {
1398			color = solid_default;
1399		}
1400	}
1401	if (last_color) {
1402		free(last_color);
1403	}
1404	last_color = strdup(color);
1405
1406	if (!strcmp(dtname, "gnome")) {
1407		desktop = 1;
1408		solid_gnome(color);
1409	} else if (!strcmp(dtname, "kde")) {
1410		desktop = 2;
1411		solid_kde(color);
1412	} else if (!strcmp(dtname, "cde")) {
1413		desktop = 3;
1414		solid_cde(color);
1415	} else if (!strcmp(dtname, "xfce")) {
1416		desktop = 4;
1417		solid_xfce(color);
1418	} else {
1419		desktop = 0;
1420		solid_root(color);
1421	}
1422	if (prev_str) {
1423		free(prev_str);
1424	}
1425	prev_str = strdup(solid_str);
1426	solid_on = 1;
1427}
1428
1429
1430