1 /*
2  * Copyright (c) 1999 Sasha Vasko <sasha at aftercode.net>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.   See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  *
18  */
19 
20 /*#define DO_CLOCKING      */
21 
22 #define TRUE 1
23 #define FALSE 0
24 
25 #include "../configure.h"
26 
27 #include <signal.h>
28 #include <fcntl.h>
29 #include <unistd.h>
30 #ifdef HAVE_SYS_WAIT_H
31 #include <sys/wait.h>
32 #endif
33 
34 #define DO_CLOCKING
35 #ifdef DO_CLOCKING
36 #if TIME_WITH_SYS_TIME
37 # include <sys/time.h>
38 # include <time.h>
39 #else
40 # if HAVE_SYS_TIME_H
41 #  include <sys/time.h>
42 # else
43 #  include <time.h>
44 # endif
45 #endif
46 #endif
47 
48 #include <X11/Intrinsic.h>
49 
50 #include "asapp.h"
51 #include "afterstep.h"
52 #include "screen.h"
53 #include "mystyle.h"
54 #include "background.h"
55 
56 
57 #ifdef DEBUG_BACKGROUNDS
58 #define LOG1(a)       fprintf( stderr, a );
59 #define LOG2(a,b)    fprintf( stderr, a, b );
60 #define LOG3(a,b,c)    fprintf( stderr, a, b, c );
61 #define LOG4(a,b,c,d)    fprintf( stderr, a, b, c, d );
62 #else
63 #define LOG1(a)
64 #define LOG2(a,b)
65 #define LOG3(a,b,c)
66 #define LOG4(a,b,c,d)
67 #endif
68 
69 /***************************************************/
70 /* this all should go into asimagelib/background.c */
71 static Atom   RootPixmapProperty = None;
72 
73 ASDeskBack   *
FindDeskBack(ASDeskBackArray * backs,long desk)74 FindDeskBack (ASDeskBackArray * backs, long desk)
75 {
76 	unsigned long i;
77 
78 	for (i = 0; i < backs->desks_num; i++)
79 		if (backs->desks[i].desk == desk)
80 			return &(backs->desks[i]);
81 
82 	return NULL;
83 }
84 
85 int
IsPurePixmap(ASDeskBack * back)86 IsPurePixmap (ASDeskBack * back)
87 {
88 	return (back->data_type == XA_PIXMAP && back->data.pixmap != None && back->MyStyle == None);
89 }
90 
91 void
BackgroundSetPixmap(Pixmap pix)92 BackgroundSetPixmap (Pixmap pix)
93 {
94 	Pixmap        current_root = GetRootPixmap (RootPixmapProperty);
95 
96 	if (current_root == pix)
97 	{
98 		XClearWindow (dpy, ASDefaultRoot);
99 		return;
100 	}
101 
102 	XSetWindowBackgroundPixmap (dpy, ASDefaultRoot, pix);
103 	XClearWindow (dpy, ASDefaultRoot);
104 
105 	/* we shell be setting root pixmap ID in X property with specifyed ID */
106 	if (RootPixmapProperty != None)
107 	{
108 		XChangeProperty (dpy, ASDefaultRoot, RootPixmapProperty, XA_PIXMAP, 32, PropModeReplace, (char *)&pix, 1);
109 		XFlush (dpy);						   /* so that everyone has time to process this change
110 												  * before we go ahead and delete old background
111 												*/
112 	}
113 }
114 
115 Pixmap
BackgroundSetMyStyle(char * style_name)116 BackgroundSetMyStyle (char *style_name)
117 {
118 	MyStyle      *style;
119 	Pixmap        pix = None;
120 
121 	/* get MySyle pointer */
122 	if ((style = mystyle_find (style_name)) != NULL)
123 	{
124 		LOG2 ("\nBackgroundSetMyStyle( %s ) ", style_name)
125 		if ((style->set_flags & F_BACKPIXMAP) && style->texture_type != 129)
126 		{
127 			/* we don't want to free this pixmap ourselves */
128 			BackgroundSetPixmap (style->back_icon.pix);
129 			return None;
130 		} else if (style->set_flags & F_BACKGRADIENT)
131 			pix = mystyle_make_pixmap (style, ASDefaultScrWidth, ASDefaultScrHeight, None);
132 		if (pix == None)
133 		{
134 			GC            backGC, foreGC;
135 
136 			pix = XCreatePixmap (dpy, ASDefaultRoot, 1, 1, Scr.d_depth);
137 /*			mystyle_get_global_gcs (style, &foreGC, &backGC, NULL, NULL);
138 			XDrawPoint (dpy, pix, (backGC != None) ? backGC : foreGC, 1, 1);
139   */		}
140 		BackgroundSetPixmap (pix);
141 	}
142 	/* we will need this pixmap ID later to destroy it gracefully */
143 	return pix;
144 }
145 
146 /******************************************************************/
147 /*     stuff for running external app to set root background      */
148 /******************************************************************/
149 #ifdef HAVE_SYS_WAIT_H
150 #define WAIT_CHILDREN(pstatus)  waitpid(-1, pstatus, WNOHANG)
151 #elif defined (HAVE_WAIT3)
152 #define WAIT_CHILDREN(pstatus)  wait3(pstatus, WNOHANG, NULL)
153 #else
154 #define WAIT_CHILDREN(pstatus)  (-1)
155 #endif
156 
157 int           DrawChildPID = 0;
158 void
sigchild_handler(int signum)159 sigchild_handler (int signum)
160 {
161 	int           pid;
162 	int           status;
163 
164 	signal (SIGCHLD, sigchild_handler);
165 	LOG3 ("\n%s:Entering SigChild_handler(%lu)", MyName, time (NULL)) while (1)
166 	{
167 		pid = WAIT_CHILDREN (&status);
168 		if (pid < 0 || pid == DrawChildPID)
169 			DrawChildPID = 0;
170 		if (pid == 0 || pid < 0)
171 			break;
172 	}
173 LOG3 ("\n%s:Exiting SigChild_handler(%lu)", MyName, time (NULL))}
174 
175 /*
176    This should return 0 if process of running external app to draw background completed or killed.
177    otherwise it returns > 0
178  */
179 int
CheckForDrawChild(int kill_it_to_death)180 CheckForDrawChild (int kill_it_to_death)
181 {
182 	int           i;
183 	int           status;
184 
185 	LOG3 ("\n%s:CheckingForDrawChild(%lu)....", MyName, time (NULL));
186 	if (DrawChildPID > 0)
187 	{
188 		LOG2 ("\n Child has been started with PID (%d).", DrawChildPID);
189 		if (kill_it_to_death > 0)
190 		{
191 			kill (DrawChildPID, SIGTERM);
192 			for (i = 0; i < 10; i++)		   /* give it 10 sec to terminate */
193 			{
194 				sleep (1);
195 				if (WAIT_CHILDREN (&status) <= 0)
196 					break;
197 			}
198 			if (i >= 10)
199 				kill (DrawChildPID, SIGKILL);  /* no more mercy */
200 			DrawChildPID = 0;
201 		}
202 	} else if (DrawChildPID < 0)
203 		DrawChildPID = 0;
204 
205 	LOG3 ("Done(%lu). Child PID on exit = %d.", time (NULL), DrawChildPID);
206 	return DrawChildPID;
207 }
208 
209 void
BackgroundSetCommand(char * cmd)210 BackgroundSetCommand (char *cmd)
211 {
212 	signal (SIGCHLD, sigchild_handler);
213 	if (!(DrawChildPID = fork ()))			   /* child process */
214 	{
215 		execl ("/bin/sh", "/bin/sh", "-c", cmd, (char *)NULL);
216 
217 		/* if all is fine then the thread will exit here */
218 		/* so displaying error if not                    */
219 		fprintf (stderr, "\n%s: bad luck running command [%s] to set root pixmap.\n", MyName, cmd);
220 		exit (0);							   /*thread completed */
221 	}
222 }
223 
224 void
KillOldDrawing()225 KillOldDrawing ()
226 {
227 	CheckForDrawChild (1);
228 }
229 
230 /* frees all the resources that we currently don't need :
231    - pixmaps used for MyStyle's
232  */
233 void
BackgroundCleanup(ASDeskBackArray * backs,long desk)234 BackgroundCleanup (ASDeskBackArray * backs, long desk)
235 {
236 	unsigned long i;
237 
238 	for (i = 0; i < backs->desks_num; i++)
239 		if (backs->desks[i].desk != desk && backs->desks[i].data.pixmap != None)
240 		{
241 			if (backs->desks[i].data_type != XA_PIXMAP && backs->desks[i].MyStyle != None)
242 			{
243 				XFreePixmap (dpy, backs->desks[i].data.pixmap);
244 				backs->desks[i].data.pixmap = None;
245 			}
246 		}
247 }
248 
249 
250 /******************************************************************/
251 /*        application interface                                   */
252 /******************************************************************/
253 
254 #ifdef DEBUG_BACKGROUNDS
255 void
PrintDeskBackArray(ASDeskBackArray * backs)256 PrintDeskBackArray (ASDeskBackArray * backs)
257 {
258 	int           i = 0;
259 
260 	fprintf (stderr, "\n Number of backgrounds = %d", backs->desks_num);
261 	for (i = 0; i < backs->desks_num; i++)
262 		fprintf (stderr, "\n %d. Desk #%ld type %ld data %ld, MyStyle %ld",
263 				 i, backs->desks[i].desk, backs->desks[i].data_type,
264 				 backs->desks[i].data.atom, backs->desks[i].MyStyle);
265 
266 }
267 #endif
268 
269 void
SetRootPixmapPropertyID(Atom id)270 SetRootPixmapPropertyID (Atom id)
271 {
272 	RootPixmapProperty = id;
273 }
274 
275 void
BackgroundSetForDesk(ASDeskBackArray * backs,long desk)276 BackgroundSetForDesk (ASDeskBackArray * backs, long desk)
277 {
278 	ASDeskBack   *back;
279 
280 	if (desk != 10000 && backs)
281 	{
282 		LOG2 ("\ntrying to find data for desk %d", desk) if ((back = FindDeskBack (backs, desk)) != NULL)
283 		{
284 			LOG3 ("\ndesk %d found with pixmap %ld", desk, back->data.pixmap) KillOldDrawing ();
285 			if ((back->data_type == XA_PIXMAP) || (back->MyStyle != None && back->data.pixmap != None))
286 				BackgroundSetPixmap (back->data.pixmap);
287 			else
288 			{
289 				char         *text = XGetAtomName (dpy, ((back->MyStyle != None) ? back->MyStyle : back->data.atom));
290 
291 				LOG3 ("\ndesk %d has data [%s]", desk, text) if (text != NULL)
292 				{
293 					if (back->MyStyle != None)
294 						back->data.pixmap = BackgroundSetMyStyle (text);
295 					else
296 						BackgroundSetCommand (text);
297 
298 					XFree (text);
299 				}
300 			}
301 		}
302 		BackgroundCleanup (backs, desk);
303 	}
304 }
305 
306 void
FreeDeskBackArray(ASDeskBackArray * backs,int free_pixmaps)307 FreeDeskBackArray (ASDeskBackArray * backs, int free_pixmaps)
308 {
309 	if (backs->desks)
310 	{
311 		int           i, k;
312 
313 		for (i = 0; i < backs->desks_num && free_pixmaps; i++)
314 		{
315 
316 			if (backs->desks[i].data_type == XA_PIXMAP || backs->desks[i].MyStyle)
317 				if (backs->desks[i].data.pixmap != None)
318 				{
319 					if (backs->desks[i].desk == Scr.CurrentDesk && RootPixmapProperty != None)
320 					{
321 						Pixmap        pix = None;
322 
323 						XChangeProperty (dpy, ASDefaultRoot, RootPixmapProperty, XA_PIXMAP, 32, PropModeReplace,
324 										 (char *)&pix, 1);
325 						XFlush (dpy);
326 						/* so that everyone has time to process this change
327 						 * before we go ahead and delet old background
328 						 */
329 					}
330 					/* checking if we have already freed this pixmap */
331 					for (k = 0; k < i; k++)
332 						if (backs->desks[k].data_type == XA_PIXMAP || backs->desks[k].MyStyle)
333 							if (backs->desks[k].data.pixmap == backs->desks[i].data.pixmap)
334 								break;
335 
336 					if (k >= i)
337 						XFreePixmap(dpy,backs->desks[i].data.pixmap);
338 				}
339 		}
340 		free (backs->desks);
341 		backs->desks = NULL;
342 	}
343 	backs->desks_num = 0;
344 }
345 
346 ASDeskBackArray *
UpdateDeskBackArray(ASDeskBackArray * old_info,ASDeskBackArray * new_info)347 UpdateDeskBackArray (ASDeskBackArray * old_info, ASDeskBackArray * new_info)
348 {
349 	ASDeskBackArray *updated;
350 	int           i, k;
351 
352 	updated = (ASDeskBackArray *) safemalloc (sizeof (ASDeskBackArray));
353 	updated->desks_num = new_info->desks_num;
354 	for (k = 0; k < old_info->desks_num; k++)
355 		if (FindDeskBack (new_info, old_info->desks[k].desk) == NULL)
356 			updated->desks_num++;
357 
358 	updated->desks = (ASDeskBack *) safemalloc (sizeof (ASDeskBack) * updated->desks_num);
359 	for (i = 0; i < new_info->desks_num; i++)
360 		updated->desks[i] = new_info->desks[i];
361 
362 	for (k = 0; k < old_info->desks_num; k++)
363 		if (FindDeskBack (new_info, old_info->desks[k].desk) == NULL)
364 		{
365 			updated->desks[i] = old_info->desks[k];
366 			i++;
367 		}
368 
369 	return updated;
370 }
371 
372 /******************************************************************/
373 /*          Backgrounds info X property manipulation              */
374 /******************************************************************/
375 
376 void
SetBackgroundsProperty(ASDeskBackArray * backs,Atom property)377 SetBackgroundsProperty (ASDeskBackArray * backs, Atom property)
378 {
379 	if (backs == NULL || property == None)
380 		return;
381 	if (backs->desks == NULL || backs->desks_num <= 0)
382 		return;
383 	set_as_property (ASDefaultRoot, property, (unsigned long *)(backs->desks), backs->desks_num * sizeof (ASDeskBack),
384 					 (1 << 8) + 0);
385 }
386 
387 void
GetBackgroundsProperty(ASDeskBackArray * backs,Atom property)388 GetBackgroundsProperty (ASDeskBackArray * backs, Atom property)
389 {
390 	unsigned long version;
391 	unsigned long *data;
392 
393 	if (backs == NULL || property == None)
394 		return;
395 	if (backs->desks != NULL || backs->desks_num > 0)
396 		FreeDeskBackArray (backs, FALSE);
397 	data = get_as_property (ASDefaultRoot, property, (size_t *) & (backs->desks_num), &version);
398 	if (data && backs->desks_num > 0 && version == (1 << 8) + 0)
399 	{
400 		backs->desks = (ASDeskBack *) safemalloc (backs->desks_num);
401 		memcpy (backs->desks, data, backs->desks_num);
402 		XFree (data);
403 		backs->desks_num = backs->desks_num / sizeof (ASDeskBack);
404 	}
405 }
406