otp.c revision 0bbfda8a
1/* 2 * Copyright 1992, 2005 Stefan Monnier. 3 * 4 * Author: Stefan Monnier [ monnier@lia.di.epfl.ch ] 5 * Adapted for use with more than one virtual screen by 6 * Olaf "Rhialto" Seibert <rhialto@falu.nl>. 7 * 8 * $Id: otp.c,v 1.1.1.1 2021/04/11 08:36:52 nia Exp $ 9 * 10 * handles all the OnTopPriority-related issues. 11 * 12 */ 13 14#include "ctwm.h" 15 16#include <stdio.h> 17#include <stdlib.h> 18#include <assert.h> 19#include <X11/Xatom.h> 20 21#include "otp.h" 22#include "ctwm_atoms.h" 23#include "screen.h" 24#include "util.h" 25#include "icons.h" 26#include "list.h" 27#include "events.h" 28#include "event_handlers.h" 29#include "vscreen.h" 30#include "win_utils.h" 31 32#define DEBUG_OTP 0 33#if DEBUG_OTP 34#define DPRINTF(x) fprintf x 35#else 36#define DPRINTF(x) 37#endif 38 39#if defined(NDEBUG) 40# define CHECK_OTP 0 41#else 42# define CHECK_OTP 1 43#endif 44 45/* number of priorities known to ctwm: [0..ONTOP_MAX] */ 46#define OTP_ZERO 8 47#define OTP_MAX (OTP_ZERO * 2) 48 49/* Shorten code a little */ 50#define PRI(owl) OwlEffectivePriority(owl) 51#define PRI_CP(from, to) do { \ 52 to->pri_base = from->pri_base; \ 53 to->pri_aflags = from->pri_aflags; \ 54 } while(0) 55 56struct OtpWinList { 57 OtpWinList *above; 58 OtpWinList *below; 59 TwmWindow *twm_win; 60 WinType type; 61 bool switching; 62 int pri_base; // Base priority 63 unsigned pri_aflags; // Flags that might alter it; OTP_AFLAG_* 64 bool stashed_aflags; 65}; 66 67struct OtpPreferences { 68 name_list *priorityL[OTP_MAX + 1]; 69 int priority; 70 name_list *switchingL; 71 bool switching; 72}; 73 74typedef struct Box { 75 int x; 76 int y; 77 int width; 78 int height; 79} Box; 80 81 82static bool OtpCheckConsistencyVS(VirtualScreen *currentvs, Window vroot); 83static void OwlSetAflagMask(OtpWinList *owl, unsigned mask, unsigned setto); 84static void OwlSetAflag(OtpWinList *owl, unsigned flag); 85static void OwlClearAflag(OtpWinList *owl, unsigned flag); 86static void OwlStashAflags(OtpWinList *owl); 87static unsigned OwlGetStashedAflags(OtpWinList *owl, bool *gotit); 88static int OwlEffectivePriority(OtpWinList *owl); 89 90static Box BoxOfOwl(OtpWinList *owl) 91{ 92 Box b; 93 94 switch(owl->type) { 95 case IconWin: { 96 Icon *icon = owl->twm_win->icon; 97 98 b.x = icon->w_x; 99 b.y = icon->w_y; 100 b.width = icon->w_width; 101 b.height = icon->w_height; 102 break; 103 } 104 case WinWin: { 105 TwmWindow *twm_win = owl->twm_win; 106 107 b.x = twm_win->frame_x; 108 b.y = twm_win->frame_y; 109 b.width = twm_win->frame_width; 110 b.height = twm_win->frame_height; 111 break; 112 } 113 default: 114 assert(false); 115 } 116 return b; 117} 118 119 120static bool BoxesIntersect(Box *b1, Box *b2) 121{ 122 bool interX = (b1->x + b1->width > b2->x) && (b2->x + b2->width > b1->x); 123 bool interY = (b1->y + b1->height > b2->y) && (b2->y + b2->height > b1->y); 124 125 return (interX && interY); 126} 127 128 129static bool isIntersectingWith(OtpWinList *owl1, OtpWinList *owl2) 130{ 131 Box b1 = BoxOfOwl(owl1); 132 Box b2 = BoxOfOwl(owl2); 133 134 return BoxesIntersect(&b1, &b2); 135} 136 137 138static bool isOnScreen(OtpWinList *owl) 139{ 140 TwmWindow *twm_win = owl->twm_win; 141 142 return (((owl->type == IconWin) ? twm_win->iconified : twm_win->mapped) 143 && OCCUPY(twm_win, Scr->currentvs->wsw->currentwspc)); 144} 145 146 147bool isTransientOf(TwmWindow *trans, TwmWindow *main) 148{ 149 return (trans->istransient && trans->transientfor == main->w); 150} 151 152bool isGroupLeader(TwmWindow *twm_win) 153{ 154 return ((twm_win->group == 0) 155 || (twm_win->group == twm_win->w)); 156} 157 158bool isGroupLeaderOf(TwmWindow *leader, TwmWindow *twm_win) 159{ 160 return (isGroupLeader(leader) 161 && !isGroupLeader(twm_win) 162 && (leader->group == twm_win->group)); 163} 164 165bool isSmallTransientOf(TwmWindow *trans, TwmWindow *main) 166{ 167 int trans_area, main_area; 168 169 if(isTransientOf(trans, main)) { 170 assert(trans->frame); 171 trans_area = trans->frame_width * trans->frame_height; 172 main_area = main->frame_width * main->frame_height; 173 174 return (trans_area < ((main_area * Scr->TransientOnTop) / 100)); 175 } 176 else { 177 return false; 178 } 179} 180 181static Window WindowOfOwl(OtpWinList *owl) 182{ 183 return (owl->type == IconWin) 184 ? owl->twm_win->icon->w : owl->twm_win->frame; 185} 186 187bool OtpCheckConsistency(void) 188{ 189#if DEBUG_OTP 190 VirtualScreen *tvs; 191 bool result = true; 192 193 for(tvs = Scr->vScreenList; tvs != NULL; tvs = tvs->next) { 194 fprintf(stderr, "OtpCheckConsistencyVS: vs:(x,y)=(%d,%d)\n", 195 tvs->x, tvs->y); 196 result = result && OtpCheckConsistencyVS(tvs, tvs->window); 197 } 198 return result; 199#else 200 return OtpCheckConsistencyVS(Scr->currentvs, Scr->Root); 201#endif 202} 203 204static bool OtpCheckConsistencyVS(VirtualScreen *currentvs, Window vroot) 205{ 206#if CHECK_OTP 207 OtpWinList *owl; 208 TwmWindow *twm_win; 209 Window root, parent, *children; 210 unsigned int nchildren; 211 int priority = 0; 212 int stack = -1; 213 int nwins = 0; 214 215 XQueryTree(dpy, vroot, &root, &parent, &children, &nchildren); 216 217#if DEBUG_OTP 218 { 219 int i; 220 fprintf(stderr, "XQueryTree: %d children:\n", nchildren); 221 222 for(i = 0; i < nchildren; i++) { 223 fprintf(stderr, "[%d]=%x ", i, (unsigned int)children[i]); 224 } 225 fprintf(stderr, "\n"); 226 } 227#endif 228 229 for(owl = Scr->bottomOwl; owl != NULL; owl = owl->above) { 230 twm_win = owl->twm_win; 231 232 /* check the back arrows are correct */ 233 assert(((owl->type == IconWin) && (owl == twm_win->icon->otp)) 234 || ((owl->type == WinWin) && (owl == twm_win->otp))); 235 236 /* check the doubly linked list's consistency */ 237 if(owl->below == NULL) { 238 assert(owl == Scr->bottomOwl); 239 } 240 else { 241 assert(owl->below->above == owl); 242 } 243 244 /* Code already ensures this */ 245 assert(owl->pri_base <= OTP_MAX); 246 247 /* List should be bottom->top, so effective pri better ascend */ 248 assert(PRI(owl) >= priority); 249 priority = PRI(owl); 250 251#if DEBUG_OTP 252 253 fprintf(stderr, "checking owl: pri %d w=%x stack=%d", 254 priority, (unsigned int)WindowOfOwl(owl), stack); 255 if(twm_win) { 256 fprintf(stderr, " title=%s occupation=%x ", 257 twm_win->name, 258 (unsigned int)twm_win->occupation); 259 if(owl->twm_win->vs) { 260 fprintf(stderr, " vs:(x,y)=(%d,%d)", 261 twm_win->vs->x, 262 twm_win->vs->y); 263 } 264 else { 265 fprintf(stderr, " vs:NULL"); 266 } 267 if(owl->twm_win->parent_vs) { 268 fprintf(stderr, " parent_vs:(x,y)=(%d,%d)", 269 twm_win->parent_vs->x, 270 twm_win->parent_vs->y); 271 } 272 else { 273 fprintf(stderr, " parent_vs:NULL"); 274 } 275 } 276 fprintf(stderr, " %s\n", (owl->type == WinWin ? "Window" : "Icon")); 277#endif 278 279 /* count the number of twm windows */ 280 if(owl->type == WinWin) { 281 nwins++; 282 } 283 284 if(twm_win->winbox) { 285 /* 286 * We can't check windows in a WindowBox, since they are 287 * not direct children of the Root window. 288 */ 289 DPRINTF((stderr, "Can't check this window, it is in a WinBox\n")); 290 continue; 291 } 292 293 /* 294 * Check only windows from the current vitual screen; the others 295 * won't show up in the tree from XQueryTree(). 296 */ 297 if(currentvs == twm_win->parent_vs) { 298 /* check the window's existence. */ 299 Window windowOfOwl = WindowOfOwl(owl); 300 301#if DEBUG_OTP 302 int i; 303 for(i = 0; i < nchildren && windowOfOwl != children[i];) { 304 i++; 305 } 306 fprintf(stderr, "search for owl in stack -> i=%d\n", i); 307 assert(i > stack && "Window not in good place in stack"); 308 assert(i < nchildren && "Window was not found in stack"); 309 if(0) { 310 char buf[128]; 311 snprintf(buf, 128, "xwininfo -all -id %d", (int)windowOfOwl); 312 system(buf); 313 } 314 315 /* we know that this always increases stack (assert i>stack) */ 316 stack = i; 317#else /* DEBUG_OTP */ 318 /* check against the Xserver's stack */ 319 do { 320 stack++; 321 DPRINTF((stderr, "stack++: children[%d] = %x\n", stack, 322 (unsigned int)children[stack])); 323 assert(stack < nchildren); 324 } 325 while(windowOfOwl != children[stack]); 326#endif /* DEBUG_OTP */ 327 } 328 } 329 330 XFree(children); 331 332 /* by decrementing nwins, check that all the wins are in our list */ 333 for(twm_win = Scr->FirstWindow; twm_win != NULL; twm_win = twm_win->next) { 334 nwins--; 335 } 336 /* if we just removed a win, it might still be somewhere, hence the -1 */ 337 assert((nwins <= 0) && (nwins >= -1)); 338#endif 339 return true; 340} 341 342 343static void RemoveOwl(OtpWinList *owl) 344{ 345 if(owl->above != NULL) { 346 owl->above->below = owl->below; 347 } 348 if(owl->below != NULL) { 349 owl->below->above = owl->above; 350 } 351 else { 352 Scr->bottomOwl = owl->above; 353 } 354 owl->below = NULL; 355 owl->above = NULL; 356} 357 358 359/** 360 * For the purpose of putting a window above another, 361 * they need to have the same parent, i.e. be in the same 362 * VirtualScreen. 363 */ 364static OtpWinList *GetOwlAtOrBelowInVS(OtpWinList *owl, VirtualScreen *vs) 365{ 366 while(owl != NULL && owl->twm_win->parent_vs != vs) { 367 owl = owl->below; 368 } 369 370 return owl; 371} 372 373/* 374 * Windows in a box don't really occur in the stacking order of the 375 * root window. 376 * In the OWL list, keep them just on top of their box, in their 377 * respective order of course. 378 * Therefore we may need to update the owl we're going to be above. 379 */ 380static OtpWinList *GetOwlAtOrBelowInWinbox(OtpWinList **owlp, WindowBox *wb) 381{ 382 OtpWinList *owl = *owlp; 383 384 while(owl != NULL && owl->twm_win->winbox != wb) { 385 owl = owl->below; 386 } 387 388 if(owl == NULL) { 389 /* we have gone below the box: put it just on top of it */ 390 *owlp = wb->twmwin->otp; 391 } 392 else { 393 *owlp = owl; 394 } 395 return owl; 396} 397 398 399static void InsertOwlAbove(OtpWinList *owl, OtpWinList *other_owl) 400{ 401#if DEBUG_OTP 402 fprintf(stderr, "InsertOwlAbove owl->pri=%d w=0x%x parent_vs:(x,y)=(%d,%d)", 403 PRI(owl), 404 (unsigned int)WindowOfOwl(owl), 405 owl->twm_win->parent_vs->x, 406 owl->twm_win->parent_vs->y); 407 if(other_owl != NULL) { 408 fprintf(stderr, "\n other_owl->pri=%d w=0x%x parent_vs:(x,y)=(%d,%d)", 409 PRI(other_owl), 410 (unsigned int)WindowOfOwl(other_owl), 411 owl->twm_win->parent_vs->x, 412 owl->twm_win->parent_vs->y); 413 } 414 fprintf(stderr, "\n"); 415#endif 416 417 assert(owl->above == NULL); 418 assert(owl->below == NULL); 419 420 421 if(other_owl == NULL) { 422 DPRINTF((stderr, "Bottom-most window overall\n")); 423 /* special case for the lowest window overall */ 424 assert(PRI(owl) <= PRI(Scr->bottomOwl)); 425 426 /* pass the action to the Xserver */ 427 XLowerWindow(dpy, WindowOfOwl(owl)); 428 429 /* update the list */ 430 owl->above = Scr->bottomOwl; 431 owl->above->below = owl; 432 Scr->bottomOwl = owl; 433 } 434 else { 435 WindowBox *winbox = owl->twm_win->winbox; 436 OtpWinList *vs_owl; 437 438 if(winbox != NULL) { 439 vs_owl = GetOwlAtOrBelowInWinbox(&other_owl, winbox); 440 } 441 else { 442 443 vs_owl = GetOwlAtOrBelowInVS(other_owl, owl->twm_win->parent_vs); 444 } 445 446 assert(PRI(owl) >= PRI(other_owl)); 447 if(other_owl->above != NULL) { 448 assert(PRI(owl) <= PRI(other_owl->above)); 449 } 450 451 if(vs_owl == NULL) { 452 DPRINTF((stderr, "Bottom-most window in VirtualScreen or window box\n")); 453 /* special case for the lowest window in this virtual screen or window box */ 454 455 /* pass the action to the Xserver */ 456 XLowerWindow(dpy, WindowOfOwl(owl)); 457 } 458 else { 459 XWindowChanges xwc; 460 int xwcm; 461 462 DPRINTF((stderr, "General case\n")); 463 /* general case */ 464 assert(PRI(vs_owl) <= PRI(other_owl)); 465 assert(owl->twm_win->parent_vs == vs_owl->twm_win->parent_vs); 466 467 /* pass the action to the Xserver */ 468 xwcm = CWStackMode | CWSibling; 469 xwc.sibling = WindowOfOwl(vs_owl); 470 xwc.stack_mode = Above; 471 XConfigureWindow(dpy, WindowOfOwl(owl), xwcm, &xwc); 472 } 473 474 /* update the list */ 475 owl->below = other_owl; 476 owl->above = other_owl->above; 477 owl->below->above = owl; 478 if(owl->above != NULL) { 479 owl->above->below = owl; 480 } 481 } 482} 483 484 485/* should owl stay above other_owl if other_owl was raised ? */ 486static bool shouldStayAbove(OtpWinList *owl, OtpWinList *other_owl) 487{ 488 return ((owl->type == WinWin) 489 && (other_owl->type == WinWin) 490 && isSmallTransientOf(owl->twm_win, other_owl->twm_win)); 491} 492 493 494static void RaiseSmallTransientsOfAbove(OtpWinList *owl, OtpWinList *other_owl) 495{ 496 OtpWinList *trans_owl, *tmp_owl; 497 498 /* the icons have no transients and we can't have windows below NULL */ 499 if((owl->type != WinWin) || other_owl == NULL) { 500 return; 501 } 502 503 /* beware: we modify the list as we scan it. This is the reason for tmp */ 504 for(trans_owl = other_owl->below; trans_owl != NULL; trans_owl = tmp_owl) { 505 tmp_owl = trans_owl->below; 506 if(shouldStayAbove(trans_owl, owl)) { 507 RemoveOwl(trans_owl); 508 PRI_CP(owl, trans_owl); 509 InsertOwlAbove(trans_owl, other_owl); 510 } 511 } 512} 513 514 515static OtpWinList *OwlRightBelow(int priority) 516{ 517 OtpWinList *owl1, *owl2; 518 519 /* in case there isn't anything below */ 520 if(priority <= PRI(Scr->bottomOwl)) { 521 return NULL; 522 } 523 524 for(owl1 = Scr->bottomOwl, owl2 = owl1->above; 525 (owl2 != NULL) && (PRI(owl2) < priority); 526 owl1 = owl2, owl2 = owl2->above) { 527 /* nada */; 528 } 529 530 assert(owl2 == owl1->above); 531 assert(PRI(owl1) < priority); 532 assert((owl2 == NULL) || (PRI(owl2) >= priority)); 533 534 535 return owl1; 536} 537 538static void InsertOwl(OtpWinList *owl, int where) 539{ 540 OtpWinList *other_owl; 541 int priority; 542 543 DPRINTF((stderr, "InsertOwl %s\n", 544 (where == Above) ? "Above" : 545 (where == Below) ? "Below" : 546 "???")); 547 assert(owl->above == NULL); 548 assert(owl->below == NULL); 549 assert((where == Above) || (where == Below)); 550 551 priority = PRI(owl) - (where == Above ? 0 : 1); 552 553 if(Scr->bottomOwl == NULL) { 554 /* for the first window: just insert it in the list */ 555 Scr->bottomOwl = owl; 556 } 557 else { 558 other_owl = OwlRightBelow(priority + 1); 559 560 /* make sure other_owl is not one of the transients */ 561 while((other_owl != NULL) 562 && shouldStayAbove(other_owl, owl)) { 563 PRI_CP(owl, other_owl); 564 565 other_owl = other_owl->below; 566 } 567 568 /* raise the transient windows that should stay on top */ 569 RaiseSmallTransientsOfAbove(owl, other_owl); 570 571 /* now go ahead and put the window where it should go */ 572 InsertOwlAbove(owl, other_owl); 573 } 574} 575 576 577static void SetOwlPriority(OtpWinList *owl, int new_pri, int where) 578{ 579 DPRINTF((stderr, "SetOwlPriority(%d)\n", new_pri)); 580 581 /* make sure the values are within bounds */ 582 if(new_pri < 0) { 583 new_pri = 0; 584 } 585 if(new_pri > OTP_MAX) { 586 new_pri = OTP_MAX; 587 } 588 589 RemoveOwl(owl); 590 owl->pri_base = new_pri; 591 InsertOwl(owl, where); 592 593 assert(owl->pri_base == new_pri); 594} 595 596 597/* 598 * Shift transients of a window to a new [base] priority, preparatory to 599 * moving that window itself there. 600 */ 601static void TryToMoveTransientsOfTo(OtpWinList *owl, int priority, int where) 602{ 603 OtpWinList *other_owl; 604 605 /* the icons have no transients */ 606 if(owl->type != WinWin) { 607 return; 608 } 609 610 /* 611 * We start looking for transients of owl at the bottom of its OTP 612 * layer. 613 */ 614 other_owl = OwlRightBelow(PRI(owl)); 615 other_owl = (other_owl == NULL) ? Scr->bottomOwl : other_owl->above; 616 assert(PRI(other_owl) >= PRI(owl)); 617 618 /* !beware! we're changing the list as we scan it, hence the tmp_owl */ 619 while((other_owl != NULL) && (PRI(other_owl) == PRI(owl))) { 620 OtpWinList *tmp_owl = other_owl->above; 621 if((other_owl->type == WinWin) 622 && isTransientOf(other_owl->twm_win, owl->twm_win)) { 623 /* Copy in our flags so it winds up in the right place */ 624 other_owl->pri_aflags = owl->pri_aflags; 625 SetOwlPriority(other_owl, priority, where); 626 } 627 other_owl = tmp_owl; 628 } 629} 630 631static void TryToSwitch(OtpWinList *owl, int where) 632{ 633 int priority; 634 635 if(!owl->switching) { 636 return; 637 } 638 639 /* 640 * Switching is purely an adjustment to the base priority, so we 641 * don't need to figure stuff based on the effective. 642 */ 643 priority = OTP_MAX - owl->pri_base; 644 if(((where == Above) && (priority > owl->pri_base)) || 645 ((where == Below) && (priority < owl->pri_base))) { 646 /* 647 * TTMTOT() before changing pri_base since it uses the current 648 * state to find the transients. 649 */ 650 TryToMoveTransientsOfTo(owl, priority, where); 651 owl->pri_base = priority; 652 } 653} 654 655static void RaiseOwl(OtpWinList *owl) 656{ 657 TryToSwitch(owl, Above); 658 RemoveOwl(owl); 659 InsertOwl(owl, Above); 660} 661 662 663static void LowerOwl(OtpWinList *owl) 664{ 665 TryToSwitch(owl, Below); 666 RemoveOwl(owl); 667 InsertOwl(owl, Below); 668} 669 670static bool isHiddenBy(OtpWinList *owl, OtpWinList *other_owl) 671{ 672 /* doesn't check that owl is on screen */ 673 return (isOnScreen(other_owl) 674 && isIntersectingWith(owl, other_owl)); 675} 676 677static void TinyRaiseOwl(OtpWinList *owl) 678{ 679 OtpWinList *other_owl = owl->above; 680 681 while((other_owl != NULL) && (PRI(other_owl) == PRI(owl))) { 682 if(isHiddenBy(owl, other_owl) 683 && !shouldStayAbove(other_owl, owl)) { 684 RemoveOwl(owl); 685 RaiseSmallTransientsOfAbove(owl, other_owl); 686 InsertOwlAbove(owl, other_owl); 687 return; 688 } 689 else { 690 other_owl = other_owl->above; 691 } 692 } 693} 694 695static void TinyLowerOwl(OtpWinList *owl) 696{ 697 OtpWinList *other_owl = owl->below; 698 699 while((other_owl != NULL) && (PRI(other_owl) == PRI(owl))) { 700 if(isHiddenBy(owl, other_owl)) { 701 RemoveOwl(owl); 702 InsertOwlAbove(owl, other_owl->below); 703 return; 704 } 705 else { 706 other_owl = other_owl->below; 707 } 708 } 709} 710 711static void RaiseLowerOwl(OtpWinList *owl) 712{ 713 OtpWinList *other_owl; 714 int priority; 715 716 /* 717 * abs(effective pri) 718 * 719 * XXX Why? This seems like it's encoding the assumption 720 * "f.raiselower should assume any negative [user-level] priorities 721 * are a result of a window that should be positive being switched, 722 * and we should switch it positive before raising if we need to", or 723 * some such. 724 */ 725 priority = MAX(PRI(owl), OTP_MAX - PRI(owl)); 726 727 for(other_owl = owl->above; 728 (other_owl != NULL) && (PRI(other_owl) <= priority); 729 other_owl = other_owl->above) { 730 if(isHiddenBy(owl, other_owl) 731 && !shouldStayAbove(other_owl, owl)) { 732 RaiseOwl(owl); 733 return; 734 } 735 } 736 LowerOwl(owl); 737} 738 739 740void OtpRaise(TwmWindow *twm_win, WinType wintype) 741{ 742 OtpWinList *owl = (wintype == IconWin) ? twm_win->icon->otp : twm_win->otp; 743 assert(owl != NULL); 744 745 RaiseOwl(owl); 746 747 OtpCheckConsistency(); 748#ifdef EWMH 749 EwmhSet_NET_CLIENT_LIST_STACKING(); 750#endif /* EWMH */ 751} 752 753 754void OtpLower(TwmWindow *twm_win, WinType wintype) 755{ 756 OtpWinList *owl = (wintype == IconWin) ? twm_win->icon->otp : twm_win->otp; 757 assert(owl != NULL); 758 759 LowerOwl(owl); 760 761 OtpCheckConsistency(); 762#ifdef EWMH 763 EwmhSet_NET_CLIENT_LIST_STACKING(); 764#endif /* EWMH */ 765} 766 767 768void OtpRaiseLower(TwmWindow *twm_win, WinType wintype) 769{ 770 OtpWinList *owl = (wintype == IconWin) ? twm_win->icon->otp : twm_win->otp; 771 assert(owl != NULL); 772 773 RaiseLowerOwl(owl); 774 775 OtpCheckConsistency(); 776#ifdef EWMH 777 EwmhSet_NET_CLIENT_LIST_STACKING(); 778#endif /* EWMH */ 779} 780 781 782void OtpTinyRaise(TwmWindow *twm_win, WinType wintype) 783{ 784 OtpWinList *owl = (wintype == IconWin) ? twm_win->icon->otp : twm_win->otp; 785 assert(owl != NULL); 786 787 TinyRaiseOwl(owl); 788 789 OtpCheckConsistency(); 790#ifdef EWMH 791 EwmhSet_NET_CLIENT_LIST_STACKING(); 792#endif /* EWMH */ 793} 794 795 796void OtpTinyLower(TwmWindow *twm_win, WinType wintype) 797{ 798 OtpWinList *owl = (wintype == IconWin) ? twm_win->icon->otp : twm_win->otp; 799 assert(owl != NULL); 800 801 TinyLowerOwl(owl); 802 803 OtpCheckConsistency(); 804#ifdef EWMH 805 EwmhSet_NET_CLIENT_LIST_STACKING(); 806#endif /* EWMH */ 807} 808 809 810/* 811 * XCirculateSubwindows() is complicated by the fact that it restacks only 812 * in case of overlapping windows. Therefore it seems easier to not 813 * try to emulate that but to leave it to the X server. 814 * 815 * If XCirculateSubwindows() actually does something, it sends a 816 * CirculateNotify event, but you only receive it if 817 * SubstructureNotifyMask is selected on the root window. 818 * However... if that is done from the beginning, for some reason all 819 * windows disappear when ctwm starts or exits. 820 * Maybe SubstructureNotifyMask interferes with SubstructureRedirectMask? 821 * 822 * To get around that, the SubstructureNotifyMask is selected only 823 * temporarily here when wanted. 824 */ 825 826void OtpCirculateSubwindows(VirtualScreen *vs, int direction) 827{ 828 Window w = vs->window; 829 XWindowAttributes winattrs; 830 Bool circulated; 831 832 DPRINTF((stderr, "OtpCirculateSubwindows %d\n", direction)); 833 834 XGetWindowAttributes(dpy, w, &winattrs); 835 XSelectInput(dpy, w, winattrs.your_event_mask | SubstructureNotifyMask); 836 XCirculateSubwindows(dpy, w, direction); 837 XSelectInput(dpy, w, winattrs.your_event_mask); 838 /* 839 * Now we should get the CirculateNotify event. 840 * It usually seems to arrive soon enough, but just to be sure, look 841 * ahead in the message queue to see if it can be expedited. 842 */ 843 circulated = XCheckTypedWindowEvent(dpy, w, CirculateNotify, &Event); 844 if(circulated) { 845 HandleCirculateNotify(); 846 } 847} 848 849/* 850 * Update our list of Owls after the Circulate action, and also 851 * enforce the priority by possibly restacking the window again. 852 */ 853 854void OtpHandleCirculateNotify(VirtualScreen *vs, TwmWindow *twm_win, 855 WinType wintype, int place) 856{ 857 switch(place) { 858 case PlaceOnTop: 859 OtpRaise(twm_win, wintype); 860 break; 861 case PlaceOnBottom: 862 OtpLower(twm_win, wintype); 863 break; 864 default: 865 DPRINTF((stderr, "OtpHandleCirculateNotify: place=%d\n", place)); 866 assert(0 && 867 "OtpHandleCirculateNotify: place equals PlaceOnTop nor PlaceOnBottom"); 868 } 869} 870 871void OtpSetPriority(TwmWindow *twm_win, WinType wintype, int new_pri, int where) 872{ 873 OtpWinList *owl = (wintype == IconWin) ? twm_win->icon->otp : twm_win->otp; 874 int priority = OTP_ZERO + new_pri; 875 876 DPRINTF((stderr, "OtpSetPriority: new_pri=%d\n", new_pri)); 877 assert(owl != NULL); 878 879 if(twm_win->winbox != NULL || twm_win->iswinbox) { 880 return; 881 } 882 883 if(ABS(new_pri) > OTP_ZERO) { 884 DPRINTF((stderr, "invalid OnTopPriority value: %d\n", new_pri)); 885 } 886 else { 887 TryToMoveTransientsOfTo(owl, priority, where); 888 SetOwlPriority(owl, priority, where); 889 } 890 891 OtpCheckConsistency(); 892} 893 894 895void OtpChangePriority(TwmWindow *twm_win, WinType wintype, int relpriority) 896{ 897 OtpWinList *owl = (wintype == IconWin) ? twm_win->icon->otp : twm_win->otp; 898 int priority = owl->pri_base + relpriority; 899 int where; 900 901 if(twm_win->winbox != NULL || twm_win->iswinbox) { 902 return; 903 } 904 905 where = relpriority < 0 ? Below : Above; 906 907 TryToMoveTransientsOfTo(owl, priority, where); 908 SetOwlPriority(owl, priority, where); 909 910 OtpCheckConsistency(); 911} 912 913 914void OtpSwitchPriority(TwmWindow *twm_win, WinType wintype) 915{ 916 OtpWinList *owl = (wintype == IconWin) ? twm_win->icon->otp : twm_win->otp; 917 int priority = OTP_MAX - owl->pri_base; 918 int where; 919 920 assert(owl != NULL); 921 922 if(twm_win->winbox != NULL || twm_win->iswinbox) { 923 return; 924 } 925 926 where = priority < OTP_ZERO ? Below : Above; 927 TryToMoveTransientsOfTo(owl, priority, where); 928 SetOwlPriority(owl, priority, where); 929 930 OtpCheckConsistency(); 931} 932 933 934void OtpToggleSwitching(TwmWindow *twm_win, WinType wintype) 935{ 936 OtpWinList *owl = (wintype == IconWin) ? twm_win->icon->otp : twm_win->otp; 937 assert(owl != NULL); 938 939 if(twm_win->winbox != NULL || twm_win->iswinbox) { 940 return; 941 } 942 943 owl->switching = !owl->switching; 944 945 OtpCheckConsistency(); 946} 947 948 949/* 950 * This is triggered as a result of a StackMode ConfigureRequest. We 951 * choose to interpret this as restacking relative to the base 952 * priorities, since all the alterations are EWMH-related, and those 953 * should probably override. 954 * 955 * XXX Or should they? Maybe we should alter until our effective is 956 * positioned as desired relative to their effective? This may also need 957 * revisiting if we grow alterations that aren't a result of EWMH stuff. 958 */ 959void OtpForcePlacement(TwmWindow *twm_win, int where, TwmWindow *other_win) 960{ 961 OtpWinList *owl = twm_win->otp; 962 OtpWinList *other_owl = other_win->otp; 963 964 assert(twm_win->otp != NULL); 965 assert(other_win->otp != NULL); 966 967 if(where == BottomIf) { 968 where = Below; 969 } 970 if(where != Below) { 971 where = Above; 972 } 973 974 /* remove the owl to change it */ 975 RemoveOwl(owl); 976 977 /* 978 * Base our priority base off that other win. Don't use PRI_CP since 979 * we shouldn't suddenly get its flags as well. 980 */ 981 owl->pri_base = other_owl->pri_base; 982 983 /* put the owl back into the list */ 984 if(where == Below) { 985 other_owl = other_owl->below; 986 } 987 InsertOwlAbove(owl, other_owl); 988 989 OtpCheckConsistency(); 990} 991 992 993static void ApplyPreferences(OtpPreferences *prefs, OtpWinList *owl) 994{ 995 int i; 996 TwmWindow *twm_win = owl->twm_win; 997 998 /* check PrioritySwitch */ 999 if(LookInList(prefs->switchingL, twm_win->name, &twm_win->class)) { 1000 owl->switching = !prefs->switching; 1001 } 1002 1003 /* check OnTopPriority */ 1004 for(i = 0; i <= OTP_MAX; i++) { 1005 if(LookInList(prefs->priorityL[i], 1006 twm_win->name, &twm_win->class)) { 1007 owl->pri_base = i; 1008 } 1009 } 1010} 1011 1012 1013/* 1014 * Reset stuff based on preferences; called during property changes if 1015 * AutoPriority set. 1016 */ 1017static void RecomputeOwlPrefs(OtpPreferences *prefs, OtpWinList *owl) 1018{ 1019 int old_pri; 1020 1021 old_pri = owl->pri_base; 1022 ApplyPreferences(prefs, owl); 1023 if(old_pri != owl->pri_base) { 1024 RemoveOwl(owl); 1025 InsertOwl(owl, Above); 1026 1027 /* 1028 * Stash flags if we don't have any yet, since we just changed 1029 * the priority. 1030 */ 1031 if(!owl->stashed_aflags) { 1032 OwlStashAflags(owl); 1033 } 1034 1035#ifdef EWMH 1036 /* Let other things know we did something with stacking */ 1037 EwmhSet_NET_WM_STATE(owl->twm_win, EWMH_STATE_ABOVE); 1038#endif 1039 } 1040} 1041 1042void OtpRecomputePrefs(TwmWindow *twm_win) 1043{ 1044 assert(twm_win->otp != NULL); 1045 1046 RecomputeOwlPrefs(Scr->OTP, twm_win->otp); 1047 if(twm_win->icon != NULL) { 1048 RecomputeOwlPrefs(Scr->IconOTP, twm_win->icon->otp); 1049 } 1050 1051 OtpCheckConsistency(); 1052} 1053 1054 1055static void free_OtpWinList(OtpWinList *owl) 1056{ 1057 assert(owl->above == NULL); 1058 assert(owl->below == NULL); 1059 free(owl); 1060} 1061 1062 1063void OtpRemove(TwmWindow *twm_win, WinType wintype) 1064{ 1065 OtpWinList **owlp; 1066 owlp = (wintype == IconWin) ? &twm_win->icon->otp : &twm_win->otp; 1067 1068 assert(*owlp != NULL); 1069 1070 RemoveOwl(*owlp); 1071 free_OtpWinList(*owlp); 1072 *owlp = NULL; 1073 1074 OtpCheckConsistency(); 1075} 1076 1077 1078static OtpWinList *new_OtpWinList(TwmWindow *twm_win, 1079 WinType wintype, 1080 bool switching, 1081 int priority) 1082{ 1083 OtpWinList *owl = malloc(sizeof(OtpWinList)); 1084 1085 owl->above = NULL; 1086 owl->below = NULL; 1087 owl->twm_win = twm_win; 1088 owl->type = wintype; 1089 owl->switching = switching; 1090 owl->pri_base = priority; 1091 owl->pri_aflags = 0; 1092 1093 /* 1094 * We never need to stash anything for icons, they don't persist 1095 * across restart anyway. So pretend we did stash already to 1096 * discourage other code from trying to stash. 1097 */ 1098 owl->stashed_aflags = (wintype == WinWin ? false : true); 1099 1100 return owl; 1101} 1102 1103static OtpWinList *AddNewOwl(TwmWindow *twm_win, WinType wintype, 1104 OtpWinList *parent) 1105{ 1106 OtpWinList *owl; 1107 OtpPreferences *prefs = (wintype == IconWin) ? Scr->IconOTP : Scr->OTP; 1108 1109 /* make the new owl */ 1110 owl = new_OtpWinList(twm_win, wintype, 1111 prefs->switching, prefs->priority); 1112 1113 /* inherit the default attributes from the parent window if appropriate */ 1114 if(parent != NULL) { 1115 PRI_CP(parent, owl); 1116 owl->switching = parent->switching; 1117 } 1118 1119 /* now see if the preferences have something to say */ 1120 if(!(parent != NULL && twm_win->istransient)) { 1121 ApplyPreferences(prefs, owl); 1122 } 1123 1124#ifdef EWMH 1125 /* If nothing came in, EWMH might have something to say */ 1126 if(owl->pri_base == 0) { 1127 owl->pri_base = EwmhGetInitPriority(twm_win) + OTP_ZERO; 1128 } 1129#endif 1130 1131 /* 1132 * Initialize flags. Right now, the only stashed flags are related 1133 * to EWMH requests, so in a sense this whole thing could be dropped 1134 * under #ifdef. But I'll assume that might not always be the case, 1135 * so for now the !(EWMH) case is just a twisty noop. 1136 */ 1137 { 1138 bool gotflags = false; 1139 unsigned aflags = 0, fromstash = 0; 1140 1141 aflags = OwlGetStashedAflags(owl, &gotflags); 1142 1143#ifdef EWMH 1144 fromstash = (OTP_AFLAG_ABOVE | OTP_AFLAG_BELOW); 1145#endif 1146 1147 if(gotflags) { 1148 /* 1149 * Got stashed OTP flags; use 'em. Explicitly mask in only 1150 * the flags we're caring about; the others aren't telling us 1151 * info we need to persist. 1152 */ 1153 aflags &= fromstash; 1154 } 1155 1156#ifdef EWMH 1157 /* FULLSCREEN we get from the normal EWMH prop no matter what */ 1158 if(twm_win->ewmhFlags & EWMH_STATE_FULLSCREEN) { 1159 aflags |= OTP_AFLAG_FULLSCREEN; 1160 } 1161 1162 if(!gotflags) { 1163 /* Nothing from OTP about above/below; check EWMH */ 1164 aflags = 0; 1165 if(twm_win->ewmhFlags & EWMH_STATE_ABOVE) { 1166 aflags |= OTP_AFLAG_ABOVE; 1167 } 1168 if(twm_win->ewmhFlags & EWMH_STATE_BELOW) { 1169 aflags |= OTP_AFLAG_BELOW; 1170 } 1171 } 1172#endif // EWMH 1173 1174 /* Set whatever we figured */ 1175 owl->pri_aflags |= aflags; 1176 owl->stashed_aflags = gotflags; 1177 1178 /* If we set a priority or flags, we should stash away flags */ 1179 if((PRI(owl) != OTP_ZERO || owl->pri_aflags != 0) 1180 && !owl->stashed_aflags) { 1181 OwlStashAflags(owl); 1182 } 1183 } 1184 1185 /* finally put the window where it should go */ 1186 InsertOwl(owl, Above); 1187 1188 return owl; 1189} 1190 1191void OtpAdd(TwmWindow *twm_win, WinType wintype) 1192{ 1193 TwmWindow *other_win; 1194 OtpWinList *parent = NULL; 1195 OtpWinList **owlp; 1196 owlp = (wintype == IconWin) ? &twm_win->icon->otp : &twm_win->otp; 1197 1198 assert(*owlp == NULL); 1199 1200 /* windows in boxes *must* inherit priority from the box */ 1201 if(twm_win->winbox) { 1202 parent = twm_win->winbox->twmwin->otp; 1203 parent->switching = false; 1204 } 1205 /* in case it's a transient, find the parent */ 1206 else if(wintype == WinWin && (twm_win->istransient 1207 || !isGroupLeader(twm_win))) { 1208 other_win = Scr->FirstWindow; 1209 while(other_win != NULL 1210 && !isTransientOf(twm_win, other_win) 1211 && !isGroupLeaderOf(other_win, twm_win)) { 1212 other_win = other_win->next; 1213 } 1214 if(other_win != NULL) { 1215 parent = other_win->otp; 1216 } 1217 } 1218 1219 /* make the new owl */ 1220 *owlp = AddNewOwl(twm_win, wintype, parent); 1221 1222 assert(*owlp != NULL); 1223 OtpCheckConsistency(); 1224} 1225 1226void OtpReassignIcon(TwmWindow *twm_win, Icon *old_icon) 1227{ 1228 if(old_icon != NULL) { 1229 /* Transfer OWL to new Icon */ 1230 Icon *new_icon = twm_win->icon; 1231 if(new_icon != NULL) { 1232 new_icon->otp = old_icon->otp; 1233 old_icon->otp = NULL; 1234 } 1235 } 1236 else { 1237 /* Create a new OWL for this Icon */ 1238 OtpAdd(twm_win, IconWin); 1239 } 1240} 1241 1242void OtpFreeIcon(TwmWindow *twm_win) 1243{ 1244 /* Remove the icon's OWL, if any */ 1245 Icon *cur_icon = twm_win->icon; 1246 if(cur_icon != NULL) { 1247 OtpRemove(twm_win, IconWin); 1248 } 1249} 1250 1251name_list **OtpScrSwitchingL(ScreenInfo *scr, WinType wintype) 1252{ 1253 OtpPreferences *prefs = (wintype == IconWin) ? scr->IconOTP : scr->OTP; 1254 1255 assert(prefs != NULL); 1256 1257 return &(prefs->switchingL); 1258} 1259 1260 1261void OtpScrSetSwitching(ScreenInfo *scr, WinType wintype, bool switching) 1262{ 1263#ifndef NDEBUG 1264 OtpPreferences *prefs = (wintype == IconWin) ? scr->IconOTP : scr->OTP; 1265 1266 assert(prefs != NULL); 1267#endif 1268 1269 scr->OTP->switching = switching; 1270} 1271 1272 1273void OtpScrSetZero(ScreenInfo *scr, WinType wintype, int priority) 1274{ 1275 OtpPreferences *prefs = (wintype == IconWin) ? scr->IconOTP : scr->OTP; 1276 1277 assert(prefs != NULL); 1278 1279 if(ABS(priority) > OTP_ZERO) { 1280 fprintf(stderr, "invalid OnTopPriority value: %d\n", priority); 1281 return; 1282 } 1283 1284 prefs->priority = priority + OTP_ZERO; 1285} 1286 1287 1288name_list **OtpScrPriorityL(ScreenInfo *scr, WinType wintype, int priority) 1289{ 1290 OtpPreferences *prefs = (wintype == IconWin) ? scr->IconOTP : scr->OTP; 1291 1292 assert(prefs != NULL); 1293 1294 if(ABS(priority) > OTP_ZERO) { 1295 fprintf(stderr, "invalid OnTopPriority value: %d\n", priority); 1296 priority = 0; 1297 } 1298 return &(prefs->priorityL[priority + OTP_ZERO]); 1299} 1300 1301 1302static OtpPreferences *new_OtpPreferences(void) 1303{ 1304 OtpPreferences *pref = malloc(sizeof(OtpPreferences)); 1305 int i; 1306 1307 /* initialize default values */ 1308 for(i = 0; i <= OTP_MAX; i++) { 1309 pref->priorityL[i] = NULL; 1310 } 1311 pref->priority = OTP_ZERO; 1312 pref->switchingL = NULL; 1313 pref->switching = false; 1314 1315 return pref; 1316} 1317 1318static void free_OtpPreferences(OtpPreferences *pref) 1319{ 1320 int i; 1321 1322 FreeList(&pref->switchingL); 1323 for(i = 0; i <= OTP_MAX; i++) { 1324 FreeList(&pref->priorityL[i]); 1325 } 1326 1327 free(pref); 1328} 1329 1330void OtpScrInitData(ScreenInfo *scr) 1331{ 1332 if(scr->OTP != NULL) { 1333 free_OtpPreferences(scr->OTP); 1334 } 1335 if(scr->IconOTP != NULL) { 1336 free_OtpPreferences(scr->IconOTP); 1337 } 1338 scr->OTP = new_OtpPreferences(); 1339 scr->IconOTP = new_OtpPreferences(); 1340} 1341 1342int ReparentWindow(Display *display, TwmWindow *twm_win, WinType wintype, 1343 Window parent, int x, int y) 1344{ 1345 int result; 1346 OtpWinList *owl = (wintype == IconWin) ? twm_win->icon->otp : twm_win->otp; 1347 OtpWinList *other = owl->below; 1348 assert(owl != NULL); 1349 1350 DPRINTF((stderr, "ReparentWindow: w=%x type=%d\n", 1351 (unsigned int)WindowOfOwl(owl), wintype)); 1352 result = XReparentWindow(display, WindowOfOwl(owl), parent, x, y); 1353 /* The raise was already done by XReparentWindow, so this call 1354 just re-places the window at the right spot in the list 1355 and enforces priority settings. */ 1356 RemoveOwl(owl); 1357 InsertOwlAbove(owl, other); 1358 OtpCheckConsistency(); 1359 return result; 1360} 1361 1362void 1363ReparentWindowAndIcon(Display *display, TwmWindow *twm_win, 1364 Window parent, int win_x, int win_y, 1365 int icon_x, int icon_y) 1366{ 1367 OtpWinList *win_owl = twm_win->otp; 1368 assert(twm_win->icon != NULL); 1369 OtpWinList *icon_owl = twm_win->icon->otp; 1370 assert(win_owl != NULL); 1371 assert(icon_owl != NULL); 1372 OtpWinList *below_win = win_owl->below; 1373 OtpWinList *below_icon = icon_owl->below; 1374 1375 DPRINTF((stderr, "ReparentWindowAndIcon %x\n", (unsigned int)twm_win->frame)); 1376 XReparentWindow(display, twm_win->frame, parent, win_x, win_y); 1377 XReparentWindow(display, twm_win->icon->w, parent, icon_x, icon_y); 1378 /* The raise was already done by XReparentWindow, so this call 1379 just re-places the window at the right spot in the list 1380 and enforces priority settings. */ 1381 RemoveOwl(win_owl); 1382 RemoveOwl(icon_owl); 1383 if(below_win != icon_owl) { 1384 /* 1385 * Only insert the window above something if it isn't the icon, 1386 * because that isn't back yet. 1387 */ 1388 InsertOwlAbove(win_owl, below_win); 1389 InsertOwlAbove(icon_owl, below_icon); 1390 } 1391 else { 1392 /* In such a case, do it in the opposite order. */ 1393 InsertOwlAbove(icon_owl, below_icon); 1394 InsertOwlAbove(win_owl, below_win); 1395 } 1396 OtpCheckConsistency(); 1397 return; 1398} 1399 1400/* Iterators. */ 1401TwmWindow *OtpBottomWin() 1402{ 1403 OtpWinList *owl = Scr->bottomOwl; 1404 while(owl && owl->type != WinWin) { 1405 owl = owl->above; 1406 } 1407 return owl ? owl->twm_win : NULL; 1408} 1409 1410TwmWindow *OtpTopWin() 1411{ 1412 OtpWinList *owl = Scr->bottomOwl, *top = NULL; 1413 while(owl) { 1414 if(owl->type == WinWin) { 1415 top = owl; 1416 } 1417 owl = owl->above; 1418 } 1419 return top ? top->twm_win : NULL; 1420} 1421 1422TwmWindow *OtpNextWinUp(TwmWindow *twm_win) 1423{ 1424 OtpWinList *owl = twm_win->otp->above; 1425 while(owl && owl->type != WinWin) { 1426 owl = owl->above; 1427 } 1428 return owl ? owl->twm_win : NULL; 1429} 1430 1431TwmWindow *OtpNextWinDown(TwmWindow *twm_win) 1432{ 1433 OtpWinList *owl = twm_win->otp->below; 1434 while(owl && owl->type != WinWin) { 1435 owl = owl->below; 1436 } 1437 return owl ? owl->twm_win : NULL; 1438} 1439 1440 1441/* 1442 * Stuff for messing with pri_aflags 1443 */ 1444/* Set the masked bits to exactly what's given */ 1445void 1446OtpSetAflagMask(TwmWindow *twm_win, unsigned mask, unsigned setto) 1447{ 1448 assert(twm_win != NULL); 1449 assert(twm_win->otp != NULL); 1450 OwlSetAflagMask(twm_win->otp, mask, setto); 1451} 1452 1453static void 1454OwlSetAflagMask(OtpWinList *owl, unsigned mask, unsigned setto) 1455{ 1456 assert(owl != NULL); 1457 1458 owl->pri_aflags &= ~mask; 1459 owl->pri_aflags |= (setto & mask); 1460 OwlStashAflags(owl); 1461} 1462 1463/* Set/clear individual ones */ 1464void 1465OtpSetAflag(TwmWindow *twm_win, unsigned flag) 1466{ 1467 assert(twm_win != NULL); 1468 assert(twm_win->otp != NULL); 1469 1470 OwlSetAflag(twm_win->otp, flag); 1471} 1472 1473static void 1474OwlSetAflag(OtpWinList *owl, unsigned flag) 1475{ 1476 assert(owl != NULL); 1477 1478 owl->pri_aflags |= flag; 1479 OwlStashAflags(owl); 1480} 1481 1482void 1483OtpClearAflag(TwmWindow *twm_win, unsigned flag) 1484{ 1485 assert(twm_win != NULL); 1486 assert(twm_win->otp != NULL); 1487 1488 OwlClearAflag(twm_win->otp, flag); 1489} 1490 1491static void 1492OwlClearAflag(OtpWinList *owl, unsigned flag) 1493{ 1494 assert(owl != NULL); 1495 1496 owl->pri_aflags &= ~flag; 1497 OwlStashAflags(owl); 1498} 1499 1500/* 1501 * Stash up flags in a property. We use this to keep track of whether we 1502 * have above/below flags set in the OTP info, so we can know what to set 1503 * when we restart. Otherwise we can't tell whether stuff like EWMH 1504 * _NET_WM_STATE flags are saying 'above' because the above flag got set 1505 * at some point, or whether other OTP config happens to have already 1506 * raised it. 1507 */ 1508void 1509OtpStashAflagsFirstTime(TwmWindow *twm_win) 1510{ 1511 if(!twm_win->otp->stashed_aflags) { 1512 OwlStashAflags(twm_win->otp); 1513 } 1514} 1515 1516static void 1517OwlStashAflags(OtpWinList *owl) 1518{ 1519 unsigned long of_prop = owl->pri_aflags; 1520 1521 /* Only "real" windows need stashed flags */ 1522 if(owl->type != WinWin) { 1523 return; 1524 } 1525 1526 XChangeProperty(dpy, owl->twm_win->w, XA_CTWM_OTP_AFLAGS, XA_INTEGER, 1527 32, PropModeReplace, (unsigned char *)&of_prop, 1); 1528 1529 owl->stashed_aflags = true; 1530} 1531 1532static unsigned 1533OwlGetStashedAflags(OtpWinList *owl, bool *gotit) 1534{ 1535 /* Lotta dummy args */ 1536 int ret; 1537 Atom act_type; 1538 int d_fmt; 1539 unsigned long nitems, d_after; 1540 unsigned long aflags, *aflags_p; 1541 1542 /* Only on real windows */ 1543 if(owl->type != WinWin) { 1544 *gotit = false; 1545 return 0; 1546 } 1547 1548 ret = XGetWindowProperty(dpy, owl->twm_win->w, XA_CTWM_OTP_AFLAGS, 0, 1, 1549 False, XA_INTEGER, &act_type, &d_fmt, &nitems, 1550 &d_after, (unsigned char **)&aflags_p); 1551 if(ret == Success && act_type == XA_INTEGER && aflags_p != NULL) { 1552 aflags = *aflags_p; 1553 XFree(aflags_p); 1554 *gotit = true; 1555 } 1556 else { 1557 *gotit = false; 1558 aflags = 0; 1559 } 1560 1561 return aflags; 1562} 1563 1564 1565/* 1566 * Figure where a window should be stacked based on the current world, 1567 * and move it there. This function pretty much assumes it's not already 1568 * there; callers should generally be figuring that out before calling 1569 * this. 1570 */ 1571void 1572OtpRestackWindow(TwmWindow *twm_win) 1573{ 1574 OtpWinList *owl = twm_win->otp; 1575 1576 RemoveOwl(owl); 1577 InsertOwl(owl, Above); 1578 OtpCheckConsistency(); 1579} 1580 1581 1582 1583/** 1584 * Focus/unfocus backend. This is used on windows whose stacking is 1585 * focus-dependent (e.g., EWMH fullscreen), to move them and their 1586 * transients around. For these windows, getting/losing focus is 1587 * practically the same as a f.setpriority, except it's on the calculated 1588 * rather than the base parts. And it's hard to re-use our existing 1589 * functions to do it because we have to move Scr->Focus before the main 1590 * window changes, but then it's too late to see where all the transients 1591 * were. 1592 * 1593 * There are a number of unpleasant assumptions in here relating to where 1594 * the transients are, and IWBNI we could be smarter and quicker about 1595 * dealing with them. But this gets us past the simple to cause 1596 * assertion failures, anyway... 1597 */ 1598static void 1599OtpFocusWindowBE(TwmWindow *twm_win, int oldprio) 1600{ 1601 OtpWinList *owl = twm_win->otp; 1602 1603 // This one comes off the list, and goes back in its new place. 1604 RemoveOwl(owl); 1605 InsertOwl(owl, Above); 1606 1607 // Now root around for any transients of it, and 1608 // nudge them into the new location. The whole Above/Below thing is 1609 // kinda a heavy-handed guess, but... 1610 // 1611 // This is nearly a reimplementation of TryToMoveTransientsOfTo(), 1612 // but the assumption that we can find the transients by starting 1613 // from where the old priority was in the list turns out to be deeply 1614 // broken. So just walk the whole thing. Which isn't ideal, but... 1615 // 1616 // We also need to do loop detection, since otherwise we'll get stuck 1617 // when a window has multiple transients to move around. Since we 1618 // read from the bottom up, if a window is moving up the stack, then 1619 // its transients move up, and we run into them again and again. 1620 // 1621 // XXX It should not be this freakin' hard to find a window's 1622 // transients. We should fix that more globally. 1623 1624 // XXX Let's just get a friggin' vector implementation already... 1625 size_t tlsz = 32; // Should hardly ever be too small 1626 size_t tlused = 0; 1627 OtpWinList **tlst = calloc(tlsz, sizeof(OtpWinList *)); 1628 if(tlst == NULL) { 1629 fprintf(stderr, "%s(): realloc() failed\n", __func__); 1630 abort(); 1631 } 1632 1633 // Loop through and find them all 1634 OtpWinList *trans = Scr->bottomOwl; 1635 while((trans != NULL)) { 1636 // Gotta pre-stash, since we're sometimes about to move trans. 1637 OtpWinList *next = trans->above; 1638 1639 if((trans->type == WinWin) 1640 && isTransientOf(trans->twm_win, twm_win)) { 1641 // Got one, stash it 1642 tlst[tlused++] = trans; 1643 1644 // Grow? 1645 if(tlused == tlsz) { 1646 tlsz *= 2; 1647 OtpWinList **tln = realloc(tlst, (tlsz * sizeof(OtpWinList *))); 1648 if(tln == NULL) { 1649 fprintf(stderr, "%s(): realloc() failed\n", __func__); 1650 abort(); 1651 } 1652 tlst = tln; 1653 } 1654 } 1655 1656 // And onward 1657 trans = next; 1658 } 1659 1660 1661 // Now loop over them and shuffle them up 1662 for(int i = 0 ; i < tlused ; i++) { 1663 RemoveOwl(tlst[i]); 1664 InsertOwl(tlst[i], Above); 1665 } 1666 1667 free(tlst); 1668 1669 OtpCheckConsistency(); 1670} 1671 1672/** 1673 * Unfocus a window. This needs to know internals of OTP because of 1674 * focus-dependent stacking of it and its transients. 1675 */ 1676void 1677OtpUnfocusWindow(TwmWindow *twm_win) 1678{ 1679 // Stash where it currently appears to be. We assume all its 1680 // transients currently have the same effective priority. See also 1681 // TryToMoveTransientsOfTo() which makes the same assumption. I'm 1682 // not sure that's entirely warranted... 1683 int oldprio = PRI(twm_win->otp); 1684 1685 // Now tell ourselves it's unfocused 1686 assert(Scr->Focus == twm_win); 1687 Scr->Focus = NULL; 1688 1689 // And do the work 1690 OtpFocusWindowBE(twm_win, oldprio); 1691} 1692 1693/** 1694 * Focus a window. This needs to know internals of OTP because of 1695 * focus-dependent stacking of it and its transients. 1696 */ 1697void 1698OtpFocusWindow(TwmWindow *twm_win) 1699{ 1700 // X-ref OtoUnfocusWindow() comments. 1701 int oldprio = PRI(twm_win->otp); 1702 1703 assert(Scr->Focus != twm_win); 1704 Scr->Focus = twm_win; 1705 1706 OtpFocusWindowBE(twm_win, oldprio); 1707} 1708 1709 1710 1711/* 1712 * Calculating effective priority. Take the base priority (what gets 1713 * set/altered by various OTP config and functions), and then tack on 1714 * whatever alterations more ephemeral things might apply. This 1715 * currently pretty much means EWMH bits. 1716 */ 1717int 1718OtpEffectiveDisplayPriority(TwmWindow *twm_win) 1719{ 1720 assert(twm_win != NULL); 1721 assert(twm_win->otp != NULL); 1722 1723 return(OwlEffectivePriority(twm_win->otp) - OTP_ZERO); 1724} 1725 1726int 1727OtpEffectivePriority(TwmWindow *twm_win) 1728{ 1729 assert(twm_win != NULL); 1730 assert(twm_win->otp != NULL); 1731 1732 return OwlEffectivePriority(twm_win->otp); 1733} 1734 1735static int 1736OwlEffectivePriority(OtpWinList *owl) 1737{ 1738 int pri; 1739 1740 assert(owl != NULL); 1741 1742 pri = owl->pri_base; 1743 1744#ifdef EWMH 1745 /* ABOVE/BELOW states shift a bit relative to the base */ 1746 if(owl->pri_aflags & OTP_AFLAG_ABOVE) { 1747 pri += EWMH_PRI_ABOVE; 1748 } 1749 if(owl->pri_aflags & OTP_AFLAG_BELOW) { 1750 pri -= EWMH_PRI_ABOVE; 1751 } 1752 1753 /* 1754 * Special magic: EWMH says that _BELOW + _DOCK = (just _BELOW). 1755 * So if both are set, and its base is where we'd expect just a _DOCK 1756 * to be, try cancelling that out. 1757 */ 1758 { 1759 EwmhWindowType ewt = owl->twm_win->ewmhWindowType; 1760 if((owl->pri_aflags & OTP_AFLAG_BELOW) && (ewt == wt_Dock) && 1761 (owl->pri_base == EWMH_PRI_DOCK + OTP_ZERO)) { 1762 pri -= EWMH_PRI_DOCK; 1763 } 1764 } 1765 1766 /* 1767 * If FULLSCREEN and focused, jam to (nearly; let the user still win 1768 * if they try) the top. We also need to handle transients; they 1769 * might not have focus, but still need to be on top of the window 1770 * they're coming up transient for, or else they'll be hidden 1771 * forever. 1772 */ 1773 if(owl->pri_aflags & OTP_AFLAG_FULLSCREEN) { 1774 if(Scr->Focus == owl->twm_win) { 1775 // It's focused, shift it up 1776 pri = EWMH_PRI_FULLSCREEN + OTP_ZERO; 1777 } 1778 else if(owl->twm_win->istransient) { 1779 // It's a transient of something else; if that something else 1780 // has the fullscreen/focus combo, we should pop this up top 1781 // too. Technically, we should perhaps test whether its 1782 // parent is also OTP_AFLAG_FULLSCREEN, but if the transient 1783 // has it, the parent probably does too. Worry about that 1784 // detail if it ever becomes a problem. 1785 TwmWindow *parent = GetTwmWindow(owl->twm_win->transientfor); 1786 if(Scr->Focus == parent) { 1787 // Shift this up so we stay on top 1788 pri = EWMH_PRI_FULLSCREEN + OTP_ZERO; 1789 } 1790 } 1791 } 1792#endif 1793 1794 /* Constrain */ 1795 pri = MAX(pri, 0); 1796 pri = MIN(pri, OTP_MAX); 1797 1798 return pri; 1799} 1800 1801 1802/* 1803 * Does the priority of a window depend on its focus state? External 1804 * code needs to know, to know when it might need restacking. 1805 */ 1806bool 1807OtpIsFocusDependent(TwmWindow *twm_win) 1808{ 1809 assert(twm_win != NULL); 1810 assert(twm_win->otp != NULL); 1811 1812#ifdef EWMH 1813 /* 1814 * EWMH says _FULLSCREEN and focused windows get shoved to the top; 1815 * this implies that _FULLSCREEN and _not_ focused don't. So if the 1816 * focus is changing, that means we may need to restack. 1817 */ 1818 if(twm_win->otp->pri_aflags & OTP_AFLAG_FULLSCREEN) { 1819 return true; 1820 } 1821#endif 1822 1823 return false; 1824} 1825