1 /* GKrellM
2 |  Copyright (C) 1999-2019 Bill Wilson
3 |
4 |  Author:  Bill Wilson    billw@gkrellm.net
5 |  Latest versions might be found at:  http://gkrellm.net
6 |
7 |
8 |  GKrellM is free software: you can redistribute it and/or modify it
9 |  under the terms of the GNU General Public License as published by
10 |  the Free Software Foundation, either version 3 of the License, or
11 |  (at your option) any later version.
12 |
13 |  GKrellM is distributed in the hope that it will be useful, but WITHOUT
14 |  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 |  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
16 |  License for more details.
17 |
18 |  You should have received a copy of the GNU General Public License
19 |  along with this program. If not, see http://www.gnu.org/licenses/
20 |
21 |
22 |  Additional permission under GNU GPL version 3 section 7
23 |
24 |  If you modify this program, or any covered work, by linking or
25 |  combining it with the OpenSSL project's OpenSSL library (or a
26 |  modified version of that library), containing parts covered by
27 |  the terms of the OpenSSL or SSLeay licenses, you are granted
28 |  additional permission to convey the resulting work.
29 |  Corresponding Source for a non-source form of such a combination
30 |  shall include the source code for the parts of OpenSSL used as well
31 |  as that of the covered work.
32 */
33 
34 #include "gkrellm.h"
35 #include "gkrellm-private.h"
36 
37 #include <errno.h>
38 #if !defined(F_TLOCK)
39 #include <sys/file.h>
40 #endif
41 
42 #include <pwd.h>
43 #include <sys/types.h>
44 
45 #include <gdk/gdkx.h>
46 #include <X11/Xmd.h>
47 #include <X11/SM/SMlib.h>
48 #include <X11/Xatom.h>
49 
50 #define	_NET_WM_STATE_REMOVE	0
51 #define	_NET_WM_STATE_ADD		1
52 #define	_NET_WM_STATE_TOGGLE	2
53 
54 
55 static Pixmap	root_xpixmap	= None;
56 static GdkGC	*trans_gc;
57 
58 static gchar 	*client_id;
59 
60 
61 void
62 gkrellm_winop_reset(void)
63 	{
64 	root_xpixmap = None;
65 	}
66 
67 
68 static void
69 cb_smc_save_yourself(SmcConn smc_conn, SmPointer client_data, gint save_type,
70 			gboolean shutdown, gint interact_style, gboolean fast)
71 	{
72 	gkrellm_save_all();
73 	SmcSaveYourselfDone(smc_conn, True);
74 	}
75 
76 static void
77 cb_smc_die(SmcConn smc_conn, SmPointer client_data)
78 	{
79 	SmcCloseConnection(smc_conn, 0, NULL);
80 	gtk_main_quit();
81 	}
82 
83 static void
84 cb_smc_save_complete(SmcConn smc_conn, SmPointer client_data)
85 	{
86 	}
87 
88 static void
89 cb_smc_shutdown_cancelled(SmcConn smc_conn, SmPointer client_data)
90 	{
91 	}
92 
93 static void
94 cb_ice_connection_messages(IceConn ice_connection, gint source,
95 		GdkInputCondition condition)
96 	{
97 	IceProcessMessages(ice_connection, NULL, NULL);
98 	}
99 
100 static void
101 smc_connect(gint argc, gchar **argv)
102 	{
103 	SmProp			userid, program, restart, clone, pid, *props[6];
104 #if 0
105 	SmProp			restart_style;
106 	CARD8			restartstyle;
107 	SmPropValue		restart_style_val;
108 #endif
109 	SmPropValue		userid_val, pid_val;
110 	SmcCallbacks	*callbacks;
111 	SmcConn			smc_connection;
112 	IceConn			ice_connection;
113 	struct passwd	*pwd;
114 	uid_t			uid;
115 	gchar			error_string[256], pid_str[16], userid_string[256];
116 	gulong			mask;
117 	gint			i, j;
118 
119 	/* Session manager callbacks
120 	*/
121 	callbacks = g_new0(SmcCallbacks, 1);
122 	callbacks->save_yourself.callback = cb_smc_save_yourself;
123 	callbacks->die.callback = cb_smc_die;
124 	callbacks->save_complete.callback = cb_smc_save_complete;
125 	callbacks->shutdown_cancelled.callback = cb_smc_shutdown_cancelled;
126 
127 	mask = SmcSaveYourselfProcMask | SmcDieProcMask | SmcSaveCompleteProcMask
128 				| SmcShutdownCancelledProcMask;
129 
130 	smc_connection = SmcOpenConnection(NULL /* SESSION_MANAGER env variable */,
131 						NULL /* share ICE connection */,
132 						SmProtoMajor, SmProtoMinor, mask,
133 						callbacks,
134 						_GK.session_id, &client_id,
135 						sizeof(error_string), error_string);
136 	g_free(callbacks);
137 	if (!smc_connection)
138 		return;
139 
140 	gdk_set_sm_client_id(client_id);
141 
142 	/* Session manager properties - 4 are required.
143 	*/
144 	userid.name = SmUserID;
145 	userid.type = SmARRAY8;
146 	userid.num_vals = 1;
147 	userid.vals = &userid_val;
148 	uid = getuid();
149 	if ((pwd = getpwuid(uid)) != NULL)
150 		snprintf(userid_string, sizeof(userid_string), "%s", pwd->pw_name);
151 	else
152 		snprintf(userid_string, sizeof(userid_string), "%d", uid);
153 	userid_val.value = userid_string;
154 	userid_val.length = strlen(userid_string);
155 
156 	pid.name = SmProcessID;
157 	pid.type = SmARRAY8;
158 	pid.num_vals = 1;
159 	pid.vals = &pid_val;
160 	sprintf(pid_str, "%i", getpid());
161 	pid_val.value = (SmPointer) pid_str;
162 	pid_val.length = strlen(pid_str);
163 
164 	restart.name = SmRestartCommand;
165 	restart.type = SmLISTofARRAY8;
166 	restart.vals = g_new0(SmPropValue, argc + 2);
167 	j = 0;
168 	for (i = 0; i < argc; ++i) {
169 		if ( strcmp(argv[i], "--sm-client-id") ) {
170 			restart.vals[j].value = (SmPointer) argv[i];
171 			restart.vals[j++].length = strlen(argv[i]);
172 		} else
173 			i++;
174 	}
175 	restart.vals[j].value = (SmPointer) "--sm-client-id";
176 	restart.vals[j++].length = strlen("--sm-client-id");
177 	restart.vals[j].value = (SmPointer) client_id;
178 	restart.vals[j++].length = strlen(client_id);
179 	restart.num_vals = j;
180 
181 #if 0
182 	restartstyle = SmRestartImmediately;
183 	restart_style.name = SmRestartStyleHint;
184 	restart_style.type = SmCARD8;
185 	restart_style.num_vals = 1;
186 	restart_style.vals = &restart_style_val;
187 	restart_style_val.value = (SmPointer) &restartstyle;
188 	restart_style_val.length = 1;
189 #endif
190 
191 	clone.name = SmCloneCommand;
192 	clone.type = SmLISTofARRAY8;
193 	clone.vals = restart.vals;
194 	clone.num_vals = restart.num_vals - 2;
195 
196 	program.name = SmProgram;
197 	program.type = SmARRAY8;
198 	program.vals = restart.vals;
199 	program.num_vals = 1;
200 
201 	props[0] = &program;
202 	props[1] = &userid;
203 	props[2] = &restart;
204 	props[3] = &clone;
205 	props[4] = &pid;
206 #if 0
207 	/* Make this an option? */
208 	props[5] = &restart_style;
209 	SmcSetProperties(smc_connection, 6, props);
210 #else
211 	SmcSetProperties(smc_connection, 5, props);
212 #endif
213 
214 	g_free(restart.vals);
215 
216 	ice_connection = SmcGetIceConnection(smc_connection);
217 	gdk_input_add(IceConnectionNumber(ice_connection), GDK_INPUT_READ,
218 				(GdkInputFunction) cb_ice_connection_messages, ice_connection);
219 	}
220 
221 static void
222 net_wm_state(gchar *hint, gboolean state)
223 	{
224 	XEvent	xev;
225 
226 	xev.type = ClientMessage;
227 	xev.xclient.type = ClientMessage;
228 	xev.xclient.window = GDK_WINDOW_XWINDOW(gkrellm_get_top_window()->window);
229 	xev.xclient.message_type = gdk_x11_get_xatom_by_name("_NET_WM_STATE");
230 	xev.xclient.format = 32;
231 	xev.xclient.data.l[0] = state ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
232 	xev.xclient.data.l[1] = gdk_x11_get_xatom_by_name(hint);
233 	xev.xclient.data.l[2] = 0;
234 
235 	XSendEvent(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
236 				False, SubstructureNotifyMask|SubstructureRedirectMask, &xev);
237 	}
238 
239 void
240 gkrellm_winop_state_skip_taskbar(gboolean state)
241 	{
242 	if (!_GK.is_dock_type)
243 		net_wm_state("_NET_WM_STATE_SKIP_TASKBAR", state);
244 	}
245 
246 void
247 gkrellm_winop_state_skip_pager(gboolean state)
248 	{
249 	if (!_GK.is_dock_type)
250 		net_wm_state("_NET_WM_STATE_SKIP_PAGER", state);
251 	}
252 
253 void
254 gkrellm_winop_state_above(gboolean state)
255 	{
256 	net_wm_state("_NET_WM_STATE_ABOVE", state);
257 	/* Apparently KDE 3.1.0 and possibly below does not implement
258 	 * _NET_WM_STATE_ABOVE but _NET_WM_STATE_STAYS_ON_TOP that implies
259 	 * approximately the same thing
260 	 */
261 	net_wm_state("_NET_WM_STATE_STAYS_ON_TOP", state);
262 	}
263 
264 void
265 gkrellm_winop_state_below(gboolean state)
266 	{
267 	net_wm_state("_NET_WM_STATE_BELOW", state);
268 	}
269 
270 static FILE		*f_lock;
271 
272 static gboolean
273 _gkrellm_get_lock(void)
274 	{
275 	gchar	*lock_dir, *lock_file, *display, *s;
276 	gchar	buf[32];
277 
278 	snprintf(buf, sizeof(buf), "LCK..gkrellm");
279 
280 #if defined(F_TLOCK)
281 	lock_dir = g_strdup_printf("/var/lock/gkrellm-%d", (gint) getuid());
282 	if (!g_file_test(lock_dir, G_FILE_TEST_IS_DIR))
283 		mkdir(lock_dir, 0755);
284 
285 	lock_file = gkrellm_make_config_file_name(lock_dir, buf);
286 	g_free(lock_dir);
287 	display = XDisplayName(NULL);
288 	if (display)
289 		{
290 		s = g_strconcat(lock_file, "_", display, NULL);
291 		g_free(lock_file);
292 		lock_file = s;
293 		}
294 	f_lock = fopen(lock_file, "w+");	/* buffering does not apply here */
295 	g_free(lock_file);
296 	if (   f_lock
297 		&& lockf(fileno(f_lock), F_TLOCK, 0) != 0
298 		&& errno == EAGAIN
299 	   )
300 		return FALSE;
301 	if (f_lock)
302 		{
303 		fprintf(f_lock, "%10d\n", (gint) getpid());
304 		fflush(f_lock);
305 		}
306 #endif
307 	return TRUE;
308 	}
309 
310 enum
311 	{
312 	STRUT_LEFT = 0,
313 	STRUT_RIGHT = 1,
314 	STRUT_TOP = 2,
315 	STRUT_BOTTOM = 3,
316 	STRUT_LEFT_START = 4,
317 	STRUT_LEFT_END = 5,
318 	STRUT_RIGHT_START = 6,
319 	STRUT_RIGHT_END = 7,
320 	STRUT_TOP_START = 8,
321 	STRUT_TOP_END = 9,
322 	STRUT_BOTTOM_START = 10,
323 	STRUT_BOTTOM_END = 11
324 	};
325 
326 static Atom net_wm_strut_partial = None;
327 static Atom net_wm_strut = None;
328 
329 void
330 gkrellm_winop_update_struts(void)
331 	{
332 	gulong	struts[12] = { 0, };
333 	Display	*display;
334 	Window	window;
335 	gint	width;
336 	gint	height;
337 
338 	if (!_GK.is_dock_type)
339 		return;
340 
341 	display = GDK_WINDOW_XDISPLAY(gkrellm_get_top_window()->window);
342 	window  = GDK_WINDOW_XWINDOW(gkrellm_get_top_window()->window);
343 
344 	if (net_wm_strut_partial == None)
345 		{
346 		net_wm_strut_partial
347 				= XInternAtom(display, "_NET_WM_STRUT_PARTIAL", False);
348 		}
349 	if (net_wm_strut == None)
350 		{
351 		net_wm_strut = XInternAtom(display, "_NET_WM_STRUT", False);
352 		}
353 
354 	gtk_window_get_size(GTK_WINDOW(gkrellm_get_top_window()), &width, &height);
355 
356 	if (_GK.x_position == 0)
357 		{
358 		struts[STRUT_LEFT] = width;
359 		struts[STRUT_LEFT_START] = _GK.y_position;
360 		struts[STRUT_LEFT_END] = _GK.y_position + height;
361 		}
362 	else if (_GK.x_position == _GK.w_display - width)
363 		{
364 		struts[STRUT_RIGHT] = width;
365 		struts[STRUT_RIGHT_START] = _GK.y_position;
366 		struts[STRUT_RIGHT_END] = _GK.y_position + height;
367 		}
368 
369 	gdk_error_trap_push();
370 	XChangeProperty (display, window, net_wm_strut,
371 					XA_CARDINAL, 32, PropModeReplace,
372 					(guchar *) &struts, 4);
373 	XChangeProperty (display, window, net_wm_strut_partial,
374 					XA_CARDINAL, 32, PropModeReplace,
375 					(guchar *) &struts, 12);
376 	gdk_error_trap_pop();
377 	}
378 
379 void
380 gkrellm_winop_options(gint argc, gchar **argv)
381 	{
382 	Display		*display;
383 	Window		window;
384 	Atom		atoms[4];
385 	gint		n = 0;
386 
387 	if (   !_GK.allow_multiple_instances_real
388 		&& !_GK.force_host_config
389 		&& !_gkrellm_get_lock()
390 	   )
391 		{
392 		g_message("gkrellm: %s\n",
393 			_("Exiting because multiple instances option is off.\n"));
394 		exit(0);
395 		}
396 	smc_connect(argc, argv);
397 	display = GDK_WINDOW_XDISPLAY(gkrellm_get_top_window()->window);
398 	window  = GDK_WINDOW_XWINDOW(gkrellm_get_top_window()->window);
399 
400 	/* Set window type or list of states using standard EWMH hints.
401 	|  See http://www.freedesktop.org/
402 	|  At least KDE3 and GNOME2 are EWMH compliant.
403 	*/
404 	if (_GK.dock_type && !_GK.command_line_decorated)
405 		{
406 		atoms[0] = XInternAtom(display, "_NET_WM_WINDOW_TYPE_DOCK", False);
407 
408 		XChangeProperty(display, window,
409 					XInternAtom(display, "_NET_WM_WINDOW_TYPE", False),
410 					XA_ATOM, 32, PropModeReplace, (guchar *) atoms, 1);
411 		_GK.is_dock_type = TRUE;
412 		_GK.state_skip_taskbar = FALSE;
413 		_GK.state_skip_pager = FALSE;
414 		}
415 	if (_GK.state_skip_taskbar)
416 		{
417 		atoms[n] = XInternAtom(display, "_NET_WM_STATE_SKIP_TASKBAR", False);
418 		++n;
419 		}
420 	if (_GK.state_skip_pager)
421 		{
422 		atoms[n] = XInternAtom(display, "_NET_WM_STATE_SKIP_PAGER", False);
423 		++n;
424 		}
425 	if (_GK.state_above)
426 		{
427 		atoms[n++] = XInternAtom(display, "_NET_WM_STATE_ABOVE", False);
428 		/* see gkrellm_winop_state_above() */
429 		atoms[n++] = XInternAtom(display, "_NET_WM_STATE_STAYS_ON_TOP", False);
430 		_GK.state_below = FALSE;
431 		}
432 	if (_GK.state_below)
433 		{
434 		atoms[n] = XInternAtom(display, "_NET_WM_STATE_BELOW", False);
435 		++n;
436 		}
437 	if (n > 0)
438 		XChangeProperty(display, window,
439 					XInternAtom(display, "_NET_WM_STATE", False),
440 					XA_ATOM, 32, PropModeReplace, (guchar *) atoms, n);
441 	}
442 
443 void
444 gkrellm_winop_withdrawn(void)
445 	{
446 	Display		*display;
447 	Window		window;
448 
449 	if (!_GK.withdrawn)
450 		return;
451 	display = GDK_WINDOW_XDISPLAY(gkrellm_get_top_window()->window);
452 	window  = GDK_WINDOW_XWINDOW(gkrellm_get_top_window()->window);
453 
454 	if (!_GK.is_dock_type)
455 		{
456 		XWMHints mywmhints;
457 		mywmhints.initial_state = WithdrawnState;
458 		mywmhints.flags=StateHint;
459 
460 		XSetWMHints(display, window, &mywmhints);
461 		}
462 	else
463 		gkrellm_message_dialog(NULL,
464 			_("Warning: -w flag is ignored when the window dock type is set"));
465 	}
466 
467   /* Use XParseGeometry, but width and height are ignored.
468   |  If GKrellM is moved, update _GK.y_position.
469   */
470 void
471 gkrellm_winop_place_gkrellm(gchar *geom)
472     {
473 	gint	place, x, y, w_gkrell, h_gkrell;
474 
475 	x = y = 0;
476 	place = XParseGeometry(geom, &x, &y,
477 				(guint *) &w_gkrell, (guint *) &h_gkrell);
478 
479 	w_gkrell = _GK.chart_width + _GK.frame_left_width + _GK.frame_right_width;
480 	h_gkrell = _GK.monitor_height + _GK.total_frame_height;
481 
482 	if (place & YNegative)
483 		y = _GK.h_display - h_gkrell + y;
484 	if (place & XNegative)
485 		x = _GK.w_display - w_gkrell + x;
486 	gdk_window_move(gkrellm_get_top_window()->window, x, y);
487 	_GK.y_position = y;
488 	_GK.x_position = x;
489 	_GK.position_valid = TRUE;
490 	gkrellm_debug(DEBUG_POSITION, "geometry moveto %d %d\n", x, y);
491 	}
492 
493 void
494 gkrellm_winop_flush_motion_events(void)
495 	{
496 	XEvent			xevent;
497 
498 	gdk_flush();
499 	while (XCheckTypedEvent(GDK_DISPLAY(), MotionNotify, &xevent))
500 		;
501 	}
502 
503   /* Check if background has changed
504   */
505 gboolean
506 gkrellm_winop_updated_background(void)
507 	{
508 	Pixmap	root_pix = None;
509 	Atom	prop, ret_type = (Atom) 0;
510 	guchar	*prop_return = NULL;
511 	gint	fmt;
512 	gulong	nitems, bytes_after;
513 
514 	if (!_GK.any_transparency)
515 		return FALSE;
516 	prop = XInternAtom(GDK_DISPLAY(), "_XROOTPMAP_ID", True);
517 	if (prop == None)
518 		return FALSE;
519 
520 	XGetWindowProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(), prop, 0L, 1L, False,
521 			AnyPropertyType, &ret_type, &fmt, &nitems, &bytes_after,
522 			&prop_return);
523 	if (prop_return && ret_type == XA_PIXMAP)
524 		{
525 		root_pix = *((Pixmap *) prop_return);
526 		XFree(prop_return);
527 		}
528 	else
529 		return FALSE;
530 
531 	if (root_pix != root_xpixmap)
532 		{
533 		root_xpixmap = root_pix;
534 		return TRUE;
535 		}
536 	return FALSE;
537 	}
538 
539 gboolean
540 gkrellm_winop_draw_rootpixmap_onto_transparent_chart(GkrellmChart *cp)
541 	{
542 	Window			child;
543 	GkrellmMargin	*m;
544 	gint			x, y;
545 
546 	if (   root_xpixmap == None || trans_gc == NULL || !cp->transparency
547 		|| !cp->drawing_area || !cp->drawing_area->window
548 	   )
549 		return FALSE;
550 	XTranslateCoordinates(GDK_DISPLAY(),
551 			GDK_WINDOW_XWINDOW(cp->drawing_area->window),
552 			GDK_ROOT_WINDOW(),
553 			0, 0, &x, &y, &child);
554 	XSetTSOrigin(GDK_DISPLAY(), GDK_GC_XGC(trans_gc), -x, -y);
555 
556 	/* First make the chart totally transparent
557 	*/
558 	gdk_draw_rectangle(cp->bg_src_pixmap, trans_gc,
559 					TRUE, 0, 0, cp->w, cp->h);
560 
561 	/* If mode permits, stencil on non transparent parts of bg_clean_pixmap.
562 	*/
563 	if (cp->transparency == 2 && cp->bg_mask)
564 		{
565 		gdk_gc_set_clip_mask(_GK.text_GC, cp->bg_mask);
566 		gdk_draw_drawable(cp->bg_src_pixmap, _GK.text_GC,
567 				cp->bg_clean_pixmap, 0, 0, 0, 0, cp->w, cp->h);
568 		}
569 	m = &cp->style->margin;
570 	if (cp->top_spacer.pixmap)
571 		{
572 		XSetTSOrigin(GDK_DISPLAY(), GDK_GC_XGC(trans_gc), -x, -(y - m->top));
573 		gdk_draw_rectangle(cp->top_spacer.pixmap, trans_gc,
574 					TRUE, 0, 0, cp->w, cp->style->margin.top);
575 		if (cp->transparency == 2 && cp->top_spacer.mask)
576 			{
577 			gdk_gc_set_clip_mask(_GK.text_GC, cp->top_spacer.mask);
578 			gdk_draw_drawable(cp->top_spacer.pixmap, _GK.text_GC,
579 						cp->top_spacer.clean_pixmap, 0, 0, 0, 0,
580 						cp->w, cp->style->margin.top);
581 			}
582 		gtk_image_set_from_pixmap(GTK_IMAGE(cp->top_spacer.image),
583 					cp->top_spacer.pixmap, NULL);
584 		}
585 
586 	if (cp->bottom_spacer.pixmap)
587 		{
588 		XSetTSOrigin(GDK_DISPLAY(), GDK_GC_XGC(trans_gc),
589 					-x, -(y + cp->h - m->bottom));
590 		gdk_draw_rectangle(cp->bottom_spacer.pixmap, trans_gc,
591 					TRUE, 0, 0, cp->w, cp->style->margin.bottom);
592 		if (cp->transparency == 2 && cp->bottom_spacer.mask)
593 			{
594 			gdk_gc_set_clip_mask(_GK.text_GC, cp->bottom_spacer.mask);
595 			gdk_draw_drawable(cp->bottom_spacer.pixmap, _GK.text_GC,
596 						cp->bottom_spacer.clean_pixmap, 0, 0, 0, 0,
597 						cp->w, cp->style->margin.bottom);
598 			}
599 		gtk_image_set_from_pixmap(GTK_IMAGE(cp->bottom_spacer.image),
600 					cp->bottom_spacer.pixmap, NULL);
601 		}
602 	gdk_gc_set_clip_mask(_GK.text_GC, NULL);
603 	cp->bg_sequence_id += 1;
604 	return TRUE;
605 	}
606 
607 gboolean
608 gkrellm_winop_draw_rootpixmap_onto_transparent_panel(GkrellmPanel *p)
609 	{
610 	Window	child;
611 	gint	x, y;
612 
613 	if (   root_xpixmap == None || trans_gc == NULL || !p->transparency
614 		|| !p->drawing_area || !p->drawing_area->window
615 	   )
616 		return FALSE;
617 	XTranslateCoordinates(GDK_DISPLAY(),
618 			GDK_WINDOW_XWINDOW(p->drawing_area->window),
619 			GDK_ROOT_WINDOW(),
620 			0, 0, &x, &y, &child);
621 	XSetTSOrigin(GDK_DISPLAY(), GDK_GC_XGC(trans_gc), -x, -y);
622 
623 	/* First make the panel totally transparent
624 	*/
625 	gdk_draw_rectangle(p->bg_pixmap, trans_gc, TRUE, 0, 0, p->w, p->h);
626 
627 	/* If mode permits, stencil on non transparent parts of bg_clean_pixmap.
628 	*/
629 	if (p->transparency == 2 && p->bg_mask)
630 		{
631         gdk_gc_set_clip_mask(_GK.text_GC, p->bg_mask);
632         gdk_draw_drawable(p->bg_pixmap, _GK.text_GC, p->bg_clean_pixmap,
633                     0, 0, 0, 0, p->w, p->h);
634         gdk_gc_set_clip_mask(_GK.text_GC, NULL);
635 		}
636 	return TRUE;
637 	}
638 
639 static void
640 draw_rootpixmap_onto_transparent_spacers(GkrellmMonitor *mon, gint xr, gint yr)
641 	{
642 	GkrellmMonprivate	*mp = mon->privat;
643 	gint				x, y;
644 
645 	if (mp->top_spacer.image)
646 		{
647 		x = xr + mp->top_spacer.image->allocation.x;
648 		y = yr + mp->top_spacer.image->allocation.y;
649 		XSetTSOrigin(GDK_DISPLAY(), GDK_GC_XGC(trans_gc), -x, -y);
650 		gdk_draw_rectangle(mp->top_spacer.pixmap, trans_gc,
651 				TRUE, 0, 0, _GK.chart_width, mp->top_spacer.height);
652 		if (mp->top_spacer.mask)
653 			{
654 			gdk_gc_set_clip_mask(_GK.text_GC, mp->top_spacer.mask);
655 			gdk_draw_drawable(mp->top_spacer.pixmap, _GK.text_GC,
656 						mp->top_spacer.clean_pixmap, 0, 0, 0, 0,
657 						_GK.chart_width, mp->top_spacer.height);
658 			}
659 		gtk_image_set_from_pixmap(GTK_IMAGE(mp->top_spacer.image),
660 				mp->top_spacer.pixmap, NULL);
661 		}
662 	if (mp->bottom_spacer.image)
663 		{
664 		x = xr + mp->bottom_spacer.image->allocation.x;
665 		y = yr + mp->bottom_spacer.image->allocation.y;
666 		XSetTSOrigin(GDK_DISPLAY(), GDK_GC_XGC(trans_gc), -x, -y);
667 		gdk_draw_rectangle(mp->bottom_spacer.pixmap, trans_gc,
668 				TRUE, 0, 0, _GK.chart_width, mp->bottom_spacer.height);
669 		if (mp->bottom_spacer.mask)
670 			{
671 			gdk_gc_set_clip_mask(_GK.text_GC, mp->bottom_spacer.mask);
672 			gdk_draw_drawable(mp->bottom_spacer.pixmap, _GK.text_GC,
673 						mp->bottom_spacer.clean_pixmap, 0, 0, 0, 0,
674 						_GK.chart_width, mp->bottom_spacer.height);
675 			}
676 		gtk_image_set_from_pixmap(GTK_IMAGE(mp->bottom_spacer.image),
677 				mp->bottom_spacer.pixmap, NULL);
678 		}
679 	}
680 
681 void
682 gkrellm_winop_apply_rootpixmap_transparency(void)
683 	{
684 	Window			child;
685 	GtkWidget		*top_window = gkrellm_get_top_window();
686 	Atom			prop,
687 					ret_type	= (Atom) 0;
688 	GList			*list;
689 	GkrellmMonitor	*mon;
690 	GkrellmChart	*cp;
691 	GkrellmPanel	*p;
692 	guchar			*prop_return = NULL;
693 	gint			fmt;
694 	gulong			nitems, bytes_after;
695 	gint			depth_visual;
696 	Window			root_return;
697 	guint			w_ret, h_ret, bw_ret, depth_ret;
698 	gint			x_ret, y_ret, x_root, y_root;
699 
700 	if (!_GK.any_transparency)
701 		return;
702 	prop = XInternAtom(GDK_DISPLAY(), "_XROOTPMAP_ID", True);
703 	if (prop == None)
704 		return;
705 	XGetWindowProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(), prop, 0L, 1L, False,
706 			AnyPropertyType, &ret_type, &fmt, &nitems, &bytes_after,
707 			&prop_return);
708 	if (prop_return && ret_type == XA_PIXMAP)
709 		{
710 		root_xpixmap = *((Pixmap *) prop_return);
711 		XFree(prop_return);
712 		}
713 	if (root_xpixmap == None)
714 		return;
715 	if (trans_gc == NULL)
716 		{
717 		trans_gc = gdk_gc_new(top_window->window);
718 		gdk_gc_copy(trans_gc, _GK.draw1_GC);
719 		}
720 
721 	depth_ret = 0;
722 	depth_visual = gdk_drawable_get_visual(top_window->window)->depth;
723 	if (   !XGetGeometry(GDK_DISPLAY(), root_xpixmap, &root_return,
724 				&x_ret, &y_ret, &w_ret, &h_ret, &bw_ret, &depth_ret)
725 		|| depth_ret != depth_visual
726 	   )
727 		{
728 		root_xpixmap = None;
729 		return;
730 		}
731 
732 	/* I could use gdk_pixmap_foreign_new() and stay in the gdk domain,
733 	|  but it fails (in XGetGeometry()) if I change backgrounds.
734 	*/
735 	XSetTile(GDK_DISPLAY(), GDK_GC_XGC(trans_gc), root_xpixmap);
736 	XSetFillStyle(GDK_DISPLAY(), GDK_GC_XGC(trans_gc), FillTiled);
737 	for (list = gkrellm_get_chart_list(); list; list = list->next)
738 		{
739 		cp = (GkrellmChart *) list->data;
740 		if (!cp->transparency || !cp->shown)
741 			continue;
742 		gkrellm_winop_draw_rootpixmap_onto_transparent_chart(cp);
743 		gkrellm_refresh_chart(cp);
744 		}
745 	for (list = gkrellm_get_panel_list(); list; list = list->next)
746 		{
747 		p = (GkrellmPanel *) list->data;
748 		if (!p->transparency || !p->shown)
749 			continue;
750 		gkrellm_draw_panel_label(p);
751 		}
752 	XTranslateCoordinates(GDK_DISPLAY(),
753 			GDK_WINDOW_XWINDOW(top_window->window),
754 			GDK_ROOT_WINDOW(),
755 			0, 0, &x_root, &y_root, &child);
756 	for (list = gkrellm_monitor_list; list; list = list->next)
757 		{
758 		mon = (GkrellmMonitor *) list->data;
759 		draw_rootpixmap_onto_transparent_spacers(mon, x_root, y_root);
760 		}
761 	gdk_gc_set_clip_mask(_GK.text_GC, NULL);
762 	}
763