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