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