1 /*
2  * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
3  * Copyright (C) 2004-2021 Kim Woelders
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy
6  * of this software and associated documentation files (the "Software"), to
7  * deal in the Software without restriction, including without limitation the
8  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9  * sell copies of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies of the Software, its documentation and marketing & publicity
14  * materials, and acknowledgment shall be given in the documentation, materials
15  * and software packages that this Software was used.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20  * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24 #include "config.h"
25 
26 #include <time.h>
27 #include <X11/Xlib.h>
28 
29 #include "E.h"
30 #include "backgrounds.h"
31 #include "desktops.h"
32 #include "dialog.h"
33 #include "ecompmgr.h"
34 #include "eimage.h"
35 #include "emodule.h"
36 #include "file.h"
37 #include "iclass.h"
38 #include "list.h"
39 #include "settings.h"
40 #include "tclass.h"
41 #include "timers.h"
42 #include "xwin.h"
43 
44 typedef struct {
45    char               *file;
46    EImage             *im;
47    char                keep_aspect;
48    int                 xjust, yjust;
49    int                 xperc, yperc;
50 } BgPart;
51 
52 struct _background {
53    dlist_t             list;
54    char               *name;
55    EX_Pixmap           pmap;
56    time_t              last_viewed;
57    unsigned int        bg_solid;
58    char                bg_tile;
59    BgPart              bg;
60    BgPart              top;
61    char                external;
62    char                keepim;
63    char                referenced;
64    unsigned int        ref_count;
65    unsigned int        seq_no;
66 };
67 
68 static              LIST_HEAD(bg_list);
69 
70 static Timer       *bg_timer = NULL;
71 static unsigned int bg_seq_no = 0;
72 
73 #define N_BG_ASSIGNED 32
74 static Background  *bg_assigned[N_BG_ASSIGNED];
75 
76 static char        *
_BackgroundGetFile(char ** ptr)77 _BackgroundGetFile(char **ptr)
78 {
79    char               *path = *ptr;
80 
81    if (isabspath(path))
82       goto done;
83 
84    path = ThemeFileFind(path, FILE_TYPE_BACKGROUND);
85    if (!path)
86       goto done;
87    EFREE_SET(*ptr, path);
88  done:
89    return path;
90 }
91 
92 static char        *
_BackgroundGetBgFile(Background * bg)93 _BackgroundGetBgFile(Background * bg)
94 {
95    if (!bg || !bg->bg.file)
96       return NULL;
97    return _BackgroundGetFile(&bg->bg.file);
98 }
99 
100 static char        *
_BackgroundGetFgFile(Background * bg)101 _BackgroundGetFgFile(Background * bg)
102 {
103    if (!bg || !bg->top.file)
104       return NULL;
105    return _BackgroundGetFile(&bg->top.file);
106 }
107 
108 char               *
BackgroundGetUniqueString(Background * bg)109 BackgroundGetUniqueString(Background * bg)
110 {
111    static const char   chmap[] =
112       "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_";
113    char                s[256], *f;
114    int                 r, g, b;
115    int                 n1, n2, n3, n4, n5, f1, f2, f3, f4, f5, f6;
116 
117    COLOR32_TO_RGB(bg->bg_solid, r, g, b);
118    n1 = (r << 24) | (g << 16) | (b << 8) | (bg->bg_tile << 7)
119       | (bg->bg.keep_aspect << 6) | (bg->top.keep_aspect << 5);
120    n2 = (bg->bg.xjust << 16) | (bg->bg.yjust);
121    n3 = (bg->bg.xperc << 16) | (bg->bg.yperc);
122    n4 = (bg->top.xjust << 16) | (bg->top.yjust);
123    n5 = (bg->top.xperc << 16) | (bg->top.yperc);
124    f1 = 0;
125    f2 = 0;
126    f3 = 0;
127    f4 = 0;
128    f5 = 0;
129    f6 = 0;
130 
131    f = _BackgroundGetBgFile(bg);
132    if (f)
133      {
134 	f1 = fileinode(f);
135 	f2 = filedev(f);
136 	f3 = (int)moddate(f);
137      }
138    f = _BackgroundGetFgFile(bg);
139    if (f)
140      {
141 	f4 = fileinode(f);
142 	f5 = filedev(f);
143 	f6 = (int)moddate(f);
144      }
145 
146    Esnprintf(s, sizeof(s),
147 	     "%c%c%c%c%c%c" "%c%c%c%c%c%c" "%c%c%c%c%c%c" "%c%c%c%c%c%c"
148 	     "%c%c%c%c%c%c" "%c%c%c%c%c%c" "%c%c%c%c%c%c" "%c%c%c%c%c%c"
149 	     "%c%c%c%c%c%c" "%c%c%c%c%c%c" "%c%c%c%c%c%c",
150 	     chmap[(n1 >> 0) & 0x3f], chmap[(n1 >> 6) & 0x3f],
151 	     chmap[(n1 >> 12) & 0x3f], chmap[(n1 >> 18) & 0x3f],
152 	     chmap[(n1 >> 24) & 0x3f], chmap[(n1 >> 28) & 0x3f],
153 	     chmap[(n2 >> 0) & 0x3f], chmap[(n2 >> 6) & 0x3f],
154 	     chmap[(n2 >> 12) & 0x3f], chmap[(n2 >> 18) & 0x3f],
155 	     chmap[(n2 >> 24) & 0x3f], chmap[(n2 >> 28) & 0x3f],
156 	     chmap[(n3 >> 0) & 0x3f], chmap[(n3 >> 6) & 0x3f],
157 	     chmap[(n3 >> 12) & 0x3f], chmap[(n3 >> 18) & 0x3f],
158 	     chmap[(n3 >> 24) & 0x3f], chmap[(n3 >> 28) & 0x3f],
159 	     chmap[(n4 >> 0) & 0x3f], chmap[(n4 >> 6) & 0x3f],
160 	     chmap[(n4 >> 12) & 0x3f], chmap[(n4 >> 18) & 0x3f],
161 	     chmap[(n4 >> 24) & 0x3f], chmap[(n4 >> 28) & 0x3f],
162 	     chmap[(n5 >> 0) & 0x3f], chmap[(n5 >> 6) & 0x3f],
163 	     chmap[(n5 >> 12) & 0x3f], chmap[(n5 >> 18) & 0x3f],
164 	     chmap[(n5 >> 24) & 0x3f], chmap[(n5 >> 28) & 0x3f],
165 	     chmap[(f1 >> 0) & 0x3f], chmap[(f1 >> 6) & 0x3f],
166 	     chmap[(f1 >> 12) & 0x3f], chmap[(f1 >> 18) & 0x3f],
167 	     chmap[(f1 >> 24) & 0x3f], chmap[(f1 >> 28) & 0x3f],
168 	     chmap[(f2 >> 0) & 0x3f], chmap[(f2 >> 6) & 0x3f],
169 	     chmap[(f2 >> 12) & 0x3f], chmap[(f2 >> 18) & 0x3f],
170 	     chmap[(f2 >> 24) & 0x3f], chmap[(f2 >> 28) & 0x3f],
171 	     chmap[(f3 >> 0) & 0x3f], chmap[(f3 >> 6) & 0x3f],
172 	     chmap[(f3 >> 12) & 0x3f], chmap[(f3 >> 18) & 0x3f],
173 	     chmap[(f3 >> 24) & 0x3f], chmap[(f3 >> 28) & 0x3f],
174 	     chmap[(f4 >> 0) & 0x3f], chmap[(f4 >> 6) & 0x3f],
175 	     chmap[(f4 >> 12) & 0x3f], chmap[(f4 >> 18) & 0x3f],
176 	     chmap[(f4 >> 24) & 0x3f], chmap[(f4 >> 28) & 0x3f],
177 	     chmap[(f5 >> 0) & 0x3f], chmap[(f5 >> 6) & 0x3f],
178 	     chmap[(f5 >> 12) & 0x3f], chmap[(f5 >> 18) & 0x3f],
179 	     chmap[(f5 >> 24) & 0x3f], chmap[(f5 >> 28) & 0x3f],
180 	     chmap[(f6 >> 0) & 0x3f], chmap[(f6 >> 6) & 0x3f],
181 	     chmap[(f6 >> 12) & 0x3f], chmap[(f6 >> 18) & 0x3f],
182 	     chmap[(f6 >> 24) & 0x3f], chmap[(f6 >> 28) & 0x3f]);
183    return Estrdup(s);
184 }
185 
186 void
BackgroundPixmapSet(Background * bg,EX_Pixmap pmap)187 BackgroundPixmapSet(Background * bg, EX_Pixmap pmap)
188 {
189    if (bg->pmap != NoXID && bg->pmap != pmap)
190       Eprintf("*** BackgroundPixmapSet %s: pmap was set %#x/%#x\n",
191 	      bg->name, bg->pmap, pmap);
192    bg->pmap = pmap;
193 }
194 
195 static void
BackgroundPixmapFree(Background * bg)196 BackgroundPixmapFree(Background * bg)
197 {
198    if (bg->pmap)
199      {
200 	EImagePixmapsFree(bg->pmap, NoXID);
201 	bg->pmap = NoXID;
202      }
203 }
204 
205 static void
BackgroundImagesFree(Background * bg)206 BackgroundImagesFree(Background * bg)
207 {
208    if (bg->bg.im)
209      {
210 	EImageFree(bg->bg.im);
211 	bg->bg.im = NULL;
212      }
213    if (bg->top.im)
214      {
215 	EImageFree(bg->top.im);
216 	bg->top.im = NULL;
217      }
218 }
219 
220 #if ENABLE_DIALOGS
221 static void
BackgroundImagesKeep(Background * bg,int onoff)222 BackgroundImagesKeep(Background * bg, int onoff)
223 {
224    if (onoff)
225      {
226 	bg->keepim = 1;
227      }
228    else
229      {
230 	bg->keepim = 0;
231 	BackgroundImagesFree(bg);
232      }
233 }
234 #endif /* ENABLE_DIALOGS */
235 
236 static void
BackgroundFilesRemove(Background * bg)237 BackgroundFilesRemove(Background * bg)
238 {
239    EFREE_NULL(bg->bg.file);
240    EFREE_NULL(bg->top.file);
241 
242    BackgroundImagesFree(bg);
243 
244    bg->keepim = 0;
245 }
246 
247 static int
BackgroundDestroy(Background * bg)248 BackgroundDestroy(Background * bg)
249 {
250    if (!bg)
251       return -1;
252 #if 0
253    Eprintf("%s: %s\n", __func__, bg->name);
254 #endif
255    if (bg->ref_count > 0)
256      {
257 	DialogOK("Background Error!", _("%u references remain"), bg->ref_count);
258 	return -1;
259      }
260 
261    LIST_REMOVE(Background, &bg_list, bg);
262 
263    BackgroundFilesRemove(bg);
264    BackgroundPixmapFree(bg);
265 
266    Efree(bg->name);
267 
268    Efree(bg);
269 
270    return 0;
271 }
272 
273 #if ENABLE_DIALOGS
274 static int
BackgroundDelete(Background * bg)275 BackgroundDelete(Background * bg)
276 {
277    char               *f;
278 
279    if (!bg)
280       return -1;
281 #if 0
282    Eprintf("%s: %s\n", __func__, bg->name);
283 #endif
284    if (bg->ref_count > 0)
285      {
286 	DialogOK("Background Error!", _("%u references remain"), bg->ref_count);
287 	return -1;
288      }
289 
290    /* And delete the actual image files */
291    f = _BackgroundGetBgFile(bg);
292    if (f)
293       E_rm(f);
294    f = _BackgroundGetFgFile(bg);
295    if (f)
296       E_rm(f);
297 
298    return BackgroundDestroy(bg);
299 }
300 #endif /* ENABLE_DIALOGS */
301 
302 static Background  *
BackgroundCreate(const char * name,unsigned int solid,const char * bgn,char tile,char keep_aspect,int xjust,int yjust,int xperc,int yperc,const char * top,char tkeep_aspect,int txjust,int tyjust,int txperc,int typerc)303 BackgroundCreate(const char *name, unsigned int solid, const char *bgn,
304 		 char tile, char keep_aspect, int xjust, int yjust, int xperc,
305 		 int yperc, const char *top, char tkeep_aspect, int txjust,
306 		 int tyjust, int txperc, int typerc)
307 {
308    Background         *bg;
309 
310    bg = ECALLOC(Background, 1);
311    if (!bg)
312       return NULL;
313 
314    if (!name)
315      {
316 	name = "NONE";
317 	bg->external = 1;
318      }
319    bg->name = Estrdup(name);
320 
321    COLOR32_FROM_RGB(bg->bg_solid, 160, 160, 160);
322    bg->bg_solid = solid;
323    if (bgn)
324       bg->bg.file = Estrdup(bgn);
325    bg->bg_tile = tile;
326    bg->bg.keep_aspect = keep_aspect;
327    bg->bg.xjust = xjust;
328    bg->bg.yjust = yjust;
329    bg->bg.xperc = xperc;
330    bg->bg.yperc = yperc;
331 
332    if (top)
333       bg->top.file = Estrdup(top);
334    bg->top.keep_aspect = tkeep_aspect;
335    bg->top.xjust = txjust;
336    bg->top.yjust = tyjust;
337    bg->top.xperc = txperc;
338    bg->top.yperc = typerc;
339 
340    bg->seq_no = ++bg_seq_no;
341 
342    LIST_PREPEND(Background, &bg_list, bg);
343 
344    return bg;
345 }
346 
347 static int
BackgroundCmp(const Background * bg,const Background * bgx)348 BackgroundCmp(const Background * bg, const Background * bgx)
349 {
350    if (*bgx->name != '.')	/* Discard only generated backgrounds */
351       return 1;
352 
353    if (bg->bg.file && bgx->bg.file)
354      {
355 	if ((strcmp(bg->bg.file, bgx->bg.file)) ||
356 	    (bg->bg.keep_aspect != bgx->bg.keep_aspect) ||
357 	    (bg->bg.xjust != bgx->bg.xjust) ||
358 	    (bg->bg.yjust != bgx->bg.yjust) ||
359 	    (bg->bg.xperc != bgx->bg.xperc) || (bg->bg.yperc != bgx->bg.yperc))
360 	   return 1;
361      }
362    else if (bg->bg.file || bgx->bg.file)
363       return 1;
364 
365    if (bg->top.file && bgx->top.file)
366      {
367 	if ((strcmp(bg->top.file, bgx->top.file)) ||
368 	    (bg->top.keep_aspect != bgx->top.keep_aspect) ||
369 	    (bg->top.xjust != bgx->top.xjust) ||
370 	    (bg->top.yjust != bgx->top.yjust) ||
371 	    (bg->top.xperc != bgx->top.xperc) ||
372 	    (bg->top.yperc != bgx->top.yperc))
373 	   return 1;
374      }
375    else if (bg->top.file || bgx->top.file)
376       return 1;
377 
378    if (bg->bg_solid != bgx->bg_solid)
379       return 1;
380    if (bg->bg_tile != bgx->bg_tile)
381       return 1;
382 
383    return 0;
384 }
385 
386 static int
_BackgroundMatchName(const void * data,const void * match)387 _BackgroundMatchName(const void *data, const void *match)
388 {
389    return strcmp(((const Background *)data)->name, (const char *)match);
390 }
391 
392 Background         *
BackgroundFind(const char * name)393 BackgroundFind(const char *name)
394 {
395    return LIST_FIND(Background, &bg_list, _BackgroundMatchName, name);
396 }
397 
398 static Background  *
BackgroundCheck(Background * bg)399 BackgroundCheck(Background * bg)
400 {
401    return LIST_CHECK(Background, &bg_list, bg);
402 }
403 
404 void
BackgroundDestroyByName(const char * name)405 BackgroundDestroyByName(const char *name)
406 {
407    BackgroundDestroy(BackgroundFind(name));
408 }
409 
410 static void
BackgroundInvalidate(Background * bg,int refresh)411 BackgroundInvalidate(Background * bg, int refresh)
412 {
413    BackgroundPixmapFree(bg);
414    bg->seq_no = ++bg_seq_no;
415    if (bg->ref_count && refresh)
416       DesksBackgroundRefresh(bg, DESK_BG_REFRESH);
417 }
418 
419 static int
BackgroundModify(Background * bg,unsigned int solid,const char * bgn,char tile,char keep_aspect,int xjust,int yjust,int xperc,int yperc,const char * top,char tkeep_aspect,int txjust,int tyjust,int txperc,int typerc)420 BackgroundModify(Background * bg, unsigned int solid, const char *bgn,
421 		 char tile, char keep_aspect, int xjust, int yjust, int xperc,
422 		 int yperc, const char *top, char tkeep_aspect, int txjust,
423 		 int tyjust, int txperc, int typerc)
424 {
425    int                 updated = 0;
426 
427    if (solid != bg->bg_solid)
428       updated = 1;
429    bg->bg_solid = solid;
430 
431    if ((bg->bg.file) && (bgn))
432      {
433 	if (strcmp(bg->bg.file, bgn))
434 	   updated = 1;
435      }
436    else
437       updated = 1;
438    EFREE_SET(bg->bg.file, (bgn && bgn[0]) ? Estrdup(bgn) : NULL);
439    if ((int)tile != bg->bg_tile)
440       updated = 1;
441    if ((int)keep_aspect != bg->bg.keep_aspect)
442       updated = 1;
443    if (xjust != bg->bg.xjust)
444       updated = 1;
445    if (yjust != bg->bg.yjust)
446       updated = 1;
447    if (xperc != bg->bg.xperc)
448       updated = 1;
449    if (yperc != bg->bg.yperc)
450       updated = 1;
451    bg->bg_tile = (char)tile;
452    bg->bg.keep_aspect = (char)keep_aspect;
453    bg->bg.xjust = xjust;
454    bg->bg.yjust = yjust;
455    bg->bg.xperc = xperc;
456    bg->bg.yperc = yperc;
457 
458    if ((bg->top.file) && (top))
459      {
460 	if (strcmp(bg->top.file, top))
461 	   updated = 1;
462      }
463    else
464       updated = 1;
465    EFREE_SET(bg->top.file, (top && top[0]) ? Estrdup(top) : NULL);
466    if ((int)tkeep_aspect != bg->top.keep_aspect)
467       updated = 1;
468    if (txjust != bg->top.xjust)
469       updated = 1;
470    if (tyjust != bg->top.yjust)
471       updated = 1;
472    if (txperc != bg->top.xperc)
473       updated = 1;
474    if (typerc != bg->top.yperc)
475       updated = 1;
476    bg->top.keep_aspect = (char)tkeep_aspect;
477    bg->top.xjust = txjust;
478    bg->top.yjust = tyjust;
479    bg->top.xperc = txperc;
480    bg->top.yperc = typerc;
481 
482    if (updated)
483       BackgroundInvalidate(bg, 1);
484 
485    return updated;
486 }
487 
488 static void
BgFindImageSize(BgPart * bgp,unsigned int rw,unsigned int rh,unsigned int * pw,unsigned int * ph)489 BgFindImageSize(BgPart * bgp, unsigned int rw, unsigned int rh,
490 		unsigned int *pw, unsigned int *ph)
491 {
492    int                 w, h, iw, ih;
493 
494    EImageGetSize(bgp->im, &iw, &ih);
495 
496 #if 0				/* FIXME - Remove? */
497    if (bgp->keep_aspect)
498       bgp->xperc = bgp->yperc;
499 #endif
500 
501    if (bgp->xperc > 0)
502       w = (rw * bgp->xperc) >> 10;
503    else
504       w = (iw * rw) / WinGetW(VROOT);
505 
506    if (bgp->yperc > 0)
507       h = (rh * bgp->yperc) >> 10;
508    else
509       h = (ih * rh) / WinGetH(VROOT);
510 
511    if (w <= 0)
512       w = 1;
513    if (h <= 0)
514       h = 1;
515 
516    if (bgp->keep_aspect)
517      {
518 	if (bgp->yperc <= 0)
519 	  {
520 	     if (((w << 10) / h) != ((iw << 10) / ih))
521 		h = ((w * ih) / iw);
522 	  }
523 	else
524 	  {
525 	     if (((h << 10) / w) != ((ih << 10) / iw))
526 		w = ((h * iw) / ih);
527 	  }
528      }
529 
530    *pw = (unsigned int)w;
531    *ph = (unsigned int)h;
532 }
533 
534 static              EX_Pixmap
BackgroundCreatePixmap(Win win,unsigned int w,unsigned int h)535 BackgroundCreatePixmap(Win win, unsigned int w, unsigned int h)
536 {
537    EX_Pixmap           pmap;
538 
539    /*
540     * Stupid hack to avoid that a new root pixmap has the same ID as the now
541     * invalid one from a previous session.
542     */
543    pmap = ECreatePixmap(win, w, h, 0);
544    if (win == RROOT && pmap == Mode.root.ext_pmap)
545      {
546 	EFreePixmap(pmap);
547 	pmap = ECreatePixmap(win, w, h, 0);
548 	Mode.root.ext_pmap = NoXID;
549 	Mode.root.ext_pmap_valid = 0;
550      }
551    return pmap;
552 }
553 
554 void
BackgroundRealize(Background * bg,Win win,EX_Drawable draw,unsigned int rw,unsigned int rh,int is_win,EX_Pixmap * ppmap,unsigned int * ppixel)555 BackgroundRealize(Background * bg, Win win, EX_Drawable draw,
556 		  unsigned int rw, unsigned int rh, int is_win,
557 		  EX_Pixmap * ppmap, unsigned int *ppixel)
558 {
559    EX_Pixmap           pmap;
560    int                 x, y, ww, hh;
561    unsigned int        w, h;
562    char               *file, hasbg, hasfg;
563    EImage             *im;
564 
565    if (!bg->bg.im)
566      {
567 	file = _BackgroundGetBgFile(bg);
568 	if (file)
569 	   bg->bg.im = EImageLoad(file);
570      }
571 
572    if (!bg->top.im)
573      {
574 	file = _BackgroundGetFgFile(bg);
575 	if (file)
576 	   bg->top.im = EImageLoad(bg->top.file);
577      }
578 
579    if (!draw)
580       draw = WinGetXwin(win);
581 
582    hasbg = !!bg->bg.im;
583    hasfg = !!bg->top.im;
584 
585    if (!hasbg && !hasfg)
586      {
587 	unsigned int        pixel;
588 
589 	/* Solid color only */
590 	pixel = EAllocColor(WinGetCmap(VROOT), bg->bg_solid);
591 
592 	if (!is_win)
593 	   EXFillAreaSolid(draw, 0, 0, rw, rh, pixel);
594 
595 	if (ppmap)
596 	   *ppmap = NoXID;
597 	if (ppixel)
598 	   *ppixel = pixel;
599 	return;
600      }
601 
602    /* Has either bg or fg image */
603 
604    w = h = x = y = 0;
605 
606    if (hasbg)
607      {
608 	BgFindImageSize(&(bg->bg), rw, rh, &w, &h);
609 	x = ((int)(rw - w) * bg->bg.xjust) >> 10;
610 	y = ((int)(rh - h) * bg->bg.yjust) >> 10;
611      }
612 
613    if (is_win && hasbg && !hasfg && x == 0 && y == 0 &&
614        ((w == rw && h == rh) || (bg->bg_tile && !ECompMgrIsActive())))
615      {
616 	/* Window, no fg, no offset, and scale to 100%, or tiled, no trans */
617 	pmap = BackgroundCreatePixmap(win, w, h);
618 	EImageRenderOnDrawable(bg->bg.im, win, pmap, EIMAGE_ANTI_ALIAS,
619 			       0, 0, w, h);
620 	goto done;
621      }
622 
623    /* The rest that require some more work */
624    if (is_win)
625       pmap = BackgroundCreatePixmap(win, rw, rh);
626    else
627       pmap = draw;
628 
629    if (hasbg && !hasfg && x == 0 && y == 0 && w == rw && h == rh)
630      {
631 	im = bg->bg.im;
632      }
633    else
634      {
635 	/* Create full size image */
636 	im = EImageCreate(rw, rh);
637 	EImageSetHasAlpha(im, 0);
638 	if (!hasbg || !bg->bg_tile)
639 	  {
640 	     /* Fill solid */
641 	     EImageFill(im, 0, 0, rw, rh, bg->bg_solid);
642 	  }
643 	if (hasbg)
644 	  {
645 	     if (bg->bg_tile)
646 	       {
647 		  EImageTile(im, bg->bg.im, 0, w, h, 0, 0, rw, rh, x, y);
648 	       }
649 	     else
650 	       {
651 		  EImageGetSize(bg->bg.im, &ww, &hh);
652 		  EImageBlend(im, bg->bg.im, EIMAGE_ANTI_ALIAS, 0, 0, ww, hh,
653 			      x, y, w, h, 1);
654 	       }
655 	  }
656      }
657 
658    if (hasfg)
659      {
660 	EImageGetSize(bg->top.im, &ww, &hh);
661 
662 	BgFindImageSize(&(bg->top), rw, rh, &w, &h);
663 	x = ((rw - w) * bg->top.xjust) >> 10;
664 	y = ((rh - h) * bg->top.yjust) >> 10;
665 
666 	EImageBlend(im, bg->top.im, EIMAGE_BLEND | EIMAGE_ANTI_ALIAS,
667 		    0, 0, ww, hh, x, y, w, h, 0);
668      }
669 
670    EImageRenderOnDrawable(im, win, pmap, EIMAGE_ANTI_ALIAS, 0, 0, rw, rh);
671    if (im != bg->bg.im)
672       EImageFree(im);
673 
674  done:
675    if (!bg->keepim)
676       BackgroundImagesFree(bg);
677 
678    if (ppmap)
679       *ppmap = pmap;
680    if (ppixel)
681       *ppixel = 0;
682 }
683 
684 void
BackgroundApplyPmap(Background * bg,Win win,EX_Drawable draw,unsigned int w,unsigned int h)685 BackgroundApplyPmap(Background * bg, Win win, EX_Drawable draw,
686 		    unsigned int w, unsigned int h)
687 {
688    BackgroundRealize(bg, win, draw, w, h, 0, NULL, NULL);
689 }
690 
691 static void
BackgroundApplyWin(Background * bg,Win win)692 BackgroundApplyWin(Background * bg, Win win)
693 {
694    EX_Pixmap           pmap;
695    unsigned int        pixel;
696 
697    BackgroundRealize(bg, win, NoXID, WinGetW(win), WinGetH(win), 1,
698 		     &pmap, &pixel);
699    if (pmap != NoXID)
700      {
701 	ESetWindowBackgroundPixmap(win, pmap, 0);
702 	EImagePixmapsFree(pmap, NoXID);
703      }
704    else
705      {
706 	ESetWindowBackground(win, pixel);
707      }
708    EClearWindow(win);
709 }
710 
711 /*
712  * Apply a background to window.
713  * The BG pixmap is stored in bg->pmap.
714  */
715 void
BackgroundSet(Background * bg,Win win,unsigned int w,unsigned int h)716 BackgroundSet(Background * bg, Win win, unsigned int w, unsigned int h)
717 {
718    EX_Pixmap           pmap = NoXID;
719    unsigned int        pixel = 0;
720 
721    if (bg->pmap)
722       pmap = bg->pmap;
723    else
724       BackgroundRealize(bg, win, NoXID, w, h, 1, &pmap, &pixel);
725 
726    bg->pmap = pmap;
727    if (pmap != NoXID)
728       ESetWindowBackgroundPixmap(win, pmap, 1);
729    else
730       ESetWindowBackground(win, pixel);
731    EClearWindow(win);
732 }
733 
734 Background         *
BrackgroundCreateFromImage(const char * bgid,const char * file,char * thumb,int thlen)735 BrackgroundCreateFromImage(const char *bgid, const char *file,
736 			   char *thumb, int thlen)
737 {
738    Background         *bg;
739    EImage             *im, *im2;
740    unsigned int        color;
741    char                tile = 1, keep_asp = 0;
742    int                 width, height;
743    int                 scalex = 0, scaley = 0;
744    int                 scr_asp, im_asp;
745    int                 w2, h2;
746    int                 maxw = Mode.backgrounds.mini_w;
747    int                 maxh = Mode.backgrounds.mini_h;
748    int                 justx = 512, justy = 512;
749 
750    bg = BackgroundFind(bgid);
751 
752    if (thumb)
753      {
754 	Esnprintf(thumb, thlen, "%s/cached/img/%s.png", EDirUserCache(), bgid);
755 	if (bg && exists(thumb) && moddate(thumb) > moddate(file))
756 	   return bg;
757 	/* The thumbnail is gone or outdated - regererate */
758      }
759    else
760      {
761 	if (bg)
762 	   return bg;
763      }
764 
765    im = EImageLoad(file);
766    if (!im)
767       return NULL;
768 
769    EImageGetSize(im, &width, &height);
770 
771    if (thumb)
772      {
773 	h2 = maxh;
774 	w2 = (width * h2) / height;
775 	if (w2 > maxw)
776 	  {
777 	     w2 = maxw;
778 	     h2 = (height * w2) / width;
779 	  }
780 	im2 = EImageCreateScaled(im, 0, 0, width, height, w2, h2);
781 	EImageSave(im2, thumb);
782 	EImageDecache(im2);
783      }
784 
785    EImageDecache(im);
786 
787    /* Quit if the background itself already exists */
788    if (bg)
789       return bg;
790 
791    scr_asp = (WinGetW(VROOT) << 16) / WinGetH(VROOT);
792    im_asp = (width << 16) / height;
793    if (width == height)
794      {
795 	justx = 0;
796 	justy = 0;
797 	scalex = 0;
798 	scaley = 0;
799 	tile = 1;
800 	keep_asp = 0;
801      }
802    else if ((!(IN_RANGE(scr_asp, im_asp, 16000)))
803 	    && ((width < 480) && (height < 360)))
804      {
805 	justx = 0;
806 	justy = 0;
807 	scalex = 0;
808 	scaley = 0;
809 	tile = 1;
810 	keep_asp = 0;
811      }
812    else if (IN_RANGE(scr_asp, im_asp, 16000))
813      {
814 	justx = 0;
815 	justy = 0;
816 	scalex = 1024;
817 	scaley = 1024;
818 	tile = 0;
819 	keep_asp = 0;
820      }
821    else if (im_asp > scr_asp)
822      {
823 	justx = 512;
824 	justy = 512;
825 	scalex = 1024;
826 	scaley = 0;
827 	tile = 0;
828 	keep_asp = 1;
829      }
830    else
831      {
832 	justx = 512;
833 	justy = 512;
834 	scalex = 0;
835 	scaley = 1024;
836 	tile = 0;
837 	keep_asp = 1;
838      }
839 
840    COLOR32_FROM_RGB(color, 0, 0, 0);
841 
842    bg = BackgroundCreate(bgid, color, file, tile,
843 			 keep_asp, justx, justy,
844 			 scalex, scaley, NULL, 0, 0, 0, 0, 0);
845 
846    return bg;
847 }
848 
849 void
BackgroundIncRefcount(Background * bg)850 BackgroundIncRefcount(Background * bg)
851 {
852    if (!bg)
853       return;
854    bg->ref_count++;
855 }
856 
857 void
BackgroundDecRefcount(Background * bg)858 BackgroundDecRefcount(Background * bg)
859 {
860    if (!bg)
861       return;
862    bg->ref_count--;
863    if (bg->ref_count <= 0)
864       bg->last_viewed = 0;	/* Clean out asap */
865 }
866 
867 void
BackgroundTouch(Background * bg)868 BackgroundTouch(Background * bg)
869 {
870    if (!bg)
871       return;
872    bg->last_viewed = time(NULL);
873 }
874 
875 const char         *
BackgroundGetName(const Background * bg)876 BackgroundGetName(const Background * bg)
877 {
878    return bg->name;
879 }
880 
881 #if ENABLE_DIALOGS
882 static const char  *
BackgroundGetBgFile(const Background * bg)883 BackgroundGetBgFile(const Background * bg)
884 {
885    return bg->bg.file;
886 }
887 
888 static const char  *
BackgroundGetFgFile(const Background * bg)889 BackgroundGetFgFile(const Background * bg)
890 {
891    return bg->top.file;
892 }
893 #endif /* ENABLE_DIALOGS */
894 
895 EX_Pixmap
BackgroundGetPixmap(const Background * bg)896 BackgroundGetPixmap(const Background * bg)
897 {
898    return (bg) ? bg->pmap : NoXID;
899 }
900 
901 unsigned int
BackgroundGetSeqNo(const Background * bg)902 BackgroundGetSeqNo(const Background * bg)
903 {
904    return bg->seq_no;
905 }
906 
907 int
BackgroundIsNone(const Background * bg)908 BackgroundIsNone(const Background * bg)
909 {
910    return (bg) ? bg->external : 1;
911 }
912 
913 #if ENABLE_DIALOGS
914 static EImage      *
BackgroundCacheMini(Background * bg,int keep,int nuke)915 BackgroundCacheMini(Background * bg, int keep, int nuke)
916 {
917    char                s[4096];
918    EImage             *im;
919    EX_Pixmap           pmap;
920    int                 mini_w = Mode.backgrounds.mini_w;
921    int                 mini_h = Mode.backgrounds.mini_h;
922 
923    Esnprintf(s, sizeof(s), "%s/cached/bgsel/%s.png", EDirUserCache(),
924 	     BackgroundGetName(bg));
925 
926    im = EImageLoad(s);
927    if (im)
928      {
929 	if (nuke)
930 	   EImageDecache(im);
931 	else
932 	   goto done;
933      }
934 
935    /* Create new cached bg mini image */
936    pmap = ECreatePixmap(VROOT, mini_w, mini_h, 0);
937    BackgroundApplyPmap(bg, VROOT, pmap, mini_w, mini_h);
938    im = EImageGrabDrawable(pmap, NoXID, 0, 0, mini_w, mini_h, 0);
939    EImageSave(im, s);
940    EFreePixmap(pmap);
941 
942  done:
943    if (keep)
944       return im;
945    EImageFree(im);
946    return NULL;
947 }
948 #endif /* ENABLE_DIALOGS */
949 
950 #define S(str) ((str) ? str : "(null)")
951 static void
BackgroundGetInfoString1(const Background * bg,char * buf,int len)952 BackgroundGetInfoString1(const Background * bg, char *buf, int len)
953 {
954    int                 r, g, b;
955 
956    COLOR32_TO_RGB(bg->bg_solid, r, g, b);
957    Esnprintf(buf, len,
958 	     "%s ref_count %u keepim %u\n"
959 	     " bg.solid\t %i %i %i\n"
960 	     " bg.file\t %s\n"
961 	     " top.file\t %s\n"
962 	     " bg.tile\t %i\n"
963 	     " bg.keep_aspect\t %i \ttop.keep_aspect\t %i\n"
964 	     " bg.xjust\t %i \ttop.xjust\t %i\n"
965 	     " bg.yjust\t %i \ttop.yjust\t %i\n"
966 	     " bg.xperc\t %i \ttop.xperc\t %i\n"
967 	     " bg.yperc\t %i \ttop.yperc\t %i\n", bg->name,
968 	     bg->ref_count, bg->keepim, r, g, b,
969 	     bg->bg.file, bg->top.file, bg->bg_tile,
970 	     bg->bg.keep_aspect, bg->top.keep_aspect,
971 	     bg->bg.xjust, bg->top.xjust, bg->bg.yjust,
972 	     bg->top.yjust, bg->bg.xperc, bg->top.xperc,
973 	     bg->bg.yperc, bg->top.yperc);
974 }
975 
976 static void
BackgroundGetInfoString2(const Background * bg,char * buf,int len)977 BackgroundGetInfoString2(const Background * bg, char *buf, int len)
978 {
979    int                 r, g, b;
980 
981    COLOR32_TO_RGB(bg->bg_solid, r, g, b);
982    Esnprintf(buf, len,
983 	     "%s %i %i %i %s %i %i %i %i %i %i %s %i %i %i %i %i",
984 	     bg->name, r, g, b, S(bg->bg.file), bg->bg_tile,
985 	     bg->bg.keep_aspect, bg->bg.xjust, bg->bg.yjust,
986 	     bg->bg.xperc, bg->bg.yperc, S(bg->top.file),
987 	     bg->top.keep_aspect, bg->top.xjust, bg->top.yjust,
988 	     bg->top.xperc, bg->top.yperc);
989 }
990 
991 void
BackgroundsInvalidate(int refresh)992 BackgroundsInvalidate(int refresh)
993 {
994    Background         *bg;
995 
996    LIST_FOR_EACH(Background, &bg_list, bg) BackgroundInvalidate(bg, refresh);
997 }
998 
999 static Background  *
BackgroundGetRandom(void)1000 BackgroundGetRandom(void)
1001 {
1002    Background         *bg;
1003    int                 num;
1004    unsigned int        rnd;
1005 
1006    num = LIST_GET_COUNT(&bg_list);
1007    for (;;)
1008      {
1009 	rnd = rand() % num;
1010 	bg = LIST_GET_BY_INDEX(Background, &bg_list, rnd);
1011 	if (num <= 1 || !BackgroundIsNone(bg))
1012 	   break;
1013      }
1014 
1015    return bg;
1016 }
1017 
1018 void
BackgroundSetForDesk(Background * bg,unsigned int desk)1019 BackgroundSetForDesk(Background * bg, unsigned int desk)
1020 {
1021    if (desk >= N_BG_ASSIGNED)
1022       return;
1023 
1024    bg_assigned[desk] = bg;
1025 }
1026 
1027 Background         *
BackgroundGetForDesk(unsigned int desk)1028 BackgroundGetForDesk(unsigned int desk)
1029 {
1030    Background         *bg;
1031 
1032    if (desk >= N_BG_ASSIGNED)
1033       return NULL;
1034 
1035    bg = bg_assigned[desk];
1036    if (bg)
1037       bg = BackgroundCheck(bg);
1038    if (!bg)
1039       bg = BackgroundGetRandom();
1040 
1041    return bg;
1042 }
1043 
1044 /*
1045  * Config load/save
1046  */
1047 #include "conf.h"
1048 
1049 int
BackgroundsConfigLoad(FILE * fs)1050 BackgroundsConfigLoad(FILE * fs)
1051 {
1052    int                 err = 0;
1053    Background         *bg = 0;
1054    unsigned int        color;
1055    char                s[FILEPATH_LEN_MAX];
1056    char                s2[FILEPATH_LEN_MAX];
1057    char               *p2, *p3;
1058    int                 ii1;
1059    int                 r, g, b;
1060    int                 i1 = 0, i2 = 0, i3 = 0, i4 = 0, i5 = 0, i6 = 0;
1061    int                 j1 = 0, j2 = 0, j3 = 0, j4 = 0, j5 = 0;
1062    char               *bg1 = 0;
1063    char               *bg2 = 0;
1064    int                 desk;
1065 
1066    COLOR32_FROM_RGB(color, 0, 0, 0);
1067 
1068    while (GetLine(s, sizeof(s), fs))
1069      {
1070 	ii1 = ConfigParseline1(s, s2, &p2, &p3);
1071 	switch (ii1)
1072 	  {
1073 	  case CONFIG_CLOSE:
1074 	     if (!bg)
1075 		goto done;
1076 	     bg->bg_solid = color;
1077 	     bg->bg.file = bg1;
1078 	     bg->top.file = bg2;
1079 	     bg1 = bg2 = NULL;
1080 	     bg->bg_tile = i1;
1081 	     bg->bg.keep_aspect = i2;
1082 	     bg->bg.xjust = i3;
1083 	     bg->bg.yjust = i4;
1084 	     bg->bg.xperc = i5;
1085 	     bg->bg.yperc = i6;
1086 	     bg->top.keep_aspect = j1;
1087 	     bg->top.xjust = j2;
1088 	     bg->top.yjust = j3;
1089 	     bg->top.xperc = j4;
1090 	     bg->top.yperc = j5;
1091 	     goto done;
1092 
1093 	  case CONFIG_COLORMOD:
1094 	  case ICLASS_COLORMOD:
1095 	     break;
1096 
1097 	  case CONFIG_CLASSNAME:
1098 	     bg = BackgroundFind(s2);
1099 	     if (!bg)
1100 	       {
1101 		  bg = BackgroundCreate(s2, color,
1102 					bg1, i1, i2, i3, i4, i5, i6,
1103 					bg2, j1, j2, j3, j4, j5);
1104 	       }
1105 	     else
1106 	       {
1107 		  color = bg->bg_solid;
1108 		  Efree(bg1);
1109 		  Efree(bg2);
1110 		  bg1 = bg->bg.file;
1111 		  bg2 = bg->top.file;
1112 		  bg->bg.file = NULL;
1113 		  bg->top.file = NULL;
1114 		  i1 = bg->bg_tile;
1115 		  i2 = bg->bg.keep_aspect;
1116 		  i3 = bg->bg.xjust;
1117 		  i4 = bg->bg.yjust;
1118 		  i5 = bg->bg.xperc;
1119 		  i6 = bg->bg.yperc;
1120 		  j1 = bg->top.keep_aspect;
1121 		  j2 = bg->top.xjust;
1122 		  j3 = bg->top.yjust;
1123 		  j4 = bg->top.xperc;
1124 		  j5 = bg->top.yperc;
1125 	       }
1126 	     break;
1127 
1128 	  case BG_DESKNUM:
1129 	     if (!bg)
1130 		break;
1131 	     desk = atoi(s2);
1132 	     if (desk >= N_BG_ASSIGNED)
1133 		break;
1134 	     if (desk >= 0)
1135 	       {
1136 		  if (!bg_assigned[desk] || Conf.backgrounds.user)
1137 		    {
1138 		       bg_assigned[desk] = bg;
1139 		       bg->referenced = 1;
1140 		    }
1141 	       }
1142 	     else
1143 	       {
1144 		  bg->referenced = 1;
1145 		  for (ii1 = 0; ii1 < N_BG_ASSIGNED; ii1++)
1146 		    {
1147 		       if (!bg_assigned[ii1])
1148 			  bg_assigned[ii1] = bg;
1149 		    }
1150 	       }
1151 	     break;
1152 
1153 	  case BG_RGB:
1154 	     r = g = b = 0;
1155 	     sscanf(p2, "%d %d %d", &r, &g, &b);
1156 	     COLOR32_FROM_RGB(color, r, g, b);
1157 	     break;
1158 
1159 	  case BG_BG_FILE:
1160 	     EFREE_DUP(bg1, p2);
1161 	     break;
1162 
1163 	  case BG_BG_PARAM:
1164 	     sscanf(p2, "%d %d %d %d %d %d", &i1, &i2, &i3, &i4, &i5, &i6);
1165 	     break;
1166 
1167 #if 1				/* Obsolete - backward compatibility */
1168 	  case BG_BG1:
1169 	     sscanf(p3, "%d %d %d %d %d %d", &i1, &i2, &i3, &i4, &i5, &i6);
1170 	     EFREE_DUP(bg1, s2);
1171 	     break;
1172 #endif
1173 
1174 	  case BG_TOP_FILE:
1175 	     EFREE_DUP(bg2, p2);
1176 	     break;
1177 
1178 	  case BG_TOP_PARAM:
1179 	     sscanf(p2, "%d %d %d %d %d", &j1, &j2, &j3, &j4, &j5);
1180 	     break;
1181 
1182 #if 1				/* Obsolete - backward compatibility */
1183 	  case BG_BG2:
1184 	     sscanf(p3, "%d %d %d %d %d", &j1, &j2, &j3, &j4, &j5);
1185 	     EFREE_DUP(bg2, s2);
1186 	     break;
1187 #endif
1188 
1189 	  default:
1190 	     break;
1191 	  }
1192      }
1193    err = -1;
1194 
1195  done:
1196    Efree(bg1);
1197    Efree(bg2);
1198 
1199    return err;
1200 }
1201 
1202 static void
BackgroundsConfigLoadUser(void)1203 BackgroundsConfigLoadUser(void)
1204 {
1205    char                s[4096];
1206 
1207    Esnprintf(s, sizeof(s), "%s.bg", EGetSavePrefix());
1208    if (!exists(s))
1209      {
1210 	Mode.backgrounds.force_scan = 1;
1211 	Esnprintf(s, sizeof(s), "%s.backgrounds", EGetSavePrefix());
1212 	if (!exists(s))
1213 	   return;
1214      }
1215    ConfigFileLoad(s, NULL, ConfigFileRead, 0);
1216 }
1217 
1218 static void
BackgroundsConfigSave(void)1219 BackgroundsConfigSave(void)
1220 {
1221    char                s[FILEPATH_LEN_MAX], st[FILEPATH_LEN_MAX];
1222    FILE               *fs;
1223    Background         *bg;
1224    unsigned int        j;
1225    int                 r, g, b;
1226 
1227    Etmp(st);
1228    fs = fopen(st, "w");
1229    if (!fs)
1230       return;
1231 
1232    /* For obscure reasons, store backgrounds in reverse order. */
1233    LIST_FOR_EACH_REV(Background, &bg_list, bg)
1234    {
1235       /* Get full path to files */
1236       _BackgroundGetBgFile(bg);
1237       _BackgroundGetFgFile(bg);
1238       /* Discard if bg file is given but cannot be found (ignore bad fg) */
1239       if (bg->bg.file && !exists(bg->bg.file))
1240 	{
1241 	   Eprintf("Discard broken background %s (%s)\n",
1242 		   bg->name, bg->bg.file);
1243 	   continue;
1244 	}
1245 
1246       fprintf(fs, "5 999\n");
1247 
1248       fprintf(fs, "100 %s\n", bg->name);
1249       COLOR32_TO_RGB(bg->bg_solid, r, g, b);
1250       if (r != 0 || g != 0 || b != 0)
1251 	 fprintf(fs, "%d %d %d %d\n", BG_RGB, r, g, b);
1252 
1253       if (bg->bg.file)
1254 	{
1255 	   fprintf(fs, "%d %s\n", BG_BG_FILE, bg->bg.file);
1256 	   fprintf(fs, "%d %d %d %d %d %d %d\n", BG_BG_PARAM,
1257 		   bg->bg_tile, bg->bg.keep_aspect,
1258 		   bg->bg.xjust, bg->bg.yjust, bg->bg.xperc, bg->bg.yperc);
1259 	}
1260 
1261       if (bg->top.file)
1262 	{
1263 	   fprintf(fs, "%d %s\n", BG_TOP_FILE, bg->top.file);
1264 	   fprintf(fs, "%d %d %d %d %d %d\n", BG_TOP_PARAM,
1265 		   bg->top.keep_aspect,
1266 		   bg->top.xjust, bg->top.yjust, bg->top.xperc, bg->top.yperc);
1267 	}
1268 
1269       for (j = 0; j < N_BG_ASSIGNED; j++)
1270 	{
1271 	   if (bg == bg_assigned[j])
1272 	      fprintf(fs, "%d %u\n", BG_DESKNUM, j);
1273 	}
1274 
1275       fprintf(fs, "1000\n");
1276    }
1277 
1278    fclose(fs);
1279 
1280    Esnprintf(s, sizeof(s), "%s.bg", EGetSavePrefix());
1281    E_mv(st, s);
1282 }
1283 
1284 /*
1285  * Backgrounds module
1286  */
1287 
1288 static void
BackgroundsCheckDups(void)1289 BackgroundsCheckDups(void)
1290 {
1291    Background         *bg, *bgx, *btmp;
1292 
1293    LIST_FOR_EACH(Background, &bg_list, bg)
1294    {
1295       bgx = LIST_NEXT(Background, &bg_list, bg);
1296       for (; bgx; bgx = btmp)
1297 	{
1298 	   btmp = LIST_NEXT(Background, &bg_list, bgx);
1299 
1300 	   if (bgx->ref_count > 0 || bgx->referenced)
1301 	      continue;
1302 	   if (BackgroundCmp(bg, bgx))
1303 	      continue;
1304 #if 0
1305 	   Eprintf("Remove duplicate background %s (==%s)\n", bgx->name,
1306 		   bg->name);
1307 #endif
1308 #ifndef __clang_analyzer__
1309 	   BackgroundDestroy(bgx);
1310 #endif
1311 	}
1312    }
1313 }
1314 
1315 static void
BackgroundsAccounting(void)1316 BackgroundsAccounting(void)
1317 {
1318    Background         *bg;
1319    time_t              now;
1320 
1321    DesksBackgroundRefresh(NULL, DESK_BG_TIMEOUT);
1322 
1323    now = time(NULL);
1324    LIST_FOR_EACH(Background, &bg_list, bg)
1325    {
1326       /* Skip if no pixmap or not timed out */
1327       if (bg->pmap == NoXID ||
1328 	  ((now - bg->last_viewed) <= Conf.backgrounds.timeout))
1329 	 continue;
1330 
1331       DesksBackgroundRefresh(bg, DESK_BG_FREE);
1332       BackgroundPixmapFree(bg);
1333    }
1334 }
1335 
1336 static int
BackgroundsTimeout(void * data __UNUSED__)1337 BackgroundsTimeout(void *data __UNUSED__)
1338 {
1339    if (Conf.backgrounds.timeout <= 0)
1340       Conf.backgrounds.timeout = 1;
1341 
1342    BackgroundsAccounting();
1343 
1344    TimerSetInterval(bg_timer, 1000 * Conf.backgrounds.timeout);
1345 
1346    return 1;
1347 }
1348 
1349 static void
BackgroundsSighan(int sig,void * prm __UNUSED__)1350 BackgroundsSighan(int sig, void *prm __UNUSED__)
1351 {
1352    switch (sig)
1353      {
1354      case ESIGNAL_INIT:
1355 	/* Create the "None" background */
1356 	BackgroundCreate(NULL, 0, NULL, 0, 0, 0, 0, 0, 0, NULL, 0, 0, 0, 0, 0);
1357 	break;
1358 
1359      case ESIGNAL_CONFIGURE:
1360 	BackgroundsConfigLoadUser();
1361 	BackgroundsCheckDups();
1362 	StartupBackgroundsDestroy();
1363 	break;
1364 
1365      case ESIGNAL_START:
1366 	TIMER_ADD(bg_timer, 30000, BackgroundsTimeout, NULL);
1367 	break;
1368 
1369      case ESIGNAL_EXIT:
1370 	if (Mode.wm.save_ok)
1371 	   BackgroundsConfigSave();
1372 	break;
1373      }
1374 }
1375 
1376 #if ENABLE_DIALOGS
1377 /*
1378  * Configuration dialog
1379  */
1380 
1381 typedef struct {
1382    DItem              *bg_sel;
1383    DItem              *bg_sel_slider;
1384    DItem              *bg_mini_disp;
1385    DItem              *bg_filename;
1386    DItem              *di[10];	/* Various dialog items */
1387 
1388    Background         *bg;	/* The background being configured */
1389    Background         *bg_set;	/* The background last applied */
1390    int                 bg_sel_sliderval;
1391    int                 bg_sel_sliderval_old;
1392    int                 bg_r;
1393    int                 bg_g;
1394    int                 bg_b;
1395    char                bg_image;
1396    char                bg_tile;
1397    char                bg_keep_aspect;
1398    int                 bg_xjust;
1399    int                 bg_yjust;
1400    int                 bg_xperc;
1401    int                 bg_yperc;
1402    char                hiq;
1403    char                userbg;
1404    char                root_hint;
1405    int                 bg_timeout;
1406 } BgDlgData;
1407 
1408 static void         BG_RedrawView(Dialog * d);
1409 static void         BGSettingsGoTo(Dialog * d, Background * bg);
1410 
1411 static void
_DlgApplyBG(Dialog * d,int val __UNUSED__,void * data __UNUSED__)1412 _DlgApplyBG(Dialog * d, int val __UNUSED__, void *data __UNUSED__)
1413 {
1414    BgDlgData          *dd = DLG_DATA_GET(d, BgDlgData);
1415 
1416    Conf.backgrounds.timeout = dd->bg_timeout;
1417    Conf.backgrounds.hiquality = dd->hiq;
1418    Conf.backgrounds.user = dd->userbg;
1419    Conf.hints.set_xroot_info_on_root_window = dd->root_hint;
1420 
1421    COLOR32_FROM_RGB(dd->bg->bg_solid, dd->bg_r, dd->bg_g, dd->bg_b);
1422    dd->bg->bg_tile = dd->bg_tile;
1423    dd->bg->bg.keep_aspect = dd->bg_keep_aspect;
1424    dd->bg->bg.xjust = dd->bg_xjust;
1425    dd->bg->bg.yjust = dd->bg_yjust;
1426    dd->bg->bg.xperc = dd->bg_xperc;
1427    dd->bg->bg.yperc = dd->bg_yperc;
1428    if (!dd->bg_image)
1429       BackgroundFilesRemove(dd->bg);
1430 
1431    BackgroundInvalidate(dd->bg, 1);
1432 
1433    BackgroundCacheMini(dd->bg, 0, 1);
1434    BG_RedrawView(d);
1435 
1436    dd->bg_set = dd->bg;
1437 
1438    autosave();
1439 }
1440 
1441 static void
_DlgBGExit(Dialog * d)1442 _DlgBGExit(Dialog * d)
1443 {
1444    BgDlgData          *dd = DLG_DATA_GET(d, BgDlgData);
1445 
1446    if (dd->bg != dd->bg_set)
1447       DeskBackgroundSet(DesksGetCurrent(), dd->bg_set);
1448 
1449    BackgroundImagesKeep(dd->bg, 0);
1450 }
1451 
1452 /* Draw the background preview image */
1453 static void
CB_DesktopMiniDisplayRedraw(Dialog * d,int val __UNUSED__,void * data __UNUSED__)1454 CB_DesktopMiniDisplayRedraw(Dialog * d,
1455 			    int val __UNUSED__, void *data __UNUSED__)
1456 {
1457    BgDlgData          *dd = DLG_DATA_GET(d, BgDlgData);
1458    Background         *bg;
1459    EX_Pixmap           pmap;
1460    int                 w, h;
1461    Win                 win;
1462    unsigned int        color;
1463    const char         *fbg, *ffg;
1464 
1465    if (!dd->bg)
1466       return;
1467 
1468    win = DialogItemAreaGetWindow(dd->bg_mini_disp);
1469    DialogItemAreaGetSize(dd->bg_mini_disp, &w, &h);
1470 
1471    pmap = EGetWindowBackgroundPixmap(win);
1472    fbg = (dd->bg_image) ? BackgroundGetBgFile(dd->bg) : NULL;
1473    ffg = (dd->bg_image) ? BackgroundGetFgFile(dd->bg) : NULL;
1474    COLOR32_FROM_RGB(color, dd->bg_r, dd->bg_g, dd->bg_b);
1475    bg = BackgroundCreate("TEMP", color,
1476 			 fbg, dd->bg_tile, dd->bg_keep_aspect,
1477 			 dd->bg_xjust, dd->bg_yjust,
1478 			 dd->bg_xperc, dd->bg_yperc,
1479 			 ffg, dd->bg->top.keep_aspect,
1480 			 dd->bg->top.xjust, dd->bg->top.yjust,
1481 			 dd->bg->top.xperc, dd->bg->top.yperc);
1482 
1483    BackgroundApplyPmap(bg, win, pmap, w, h);
1484    BackgroundDestroy(bg);
1485    EClearWindow(win);
1486 }
1487 
1488 static void
BG_DialogSetFileName(DItem * di)1489 BG_DialogSetFileName(DItem * di)
1490 {
1491    Dialog             *d = DialogItemGetDialog(di);
1492    BgDlgData          *dd = DLG_DATA_GET(d, BgDlgData);
1493    const char         *stmp;
1494    char                s[1024];
1495 
1496    stmp = fullfileof(BackgroundGetBgFile(dd->bg));
1497    Esnprintf(s, sizeof(s),
1498 	     _("Background definition information:\nName: %s\nFile: %s"),
1499 	     BackgroundGetName(dd->bg), (stmp) ? stmp : _("-NONE-"));
1500    DialogItemSetText(di, s);
1501 }
1502 
1503 static void
BgDialogSetNewCurrent(Dialog * d,Background * bg)1504 BgDialogSetNewCurrent(Dialog * d, Background * bg)
1505 {
1506    BgDlgData          *dd = DLG_DATA_GET(d, BgDlgData);
1507    int                 r, g, b;
1508 
1509    if (dd->bg && dd->bg != bg)
1510       BackgroundImagesKeep(dd->bg, 0);
1511    dd->bg = bg;
1512    BackgroundImagesKeep(dd->bg, 1);
1513 
1514    /* Update dialog items */
1515    BG_DialogSetFileName(dd->bg_filename);
1516 
1517    COLOR32_TO_RGB(bg->bg_solid, r, g, b);
1518 
1519    DialogItemCheckButtonSetState(dd->di[0], bg->bg.file ? 1 : 0);
1520    DialogItemCheckButtonSetState(dd->di[1], bg->bg.keep_aspect);
1521    DialogItemCheckButtonSetState(dd->di[2], bg->bg_tile);
1522    DialogItemSliderSetVal(dd->di[3], r);
1523    DialogItemSliderSetVal(dd->di[4], g);
1524    DialogItemSliderSetVal(dd->di[5], b);
1525    DialogItemSliderSetVal(dd->di[6], bg->bg.xjust);
1526    DialogItemSliderSetVal(dd->di[7], bg->bg.yjust);
1527    DialogItemSliderSetVal(dd->di[8], bg->bg.yperc);
1528    DialogItemSliderSetVal(dd->di[9], bg->bg.xperc);
1529 
1530    /* Redraw mini BG display */
1531    CB_DesktopMiniDisplayRedraw(d, 0, NULL);
1532 
1533    /* Redraw scrolling BG list */
1534    BG_RedrawView(d);
1535 }
1536 
1537 /* Duplicate current (dd->bg) to new */
1538 static void
CB_ConfigureNewBG(Dialog * d,int val __UNUSED__,void * data __UNUSED__)1539 CB_ConfigureNewBG(Dialog * d, int val __UNUSED__, void *data __UNUSED__)
1540 {
1541    BgDlgData          *dd = DLG_DATA_GET(d, BgDlgData);
1542    char                s[1024];
1543    unsigned int        color;
1544    int                 lower, upper;
1545 
1546    Esnprintf(s, sizeof(s), "__NEWBG_%i", (unsigned)time(NULL));
1547 
1548    COLOR32_FROM_RGB(color, dd->bg_r, dd->bg_g, dd->bg_b);
1549 
1550    dd->bg = BackgroundCreate(s, color,
1551 			     dd->bg->bg.file, dd->bg_tile, dd->bg_keep_aspect,
1552 			     dd->bg_xjust, dd->bg_yjust,
1553 			     dd->bg_xperc, dd->bg_yperc,
1554 			     dd->bg->top.file, dd->bg->top.keep_aspect,
1555 			     dd->bg->top.xjust, dd->bg->top.yjust,
1556 			     dd->bg->top.xperc, dd->bg->top.yperc);
1557 
1558    DialogItemSliderGetBounds(dd->bg_sel_slider, &lower, &upper);
1559    upper += 4;
1560    DialogItemSliderSetBounds(dd->bg_sel_slider, lower, upper);
1561 
1562    DialogItemSliderSetVal(dd->bg_sel_slider, 0);
1563 
1564    DeskBackgroundSet(DesksGetCurrent(), dd->bg);
1565 
1566    BG_RedrawView(d);
1567 
1568    autosave();
1569 }
1570 
1571 static void
CB_ConfigureDelBG(Dialog * d,int val,void * data __UNUSED__)1572 CB_ConfigureDelBG(Dialog * d, int val, void *data __UNUSED__)
1573 {
1574    BgDlgData          *dd = DLG_DATA_GET(d, BgDlgData);
1575    Background         *bg, *bgn;
1576    int                 err, lower, upper;
1577 
1578    bg = LIST_CHECK(Background, &bg_list, dd->bg);
1579    if (!bg)
1580       return;
1581    if (BackgroundIsNone(bg))
1582       return;
1583 
1584    bgn = LIST_NEXT(Background, &bg_list, bg);
1585    if (!bgn)
1586       bgn = LIST_PREV(Background, &bg_list, bg);
1587 
1588    DeskBackgroundSet(DesksGetCurrent(), bgn);
1589 
1590    if (val == 0)
1591       err = BackgroundDestroy(bg);
1592    else
1593       err = BackgroundDelete(bg);
1594 
1595    if (!err)
1596      {
1597 	DialogItemSliderGetBounds(dd->bg_sel_slider, &lower, &upper);
1598 	upper -= 4;
1599 	DialogItemSliderSetBounds(dd->bg_sel_slider, lower, upper);
1600 	if (dd->bg_sel_sliderval > upper)
1601 	   DialogItemSliderSetVal(dd->bg_sel_slider, upper);
1602      }
1603 
1604    dd->bg = NULL;
1605    BgDialogSetNewCurrent(d, bgn);
1606 
1607    autosave();
1608 }
1609 
1610 /* Move current background to first position in list */
1611 static void
CB_ConfigureFrontBG(Dialog * d,int val __UNUSED__,void * data __UNUSED__)1612 CB_ConfigureFrontBG(Dialog * d, int val __UNUSED__, void *data __UNUSED__)
1613 {
1614    BgDlgData          *dd = DLG_DATA_GET(d, BgDlgData);
1615    Background         *bg;
1616 
1617    if (BackgroundIsNone(dd->bg))
1618       return;			/* Don't move "None" background */
1619 
1620    bg = LIST_REMOVE(Background, &bg_list, dd->bg);
1621    LIST_PREPEND(Background, &bg_list, bg);
1622    BGSettingsGoTo(d, bg);
1623    BG_RedrawView(d);
1624    autosave();
1625 }
1626 
1627 /* Draw the scrolling background image window */
1628 static void
BG_RedrawView(Dialog * d)1629 BG_RedrawView(Dialog * d)
1630 {
1631    BgDlgData          *dd = DLG_DATA_GET(d, BgDlgData);
1632    Background         *bg;
1633    int                 x, w, h, num;
1634    Win                 win;
1635    EX_Pixmap           pmap;
1636    ImageClass         *ic;
1637    int                 mini_w = Mode.backgrounds.mini_w;
1638    int                 mini_h = Mode.backgrounds.mini_h;
1639 
1640    num = LIST_GET_COUNT(&bg_list);
1641    if (num <= 0)
1642       return;
1643 
1644    win = DialogItemAreaGetWindow(dd->bg_sel);
1645    DialogItemAreaGetSize(dd->bg_sel, &w, &h);
1646 
1647    pmap = EGetWindowBackgroundPixmap(win);
1648 
1649    ic = ImageclassFind("DIALOG_BUTTON", 0);
1650    if (!ic)
1651       ic = ImageclassFind("DIALOG_WIDGET_BUTTON", 1);
1652 
1653    ImageclassApplySimple(ic, win, pmap, STATE_NORMAL, 0, 0, w, h);
1654 
1655    x = -(num * (mini_w + 8) - w) * dd->bg_sel_sliderval / (4 * num);
1656 
1657    LIST_FOR_EACH(Background, &bg_list, bg)
1658    {
1659       if (((x + mini_w + 8) >= 0) && (x < w))
1660 	{
1661 	   EImage             *im;
1662 
1663 	   ImageclassApplySimple(ic, win, pmap,
1664 				 (bg == dd->bg) ? STATE_CLICKED : STATE_NORMAL,
1665 				 x, 0, mini_w + 8, mini_h + 8);
1666 
1667 	   if (BackgroundIsNone(bg))
1668 	     {
1669 		TextClass          *tc;
1670 
1671 		tc = TextclassFind("DIALOG", 1);
1672 		if (tc)
1673 		  {
1674 		     int                 tw, th;
1675 
1676 		     TextSize(tc, 0, 0, STATE_NORMAL,
1677 			      _("No\nBackground"), &tw, &th, 17);
1678 		     TextDraw(tc, win, pmap, 0, 0, STATE_NORMAL,
1679 			      _("No\nBackground"), x + 4,
1680 			      4 + ((mini_h - th) / 2), mini_w, mini_h, 17, 512);
1681 		  }
1682 	     }
1683 	   else
1684 	     {
1685 		im = BackgroundCacheMini(bg, 1, 0);
1686 		if (im)
1687 		  {
1688 		     EImageRenderOnDrawable(im, win, pmap, 0, x + 4, 4,
1689 					    mini_w, mini_h);
1690 		     EImageFree(im);
1691 		  }
1692 	     }
1693 	}
1694       x += (mini_w + 8);
1695    }
1696 
1697    EClearWindow(win);
1698 }
1699 
1700 static void
CB_BGAreaSlide(Dialog * d,int val __UNUSED__,void * data __UNUSED__)1701 CB_BGAreaSlide(Dialog * d, int val __UNUSED__, void *data __UNUSED__)
1702 {
1703    BgDlgData          *dd = DLG_DATA_GET(d, BgDlgData);
1704 
1705    if (dd->bg_sel_sliderval == dd->bg_sel_sliderval_old)
1706       return;
1707    BG_RedrawView(d);
1708    dd->bg_sel_sliderval_old = dd->bg_sel_sliderval;
1709 }
1710 
1711 static void
CB_BGScan(Dialog * d,int val __UNUSED__,void * data __UNUSED__)1712 CB_BGScan(Dialog * d, int val __UNUSED__, void *data __UNUSED__)
1713 {
1714    BgDlgData          *dd = DLG_DATA_GET(d, BgDlgData);
1715    int                 num;
1716 
1717    SoundPlay(SOUND_WAIT);
1718 
1719    /* Forcing re-scan should not be necessary but provides the progress bars
1720     * so it actually looks like something is going on */
1721    Mode.backgrounds.force_scan = 1;
1722    ScanBackgroundMenu();
1723 
1724    num = LIST_GET_COUNT(&bg_list);
1725    DialogItemSliderSetBounds(dd->bg_sel_slider, 0, num * 4);
1726    DialogItemCallCallback(d, dd->bg_sel_slider);
1727 }
1728 
1729 static void
CB_BGAreaEvent(DItem * di,int val __UNUSED__,void * data)1730 CB_BGAreaEvent(DItem * di, int val __UNUSED__, void *data)
1731 {
1732    Dialog             *d = DialogItemGetDialog(di);
1733    BgDlgData          *dd = DLG_DATA_GET(d, BgDlgData);
1734    int                 x, num, w, h;
1735    Background         *bg;
1736    XEvent             *ev = (XEvent *) data;
1737    int                 mini_w = Mode.backgrounds.mini_w;
1738 
1739    DialogItemAreaGetSize(di, &w, &h);
1740 
1741    switch (ev->type)
1742      {
1743      case ButtonPress:
1744 	switch (ev->xbutton.button)
1745 	  {
1746 	  case 1:
1747 	     num = LIST_GET_COUNT(&bg_list);
1748 	     x = (num * (mini_w + 8) - w) * dd->bg_sel_sliderval / (4 * num) +
1749 		ev->xbutton.x;
1750 	     x /= mini_w + 8;
1751 	     bg = LIST_GET_BY_INDEX(Background, &bg_list, x);
1752 	     if (!bg || bg == DeskBackgroundGet(DesksGetCurrent()))
1753 		break;
1754 	     BgDialogSetNewCurrent(d, bg);
1755 	     DeskBackgroundSet(DesksGetCurrent(), bg);
1756 	     autosave();
1757 	     break;
1758 	  case 4:
1759 	     dd->bg_sel_sliderval += 4;
1760 	     goto do_slide;
1761 	  case 5:
1762 	     dd->bg_sel_sliderval -= 4;
1763 	     goto do_slide;
1764 	   do_slide:
1765 	     DialogItemSliderSetVal(dd->bg_sel_slider, dd->bg_sel_sliderval);
1766 	     CB_BGAreaSlide(d, 0, NULL);
1767 	     break;
1768 	  }
1769      }
1770 }
1771 
1772 static void
CB_DesktopTimeout(Dialog * d,int val __UNUSED__,void * data)1773 CB_DesktopTimeout(Dialog * d, int val __UNUSED__, void *data)
1774 {
1775    BgDlgData          *dd = DLG_DATA_GET(d, BgDlgData);
1776    DItem              *di = (DItem *) data;
1777    char                s[256];
1778 
1779    Esnprintf(s, sizeof(s), _("Unused backgrounds freed after %2i:%02i:%02i"),
1780 	     dd->bg_timeout / 3600,
1781 	     (dd->bg_timeout / 60) - (60 * (dd->bg_timeout / 3600)),
1782 	     (dd->bg_timeout) - (60 * (dd->bg_timeout / 60)));
1783    DialogItemSetText(di, s);
1784 }
1785 
1786 static void
BGSettingsGoTo(Dialog * d,Background * bg)1787 BGSettingsGoTo(Dialog * d, Background * bg)
1788 {
1789    BgDlgData          *dd = DLG_DATA_GET(d, BgDlgData);
1790    int                 i, num;
1791 
1792    if (!dd->bg_sel_slider)
1793       return;
1794 
1795    num = LIST_GET_COUNT(&bg_list);
1796    if (num <= 0)
1797       return;
1798    i = LIST_GET_INDEX(Background, &bg_list, bg);
1799    if (i < 0)
1800       return;
1801    i = ((4 * num + 20) * i) / num - 8;
1802    if (i < 0)
1803       i = 0;
1804    else if (i > 4 * num)
1805       i = 4 * num;
1806    DialogItemSliderSetVal(dd->bg_sel_slider, i);
1807    BgDialogSetNewCurrent(d, bg);
1808 }
1809 
1810 static void
CB_BGNext(Dialog * d,int val,void * data __UNUSED__)1811 CB_BGNext(Dialog * d, int val, void *data __UNUSED__)
1812 {
1813    BgDlgData          *dd = DLG_DATA_GET(d, BgDlgData);
1814    Background         *bg;
1815 
1816    bg = dd->bg;
1817    if (val >= 0)
1818      {
1819 	while (bg && val--)
1820 	   bg = LIST_NEXT(Background, &bg_list, bg);
1821      }
1822    else
1823      {
1824 	while (bg && val++)
1825 	   bg = LIST_PREV(Background, &bg_list, bg);
1826      }
1827 
1828    if (!bg)
1829       return;
1830 
1831    BGSettingsGoTo(d, bg);
1832    DeskBackgroundSet(DesksGetCurrent(), bg);
1833 }
1834 
1835 static int
BG_SortFileCompare(const void * _bg1,const void * _bg2)1836 BG_SortFileCompare(const void *_bg1, const void *_bg2)
1837 {
1838    const Background   *bg1 = *(const Background **)_bg1;
1839    const Background   *bg2 = *(const Background **)_bg2;
1840    const char         *name1, *name2;
1841 
1842    /* return < 0 is b1 <  b2 */
1843    /* return > 0 is b1 >  b2 */
1844    /* return   0 is b1 == b2 */
1845 
1846    name1 = BackgroundGetBgFile(bg1);
1847    name2 = BackgroundGetBgFile(bg2);
1848    if (name1 && name2)
1849       return strcmp(name1, name2);
1850    if (name1)
1851       return 1;
1852    if (name2)
1853       return -1;
1854    return (bg1 < bg2) ? -1 : 1;
1855 }
1856 
1857 static void
CB_BGSortFile(Dialog * d,int val __UNUSED__,void * data __UNUSED__)1858 CB_BGSortFile(Dialog * d, int val __UNUSED__, void *data __UNUSED__)
1859 {
1860    BgDlgData          *dd = DLG_DATA_GET(d, BgDlgData);
1861    Background        **bglist;
1862    int                 i, num;
1863 
1864    bglist = LIST_GET_ITEMS(Background, &bg_list, &num);
1865    if (!bglist)
1866       return;
1867 
1868    /* remove them all from the list */
1869    LIST_INIT(Background, &bg_list);
1870    qsort(bglist, num - 1, sizeof(Background *), BG_SortFileCompare);
1871    for (i = 0; i < num; i++)
1872       LIST_APPEND(Background, &bg_list, bglist[i]);
1873 
1874    Efree(bglist);
1875 
1876    BGSettingsGoTo(d, dd->bg);
1877 
1878    autosave();
1879 }
1880 
1881 static void
CB_BGSortAttrib(Dialog * d,int val __UNUSED__,void * data __UNUSED__)1882 CB_BGSortAttrib(Dialog * d, int val __UNUSED__, void *data __UNUSED__)
1883 {
1884    BgDlgData          *dd = DLG_DATA_GET(d, BgDlgData);
1885    Background        **bglist, *bg;
1886    int                 i, num;
1887 
1888    bglist = LIST_GET_ITEMS(Background, &bg_list, &num);
1889    if (!bglist)
1890       return;
1891 
1892    /* remove them all from the list */
1893    LIST_INIT(Background, &bg_list);
1894    for (i = 0; i < num; i++)
1895      {
1896 	bg = bglist[i];
1897 	if ((bg) && (bg->bg_tile) && (bg->bg.xperc == 0) && (bg->bg.yperc == 0))
1898 	  {
1899 	     LIST_APPEND(Background, &bg_list, bg);
1900 	     bglist[i] = NULL;
1901 	  }
1902      }
1903    for (i = 0; i < num; i++)
1904      {
1905 	bg = bglist[i];
1906 	if (bg)
1907 	  {
1908 	     LIST_APPEND(Background, &bg_list, bg);
1909 	     bglist[i] = NULL;
1910 	  }
1911      }
1912 
1913    Efree(bglist);
1914 
1915    BGSettingsGoTo(d, dd->bg);
1916 
1917    autosave();
1918 }
1919 
1920 #if 0				/* Doesn't do anything useful */
1921 static void
1922 CB_BGSortContent(Dialog * d __UNUSED__, int val __UNUSED__,
1923 		 void *data __UNUSED__)
1924 {
1925    Background        **bglist;
1926    int                 i, num;
1927 
1928    bglist = LIST_GET_ITEMS(Background, &bg_list, &num);
1929    if (!bglist)
1930       return;
1931 
1932    /* remove them all from the list */
1933    LIST_INIT(Background, &bg_list);
1934    for (i = 0; i < num; i++)
1935       LIST_PREPEND(Background, &bg_list, bglist[i]);
1936 
1937    Efree(bglist);
1938 
1939    autosave();
1940 }
1941 #endif
1942 
1943 static void
CB_InitView(DItem * di,int val __UNUSED__,void * data __UNUSED__)1944 CB_InitView(DItem * di, int val __UNUSED__, void *data __UNUSED__)
1945 {
1946    Dialog             *d = DialogItemGetDialog(di);
1947    BgDlgData          *dd = DLG_DATA_GET(d, BgDlgData);
1948 
1949    dd->bg_sel_sliderval_old = dd->bg_sel_sliderval = -1;
1950    BGSettingsGoTo(d, dd->bg);
1951 }
1952 
1953 static void
_DlgFillBackground(Dialog * d,DItem * table,void * data)1954 _DlgFillBackground(Dialog * d, DItem * table, void *data)
1955 {
1956    BgDlgData          *dd = DLG_DATA_GET(d, BgDlgData);
1957    Background         *bg = (Background *) data;
1958    DItem              *di, *table2, *table3, *label;
1959    int                 i, num;
1960    char                s[1024];
1961    int                 mini_w = Mode.backgrounds.mini_w;
1962    int                 mini_h = Mode.backgrounds.mini_h;
1963 
1964    if (!Conf.backgrounds.no_scan)
1965       ScanBackgroundMenu();
1966 
1967    if (!bg)
1968       bg = DeskBackgroundGet(DesksGetCurrent());
1969    if (!bg)
1970       bg = BackgroundFind("NONE");
1971    dd->bg = bg;
1972    dd->bg_set = bg;
1973 
1974    dd->bg_image = (dd->bg->bg.file) ? 1 : 0;
1975 
1976    COLOR32_TO_RGB(dd->bg->bg_solid, dd->bg_r, dd->bg_g, dd->bg_b);
1977    dd->bg_tile = dd->bg->bg_tile;
1978    dd->bg_keep_aspect = dd->bg->bg.keep_aspect;
1979    dd->bg_xjust = dd->bg->bg.xjust;
1980    dd->bg_yjust = dd->bg->bg.yjust;
1981    dd->bg_xperc = dd->bg->bg.xperc;
1982    dd->bg_yperc = dd->bg->bg.yperc;
1983 
1984    dd->hiq = Conf.backgrounds.hiquality;
1985    dd->userbg = Conf.backgrounds.user;
1986    dd->root_hint = Conf.hints.set_xroot_info_on_root_window;
1987    dd->bg_timeout = Conf.backgrounds.timeout;
1988 
1989    DialogItemTableSetOptions(table, 1, 0, 0, 0);
1990 
1991    table2 = DialogAddItem(table, DITEM_TABLE);
1992    DialogItemTableSetOptions(table2, 2, 0, 1, 0);
1993 
1994    di = dd->bg_filename = DialogAddItem(table2, DITEM_TEXT);
1995    DialogItemSetFill(di, 1, 0);
1996    BG_DialogSetFileName(dd->bg_filename);
1997 
1998    table3 = DialogAddItem(table2, DITEM_TABLE);
1999 
2000    di = dd->di[0] = DialogAddItem(table3, DITEM_CHECKBUTTON);
2001    DialogItemSetText(di, _("Use background image"));
2002    DialogItemCheckButtonSetPtr(di, &dd->bg_image);
2003 
2004    di = dd->di[1] = DialogAddItem(table3, DITEM_CHECKBUTTON);
2005    DialogItemSetText(di, _("Keep aspect on scale"));
2006    DialogItemCheckButtonSetPtr(di, &dd->bg_keep_aspect);
2007 
2008    di = dd->di[2] = DialogAddItem(table3, DITEM_CHECKBUTTON);
2009    DialogItemSetText(di, _("Tile image across background"));
2010    DialogItemCheckButtonSetPtr(di, &dd->bg_tile);
2011 
2012    table2 = DialogAddItem(table, DITEM_TABLE);
2013    DialogItemTableSetOptions(table2, 4, 0, 1, 0);
2014    DialogItemSetFill(table2, 0, 0);
2015    DialogItemSetAlign(table2, 512, 0);
2016 
2017    di = DialogAddItem(table2, DITEM_BUTTON);
2018    DialogItemSetText(di, _("Move to Front"));
2019    DialogItemSetCallback(di, CB_ConfigureFrontBG, 0, NULL);
2020    DialogBindKey(d, "F", CB_ConfigureFrontBG, 0, NULL);
2021 
2022    di = DialogAddItem(table2, DITEM_BUTTON);
2023    DialogItemSetText(di, _("Duplicate"));
2024    DialogItemSetCallback(di, CB_ConfigureNewBG, 0, NULL);
2025 
2026    di = DialogAddItem(table2, DITEM_BUTTON);
2027    DialogItemSetText(di, _("Unlist"));
2028    DialogItemSetCallback(di, CB_ConfigureDelBG, 0, NULL);
2029    DialogBindKey(d, "D", CB_ConfigureDelBG, 0, NULL);
2030 
2031    di = DialogAddItem(table2, DITEM_BUTTON);
2032    DialogItemSetText(di, _("Delete File"));
2033    DialogItemSetCallback(di, CB_ConfigureDelBG, 1, NULL);
2034    DialogBindKey(d, "Delete", CB_ConfigureDelBG, 1, NULL);
2035 
2036    table2 = DialogAddItem(table, DITEM_TABLE);
2037    DialogItemTableSetOptions(table2, 3, 0, 1, 0);
2038 
2039    di = DialogAddItem(table2, DITEM_TEXT);
2040    DialogItemSetFill(di, 0, 0);
2041    DialogItemSetAlign(di, 512, 512);
2042    DialogItemSetText(di,
2043 		     _("Background\n" "Image\n" "Scaling\n" "and\n"
2044 		       "Alignment\n"));
2045 
2046    table3 = DialogAddItem(table2, DITEM_TABLE);
2047    DialogItemTableSetOptions(table3, 3, 0, 0, 0);
2048 
2049    DialogAddItem(table3, DITEM_NONE);
2050 
2051    di = dd->di[6] = DialogAddItem(table3, DITEM_SLIDER);
2052    DialogItemSliderSetMinLength(di, 10);
2053    DialogItemSliderSetBounds(di, 0, 1024);
2054    DialogItemSliderSetUnits(di, 1);
2055    DialogItemSliderSetJump(di, mini_w);
2056    DialogItemSliderSetValPtr(di, &dd->bg_xjust);
2057 
2058    DialogAddItem(table3, DITEM_NONE);
2059 
2060    di = dd->di[7] = DialogAddItem(table3, DITEM_SLIDER);
2061    DialogItemSliderSetMinLength(di, 10);
2062    DialogItemSliderSetOrientation(di, 0);
2063    DialogItemSetFill(di, 0, 1);
2064    DialogItemSliderSetBounds(di, 0, 1024);
2065    DialogItemSliderSetUnits(di, 1);
2066    DialogItemSliderSetJump(di, mini_w);
2067    DialogItemSliderSetValPtr(di, &dd->bg_yjust);
2068 
2069    di = dd->bg_mini_disp = DialogAddItem(table3, DITEM_AREA);
2070    DialogItemAreaSetSize(di, mini_w, mini_h);
2071 
2072    di = dd->di[8] = DialogAddItem(table3, DITEM_SLIDER);
2073    DialogItemSliderSetMinLength(di, 10);
2074    DialogItemSliderSetOrientation(di, 0);
2075    DialogItemSetFill(di, 0, 1);
2076    DialogItemSliderSetBounds(di, 0, 1024);
2077    DialogItemSliderSetUnits(di, 1);
2078    DialogItemSliderSetJump(di, mini_w);
2079    DialogItemSliderSetValPtr(di, &dd->bg_yperc);
2080 
2081    DialogAddItem(table3, DITEM_NONE);
2082 
2083    di = dd->di[9] = DialogAddItem(table3, DITEM_SLIDER);
2084    DialogItemSliderSetMinLength(di, 10);
2085    DialogItemSliderSetBounds(di, 0, 1024);
2086    DialogItemSliderSetUnits(di, 1);
2087    DialogItemSliderSetJump(di, mini_w);
2088    DialogItemSliderSetValPtr(di, &dd->bg_xperc);
2089 
2090    table3 = DialogAddItem(table2, DITEM_TABLE);
2091    DialogItemTableSetOptions(table3, 2, 0, 0, 0);
2092 
2093    di = DialogAddItem(table3, DITEM_TEXT);
2094    DialogItemSetColSpan(di, 2);
2095    DialogItemSetFill(di, 0, 0);
2096    DialogItemSetAlign(di, 512, 512);
2097    DialogItemSetText(di, _("BG Colour"));
2098 
2099    di = DialogAddItem(table3, DITEM_TEXT);
2100    DialogItemSetFill(di, 0, 0);
2101    DialogItemSetAlign(di, 1024, 512);
2102    DialogItemSetText(di, _("Red:"));
2103 
2104    di = dd->di[3] = DialogAddItem(table3, DITEM_SLIDER);
2105    DialogItemSliderSetBounds(di, 0, 255);
2106    DialogItemSliderSetUnits(di, 1);
2107    DialogItemSliderSetJump(di, 16);
2108    DialogItemSliderSetValPtr(di, &dd->bg_r);
2109 
2110    di = DialogAddItem(table3, DITEM_TEXT);
2111    DialogItemSetFill(di, 0, 0);
2112    DialogItemSetAlign(di, 1024, 512);
2113    DialogItemSetText(di, _("Green:"));
2114 
2115    di = dd->di[4] = DialogAddItem(table3, DITEM_SLIDER);
2116    DialogItemSliderSetBounds(di, 0, 255);
2117    DialogItemSliderSetUnits(di, 1);
2118    DialogItemSliderSetJump(di, 16);
2119    DialogItemSliderSetValPtr(di, &dd->bg_g);
2120 
2121    di = DialogAddItem(table3, DITEM_TEXT);
2122    DialogItemSetFill(di, 0, 0);
2123    DialogItemSetAlign(di, 1024, 512);
2124    DialogItemSetText(di, _("Blue:"));
2125 
2126    di = dd->di[5] = DialogAddItem(table3, DITEM_SLIDER);
2127    DialogItemSliderSetBounds(di, 0, 255);
2128    DialogItemSliderSetUnits(di, 1);
2129    DialogItemSliderSetJump(di, 16);
2130    DialogItemSliderSetValPtr(di, &dd->bg_b);
2131 
2132    for (i = 0; i < 10; i++)
2133       DialogItemSetCallback(dd->di[i], CB_DesktopMiniDisplayRedraw, 0, NULL);
2134 
2135    DialogAddItem(table, DITEM_SEPARATOR);
2136 
2137    table2 = DialogAddItem(table, DITEM_TABLE);
2138    DialogItemTableSetOptions(table2, 3, 0, 0, 0);
2139 
2140    table3 = DialogAddItem(table2, DITEM_TABLE);
2141    DialogItemTableSetOptions(table3, 2, 0, 0, 0);
2142 
2143    di = DialogAddItem(table3, DITEM_BUTTON);
2144    DialogItemSetFill(di, 0, 0);
2145    DialogItemSetText(di, "<-");
2146    DialogItemSetCallback(di, CB_BGNext, -1, NULL);
2147    DialogBindKey(d, "Left", CB_BGNext, -1, NULL);
2148 
2149    di = DialogAddItem(table3, DITEM_BUTTON);
2150    DialogItemSetFill(di, 0, 0);
2151    DialogItemSetText(di, "->");
2152    DialogItemSetCallback(di, CB_BGNext, 1, NULL);
2153    DialogBindKey(d, "Right", CB_BGNext, 1, NULL);
2154 
2155    di = DialogAddItem(table2, DITEM_BUTTON);
2156    DialogItemSetFill(di, 0, 0);
2157    DialogItemSetText(di, _("Pre-scan BG's"));
2158    DialogItemSetCallback(di, CB_BGScan, 0, NULL);
2159 
2160    table3 = DialogAddItem(table2, DITEM_TABLE);
2161    DialogItemTableSetOptions(table3, 3, 0, 0, 0);
2162 
2163    di = DialogAddItem(table3, DITEM_BUTTON);
2164    DialogItemSetFill(di, 0, 0);
2165    DialogItemSetText(di, _("Sort by File"));
2166    DialogItemSetCallback(di, CB_BGSortFile, 0, NULL);
2167 
2168    di = DialogAddItem(table3, DITEM_BUTTON);
2169    DialogItemSetFill(di, 0, 0);
2170    DialogItemSetText(di, _("Sort by Attr."));
2171    DialogItemSetCallback(di, CB_BGSortAttrib, 0, NULL);
2172 
2173 #if 0				/* Doesn't do anything useful */
2174    di = DialogAddItem(table3, DITEM_BUTTON);
2175    DialogItemSetFill(di, 0, 0);
2176    DialogItemSetText(di, _("Sort by Image"));
2177    DialogItemSetCallback(di, CB_BGSortContent, 0, NULL);
2178 #endif
2179 
2180    di = dd->bg_sel = DialogAddItem(table, DITEM_AREA);
2181    DialogItemAreaSetSize(di, 160, 8 + Mode.backgrounds.mini_h);
2182    DialogItemAreaSetEventFunc(di, CB_BGAreaEvent);
2183    DialogItemAreaSetInitFunc(di, CB_InitView);
2184 
2185    num = LIST_GET_COUNT(&bg_list);
2186    di = dd->bg_sel_slider = DialogAddItem(table, DITEM_SLIDER);
2187    DialogItemSliderSetBounds(di, 0, num * 4);
2188    DialogItemSliderSetUnits(di, 1);
2189    DialogItemSliderSetJump(di, 9);
2190    DialogItemSliderSetValPtr(di, &dd->bg_sel_sliderval);
2191    DialogItemSetCallback(dd->bg_sel_slider, CB_BGAreaSlide, 0, NULL);
2192 
2193    DialogAddItem(table, DITEM_SEPARATOR);
2194 
2195    di = DialogAddItem(table, DITEM_CHECKBUTTON);
2196    DialogItemSetText(di, _("Use dithering in Hi-Colour"));
2197    DialogItemCheckButtonSetPtr(di, &dd->hiq);
2198 
2199    di = DialogAddItem(table, DITEM_CHECKBUTTON);
2200    DialogItemSetText(di, _("Background overrides theme"));
2201    DialogItemCheckButtonSetPtr(di, &dd->userbg);
2202 
2203    di = DialogAddItem(table, DITEM_CHECKBUTTON);
2204    DialogItemSetText(di,
2205 		     _("Enable background transparency compatibility mode"));
2206    DialogItemCheckButtonSetPtr(di, &dd->root_hint);
2207 
2208    DialogAddItem(table, DITEM_SEPARATOR);
2209 
2210    di = label = DialogAddItem(table, DITEM_TEXT);
2211    DialogItemSetAlign(di, 512, 512);
2212    Esnprintf(s, sizeof(s), _("Unused backgrounds freed after %2i:%02i:%02i"),
2213 	     dd->bg_timeout / 3600,
2214 	     (dd->bg_timeout / 60) - (60 * (dd->bg_timeout / 3600)),
2215 	     (dd->bg_timeout) - (60 * (dd->bg_timeout / 60)));
2216    DialogItemSetText(di, s);
2217 
2218    di = DialogAddItem(table, DITEM_SLIDER);
2219    DialogItemSliderSetMinLength(di, 10);
2220    DialogItemSliderSetBounds(di, 0, 60 * 60 * 4);
2221    DialogItemSliderSetUnits(di, 30);
2222    DialogItemSliderSetJump(di, 60);
2223    DialogItemSliderSetValPtr(di, &dd->bg_timeout);
2224    DialogItemSetCallback(di, CB_DesktopTimeout, 0, label);
2225 }
2226 
2227 const DialogDef     DlgBackground = {
2228    "CONFIGURE_BG",
2229    N_("Background"), N_("Desktop Background Settings"),
2230    sizeof(BgDlgData),
2231    SOUND_SETTINGS_BG,
2232    "pix/bg.png",
2233    N_("Enlightenment Desktop\n" "Background Settings Dialog"),
2234    _DlgFillBackground,
2235    DLG_OAC, _DlgApplyBG, _DlgBGExit
2236 };
2237 
2238 #endif /* ENABLE_DIALOGS */
2239 
2240 /*
2241  * IPC functions
2242  */
2243 
2244 static void
BackgroundSet1(const char * name,const char * params)2245 BackgroundSet1(const char *name, const char *params)
2246 {
2247    const char         *p = params;
2248    char                type[FILEPATH_LEN_MAX];
2249    int                 len, value;
2250    Background         *bg;
2251    unsigned int        color;
2252 
2253    if (!p || !p[0])
2254       return;
2255 
2256    bg = BackgroundFind(name);
2257    if (!bg)
2258      {
2259 	COLOR32_FROM_RGB(color, 0, 0, 0);
2260 	bg = BackgroundCreate(name, color, NULL, 0, 0, 0,
2261 			      0, 0, 0, NULL, 0, 0, 0, 0, 0);
2262 	if (!bg)
2263 	  {
2264 	     IpcPrintf("Error: could not create background '%s'\n", name);
2265 	     return;
2266 	  }
2267      }
2268 
2269    type[0] = '\0';
2270    len = 0;
2271    sscanf(p, "%400s %n", type, &len);
2272    p += len;
2273    value = atoi(p);
2274 
2275    if (!strcmp(type, "bg.solid"))
2276      {
2277 	int                 r, b, g;
2278 
2279 	r = g = b = 0;
2280 	sscanf(p, "%i %i %i", &r, &g, &b);
2281 	COLOR32_FROM_RGB(bg->bg_solid, r, g, b);
2282      }
2283    else if (!strcmp(type, "bg.file"))
2284      {
2285 	EFREE_DUP(bg->bg.file, p);
2286      }
2287    else if (!strcmp(type, "bg.tile"))
2288      {
2289 	bg->bg_tile = value;
2290      }
2291    else if (!strcmp(type, "bg.keep_aspect"))
2292      {
2293 	bg->bg.keep_aspect = value;
2294      }
2295    else if (!strcmp(type, "bg.xjust"))
2296      {
2297 	bg->bg.xjust = value;
2298      }
2299    else if (!strcmp(type, "bg.yjust"))
2300      {
2301 	bg->bg.yjust = value;
2302      }
2303    else if (!strcmp(type, "bg.xperc"))
2304      {
2305 	bg->bg.xperc = value;
2306      }
2307    else if (!strcmp(type, "bg.yperc"))
2308      {
2309 	bg->bg.yperc = value;
2310      }
2311    else if (!strcmp(type, "top.file"))
2312      {
2313 	EFREE_DUP(bg->top.file, p);
2314      }
2315    else if (!strcmp(type, "top.keep_aspect"))
2316      {
2317 	bg->top.keep_aspect = value;
2318      }
2319    else if (!strcmp(type, "top.xjust"))
2320      {
2321 	bg->top.xjust = value;
2322      }
2323    else if (!strcmp(type, "top.yjust"))
2324      {
2325 	bg->top.yjust = value;
2326      }
2327    else if (!strcmp(type, "top.xperc"))
2328      {
2329 	bg->top.xperc = value;
2330      }
2331    else if (!strcmp(type, "top.yperc"))
2332      {
2333 	bg->top.yperc = value;
2334      }
2335    else
2336      {
2337 	IpcPrintf("Error: unknown background value type '%s'\n", type);
2338      }
2339    autosave();
2340 }
2341 
2342 static void
BackgroundSet2(const char * name,const char * params)2343 BackgroundSet2(const char *name, const char *params)
2344 {
2345    Background         *bg;
2346    unsigned int        color;
2347    int                 r, g, b;
2348    char                bgf[FILEPATH_LEN_MAX], topf[FILEPATH_LEN_MAX];
2349    int                 tile, keep_aspect, tkeep_aspect;
2350    int                 xjust, yjust, xperc, yperc;
2351    int                 txjust, tyjust, txperc, typerc;
2352 
2353    if (!params)
2354       return;
2355 
2356    bgf[0] = topf[0] = '\0';
2357    r = g = b = 99;
2358    sscanf(params,
2359 	  "%i %i %i %4000s %i %i %i %i %i %i %4000s %i %i %i %i %i",
2360 	  &r, &g, &b,
2361 	  bgf, &tile, &keep_aspect, &xjust, &yjust, &xperc, &yperc,
2362 	  topf, &tkeep_aspect, &txjust, &tyjust, &txperc, &typerc);
2363    COLOR32_FROM_RGB(color, r, g, b);
2364 
2365    bg = BackgroundFind(name);
2366    if (bg)
2367      {
2368 	BackgroundModify(bg, color, bgf, tile, keep_aspect, xjust,
2369 			 yjust, xperc, yperc, topf, tkeep_aspect,
2370 			 txjust, tyjust, txperc, typerc);
2371      }
2372    else
2373      {
2374 	BackgroundCreate(name, color, bgf, tile, keep_aspect, xjust,
2375 			 yjust, xperc, yperc, topf, tkeep_aspect,
2376 			 txjust, tyjust, txperc, typerc);
2377      }
2378 }
2379 
2380 static void
BackgroundsIpc(const char * params)2381 BackgroundsIpc(const char *params)
2382 {
2383    const char         *p;
2384    char                cmd[128], prm[128], buf[4096];
2385    int                 i, len, num, len2;
2386    Background         *bg;
2387 
2388    len = len2 = 0;
2389    cmd[0] = prm[0] = '\0';
2390    p = params;
2391    if (p)
2392      {
2393 	sscanf(p, "%100s %n%100s %n", cmd, &len2, prm, &len);
2394 	p += len;
2395      }
2396 
2397    if (!p || cmd[0] == '?')
2398      {
2399 	for (i = 0; i < (int)DesksGetNumber(); i++)
2400 	  {
2401 	     bg = DeskBackgroundGet(DeskGet(i));
2402 	     if (bg)
2403 		IpcPrintf("%i %s\n", i, BackgroundGetName(bg));
2404 	     else
2405 		IpcPrintf("%i %s\n", i, "-NONE-");
2406 	  }
2407      }
2408    else if (!strncmp(cmd, "apply", 2))
2409      {
2410 	EX_Window           xwin;
2411 	Win                 win;
2412 
2413 	bg = BackgroundFind(prm);
2414 	if (!bg)
2415 	   return;
2416 
2417 	xwin = NoXID;
2418 	sscanf(p, "%x", &xwin);
2419 
2420 	win = ECreateWinFromXwin(xwin);
2421 	if (!win)
2422 	   return;
2423 	BackgroundApplyWin(bg, win);
2424 	EDestroyWin(win);
2425      }
2426    else if (!strncmp(cmd, "del", 2))
2427      {
2428 	BackgroundDestroyByName(prm);
2429      }
2430    else if (!strncmp(cmd, "list", 2))
2431      {
2432 	LIST_FOR_EACH(Background, &bg_list, bg) IpcPrintf("%s\n", bg->name);
2433      }
2434    else if (!strncmp(cmd, "load", 2))
2435      {
2436 	bg = BackgroundFind(prm);
2437 	if (bg)
2438 	  {
2439 	     IpcPrintf("Background already defined\n");
2440 	  }
2441 	else
2442 	  {
2443 	     BrackgroundCreateFromImage(prm, p, NULL, 0);
2444 	  }
2445      }
2446    else if (!strncmp(cmd, "set", 2))
2447      {
2448 	BackgroundSet1(prm, p);
2449      }
2450    else if (!strncmp(cmd, "show", 2))
2451      {
2452 	bg = BackgroundFind(prm);
2453 
2454 	if (bg)
2455 	  {
2456 	     BackgroundGetInfoString1(bg, buf, sizeof(buf));
2457 	     IpcPrintf("%s\n", buf);
2458 	  }
2459 	else
2460 	   IpcPrintf("Error: background '%s' does not exist\n", prm);
2461      }
2462    else if (!strcmp(cmd, "use"))
2463      {
2464 	if (!strcmp(prm, "-"))
2465 	   bg = NULL;
2466 	else
2467 	   bg = BackgroundFind(prm);
2468 
2469 	num = DesksGetCurrentNum();
2470 	sscanf(p, "%d %n", &num, &len);
2471 	DeskBackgroundSet(DeskGet(num), bg);
2472 	autosave();
2473      }
2474    else if (!strncmp(cmd, "xget", 2))
2475      {
2476 	bg = BackgroundFind(prm);
2477 
2478 	if (bg)
2479 	  {
2480 	     BackgroundGetInfoString2(bg, buf, sizeof(buf));
2481 	     IpcPrintf("%s\n", buf);
2482 	  }
2483 	else
2484 	   IpcPrintf("Error: background '%s' does not exist\n", prm);
2485      }
2486    else if (!strncmp(cmd, "xset", 2))
2487      {
2488 	BackgroundSet2(prm, p);
2489      }
2490    else
2491      {
2492 	/* Compatibility with pre- 0.16.8 clients */
2493 	BackgroundSet1(cmd, params + len2);
2494      }
2495 }
2496 
2497 static void
IPC_BackgroundUse(const char * params)2498 IPC_BackgroundUse(const char *params)
2499 {
2500    char                name[1024];
2501    const char         *p;
2502    Background         *bg;
2503    int                 i, l;
2504 
2505    p = params;
2506    name[0] = '\0';
2507    l = 0;
2508    sscanf(p, "%1000s %n", name, &l);
2509    p += l;
2510 
2511    bg = BackgroundFind(name);
2512    if (!bg)
2513       return;
2514 
2515    for (;;)
2516      {
2517 	i = l = -1;
2518 	sscanf(p, "%d %n", &i, &l);
2519 	p += l;
2520 	if (i < 0)
2521 	   break;
2522 	DeskBackgroundSet(DeskGet(i), bg);
2523      }
2524 
2525    autosave();
2526 }
2527 
2528 static const IpcItem BackgroundsIpcArray[] = {
2529    {
2530     BackgroundsIpc,
2531     "background", "bg",
2532     "Background commands",
2533     "  background                       Show current background\n"
2534     "  background apply <name> <win>    Apply background to window\n"
2535     "  background del <name>            Delete background\n"
2536     "  background list                  Show all background\n"
2537     "  background load <name> <file>    Load new wallpaper from file\n"
2538     "  background set <name> ...        Set background parameters\n"
2539     "  background show <name>           Show background info\n"
2540     "  background use <name> <desks...> Switch to background <name>\n"
2541     "  background xget <name>           Special show background parameters\n"
2542     "  background xset <name> ...       Special set background parameters\n"}
2543    ,
2544    {
2545     IPC_BackgroundUse, "use_bg", NULL, "Deprecated - do not use", NULL}
2546    ,
2547 };
2548 
2549 /*
2550  * Configuration items
2551  */
2552 static const CfgItem BackgroundsCfgItems[] = {
2553    CFG_ITEM_BOOL(Conf.backgrounds, hiquality, 1),
2554    CFG_ITEM_BOOL(Conf.backgrounds, user, 1),
2555    CFG_ITEM_BOOL(Conf.backgrounds, no_scan, 0),
2556    CFG_ITEM_INT(Conf.backgrounds, timeout, 240),
2557 };
2558 
2559 /*
2560  * Module descriptor
2561  */
2562 extern const EModule ModBackgrounds;
2563 
2564 const EModule       ModBackgrounds = {
2565    "backgrounds", "bg",
2566    BackgroundsSighan,
2567    MOD_ITEMS(BackgroundsIpcArray),
2568    MOD_ITEMS(BackgroundsCfgItems)
2569 };
2570