ctwm_main.c revision b18c2d1e
1/* 2 * Copyright 1988 by Evans & Sutherland Computer Corporation, 3 * Salt Lake City, Utah 4 * Portions Copyright 1989 by the Massachusetts Institute of Technology 5 * Cambridge, Massachusetts 6 * 7 * $XConsortium: twm.c,v 1.124 91/05/08 11:01:54 dave Exp $ 8 * 9 * twm - "Tom's Window Manager" 10 * 11 * 27-Oct-87 Thomas E. LaStrange File created 12 * 10-Oct-90 David M. Sternlicht Storing saved colors on root 13 * 14 * Copyright 1992 Claude Lecommandeur. 15 * 16 * Do the necessary modification to be integrated in ctwm. 17 * Can no longer be used for the standard twm. 18 * 19 * 22-April-92 Claude Lecommandeur. 20 */ 21 22#include "ctwm.h" 23 24#include <stdio.h> 25#include <stdlib.h> 26#include <unistd.h> 27#include <locale.h> 28 29#include <fcntl.h> 30#include <X11/Xatom.h> 31#include <X11/extensions/shape.h> 32 33 34#include "ctwm_atoms.h" 35#include "ctwm_main.h" 36#include "ctwm_takeover.h" 37#include "clargs.h" 38#include "add_window.h" 39#include "gc.h" 40#include "parse.h" 41#include "version.h" 42#include "colormaps.h" 43#include "events.h" 44#include "util.h" 45#include "mask_screen.h" 46#include "animate.h" 47#include "screen.h" 48#include "icons.h" 49#include "iconmgr.h" 50#include "list.h" 51#ifdef SESSION 52#include "session.h" 53#endif 54#include "occupation.h" 55#include "otp.h" 56#include "cursor.h" 57#ifdef WINBOX 58#include "windowbox.h" 59#endif 60#ifdef CAPTIVE 61#include "captive.h" 62#endif 63#ifdef XRANDR 64#include "xrandr.h" 65#endif 66#include "r_area.h" 67#include "r_area_list.h" 68#include "r_layout.h" 69#include "signals.h" 70#include "vscreen.h" 71#include "win_decorations_init.h" 72#include "win_ops.h" 73#include "win_regions.h" 74#include "win_utils.h" 75#include "workspace_manager.h" 76#ifdef SOUNDS 77# include "sound.h" 78#endif 79 80#include "gram.tab.h" 81 82 83XtAppContext appContext; /* Xt application context */ 84Display *dpy; /* which display are we talking to */ 85Window ResizeWindow; /* the window we are resizing */ 86 87Atom XCTWMAtom[NUM_CTWM_XATOMS]; ///< Our various common atoms 88 89int NumScreens; /* number of screens in ScreenList */ 90bool HasShape; /* server supports shape extension? */ 91int ShapeEventBase, ShapeErrorBase; 92ScreenInfo **ScreenList; /* structures for each screen */ 93ScreenInfo *Scr = NULL; /* the cur and prev screens */ 94int PreviousScreen; /* last screen that we were on */ 95static bool cfgerrs = false; ///< Whether there were config parsing errors 96 97#ifdef CAPTIVE 98static Window CreateCaptiveRootWindow(int x, int y, 99 unsigned int width, unsigned int height); 100#endif 101ScreenInfo *InitScreenInfo(int scrnum, Window croot, int crootx, int crooty, 102 unsigned int crootw, unsigned int crooth); 103static bool MappedNotOverride(Window w); 104 105Cursor UpperLeftCursor; 106Cursor TopRightCursor, 107 TopLeftCursor, 108 BottomRightCursor, 109 BottomLeftCursor, 110 LeftCursor, 111 RightCursor, 112 TopCursor, 113 BottomCursor; 114 115Cursor RightButt; 116Cursor MiddleButt; 117Cursor LeftButt; 118 119XContext TwmContext; /* context for twm windows */ 120XContext MenuContext; /* context for all menu windows */ 121XContext ScreenContext; /* context to get screen data */ 122XContext ColormapContext; /* context for colormap operations */ 123 124XClassHint NoClass; /* for applications with no class */ 125 126XGCValues Gcv; 127 128char *Home; /* the HOME environment variable */ 129int HomeLen; /* length of Home */ 130 131bool HandlingEvents = false; /* are we handling events yet? */ 132 133/* 134 * Various junk vars for xlib calls. Many calls have to get passed these 135 * pointers to return values into, but in a lot of cases we don't care 136 * about some/all of them, and since xlib blindly derefs and stores into 137 * them, we can't just pass NULL for the ones we don't care about. So we 138 * make this set of globals to use as standin. These should never be 139 * used or read in our own code; use real vars for the values we DO use 140 * from the calls. 141 */ 142Window JunkRoot, JunkChild; 143int JunkX, JunkY; 144unsigned int JunkWidth, JunkHeight, JunkBW, JunkDepth, JunkMask; 145 146char *ProgramName; 147size_t ProgramNameLen; 148int Argc; 149char **Argv; 150 151bool RestartPreviousState = true; /* try to restart in previous state */ 152 153 154/// Magic flag for tests. Nothing else should touch this! 155bool ctwm_test = false; 156 157/// Magic callback for tests. This will trigger right after config file 158/// parsing if it's set, and then exit. Nothing else should ever touch 159/// this! 160int (*ctwm_test_postparse)(void) = NULL; 161 162 163 164 165/** 166 * Start up ctwm. This is effectively main(), just wrapped for various 167 * unimportant reasons. 168 */ 169int 170ctwm_main(int argc, char *argv[]) 171{ 172 int numManaged, firstscrn, lastscrn; 173 bool FirstScreen; 174 bool takeover = true; 175 bool nodpyok = false; 176 177 setlocale(LC_ALL, ""); 178 179 ProgramName = argv[0]; 180 ProgramNameLen = strlen(ProgramName); 181 Argc = argc; 182 Argv = argv; 183 184 185 /* 186 * Run consistency check. This is mostly to keep devs from 187 * accidental screwups; if a user ever sees anything from this, 188 * something went very very wrong. 189 */ 190 chk_keytable_order(); 191 192 /* 193 * Parse-out command line args, and check the results. 194 */ 195 clargs_parse(argc, argv); 196 clargs_check(); 197 /* If we get this far, it was all good */ 198 199 /* Some clargs mean we're not actually trying to take over the screen */ 200 if(CLarg.cfgchk) { 201 takeover = false; 202 } 203#ifdef CAPTIVE 204 if(CLarg.is_captive) { 205 takeover = false; 206 } 207#endif 208 209 /* And some mean we actually don't care if we lack an X server */ 210 if(CLarg.cfgchk) { 211 nodpyok = true; 212 } 213 214 /* Support for tests: be ready to fake everything */ 215 if(ctwm_test) { 216 takeover = false; 217 nodpyok = true; 218 } 219 220 221 /* 222 * Hook up signal handlers 223 */ 224 setup_signal_handlers(); 225 226 227 // Various bits of code care about $HOME 228 Home = getenv("HOME"); 229 if(Home == NULL) { 230 Home = "./"; 231 } 232 HomeLen = strlen(Home); 233 234 235 // XXX This is only used in AddWindow(), and is probably bogus to 236 // have globally.... 237 NoClass.res_name = NoName; 238 NoClass.res_class = NoName; 239 240 241 /* 242 * Initialize our X connection and state bits. 243 */ 244 { 245 int zero = 0; // Fakey 246 247 XtToolkitInitialize(); 248 appContext = XtCreateApplicationContext(); 249 250 // Tests don't talk to a real X server. 251 // XXX This needs revisiting if we ever get one that _does_. 252 // We'll have to add another flag... 253 if(!ctwm_test) { 254 // Connect 255 dpy = XtOpenDisplay(appContext, CLarg.display_name, "twm", "twm", 256 NULL, 0, &zero, NULL); 257 } 258 259 // Failed? Usually a problem, but somethings we allow faking... 260 if(!dpy && !nodpyok) { 261 fprintf(stderr, "%s: unable to open display \"%s\"\n", 262 ProgramName, XDisplayName(CLarg.display_name)); 263 exit(1); 264 } 265 266 if(dpy && fcntl(ConnectionNumber(dpy), F_SETFD, FD_CLOEXEC) == -1) { 267 fprintf(stderr, 268 "%s: unable to mark display connection as close-on-exec\n", 269 ProgramName); 270 exit(1); 271 } 272 273 if(!dpy && !ctwm_test) { 274 // At least warn, except for tests 275 fprintf(stderr, "%s: Can't connect to X server, proceeding anyway...\n", 276 ProgramName); 277 } 278 } 279 280 281#ifdef SESSION 282 // Load session stuff 283 if(CLarg.restore_filename) { 284 ReadWinConfigFile(CLarg.restore_filename); 285 } 286#endif 287 288 289 if(dpy) { 290 // Load up info about X extensions 291 HasShape = XShapeQueryExtension(dpy, &ShapeEventBase, &ShapeErrorBase); 292 293 // Allocate contexts/atoms/etc we use 294 TwmContext = XUniqueContext(); 295 MenuContext = XUniqueContext(); 296 ScreenContext = XUniqueContext(); 297 ColormapContext = XUniqueContext(); 298 InitWorkSpaceManagerContext(); 299 300 // Load up our standard set of atoms 301 XInternAtoms(dpy, XCTWMAtomNames, NUM_CTWM_XATOMS, False, XCTWMAtom); 302 303 NumScreens = ScreenCount(dpy); 304 PreviousScreen = DefaultScreen(dpy); 305 } 306 else { 307 NumScreens = 1; 308 PreviousScreen = 0; 309 } 310 311 // Allocate/define common cursors 312 NewFontCursor(&TopLeftCursor, "top_left_corner"); 313 NewFontCursor(&TopRightCursor, "top_right_corner"); 314 NewFontCursor(&BottomLeftCursor, "bottom_left_corner"); 315 NewFontCursor(&BottomRightCursor, "bottom_right_corner"); 316 NewFontCursor(&LeftCursor, "left_side"); 317 NewFontCursor(&RightCursor, "right_side"); 318 NewFontCursor(&TopCursor, "top_side"); 319 NewFontCursor(&BottomCursor, "bottom_side"); 320 321 NewFontCursor(&UpperLeftCursor, "top_left_corner"); 322 NewFontCursor(&RightButt, "rightbutton"); 323 NewFontCursor(&LeftButt, "leftbutton"); 324 NewFontCursor(&MiddleButt, "middlebutton"); 325 326 327 // Prep up the per-screen global info 328 if(CLarg.MultiScreen) { 329 firstscrn = 0; 330 lastscrn = NumScreens - 1; 331 } 332 else if(!dpy) { 333 firstscrn = lastscrn = 0; 334 } 335 else { 336 firstscrn = lastscrn = DefaultScreen(dpy); 337 } 338 339 // For simplicity, pre-allocate NumScreens ScreenInfo struct pointers 340 ScreenList = calloc(NumScreens, sizeof(ScreenInfo *)); 341 if(ScreenList == NULL) { 342 fprintf(stderr, "%s: Unable to allocate memory for screen list, exiting.\n", 343 ProgramName); 344 exit(1); 345 } 346 347 348 // Do a little early initialization 349#ifdef EWMH 350 if(dpy) { 351 EwmhInit(); 352 } 353#endif /* EWMH */ 354#ifdef SOUNDS 355 // Needs init'ing before we get to config parsing 356 sound_init(); 357#endif 358 InitEvents(); 359 360 361 362 // Start looping over the screens 363 numManaged = 0; 364 FirstScreen = true; 365 for(int scrnum = firstscrn ; scrnum <= lastscrn; scrnum++) { 366 Window croot; 367 int crootx, crooty; 368 unsigned int crootw, crooth; 369 bool screenmasked; 370 char *welcomefile; 371 372 373 /* 374 * First, setup the root window for the screen. 375 */ 376 if(0) { 377 // Dummy 378 } 379#ifdef CAPTIVE 380 else if(CLarg.is_captive) { 381 // Captive ctwm. We make a fake root. 382 XWindowAttributes wa; 383 if(CLarg.capwin && XGetWindowAttributes(dpy, CLarg.capwin, &wa)) { 384 Window junk; 385 croot = CLarg.capwin; 386 crootw = wa.width; 387 crooth = wa.height; 388 XTranslateCoordinates(dpy, CLarg.capwin, wa.root, 0, 0, &crootx, &crooty, 389 &junk); 390 } 391 else { 392 // Fake up default size. Probably ideally should be 393 // configurable, but even more ideally we wouldn't have 394 // captive... 395 crootx = crooty = 100; 396 crootw = 1280; 397 crooth = 768; 398 croot = CreateCaptiveRootWindow(crootx, crooty, crootw, crooth); 399 } 400 } 401#endif 402 else { 403 // Normal; get the real display's root. 404 crootx = 0; 405 crooty = 0; 406 407 if(dpy) { 408 croot = RootWindow(dpy, scrnum); 409 crootw = DisplayWidth(dpy, scrnum); 410 crooth = DisplayHeight(dpy, scrnum); 411 } 412 else { 413 croot = None; 414 crootw = 1280; 415 crooth = 768; 416 } 417 } 418 419 420 421 /* 422 * Create ScreenInfo for this Screen, and populate various 423 * default/initial config. 424 */ 425 Scr = ScreenList[scrnum] = InitScreenInfo(scrnum, croot, 426 crootx, crooty, crootw, crooth); 427 if(Scr == NULL) { 428 fprintf(stderr, 429 "%s: unable to allocate memory for ScreenInfo structure" 430 " for screen %d.\n", 431 ProgramName, scrnum); 432 continue; 433 } 434 435 // Other misc adjustments to default config. 436 Scr->ShowWelcomeWindow = CLarg.ShowWelcomeWindow; 437 438 439 440 /* 441 * Figure out the layout of our various monitors if RANDR is 442 * around and can tell us. 443 */ 444#ifdef XRANDR 445 if(dpy) { 446 Scr->Layout = XrandrNewLayout(dpy, Scr->XineramaRoot); 447 } 448#endif 449 if(Scr->Layout == NULL) { 450 // No RANDR, so as far as we know, the layout is just one 451 // monitor with our full size. 452 RArea *fs; 453 RAreaList *fsl; 454 455 fs = RAreaNewStatic(Scr->rootx, Scr->rooty, Scr->rootw, Scr->rooth); 456 fsl = RAreaListNew(1, fs, NULL); 457 Scr->Layout = RLayoutNew(fsl); 458 } 459#ifdef DEBUG 460 fprintf(stderr, "Layout: "); 461 RLayoutPrint(Scr->Layout); 462#endif 463 if(RLayoutNumMonitors(Scr->Layout) < 1) { 464 fprintf(stderr, "Error: No monitors found on screen %d!\n", scrnum); 465 continue; 466 } 467 468 469 470 // Now we can stash some info about the screen 471 if(dpy) { 472 Scr->d_depth = DefaultDepth(dpy, scrnum); 473 Scr->d_visual = DefaultVisual(dpy, scrnum); 474 Scr->RealRoot = RootWindow(dpy, scrnum); 475 { 476 // Stash these for m4 477 Screen *tscr = ScreenOfDisplay(dpy, scrnum); 478 Scr->mm_w = tscr->mwidth; 479 Scr->mm_h = tscr->mheight; 480 } 481 } 482 else { 483 // Standin; fake the values we need in m4 parsing 484 Scr->d_visual = calloc(1, sizeof(Visual)); 485 Scr->d_visual->bits_per_rgb = 8; 486 Scr->d_visual->class = TrueColor; 487 } 488 489 490 // Now that we have d_depth... 491 Scr->XORvalue = (((unsigned long) 1) << Scr->d_depth) - 1; 492 493#ifdef CAPTIVE 494 // Init captive bits. We stick this name into m4 props, so do it 495 // before config processing. 496 if(CLarg.is_captive) { 497 Scr->CaptiveRoot = croot; 498 Scr->captivename = AddToCaptiveList(CLarg.captivename); 499 if(Scr->captivename) { 500 XmbSetWMProperties(dpy, croot, 501 Scr->captivename, Scr->captivename, 502 NULL, 0, NULL, NULL, NULL); 503 } 504 } 505#endif 506 507 508 // Init some colormap bits. We need this before we get into the 509 // config parsing, since various things in there poke into 510 // colormaps. 511 { 512 // 1 on the root 513 Scr->RootColormaps.number_cwins = 1; 514 Scr->RootColormaps.cwins = malloc(sizeof(ColormapWindow *)); 515 Scr->RootColormaps.cwins[0] = CreateColormapWindow(Scr->Root, true, 516 false); 517 Scr->RootColormaps.cwins[0]->visibility = VisibilityPartiallyObscured; 518 519 // Initialize storage for all maps the Screen can hold 520 Scr->cmapInfo.cmaps = NULL; 521 if(dpy) { 522 Scr->cmapInfo.maxCmaps = MaxCmapsOfScreen(ScreenOfDisplay(dpy, 523 Scr->screen)); 524 } 525 Scr->cmapInfo.root_pushes = 0; 526 InstallColormaps(0, &Scr->RootColormaps); 527 528 // Setup which we're using 529 Scr->StdCmapInfo.head = Scr->StdCmapInfo.tail 530 = Scr->StdCmapInfo.mru = NULL; 531 Scr->StdCmapInfo.mruindex = 0; 532 if(dpy) { 533 LocateStandardColormaps(); 534 } 535 } 536 537 538 // Are we monochrome? Or do we care this millennium? 539 if(CLarg.Monochrome || (dpy && DisplayCells(dpy, scrnum) < 3)) { 540 Scr->Monochrome = MONOCHROME; 541 } 542 else { 543 Scr->Monochrome = COLOR; 544 } 545 546 547 // With the colormap/monochrome bits set, we can setup our 548 // default color bits. 549 GetColor(Scr->Monochrome, &(Scr->Black), "black"); 550 GetColor(Scr->Monochrome, &(Scr->White), "white"); 551 552 Scr->MenuShadowColor = Scr->Black; 553 Scr->IconBorderColor = Scr->Black; 554 Scr->IconManagerHighlight = Scr->Black; 555 556#define SETFB(fld) Scr->fld.fore = Scr->Black; Scr->fld.back = Scr->White; 557 SETFB(DefaultC) 558 SETFB(BorderColorC) 559 SETFB(BorderTileC) 560 SETFB(TitleC) 561 SETFB(MenuC) 562 SETFB(MenuTitleC) 563 SETFB(IconC) 564 SETFB(IconManagerC) 565 SETFB(workSpaceMgr.windowcp) 566 SETFB(workSpaceMgr.curColors) 567 SETFB(workSpaceMgr.defColors) 568#undef SETFB 569 570 571 // The first time around, we focus onto the root [of the first 572 // Screen]. Maybe we should revisit this... 573 if(dpy && FirstScreen) { 574 // XXX This func also involves a lot of stuff that isn't 575 // setup yet, and probably only works by accident. Maybe we 576 // should just manually extract out the couple bits we 577 // actually want to run? 578 SetFocus(NULL, CurrentTime); 579 FirstScreen = false; 580 } 581 582 // Create default icon manager memory bits (in the first 583 // workspace) 584 AllocateIconManager("TWM", "Icons", "", 1); 585 586 587 /* 588 * Mask over the screen with our welcome window stuff if we were 589 * asked to on the command line/environment; too early to get 590 * info from config file about it. 591 */ 592 screenmasked = false; 593 if(dpy && takeover && Scr->ShowWelcomeWindow 594 && (welcomefile = getenv("CTWM_WELCOME_FILE"))) { 595 screenmasked = true; 596 MaskScreen(welcomefile); 597 } 598 599 600 601 /* 602 * Load up config file 603 */ 604 { 605 bool ok = LoadTwmrc(CLarg.InitFile); 606 607 // cfgchk just displays whether there are errors, then moves 608 // on. 609 if(CLarg.cfgchk) { 610 if(ok) { 611 fprintf(stderr, "%d: No errors found\n", scrnum); 612 } 613 else { 614 fprintf(stderr, "%d: Errors found\n", scrnum); 615 cfgerrs = true; 616 } 617 continue; 618 } 619 620 // In non-config-check mode, we historically proceed even if 621 // there were errors, so keep doing that... 622 } 623 624 625 // For testing, it's useful to do all that initial setup up 626 // through parsing, and then inspect Scr and the like. 627 // Long-term, IWBNI we had a better way to do all the necessary 628 // initialization and then call the parse ourselves at that 629 // level. But for now, provide a callback func that can pass 630 // control back to the test code, then just exits. 631 if(ctwm_test_postparse != NULL) { 632 exit(ctwm_test_postparse()); 633 } 634 635 636 637 /* 638 * Since we've loaded the config, go ahead and take over the 639 * screen. 640 */ 641 if(takeover) { 642 if(takeover_screen(Scr) != true) { 643 // Well, move on to the next one, maybe we'll get it... 644 if(screenmasked) { 645 UnmaskScreen(); 646 } 647 continue; 648 } 649 650 // Well, we got this one 651 numManaged++; 652 } 653 654 // If the config wants us to show the splash screen and we 655 // haven't already, do it now. 656 if(Scr->ShowWelcomeWindow && !screenmasked) { 657 MaskScreen(NULL); 658 } 659 660 661 662 /* 663 * Do various setup based on the results from the config file. 664 */ 665 666 // Few simple var defaults 667 if(Scr->ClickToFocus) { 668 Scr->FocusRoot = false; 669 Scr->TitleFocus = false; 670 } 671 672 if(Scr->use3Dborders) { 673 Scr->ClientBorderWidth = false; 674 } 675 676 677 // Now that we know what Border's there may be, create our 678 // BorderedLayout. 679 Scr->BorderedLayout = RLayoutCopyCropped(Scr->Layout, 680 Scr->BorderLeft, Scr->BorderRight, 681 Scr->BorderTop, Scr->BorderBottom); 682 if(Scr->BorderedLayout == NULL) { 683 Scr->BorderedLayout = Scr->Layout; // nothing to crop 684 } 685 else if(Scr->BorderedLayout->monitors->len == 0) { 686 fprintf(stderr, 687 "Borders too large! correct BorderLeft, BorderRight, BorderTop and/or BorderBottom parameters\n"); 688 exit(1); 689 } 690#ifdef DEBUG 691 fprintf(stderr, "Bordered: "); 692 RLayoutPrint(Scr->BorderedLayout); 693#endif 694 695 696 /* 697 * Setup stuff relating to VirtualScreens. If something to do 698 * with it is set in the config, this all implements stuff needed 699 * for that. If not, InitVirtualScreens() creates a single one 700 * mirroring our real root. 701 */ 702 InitVirtualScreens(Scr); 703#ifdef VSCREEN 704#ifdef EWMH 705 EwmhInitVirtualRoots(Scr); 706#endif /* EWMH */ 707#endif // vscreen 708 709 // Setup WSM[s] (per-vscreen). This also sets up the about the 710 // workspaces for each vscreen and which is currently displayed. 711 ConfigureWorkSpaceManager(Scr); 712 713 714 /* 715 * Various decoration default overrides for 3d/2d. Values that 716 * [presumtively] look "nice" on 75/100dpi displays. -100 is a 717 * sentinel value we set before the config file parsing; since 718 * these defaults differ for 3d vs not, we can't just set them as 719 * default before the parse. 720 */ 721#define SETDEF(fld, num) if(Scr->fld == -100) { Scr->fld = num; } 722 if(Scr->use3Dtitles) { 723 SETDEF(FramePadding, 0); 724 SETDEF(TitlePadding, 0); 725 SETDEF(ButtonIndent, 0); 726 SETDEF(TBInfo.border, 0); 727 } 728 else { 729 SETDEF(FramePadding, 2); 730 SETDEF(TitlePadding, 8); 731 SETDEF(ButtonIndent, 1); 732 SETDEF(TBInfo.border, 1); 733 } 734#undef SETDEF 735 736 // These values are meaningless in !3d cases, so always zero them 737 // out. 738 if(! Scr->use3Dtitles) { 739 Scr->TitleShadowDepth = 0; 740 Scr->TitleButtonShadowDepth = 0; 741 } 742 if(! Scr->use3Dborders) { 743 Scr->BorderShadowDepth = 0; 744 } 745 if(! Scr->use3Dmenus) { 746 Scr->MenuShadowDepth = 0; 747 } 748 if(! Scr->use3Diconmanagers) { 749 Scr->IconManagerShadowDepth = 0; 750 } 751 if(! Scr->use3Dborders) { 752 Scr->ThreeDBorderWidth = 0; 753 } 754 755 // Setup colors stuff 756 if(!Scr->BeNiceToColormap) { 757 // Default pair 758 GetShadeColors(&Scr->DefaultC); 759 760 // Various conditionally 3d bits 761 if(Scr->use3Dtitles) { 762 GetShadeColors(&Scr->TitleC); 763 } 764 if(Scr->use3Dmenus) { 765 GetShadeColors(&Scr->MenuC); 766 } 767 if(Scr->use3Dmenus) { 768 GetShadeColors(&Scr->MenuTitleC); 769 } 770 if(Scr->use3Dborders) { 771 GetShadeColors(&Scr->BorderColorC); 772 } 773 } 774 775 // Defaults for IconRegion bits that aren't set. 776 for(IconRegion *ir = Scr->FirstRegion; ir; ir = ir->next) { 777 if(ir->TitleJustification == TJ_UNDEF) { 778 ir->TitleJustification = Scr->IconJustification; 779 } 780 if(ir->Justification == IRJ_UNDEF) { 781 ir->Justification = Scr->IconRegionJustification; 782 } 783 if(ir->Alignement == IRA_UNDEF) { 784 ir->Alignement = Scr->IconRegionAlignement; 785 } 786 } 787 788 // Put the results of SaveColor{} into _MIT_PRIORITY_COLORS. 789 assign_var_savecolor(); 790 791 // Setup cursor values that weren't give in the config 792#define DEFCURSOR(name, val) if(!Scr->name) NewFontCursor(&Scr->name, val) 793 DEFCURSOR(FrameCursor, "top_left_arrow"); 794 DEFCURSOR(TitleCursor, "top_left_arrow"); 795 DEFCURSOR(IconCursor, "top_left_arrow"); 796 DEFCURSOR(IconMgrCursor, "top_left_arrow"); 797 DEFCURSOR(MoveCursor, "fleur"); 798 DEFCURSOR(ResizeCursor, "fleur"); 799 DEFCURSOR(MenuCursor, "sb_left_arrow"); 800 DEFCURSOR(ButtonCursor, "hand2"); 801 DEFCURSOR(WaitCursor, "watch"); 802 DEFCURSOR(SelectCursor, "dot"); 803 DEFCURSOR(DestroyCursor, "pirate"); 804 DEFCURSOR(AlterCursor, "question_arrow"); 805#undef DEFCURSOR 806 807 // Load up fonts for the screen. 808 // 809 // XXX HaveFonts is kinda stupid, however it gets useful in one 810 // place: when loading button bindings, we make some sort of 811 // "menu" for things (x-ref GotButton()), and the menu gen code 812 // needs to load font stuff, so if that happened in the config 813 // process, we would have already run CreateFonts(). Of course, 814 // that's a order-dependent bit of the config file parsing too; 815 // if you define the fonts too late, they wouldn't have been set 816 // by then, and we won't [re]try them now... arg. 817 if(!Scr->HaveFonts) { 818 CreateFonts(Scr); 819 } 820 821 // Adjust settings for titlebar. Must follow CreateFonts() call 822 // so we know these bits are populated 823 Scr->TitleBarFont.y += Scr->FramePadding; 824 Scr->TitleHeight = Scr->TitleBarFont.height + Scr->FramePadding * 2; 825 if(Scr->use3Dtitles) { 826 Scr->TitleHeight += 2 * Scr->TitleShadowDepth; 827 } 828 /* make title height be odd so buttons look nice and centered */ 829 if(!(Scr->TitleHeight & 1)) { 830 Scr->TitleHeight++; 831 } 832 833 834 835 /* 836 * Now we can start making various things. 837 */ 838 839 // Stash up a ref to our Scr on the root, so we can find the 840 // right Scr for events etc. 841 XSaveContext(dpy, Scr->Root, ScreenContext, (XPointer) Scr); 842 843 // Setup GC's for drawing, so we can start making stuff we have 844 // to actually draw. Could move earlier, has to preceed a lot of 845 // following. 846 CreateGCs(); 847 848 // Create and draw the menus we config'd 849 MakeMenus(); 850 851 // Load up the images for titlebar buttons 852 InitTitlebarButtons(); 853 854 // Allocate controls for WindowRegion's. Has to follow 855 // workspaces setup, but doesn't talk to X. 856 CreateWindowRegions(); 857 858 // Copy the icon managers over to workspaces past the first as 859 // necessary. AllocateIconManager() and the config parsing 860 // already made them on the first WS. 861 AllocateOtherIconManagers(); 862 863 // Create the windows for our icon managers now that all our 864 // tracking for it is setup. 865 CreateIconManagers(); 866 867 // Create the WSM window (per-vscreen) and stash info on the root 868 // about our WS's. 869 CreateWorkSpaceManager(); 870 871 // Create the f.occupy window 872 CreateOccupyWindow(); 873 874 // Setup TwmWorkspaces menu. Needs workspaces setup, as well as 875 // menus made. 876 MakeWorkspacesMenu(); 877 878#ifdef WINBOX 879 // setup WindowBox's 880 createWindowBoxes(); 881#endif 882 883 // Initialize Xrm stuff; things with setting occupation etc use 884 // Xrm bits. 885 XrmInitialize(); 886 887#ifdef EWMH 888 // Set EWMH-related properties on various root-ish windows, for 889 // other programs to read to find out how we view the world. 890 EwmhInitScreenLate(Scr); 891#endif /* EWMH */ 892 893 894 /* 895 * Look up and handle all the windows on the screen. 896 */ 897 { 898 Window parent, *children; 899 unsigned int nchildren; 900 901 XQueryTree(dpy, Scr->Root, &croot, &parent, &children, &nchildren); 902 903 /* Weed out icon windows */ 904 for(int i = 0; i < nchildren; i++) { 905 if(children[i]) { 906 XWMHints *wmhintsp = XGetWMHints(dpy, children[i]); 907 908 if(wmhintsp) { 909 if(wmhintsp->flags & IconWindowHint) { 910 for(int j = 0; j < nchildren; j++) { 911 if(children[j] == wmhintsp->icon_window) { 912 children[j] = None; 913 break; 914 } 915 } 916 } 917 XFree(wmhintsp); 918 } 919 } 920 } 921 922 /* 923 * Map all of the non-override windows. This winds down 924 * into AddWindow() and friends through SimulateMapRequest(), 925 * so this is where we actually adopt the windows on the 926 * screen. 927 */ 928 for(int i = 0; i < nchildren; i++) { 929 if(children[i] && MappedNotOverride(children[i])) { 930 XUnmapWindow(dpy, children[i]); 931 SimulateMapRequest(children[i]); 932 } 933 } 934 935 /* 936 * At this point, we've adopted all the windows currently on 937 * the screen (aside from those we're intentionally not). 938 * Note that this happens _before_ the various other windows 939 * we create below, which is why they don't wind up getting 940 * TwmWindow's tied to them or show up in icon managers, etc. 941 * We'd need to actually make it _explicit_ that those 942 * windows aren't tracked by us if we changed that order... 943 */ 944 } 945 946 947 // Show the WSM window if we should 948 if(Scr->ShowWorkspaceManager && Scr->workSpaceManagerActive) { 949 VirtualScreen *vs; 950 if(Scr->WindowMask) { 951 XRaiseWindow(dpy, Scr->WindowMask); 952 } 953 for(vs = Scr->vScreenList; vs != NULL; vs = vs->next) { 954 SetMapStateProp(vs->wsw->twm_win, NormalState); 955 XMapWindow(dpy, vs->wsw->twm_win->frame); 956 if(vs->wsw->twm_win->StartSqueezed) { 957 Squeeze(vs->wsw->twm_win); 958 } 959 else { 960 XMapWindow(dpy, vs->wsw->w); 961 } 962 vs->wsw->twm_win->mapped = true; 963 } 964 } 965 966 967 /* 968 * Setup the Info window, used for f.identify and f.version. 969 */ 970 { 971 unsigned long valuemask; 972 XSetWindowAttributes attributes; 973 974 attributes.border_pixel = Scr->DefaultC.fore; 975 attributes.background_pixel = Scr->DefaultC.back; 976 attributes.event_mask = (ExposureMask | ButtonPressMask | 977 KeyPressMask | ButtonReleaseMask); 978 NewFontCursor(&attributes.cursor, "hand2"); 979 valuemask = (CWBorderPixel | CWBackPixel | CWEventMask | CWCursor); 980 Scr->InfoWindow.win = 981 XCreateWindow(dpy, Scr->Root, 0, 0, 982 5, 5, 983 0, 0, 984 CopyFromParent, CopyFromParent, 985 valuemask, &attributes); 986 } 987 988 989 /* 990 * Setup the Size/Position window for showing during resize/move 991 * operations. 992 */ 993 { 994 // Stick the SizeWindow at the top left of the first monitor 995 // we found on this Screen. That _may_ not be (0,0) (imagine 996 // a shorter left and taller right monitor, with their bottom 997 // edges lined up instead of top), so we have to look up what 998 // that coordinate is. If we're CenterFeedbackWindow'ing, 999 // the window will have to move between monitors depending on 1000 // where the window we're moving is (starts), but 1001 // MoveResizeSizeWindow() will handle that. If not, it 1002 // always stays in the top-left of the first display. 1003 RArea area = RLayoutGetAreaIndex(Scr->Layout, 0); 1004 XRectangle ink_rect; 1005 XRectangle logical_rect; 1006 unsigned long valuemask; 1007 XSetWindowAttributes attributes; 1008 1009 XmbTextExtents(Scr->SizeFont.font_set, 1010 " 8888 x 8888 ", 13, 1011 &ink_rect, &logical_rect); 1012 Scr->SizeStringWidth = logical_rect.width; 1013 valuemask = (CWBorderPixel | CWBackPixel | CWBitGravity); 1014 attributes.bit_gravity = NorthWestGravity; 1015 1016 if(Scr->SaveUnder) { 1017 attributes.save_under = True; 1018 valuemask |= CWSaveUnder; 1019 } 1020 1021 Scr->SizeWindow = XCreateWindow(dpy, Scr->Root, 1022 area.x, area.y, 1023 Scr->SizeStringWidth, 1024 (Scr->SizeFont.height + 1025 SIZE_VINDENT * 2), 1026 0, 0, 1027 CopyFromParent, 1028 CopyFromParent, 1029 valuemask, &attributes); 1030 } 1031 1032 // Create util window used in animation 1033 Scr->ShapeWindow = XCreateSimpleWindow(dpy, Scr->Root, 0, 0, 1034 Scr->rootw, Scr->rooth, 0, 0, 0); 1035 1036 1037 // Clear out the splash screen if we had one 1038 if(Scr->ShowWelcomeWindow) { 1039 UnmaskScreen(); 1040 } 1041 1042 // Done setting up this Screen. x-ref XXX's about whether this 1043 // element is worth anything... 1044 Scr->FirstTime = false; 1045 } // for each screen on display 1046 1047 1048 // If we're just checking the config, there's nothing more to do. 1049 if(CLarg.cfgchk) { 1050 exit(cfgerrs); 1051 } 1052 1053 1054 // We're not much of a window manager if we didn't get stuff to 1055 // manage... 1056 if(numManaged == 0) { 1057 if(CLarg.MultiScreen && NumScreens > 0) 1058 fprintf(stderr, "%s: unable to find any unmanaged screens\n", 1059 ProgramName); 1060 exit(1); 1061 } 1062 1063#ifdef SESSION 1064 // Hook up session 1065 ConnectToSessionManager(CLarg.client_id); 1066#endif 1067 1068#ifdef SOUNDS 1069 // Announce ourselves 1070 sound_load_list(); 1071 play_startup_sound(); 1072#endif 1073 1074 // Hard-reset this flag. 1075 // XXX This doesn't seem right? 1076 RestartPreviousState = true; 1077 1078 // Set vars to enable animation bits 1079 StartAnimation(); 1080 1081 // Main loop. 1082 HandlingEvents = true; 1083 HandleEvents(); 1084 1085 // Should never get here... 1086 fprintf(stderr, "Shouldn't return from HandleEvents()!\n"); 1087 exit(1); 1088} 1089 1090 1091 1092/** 1093 * Initialize ScreenInfo for a Screen. This allocates the struct, 1094 * assigns in the info we pass it about the screen and dimensions, and 1095 * then puts in our various default/fallback/sentinel/etc values to 1096 * prepare it for later use. 1097 * 1098 * It is intentional that this doesn't do any of the initialization that 1099 * involves calling out to X functions; it operates as a pure function. 1100 * This makes it easier to use it to fake up a ScreenInfo for something 1101 * that isn't actually an X Screen, for testing etc. 1102 * 1103 * \param scrnum The Screen number (e.g, :0.0 -> 0) 1104 * \param croot The X Window for the Screen's root window 1105 * \param crootx Root X coordinate 1106 * \param crooty Root Y coordinate 1107 * \param crootw Root width 1108 * \param crooth Root height 1109 * \return Allocated and populated ScreenInfo 1110 */ 1111ScreenInfo * 1112InitScreenInfo(int scrnum, Window croot, int crootx, int crooty, 1113 unsigned int crootw, unsigned int crooth) 1114{ 1115 ScreenInfo *scr; 1116 scr = calloc(1, sizeof(ScreenInfo)); 1117 if(scr == NULL) { 1118 return NULL; 1119 } 1120 // Because of calloc(), it's already all 0 bytes, which are NULL and 1121 // false and 0 and similar. Some following initializations are 1122 // nugatory because of that, but are left for clarity. 1123 1124 // Poison the global Scr to protect against typos 1125#define Scr StupidProgrammer 1126 1127 1128 // Basic pieces about the X screen we're talking about, and some 1129 // derived dimension-related bits. 1130 scr->screen = scrnum; 1131 scr->XineramaRoot = scr->Root = croot; 1132 scr->rootx = crootx; 1133 scr->rooty = crooty; 1134 scr->rootw = crootw; 1135 scr->rooth = crooth; 1136 1137#ifdef CAPTIVE 1138 scr->crootx = crootx; 1139 scr->crooty = crooty; 1140 scr->crootw = crootw; 1141 scr->crooth = crooth; 1142#endif 1143 1144 // Don't allow icon titles wider than the screen 1145 scr->MaxIconTitleWidth = scr->rootw; 1146 1147 // Attempt to come up with a sane default for the max sizes. Start 1148 // by limiting so that a window with its left/top on the right/bottom 1149 // edge of the screen can't extend further than X can address (signed 1150 // 16-bit). However, when your screen size starts approaching that 1151 // limit, reducing the max window sizes too much gets stupid too, so 1152 // set an arbitrary floor on how low this will take it. 1153 // MaxWindowSize in the config will override whatever's here anyway. 1154 scr->MaxWindowWidth = 32767 - (scr->rootx + scr->rootw); 1155 scr->MaxWindowHeight = 32767 - (scr->rooty + scr->rooth); 1156 if(scr->MaxWindowWidth < 4096) { 1157 scr->MaxWindowWidth = 4096; 1158 } 1159 if(scr->MaxWindowHeight < 4096) { 1160 scr->MaxWindowHeight = 4096; 1161 } 1162 1163 1164 // Flags used in the code to keep track of where in various processes 1165 // (especially startup) we are. 1166 scr->HaveFonts = false; 1167 1168 // Flag which basically means "initial screen setup time". 1169 // XXX Not clear to what extent this should even exist; a lot of 1170 // uses are fairly bogus. 1171 scr->FirstTime = true; 1172 1173 // Sentinel values for defaulting config values 1174 scr->FramePadding = -100; 1175 scr->TitlePadding = -100; 1176 scr->ButtonIndent = -100; 1177 scr->TBInfo.border = -100; 1178 1179 // Default values for all sorts of config params 1180 scr->SizeStringOffset = 0; 1181 scr->ThreeDBorderWidth = 6; 1182 scr->BorderWidth = BW; 1183 scr->IconBorderWidth = BW; 1184 scr->NumAutoRaises = 0; 1185 scr->NumAutoLowers = 0; 1186 scr->TransientOnTop = 30; 1187 scr->NoDefaults = false; 1188 scr->UsePPosition = PPOS_OFF; 1189 scr->UseSunkTitlePixmap = false; 1190 scr->FocusRoot = true; 1191 scr->WarpCursor = false; 1192 scr->ForceIcon = false; 1193 scr->NoGrabServer = true; 1194 scr->NoRaiseMove = false; 1195 scr->NoRaiseResize = false; 1196 scr->NoRaiseDeicon = false; 1197 scr->RaiseOnWarp = true; 1198 scr->DontMoveOff = false; 1199 scr->DoZoom = false; 1200 scr->TitleFocus = true; 1201 scr->IconManagerFocus = true; 1202 scr->StayUpMenus = false; 1203 scr->WarpToDefaultMenuEntry = false; 1204 scr->ClickToFocus = false; 1205 scr->SloppyFocus = false; 1206 scr->SaveWorkspaceFocus = false; 1207 scr->NoIconTitlebar = false; 1208 scr->NoTitlebar = false; 1209 scr->DecorateTransients = true; 1210 scr->IconifyByUnmapping = false; 1211 scr->ShowIconManager = false; 1212 scr->ShowWorkspaceManager = false; 1213 scr->WMgrButtonShadowDepth = 2; 1214 scr->WMgrVertButtonIndent = 5; 1215 scr->WMgrHorizButtonIndent = 5; 1216 scr->BorderShadowDepth = 2; 1217 scr->TitleShadowDepth = 2; 1218 scr->TitleButtonShadowDepth = 2; 1219 scr->MenuShadowDepth = 2; 1220 scr->IconManagerShadowDepth = 2; 1221 scr->AutoOccupy = false; 1222 scr->TransientHasOccupation = false; 1223 scr->DontPaintRootWindow = false; 1224 scr->IconManagerDontShow = false; 1225 scr->BackingStore = false; 1226 scr->SaveUnder = true; 1227 scr->RandomPlacement = RP_ALL; 1228 scr->RandomDisplacementX = 30; 1229 scr->RandomDisplacementY = 30; 1230 scr->DoOpaqueMove = true; 1231 scr->OpaqueMove = false; 1232 scr->OpaqueMoveThreshold = 200; 1233 scr->OpaqueResize = false; 1234 scr->DoOpaqueResize = true; 1235 scr->OpaqueResizeThreshold = 1000; 1236 scr->Highlight = true; 1237 scr->StackMode = true; 1238 scr->TitleHighlight = true; 1239 scr->MoveDelta = 1; 1240 scr->MoveOffResistance = -1; 1241 scr->MovePackResistance = 20; 1242 scr->ZoomCount = 8; 1243 scr->SortIconMgr = true; 1244 scr->Shadow = true; 1245 scr->InterpolateMenuColors = false; 1246 scr->NoIconManagers = false; 1247 scr->ClientBorderWidth = false; 1248 scr->SqueezeTitle = false; 1249 scr->FirstTime = true; 1250 scr->CaseSensitive = true; 1251 scr->WarpUnmapped = false; 1252 scr->WindowRingAll = false; 1253 scr->WarpRingAnyWhere = true; 1254 scr->ShortAllWindowsMenus = false; 1255 scr->use3Diconmanagers = false; 1256 scr->use3Dmenus = false; 1257 scr->use3Dtitles = false; 1258 scr->use3Dborders = false; 1259 scr->use3Dwmap = false; 1260 scr->SunkFocusWindowTitle = false; 1261 scr->ClearShadowContrast = 50; 1262 scr->DarkShadowContrast = 40; 1263 scr->BeNiceToColormap = false; 1264 scr->BorderCursors = false; 1265 scr->IconJustification = TJ_CENTER; 1266 scr->IconRegionJustification = IRJ_CENTER; 1267 scr->IconRegionAlignement = IRA_CENTER; 1268 scr->TitleJustification = TJ_LEFT; 1269 scr->IconifyStyle = ICONIFY_NORMAL; 1270 scr->ReallyMoveInWorkspaceManager = false; 1271 scr->ShowWinWhenMovingInWmgr = false; 1272 scr->ReverseCurrentWorkspace = false; 1273 scr->DontWarpCursorInWMap = false; 1274 scr->XMoveGrid = 1; 1275 scr->YMoveGrid = 1; 1276 scr->CenterFeedbackWindow = false; 1277 scr->ShrinkIconTitles = false; 1278 scr->AutoRaiseIcons = false; 1279 scr->AutoFocusToTransients = false; 1280 scr->OpenWindowTimeout = 0; 1281 scr->RaiseWhenAutoUnSqueeze = false; 1282 scr->RaiseOnClick = false; 1283 scr->RaiseOnClickButton = 1; 1284 scr->IgnoreModifier = 0; 1285 scr->IgnoreCaseInMenuSelection = false; 1286 scr->PackNewWindows = false; 1287 scr->AlwaysSqueezeToGravity = false; 1288 scr->NoWarpToMenuTitle = false; 1289 scr->DontToggleWorkspaceManagerState = false; 1290 scr->NameDecorations = true; 1291 scr->ForceFocus = false; 1292 scr->BorderTop = 0; 1293 scr->BorderBottom = 0; 1294 scr->BorderLeft = 0; 1295 scr->BorderRight = 0; 1296 scr->PixmapDirectory = PIXMAP_DIRECTORY; 1297#ifdef EWMH 1298 scr->PreferredIconWidth = 48; 1299 scr->PreferredIconHeight = 48; 1300 1301 scr->ewmh_CLIENT_LIST_used = 0; 1302 scr->ewmh_CLIENT_LIST_size = 16; 1303 scr->ewmh_CLIENT_LIST = calloc(scr->ewmh_CLIENT_LIST_size, 1304 sizeof(scr->ewmh_CLIENT_LIST[0])); 1305 if(scr->ewmh_CLIENT_LIST == NULL) { 1306 free(scr); 1307 return NULL; 1308 } 1309#endif 1310 1311 // OTP structure bits 1312 OtpScrInitData(scr); 1313 1314 1315 // WorkSpaceManager stuff 1316 scr->workSpaceMgr.initialstate = WMS_map; 1317 scr->workSpaceMgr.buttonStyle = STYLE_NORMAL; 1318 scr->workSpaceMgr.vspace = scr->WMgrVertButtonIndent; 1319 scr->workSpaceMgr.hspace = scr->WMgrHorizButtonIndent; 1320 1321 scr->workSpaceMgr.occupyWindow = calloc(1, sizeof(OccupyWindow)); 1322 scr->workSpaceMgr.occupyWindow->vspace = scr->WMgrVertButtonIndent; 1323 scr->workSpaceMgr.occupyWindow->hspace = scr->WMgrHorizButtonIndent; 1324 scr->workSpaceMgr.occupyWindow->name = "Occupy Window"; 1325 scr->workSpaceMgr.occupyWindow->icon_name = "Occupy Window Icon"; 1326 1327 scr->workSpaceMgr.name = "WorkSpaceManager"; 1328 scr->workSpaceMgr.icon_name = "WorkSpaceManager Icon"; 1329 1330 1331 // Setup default fonts in case the config file doesn't 1332#define DEFAULT_NICE_FONT "-*-helvetica-bold-r-normal-*-*-120-*" 1333#define DEFAULT_FAST_FONT "-misc-fixed-medium-r-semicondensed--13-120-75-75-c-60-*" 1334#define SETFONT(fld, var) (scr->fld##Font.basename = DEFAULT_##var##_FONT) 1335 1336 SETFONT(TitleBar, NICE); 1337 SETFONT(Menu, NICE); 1338 SETFONT(Icon, NICE); 1339 SETFONT(Size, FAST); 1340 SETFONT(IconManager, NICE); 1341 SETFONT(Default, FAST); 1342 scr->workSpaceMgr.windowFont.basename = 1343 "-adobe-courier-medium-r-normal--10-100-75-75-m-60-iso8859-1"; 1344 1345#undef SETFONT 1346#undef DEFAULT_FAST_FONT 1347#undef DEFAULT_NICE_FONT 1348 1349 1350 // Set some fallback values that we set from the X server, for 1351 // special cases where we may not actually be talking to one. 1352 scr->d_depth = 24; 1353 scr->RealRoot = croot; 1354 scr->mm_w = 406; // 16 in 1355 scr->mm_h = 229; // 9 in 1356 scr->Monochrome = COLOR; 1357 1358 // Cleanup poisoning 1359#undef Scr 1360 return scr; 1361} 1362 1363 1364 1365 1366#ifdef CAPTIVE 1367/** 1368 * Create a new window to use for a captive ctwm. 1369 */ 1370static Window 1371CreateCaptiveRootWindow(int x, int y, 1372 unsigned int width, unsigned int height) 1373{ 1374 int scrnum; 1375 Window ret; 1376 XWMHints wmhints; 1377 1378 scrnum = DefaultScreen(dpy); 1379 ret = XCreateSimpleWindow(dpy, RootWindow(dpy, scrnum), 1380 x, y, width, height, 2, WhitePixel(dpy, scrnum), 1381 BlackPixel(dpy, scrnum)); 1382 wmhints.initial_state = NormalState; 1383 wmhints.input = True; 1384 wmhints.flags = InputHint | StateHint; 1385 1386 XmbSetWMProperties(dpy, ret, "Captive ctwm", NULL, NULL, 0, NULL, 1387 &wmhints, NULL); 1388 XChangeProperty(dpy, ret, XA_WM_CTWM_ROOT, XA_WINDOW, 32, 1389 PropModeReplace, (unsigned char *) &ret, 1); 1390 XSelectInput(dpy, ret, StructureNotifyMask); 1391 XMapWindow(dpy, ret); 1392 return (ret); 1393} 1394#endif 1395 1396 1397 1398/** 1399 * Return true if a window is not set to override_redirect ("Hey! WM! 1400 * Leave those wins alone!"), and isn't unmapped. Used during startup to 1401 * fake mapping for wins that should be up. 1402 */ 1403static bool 1404MappedNotOverride(Window w) 1405{ 1406 XWindowAttributes wa; 1407 1408 XGetWindowAttributes(dpy, w, &wa); 1409 return ((wa.map_state != IsUnmapped) && (wa.override_redirect != True)); 1410} 1411