1 /*
2  * Copyright 1989 Massachusetts Institute of Technology
3  * Copyright 1992 Claude Lecommandeur.
4  */
5 
6 /**********************************************************************
7  *
8  * $XConsortium: icons.c,v 1.22 91/07/12 09:58:38 dave Exp $
9  *
10  * Icon releated routines
11  *
12  * 10-Apr-89 Tom LaStrange        Initial Version.
13  *
14  * Do the necessary modification to be integrated in ctwm.
15  * Can no longer be used for the standard twm.
16  *
17  * 22-April-92 Claude Lecommandeur.
18  *
19  *
20  **********************************************************************/
21 
22 #include "ctwm.h"
23 
24 #include <stdio.h>
25 #include <stdlib.h>
26 
27 #include <X11/extensions/shape.h>
28 
29 #include "drawing.h"
30 #include "screen.h"
31 #include "iconmgr.h"
32 #include "icons.h"
33 #include "otp.h"
34 #include "list.h"
35 #include "parse.h"
36 #include "util.h"
37 #include "animate.h"
38 #include "image.h"
39 #include "win_utils.h"
40 #include "workspace_manager.h"
41 
42 static void splitIconRegionEntry(IconEntry *ie, RegGravity grav1,
43                                  RegGravity grav2, int w, int h);
44 static void PlaceIcon(TwmWindow *tmp_win, int def_x, int def_y,
45                       int *final_x, int *final_y);
46 static IconEntry *FindIconEntry(TwmWindow *tmp_win, IconRegion **irp);
47 static IconEntry *prevIconEntry(IconEntry *ie, IconRegion *ir);
48 static void mergeEntries(IconEntry *old, IconEntry *ie);
49 static void ReshapeIcon(Icon *icon);
50 static int roundUp(int v, int multiple);
51 static Image *LookupIconNameOrClass(TwmWindow *tmp_win, Icon *icon,
52                                     char **pattern);
53 
54 
55 
56 /*
57  ****************************************************************
58  *
59  * First some bits related to figuring out where icons go.  Lots of
60  * IconRegion handling stuff, handling of IconEntry tracking, etc.
61  *
62  ****************************************************************
63  */
64 
65 
66 /*
67  * This function operates in very weird and obtuse ways, especially in
68  * how it handles vertical vs. horizontal in weird recursive calls.  Part
69  * of this is what previously allowed specs with "hgrav vgrav" instead of
70  * the proper "vgrav hgrav" to sorta-work.  This should be broken up at
71  * some point into clean h/v functions, but because of the recursion it's
72  * not exactly trivial.  The parsing code now enforces v/h, so at least
73  * things can be known to come in in the right order initially.  Revisit
74  * someday.
75  */
76 static void
splitIconRegionEntry(IconEntry * ie,RegGravity grav1,RegGravity grav2,int w,int h)77 splitIconRegionEntry(IconEntry *ie, RegGravity grav1, RegGravity grav2,
78                      int w, int h)
79 {
80 	switch(grav1) {
81 		case GRAV_NORTH:
82 		case GRAV_SOUTH:
83 			if(w != ie->w) {
84 				splitIconRegionEntry(ie, grav2, grav1, w, ie->h);
85 			}
86 			if(h != ie->h) {
87 				IconEntry *new = calloc(1, sizeof(IconEntry));
88 				new->next = ie->next;
89 				ie->next = new;
90 				new->x = ie->x;
91 				new->h = (ie->h - h);
92 				new->w = ie->w;
93 				ie->h = h;
94 				if(grav1 == GRAV_SOUTH) {
95 					new->y = ie->y;
96 					ie->y = new->y + new->h;
97 				}
98 				else {
99 					new->y = ie->y + ie->h;
100 				}
101 			}
102 			break;
103 		case GRAV_EAST:
104 		case GRAV_WEST:
105 			if(h != ie->h) {
106 				splitIconRegionEntry(ie, grav2, grav1, ie->w, h);
107 			}
108 			if(w != ie->w) {
109 				IconEntry *new = calloc(1, sizeof(IconEntry));
110 				new->next = ie->next;
111 				ie->next = new;
112 				new->y = ie->y;
113 				new->w = (ie->w - w);
114 				new->h = ie->h;
115 				ie->w = w;
116 				if(grav1 == GRAV_EAST) {
117 					new->x = ie->x;
118 					ie->x = new->x + new->w;
119 				}
120 				else {
121 					new->x = ie->x + ie->w;
122 				}
123 			}
124 			break;
125 	}
126 }
127 
128 
129 /*
130  * Backend for parsing IconRegion config
131  */
132 name_list **
AddIconRegion(const char * geom,RegGravity grav1,RegGravity grav2,int stepx,int stepy,const char * ijust,const char * just,const char * align)133 AddIconRegion(const char *geom, RegGravity grav1, RegGravity grav2,
134               int stepx, int stepy,
135               const char *ijust, const char *just, const char *align)
136 {
137 	IconRegion *ir;
138 	int mask, tmp;
139 
140 	ir = malloc(sizeof(IconRegion));
141 	ir->next = NULL;
142 
143 	if(Scr->LastRegion) {
144 		Scr->LastRegion->next = ir;
145 	}
146 	Scr->LastRegion = ir;
147 	if(!Scr->FirstRegion) {
148 		Scr->FirstRegion = ir;
149 	}
150 
151 	ir->entries = NULL;
152 	ir->clientlist = NULL;
153 	ir->grav1 = grav1;
154 	ir->grav2 = grav2;
155 	if(stepx <= 0) {
156 		stepx = 1;
157 	}
158 	if(stepy <= 0) {
159 		stepy = 1;
160 	}
161 	ir->stepx = stepx;
162 	ir->stepy = stepy;
163 	ir->x = ir->y = ir->w = ir->h = 0;
164 
165 	mask = XParseGeometry(geom, &ir->x, &ir->y, (unsigned int *)&ir->w,
166 	                      (unsigned int *)&ir->h);
167 
168 	if(mask & XNegative) {
169 		ir->x += Scr->rootw - ir->w;
170 	}
171 	if(mask & YNegative) {
172 		ir->y += Scr->rooth - ir->h;
173 	}
174 
175 	ir->entries = calloc(1, sizeof(IconEntry));
176 	ir->entries->x = ir->x;
177 	ir->entries->y = ir->y;
178 	ir->entries->w = ir->w;
179 	ir->entries->h = ir->h;
180 
181 	if((tmp = ParseTitleJustification(ijust)) < 0) {
182 		twmrc_error_prefix();
183 		fprintf(stderr, "ignoring invalid IconRegion argument \"%s\"\n", ijust);
184 		tmp = TJ_UNDEF;
185 	}
186 	ir->TitleJustification = tmp;
187 
188 	if((tmp = ParseIRJustification(just)) < 0) {
189 		twmrc_error_prefix();
190 		fprintf(stderr, "ignoring invalid IconRegion argument \"%s\"\n", just);
191 		tmp = IRJ_UNDEF;
192 	}
193 	ir->Justification = tmp;
194 
195 	if((tmp = ParseAlignement(align)) < 0) {
196 		twmrc_error_prefix();
197 		fprintf(stderr, "ignoring invalid IconRegion argument \"%s\"\n", align);
198 		tmp = IRA_UNDEF;
199 	}
200 	ir->Alignement = tmp;
201 
202 	return(&(ir->clientlist));
203 }
204 
205 
206 /*
207  * Figure out where to put a window's icon based on the IconRegion
208  * specifications given in config.  Passed def_[xy] which are used
209  * if we don't find a better location ourselves.  Returns the chosen
210  * location in final_[xy], and also sets the IconRegion in tmp_win->icon
211  * if we chose one.
212  */
213 static void
PlaceIcon(TwmWindow * tmp_win,int def_x,int def_y,int * final_x,int * final_y)214 PlaceIcon(TwmWindow *tmp_win, int def_x, int def_y,
215           int *final_x, int *final_y)
216 {
217 	IconRegion  *ir, *oldir;
218 	IconEntry   *ie;
219 	int         w, h;
220 
221 	const int iconWidth = tmp_win->icon->border_width * 2
222 	                      + (Scr->ShrinkIconTitles ? tmp_win->icon->width
223 	                         : tmp_win->icon->w_width);
224 	const int iconHeight = tmp_win->icon->border_width * 2
225 	                       + tmp_win->icon->w_height;
226 
227 	/*
228 	 * First, check to see if the window is in a region's client list
229 	 * (i.e., the win-list on an IconRegion specifier in the config).
230 	 */
231 	ie = NULL;
232 	for(ir = Scr->FirstRegion; ir; ir = ir->next) {
233 		if(LookInList(ir->clientlist, tmp_win->name, &tmp_win->class)) {
234 			/*
235 			 * Found one that claims it.  Figure the necessary local
236 			 * size, based on the icon's side itself and the grid for
237 			 * this IR.
238 			 */
239 			w = roundUp(iconWidth, ir->stepx);
240 			h = roundUp(iconHeight, ir->stepy);
241 
242 			/* Find a currently-unused region that's big enough */
243 			for(ie = ir->entries; ie; ie = ie->next) {
244 				if(ie->used) {
245 					continue;
246 				}
247 				if(ie->w >= w && ie->h >= h) {
248 					/* Bingo */
249 					break;
250 				}
251 			}
252 
253 			/* If we found one, we're done here */
254 			if(ie) {
255 				break;
256 			}
257 		}
258 	}
259 
260 
261 	/*
262 	 * If we found a slot in a region claiming it, ie is set to the
263 	 * IconEntry.  If not, start over and find the first available berth.
264 	 */
265 	if(!ie) {
266 		for(ir = Scr->FirstRegion; ir; ir = ir->next) {
267 			w = roundUp(iconWidth, ir->stepx);
268 			h = roundUp(iconHeight, ir->stepy);
269 			for(ie = ir->entries; ie; ie = ie->next) {
270 				if(ie->used) {
271 					continue;
272 				}
273 				if(ie->w >= w && ie->h >= h) {
274 					/* Bingo */
275 					break;
276 				}
277 			}
278 			if(ie) {
279 				break;
280 			}
281 		}
282 	}
283 
284 	/* Stash for comparison */
285 	oldir = tmp_win->icon->ir;
286 
287 	/*
288 	 * If we found an appropriate region, use it.  Else, we have no
289 	 * better idea, so use the x/y coords the caller passed us as our
290 	 * basis.
291 	 */
292 	if(ie) {
293 		/* XXX whatever sIRE() does */
294 		splitIconRegionEntry(ie, ir->grav1, ir->grav2, w, h);
295 
296 		/* Adjust horizontal positioning based on IconRegionJustification */
297 		switch(ir->Justification) {
298 			case IRJ_LEFT:
299 				*final_x = ie->x;
300 				break;
301 			case IRJ_UNDEF:
302 			case IRJ_CENTER:
303 				*final_x = ie->x + (ie->w - iconWidth) / 2;
304 				break;
305 			case IRJ_RIGHT:
306 				*final_x = ie->x + ie->w - iconWidth;
307 				break;
308 			case IRJ_BORDER:
309 				if(ir->grav2 == GRAV_EAST) {
310 					*final_x = ie->x + ie->w - iconWidth;
311 				}
312 				else {
313 					*final_x = ie->x;
314 				}
315 				break;
316 		}
317 
318 		/* And vertical based on IconRegionAlignement */
319 		switch(ir->Alignement) {
320 			case IRA_TOP :
321 				*final_y = ie->y;
322 				break;
323 			case IRA_UNDEF :
324 			case IRA_CENTER :
325 				*final_y = ie->y + (ie->h - iconHeight) / 2;
326 				break;
327 			case IRA_BOTTOM :
328 				*final_y = ie->y + ie->h - iconHeight;
329 				break;
330 			case IRA_BORDER :
331 				if(ir->grav1 == GRAV_SOUTH) {
332 					*final_y = ie->y + ie->h - iconHeight;
333 				}
334 				else {
335 					*final_y = ie->y;
336 				}
337 				break;
338 		}
339 
340 		/* Tell the win/icon what region it's in, and the entry what's in it */
341 		tmp_win->icon->ir = ir;
342 		ie->used = true;
343 		ie->twm_win = tmp_win;
344 	}
345 	else {
346 		/* No better idea, tell caller to use theirs */
347 		*final_x = def_x;
348 		*final_y = def_y;
349 		tmp_win->icon->ir = NULL;
350 		return;
351 		/* XXX Should we be doing the below in this case too? */
352 	}
353 
354 	/* Alterations if ShrinkIconTitles is set */
355 	if(Scr->ShrinkIconTitles && tmp_win->icon->has_title) {
356 		*final_x -= GetIconOffset(tmp_win->icon);
357 		if(tmp_win->icon->ir != oldir) {
358 			ReshapeIcon(tmp_win->icon);
359 		}
360 	}
361 
362 	return;
363 }
364 
365 
366 /*
367  * Look up an IconEntry holding the icon for a given window, and
368  * optionally stash its IconRegion in irp.  Used internally in
369  * IconDown().
370  */
371 static IconEntry *
FindIconEntry(TwmWindow * tmp_win,IconRegion ** irp)372 FindIconEntry(TwmWindow *tmp_win, IconRegion **irp)
373 {
374 	IconRegion  *ir;
375 	IconEntry   *ie;
376 
377 	for(ir = Scr->FirstRegion; ir; ir = ir->next) {
378 		for(ie = ir->entries; ie; ie = ie->next)
379 			if(ie->twm_win == tmp_win) {
380 				if(irp) {
381 					*irp = ir;
382 				}
383 				return ie;
384 			}
385 	}
386 	return NULL;
387 }
388 
389 
390 /*
391  * Find prior IE in list.  Used internally in IconDown().
392  */
393 static IconEntry *
prevIconEntry(IconEntry * ie,IconRegion * ir)394 prevIconEntry(IconEntry *ie, IconRegion *ir)
395 {
396 	IconEntry   *ip;
397 
398 	if(ie == ir->entries) {
399 		return NULL;
400 	}
401 	for(ip = ir->entries; ip->next != ie; ip = ip->next)
402 		;
403 	return ip;
404 }
405 
406 
407 /*
408  * Merge two adjacent IconEntry's.  old is being freed; and is adjacent
409  * to ie.  Merge regions together.
410  */
411 static void
mergeEntries(IconEntry * old,IconEntry * ie)412 mergeEntries(IconEntry *old, IconEntry *ie)
413 {
414 	if(old->y == ie->y) {
415 		ie->w = old->w + ie->w;
416 		if(old->x < ie->x) {
417 			ie->x = old->x;
418 		}
419 	}
420 	else {
421 		ie->h = old->h + ie->h;
422 		if(old->y < ie->y) {
423 			ie->y = old->y;
424 		}
425 	}
426 }
427 
428 
429 
430 
431 /*
432  ****************************************************************
433  *
434  * Next, the bits related to creating and putting together the icon
435  * windows, as well as destroying them.
436  *
437  ****************************************************************
438  */
439 
440 
441 /*
442  * Create the window scaffolding for an icon.  Called when we need to
443  * make one, e.g. the first time a window is iconified.
444  */
445 void
CreateIconWindow(TwmWindow * tmp_win,int def_x,int def_y)446 CreateIconWindow(TwmWindow *tmp_win, int def_x, int def_y)
447 {
448 	unsigned long event_mask;
449 	unsigned long valuemask;            /* mask for create windows */
450 	XSetWindowAttributes attributes;    /* attributes for create windows */
451 	int final_x, final_y;
452 	int x;
453 	Icon        *icon;
454 	Image       *image = NULL;
455 	char        *pattern;
456 
457 	icon = malloc(sizeof(struct Icon));
458 
459 	icon->otp           = NULL;
460 	icon->border        = Scr->IconBorderColor;
461 	icon->iconc.fore    = Scr->IconC.fore;
462 	icon->iconc.back    = Scr->IconC.back;
463 	icon->title_shrunk  = false;
464 
465 	GetColorFromList(Scr->IconBorderColorL, tmp_win->name, &tmp_win->class,
466 	                 &icon->border);
467 	GetColorFromList(Scr->IconForegroundL, tmp_win->name, &tmp_win->class,
468 	                 &icon->iconc.fore);
469 	GetColorFromList(Scr->IconBackgroundL, tmp_win->name, &tmp_win->class,
470 	                 &icon->iconc.back);
471 	if(Scr->use3Diconmanagers && !Scr->BeNiceToColormap) {
472 		GetShadeColors(&icon->iconc);
473 	}
474 
475 	FB(icon->iconc.fore, icon->iconc.back);
476 
477 	icon->match   = match_none;
478 	icon->image   = NULL;
479 	icon->ir      = NULL;
480 
481 	tmp_win->forced = false;
482 	icon->w_not_ours = false;
483 
484 	pattern = NULL;
485 
486 	/* now go through the steps to get an icon window,  if ForceIcon is
487 	 * set, then no matter what else is defined, the bitmap from the
488 	 * .twmrc file is used
489 	 */
490 	if(Scr->ForceIcon) {
491 		image = LookupIconNameOrClass(tmp_win, icon, &pattern);
492 	}
493 
494 #ifdef EWMH
495 	/*
496 	 * Look to see if there is a _NET_WM_ICON property to provide an icon.
497 	 */
498 	if(image == NULL) {
499 		image = EwmhGetIcon(Scr, tmp_win);
500 		if(image != NULL) {
501 			icon->match   = match_net_wm_icon;
502 			icon->width   = image->width;
503 			icon->height  = image->height;
504 			icon->image   = image;
505 		}
506 	}
507 #endif /* EWMH */
508 
509 	/* if the pixmap is still NULL, we didn't get one from the above code,
510 	 * that could mean that ForceIcon was not set, or that the window
511 	 * was not in the Icons list, now check the WM hints for an icon
512 	 */
513 	if(image == NULL && tmp_win->wmhints->flags & IconPixmapHint) {
514 		unsigned int IconDepth, IconWidth, IconHeight;
515 
516 		if(XGetGeometry(dpy, tmp_win->wmhints->icon_pixmap,
517 		                &JunkRoot, &JunkX, &JunkY, &IconWidth, &IconHeight, &JunkBW, &IconDepth)) {
518 			image = AllocImage();
519 			image->width  = IconWidth;
520 			image->height = IconHeight;
521 			image->pixmap = XCreatePixmap(dpy, Scr->Root, image->width,
522 			                              image->height, Scr->d_depth);
523 			if(IconDepth == Scr->d_depth)
524 				XCopyArea(dpy, tmp_win->wmhints->icon_pixmap, image->pixmap, Scr->NormalGC,
525 				          0, 0, image->width, image->height, 0, 0);
526 			else
527 				XCopyPlane(dpy, tmp_win->wmhints->icon_pixmap, image->pixmap, Scr->NormalGC,
528 				           0, 0, image->width, image->height, 0, 0, 1);
529 
530 			icon->width   = image->width;
531 			icon->height  = image->height;
532 			icon->match   = match_icon_pixmap_hint;
533 
534 			if((tmp_win->wmhints->flags & IconMaskHint) &&
535 			                XGetGeometry(dpy, tmp_win->wmhints->icon_mask,
536 			                             &JunkRoot, &JunkX, &JunkY, &IconWidth, &IconHeight, &JunkBW, &IconDepth) &&
537 			                (IconDepth == 1)) {
538 				GC gc;
539 
540 				image->mask = XCreatePixmap(dpy, Scr->Root, IconWidth, IconHeight, 1);
541 				if(image->mask) {
542 					gc = XCreateGC(dpy, image->mask, 0, NULL);
543 					if(gc) {
544 						XCopyArea(dpy, tmp_win->wmhints->icon_mask, image->mask, gc,
545 						          0, 0, IconWidth, IconHeight, 0, 0);
546 						XFreeGC(dpy, gc);
547 					}
548 				}
549 			}
550 			icon->image = image;
551 		}
552 	}
553 
554 	/* if we still haven't got an icon, let's look in the Icon list
555 	 * if ForceIcon is not set
556 	 */
557 	if(image == NULL && !Scr->ForceIcon) {
558 		image = LookupIconNameOrClass(tmp_win, icon, &pattern);
559 	}
560 
561 	/* if we still don't have an icon, assign the UnknownIcon */
562 	if(image == NULL && Scr->UnknownImage != NULL) {
563 		image = Scr->UnknownImage;
564 		icon->match   = match_unknown_default;
565 		icon->width   = image->width;
566 		icon->height  = image->height;
567 		icon->image   = image;
568 	}
569 
570 	if(image == NULL) {
571 		icon->height = 0;
572 		icon->width  = 0;
573 		valuemask    = 0;
574 	}
575 	else {
576 		valuemask = CWBackPixmap;
577 		attributes.background_pixmap = image->pixmap;
578 	}
579 
580 	icon->border_width = Scr->IconBorderWidth;
581 	if(Scr->NoIconTitlebar ||
582 	                LookInNameList(Scr->NoIconTitle, tmp_win->icon_name) ||
583 	                LookInList(Scr->NoIconTitle, tmp_win->name, &tmp_win->class)) {
584 		icon->w_width  = icon->width;
585 		icon->w_height = icon->height;
586 		icon->x = 0;
587 		icon->y = 0;
588 		icon->has_title = false;
589 	}
590 	else {
591 		XRectangle inc_rect;
592 		XRectangle logical_rect;
593 
594 		XmbTextExtents(Scr->IconFont.font_set,
595 		               tmp_win->icon_name, strlen(tmp_win->icon_name),
596 		               &inc_rect, &logical_rect);
597 		icon->w_width = logical_rect.width;
598 
599 		icon->w_width += 2 * (Scr->IconManagerShadowDepth + ICON_MGR_IBORDER);
600 		if(icon->w_width > Scr->MaxIconTitleWidth) {
601 			icon->w_width = Scr->MaxIconTitleWidth;
602 		}
603 		if(icon->w_width < icon->width) {
604 			icon->x  = (icon->width - icon->w_width) / 2;
605 			icon->x += Scr->IconManagerShadowDepth + ICON_MGR_IBORDER;
606 			icon->w_width = icon->width;
607 		}
608 		else {
609 			icon->x = Scr->IconManagerShadowDepth + ICON_MGR_IBORDER;
610 		}
611 		icon->y = icon->height + Scr->IconFont.height + Scr->IconManagerShadowDepth;
612 		icon->w_height = icon->height + Scr->IconFont.height +
613 		                 2 * (Scr->IconManagerShadowDepth + ICON_MGR_IBORDER);
614 		icon->has_title = true;
615 		if(icon->height) {
616 			icon->border_width = 0;
617 		}
618 	}
619 
620 	event_mask = 0;
621 	if(tmp_win->wmhints->flags & IconWindowHint) {
622 		icon->w = tmp_win->wmhints->icon_window;
623 		if(tmp_win->forced ||
624 		                XGetGeometry(dpy, icon->w, &JunkRoot, &JunkX, &JunkY,
625 		                             (unsigned int *)&icon->w_width, (unsigned int *)&icon->w_height,
626 		                             &JunkBW, &JunkDepth) == 0) {
627 			icon->w = None;
628 			tmp_win->wmhints->flags &= ~IconWindowHint;
629 		}
630 		else {
631 			image = NULL;
632 			icon->w_not_ours = true;
633 			icon->width  = icon->w_width;
634 			icon->height = icon->w_height;
635 			icon->image  = image;
636 			icon->has_title = false;
637 			event_mask = 0;
638 		}
639 	}
640 	else {
641 		icon->w = None;
642 	}
643 
644 	if((image != NULL) &&
645 	                image->mask != None &&
646 	                !(tmp_win->wmhints->flags & IconWindowHint)) {
647 		icon->border_width = 0;
648 	}
649 	if(icon->w == None) {
650 		icon->w = XCreateSimpleWindow(dpy, Scr->Root,
651 		                              0, 0,
652 		                              icon->w_width, icon->w_height,
653 		                              icon->border_width, icon->border, icon->iconc.back);
654 		event_mask = ExposureMask;
655 	}
656 
657 	if(Scr->AutoRaiseIcons || Scr->ShrinkIconTitles) {
658 		event_mask |= EnterWindowMask | LeaveWindowMask;
659 	}
660 	event_mask |= KeyPressMask | ButtonPressMask | ButtonReleaseMask;
661 
662 	if(icon->w_not_ours) {
663 		XWindowAttributes wattr;
664 
665 		XGetWindowAttributes(dpy, icon->w, &wattr);
666 		if(wattr.all_event_masks & ButtonPressMask) {
667 			event_mask &= ~ButtonPressMask;
668 		}
669 	}
670 	XSelectInput(dpy, icon->w, event_mask);
671 
672 	if(icon->width == 0) {
673 		icon->width = icon->w_width;
674 	}
675 	icon->bm_w = None;
676 	if(image && !(tmp_win->wmhints->flags & IconWindowHint)) {
677 		XRectangle rect;
678 
679 		x = GetIconOffset(icon);
680 		icon->bm_w = XCreateWindow(dpy, icon->w, x, 0,
681 		                           icon->width,
682 		                           icon->height,
683 		                           0, Scr->d_depth,
684 		                           CopyFromParent,
685 		                           Scr->d_visual, valuemask,
686 		                           &attributes);
687 		if(image->mask) {
688 			XShapeCombineMask(dpy, icon->bm_w, ShapeBounding, 0, 0, image->mask, ShapeSet);
689 			XShapeCombineMask(dpy, icon->w,    ShapeBounding, x, 0, image->mask, ShapeSet);
690 		}
691 		else if(icon->has_title) {
692 			rect.x      = x;
693 			rect.y      = 0;
694 			rect.width  = icon->width;
695 			rect.height = icon->height;
696 			XShapeCombineRectangles(dpy, icon->w, ShapeBounding,
697 			                        0, 0, &rect, 1, ShapeSet, 0);
698 		}
699 		if(icon->has_title) {
700 			if(Scr->ShrinkIconTitles) {
701 				rect.x      = x;
702 				rect.y      = icon->height;
703 				rect.width  = icon->width;
704 				rect.height = icon->w_height - icon->height;
705 				icon->title_shrunk = true;
706 			}
707 			else {
708 				rect.x      = 0;
709 				rect.y      = icon->height;
710 				rect.width  = icon->w_width;
711 				rect.height = icon->w_height - icon->height;
712 				icon->title_shrunk = false;
713 			}
714 			XShapeCombineRectangles(dpy, icon->w, ShapeBounding,
715 			                        0, 0, &rect, 1, ShapeUnion, 0);
716 		}
717 	}
718 
719 	if(pattern != NULL) {
720 		AddToList(&tmp_win->iconslist, pattern, icon);
721 	}
722 
723 	tmp_win->icon = icon;
724 	/* I need to figure out where to put the icon window now, because
725 	 * getting here means that I am going to make the icon visible
726 	 */
727 	final_x = final_y = 0;
728 	if(tmp_win->wmhints->flags & IconPositionHint) {
729 		final_x = tmp_win->wmhints->icon_x;
730 		final_y = tmp_win->wmhints->icon_y;
731 	}
732 	else {
733 		if(visible(tmp_win)) {
734 			PlaceIcon(tmp_win, def_x, def_y, &final_x, &final_y);
735 		}
736 	}
737 
738 	if(visible(tmp_win) || (tmp_win->wmhints->flags & IconPositionHint)) {
739 		if(final_x > Scr->rootw) {
740 			final_x = Scr->rootw - icon->w_width - (2 * Scr->IconBorderWidth);
741 		}
742 		if(Scr->ShrinkIconTitles && icon->bm_w) {
743 			if(final_x + (icon->w_width - icon->width) < 0) {
744 				final_x = 0;
745 			}
746 		}
747 		else {
748 			if(final_x < 0) {
749 				final_x = 0;
750 			}
751 		}
752 		if(final_y > Scr->rooth)
753 			final_y = Scr->rooth - icon->height -
754 			          Scr->IconFont.height - 6 - (2 * Scr->IconBorderWidth);
755 		if(final_y < 0) {
756 			final_y = 0;
757 		}
758 
759 		XMoveWindow(dpy, icon->w, final_x, final_y);
760 		icon->w_x = final_x;
761 		icon->w_y = final_y;
762 	}
763 	tmp_win->iconified = true;
764 	OtpAdd(tmp_win, IconWin);
765 
766 	XMapSubwindows(dpy, icon->w);
767 	XSaveContext(dpy, icon->w, TwmContext, (XPointer)tmp_win);
768 	XSaveContext(dpy, icon->w, ScreenContext, (XPointer)Scr);
769 	XDefineCursor(dpy, icon->w, Scr->IconCursor);
770 	MaybeAnimate = true;
771 }
772 
773 
774 /*
775  * Delete TwmWindow.iconslist.
776  * Call it before deleting TwmWindow.icon, since we need to check
777  * that we're not deleting that Icon.
778  */
779 void
DeleteIconsList(TwmWindow * tmp_win)780 DeleteIconsList(TwmWindow *tmp_win)
781 {
782 	/*
783 	 * Only the list itself needs to be freed, since the pointers it
784 	 * contains point into various lists that belong to Scr.
785 	 *
786 	 * Rhialto: Hmmmm not quite sure about that! CreateIconWindow() above
787 	 * always allocates a struct Icon, and doesn't attach it to Scr...
788 	 * It is probably correct for the Image pointers inside those Icons though.
789 	 */
790 	name_list *nptr;
791 	name_list *next;
792 
793 	for(nptr = tmp_win->iconslist; nptr != NULL;) {
794 		next = nptr->next;
795 		Icon *icon = (Icon *)nptr->ptr;
796 		if(icon != tmp_win->icon) {
797 			DeleteIcon(icon);
798 		}
799 		free(nptr->name);
800 		free(nptr);
801 		nptr = next;
802 	}
803 	tmp_win->iconslist = NULL;
804 }
805 
806 
807 /*
808  * Delete a single Icon.  Called iteratively from DeleteIconList(), and
809  * directly during window destruction.
810  */
811 void
DeleteIcon(Icon * icon)812 DeleteIcon(Icon *icon)
813 {
814 	if(icon->w && !icon->w_not_ours) {
815 		XDestroyWindow(dpy, icon->w);
816 	}
817 	ReleaseIconImage(icon);
818 	free(icon);
819 }
820 
821 
822 /*
823  * Delete the Image from an icon, if it is not a shared one.  match_list
824  * ands match_unknown_default need not be freed.
825  *
826  * Formerly ReleaseImage()
827  */
828 void
ReleaseIconImage(Icon * icon)829 ReleaseIconImage(Icon *icon)
830 {
831 	if(icon->match == match_icon_pixmap_hint ||
832 	                icon->match == match_net_wm_icon) {
833 		FreeImage(icon->image);
834 	}
835 }
836 
837 
838 
839 
840 /*
841  ****************************************************************
842  *
843  * Bringing an icon up or down.
844  *
845  ****************************************************************
846  */
847 
848 
849 /*
850  * Show up an icon.  Note that neither IconUp nor IconDown actually map
851  * or unmap the icon window; that's handled by the callers.  These
852  * functions limit themselves to figuring out where it should be, moving
853  * it (still unmapped) there, and linking/unlinking it from the iconentry
854  * lists.
855  */
856 void
IconUp(TwmWindow * tmp_win)857 IconUp(TwmWindow *tmp_win)
858 {
859 	int         x, y;
860 	int         defx, defy;
861 
862 	/*
863 	 * If the client specified a particular location, let's use it (this might
864 	 * want to be an option at some point).  Otherwise, try to fit within the
865 	 * icon region.
866 	 */
867 	if(tmp_win->wmhints->flags & IconPositionHint) {
868 		return;
869 	}
870 
871 	if(tmp_win->icon_moved) {
872 		struct IconRegion *ir;
873 		unsigned int iww, iwh;
874 
875 		if(!XGetGeometry(dpy, tmp_win->icon->w, &JunkRoot, &defx, &defy,
876 		                 &iww, &iwh, &JunkBW, &JunkDepth)) {
877 			return;
878 		}
879 
880 		x = defx + ((int) iww) / 2;
881 		y = defy + ((int) iwh) / 2;
882 
883 		for(ir = Scr->FirstRegion; ir; ir = ir->next) {
884 			if(x >= ir->x && x < (ir->x + ir->w) &&
885 			                y >= ir->y && y < (ir->y + ir->h)) {
886 				break;
887 			}
888 		}
889 		if(!ir) {
890 			return;        /* outside icon regions, leave alone */
891 		}
892 	}
893 
894 	defx = -100;
895 	defy = -100;
896 	PlaceIcon(tmp_win, defx, defy, &x, &y);
897 	if(x != defx || y != defy) {
898 		XMoveWindow(dpy, tmp_win->icon->w, x, y);
899 		tmp_win->icon->w_x = x;
900 		tmp_win->icon->w_y = y;
901 		tmp_win->icon_moved = false;    /* since we've restored it */
902 	}
903 	MaybeAnimate = true;
904 	return;
905 }
906 
907 
908 /*
909  * Remove an icon from its displayed IconEntry.  x-ref comment on
910  * IconUp().
911  */
912 void
IconDown(TwmWindow * tmp_win)913 IconDown(TwmWindow *tmp_win)
914 {
915 	IconEntry   *ie, *ip, *in;
916 	IconRegion  *ir;
917 
918 	ie = FindIconEntry(tmp_win, &ir);
919 	if(ie) {
920 		ie->twm_win = NULL;
921 		ie->used = false;
922 		ip = prevIconEntry(ie, ir);
923 		in = ie->next;
924 		for(;;) {
925 			if(ip && ip->used == false &&
926 			                ((ip->x == ie->x && ip->w == ie->w) ||
927 			                 (ip->y == ie->y && ip->h == ie->h))) {
928 				ip->next = ie->next;
929 				mergeEntries(ie, ip);
930 				free(ie);
931 				ie = ip;
932 				ip = prevIconEntry(ip, ir);
933 			}
934 			else if(in && in->used == false &&
935 			                ((in->x == ie->x && in->w == ie->w) ||
936 			                 (in->y == ie->y && in->h == ie->h))) {
937 				ie->next = in->next;
938 				mergeEntries(in, ie);
939 				free(in);
940 				in = ie->next;
941 			}
942 			else {
943 				break;
944 			}
945 		}
946 	}
947 }
948 
949 
950 
951 
952 /*
953  ****************************************************************
954  *
955  * Funcs related to drawing the icon.
956  *
957  ****************************************************************
958  */
959 
960 
961 /*
962  * Slightly misnamed: draws the text label under an icon.
963  */
964 void
PaintIcon(TwmWindow * tmp_win)965 PaintIcon(TwmWindow *tmp_win)
966 {
967 	int         width, twidth, mwidth, len, x;
968 	Icon        *icon;
969 	XRectangle ink_rect;
970 	XRectangle logical_rect;
971 
972 	if(!tmp_win || !tmp_win->icon) {
973 		return;
974 	}
975 	icon = tmp_win->icon;
976 	if(!icon->has_title) {
977 		return;
978 	}
979 
980 	x     = 0;
981 	width = icon->w_width;
982 	if(Scr->ShrinkIconTitles && icon->title_shrunk) {
983 		x     = GetIconOffset(icon);
984 		width = icon->width;
985 	}
986 	len    = strlen(tmp_win->icon_name);
987 	XmbTextExtents(Scr->IconFont.font_set,
988 	               tmp_win->icon_name, len,
989 	               &ink_rect, &logical_rect);
990 	twidth = logical_rect.width;
991 	mwidth = width - 2 * (Scr->IconManagerShadowDepth + ICON_MGR_IBORDER);
992 	if(Scr->use3Diconmanagers) {
993 		Draw3DBorder(icon->w, x, icon->height, width,
994 		             Scr->IconFont.height +
995 		             2 * (Scr->IconManagerShadowDepth + ICON_MGR_IBORDER),
996 		             Scr->IconManagerShadowDepth, icon->iconc, off, false, false);
997 	}
998 	while((len > 0) && (twidth > mwidth)) {
999 		len--;
1000 		XmbTextExtents(Scr->IconFont.font_set,
1001 		               tmp_win->icon_name, len,
1002 		               &ink_rect, &logical_rect);
1003 		twidth = logical_rect.width;
1004 	}
1005 	FB(icon->iconc.fore, icon->iconc.back);
1006 	XmbDrawString(dpy, icon->w, Scr->IconFont.font_set, Scr->NormalGC,
1007 	              x + ((mwidth - twidth) / 2) +
1008 	              Scr->IconManagerShadowDepth + ICON_MGR_IBORDER,
1009 	              icon->y, tmp_win->icon_name, len);
1010 }
1011 
1012 
1013 /*
1014  * Handling for ShrinkIconTitles; when pointer is away from them, shrink
1015  * the titles down to the width of the image, and expand back out when it
1016  * enters.
1017  */
1018 void
ShrinkIconTitle(TwmWindow * tmp_win)1019 ShrinkIconTitle(TwmWindow *tmp_win)
1020 {
1021 	Icon        *icon;
1022 	XRectangle  rect;
1023 
1024 	if(!tmp_win || !tmp_win->icon) {
1025 		return;
1026 	}
1027 	icon = tmp_win->icon;
1028 	if(!icon->has_title) {
1029 		return;
1030 	}
1031 	if(icon->w_width == icon->width) {
1032 		return;
1033 	}
1034 	if(icon->height  == 0) {
1035 		return;
1036 	}
1037 
1038 	rect.x      = GetIconOffset(icon);
1039 	rect.y      = 0;
1040 	rect.width  = icon->width;
1041 	rect.height = icon->w_height;
1042 	XShapeCombineRectangles(dpy, icon->w, ShapeBounding, 0, 0, &rect, 1,
1043 	                        ShapeIntersect, 0);
1044 	icon->title_shrunk = true;
1045 	XClearArea(dpy, icon->w, 0, icon->height, icon->w_width,
1046 	           icon->w_height - icon->height, True);
1047 }
1048 
1049 
1050 void
ExpandIconTitle(TwmWindow * tmp_win)1051 ExpandIconTitle(TwmWindow *tmp_win)
1052 {
1053 	Icon        *icon;
1054 	XRectangle  rect;
1055 
1056 	if(!tmp_win || !tmp_win->icon) {
1057 		return;
1058 	}
1059 	icon = tmp_win->icon;
1060 	if(!icon->has_title) {
1061 		return;
1062 	}
1063 	if(icon->w_width == icon->width) {
1064 		return;
1065 	}
1066 	if(icon->height  == 0) {
1067 		return;
1068 	}
1069 
1070 	rect.x      = 0;
1071 	rect.y      = icon->height;
1072 	rect.width  = icon->w_width;
1073 	rect.height = icon->w_height - icon->height;
1074 	XShapeCombineRectangles(dpy, icon->w, ShapeBounding, 0, 0, &rect, 1, ShapeUnion,
1075 	                        0);
1076 	icon->title_shrunk = false;
1077 	XClearArea(dpy, icon->w, 0, icon->height, icon->w_width,
1078 	           icon->w_height - icon->height, True);
1079 }
1080 
1081 
1082 /*
1083  * Setup X Shape'ing around icons and their titles.
1084  *
1085  * XXX should this be checking HasShape?  It seems to be called
1086  * unconditionally...
1087  */
1088 static void
ReshapeIcon(Icon * icon)1089 ReshapeIcon(Icon *icon)
1090 {
1091 	int x;
1092 	XRectangle  rect;
1093 
1094 	if(!icon) {
1095 		return;
1096 	}
1097 	x = GetIconOffset(icon);
1098 	XMoveWindow(dpy, icon->bm_w, x, 0);
1099 
1100 	if(icon->image && icon->image->mask) {
1101 		XShapeCombineMask(dpy, icon->w, ShapeBounding, x, 0, icon->image->mask,
1102 		                  ShapeSet);
1103 	}
1104 	else {
1105 		rect.x      = x;
1106 		rect.y      = 0;
1107 		rect.width  = icon->width;
1108 		rect.height = icon->height;
1109 		XShapeCombineRectangles(dpy, icon->w, ShapeBounding, 0, 0, &rect, 1, ShapeSet,
1110 		                        0);
1111 	}
1112 	rect.x      = x;
1113 	rect.y      = icon->height;
1114 	rect.width  = icon->width;
1115 	rect.height = icon->w_height - icon->height;
1116 	XShapeCombineRectangles(dpy, icon->w, ShapeBounding, 0, 0, &rect, 1, ShapeUnion,
1117 	                        0);
1118 }
1119 
1120 
1121 /*
1122  * Figure horizontal positioning/offset for the icon image within its
1123  * window.
1124  */
1125 int
GetIconOffset(Icon * icon)1126 GetIconOffset(Icon *icon)
1127 {
1128 	TitleJust justif;
1129 
1130 	if(!icon) {
1131 		return 0;
1132 	}
1133 
1134 	justif = icon->ir ? icon->ir->TitleJustification : Scr->IconJustification;
1135 	switch(justif) {
1136 		case TJ_LEFT:
1137 			return 0;
1138 
1139 		case TJ_CENTER:
1140 			return ((icon->w_width - icon->width) / 2);
1141 
1142 		case TJ_RIGHT:
1143 			return (icon->w_width - icon->width);
1144 
1145 		default:
1146 			/* Can't happen? */
1147 			fprintf(stderr, "%s(): Invalid TitleJustification %d\n",
1148 			        __func__, justif);
1149 			return 0;
1150 	}
1151 }
1152 
1153 
1154 /*
1155  * [Re-]lookup the image for an icon and [re-]layout it.
1156  */
1157 void
RedoIcon(TwmWindow * win)1158 RedoIcon(TwmWindow *win)
1159 {
1160 	Icon *icon, *old_icon;
1161 	char *pattern;
1162 
1163 	old_icon = win->icon;
1164 
1165 	if(old_icon && (old_icon->w_not_ours || old_icon->match != match_list)) {
1166 		RedoIconName(win);
1167 		return;
1168 	}
1169 	icon = NULL;
1170 	if((pattern = LookPatternInNameList(Scr->IconNames, win->icon_name))) {
1171 		icon = LookInNameList(win->iconslist, pattern);
1172 	}
1173 	else if((pattern = LookPatternInNameList(Scr->IconNames, win->name))) {
1174 		icon = LookInNameList(win->iconslist, pattern);
1175 	}
1176 	else if((pattern = LookPatternInList(Scr->IconNames, win->name,
1177 	                                     &win->class))) {
1178 		icon = LookInNameList(win->iconslist, pattern);
1179 	}
1180 	if(pattern == NULL) {
1181 		RedoIconName(win);
1182 		return;
1183 	}
1184 	if(icon != NULL) {
1185 		if(old_icon == icon) {
1186 			RedoIconName(win);
1187 			return;
1188 		}
1189 		if(win->icon_on && visible(win)) {
1190 			IconDown(win);
1191 			if(old_icon && old_icon->w) {
1192 				XUnmapWindow(dpy, old_icon->w);
1193 			}
1194 			win->icon = icon;
1195 			OtpReassignIcon(win, old_icon);
1196 			IconUp(win);
1197 			OtpRaise(win, IconWin);
1198 			XMapWindow(dpy, win->icon->w);
1199 		}
1200 		else {
1201 			win->icon = icon;
1202 			OtpReassignIcon(win, old_icon);
1203 		}
1204 		RedoIconName(win);
1205 	}
1206 	else {
1207 		if(win->icon_on && visible(win)) {
1208 			IconDown(win);
1209 			if(old_icon && old_icon->w) {
1210 				XUnmapWindow(dpy, old_icon->w);
1211 			}
1212 			/*
1213 			 * If the icon name/class was found on one of the above lists,
1214 			 * the call to CreateIconWindow() will find it again there
1215 			 * and keep track of it on win->iconslist for eventual
1216 			 * deallocation. (It is now checked that the current struct
1217 			 * Icon is also already on that list)
1218 			 */
1219 			OtpFreeIcon(win);
1220 			bool saveForceIcon = Scr->ForceIcon;
1221 			Scr->ForceIcon = true;
1222 			CreateIconWindow(win, -100, -100);
1223 			Scr->ForceIcon = saveForceIcon;
1224 			OtpRaise(win, IconWin);
1225 			XMapWindow(dpy, win->icon->w);
1226 		}
1227 		else {
1228 			OtpFreeIcon(win);
1229 			win->icon = NULL;
1230 			WMapUpdateIconName(win);
1231 		}
1232 		RedoIconName(win);
1233 	}
1234 }
1235 
1236 
1237 /*
1238  * Resize the icon window, and reposition the image and name within it.
1239  * (a lot of the actual repositioning gets done during the later expose).
1240  */
1241 void
RedoIconName(TwmWindow * win)1242 RedoIconName(TwmWindow *win)
1243 {
1244 	int x;
1245 	XRectangle ink_rect;
1246 	XRectangle logical_rect;
1247 
1248 	if(Scr->NoIconTitlebar ||
1249 	                LookInNameList(Scr->NoIconTitle, win->icon_name) ||
1250 	                LookInList(Scr->NoIconTitle, win->name, &win->class)) {
1251 		WMapUpdateIconName(win);
1252 		return;
1253 	}
1254 	if(win->iconmanagerlist) {
1255 		/* let the expose event cause the repaint */
1256 		XClearArea(dpy, win->iconmanagerlist->w, 0, 0, 0, 0, True);
1257 
1258 		if(Scr->SortIconMgr) {
1259 			SortIconManager(win->iconmanagerlist->iconmgr);
1260 		}
1261 	}
1262 
1263 	if(!win->icon  || !win->icon->w) {
1264 		WMapUpdateIconName(win);
1265 		return;
1266 	}
1267 
1268 	if(win->icon->w_not_ours) {
1269 		WMapUpdateIconName(win);
1270 		return;
1271 	}
1272 
1273 	XmbTextExtents(Scr->IconFont.font_set,
1274 	               win->icon_name, strlen(win->icon_name),
1275 	               &ink_rect, &logical_rect);
1276 	win->icon->w_width = logical_rect.width;
1277 	win->icon->w_width += 2 * (Scr->IconManagerShadowDepth + ICON_MGR_IBORDER);
1278 	if(win->icon->w_width > Scr->MaxIconTitleWidth) {
1279 		win->icon->w_width = Scr->MaxIconTitleWidth;
1280 	}
1281 
1282 	if(win->icon->w_width < win->icon->width) {
1283 		win->icon->x = (win->icon->width - win->icon->w_width) / 2;
1284 		win->icon->x += Scr->IconManagerShadowDepth + ICON_MGR_IBORDER;
1285 		win->icon->w_width = win->icon->width;
1286 	}
1287 	else {
1288 		win->icon->x = Scr->IconManagerShadowDepth + ICON_MGR_IBORDER;
1289 	}
1290 
1291 	x = GetIconOffset(win->icon);
1292 	win->icon->y = win->icon->height + Scr->IconFont.height +
1293 	               Scr->IconManagerShadowDepth;
1294 	win->icon->w_height = win->icon->height + Scr->IconFont.height +
1295 	                      2 * (Scr->IconManagerShadowDepth + ICON_MGR_IBORDER);
1296 
1297 	XResizeWindow(dpy, win->icon->w, win->icon->w_width,
1298 	              win->icon->w_height);
1299 	if(win->icon->bm_w) {
1300 		XRectangle rect;
1301 
1302 		XMoveWindow(dpy, win->icon->bm_w, x, 0);
1303 		XMapWindow(dpy, win->icon->bm_w);
1304 		if(win->icon->image && win->icon->image->mask) {
1305 			XShapeCombineMask(dpy, win->icon->bm_w, ShapeBounding, 0, 0,
1306 			                  win->icon->image->mask, ShapeSet);
1307 			XShapeCombineMask(dpy, win->icon->w, ShapeBounding, x, 0,
1308 			                  win->icon->image->mask, ShapeSet);
1309 		}
1310 		else if(win->icon->has_title) {
1311 			rect.x      = x;
1312 			rect.y      = 0;
1313 			rect.width  = win->icon->width;
1314 			rect.height = win->icon->height;
1315 			XShapeCombineRectangles(dpy, win->icon->w, ShapeBounding,
1316 			                        0, 0, &rect, 1, ShapeSet, 0);
1317 		}
1318 		if(win->icon->has_title) {
1319 			if(Scr->ShrinkIconTitles && win->icon->title_shrunk) {
1320 				rect.x      = x;
1321 				rect.y      = win->icon->height;
1322 				rect.width  = win->icon->width;
1323 				rect.height = win->icon->w_height - win->icon->height;
1324 			}
1325 			else {
1326 				rect.x      = 0;
1327 				rect.y      = win->icon->height;
1328 				rect.width  = win->icon->w_width;
1329 				rect.height = win->icon->w_height - win->icon->height;
1330 			}
1331 			XShapeCombineRectangles(dpy,  win->icon->w, ShapeBounding, 0,
1332 			                        0, &rect, 1, ShapeUnion, 0);
1333 		}
1334 	}
1335 	if(Scr->ShrinkIconTitles &&
1336 	                win->icon->title_shrunk &&
1337 	                win->icon_on && (visible(win))) {
1338 		IconDown(win);
1339 		IconUp(win);
1340 	}
1341 	if(win->isicon) {
1342 		XClearArea(dpy, win->icon->w, 0, 0, 0, 0, True);
1343 	}
1344 
1345 	WMapUpdateIconName(win);
1346 }
1347 
1348 
1349 
1350 
1351 /*
1352  ****************************************************************
1353  *
1354  * Misc internal utils.
1355  *
1356  ****************************************************************
1357  */
1358 
1359 
1360 /*
1361  * What it says on the tin.
1362  */
1363 static int
roundUp(int v,int multiple)1364 roundUp(int v, int multiple)
1365 {
1366 	return ((v + multiple - 1) / multiple) * multiple;
1367 }
1368 
1369 
1370 /*
1371  * Find the image set in Icons{} for a TwmWindow if possible.  Return the
1372  * image, record its provenance inside *icon, and pass back what pattern
1373  * it matched in **pattern.
1374  */
1375 static Image *
LookupIconNameOrClass(TwmWindow * tmp_win,Icon * icon,char ** pattern)1376 LookupIconNameOrClass(TwmWindow *tmp_win, Icon *icon, char **pattern)
1377 {
1378 	char *icon_name = NULL;
1379 	Image *image;
1380 	Matchtype matched = match_none;
1381 
1382 	icon_name = LookInNameList(Scr->IconNames, tmp_win->icon_name);
1383 	if(icon_name != NULL) {
1384 		*pattern = LookPatternInNameList(Scr->IconNames, tmp_win->icon_name);
1385 		matched = match_list;
1386 	}
1387 
1388 	if(matched == match_none) {
1389 		icon_name = LookInNameList(Scr->IconNames, tmp_win->name);
1390 		if(icon_name != NULL) {
1391 			*pattern = LookPatternInNameList(Scr->IconNames, tmp_win->name);
1392 			matched = match_list;
1393 		}
1394 	}
1395 
1396 	if(matched == match_none) {
1397 		icon_name = LookInList(Scr->IconNames, tmp_win->name, &tmp_win->class);
1398 		if(icon_name != NULL) {
1399 			*pattern = LookPatternInList(Scr->IconNames, tmp_win->name,
1400 			                             &tmp_win->class);
1401 			matched = match_list;
1402 		}
1403 	}
1404 
1405 	if((image  = GetImage(icon_name, icon->iconc)) != NULL) {
1406 		icon->match  = matched;
1407 		icon->image  = image;
1408 		icon->width  = image->width;
1409 		icon->height = image->height;
1410 		tmp_win->forced = true;
1411 	}
1412 	else {
1413 		icon->match = match_none;
1414 		*pattern = NULL;
1415 	}
1416 
1417 	return image;
1418 }
1419