1 /**
2  * @file icon.c
3  * @author Joe Wingbermuehle
4  * @date 2004-2006
5  *
6  * @brief Icon functions.
7  *
8  */
9 
10 #include "jwm.h"
11 #include "icon.h"
12 #include "client.h"
13 #include "render.h"
14 #include "main.h"
15 #include "image.h"
16 #include "misc.h"
17 #include "hint.h"
18 #include "color.h"
19 #include "settings.h"
20 #include "border.h"
21 
22 IconNode emptyIcon;
23 
24 #ifdef USE_ICONS
25 
26 /* Must be a power of two. */
27 #define HASH_SIZE 128
28 
29 /** Linked list of icon paths. */
30 typedef struct IconPathNode {
31    char *path;
32    struct IconPathNode *next;
33 } IconPathNode;
34 
35 /* These extensions are appended to icon names during search. */
36 const char *ICON_EXTENSIONS[] = {
37    "",
38 #ifdef USE_PNG
39    ".png",
40    ".PNG",
41 #endif
42 #if defined(USE_CAIRO) && defined(USE_RSVG)
43    ".svg",
44    ".SVG",
45 #endif
46 #ifdef USE_XPM
47    ".xpm",
48    ".XPM",
49 #endif
50 #ifdef USE_JPEG
51    ".jpg",
52    ".JPG",
53    ".jpeg",
54    ".JPEG",
55 #endif
56 #ifdef USE_XBM
57    ".xbm",
58    ".XBM",
59 #endif
60 };
61 static const unsigned EXTENSION_COUNT = ARRAY_LENGTH(ICON_EXTENSIONS);
62 static const unsigned MAX_EXTENSION_LENGTH = 5;
63 
64 static IconNode **iconHash;
65 static IconPathNode *iconPaths;
66 static IconPathNode *iconPathsTail;
67 static GC iconGC;
68 static char iconSizeSet = 0;
69 static char *defaultIconName;
70 
71 static void DoDestroyIcon(int index, IconNode *icon);
72 static IconNode *ReadNetWMIcon(Window win);
73 static IconNode *ReadWMHintIcon(Window win);
74 static IconNode *CreateIcon(const ImageNode *image);
75 static IconNode *CreateIconFromDrawable(Drawable d, Pixmap mask);
76 static IconNode *CreateIconFromBinary(const unsigned long *data,
77                                       unsigned int length);
78 static IconNode *LoadNamedIconHelper(const char *name, const char *path,
79                                      char save, char preserveAspect);
80 
81 static ImageNode *GetBestImage(IconNode *icon, int rwidth, int rheight);
82 static ScaledIconNode *GetScaledIcon(IconNode *icon, long fg,
83                                      int rwidth, int rheight);
84 
85 static void InsertIcon(IconNode *icon);
86 static IconNode *FindIcon(const char *name);
87 static unsigned int GetHash(const char *str);
88 
89 /** Initialize icon data.
90  * This must be initialized before parsing the configuration.
91  */
InitializeIcons(void)92 void InitializeIcons(void)
93 {
94    unsigned int x;
95    iconPaths = NULL;
96    iconPathsTail = NULL;
97    iconHash = Allocate(sizeof(IconNode*) * HASH_SIZE);
98    for(x = 0; x < HASH_SIZE; x++) {
99       iconHash[x] = NULL;
100    }
101    memset(&emptyIcon, 0, sizeof(emptyIcon));
102    iconSizeSet = 0;
103    defaultIconName = NULL;
104 }
105 
106 /** Startup icon support. */
StartupIcons(void)107 void StartupIcons(void)
108 {
109    XGCValues gcValues;
110    XIconSize iconSize;
111    unsigned long gcMask;
112    gcMask = GCGraphicsExposures;
113    gcValues.graphics_exposures = False;
114    iconGC = JXCreateGC(display, rootWindow, gcMask, &gcValues);
115 
116    iconSize.min_width = GetBorderIconSize();
117    iconSize.min_height = GetBorderIconSize();
118    iconSize.max_width = iconSize.min_width;
119    iconSize.max_height = iconSize.min_height;
120    iconSize.width_inc = 1;
121    iconSize.height_inc = 1;
122    JXSetIconSizes(display, rootWindow, &iconSize, 1);
123 }
124 
125 /** Shutdown icon support. */
ShutdownIcons(void)126 void ShutdownIcons(void)
127 {
128    unsigned int x;
129    for(x = 0; x < HASH_SIZE; x++) {
130       while(iconHash[x]) {
131          DoDestroyIcon(x, iconHash[x]);
132       }
133    }
134    JXFreeGC(display, iconGC);
135 }
136 
137 /** Destroy icon data. */
DestroyIcons(void)138 void DestroyIcons(void)
139 {
140    IconPathNode *pn;
141    while(iconPaths) {
142       pn = iconPaths->next;
143       Release(iconPaths->path);
144       Release(iconPaths);
145       iconPaths = pn;
146    }
147    iconPathsTail = NULL;
148    if(iconHash) {
149       Release(iconHash);
150       iconHash = NULL;
151    }
152    if(defaultIconName) {
153       Release(defaultIconName);
154       defaultIconName = NULL;
155    }
156 }
157 
158 /** Add an icon search path. */
AddIconPath(char * path)159 void AddIconPath(char *path)
160 {
161 
162    IconPathNode *ip;
163    int length;
164    char addSep;
165 
166    if(!path) {
167       return;
168    }
169 
170    Trim(path);
171 
172    length = strlen(path);
173    if(path[length - 1] != '/') {
174       addSep = 1;
175    } else {
176       addSep = 0;
177    }
178 
179    ip = Allocate(sizeof(IconPathNode));
180    ip->path = Allocate(length + addSep + 1);
181    memcpy(ip->path, path, length + 1);
182    if(addSep) {
183       ip->path[length] = '/';
184       ip->path[length + 1] = 0;
185    }
186    ExpandPath(&ip->path);
187    ip->next = NULL;
188 
189    if(iconPathsTail) {
190       iconPathsTail->next = ip;
191    } else {
192       iconPaths = ip;
193    }
194    iconPathsTail = ip;
195 
196 }
197 
198 /** Draw an icon. */
PutIcon(IconNode * icon,Drawable d,long fg,int x,int y,int width,int height)199 void PutIcon(IconNode *icon, Drawable d, long fg,
200              int x, int y, int width, int height)
201 {
202    ScaledIconNode *node;
203 
204    Assert(icon);
205 
206    if(icon == &emptyIcon) {
207       return;
208    }
209 
210    /* Scale the icon. */
211    node = GetScaledIcon(icon, fg, width, height);
212    if(node) {
213 
214       /* If we support xrender, use it. */
215 #ifdef USE_XRENDER
216       if(icon->render) {
217          PutScaledRenderIcon(icon, node, d, x, y, width, height);
218          return;
219       }
220 #endif
221 
222       /* Draw the icon the old way. */
223       if(node->image != None) {
224 
225          const int ix = x + (width - node->width) / 2;
226          const int iy = y + (height - node->height) / 2;
227 
228          /* Set the clip mask. */
229          if(node->mask != None) {
230             JXSetClipOrigin(display, iconGC, ix, iy);
231             JXSetClipMask(display, iconGC, node->mask);
232          }
233 
234          /* Draw the icon. */
235          JXCopyArea(display, node->image, d, iconGC, 0, 0,
236                     node->width, node->height, ix, iy);
237 
238          /* Reset the clip mask. */
239          if(node->mask != None) {
240             JXSetClipMask(display, iconGC, None);
241             JXSetClipOrigin(display, iconGC, 0, 0);
242          }
243 
244       }
245 
246    }
247 
248 }
249 
250 /** Load the icon for a client. */
LoadIcon(ClientNode * np)251 void LoadIcon(ClientNode *np)
252 {
253    /* If client already has an icon, destroy it first. */
254    DestroyIcon(np->icon);
255    np->icon = NULL;
256 
257    /* Attempt to read _NET_WM_ICON for an icon. */
258    np->icon = ReadNetWMIcon(np->window);
259    if(np->icon) {
260       return;
261    }
262    if(np->owner != None) {
263       np->icon = ReadNetWMIcon(np->owner);
264       if(np->icon) {
265          return;
266       }
267    }
268 
269    /* Attempt to read an icon from XWMHints. */
270    np->icon = ReadWMHintIcon(np->window);
271    if(np->icon) {
272       return;
273    }
274    if(np->owner != None) {
275       np->icon = ReadNetWMIcon(np->owner);
276       if(np->icon) {
277          return;
278       }
279    }
280 
281    /* Attempt to read an icon based on the window name. */
282    if(np->instanceName) {
283       np->icon = LoadNamedIcon(np->instanceName, 1, 1);
284       if(np->icon) {
285          return;
286       }
287    }
288 }
289 
290 /** Load an icon from a file. */
LoadNamedIcon(const char * name,char save,char preserveAspect)291 IconNode *LoadNamedIcon(const char *name, char save, char preserveAspect)
292 {
293 
294    IconNode *icon;
295    IconPathNode *ip;
296 
297    Assert(name);
298 
299    /* If no icon is specified, return an empty icon. */
300    if(name[0] == 0) {
301       return &emptyIcon;
302    }
303 
304    /* See if this icon has already been loaded. */
305    icon = FindIcon(name);
306    if(icon) {
307       return icon;
308    }
309 
310    /* Check for an absolute file name. */
311    if(name[0] == '/') {
312       ImageNode *image = LoadImage(name, 0, 0, 1);
313       if(image) {
314          icon = CreateIcon(image);
315          icon->preserveAspect = preserveAspect;
316          icon->name = CopyString(name);
317          if(save) {
318             InsertIcon(icon);
319          }
320          DestroyImage(image);
321          return icon;
322       } else {
323          return &emptyIcon;
324       }
325    }
326 
327    /* Try icon paths. */
328    for(ip = iconPaths; ip; ip = ip->next) {
329       icon = LoadNamedIconHelper(name, ip->path, save, preserveAspect);
330       if(icon) {
331          return icon;
332       }
333    }
334 
335    /* The default icon. */
336    return NULL;
337 }
338 
339 /** Helper for loading icons by name. */
LoadNamedIconHelper(const char * name,const char * path,char save,char preserveAspect)340 IconNode *LoadNamedIconHelper(const char *name, const char *path,
341                               char save, char preserveAspect)
342 {
343    ImageNode *image;
344    char *temp;
345    const unsigned nameLength = strlen(name);
346    const unsigned pathLength = strlen(path);
347    unsigned i;
348    char hasExtension;
349 
350    /* Full file name. */
351    temp = AllocateStack(nameLength + pathLength + MAX_EXTENSION_LENGTH + 1);
352    memcpy(&temp[0], path, pathLength);
353    memcpy(&temp[pathLength], name, nameLength + 1);
354 
355    /* Determine if the extension is provided.
356     * We avoid extra file opens if so.
357     */
358    hasExtension = 0;
359    for(i = 1; i < EXTENSION_COUNT; i++) {
360       const unsigned offset = nameLength + pathLength;
361       const unsigned extLength = strlen(ICON_EXTENSIONS[i]);
362       if(JUNLIKELY(offset < extLength)) {
363          continue;
364       }
365       if(!strcmp(ICON_EXTENSIONS[i], &temp[offset])) {
366          hasExtension = 1;
367          break;
368       }
369    }
370 
371    /* Attempt to load the image. */
372    image = NULL;
373    if(hasExtension) {
374       image = LoadImage(temp, 0, 0, 1);
375    } else {
376       for(i = 0; i < EXTENSION_COUNT; i++) {
377          const unsigned len = strlen(ICON_EXTENSIONS[i]);
378          memcpy(&temp[pathLength + nameLength], ICON_EXTENSIONS[i], len + 1);
379          image = LoadImage(temp, 0, 0, 1);
380          if(image) {
381             break;
382          }
383       }
384    }
385    ReleaseStack(temp);
386 
387    /* Create the icon if we were able to load the image. */
388    if(image) {
389       IconNode *result = CreateIcon(image);
390       result->preserveAspect = preserveAspect;
391       result->name = CopyString(temp);
392       if(save) {
393          InsertIcon(result);
394       }
395       DestroyImage(image);
396       return result;
397    }
398 
399    return NULL;
400 }
401 
402 /** Read the icon property from a client. */
ReadNetWMIcon(Window win)403 IconNode *ReadNetWMIcon(Window win)
404 {
405    static const long MAX_LENGTH = 1 << 20;
406    IconNode *icon = NULL;
407    unsigned long count;
408    int status;
409    unsigned long extra;
410    Atom realType;
411    int realFormat;
412    unsigned char *data;
413    status = JXGetWindowProperty(display, win, atoms[ATOM_NET_WM_ICON],
414                                 0, MAX_LENGTH, False, XA_CARDINAL,
415                                 &realType, &realFormat, &count, &extra, &data);
416    if(status == Success && realFormat != 0 && data) {
417       icon = CreateIconFromBinary((unsigned long*)data, count);
418       JXFree(data);
419    }
420    return icon;
421 }
422 
423 /** Read the icon WMHint property from a client. */
ReadWMHintIcon(Window win)424 IconNode *ReadWMHintIcon(Window win)
425 {
426    IconNode *icon = NULL;
427    XWMHints *hints = JXGetWMHints(display, win);
428    if(hints) {
429       Drawable d = None;
430       Pixmap mask = None;
431       if(hints->flags & IconMaskHint) {
432          mask = hints->icon_mask;
433       }
434       if(hints->flags & IconPixmapHint) {
435          d = hints->icon_pixmap;
436       }
437       if(d != None) {
438          icon = CreateIconFromDrawable(d, mask);
439       }
440       JXFree(hints);
441    }
442    return icon;
443 }
444 
445 /** Create an icon from XPM image data. */
GetDefaultIcon(void)446 IconNode *GetDefaultIcon(void)
447 {
448    static const char * const name = "default";
449    const unsigned width = 8;
450    const unsigned height = 8;
451    const unsigned border = 1;
452    ImageNode *image;
453    IconNode *result;
454    unsigned bytes;
455    unsigned x, y;
456 
457    /* Load the specified default, if configured. */
458    if(defaultIconName) {
459       result = LoadNamedIcon(defaultIconName, 1, 1);
460       return result ? result : &emptyIcon;
461    }
462 
463    /* Check if this icon has already been loaded */
464    result = FindIcon(name);
465    if(result) {
466       return result;
467    }
468 
469    /* Allocate image data. */
470    bytes = (width * height + 7) / 8;
471    image = CreateImage(width, height, 1);
472    memset(image->data, 0, bytes);
473 #ifdef USE_XRENDER
474    image->render = 0;
475 #endif
476 
477    /* Allocate the icon node. */
478    result = CreateIcon(image);
479    result->name = CopyString(name);
480    result->images = image;
481    InsertIcon(result);
482 
483    /* Draw the icon. */
484    for(y = border; y < height - border; y++) {
485       const unsigned pixel_left = y * width + border;
486       const unsigned pixel_right = y * width + width - 1 - border;
487       const unsigned offset_left = pixel_left / 8;
488       const unsigned mask_left = 1 << (pixel_left % 8);
489       const unsigned offset_right = pixel_right / 8;
490       const unsigned mask_right = 1 << (pixel_right % 8);
491       image->data[offset_left] |= mask_left;
492       image->data[offset_right] |= mask_right;
493    }
494    for(x = border; x < width - border; x++) {
495       const unsigned pixel_top = x + border * width;
496       const unsigned pixel_bottom = x + width * (height - 1 - border);
497       const unsigned offset_top = pixel_top / 8;
498       const unsigned mask_top = 1 << (pixel_top % 8);
499       const unsigned offset_bottom = pixel_bottom / 8;
500       const unsigned mask_bottom = 1 << (pixel_bottom % 8);
501       image->data[offset_top] |= mask_top;
502       image->data[offset_bottom] |= mask_bottom;
503    }
504 
505    return result;
506 }
507 
CreateIconFromDrawable(Drawable d,Pixmap mask)508 IconNode *CreateIconFromDrawable(Drawable d, Pixmap mask)
509 {
510    ImageNode *image;
511 
512    image = LoadImageFromDrawable(d, mask);
513    if(image) {
514       IconNode *result = CreateIcon(image);
515       result->images = image;
516       return result;
517    } else {
518       return NULL;
519    }
520 }
521 
522 /** Get the best image for the requested size. */
GetBestImage(IconNode * icon,int rwidth,int rheight)523 ImageNode *GetBestImage(IconNode *icon, int rwidth, int rheight)
524 {
525    ImageNode *best;
526    ImageNode *ip;
527 
528    /* If we don't have an image loaded, load one. */
529    if(icon->images == NULL) {
530       return LoadImage(icon->name, rwidth, rheight, icon->preserveAspect);
531    }
532 
533    /* Find the best image to use.
534     * Select the smallest image to completely cover the
535     * requested size.  If no image completely covers the
536     * requested size, select the one that overlaps the most area.
537     * If no size is specified, use the largest. */
538    best = icon->images;
539    for(ip = icon->images->next; ip; ip = ip->next) {
540       const int best_area = best->width * best->height;
541       const int other_area = ip->width * ip->height;
542       int best_overlap;
543       int other_overlap;
544       if(rwidth == 0 && rheight == 0) {
545          best_overlap = 0;
546          other_overlap = 0;
547       } else if(rwidth == 0) {
548          best_overlap = Min(best->height, rheight);
549          other_overlap = Min(ip->height, rheight);
550       } else if(rheight == 0) {
551          best_overlap = Min(best->width, rwidth);
552          other_overlap = Min(ip->width, rwidth);
553       } else {
554          best_overlap = Min(best->width, rwidth)
555                       * Min(best->height, rheight);
556          other_overlap = Min(ip->width, rwidth)
557                        * Min(ip->height, rheight);
558       }
559       if(other_overlap > best_overlap) {
560          best = ip;
561       } else if(other_overlap == best_overlap) {
562          if(other_area < best_area) {
563             best = ip;
564          }
565       }
566    }
567    return best;
568 }
569 
570 /** Get a scaled icon. */
GetScaledIcon(IconNode * icon,long fg,int rwidth,int rheight)571 ScaledIconNode *GetScaledIcon(IconNode *icon, long fg,
572                               int rwidth, int rheight)
573 {
574 
575    XColor color;
576    XImage *image;
577    XPoint *points;
578    ImageNode *imageNode;
579    ScaledIconNode *np;
580    GC maskGC;
581    int x, y;
582    int scalex, scaley;     /* Fixed point. */
583    int srcx, srcy;         /* Fixed point. */
584    int nwidth, nheight;
585    unsigned char *data;
586    unsigned perLine;
587 
588    if(rwidth == 0) {
589       rwidth = icon->width;
590    }
591    if(rheight == 0) {
592       rheight = icon->height;
593    }
594 
595    if(icon->preserveAspect) {
596       const int ratio = (icon->width << 16) / icon->height;
597       nwidth = Min(rwidth, (rheight * ratio) >> 16);
598       nheight = Min(rheight, (nwidth << 16) / ratio);
599       nwidth = (nheight * ratio) >> 16;
600    } else {
601       nheight = rheight;
602       nwidth = rwidth;
603    }
604    nwidth = Max(1, nwidth);
605    nheight = Max(1, nheight);
606 
607    /* Check if this size already exists. */
608    for(np = icon->nodes; np; np = np->next) {
609       if(!icon->bitmap || np->fg == fg) {
610 #ifdef USE_XRENDER
611          /* If we are using xrender and only have one image size
612           * available, we can simply scale the existing icon. */
613          if(icon->render) {
614             if(icon->images == NULL || icon->images->next == NULL) {
615                return np;
616             }
617          }
618 #endif
619          if(np->width == nwidth && np->height == nheight) {
620             return np;
621          }
622       }
623    }
624 
625    /* Need to load the image. */
626    imageNode = GetBestImage(icon, nwidth, nheight);
627    if(JUNLIKELY(!imageNode)) {
628       return NULL;
629    }
630 
631    /* See if we can use XRender to create the icon. */
632 #ifdef USE_XRENDER
633    if(icon->render) {
634       np = CreateScaledRenderIcon(imageNode, fg);
635       np->next = icon->nodes;
636       icon->nodes = np;
637 
638       /* Don't keep the image data around after creating the icon. */
639       if(icon->images == NULL) {
640          DestroyImage(imageNode);
641       }
642 
643       return np;
644    }
645 #endif
646 
647    /* Create a new ScaledIconNode the old-fashioned way. */
648    np = Allocate(sizeof(ScaledIconNode));
649    np->fg = fg;
650    np->width = nwidth;
651    np->height = nheight;
652    np->next = icon->nodes;
653    icon->nodes = np;
654 
655    /* Create a mask. */
656    np->mask = JXCreatePixmap(display, rootWindow, nwidth, nheight, 1);
657    maskGC = JXCreateGC(display, np->mask, 0, NULL);
658    JXSetForeground(display, maskGC, 0);
659    JXFillRectangle(display, np->mask, maskGC, 0, 0, nwidth, nheight);
660    JXSetForeground(display, maskGC, 1);
661 
662    /* Create a temporary XImage for scaling. */
663    image = JXCreateImage(display, rootVisual, rootDepth,
664                          ZPixmap, 0, NULL, nwidth, nheight, 8, 0);
665    image->data = Allocate(sizeof(unsigned long) * nwidth * nheight);
666 
667    /* Determine the scale factor. */
668    scalex = (imageNode->width << 16) / nwidth;
669    scaley = (imageNode->height << 16) / nheight;
670 
671    points = Allocate(sizeof(XPoint) * nwidth);
672    data = imageNode->data;
673    if(imageNode->bitmap) {
674       perLine = (imageNode->width >> 3) + ((imageNode->width & 7) ? 1 : 0);
675    } else {
676       perLine = imageNode->width;
677    }
678    srcy = 0;
679    for(y = 0; y < nheight; y++) {
680       const int yindex = (srcy >> 16) * perLine;
681       int pindex = 0;
682       srcx = 0;
683       for(x = 0; x < nwidth; x++) {
684          if(imageNode->bitmap) {
685             const int tx = srcx >> 16;
686             const int offset = yindex + (tx >> 3);
687             const int mask = 1 << (tx & 7);
688             if(data[offset] & mask) {
689                points[pindex].x = x;
690                points[pindex].y = y;
691                XPutPixel(image, x, y, fg);
692                pindex += 1;
693             }
694          } else {
695             const int yindex = (srcy >> 16) * imageNode->width;
696             const int index = 4 * (yindex + (srcx >> 16));
697             color.red = data[index + 1];
698             color.red |= color.red << 8;
699             color.green = data[index + 2];
700             color.green |= color.green << 8;
701             color.blue = data[index + 3];
702             color.blue |= color.blue << 8;
703             GetColor(&color);
704             XPutPixel(image, x, y, color.pixel);
705             if(data[index] >= 128) {
706                points[pindex].x = x;
707                points[pindex].y = y;
708                pindex += 1;
709             }
710          }
711          srcx += scalex;
712       }
713       JXDrawPoints(display, np->mask, maskGC, points, pindex, CoordModeOrigin);
714       srcy += scaley;
715    }
716    Release(points);
717 
718    /* Release the mask GC. */
719    JXFreeGC(display, maskGC);
720 
721    /* Create the color data pixmap. */
722    np->image = JXCreatePixmap(display, rootWindow, nwidth, nheight,
723                               rootDepth);
724 
725    /* Render the image to the color data pixmap. */
726    JXPutImage(display, np->image, rootGC, image, 0, 0, 0, 0, nwidth, nheight);
727    /* Release the XImage. */
728    Release(image->data);
729    image->data = NULL;
730    JXDestroyImage(image);
731 
732    if(icon->images == NULL) {
733       DestroyImage(imageNode);
734    }
735 
736    return np;
737 
738 }
739 
740 /** Create an icon from binary data (as specified via window properties). */
CreateIconFromBinary(const unsigned long * input,unsigned int length)741 IconNode *CreateIconFromBinary(const unsigned long *input,
742                                unsigned int length)
743 {
744    IconNode *result = NULL;
745    unsigned int offset = 0;
746 
747    if(!input) {
748       return NULL;
749    }
750 
751    while(offset < length) {
752 
753       const unsigned width = input[offset + 0];
754       const unsigned height = input[offset + 1];
755       unsigned char *data;
756       ImageNode *image;
757       unsigned x;
758 
759       if(JUNLIKELY(width * height + 2 > length - offset)) {
760          Debug("invalid image size: %d x %d + 2 > %d",
761                width, height, length - offset);
762          return result;
763       } else if(JUNLIKELY(width == 0 || height == 0)) {
764          Debug("invalid image size: %d x %d", width, height);
765          return result;
766       }
767 
768       image = CreateImage(width, height, 0);
769       if(result == NULL) {
770          result = CreateIcon(image);
771       }
772       image->next = result->images;
773       result->images = image;
774       data = image->data;
775 
776       /* Note: the data types here might be of different sizes. */
777       offset += 2;
778       for(x = 0; x < width * height; x++) {
779          *data++ = (input[offset] >> 24) & 0xFF;
780          *data++ = (input[offset] >> 16) & 0xFF;
781          *data++ = (input[offset] >>  8) & 0xFF;
782          *data++ = (input[offset] >>  0) & 0xFF;
783          offset += 1;
784       }
785 
786       /* Don't insert this icon into the hash since it is transient. */
787 
788    }
789 
790    return result;
791 }
792 
793 /** Create an empty icon node. */
CreateIcon(const ImageNode * image)794 IconNode *CreateIcon(const ImageNode *image)
795 {
796    IconNode *icon;
797    icon = Allocate(sizeof(IconNode));
798    icon->nodes = NULL;
799    icon->name = NULL;
800    icon->images = NULL;
801    icon->next = NULL;
802    icon->prev = NULL;
803    icon->width = image->width;
804    icon->height = image->height;
805    icon->bitmap = image->bitmap;
806 #ifdef USE_XRENDER
807    icon->render = image->render;
808 #endif
809    icon->preserveAspect = 1;
810    icon->transient = 1;
811    return icon;
812 }
813 
814 /** Helper method for destroy icons. */
DoDestroyIcon(int index,IconNode * icon)815 void DoDestroyIcon(int index, IconNode *icon)
816 {
817    if(icon && icon != &emptyIcon) {
818       while(icon->nodes) {
819          ScaledIconNode *np = icon->nodes;
820 #ifdef USE_XRENDER
821          if(icon->render) {
822             if(np->image != None) {
823                JXRenderFreePicture(display, np->image);
824             }
825             if(np->mask != None) {
826                JXRenderFreePicture(display, np->mask);
827             }
828 #else
829          if(0) {
830 #endif
831          } else {
832             if(np->image != None) {
833                JXFreePixmap(display, np->image);
834             }
835             if(np->mask != None) {
836                JXFreePixmap(display, np->mask);
837             }
838          }
839          icon->nodes = np->next;
840          Release(np);
841       }
842       DestroyImage(icon->images);
843       if(icon->name) {
844          Release(icon->name);
845       }
846 
847       if(icon->prev) {
848          icon->prev->next = icon->next;
849       } else {
850          iconHash[index] = icon->next;
851       }
852       if(icon->next) {
853          icon->next->prev = icon->prev;
854       }
855       Release(icon);
856    }
857 }
858 
859 /** Destroy an icon. */
860 void DestroyIcon(IconNode *icon)
861 {
862    if(icon && icon->transient) {
863       const unsigned int index = GetHash(icon->name);
864       DoDestroyIcon(index, icon);
865    }
866 }
867 
868 /** Insert an icon to the icon hash table. */
869 void InsertIcon(IconNode *icon)
870 {
871    unsigned int index;
872    Assert(icon);
873    Assert(icon->name);
874    index = GetHash(icon->name);
875    icon->prev = NULL;
876    if(iconHash[index]) {
877       iconHash[index]->prev = icon;
878    }
879    icon->next = iconHash[index];
880    icon->transient = 0;
881    iconHash[index] = icon;
882 }
883 
884 /** Find a icon in the icon hash table. */
885 IconNode *FindIcon(const char *name)
886 {
887    const unsigned int index = GetHash(name);
888    IconNode *icon = iconHash[index];
889    while(icon) {
890       if(!strcmp(icon->name, name)) {
891          return icon;
892       }
893       icon = icon->next;
894    }
895 
896    return NULL;
897 }
898 
899 /** Get the hash for a string. */
900 unsigned int GetHash(const char *str)
901 {
902    unsigned int hash = 0;
903    if(str) {
904       unsigned int x;
905       for(x = 0; str[x]; x++) {
906          hash = (hash + (hash << 5)) ^ (unsigned int)str[x];
907       }
908       hash &= (HASH_SIZE - 1);
909    }
910    return hash;
911 }
912 
913 /** Set the name of the default icon. */
914 void SetDefaultIcon(const char *name)
915 {
916    if(defaultIconName) {
917       Release(defaultIconName);
918    }
919    defaultIconName = CopyString(name);
920 }
921 
922 #endif /* USE_ICONS */
923