icons.c revision 0bbfda8a
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
42static void splitIconRegionEntry(IconEntry *ie, RegGravity grav1,
43                                 RegGravity grav2, int w, int h);
44static void PlaceIcon(TwmWindow *tmp_win, int def_x, int def_y,
45                      int *final_x, int *final_y);
46static IconEntry *FindIconEntry(TwmWindow *tmp_win, IconRegion **irp);
47static IconEntry *prevIconEntry(IconEntry *ie, IconRegion *ir);
48static void mergeEntries(IconEntry *old, IconEntry *ie);
49static void ReshapeIcon(Icon *icon);
50static int roundUp(int v, int multiple);
51static 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 */
76static void
77splitIconRegionEntry(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 */
132name_list **
133AddIconRegion(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 */
213static void
214PlaceIcon(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 */
371static IconEntry *
372FindIconEntry(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 */
393static IconEntry *
394prevIconEntry(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 */
411static void
412mergeEntries(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 */
445void
446CreateIconWindow(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 */
779void
780DeleteIconsList(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 */
811void
812DeleteIcon(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 */
828void
829ReleaseIconImage(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 */
856void
857IconUp(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 */
912void
913IconDown(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 */
964void
965PaintIcon(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 */
1018void
1019ShrinkIconTitle(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
1050void
1051ExpandIconTitle(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 */
1088static void
1089ReshapeIcon(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 */
1125int
1126GetIconOffset(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 */
1157void
1158RedoIcon(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 */
1241void
1242RedoIconName(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 */
1363static int
1364roundUp(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 */
1375static Image *
1376LookupIconNameOrClass(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