JWM Source Documentation
icon.c
Go to the documentation of this file.
1 
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 
23 
24 #ifdef USE_ICONS
25 
26 /* Must be a power of two. */
27 #define HASH_SIZE 128
28 
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;
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 
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 
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 
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  }
135 }
136 
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) {
154  defaultIconName = NULL;
155  }
156 }
157 
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 
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);
242  }
243 
244  }
245 
246  }
247 
248 }
249 
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 
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 
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 
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;
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 
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 
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 
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 
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 
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. */
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 
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 
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 
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) {
824  }
825  if(np->mask != None) {
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 
861 {
862  if(icon && icon->transient) {
863  const unsigned int index = GetHash(icon->name);
864  DoDestroyIcon(index, icon);
865  }
866 }
867 
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 
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 
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 
914 void SetDefaultIcon(const char *name)
915 {
916  if(defaultIconName) {
918  }
919  defaultIconName = CopyString(name);
920 }
921 
922 #endif /* USE_ICONS */

joewing.net / Projects / JWM