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 <X11/Xlib.h>
27 
28 #include "E.h"
29 #include "backgrounds.h"
30 #include "conf.h"
31 #include "desktops.h"
32 #include "eimage.h"
33 #include "emodule.h"
34 #include "iclass.h"
35 #include "list.h"
36 #include "tclass.h"
37 #include "xwin.h"
38 
39 #define ENABLE_DESTROY 0	/* Broken */
40 
41 struct _imagestate {
42    char               *im_file;
43    char               *real_file;
44    char                got_colors;
45    char                unloadable;
46    char                transparent;
47    char                pixmapfillstyle;
48    char                bevelstyle;
49    char                rotate;
50    EImage             *im;
51    EImageBorder       *border;
52    unsigned int        bg, hi, lo, hihi, lolo;
53    unsigned int        bg_pixel, hi_pixel, lo_pixel, hihi_pixel, lolo_pixel;
54 };
55 
56 typedef struct {
57    ImageState         *normal;
58    ImageState         *hilited;
59    ImageState         *clicked;
60    ImageState         *disabled;
61 } ImageStateArray;
62 
63 struct _imageclass {
64    dlist_t             list;
65    char               *name;
66    ImageStateArray     norm, active, sticky, sticky_active;
67    EImageBorder        padding;
68    unsigned int        ref_count;
69 };
70 
71 static              LIST_HEAD(iclass_list);
72 
73 static ImageClass  *ImageclassGetFallback(void);
74 
75 EImage             *
ThemeImageLoad(const char * file)76 ThemeImageLoad(const char *file)
77 {
78    EImage             *im;
79    char               *f;
80 
81    if (!file)
82       return NULL;
83 
84    if (file[0] == '/')
85      {
86 	im = EImageLoad(file);
87 	return im;
88      }
89 
90    f = ThemeFileFind(file, FILE_TYPE_IMAGE);
91    if (f)
92      {
93 	im = EImageLoad(f);
94 	Efree(f);
95 	return im;
96      }
97 
98    return NULL;
99 }
100 
101 static void
ImagestateColorsSetGray(ImageState * is,unsigned int hihi,unsigned int hi,unsigned int bg,unsigned int lo,unsigned int lolo)102 ImagestateColorsSetGray(ImageState * is,
103 			unsigned int hihi, unsigned int hi,
104 			unsigned int bg, unsigned int lo, unsigned int lolo)
105 {
106    if (!is)
107       return;
108    COLOR32_FROM_RGB(is->hihi, hihi, hihi, hihi);
109    COLOR32_FROM_RGB(is->hi, hi, hi, hi);
110    COLOR32_FROM_RGB(is->bg, bg, bg, bg);
111    COLOR32_FROM_RGB(is->lo, lo, lo, lo);
112    COLOR32_FROM_RGB(is->lolo, lolo, lolo, lolo);
113 }
114 
115 static ImageState  *
ImagestateCreate(const char * file)116 ImagestateCreate(const char *file)
117 {
118    ImageState         *is;
119 
120    is = ECALLOC(ImageState, 1);
121    if (!is)
122       return NULL;
123 
124    is->pixmapfillstyle = FILL_STRETCH;
125    ImagestateColorsSetGray(is, 255, 200, 160, 120, 64);
126    is->bevelstyle = BEVEL_NONE;
127    is->im_file = Estrdup(file);
128 
129    return is;
130 }
131 
132 static ImageState  *
ImagestateCreateX(unsigned int hihi,unsigned int hi,unsigned int lo,unsigned int lolo,unsigned int bg_r,unsigned int bg_g,unsigned int bg_b)133 ImagestateCreateX(unsigned int hihi, unsigned int hi,
134 		  unsigned int lo, unsigned int lolo,
135 		  unsigned int bg_r, unsigned int bg_g, unsigned int bg_b)
136 {
137    ImageState         *is;
138 
139    is = ImagestateCreate(NULL);
140    if (!is)
141       return NULL;
142 
143    ImagestateColorsSetGray(is, hihi, hi, 0, lo, lolo);
144    COLOR32_FROM_RGB(is->bg, bg_r, bg_g, bg_b);
145    is->bevelstyle = BEVEL_AMIGA;
146 
147    return is;
148 }
149 
150 static void
ImagestateDestroy(ImageState * is)151 ImagestateDestroy(ImageState * is)
152 {
153    if (!is)
154       return;
155 
156    Efree(is->im_file);
157    Efree(is->real_file);
158 
159    if (is->im)
160       EImageFree(is->im);
161 
162    Efree(is->border);
163 
164    Efree(is);
165 }
166 
167 static ImageState  *
ImagestateSet(ImageState ** isp,const char * name)168 ImagestateSet(ImageState ** isp, const char *name)
169 {
170    ImageState         *is;
171 
172    is = ImagestateCreate(name);
173 
174    if (*isp)
175       ImagestateDestroy(*isp);
176    *isp = is;
177 
178    return is;
179 }
180 
181 static void
ImagestateColorsAlloc(ImageState * is)182 ImagestateColorsAlloc(ImageState * is)
183 {
184    if (!is || is->got_colors)
185       return;
186 
187    is->bg_pixel = EAllocColor(WinGetCmap(VROOT), is->bg);
188    is->hi_pixel = EAllocColor(WinGetCmap(VROOT), is->hi);
189    is->lo_pixel = EAllocColor(WinGetCmap(VROOT), is->lo);
190    is->hihi_pixel = EAllocColor(WinGetCmap(VROOT), is->hihi);
191    is->lolo_pixel = EAllocColor(WinGetCmap(VROOT), is->lolo);
192 
193    is->got_colors = 1;
194 }
195 
196 static void
ImagestateRealize(ImageState * is)197 ImagestateRealize(ImageState * is)
198 {
199    if (!is)
200       return;
201    if (is->im)			/* Image is already loaded */
202       return;
203 
204    if (!is->real_file)
205      {
206 	if (!is->im_file)
207 	   return;		/* No file - quit */
208 	/* not loaded, load and setup */
209 	is->real_file = ThemeFileFind(is->im_file, FILE_TYPE_IMAGE);
210      }
211    if (is->real_file)
212      {
213 	if (is->rotate && !Conf.memory_paranoia)
214 	   is->im = EImageLoadOrientate(is->real_file, is->rotate);
215 	else
216 	   is->im = EImageLoad(is->real_file);
217      }
218    if (!is->im)
219      {
220 #define S(s) ((s) ? (s) : "(null)")
221 	Eprintf
222 	   ("%s: Hmmm... is->im is NULL (im_file=%s real_file=%s)\n", __func__,
223 	    S(is->im_file), S(is->real_file));
224 	EFREE_NULL(is->real_file);
225 	return;
226      }
227 
228    EFREE_NULL(is->im_file);
229 
230    EImageCheckAlpha(is->im);
231 
232    if (is->border)
233       EImageSetBorder(is->im, is->border);
234 
235 #if 0				/* To be implemented? */
236    if (is->colmod)
237      {
238 	Imlib_set_image_red_curve(pImlib_Context, is->im, is->colmod->red.map);
239 	Imlib_set_image_green_curve(pImlib_Context, is->im,
240 				    is->colmod->green.map);
241 	Imlib_set_image_blue_curve(pImlib_Context, is->im,
242 				   is->colmod->blue.map);
243      }
244 #endif
245 }
246 
247 static ImageClass  *
ImageclassCreate(const char * name)248 ImageclassCreate(const char *name)
249 {
250    ImageClass         *ic;
251 
252    ic = ECALLOC(ImageClass, 1);
253    if (!ic)
254       return NULL;
255 
256    LIST_PREPEND(ImageClass, &iclass_list, ic);
257 
258    ic->name = Estrdup(name);
259 
260    return ic;
261 }
262 
263 #if ENABLE_DESTROY
264 static void
FreeImageStateArray(ImageStateArray * isa)265 FreeImageStateArray(ImageStateArray * isa)
266 {
267    ImagestateDestroy(isa->normal);
268    ImagestateDestroy(isa->hilited);
269    ImagestateDestroy(isa->clicked);
270    ImagestateDestroy(isa->disabled);
271 }
272 
273 static void
ImageclassDestroy(ImageClass * ic)274 ImageclassDestroy(ImageClass * ic)
275 {
276    if (!ic)
277       return;
278 
279    if (ic->ref_count > 0)
280      {
281 	DialogOK("ImageClass Error!", _("%u references remain"), ic->ref_count);
282 	return;
283      }
284 
285    LIST_REMOVE(ImageClass, &iclass_list, ic);
286 
287    Efree(ic->name);
288 
289    FreeImageStateArray(&(ic->norm));
290    FreeImageStateArray(&(ic->active));
291    FreeImageStateArray(&(ic->sticky));
292    FreeImageStateArray(&(ic->sticky_active));
293 
294    Efree(ic);
295 }
296 #endif /* ENABLE_DESTROY */
297 
298 ImageClass         *
ImageclassAlloc(const char * name,int fallback)299 ImageclassAlloc(const char *name, int fallback)
300 {
301    ImageClass         *ic;
302 
303    if (!name || !name[0])
304       return NULL;
305 
306    ic = ImageclassFind(name, fallback);
307    if (ic)
308       ic->ref_count++;
309 
310    return ic;
311 }
312 
313 void
ImageclassFree(ImageClass * ic)314 ImageclassFree(ImageClass * ic)
315 {
316    if (ic)
317       ic->ref_count--;
318 }
319 
320 const char         *
ImageclassGetName(ImageClass * ic)321 ImageclassGetName(ImageClass * ic)
322 {
323    return (ic) ? ic->name : NULL;
324 }
325 
326 EImageBorder       *
ImageclassGetPadding(ImageClass * ic)327 ImageclassGetPadding(ImageClass * ic)
328 {
329    return (ic) ? &(ic->padding) : NULL;
330 }
331 
332 static int
_ImageclassMatchName(const void * data,const void * match)333 _ImageclassMatchName(const void *data, const void *match)
334 {
335    return strcmp(((const ImageClass *)data)->name, (const char *)match);
336 }
337 
338 ImageClass         *
ImageclassFind(const char * name,int fallback)339 ImageclassFind(const char *name, int fallback)
340 {
341    ImageClass         *ic = NULL;
342 
343    if (name)
344       ic = LIST_FIND(ImageClass, &iclass_list, _ImageclassMatchName, name);
345    if (ic || !fallback)
346       return ic;
347 
348 #if 0
349    Eprintf("%s: Get fallback (%s)\n", __func__, name);
350 #endif
351    return ImageclassGetFallback();
352 }
353 
354 #define ISTATE_SET_STATE(which, fallback) \
355    if (!ic->which) ic->which = ic->fallback;
356 
357 static void
ImageclassPopulate(ImageClass * ic)358 ImageclassPopulate(ImageClass * ic)
359 {
360    if (!ic || !ic->norm.normal)
361       return;
362 
363    ISTATE_SET_STATE(norm.hilited, norm.normal);
364    ISTATE_SET_STATE(norm.clicked, norm.normal);
365    ISTATE_SET_STATE(norm.disabled, norm.normal);
366 
367    ISTATE_SET_STATE(active.normal, norm.normal);
368    ISTATE_SET_STATE(active.hilited, active.normal);
369    ISTATE_SET_STATE(active.clicked, active.normal);
370    ISTATE_SET_STATE(active.disabled, active.normal);
371 
372    ISTATE_SET_STATE(sticky.normal, norm.normal);
373    ISTATE_SET_STATE(sticky.hilited, sticky.normal);
374    ISTATE_SET_STATE(sticky.clicked, sticky.normal);
375    ISTATE_SET_STATE(sticky.disabled, sticky.normal);
376 
377    ISTATE_SET_STATE(sticky_active.normal, norm.normal);
378    ISTATE_SET_STATE(sticky_active.hilited, sticky_active.normal);
379    ISTATE_SET_STATE(sticky_active.clicked, sticky_active.normal);
380    ISTATE_SET_STATE(sticky_active.disabled, sticky_active.normal);
381 }
382 
383 int
ImageclassConfigLoad(FILE * fs)384 ImageclassConfigLoad(FILE * fs)
385 {
386    int                 err = 0;
387    char                s[FILEPATH_LEN_MAX];
388    char                s2[FILEPATH_LEN_MAX];
389    char               *p2;
390    int                 i1;
391    ImageClass         *ic = NULL;
392    ImageState         *is = NULL;
393    int                 l, r, t, b;
394 
395    while (GetLine(s, sizeof(s), fs))
396      {
397 	i1 = ConfigParseline1(s, s2, &p2, NULL);
398 
399 	/* ic not needed */
400 	switch (i1)
401 	  {
402 	  case CONFIG_CLOSE:
403 	     ImageclassPopulate(ic);
404 	     goto done;
405 	  case CONFIG_COLORMOD:
406 	  case ICLASS_COLORMOD:
407 	     continue;
408 	  case CONFIG_CLASSNAME:
409 	     if (ImageclassFind(s2, 0))
410 	       {
411 		  SkipTillEnd(fs);
412 		  goto done;
413 	       }
414 	     ic = ImageclassCreate(s2);
415 	     continue;
416 	  }
417 
418 	/* ic needed */
419 	if (!ic)
420 	   break;
421 
422 	switch (i1)
423 	  {
424 	  case CONFIG_INHERIT:
425 	     {
426 		ImageClass         *ic2;
427 
428 		ic2 = ImageclassFind(s2, 0);
429 		if (!ic2)
430 		   goto not_ok;
431 		ic->norm = ic2->norm;
432 		ic->active = ic2->active;
433 		ic->sticky = ic2->sticky;
434 		ic->sticky_active = ic2->sticky_active;
435 		ic->padding = ic2->padding;
436 	     }
437 	     continue;
438 	  case ICLASS_PADDING:
439 	     l = r = t = b = 0;
440 	     sscanf(p2, "%i %i %i %i", &l, &r, &t, &b);
441 	     ic->padding.left = l;
442 	     ic->padding.right = r;
443 	     ic->padding.top = t;
444 	     ic->padding.bottom = b;
445 	     continue;
446 	  case CONFIG_DESKTOP:
447 	     /* don't ask... --mandrake */
448 	  case ICLASS_NORMAL:
449 	     is = ImagestateSet(&ic->norm.normal, s2);
450 	     continue;
451 	  case ICLASS_CLICKED:
452 	     is = ImagestateSet(&ic->norm.clicked, s2);
453 	     continue;
454 	  case ICLASS_HILITED:
455 	     is = ImagestateSet(&ic->norm.hilited, s2);
456 	     continue;
457 	  case ICLASS_DISABLED:
458 	     is = ImagestateSet(&ic->norm.disabled, s2);
459 	     continue;
460 	  case ICLASS_STICKY_NORMAL:
461 	     is = ImagestateSet(&ic->sticky.normal, s2);
462 	     continue;
463 	  case ICLASS_STICKY_CLICKED:
464 	     is = ImagestateSet(&ic->sticky.clicked, s2);
465 	     continue;
466 	  case ICLASS_STICKY_HILITED:
467 	     is = ImagestateSet(&ic->sticky.hilited, s2);
468 	     continue;
469 	  case ICLASS_STICKY_DISABLED:
470 	     is = ImagestateSet(&ic->sticky.disabled, s2);
471 	     continue;
472 	  case ICLASS_ACTIVE_NORMAL:
473 	     is = ImagestateSet(&ic->active.normal, s2);
474 	     continue;
475 	  case ICLASS_ACTIVE_CLICKED:
476 	     is = ImagestateSet(&ic->active.clicked, s2);
477 	     continue;
478 	  case ICLASS_ACTIVE_HILITED:
479 	     is = ImagestateSet(&ic->active.hilited, s2);
480 	     continue;
481 	  case ICLASS_ACTIVE_DISABLED:
482 	     is = ImagestateSet(&ic->active.disabled, s2);
483 	     continue;
484 	  case ICLASS_STICKY_ACTIVE_NORMAL:
485 	     is = ImagestateSet(&ic->sticky_active.normal, s2);
486 	     continue;
487 	  case ICLASS_STICKY_ACTIVE_CLICKED:
488 	     is = ImagestateSet(&ic->sticky_active.clicked, s2);
489 	     continue;
490 	  case ICLASS_STICKY_ACTIVE_HILITED:
491 	     is = ImagestateSet(&ic->sticky_active.hilited, s2);
492 	     continue;
493 	  case ICLASS_STICKY_ACTIVE_DISABLED:
494 	     is = ImagestateSet(&ic->sticky_active.disabled, s2);
495 	     continue;
496 	  }
497 
498 	/* is needed */
499 	if (!is)
500 	   break;
501 
502 	switch (i1)
503 	  {
504 	  case ICLASS_LRTB:
505 	     if (!is->border)
506 		is->border = EMALLOC(EImageBorder, 1);
507 	     l = r = t = b = 0;
508 	     sscanf(p2, "%i %i %i %i", &l, &r, &t, &b);
509 	     is->border->left = l;
510 	     is->border->right = r;
511 	     is->border->top = t;
512 	     is->border->bottom = b;
513 	     /* Hmmm... imlib2 works better with this */
514 	     is->border->right++;
515 	     is->border->bottom++;
516 	     continue;
517 	  case ICLASS_FILLRULE:
518 	     is->pixmapfillstyle = atoi(s2);
519 	     continue;
520 	  case ICLASS_TRANSPARENT:
521 	     is->transparent = strtoul(s2, NULL, 0);
522 	     continue;
523 	  case ICLASS_ROTATE:	/* flip goes here too */
524 	     is->rotate = strtoul(s2, NULL, 0);
525 	     continue;
526 	  case ICLASS_BEVEL:
527 	     {
528 		int                 n, bevel;
529 
530 		n = sscanf(p2, "%i %i %i %i %i %i",
531 			   &bevel, &is->hihi, &is->hi, &is->bg, &is->lo,
532 			   &is->lolo);
533 		if (n < 6)
534 		   goto not_ok;
535 		is->bevelstyle = bevel;
536 	     }
537 	     continue;
538 	  default:
539 	     goto not_ok;
540 	  }
541      }
542  not_ok:
543    err = -1;
544 
545  done:
546    return err;
547 }
548 
549 ImageClass         *
ImageclassCreateSimple(const char * name,const char * image)550 ImageclassCreateSimple(const char *name, const char *image)
551 {
552    ImageClass         *ic;
553 
554    ic = ImageclassCreate(name);
555    if (!ic)
556       return NULL;
557 
558    ic->norm.normal = ImagestateCreate(image);
559    ic->norm.normal->unloadable = 1;
560    ImageclassPopulate(ic);
561 
562    ImagestateRealize(ic->norm.normal);
563 
564    return ic;
565 }
566 
567 static ImageState  *
ImageclassGetImageState1(ImageStateArray * pisa,int state)568 ImageclassGetImageState1(ImageStateArray * pisa, int state)
569 {
570    ImageState         *is;
571 
572    switch (state)
573      {
574      case STATE_NORMAL:
575 	is = pisa->normal;
576 	break;
577      case STATE_HILITED:
578 	is = pisa->hilited;
579 	break;
580      case STATE_CLICKED:
581 	is = pisa->clicked;
582 	break;
583      case STATE_DISABLED:
584 	is = pisa->disabled;
585 	break;
586      default:
587 	is = NULL;
588 	break;
589      }
590 
591    return is;
592 }
593 
594 ImageState         *
ImageclassGetImageState(ImageClass * ic,int state,int active,int sticky)595 ImageclassGetImageState(ImageClass * ic, int state, int active, int sticky)
596 {
597    ImageState         *is;
598 
599    if (active)
600      {
601 	if (sticky)
602 	   is = ImageclassGetImageState1(&ic->sticky_active, state);
603 	else
604 	   is = ImageclassGetImageState1(&ic->active, state);
605      }
606    else
607      {
608 	if (sticky)
609 	   is = ImageclassGetImageState1(&ic->sticky, state);
610 	else
611 	   is = ImageclassGetImageState1(&ic->norm, state);
612      }
613 
614    return is;
615 }
616 
617 EImage             *
ImageclassGetImage(ImageClass * ic,int active,int sticky,int state)618 ImageclassGetImage(ImageClass * ic, int active, int sticky, int state)
619 {
620    EImage             *im;
621    ImageState         *is;
622 
623    if (!ic)
624       return NULL;
625 
626    is = ImageclassGetImageState(ic, state, active, sticky);
627    if (!is)
628       return NULL;
629 
630    if (!is->im)
631       ImagestateRealize(is);
632 
633    im = is->im;
634    if (!im)
635       return NULL;
636    is->im = NULL;
637 
638    return im;
639 }
640 
641 char               *
ImageclassGetFile(ImageClass * ic)642 ImageclassGetFile(ImageClass * ic)
643 {
644    ImageState         *is;
645 
646    if (!ic)
647       return NULL;
648 
649    is = ImageclassGetImageState(ic, STATE_NORMAL, 0, 0);
650    if (!is || !is->im_file)
651       return NULL;
652 
653    return ThemeFileFind(is->im_file, FILE_TYPE_IMAGE);
654 }
655 
656 EImage             *
ImageclassGetImageBlended(ImageClass * ic,Win win __UNUSED__,int w,int h,int active,int sticky,int state)657 ImageclassGetImageBlended(ImageClass * ic, Win win __UNUSED__, int w, int h,
658 			  int active, int sticky, int state)
659 {
660    ImageState         *is;
661    EImage             *im, *bg;
662 
663    if (!ic)
664       return NULL;
665 
666    is = ImageclassGetImageState(ic, state, active, sticky);
667    if (!is)
668       return NULL;
669 
670    if (!is->im)
671       ImagestateRealize(is);
672    im = is->im;
673    if (!im)
674       return NULL;
675 
676    if (is->pixmapfillstyle == FILL_STRETCH)
677      {
678 	bg = EImageCreateScaled(im, 0, 0, 0, 0, w, h);
679      }
680    else
681      {
682 	int                 iw, ih, tw, th;
683 
684 	EImageGetSize(im, &iw, &ih);
685 
686 	tw = w;
687 	th = h;
688 	if (is->pixmapfillstyle & FILL_TILE_H)
689 	   tw = iw;
690 	if (is->pixmapfillstyle & FILL_TILE_V)
691 	   th = ih;
692 
693 	bg = EImageCreate(w, h);
694 	EImageTile(bg, im, 0, tw, th, 0, 0, w, h, 0, 0);
695      }
696 
697    if ((is->unloadable) || (Conf.memory_paranoia))
698      {
699 	EImageFree(is->im);
700 	is->im = NULL;
701      }
702 
703    return bg;
704 }
705 
706 static void
ImagestateMakePmapMask(ImageState * is,Win win,PmapMask * pmm,int pmapflags __UNUSED__,int w,int h)707 ImagestateMakePmapMask(ImageState * is, Win win, PmapMask * pmm,
708 		       int pmapflags __UNUSED__, int w, int h)
709 {
710    if (is->pixmapfillstyle == FILL_STRETCH)
711      {
712 	pmm->type = 1;
713 	pmm->pmap = pmm->mask = NoXID;
714 	pmm->w = w;
715 	pmm->h = h;
716 	EImageRenderPixmaps(is->im, win, EIMAGE_ANTI_ALIAS, &pmm->pmap,
717 			    &pmm->mask, w, h);
718      }
719    else
720      {
721 	int                 ww, hh, cw, ch, pw, ph;
722 
723 	EImageGetSize(is->im, &ww, &hh);
724 
725 	pw = w;
726 	ph = h;
727 	if (is->pixmapfillstyle & FILL_TILE_H)
728 	   pw = ww;
729 	if (is->pixmapfillstyle & FILL_TILE_V)
730 	   ph = hh;
731 	if (is->pixmapfillstyle & FILL_INT_TILE_H)
732 	  {
733 	     cw = w / ww;
734 	     if (cw * ww < w)
735 		cw++;
736 	     if (cw < 1)
737 		cw = 1;
738 	     pw = w / cw;
739 	  }
740 	if (is->pixmapfillstyle & FILL_INT_TILE_V)
741 	  {
742 	     ch = h / hh;
743 	     if (ch * hh < h)
744 		ch++;
745 	     if (ch < 1)
746 		ch = 1;
747 	     ph = h / ch;
748 	  }
749 	pmm->type = 1;
750 	pmm->pmap = pmm->mask = NoXID;
751 	pmm->w = pw;
752 	pmm->h = ph;
753 	EImageRenderPixmaps(is->im, win, EIMAGE_ANTI_ALIAS, &pmm->pmap,
754 			    &pmm->mask, pw, ph);
755      }
756 }
757 
758 #define LINE(x1, y1, x2, y2) \
759    XDrawLine(disp, win, gc, x + (x1), y + (y1), x + (x2), y + (y2))
760 #define RECT(x, y, w, h) \
761 	XDrawRectangle(disp, win, gc, x, y, w, h);
762 
763 static void
ImagestateDrawBevel(ImageState * is,EX_Drawable win,int x,int y,int w,int h)764 ImagestateDrawBevel(ImageState * is, EX_Drawable win,
765 		    int x, int y, int w, int h)
766 {
767    GC                  gc;
768 
769    gc = EXCreateGC(win, 0, NULL);
770 
771    ImagestateColorsAlloc(is);
772 
773    switch (is->bevelstyle)
774      {
775      case BEVEL_AMIGA:
776 	XSetForeground(disp, gc, is->hihi_pixel);
777 	LINE(0, 0, w - 2, 0);
778 	LINE(0, 0, 0, h - 2);
779 	XSetForeground(disp, gc, is->lolo_pixel);
780 	LINE(1, h - 1, w - 1, h - 1);
781 	LINE(w - 1, 1, w - 1, h - 1);
782 	break;
783      case BEVEL_MOTIF:
784 	XSetForeground(disp, gc, is->hi_pixel);
785 	LINE(0, 0, w - 1, 0);
786 	LINE(0, 0, 0, h - 1);
787 	LINE(1, 1, w - 2, 1);
788 	LINE(1, 1, 1, h - 2);
789 	XSetForeground(disp, gc, is->lo_pixel);
790 	LINE(0, h - 1, w - 1, h - 1);
791 	LINE(w - 1, 1, w - 1, h - 1);
792 	LINE(1, h - 2, w - 2, h - 2);
793 	LINE(w - 2, 2, w - 2, h - 2);
794 	break;
795      case BEVEL_NEXT:
796 	XSetForeground(disp, gc, is->hihi_pixel);
797 	LINE(0, 0, w - 1, 0);
798 	LINE(0, 0, 0, h - 1);
799 	XSetForeground(disp, gc, is->hi_pixel);
800 	LINE(1, 1, w - 2, 1);
801 	LINE(1, 1, 1, h - 2);
802 	XSetForeground(disp, gc, is->lolo_pixel);
803 	LINE(1, h - 1, w - 1, h - 1);
804 	LINE(w - 1, 1, w - 1, h - 1);
805 	XSetForeground(disp, gc, is->lo_pixel);
806 	LINE(2, h - 2, w - 2, h - 2);
807 	LINE(w - 2, 2, w - 2, h - 2);
808 	break;
809      case BEVEL_DOUBLE:
810 	XSetForeground(disp, gc, is->hi_pixel);
811 	LINE(0, 0, w - 2, 0);
812 	LINE(0, 0, 0, h - 2);
813 	XSetForeground(disp, gc, is->lo_pixel);
814 	LINE(1, 1, w - 3, 1);
815 	LINE(1, 1, 1, h - 3);
816 	XSetForeground(disp, gc, is->lo_pixel);
817 	LINE(1, h - 1, w - 1, h - 1);
818 	LINE(w - 1, 1, w - 1, h - 1);
819 	XSetForeground(disp, gc, is->hi_pixel);
820 	LINE(2, h - 2, w - 2, h - 2);
821 	LINE(w - 2, 2, w - 2, h - 2);
822 	break;
823      case BEVEL_WIDEDOUBLE:
824 	XSetForeground(disp, gc, is->hihi_pixel);
825 	LINE(0, 0, w - 1, 0);
826 	LINE(0, 0, 0, h - 1);
827 	XSetForeground(disp, gc, is->hi_pixel);
828 	LINE(1, 1, w - 2, 1);
829 	LINE(1, 1, 1, h - 2);
830 	LINE(3, h - 4, w - 4, h - 4);
831 	LINE(w - 4, 3, w - 4, h - 4);
832 	XSetForeground(disp, gc, is->lolo_pixel);
833 	LINE(1, h - 1, w - 1, h - 1);
834 	LINE(w - 1, 1, w - 1, h - 1);
835 	XSetForeground(disp, gc, is->lo_pixel);
836 	LINE(2, h - 2, w - 2, h - 2);
837 	LINE(w - 2, 2, w - 2, h - 2);
838 	LINE(3, 3, w - 4, 3);
839 	LINE(3, 3, 3, h - 4);
840 	break;
841      case BEVEL_THINPOINT:
842 	XSetForeground(disp, gc, is->hi_pixel);
843 	LINE(0, 0, w - 2, 0);
844 	LINE(0, 0, 0, h - 2);
845 	XSetForeground(disp, gc, is->lo_pixel);
846 	LINE(1, h - 1, w - 1, h - 1);
847 	LINE(w - 1, 1, w - 1, h - 1);
848 	XSetForeground(disp, gc, is->hihi_pixel);
849 	LINE(0, 0, 1, 0);
850 	LINE(0, 0, 0, 1);
851 	XSetForeground(disp, gc, is->lolo_pixel);
852 	LINE(w - 2, h - 1, w - 1, h - 1);
853 	LINE(w - 1, h - 2, w - 1, h - 1);
854 	break;
855      case BEVEL_THICKPOINT:
856 	XSetForeground(disp, gc, is->hi_pixel);
857 	RECT(x, y, w - 1, h - 1);
858 	break;
859      default:
860 	break;
861      }
862 
863    EXFreeGC(gc);
864 }
865 
866 static void
ImagestateDrawNoImg(ImageState * is,EX_Drawable draw,int x,int y,int w,int h)867 ImagestateDrawNoImg(ImageState * is, EX_Drawable draw, int x, int y, int w,
868 		    int h)
869 {
870    ImagestateColorsAlloc(is);
871 
872    EXFillAreaSolid(draw, x, y, w, h, is->bg_pixel);
873    if (is->bevelstyle != BEVEL_NONE)
874       ImagestateDrawBevel(is, draw, x, y, w, h);
875 }
876 
877 void
ITApply(Win win,ImageClass * ic,ImageState * is,int state,int active,int sticky,TextClass * tc,TextState * ts,const char * text,int flags)878 ITApply(Win win, ImageClass * ic, ImageState * is,
879 	int state, int active, int sticky,
880 	TextClass * tc, TextState * ts, const char *text, int flags)
881 {
882    int                 w, h;
883 
884    if (!win || !ic)
885       return;
886 
887    w = WinGetW(win);
888    h = WinGetH(win);
889    if (w <= 0 || h <= 0)
890       return;
891 
892    if (!is)
893       is = ImageclassGetImageState(ic, state, active, sticky);
894    if (!is)
895       return;
896 
897    if (tc && text)
898      {
899 	if (!ts)
900 	   ts = TextclassGetTextState(tc, state, active, sticky);
901      }
902 
903    if (!is->im)
904       ImagestateRealize(is);
905 
906    /* Imlib2 will not render pixmaps with dimensions > 8192 */
907    if (is->im && w <= 8192 && h <= 8192)
908      {
909 	PmapMask            pmm;
910 
911 	ImagestateMakePmapMask(is, win, &pmm, IC_FLAG_MAKE_MASK, w, h);
912 
913 	if (pmm.pmap)
914 	  {
915 	     EX_Pixmap           pmap = pmm.pmap;
916 
917 	     if ((ts && text) || (is->bevelstyle != BEVEL_NONE) ||
918 		 (flags & ITA_BGPMAP))
919 	       {
920 		  if (pmm.type != 0)
921 		    {
922 		       pmap = EGetWindowBackgroundPixmap(win);
923 		       EXCopyAreaTiled(pmm.pmap, NoXID, pmap, 0, 0, w, h, 0, 0);
924 		    }
925 
926 		  if (is->bevelstyle != BEVEL_NONE)
927 		     ImagestateDrawBevel(is, pmap, 0, 0, w, h);
928 
929 		  if (ts && text)
930 		     TextstateTextDraw(ts, win, pmap, text, 0, 0, w, h,
931 				       &(ic->padding), 0,
932 				       TextclassGetJustification(tc),
933 				       flags & ITA_JUSTV);
934 	       }
935 
936 	     /* Set window pixmap */
937 	     if (pmap == pmm.pmap)
938 		ESetWindowBackgroundPixmap(win, pmap, 0);
939 
940 	     if (pmm.w == w && pmm.h == h)
941 		EShapeSetMask(win, 0, 0, pmm.mask);
942 	     else if (pmm.mask)
943 		EShapeSetMaskTiled(win, 0, 0, pmm.mask, w, h);
944 	  }
945 
946 	PmapMaskFree(&pmm);
947 
948 	if ((is->unloadable) || (Conf.memory_paranoia))
949 	  {
950 	     EImageFree(is->im);
951 	     is->im = NULL;
952 	  }
953      }
954    else
955      {
956 	ImagestateColorsAlloc(is);
957 
958 	if (is->bevelstyle == BEVEL_NONE && !text)
959 	  {
960 	     ESetWindowBackground(win, is->bg_pixel);
961 	  }
962 	else
963 	  {
964 	     EX_Pixmap           pmap;
965 
966 	     pmap = EGetWindowBackgroundPixmap(win);
967 	     ImagestateDrawNoImg(is, pmap, 0, 0, w, h);
968 	     if (ts && text)
969 		TextstateTextDraw(ts, win, pmap, text, 0, 0, w, h,
970 				  &(ic->padding), 0,
971 				  TextclassGetJustification(tc),
972 				  flags & ITA_JUSTV);
973 	  }
974      }
975    EClearWindow(win);
976 }
977 
978 void
ImageclassApply(ImageClass * ic,Win win,int active,int sticky,int state)979 ImageclassApply(ImageClass * ic, Win win, int active, int sticky, int state)
980 {
981    ITApply(win, ic, NULL, state, active, sticky, NULL, NULL, NULL, 0);
982 }
983 
984 static void
PmapMaskTile(PmapMask * pmm,Win win,unsigned int w,unsigned int h)985 PmapMaskTile(PmapMask * pmm, Win win, unsigned int w, unsigned int h)
986 {
987    EX_Pixmap           pmap, mask;
988 
989    pmap = ECreatePixmap(win, w, h, 0);
990    if (pmap == NoXID)
991       return;
992    EXCopyAreaTiled(pmm->pmap, NoXID, pmap, 0, 0, w, h, 0, 0);
993 
994    mask = NoXID;
995    if (pmm->mask != NoXID)
996       mask = ECreatePixmap(win, w, h, 1);
997    if (mask != NoXID)
998       EXCopyAreaTiled(pmm->mask, NoXID, mask, 0, 0, w, h, 0, 0);
999 
1000    PmapMaskFree(pmm);
1001    pmm->type = 0;
1002    pmm->w = w;
1003    pmm->h = h;
1004    pmm->pmap = pmap;
1005    pmm->mask = mask;
1006 }
1007 
1008 void
ImageclassApplyCopy(ImageClass * ic,Win win,int w,int h,int active,int sticky,int state,PmapMask * pmm,int pmapflags)1009 ImageclassApplyCopy(ImageClass * ic, Win win, int w, int h,
1010 		    int active, int sticky, int state,
1011 		    PmapMask * pmm, int pmapflags)
1012 {
1013    ImageState         *is;
1014 
1015    if (!pmm)
1016       return;
1017 
1018    pmm->type = 0;
1019    pmm->pmap = pmm->mask = 0;
1020 
1021    if ((!ic) || (!win) || (w <= 0) || (h <= 0))
1022       return;
1023 
1024    is = ImageclassGetImageState(ic, state, active, sticky);
1025    if (!is)
1026       return;
1027 
1028    if (!is->im)
1029       ImagestateRealize(is);
1030 
1031    /* Imlib2 will not render pixmaps with dimensions > 8192 */
1032    if (is->im && w <= 8192 && h <= 8192)
1033      {
1034 	ImagestateMakePmapMask(is, win, pmm, pmapflags, w, h);
1035 
1036 	if ((pmapflags & IC_FLAG_FULL_SIZE) && pmm->pmap &&
1037 	    (pmm->w != w || pmm->h != h))
1038 	  {
1039 	     /* Create new full sized pixmaps and fill them with the */
1040 	     /* pmap and mask tiles                                  */
1041 	     PmapMaskTile(pmm, win, w, h);
1042 	  }
1043 
1044 	if ((is->unloadable) || (Conf.memory_paranoia))
1045 	  {
1046 	     EImageFree(is->im);
1047 	     is->im = NULL;
1048 	  }
1049      }
1050    else
1051      {
1052 	if (pmm->pmap)
1053 	   Eprintf("%s: Hmm... pmm->pmap already set\n", __func__);
1054 
1055 	pmm->type = 0;
1056 	pmm->pmap = ECreatePixmap(win, w, h, 0);
1057 	pmm->mask = 0;
1058 
1059 	ImagestateDrawNoImg(is, pmm->pmap, 0, 0, w, h);
1060 	/* FIXME - No text */
1061      }
1062 }
1063 
1064 void
ImageclassApplySimple(ImageClass * ic,Win win,EX_Drawable draw,int state,int x,int y,int w,int h)1065 ImageclassApplySimple(ImageClass * ic, Win win, EX_Drawable draw, int state,
1066 		      int x, int y, int w, int h)
1067 {
1068    EImage             *im;
1069 
1070    if (!ic)
1071       return;
1072 
1073    im = ImageclassGetImage(ic, 0, 0, state);
1074    if (im)
1075      {
1076 	EImageRenderOnDrawable(im, win, draw, 0, x, y, w, h);
1077 	EImageFree(im);
1078      }
1079    else
1080      {
1081 	ImageState         *is;
1082 
1083 	is = ImageclassGetImageState(ic, state, 0, 0);
1084 	if (!is)
1085 	   return;
1086 
1087 	ImagestateDrawNoImg(is, draw, x, y, w, h);
1088      }
1089 }
1090 
1091 static ImageClass  *
ImageclassGetFallback(void)1092 ImageclassGetFallback(void)
1093 {
1094    ImageClass         *ic;
1095 
1096    ic = ImageclassFind("__fb_ic", 0);
1097    if (ic)
1098       return ic;
1099 
1100    /* Create fallback imageclass */
1101    ic = ImageclassCreate("__fb_ic");
1102    if (!ic)
1103       return ic;
1104 
1105    ic->norm.normal = ImagestateCreateX(255, 255, 0, 0, 160, 160, 160);
1106    ic->norm.hilited = ImagestateCreateX(255, 255, 0, 0, 192, 192, 192);
1107    ic->norm.clicked = ImagestateCreateX(0, 0, 255, 255, 192, 192, 192);
1108    ic->active.normal = ImagestateCreateX(255, 255, 0, 0, 180, 140, 160);
1109    ic->active.hilited = ImagestateCreateX(255, 255, 0, 0, 230, 190, 210);
1110    ic->active.clicked = ImagestateCreateX(0, 0, 255, 255, 230, 190, 210);
1111 
1112    ic->padding.left = 4;
1113    ic->padding.right = 4;
1114    ic->padding.top = 4;
1115    ic->padding.bottom = 4;
1116 
1117    ImageclassPopulate(ic);
1118 
1119    return ic;
1120 }
1121 
1122 ImageClass         *
ImageclassGetBlack(void)1123 ImageclassGetBlack(void)
1124 {
1125    ImageClass         *ic;
1126 
1127    ic = ImageclassFind("__BLACK", 0);
1128    if (ic)
1129       return ic;
1130 
1131    /* Create all black image class for filler borders */
1132    ic = ImageclassCreate("__BLACK");
1133    if (!ic)
1134       return ic;
1135 
1136    ic->norm.normal = ImagestateCreate(NULL);
1137    ImagestateColorsSetGray(ic->norm.normal, 0, 0, 0, 0, 0);
1138 
1139    ImageclassPopulate(ic);
1140 
1141    return ic;
1142 }
1143 
1144 /*
1145  * Imageclass Module
1146  */
1147 
1148 static void
ImageclassIpc(const char * params)1149 ImageclassIpc(const char *params)
1150 {
1151    char                param1[1024];
1152    char                param2[1024];
1153    int                 l;
1154    const char         *p;
1155    ImageClass         *ic;
1156 
1157    if (!params)
1158      {
1159 	IpcPrintf("Please specify...\n");
1160 	return;
1161      }
1162 
1163    p = params;
1164    l = 0;
1165    param1[0] = param2[0] = '\0';
1166    sscanf(p, "%1000s %1000s %n", param1, param2, &l);
1167    p += l;
1168 
1169    if (!strncmp(param1, "list", 2))
1170      {
1171 	LIST_FOR_EACH(ImageClass, &iclass_list, ic) IpcPrintf("%s\n", ic->name);
1172 	return;
1173      }
1174 
1175    if (!param1[0])
1176      {
1177 	IpcPrintf("ImageClass not specified\n");
1178 	return;
1179      }
1180 
1181    if (!strcmp(param2, "free_pixmap"))
1182      {
1183 	EX_Pixmap           pmap;
1184 
1185 	pmap = (EX_Pixmap) strtol(p, NULL, 0);
1186 	EImagePixmapsFree(pmap, NoXID);
1187 	return;
1188      }
1189 
1190    ic = ImageclassFind(param1, 0);
1191    if (!ic)
1192      {
1193 	IpcPrintf("ImageClass not found: %s\n", param1);
1194 	return;
1195      }
1196 
1197    if (!strcmp(param2, "get_padding"))
1198      {
1199 	IpcPrintf("%i %i %i %i\n",
1200 		  ic->padding.left, ic->padding.right,
1201 		  ic->padding.top, ic->padding.bottom);
1202      }
1203    else if (!strcmp(param2, "get_image_size"))
1204      {
1205 	ImagestateRealize(ic->norm.normal);
1206 	if (ic->norm.normal->im)
1207 	  {
1208 	     int                 w, h;
1209 
1210 	     EImageGetSize(ic->norm.normal->im, &w, &h);
1211 	     EImageFree(ic->norm.normal->im);
1212 	     IpcPrintf("%i %i\n", w, h);
1213 	  }
1214      }
1215    else if (!strcmp(param2, "apply"))
1216      {
1217 	EX_Window           xwin;
1218 	Win                 win;
1219 	char                state[20];
1220 	int                 st, w, h;
1221 
1222 	/* 3:xwin 4:state 5:w 6:h */
1223 	xwin = NoXID;
1224 	state[0] = '\0';
1225 	w = h = -1;
1226 	sscanf(p, "%x %16s %d %d", &xwin, state, &w, &h);
1227 
1228 	win = ECreateWinFromXwin(xwin);
1229 	if (!win)
1230 	   return;
1231 
1232 	if (!strcmp(state, "normal"))
1233 	   st = STATE_NORMAL;
1234 	else if (!strcmp(state, "hilited"))
1235 	   st = STATE_HILITED;
1236 	else if (!strcmp(state, "clicked"))
1237 	   st = STATE_CLICKED;
1238 	else if (!strcmp(state, "disabled"))
1239 	   st = STATE_DISABLED;
1240 	else
1241 	   st = STATE_NORMAL;
1242 
1243 	ImageclassApply(ic, win, 0, 0, st);
1244 	EDestroyWin(win);
1245      }
1246    else if (!strcmp(param2, "apply_copy"))
1247      {
1248 	EX_Window           xwin;
1249 	Win                 win;
1250 	char                state[20];
1251 	int                 st, w, h;
1252 	PmapMask            pmm;
1253 
1254 	/* 3:xwin 4:state 5:w 6:h */
1255 	xwin = NoXID;
1256 	state[0] = '\0';
1257 	w = h = -1;
1258 	sscanf(p, "%x %16s %d %d", &xwin, state, &w, &h);
1259 
1260 	win = ECreateWinFromXwin(xwin);
1261 	if (!win)
1262 	   return;
1263 
1264 	if (!strcmp(state, "normal"))
1265 	   st = STATE_NORMAL;
1266 	else if (!strcmp(state, "hilited"))
1267 	   st = STATE_HILITED;
1268 	else if (!strcmp(state, "clicked"))
1269 	   st = STATE_CLICKED;
1270 	else if (!strcmp(state, "disabled"))
1271 	   st = STATE_DISABLED;
1272 	else
1273 	   st = STATE_NORMAL;
1274 
1275 	if (w < 0 || h < 0)
1276 	  {
1277 	     IpcPrintf("Error:  missing width and/or height\n");
1278 	     return;
1279 	  }
1280 
1281 	ImageclassApplyCopy(ic, win, w, h, 0, 0, st, &pmm,
1282 			    IC_FLAG_MAKE_MASK | IC_FLAG_FULL_SIZE);
1283 	IpcPrintf("0x%08x 0x%08x\n", pmm.pmap, pmm.mask);
1284 	EDestroyWin(win);
1285      }
1286    else if (!strcmp(param2, "query"))
1287      {
1288 	IpcPrintf("ImageClass %s found\n", ic->name);
1289      }
1290    else if (!strcmp(param2, "ref_count"))
1291      {
1292 	IpcPrintf("%u references remain\n", ic->ref_count);
1293      }
1294    else
1295      {
1296 	IpcPrintf("Error: unknown operation specified\n");
1297      }
1298 }
1299 
1300 static const IpcItem ImageclassIpcArray[] = {
1301    {
1302     ImageclassIpc,
1303     "imageclass", "ic",
1304     "List imageclasses, apply an imageclass",
1305     NULL}
1306    ,
1307 };
1308 
1309 /*
1310  * Module descriptor
1311  */
1312 extern const EModule ModImageclass;
1313 
1314 const EModule       ModImageclass = {
1315    "imageclass", "ic",
1316    NULL,
1317    MOD_ITEMS(ImageclassIpcArray),
1318    {0, NULL}
1319 };
1320