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