win_decorations.c revision 0bbfda8a
1/* 2 * Window decoration routines 3 */ 4 5 6#include "ctwm.h" 7 8#include <stdio.h> 9#include <stdlib.h> 10#include <string.h> 11 12#include <X11/extensions/shape.h> 13 14#include "gram.tab.h" 15#include "image.h" 16#include "iconmgr.h" 17#include "screen.h" 18#include "drawing.h" 19#include "occupation.h" 20#include "win_utils.h" 21#include "workspace_manager.h" 22 23#include "win_decorations.h" 24 25 26/* Internal bits */ 27static void ComputeWindowTitleOffsets(TwmWindow *tmp_win, unsigned int width, 28 bool squeeze); 29static void CreateHighlightWindows(TwmWindow *tmp_win); 30static void CreateLowlightWindows(TwmWindow *tmp_win); 31 32typedef enum { TopLeft, TopRight, BottomRight, BottomLeft } CornerType; 33static void Draw3DCorner(Window w, int x, int y, int width, int height, 34 int thick, int bw, ColorPair cp, CornerType type); 35 36 37 38/* 39 * First, the bits for setting up the frame window 40 */ 41 42/*********************************************************************** 43 * 44 * Procedure: 45 * SetupWindow - set window sizes, this was called from either 46 * AddWindow, EndResize, or HandleConfigureNotify. 47 * 48 * Inputs: 49 * tmp_win - the TwmWindow pointer 50 * x - the x coordinate of the upper-left outer corner of the frame 51 * y - the y coordinate of the upper-left outer corner of the frame 52 * w - the width of the frame window w/o border 53 * h - the height of the frame window w/o border 54 * bw - the border width of the frame window or -1 not to change 55 * 56 * Special Considerations: 57 * This routine will check to make sure the window is not completely 58 * off the display, if it is, it'll bring some of it back on. 59 * 60 * The tmp_win->frame_XXX variables should NOT be updated with the 61 * values of x,y,w,h prior to calling this routine, since the new 62 * values are compared against the old to see whether a synthetic 63 * ConfigureNotify event should be sent. (It should be sent if the 64 * window was moved but not resized.) 65 * 66 *********************************************************************** 67 */ 68void 69SetupWindow(TwmWindow *tmp_win, int x, int y, int w, int h, int bw) 70{ 71 SetupFrame(tmp_win, x, y, w, h, bw, false); 72} 73 74void 75SetupFrame(TwmWindow *tmp_win, int x, int y, int w, int h, int bw, 76 bool sendEvent) /* whether or not to force a send */ 77{ 78 bool reShape; 79 80#ifdef DEBUG 81 fprintf(stderr, "SetupFrame: x=%d, y=%d, w=%d, h=%d, bw=%d\n", 82 x, y, w, h, bw); 83#endif 84 85 /* Negative border width is a magic value for "use current frame's" */ 86 if(bw < 0) { 87 bw = tmp_win->frame_bw; 88 } 89 90 91 /* 92 * Set some bounds on the window location, to be sure part of it is 93 * visible. 94 */ 95#define MARGIN 16 /* one "average" cursor width */ 96 97 /* 98 * (x,y) is the top left of the window. Make sure it's not off the 99 * right or bottom of the screen 100 */ 101 if(x >= Scr->rootw) { 102 x = Scr->rootw - MARGIN; 103 } 104 if(y >= Scr->rooth) { 105 y = Scr->rooth - MARGIN; 106 } 107 108 /* 109 * Make sure the bottom right isn't off the left or top of the 110 * screen. 111 * 112 * XXX Should this be 2*bw? 113 */ 114 if((x + w + bw <= 0)) { 115 x = -w + MARGIN; 116 } 117 if((y + h + bw <= 0)) { 118 y = -h + MARGIN; 119 } 120 121#undef MARGIN 122 123 124 /* 125 * Do some magic if the window being Setup'd is an icon manager. The 126 * width of an icon manager is variable, so something changing the 127 * width of the window needs to pass that info down to the control 128 * struct for the iconmgr. The height is solely determined by its 129 * contents though, so the h we're passed actually needs to be 130 * overridden based on how tall the iconmgr itself thinks it should 131 * be. 132 */ 133 if(tmp_win->isiconmgr) { 134 tmp_win->iconmgrp->width = w - (2 * tmp_win->frame_bw3D); 135 h = tmp_win->iconmgrp->height + tmp_win->title_height + 136 (2 * tmp_win->frame_bw3D); 137 } 138 139 /* 140 * If the window is an Occupy window, we have to tell it about its 141 * new size too. 142 */ 143 if(tmp_win->isoccupy) { 144 /* XXX maybe add something like ->iconmgrp above? */ 145 OccupyWindow *occwin = Scr->workSpaceMgr.occupyWindow; 146 147 /* occwin not yet set during startup */ 148 if(occwin != NULL && occwin->twm_win != NULL) { 149 if(tmp_win != occwin->twm_win) { 150 fprintf(stderr, "%s(): %p not the expected Occupy window %p.\n", 151 __func__, tmp_win, occwin->twm_win); 152 } 153 else { 154 ResizeOccupyWindow(tmp_win); 155 } 156 } 157 } 158 159 /* 160 * According to the July 27, 1988 ICCCM draft, we should send a 161 * "synthetic" ConfigureNotify event to the client if the window 162 * was moved but not resized. 163 * 164 * In section "4.2.3 Window Move" in ICCCM 2.0. x-ref 165 * <https://tronche.com/gui/x/icccm/sec-4.html#s-4.2.3> 166 */ 167 if(((x != tmp_win->frame_x || y != tmp_win->frame_y) && 168 (w == tmp_win->frame_width && h == tmp_win->frame_height)) || 169 (bw != tmp_win->frame_bw)) { 170 sendEvent = true; 171 } 172 173 174 /* 175 * Do the necessary sizing on the title window 176 */ 177 { 178 XWindowChanges xwc; 179 unsigned int xwcm; 180 int title_width; 181 182 /* We're gonna be setting the width, even if it's unchanged */ 183 xwcm = CWWidth; 184 185 /* Init: it's as wide as the window, minus borders */ 186 title_width = xwc.width = w - (2 * tmp_win->frame_bw3D); 187 188 /* 189 * We really want to compute the offsets later, after the below 190 * block potentially changes title_width to deal with squeezing. 191 * However, adjusting and setting w->rightx based on the final 192 * 'squeeze' argument to CWTO() is what determines how far things 193 * get squeezed, so we need to call that first so the block can 194 * figure out the proper width to squeeze to. 195 * 196 * In the non-squeezing case, that arg does nothing, and we get 197 * all our values set. In the squeezing, though, all the values 198 * _but_ w->rightx get bogus values, so we'll have to call it 199 * again after we re-figure the width. 200 */ 201 ComputeWindowTitleOffsets(tmp_win, title_width, true); 202 203 reShape = tmp_win->wShaped; 204 205 /* 206 * If the window has SqueezeTitle, the width of the titlebar may 207 * not be the width of the window (the w we're passed), so figure 208 * what it should be. 209 */ 210 if(tmp_win->squeeze_info) { 211 title_width = tmp_win->rightx + Scr->TBInfo.rightoff; 212 if(title_width < xwc.width) { 213 xwc.width = title_width; 214 /* 215 * x-ref above comment. We set squeezed=false here so 216 * w->rightx gets figured right, because we're now 217 * passing the squeezed width. The remaining values are 218 * calculated the same, but will now be set right for the 219 * smaller size. 220 * 221 * See CWTO() comment for possible future cleanup. 222 */ 223 ComputeWindowTitleOffsets(tmp_win, title_width, false); 224 if(tmp_win->frame_height != h || 225 tmp_win->frame_width != w || 226 tmp_win->frame_bw != bw || 227 title_width != tmp_win->title_width) { 228 reShape = true; 229 } 230 } 231 else { 232 if(!tmp_win->wShaped) { 233 reShape = true; 234 } 235 title_width = xwc.width; 236 } 237 } 238 239 /* Write back whatever width we figured */ 240 tmp_win->title_width = title_width; 241 242 /* 243 * If there is a titlebar, set the height. 244 * 245 * title_height=0 is a slightly stupid and nonintuitive way of 246 * flagging "we don't show a titlebar here", but what the heck... 247 */ 248 if(tmp_win->title_height != 0) { 249 tmp_win->title_height = Scr->TitleHeight + bw; 250 } 251 252 /* 253 * If we've got a title window, XConfigure it. 254 * 255 * XXX Hang on, if we don't have a title window, all that work we 256 * just did was bogus, right? And in fact, doesn't accomplish 257 * much of anything anyway. Should this if() be around this 258 * whole block?? 259 */ 260 if(tmp_win->title_w) { 261 /* If border width is changing, update it and the X/Y too */ 262 if(bw != tmp_win->frame_bw) { 263 xwc.border_width = bw; 264 tmp_win->title_x = xwc.x = tmp_win->frame_bw3D - bw; 265 tmp_win->title_y = xwc.y = tmp_win->frame_bw3D - bw; 266 xwcm |= (CWX | CWY | CWBorderWidth); 267 } 268 269 XConfigureWindow(dpy, tmp_win->title_w, xwcm, &xwc); 270 } 271 } 272 273 274 /* 275 * Set a few flags and values for the window as a whole 276 */ 277 /* width/height changed? */ 278 if(tmp_win->attr.width != w) { 279 tmp_win->widthEverChangedByUser = true; 280 } 281 if(tmp_win->attr.height != (h - tmp_win->title_height)) { 282 tmp_win->heightEverChangedByUser = true; 283 } 284 285 /* Write in new values, if the window isn't squeezed away */ 286 if(!tmp_win->squeezed) { 287 tmp_win->attr.width = w - (2 * tmp_win->frame_bw3D); 288 tmp_win->attr.height = h - tmp_win->title_height - (2 * tmp_win->frame_bw3D); 289 } 290 291 /* If it is squeezed, stash values for when we unsqueeze */ 292 if(tmp_win->squeezed) { 293 if(x != tmp_win->frame_x) { 294 tmp_win->actual_frame_x += x - tmp_win->frame_x; 295 } 296 if(y != tmp_win->frame_y) { 297 tmp_win->actual_frame_y += y - tmp_win->frame_y; 298 } 299 } 300 301 302 /* 303 * fix up frame and assign size/location values in tmp_win 304 */ 305 { 306 XWindowChanges frame_wc; 307 unsigned int frame_mask; 308 309 frame_mask = 0; 310 if(bw != tmp_win->frame_bw) { 311 frame_wc.border_width = tmp_win->frame_bw = bw; 312 if(bw == 0) { 313 tmp_win->frame_bw3D = 0; 314 } 315 frame_mask |= CWBorderWidth; 316 } 317 tmp_win->frame_x = x; 318 tmp_win->frame_y = y; 319 if(tmp_win->UnmapByMovingFarAway && !visible(tmp_win)) { 320 frame_wc.x = Scr->rootw + 1; 321 frame_wc.y = Scr->rooth + 1; 322 } 323 else { 324 frame_wc.x = tmp_win->frame_x; 325 frame_wc.y = tmp_win->frame_y; 326 } 327 frame_wc.width = tmp_win->frame_width = w; 328 frame_wc.height = tmp_win->frame_height = h; 329 330 /* Move/resize the frame */ 331 frame_mask |= (CWX | CWY | CWWidth | CWHeight); 332 XConfigureWindow(dpy, tmp_win->frame, frame_mask, &frame_wc); 333 334 /* 335 * Move/resize the "real" window inside the frame. Is it 336 * actually meaningful to "move", since it's always the same 337 * place inside the frame? I'm not sure; this may be necessary 338 * for the client to re-learn its new position in the screen as a 339 * whole? 340 */ 341 XMoveResizeWindow(dpy, tmp_win->w, tmp_win->frame_bw3D, 342 tmp_win->title_height + tmp_win->frame_bw3D, 343 tmp_win->attr.width, tmp_win->attr.height); 344 } 345 346 347 /* 348 * If there's a titlebar, we may have hilight/lolight windows in it 349 * to fix up. 350 * 351 * The sizing/positioning is all wonked up. In particular, the 352 * left-side hi/lolite windows don't work out right because they 353 * extend from the left side (after buttons) until name_x, which is 354 * the start of the title, which means they jam right up against the 355 * text. The math happens to mostly work out OK for UseThreeDTitles, 356 * but it doesn't do well in the opposing case. 357 * 358 * The right side never jam right up against the text, because their 359 * inside edge is highlightxr, figured in ComputeWindowTitleOffsets() 360 * to be name_x + name_width. Their placement is asymmetric with the 361 * above especially in the 2d case, but that may be a case of the R 362 * being wrong, not the L; x-ref discussion in CWTO() about it. 363 * 364 * It's probably necessary to fix both at once to get things coming 365 * out right. Of course, all the issues are invisible unless you're 366 * using TitleJustification center or right, which may be rare 367 * enough that nobody who cares enough has noticed... 368 */ 369 if(tmp_win->title_height != 0) { 370 XWindowChanges xwc; 371 unsigned int xwcm; 372 373 /* 374 * Left-side window bits 375 */ 376 /* Starts from highlightxl, goes to name_x */ 377 xwc.width = (tmp_win->name_x - tmp_win->highlightxl); 378 379 /* Pad for 3d pop-in/out */ 380 if(Scr->use3Dtitles) { 381 xwc.width -= Scr->TitleButtonShadowDepth; 382 } 383 384 /* Move offscreen if it's got no width to display, else place */ 385 if(xwc.width <= 0) { 386 xwc.x = Scr->rootw; /* move offscreen */ 387 xwc.width = 1; 388 } 389 else { 390 xwc.x = tmp_win->highlightxl; 391 } 392 393 /* We're setting the X placement and width */ 394 xwcm = CWX | CWWidth; 395 396 /* Move it/them */ 397 if(tmp_win->hilite_wl) { 398 XConfigureWindow(dpy, tmp_win->hilite_wl, xwcm, &xwc); 399 } 400 if(tmp_win->lolite_wl) { 401 XConfigureWindow(dpy, tmp_win->lolite_wl, xwcm, &xwc); 402 } 403 404 405 /* 406 * Right-side window bits 407 */ 408 /* Full width is from the *lite window start to buttons start */ 409 xwc.width = (tmp_win->rightx - tmp_win->highlightxr); 410 411 /* If there are buttons to our right, cut down for the padding */ 412 if(Scr->TBInfo.nright > 0) { 413 xwc.width -= 2 * Scr->TitlePadding; 414 } 415 416 /* Rest is similar to above for left-side */ 417 if(Scr->use3Dtitles) { 418 xwc.width -= Scr->TitleButtonShadowDepth; 419 } 420 421 /* xwc.width/x different from left, so can't just reuse the values */ 422 if(xwc.width <= 0) { 423 xwc.x = Scr->rootw; 424 xwc.width = 1; 425 } 426 else { 427 xwc.x = tmp_win->highlightxr; 428 } 429 430 xwcm = CWX | CWWidth; // Not strictly necessary, same as above 431 if(tmp_win->hilite_wr) { 432 XConfigureWindow(dpy, tmp_win->hilite_wr, xwcm, &xwc); 433 } 434 if(tmp_win->lolite_wr) { 435 XConfigureWindow(dpy, tmp_win->lolite_wr, xwcm, &xwc); 436 } 437 } 438 439 440 /* Set X Shape stuff if we need to */ 441 if(HasShape && reShape) { 442 SetFrameShape(tmp_win); 443 } 444 445 /* Possible change how it looks in the WorkspaceManager */ 446 WMapSetupWindow(tmp_win, x, y, w, h); 447 448 /* 449 * And send Configure notification to the (real) window if we decided 450 * we have to, telling it about what all has happened. 451 */ 452 if(sendEvent) { 453 XEvent client_event; 454 455 memset(&client_event, 0, sizeof(client_event)); // JIC 456 457 client_event.type = ConfigureNotify; 458 client_event.xconfigure.display = dpy; 459 client_event.xconfigure.event = tmp_win->w; 460 client_event.xconfigure.window = tmp_win->w; 461 client_event.xconfigure.x = (x + tmp_win->frame_bw - tmp_win->old_bw 462 + tmp_win->frame_bw3D); 463 client_event.xconfigure.y = (y + tmp_win->frame_bw + 464 tmp_win->title_height - tmp_win->old_bw 465 + tmp_win->frame_bw3D); 466 client_event.xconfigure.width = tmp_win->attr.width; 467 client_event.xconfigure.height = tmp_win->attr.height; 468 client_event.xconfigure.border_width = tmp_win->old_bw; 469 /* Real ConfigureNotify events say we're above title window, so ... */ 470 /* what if we don't have a title ????? */ 471 client_event.xconfigure.above = tmp_win->frame; 472 client_event.xconfigure.override_redirect = False; 473 XSendEvent(dpy, tmp_win->w, False, StructureNotifyMask, &client_event); 474 } 475} 476 477 478/* 479 * Set X Shape extension bits for the window. This only gets called if 480 * we already know the server supports Shape, and if there's shaping to 481 * do. There's shaping to do if either the real window itself wants 482 * Shape'ing, or if we're SqueezeTitle'ing it. 483 */ 484void 485SetFrameShape(TwmWindow *tmp) 486{ 487 /* 488 * See if the titlebar needs to move (relative to the frame). A 489 * common reason for this is using SqueezeTitle and squeezing the 490 * window as well; when the window is squeezed away, the titlebar is 491 * the only thing displayed, so the frame is coincident with it, and 492 * it starts at (0,0). But when the window is opened, and the 493 * titlebar is narrower than it, it starts at some x offset, so 494 * opening/closing the window squeeze needs to move the position 495 * relative to the frame. 496 */ 497 if(tmp->title_w) { 498 int oldx = tmp->title_x, oldy = tmp->title_y; 499 ComputeTitleLocation(tmp); 500 if(oldx != tmp->title_x || oldy != tmp->title_y) { 501 XMoveWindow(dpy, tmp->title_w, tmp->title_x, tmp->title_y); 502 } 503 } 504 505 /* 506 * The frame consists of the shape of the contents window offset by 507 * title_height or'ed with the shape of title_w (which is always 508 * rectangular). 509 */ 510 if(tmp->wShaped) { 511 /* 512 * need to do general case 513 */ 514 XShapeCombineShape(dpy, tmp->frame, ShapeBounding, 515 tmp->frame_bw3D, tmp->title_height + tmp->frame_bw3D, tmp->w, 516 ShapeBounding, ShapeSet); 517 if(tmp->title_w) { 518 XShapeCombineShape(dpy, tmp->frame, ShapeBounding, 519 tmp->title_x + tmp->frame_bw, 520 tmp->title_y + tmp->frame_bw, 521 tmp->title_w, ShapeBounding, 522 ShapeUnion); 523 } 524 } 525 else { 526 /* 527 * The window itself isn't shaped, so we only need to handle 528 * shaping for what we're doing. 529 */ 530 if(tmp->squeeze_info && !tmp->squeezed) { 531 /* 532 * Titlebar is squeezed and window is shown, so we need to 533 * shape out the missing bits on the side 534 * */ 535 XRectangle newBounding[2]; 536 XRectangle newClip[2]; 537 int fbw2 = 2 * tmp->frame_bw; 538 539 /* 540 * Build the border clipping rectangles; one around title, one 541 * around window. The title_[xy] field already have had frame_bw 542 * subtracted off them so that they line up properly in the frame. 543 * 544 * The frame_width and frame_height do *not* include borders. 545 */ 546 /* border */ 547 newBounding[0].x = tmp->title_x - tmp->frame_bw3D; 548 newBounding[0].y = tmp->title_y - tmp->frame_bw3D; 549 newBounding[0].width = tmp->title_width + fbw2 + 2 * tmp->frame_bw3D; 550 newBounding[0].height = tmp->title_height; 551 newBounding[1].x = -tmp->frame_bw; 552 newBounding[1].y = Scr->TitleHeight; 553 newBounding[1].width = tmp->attr.width + fbw2 + 2 * tmp->frame_bw3D; 554 newBounding[1].height = tmp->attr.height + fbw2 + 2 * tmp->frame_bw3D; 555 XShapeCombineRectangles(dpy, tmp->frame, ShapeBounding, 0, 0, 556 newBounding, 2, ShapeSet, YXBanded); 557 /* insides */ 558 newClip[0].x = tmp->title_x + tmp->frame_bw - tmp->frame_bw3D; 559 newClip[0].y = 0; 560 newClip[0].width = tmp->title_width + 2 * tmp->frame_bw3D; 561 newClip[0].height = Scr->TitleHeight + tmp->frame_bw3D; 562 newClip[1].x = 0; 563 newClip[1].y = tmp->title_height; 564 newClip[1].width = tmp->attr.width + 2 * tmp->frame_bw3D; 565 newClip[1].height = tmp->attr.height + 2 * tmp->frame_bw3D; 566 XShapeCombineRectangles(dpy, tmp->frame, ShapeClip, 0, 0, 567 newClip, 2, ShapeSet, YXBanded); 568 } 569 else { 570 /* 571 * Full width title (or it's squeezed, but the window is also 572 * squeezed away, so it's the full width of what we're 573 * showing anyway), so our simple rectangle covers 574 * everything. 575 */ 576 XShapeCombineMask(dpy, tmp->frame, ShapeBounding, 0, 0, 577 None, ShapeSet); 578 XShapeCombineMask(dpy, tmp->frame, ShapeClip, 0, 0, 579 None, ShapeSet); 580 } 581 } 582} 583 584 585 586/* 587 * Bits related to setting up titlebars. Their subwindows, icons, 588 * highlights, etc. 589 */ 590 591/* 592 * ComputeTitleLocation - calculate the position of the title window; we need 593 * to take the frame_bw into account since we want (0,0) of the title window 594 * to line up with (0,0) of the frame window. 595 * 596 * This sets ->title_[xy], which are the (x,y) of the ->title_w relative 597 * to the frame window. 598 */ 599void 600ComputeTitleLocation(TwmWindow *tmp) 601{ 602 /* y position is always the same */ 603 tmp->title_y = tmp->frame_bw3D - tmp->frame_bw; 604 605 /* x can vary depending on squeezing */ 606 if(tmp->squeeze_info && !tmp->squeezed) { 607 SqueezeInfo *si = tmp->squeeze_info; 608 int basex; 609 int maxwidth = tmp->frame_width; 610 int tw = tmp->title_width + 2 * tmp->frame_bw3D; 611 612 /* figure label base from squeeze info (justification fraction) */ 613 if(si->denom == 0) { /* num is pixel based */ 614 basex = si->num; 615 } 616 else { /* num/denom is fraction */ 617 basex = ((si->num * maxwidth) / si->denom); 618 } 619 if(si->num < 0) { 620 basex += maxwidth; 621 } 622 623 /* adjust for left (nop), center, right justify */ 624 switch(si->justify) { 625 case SIJ_LEFT: 626 break; // nop 627 case SIJ_CENTER: 628 basex -= tw / 2; 629 break; 630 case SIJ_RIGHT: 631 basex -= tw - 1; 632 break; 633 } 634 635 /* Clip */ 636 if(basex > maxwidth - tw) { 637 basex = maxwidth - tw; 638 } 639 if(basex < 0) { 640 basex = 0; 641 } 642 643 tmp->title_x = basex - tmp->frame_bw + tmp->frame_bw3D; 644 } 645 else { 646 tmp->title_x = tmp->frame_bw3D - tmp->frame_bw; 647 } 648} 649 650 651/* 652 * Despite being called "TitlebarButtons", this actually sets up most of 653 * the subwindows inside the titlebar. There are windows for each 654 * button, but also windows for the shifting-color regions on un/focus. 655 */ 656void 657CreateWindowTitlebarButtons(TwmWindow *tmp_win) 658{ 659 unsigned long valuemask; /* mask for create windows */ 660 XSetWindowAttributes attributes; /* attributes for create windows */ 661 int leftx, rightx, y; 662 TitleButton *tb; 663 int nb; 664 665 /* 666 * If there's no titlebar, we don't need any subwindows or anything, 667 * so just make sure it's all empty and return. 668 */ 669 if(tmp_win->title_height == 0) { 670 tmp_win->hilite_wl = (Window) 0; 671 tmp_win->hilite_wr = (Window) 0; 672 tmp_win->lolite_wl = (Window) 0; 673 tmp_win->lolite_wr = (Window) 0; 674 return; 675 } 676 677 678 /* Figure where things go */ 679 ComputeWindowTitleOffsets(tmp_win, tmp_win->attr.width, false); 680 681 leftx = y = Scr->TBInfo.leftx; 682 rightx = tmp_win->rightx; 683 684 /* 685 * Setup default attributes for creating the subwindows for each 686 * button. 687 */ 688 attributes.win_gravity = NorthWestGravity; 689 attributes.background_pixel = tmp_win->title.back; 690 attributes.border_pixel = tmp_win->title.fore; 691 attributes.event_mask = (ButtonPressMask | ButtonReleaseMask | 692 ExposureMask); 693 attributes.cursor = Scr->ButtonCursor; 694 valuemask = (CWWinGravity | CWBackPixel | CWBorderPixel | CWEventMask | 695 CWCursor); 696 697 /* 698 * Initialize the button images/subwindows for the left/right. 699 */ 700 tmp_win->titlebuttons = NULL; 701 nb = Scr->TBInfo.nleft + Scr->TBInfo.nright; 702 if(nb > 0) { 703 /* 704 * XXX Rework this into a proper array, either NULL-terminated or 705 * with a stored size, instead of manually implementing a re-calc 706 * of the size and incrementing pointers every time we want to 707 * walk this. 708 */ 709 tmp_win->titlebuttons = calloc(nb, sizeof(TBWindow)); 710 if(!tmp_win->titlebuttons) { 711 fprintf(stderr, "%s: unable to allocate %d titlebuttons\n", 712 ProgramName, nb); 713 } 714 else { 715 TBWindow *tbw; 716 int boxwidth = (Scr->TBInfo.width + Scr->TBInfo.pad); 717 unsigned int h = (Scr->TBInfo.width - Scr->TBInfo.border * 2); 718 719 for(tb = Scr->TBInfo.head, tbw = tmp_win->titlebuttons; tb; 720 tb = tb->next, tbw++) { 721 int x; 722 if(tb->rightside) { 723 x = rightx; 724 rightx += boxwidth; 725 attributes.win_gravity = NorthEastGravity; 726 } 727 else { 728 x = leftx; 729 leftx += boxwidth; 730 attributes.win_gravity = NorthWestGravity; 731 } 732 tbw->window = XCreateWindow(dpy, tmp_win->title_w, x, y, h, h, 733 Scr->TBInfo.border, 734 0, CopyFromParent, 735 CopyFromParent, 736 valuemask, &attributes); 737 if(Scr->NameDecorations) { 738 XStoreName(dpy, tbw->window, "TB button"); 739 } 740 741 /* 742 * XXX Can we just use tb->image for this instead? I 743 * think we can. The TBInfo.head list is assembled in 744 * calls to CreateTitleButton(), which happen during 745 * config file parsing, and then during 746 * InitTitlebarButtons(), which then goes through and 747 * tb->image = GetImage()'s each of the entries. I don't 748 * believe anything ever gets added to TBInfo.head after 749 * that. And the setting in ITB() could only fail in 750 * cases that would presumably also fail for us here. So 751 * this whole block is redundant? 752 */ 753 tbw->image = GetImage(tb->name, tmp_win->title); 754 if(! tbw->image) { 755 tbw->image = GetImage(TBPM_QUESTION, tmp_win->title); 756 if(! tbw->image) { /* cannot happen (see util.c) */ 757 fprintf(stderr, "%s: unable to add titlebar button \"%s\"\n", 758 ProgramName, tb->name); 759 } 760 } 761 tbw->info = tb; 762 } 763 } 764 } 765 766 /* Windows in the titlebar that show focus */ 767 CreateHighlightWindows(tmp_win); 768 CreateLowlightWindows(tmp_win); 769 770 /* Map all those windows we just created... */ 771 XMapSubwindows(dpy, tmp_win->title_w); 772 773 /* 774 * ...but hide away the hilite's, since they'll only show up when we 775 * give the window focus. And when we do (even if that when is 776 * "right now"), the focus handler will handle mapping them for us. 777 */ 778 if(tmp_win->hilite_wl) { 779 XUnmapWindow(dpy, tmp_win->hilite_wl); 780 } 781 if(tmp_win->hilite_wr) { 782 XUnmapWindow(dpy, tmp_win->hilite_wr); 783 } 784 785 /* 786 * ... but DO show the lolite's, because... XXX this shouldn't be 787 * necessary at all, because they would already have been mapped 788 * during the XMapSubwindows() call above? 789 */ 790 if(tmp_win->lolite_wl) { 791 XMapWindow(dpy, tmp_win->lolite_wl); 792 } 793 if(tmp_win->lolite_wr) { 794 XMapWindow(dpy, tmp_win->lolite_wr); 795 } 796 797 return; 798} 799 800 801/* 802 * Figure out where the window title and the hi/lolite windows go within 803 * the titlebar as a whole. 804 * 805 * For a particular window, called during the AddWindow() process, and 806 * also via Setup{Window,Frame}(). 807 * 808 * This sets w->name_x (x offset for writing the name), w->highlightx[lr] 809 * (x offset for left/right hilite windows), and w->rightx (x offset for 810 * the right buttons), all relative to the title window. 811 * 812 * 813 * The 'squeeze' argument controls whether rightoff should be corrected 814 * for squeezing; when true, it means the passed width doesn't take into 815 * account squeezing. In fact, this adjustment of rightx is what winds 816 * up determining how small the bar gets squeezed to. This relates to 817 * why it's called twice in SetupFrame() to set things up right. 818 * 819 * XXX Should probably either rework how the squeezed width is figured, 820 * or use squeeze to correct everything in here to reduce the scary magic 821 * double-calling. 822 */ 823static void 824ComputeWindowTitleOffsets(TwmWindow *tmp_win, unsigned int width, bool squeeze) 825{ 826 /* 827 * Space available for the window title for calculating name_x. 828 * (window width) - (space reserved l and r for buttons) 829 */ 830 int titlew = width - Scr->TBInfo.titlex - Scr->TBInfo.rightoff; 831 832 /* 833 * First figure where the window name goes, depending on 834 * TitleJustification. If it's on the left/right, and we're using 3d 835 * titles, we have to move it past the TitleShadowDepth, plus a 836 * little extra for visual padding. 837 * 838 * If it's in the middle, we just center on the middle of the 839 * name, without taking into account what that will do if the name is 840 * "too long" for our space, which causes really bad side effects. 841 * The fixing below at least theoretically fixes that, though other 842 * parts of the drawing will still cause Bad Side Effects. 843 */ 844 switch(Scr->TitleJustification) { 845 case TJ_UNDEF: 846 /* Can't happen; fallthru to TJ_LEFT */ 847 fprintf(stderr, "%s(): Unexpected Scr->TitleJustification %d, " 848 "treating as left\n", __func__, Scr->TitleJustification); 849 case TJ_LEFT: 850 tmp_win->name_x = Scr->TBInfo.titlex; 851 if(Scr->use3Dtitles) { 852 tmp_win->name_x += Scr->TitleShadowDepth + 2; 853 } 854 break; 855 case TJ_CENTER: 856 tmp_win->name_x = Scr->TBInfo.titlex + (titlew - tmp_win->name_width) / 2; 857 break; 858 case TJ_RIGHT: 859 /* 860 * XXX Since this pushes the end of the name way over to the 861 * right, there's no room for the right highlight window. 862 * But shrinking down the size of that is how the titlebar 863 * gets squeezed for SqueezeTitle. So if TJ_RIGHT, the 864 * titlebar will never get squeezed. 865 */ 866 tmp_win->name_x = Scr->TBInfo.titlex + titlew - tmp_win->name_width; 867 if(Scr->use3Dtitles) { 868 tmp_win->name_x -= Scr->TitleShadowDepth - 2; 869 } 870 break; 871 } 872 873 /* 874 * Adjust for sanity. Make sure it's always no earlier than the 875 * start of the titlebar (possible especially in the TJ_CENTER case, 876 * but also theoretically if you set a negative ShadowDepth, which 877 * would be stupid and might break other stuff). In the 3d case, 878 * allow twice the ShadowDepth (once for the shadow itself, the 879 * second time for visual padding). 880 */ 881 if(Scr->use3Dtitles) { 882 if(tmp_win->name_x < (Scr->TBInfo.titlex + 2 * Scr->TitleShadowDepth)) { 883 tmp_win->name_x = Scr->TBInfo.titlex + 2 * Scr->TitleShadowDepth; 884 } 885 } 886 else if(tmp_win->name_x < Scr->TBInfo.titlex) { 887 tmp_win->name_x = Scr->TBInfo.titlex; 888 } 889 890 891 /* 892 * Left hilite window starts at the left side, plus some space for a 893 * shadow for 3d effects. That's easy. 894 */ 895 tmp_win->highlightxl = Scr->TBInfo.titlex; 896 if(Scr->use3Dtitles) { 897 tmp_win->highlightxl += Scr->TitleShadowDepth; 898 } 899 900 /* 901 * Right hilite window starts after the window name. 902 * 903 * With ThreeDTitles, add +2 to match the spacing added onto the left 904 * size of name_x above. 905 * 906 * If there's a window to show for the hilite, and there are buttons 907 * for the right side, we move it over even further. This 908 * particularly causes extra blank space between the name and hilite 909 * bar in the !(UseThreeDTitles) case (because TitlePadding is >0 by 910 * default there). I'm not sure why this is here. I seem to get 911 * better results in both 3D/!3D cases by unconditionally doing the 912 * +=2, and never adding the TitlePadding. Perhaps it should be 913 * changed? 914 */ 915 tmp_win->highlightxr = tmp_win->name_x + tmp_win->name_width; 916 if(Scr->use3Dtitles) { 917 tmp_win->highlightxr += 2; 918 } 919 if(tmp_win->hilite_wr || Scr->TBInfo.nright > 0) { 920 tmp_win->highlightxr += Scr->TitlePadding; 921 } 922 923 924 /* 925 * rightoff tells us how much space we need on the right for the 926 * buttons, a little math with the width tells us how far in from the 927 * left to start for that. 928 * 929 * However, if the title bar is squeezed and the window's up, the 930 * titlebar width will be smaller than our 'width' var (which 931 * describes the window as a whole), so we have to make sure it can't 932 * be too far. So start where the right hilite window goes, with a 933 * little space for it to show up, plus misc padding. x-ref comment 934 * at top of function about the weird ways this gets used. 935 */ 936 tmp_win->rightx = width - Scr->TBInfo.rightoff; 937 if(squeeze && tmp_win->squeeze_info && !tmp_win->squeezed) { 938 int rx = (tmp_win->highlightxr 939 + (tmp_win->hilite_wr ? Scr->TBInfo.width * 2 : 0) 940 + (Scr->TBInfo.nright > 0 ? Scr->TitlePadding : 0) 941 + Scr->FramePadding); 942 if(rx < tmp_win->rightx) { 943 tmp_win->rightx = rx; 944 } 945 } 946 return; 947} 948 949 950/* 951 * Creation/destruction of "hi/lolite windows". These are the 952 * portion[s] of the title bar which change color/form to indicate focus. 953 */ 954static void 955CreateHighlightWindows(TwmWindow *tmp_win) 956{ 957 XSetWindowAttributes attributes; /* attributes for create windows */ 958 unsigned long valuemask; 959 int h = (Scr->TitleHeight - 2 * Scr->FramePadding); 960 int y = Scr->FramePadding; 961 962 /* Init */ 963 tmp_win->hilite_wl = (Window) 0; 964 tmp_win->hilite_wr = (Window) 0; 965 966 /* If this window has NoTitleHighlight, don't do nuthin' */ 967 if(! tmp_win->titlehighlight) { 968 return; 969 } 970 971 /* 972 * If a special highlight pixmap was given, use that. Otherwise, 973 * use a nice, even gray pattern. The old horizontal lines look really 974 * awful on interlaced monitors (as well as resembling other looks a 975 * little bit too closely), but can be used by putting 976 * 977 * Pixmaps { TitleHighlight "hline2" } 978 * 979 * (or whatever the horizontal line bitmap is named) in the startup 980 * file. If all else fails, use the foreground color to look like a 981 * solid line. 982 */ 983 if(! tmp_win->HiliteImage) { 984 if(Scr->HighlightPixmapName) { 985 tmp_win->HiliteImage = GetImage(Scr->HighlightPixmapName, tmp_win->title); 986 } 987 } 988 if(! tmp_win->HiliteImage) { 989 /* No defined image, create shaded bars */ 990 Pixmap pm; 991 char *which; 992 993 if(Scr->use3Dtitles && (Scr->Monochrome != COLOR)) { 994 which = "black"; 995 } 996 else { 997 which = "gray"; 998 } 999 1000 pm = mk_blackgray_pixmap(which, tmp_win->title_w, 1001 tmp_win->title.fore, tmp_win->title.back); 1002 1003 tmp_win->HiliteImage = AllocImage(); 1004 tmp_win->HiliteImage->pixmap = pm; 1005 get_blackgray_size(&(tmp_win->HiliteImage->width), 1006 &(tmp_win->HiliteImage->height)); 1007 } 1008 1009 /* Use what we came up with, or fall back to solid pixels */ 1010 if(tmp_win->HiliteImage) { 1011 valuemask = CWBackPixmap; 1012 attributes.background_pixmap = tmp_win->HiliteImage->pixmap; 1013 } 1014 else { 1015 valuemask = CWBackPixel; 1016 attributes.background_pixel = tmp_win->title.fore; 1017 } 1018 1019 /* 1020 * Adjust y-positioning and height for 3d extras. Both are fixed 1021 * from the time the titlebar is created. The X position gets 1022 * changed on any sort of resize etc, and SetupFrame() handles that. 1023 * We just left 'em at X position 0 here, they'll get moved by SF() 1024 * before being displayed anyway. 1025 */ 1026 if(Scr->use3Dtitles) { 1027 y += Scr->TitleShadowDepth; 1028 h -= 2 * Scr->TitleShadowDepth; 1029 } 1030 1031 /* 1032 * There's a left hilite window unless the title is flush left, and 1033 * similarly for the right. 1034 */ 1035#define MKWIN() XCreateWindow(dpy, tmp_win->title_w, 0, y, \ 1036 Scr->TBInfo.width, h, \ 1037 0, Scr->d_depth, CopyFromParent, \ 1038 Scr->d_visual, valuemask, &attributes) 1039 if(Scr->TitleJustification != TJ_LEFT) { 1040 tmp_win->hilite_wl = MKWIN(); 1041 if(Scr->NameDecorations) { 1042 XStoreName(dpy, tmp_win->hilite_wl, "hilite_wl"); 1043 } 1044 } 1045 if(Scr->TitleJustification != TJ_RIGHT) { 1046 tmp_win->hilite_wr = MKWIN(); 1047 if(Scr->NameDecorations) { 1048 XStoreName(dpy, tmp_win->hilite_wr, "hilite_wr"); 1049 } 1050 } 1051#undef MKWIN 1052} 1053 1054 1055/* 1056 * Used in events.c in HandleDestroyNotify(), not here. Called during 1057 * window destruction. Technically, this isn't actually deleting the 1058 * windows; the XDestroyWindow() call it makes will destroy all the 1059 * sub-windows. This is actually just for freeing the image we put in 1060 * the window, if there is one. 1061 */ 1062void 1063DeleteHighlightWindows(TwmWindow *tmp_win) 1064{ 1065 if(tmp_win->HiliteImage) { 1066 if(Scr->HighlightPixmapName) { 1067 /* 1068 * Image obtained from GetImage(): it is in a cache 1069 * so we don't need to free it. There will not be multiple 1070 * copies if the same xpm:foo image is requested again. 1071 */ 1072 } 1073 else { 1074 XFreePixmap(dpy, tmp_win->HiliteImage->pixmap); 1075 free(tmp_win->HiliteImage); 1076 } 1077 tmp_win->HiliteImage = NULL; 1078 } 1079} 1080 1081 1082static void 1083CreateLowlightWindows(TwmWindow *tmp_win) 1084{ 1085 XSetWindowAttributes attributes; /* attributes for create windows */ 1086 unsigned long valuemask; 1087 int h = (Scr->TitleHeight - 2 * Scr->FramePadding); 1088 int y = Scr->FramePadding; 1089 ColorPair cp; 1090 1091 /* Init */ 1092 tmp_win->lolite_wl = (Window) 0; 1093 tmp_win->lolite_wr = (Window) 0; 1094 1095 /* 1096 * We don't even make lolite windows unless UseSunkTitlePixmap is 1097 * set. 1098 */ 1099 if(!Scr->UseSunkTitlePixmap || ! tmp_win->titlehighlight) { 1100 return; 1101 } 1102 1103 /* 1104 * If there's a defined pixmap for highlights, use that with some 1105 * flipped colors. 1106 * */ 1107 if(! tmp_win->LoliteImage) { 1108 if(Scr->HighlightPixmapName) { 1109 cp = tmp_win->title; 1110 cp.shadc = tmp_win->title.shadd; 1111 cp.shadd = tmp_win->title.shadc; 1112 tmp_win->LoliteImage = GetImage(Scr->HighlightPixmapName, cp); 1113 } 1114 } 1115 1116 /* Use our image, or fall back to solid colored bar */ 1117 if(tmp_win->LoliteImage) { 1118 valuemask = CWBackPixmap; 1119 attributes.background_pixmap = tmp_win->LoliteImage->pixmap; 1120 } 1121 else { 1122 valuemask = CWBackPixel; 1123 attributes.background_pixel = tmp_win->title.fore; 1124 } 1125 1126 /* Extra padding for 3d decorations */ 1127 if(Scr->use3Dtitles) { 1128 y += Scr->TitleShadowDepth; 1129 h -= 2 * Scr->TitleShadowDepth; 1130 } 1131 1132 /* 1133 * Bar on the left, unless the title is flush left, and ditto right. 1134 * Same invocation as above for hilites. 1135 */ 1136#define MKWIN() XCreateWindow(dpy, tmp_win->title_w, 0, y, \ 1137 Scr->TBInfo.width, h, \ 1138 0, Scr->d_depth, CopyFromParent, \ 1139 Scr->d_visual, valuemask, &attributes) 1140 if(Scr->TitleJustification != TJ_LEFT) { 1141 tmp_win->lolite_wl = MKWIN(); 1142 if(Scr->NameDecorations) { 1143 XStoreName(dpy, tmp_win->lolite_wl, "lolite_wl"); 1144 } 1145 } 1146 if(Scr->TitleJustification != TJ_RIGHT) { 1147 tmp_win->lolite_wr = MKWIN(); 1148 if(Scr->NameDecorations) { 1149 XStoreName(dpy, tmp_win->lolite_wr, "lolite_wr"); 1150 } 1151 } 1152#undef MKWIN 1153} 1154 1155/* 1156 * There is no DeleteLowlightWindows() as a counterpart to the 1157 * HighlightWindows variant. That func doesn't delete the [sub-]window; 1158 * that happens semi-automatically when the frame window is destroyed. 1159 * It only cleans up the Pixmap if there is one. And the only way the 1160 * Lowlight window can wind up with a pixmap is as a copy of the 1161 * highlight window one, in which case when THAT delete gets called all 1162 * the cleanup is done. 1163 */ 1164 1165 1166 1167 1168/* 1169 * Painting the titlebars. The actual displaying of the stuff that's 1170 * figured or stored above. 1171 */ 1172 1173/* 1174 * Write in the window title 1175 */ 1176void 1177PaintTitle(TwmWindow *tmp_win) 1178{ 1179 /* Draw 3d border around title bits */ 1180 if(Scr->use3Dtitles) { 1181 /* 1182 * From the start of the title bits (after left button), to the 1183 * start of the right buttons, minus padding. 1184 */ 1185 int wid = tmp_win->title_width - Scr->TBInfo.titlex 1186 - Scr->TBInfo.rightoff - Scr->TitlePadding; 1187 ButtonState state = off; 1188 1189 /* 1190 * If SunkFocusWindowTitle, then we "sink in" the whole title 1191 * window when it's focused. Otherwise (!SunkFocus || !focused) 1192 * it's popped up. 1193 */ 1194 if(Scr->SunkFocusWindowTitle && (Scr->Focus == tmp_win) && 1195 (tmp_win->title_height != 0)) { 1196 state = on; 1197 } 1198 1199 Draw3DBorder(tmp_win->title_w, Scr->TBInfo.titlex, 0, wid, 1200 Scr->TitleHeight, Scr->TitleShadowDepth, 1201 tmp_win->title, state, true, false); 1202 } 1203 1204 /* Setup the X graphics context for the drawing */ 1205 FB(tmp_win->title.fore, tmp_win->title.back); 1206 1207 /* And write in the name */ 1208 if(Scr->use3Dtitles) { 1209 int width, mwidth, len; 1210 XRectangle ink_rect; 1211 XRectangle logical_rect; 1212 1213 /* 1214 * Do a bunch of trying to chop the length down until it will fit 1215 * into the space. This doesn't seem to actually accomplish 1216 * anything at the moment, as somehow we wind up with nothing 1217 * visible in the case of a long enough title. 1218 */ 1219 len = strlen(tmp_win->name); 1220 XmbTextExtents(Scr->TitleBarFont.font_set, 1221 tmp_win->name, len, 1222 &ink_rect, &logical_rect); 1223 width = logical_rect.width; 1224 mwidth = tmp_win->title_width - Scr->TBInfo.titlex - 1225 Scr->TBInfo.rightoff - Scr->TitlePadding - 1226 Scr->TitleShadowDepth - 4; 1227 while((len > 0) && (width > mwidth)) { 1228 len--; 1229 XmbTextExtents(Scr->TitleBarFont.font_set, 1230 tmp_win->name, len, 1231 &ink_rect, &logical_rect); 1232 width = logical_rect.width; 1233 } 1234 1235 /* 1236 * Write it in. The Y position is subtly different from the 1237 * !3Dtitles case due to the potential bordering around it. It's 1238 * not quite clear whether it should be. 1239 */ 1240 ((Scr->Monochrome != COLOR) ? XmbDrawImageString : XmbDrawString) 1241 (dpy, tmp_win->title_w, Scr->TitleBarFont.font_set, 1242 Scr->NormalGC, 1243 tmp_win->name_x, 1244 (Scr->TitleHeight - logical_rect.height) / 2 + (- logical_rect.y), 1245 tmp_win->name, len); 1246 } 1247 else { 1248 /* 1249 * XXX The 3Dtitle case above has attempted correction for a lot of 1250 * stuff. It's not entirely clear that it's either needed there, 1251 * or not needed here. It's also not obvious that the magic 1252 * it does to support monochrome isn't applicable here, thought 1253 * it may be a side effect of differences in how the backing 1254 * titlebar is painted. This requires investigation, and either 1255 * fixing the wrong or documentation of why it's right. 1256 */ 1257 XmbDrawString(dpy, tmp_win->title_w, Scr->TitleBarFont.font_set, 1258 Scr->NormalGC, 1259 tmp_win->name_x, Scr->TitleBarFont.y, 1260 tmp_win->name, strlen(tmp_win->name)); 1261 } 1262} 1263 1264 1265/* 1266 * Painting in the buttons on the titlebar 1267 */ 1268/* Iterate and show them all */ 1269void 1270PaintTitleButtons(TwmWindow *tmp_win) 1271{ 1272 int i; 1273 TBWindow *tbw; 1274 int nb = Scr->TBInfo.nleft + Scr->TBInfo.nright; 1275 1276 for(i = 0, tbw = tmp_win->titlebuttons; i < nb; i++, tbw++) { 1277 if(tbw) { 1278 PaintTitleButton(tmp_win, tbw); 1279 } 1280 } 1281} 1282 1283/* Blit the pixmap into the right place */ 1284void 1285PaintTitleButton(TwmWindow *tmp_win, TBWindow *tbw) 1286{ 1287 TitleButton *tb = tbw->info; 1288 1289 XCopyArea(dpy, tbw->image->pixmap, tbw->window, Scr->NormalGC, 1290 tb->srcx, tb->srcy, tb->width, tb->height, 1291 tb->dstx, tb->dsty); 1292 return; 1293} 1294 1295 1296 1297 1298/* 1299 * Stuff for window borders 1300 */ 1301 1302 1303/* 1304 * Currently only used in drawing window decoration borders. Contrast 1305 * with Draw3DBorder() which is used for all sorts of generalized 1306 * drawing. 1307 */ 1308static void 1309Draw3DCorner(Window w, int x, int y, int width, int height, 1310 int thick, int bw, ColorPair cp, CornerType type) 1311{ 1312 XRectangle rects [2]; 1313 1314 switch(type) { 1315 case TopLeft: 1316 Draw3DBorder(w, x, y, width, height, bw, cp, off, true, false); 1317 Draw3DBorder(w, x + thick - bw, y + thick - bw, 1318 width - thick + 2 * bw, height - thick + 2 * bw, 1319 bw, cp, on, true, false); 1320 break; 1321 case TopRight: 1322 Draw3DBorder(w, x, y, width, height, bw, cp, off, true, false); 1323 Draw3DBorder(w, x, y + thick - bw, 1324 width - thick + bw, height - thick, 1325 bw, cp, on, true, false); 1326 break; 1327 case BottomRight: 1328 rects [0].x = x + width - thick; 1329 rects [0].y = y; 1330 rects [0].width = thick; 1331 rects [0].height = height; 1332 rects [1].x = x; 1333 rects [1].y = y + width - thick; 1334 rects [1].width = width - thick; 1335 rects [1].height = thick; 1336 XSetClipRectangles(dpy, Scr->BorderGC, 0, 0, rects, 2, Unsorted); 1337 Draw3DBorder(w, x, y, width, height, bw, cp, off, true, false); 1338 Draw3DBorder(w, x, y, 1339 width - thick + bw, height - thick + bw, 1340 bw, cp, on, true, false); 1341 XSetClipMask(dpy, Scr->BorderGC, None); 1342 break; 1343 case BottomLeft: 1344 rects [0].x = x; 1345 rects [0].y = y; 1346 rects [0].width = thick; 1347 rects [0].height = height; 1348 rects [1].x = x + thick; 1349 rects [1].y = y + height - thick; 1350 rects [1].width = width - thick; 1351 rects [1].height = thick; 1352 XSetClipRectangles(dpy, Scr->BorderGC, 0, 0, rects, 2, Unsorted); 1353 Draw3DBorder(w, x, y, width, height, bw, cp, off, true, false); 1354 Draw3DBorder(w, x + thick - bw, y, 1355 width - thick, height - thick + bw, 1356 bw, cp, on, true, false); 1357 XSetClipMask(dpy, Scr->BorderGC, None); 1358 break; 1359 default: 1360 /* Bad code */ 1361 fprintf(stderr, "Internal error: Invalid Draw3DCorner type %d\n", 1362 type); 1363 break; 1364 } 1365 return; 1366} 1367 1368 1369/* 1370 * Draw the borders onto the frame for a window 1371 */ 1372void 1373PaintBorders(TwmWindow *tmp_win, bool focus) 1374{ 1375 ColorPair cp; 1376 1377 /* Set coloring based on focus/highlight state */ 1378 cp = (focus && tmp_win->highlight) ? tmp_win->borderC : tmp_win->border_tile; 1379 1380 /* 1381 * If there's no height to the title bar (e.g., on NoTitle windows), 1382 * there's nothing much to corner around, so we can just border up 1383 * the whole thing. Since the bordering on the frame is "below" the 1384 * real window, we can just draw one giant square, and then one 1385 * slightly smaller (but still larger than the real-window itself) 1386 * square on top of it, and voila; border! 1387 */ 1388 if(tmp_win->title_height == 0) { 1389 Draw3DBorder(tmp_win->frame, 0, 0, 1390 tmp_win->frame_width, tmp_win->frame_height, 1391 Scr->BorderShadowDepth, cp, off, true, false); 1392 Draw3DBorder(tmp_win->frame, 1393 tmp_win->frame_bw3D - Scr->BorderShadowDepth, 1394 tmp_win->frame_bw3D - Scr->BorderShadowDepth, 1395 tmp_win->frame_width - 2 * tmp_win->frame_bw3D + 2 * Scr->BorderShadowDepth, 1396 tmp_win->frame_height - 2 * tmp_win->frame_bw3D + 2 * Scr->BorderShadowDepth, 1397 Scr->BorderShadowDepth, cp, on, true, false); 1398 return; 1399 } 1400 1401 /* 1402 * Otherwise, we have to draw corners, which means we have to 1403 * individually draw the 4 side borders between them as well. 1404 * 1405 * So start by laying out the 4 corners. 1406 */ 1407 1408 /* How far the corners extend along the sides */ 1409#define CORNERLEN (Scr->TitleHeight + tmp_win->frame_bw3D) 1410 1411 Draw3DCorner(tmp_win->frame, 1412 tmp_win->title_x - tmp_win->frame_bw3D, 1413 0, 1414 CORNERLEN, CORNERLEN, 1415 tmp_win->frame_bw3D, Scr->BorderShadowDepth, cp, TopLeft); 1416 Draw3DCorner(tmp_win->frame, 1417 tmp_win->title_x + tmp_win->title_width - Scr->TitleHeight, 1418 0, 1419 CORNERLEN, CORNERLEN, 1420 tmp_win->frame_bw3D, Scr->BorderShadowDepth, cp, TopRight); 1421 Draw3DCorner(tmp_win->frame, 1422 tmp_win->frame_width - CORNERLEN, 1423 tmp_win->frame_height - CORNERLEN, 1424 CORNERLEN, CORNERLEN, 1425 tmp_win->frame_bw3D, Scr->BorderShadowDepth, cp, BottomRight); 1426 Draw3DCorner(tmp_win->frame, 1427 0, 1428 tmp_win->frame_height - CORNERLEN, 1429 CORNERLEN, CORNERLEN, 1430 tmp_win->frame_bw3D, Scr->BorderShadowDepth, cp, BottomLeft); 1431 1432 1433 /* 1434 * And draw the borders on the 4 sides between the corners 1435 */ 1436 /* Top */ 1437 Draw3DBorder(tmp_win->frame, 1438 tmp_win->title_x + Scr->TitleHeight, 1439 0, 1440 tmp_win->title_width - 2 * Scr->TitleHeight, 1441 tmp_win->frame_bw3D, 1442 Scr->BorderShadowDepth, cp, off, true, false); 1443 /* Bottom */ 1444 Draw3DBorder(tmp_win->frame, 1445 tmp_win->frame_bw3D + Scr->TitleHeight, 1446 tmp_win->frame_height - tmp_win->frame_bw3D, 1447 tmp_win->frame_width - 2 * CORNERLEN, 1448 tmp_win->frame_bw3D, 1449 Scr->BorderShadowDepth, cp, off, true, false); 1450 /* Left */ 1451 Draw3DBorder(tmp_win->frame, 1452 0, 1453 Scr->TitleHeight + tmp_win->frame_bw3D, 1454 tmp_win->frame_bw3D, 1455 tmp_win->frame_height - 2 * CORNERLEN, 1456 Scr->BorderShadowDepth, cp, off, true, false); 1457 /* Right */ 1458 Draw3DBorder(tmp_win->frame, 1459 tmp_win->frame_width - tmp_win->frame_bw3D, 1460 Scr->TitleHeight + tmp_win->frame_bw3D, 1461 tmp_win->frame_bw3D, 1462 tmp_win->frame_height - 2 * CORNERLEN, 1463 Scr->BorderShadowDepth, cp, off, true, false); 1464 1465#undef CORNERLEN 1466 1467 1468 /* 1469 * If SqueezeTitle is set for the window, and the window isn't 1470 * squeezed away (whether because it's focused, or it's just not 1471 * squeezed at all), then we need to draw a "top" border onto the 1472 * bare bits of the window to the left/right of where the titlebar 1473 * is. 1474 */ 1475 if(tmp_win->squeeze_info && !tmp_win->squeezed) { 1476 /* To the left */ 1477 Draw3DBorder(tmp_win->frame, 1478 0, 1479 Scr->TitleHeight, 1480 tmp_win->title_x, 1481 tmp_win->frame_bw3D, 1482 Scr->BorderShadowDepth, cp, off, true, false); 1483 /* And the right */ 1484 Draw3DBorder(tmp_win->frame, 1485 tmp_win->title_x + tmp_win->title_width, 1486 Scr->TitleHeight, 1487 tmp_win->frame_width - tmp_win->title_x - tmp_win->title_width, 1488 tmp_win->frame_bw3D, 1489 Scr->BorderShadowDepth, cp, off, true, false); 1490 } 1491} 1492 1493 1494/* 1495 * Setup the mouse cursor for various locations on the border of a 1496 * window. 1497 * 1498 * Formerly in util.c 1499 */ 1500void 1501SetBorderCursor(TwmWindow *tmp_win, int x, int y) 1502{ 1503 Cursor cursor; 1504 XSetWindowAttributes attr; 1505 int h, fw, fh, wd; 1506 1507 if(!tmp_win) { 1508 return; 1509 } 1510 1511 /* Use the max of these, but since one is always 0 we can add them. */ 1512 wd = tmp_win->frame_bw + tmp_win->frame_bw3D; 1513 h = Scr->TitleHeight + wd; 1514 fw = tmp_win->frame_width; 1515 fh = tmp_win->frame_height; 1516 1517#if defined DEBUG && DEBUG 1518 fprintf(stderr, "wd=%d h=%d, fw=%d fh=%d x=%d y=%d\n", 1519 wd, h, fw, fh, x, y); 1520#endif 1521 1522 /* 1523 * If not using 3D borders: 1524 * 1525 * The left border has negative x coordinates, 1526 * The top border (above the title) has negative y coordinates. 1527 * The title is TitleHeight high, the next wd pixels are border. 1528 * The bottom border has coordinates >= the frame height. 1529 * The right border has coordinates >= the frame width. 1530 * 1531 * If using 3D borders: all coordinates are >= 0, and all coordinates 1532 * are higher by the border width. 1533 * 1534 * Since we only get events when we're actually in the border, we simply 1535 * allow for both cases at the same time. 1536 */ 1537 1538 if((x < -wd) || (y < -wd)) { 1539 cursor = Scr->FrameCursor; 1540 } 1541 else if(x < h) { 1542 if(y < h) { 1543 cursor = TopLeftCursor; 1544 } 1545 else if(y >= fh - h) { 1546 cursor = BottomLeftCursor; 1547 } 1548 else { 1549 cursor = LeftCursor; 1550 } 1551 } 1552 else if(x >= fw - h) { 1553 if(y < h) { 1554 cursor = TopRightCursor; 1555 } 1556 else if(y >= fh - h) { 1557 cursor = BottomRightCursor; 1558 } 1559 else { 1560 cursor = RightCursor; 1561 } 1562 } 1563 else if(y < h) { /* also include title bar in top border area */ 1564 cursor = TopCursor; 1565 } 1566 else if(y >= fh - h) { 1567 cursor = BottomCursor; 1568 } 1569 else { 1570 cursor = Scr->FrameCursor; 1571 } 1572 attr.cursor = cursor; 1573 XChangeWindowAttributes(dpy, tmp_win->frame, CWCursor, &attr); 1574 tmp_win->curcurs = cursor; 1575} 1576 1577 1578 1579/* 1580 * End of code. Random doc/notes follow. 1581 */ 1582 1583 1584/* 1585 * n.b.: Old doc about squeezed title. Not recently vetted. I'm pretty 1586 * sure it's definitely wrong for the 3D-borders case at the least. 1587 * Should be updated and migrated into developer docs at some point. 1588 * 1589 * Squeezed Title: 1590 * 1591 * tmp->title_x 1592 * 0 | 1593 * tmp->title_y ........+--------------+......... -+,- tmp->frame_bw 1594 * 0 : ......| +----------+ |....... : -++ 1595 * : : | | | | : : ||-Scr->TitleHeight 1596 * : : | | | | : : || 1597 * +-------+ +----------+ +--------+ -+|-tmp->title_height 1598 * | +---------------------------+ | --+ 1599 * | | | | 1600 * | | | | 1601 * | | | | 1602 * | | | | 1603 * | | | | 1604 * | +---------------------------+ | 1605 * +-------------------------------+ 1606 * 1607 * 1608 * Unsqueezed Title: 1609 * 1610 * tmp->title_x 1611 * | 0 1612 * tmp->title_y +-------------------------------+ -+,tmp->frame_bw 1613 * 0 | +---------------------------+ | -+' 1614 * | | | | |-Scr->TitleHeight 1615 * | | | | | 1616 * + +---------------------------+ + -+ 1617 * |-+---------------------------+-| 1618 * | | | | 1619 * | | | | 1620 * | | | | 1621 * | | | | 1622 * | | | | 1623 * | +---------------------------+ | 1624 * +-------------------------------+ 1625 * 1626 * 1627 * 1628 * Dimensions and Positions: 1629 * 1630 * frame orgin (0, 0) 1631 * frame upper left border (-tmp->frame_bw, -tmp->frame_bw) 1632 * frame size w/o border tmp->frame_width , tmp->frame_height 1633 * frame/title border width tmp->frame_bw 1634 * extra title height w/o bdr tmp->title_height = TitleHeight + frame_bw 1635 * title window height Scr->TitleHeight 1636 * title origin w/o border (tmp->title_x, tmp->title_y) 1637 * client origin (0, Scr->TitleHeight + tmp->frame_bw) 1638 * client size tmp->attr.width , tmp->attr.height 1639 * 1640 * When shaping, need to remember that the width and height of rectangles 1641 * are really deltax and deltay to lower right handle corner, so they need 1642 * to have -1 subtracted from would normally be the actual extents. 1643 */ 1644