1/* 2 * Occupation handling bits 3 * 4 * This is in fact pretty tightly tied and extremely similar to the 5 * handling of the WorkSpaceManager in workmgr.c, and used to be there. 6 * It makes sense to consider them together (and indeed, many of the 7 * configs that affect how this works are really WorkSpaceManager* or 8 * WMgr* commands. But having them crammed together in one file is 9 * unwieldy. 10 */ 11 12#include "ctwm.h" 13 14#include <stdio.h> 15#include <string.h> 16#include <stdlib.h> 17 18#include <X11/Xatom.h> 19 20#include "add_window.h" 21#include "ctwm_atoms.h" 22#include "drawing.h" 23#include "events.h" 24#include "iconmgr.h" 25#include "list.h" 26#include "screen.h" 27#include "occupation.h" 28#include "otp.h" 29#include "util.h" 30#include "vscreen.h" 31#include "win_iconify.h" 32#include "win_regions.h" 33#include "win_utils.h" 34#include "workspace_manager.h" 35#include "workspace_utils.h" 36 37 38static int GetMaskFromResource(TwmWindow *win, char *res); 39static char *mk_nullsep_string(const char *prop, int len); 40 41static bool CanChangeOccupation(TwmWindow **twm_winp); 42 43int fullOccupation = 0; 44 45/* 46 * The window whose occupation is currently being manipulated. 47 * 48 * XXX Should probably be static, but currently needed in 49 * WMapRemoveWindow(). Revisit. 50 */ 51TwmWindow *occupyWin = NULL; 52 53 54/* XXX Share with captive.c? */ 55static XrmOptionDescRec table [] = { 56 {"-xrm", NULL, XrmoptionResArg, (XPointer) NULL}, 57}; 58 59 60 61 62/* 63 **************************************************************** 64 * 65 * First, funcs related to setting and changing a window's occupation. 66 * 67 **************************************************************** 68 */ 69 70 71/* 72 * Setup the occupation of a TwmWindow. Called as part of the 73 * AddWindow() process. 74 * 75 * XXX The logic flow in this is kinda weird, and it's not at all clear 76 * to what extent it's really doing the right on on what should override 77 * what, or which things should expand/contract on others... 78 */ 79void 80SetupOccupation(TwmWindow *twm_win, int occupation_hint) 81{ 82 char **cliargv = NULL; 83 int cliargc; 84 WorkSpace *ws; 85 86 /* If there aren't any config'd workspaces, there's only 0 */ 87 if(! Scr->workSpaceManagerActive) { 88 twm_win->occupation = 1 << 0; /* occupy workspace #0 */ 89 /* more?... */ 90 91 return; 92 } 93 94 /* Workspace manager window doesn't get futzed with */ 95 if(twm_win->iswspmgr) { 96 return; 97 } 98 99 /*twm_win->occupation = twm_win->iswinbox ? fullOccupation : 0;*/ 100 twm_win->occupation = 0; 101 102 /* Specified in any Occupy{} config params? */ 103 for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) { 104 if(LookInList(ws->clientlist, twm_win->name, &twm_win->class)) { 105 twm_win->occupation |= 1 << ws->number; 106 } 107 } 108 109 /* OccupyAll{} */ 110 if(LookInList(Scr->OccupyAll, twm_win->name, &twm_win->class)) { 111 twm_win->occupation = fullOccupation; 112 } 113 114 /* See if it specified in -xrm stuff */ 115 if(XGetCommand(dpy, twm_win->w, &cliargv, &cliargc)) { 116 Bool status; 117 char *str_type; 118 XrmValue value; 119 XrmDatabase db = NULL; 120 121 XrmParseCommand(&db, table, 1, "ctwm", &cliargc, cliargv); 122 XFreeStringList(cliargv); 123 status = XrmGetResource(db, "ctwm.workspace", "Ctwm.Workspace", 124 &str_type, &value); 125 if((status == True) && (value.size != 0)) { 126 /* Copy the value.addr because it's in XRM memory not ours */ 127 char wrkSpcList[512]; 128 safe_strncpy(wrkSpcList, value.addr, MIN(value.size, 512)); 129 130 twm_win->occupation = GetMaskFromResource(twm_win, wrkSpcList); 131 } 132 XrmDestroyDatabase(db); 133 } 134 135 /* Does it have a property telling us */ 136 if(RestartPreviousState) { 137 Atom actual_type; 138 int actual_format; 139 unsigned long nitems, bytesafter; 140 unsigned char *prop; 141 142 if(XGetWindowProperty(dpy, twm_win->w, XA_WM_OCCUPATION, 0L, 2500, False, 143 XA_STRING, &actual_type, &actual_format, &nitems, 144 &bytesafter, &prop) == Success) { 145 if(nitems != 0) { 146 twm_win->occupation = GetMaskFromProperty(prop, nitems); 147 XFree(prop); 148 } 149 } 150 } 151 152#ifdef EWMH 153 /* Maybe EWMH has something to tell us? */ 154 if(twm_win->occupation == 0) { 155 twm_win->occupation = EwmhGetOccupation(twm_win); 156 } 157#endif /* EWMH */ 158 159 /* Icon Managers shouldn't get altered */ 160 /* XXX Should this be up near the top? */ 161 if(twm_win->isiconmgr) { 162 return; /* someone tried to modify occupation of icon managers */ 163 } 164 165 166 /* 167 * Transient-ish things go with their parents unless 168 * TransientHasOccupation set in the config. 169 */ 170 if(! Scr->TransientHasOccupation) { 171 TwmWindow *t; 172 173 if(twm_win->istransient) { 174 t = GetTwmWindow(twm_win->transientfor); 175 if(t != NULL) { 176 twm_win->occupation = t->occupation; 177 } 178 } 179 else if(twm_win->group != 0) { 180 t = GetTwmWindow(twm_win->group); 181 if(t != NULL) { 182 twm_win->occupation = t->occupation; 183 } 184 } 185 } 186 187 188 /* If we were told something specific, go with that */ 189 if(occupation_hint != 0) { 190 twm_win->occupation = occupation_hint; 191 } 192 193 /* If it's apparently-nonsensical, put it in its vs's workspace */ 194 if((twm_win->occupation & fullOccupation) == 0) { 195 twm_win->occupation = 1 << twm_win->vs->wsw->currentwspc->number; 196 } 197 198 /* 199 * If the occupation would not show it in the current vscreen, 200 * make it vanish. 201 * 202 * If it could be shown in one of the other vscreens, change the vscreen. 203 */ 204 if(!OCCUPY(twm_win, twm_win->vs->wsw->currentwspc)) { 205 206 twm_win->vs = NULL; 207 208#ifdef VSCREEN 209 if(Scr->numVscreens > 1) { 210 VirtualScreen *vs; 211 for(vs = Scr->vScreenList; vs != NULL; vs = vs->next) { 212 if(OCCUPY(twm_win, vs->wsw->currentwspc)) { 213 twm_win->vs = vs; 214 twm_win->parent_vs = vs; 215 break; 216 } 217 } 218 } 219#endif 220 } 221 222 223 /* Set the property for the occupation */ 224 { 225 long eventMask; 226 char *wsstr; 227 int len; 228 229 /* Ignore the PropertyChange we're about to do */ 230 if((eventMask = mask_out_event(twm_win->w, PropertyChangeMask)) < 0) { 231 /* Window is horked, not much we can do */ 232 return; 233 } 234 235 /* Set the property for the occupation */ 236 len = GetPropertyFromMask(twm_win->occupation, &wsstr); 237 XChangeProperty(dpy, twm_win->w, XA_WM_OCCUPATION, XA_STRING, 8, 238 PropModeReplace, (unsigned char *) wsstr, len); 239 free(wsstr); 240 241#ifdef EWMH 242 EwmhSet_NET_WM_DESKTOP(twm_win); 243#endif 244 245 /* Restore event mask */ 246 restore_mask(twm_win->w, eventMask); 247 } 248 249 /* Set WM_STATE prop */ 250 { 251 int state = NormalState; 252 Window icon; 253 254 if(!(RestartPreviousState 255 && GetWMState(twm_win->w, &state, &icon) 256 && (state == NormalState || state == IconicState 257 || state == InactiveState))) { 258 if(twm_win->wmhints->flags & StateHint) { 259 state = twm_win->wmhints->initial_state; 260 } 261 } 262 if(visible(twm_win)) { 263 if(state == InactiveState) { 264 SetMapStateProp(twm_win, NormalState); 265 } 266 } 267 else { 268 if(state == NormalState) { 269 SetMapStateProp(twm_win, InactiveState); 270 } 271 } 272 } 273} 274 275 276/* 277 * Make sure a window is marked in a given workspace. f.addtoworkspace. 278 * Also gets called as part of the process of mapping a window; if we're 279 * mapping it here, it should know that it's here. And Xinerama magic 280 * moves. 281 */ 282void 283AddToWorkSpace(char *wname, TwmWindow *twm_win) 284{ 285 WorkSpace *ws; 286 int newoccupation; 287 288 if(!CanChangeOccupation(&twm_win)) { 289 return; 290 } 291 ws = GetWorkspace(wname); 292 if(!ws) { 293 return; 294 } 295 296 if(twm_win->occupation & (1 << ws->number)) { 297 return; 298 } 299 newoccupation = twm_win->occupation | (1 << ws->number); 300 ChangeOccupation(twm_win, newoccupation); 301} 302 303 304/* 305 * Converse of the above. f.removefromworkspace, also called from 306 * Xinerama-related magic. 307 */ 308void 309RemoveFromWorkSpace(char *wname, TwmWindow *twm_win) 310{ 311 WorkSpace *ws; 312 int newoccupation; 313 314 if(!CanChangeOccupation(&twm_win)) { 315 return; 316 } 317 ws = GetWorkspace(wname); 318 if(!ws) { 319 return; 320 } 321 322 newoccupation = twm_win->occupation & ~(1 << ws->number); 323 if(!newoccupation) { 324 return; 325 } 326 ChangeOccupation(twm_win, newoccupation); 327} 328 329 330/* f.toggleoccupation - flip setting for [current] workspace */ 331void 332ToggleOccupation(char *wname, TwmWindow *twm_win) 333{ 334 WorkSpace *ws; 335 int newoccupation; 336 337 if(!CanChangeOccupation(&twm_win)) { 338 return; 339 } 340 ws = GetWorkspace(wname); 341 if(!ws) { 342 return; 343 } 344 345 newoccupation = twm_win->occupation ^ (1 << ws->number); 346 if(!newoccupation) { 347 /* Don't allow de-occupying _every_ ws */ 348 return; 349 } 350 ChangeOccupation(twm_win, newoccupation); 351} 352 353 354/* f.movetonextworkspace */ 355void 356MoveToNextWorkSpace(VirtualScreen *vs, TwmWindow *twm_win) 357{ 358 WorkSpace *wlist1, *wlist2; 359 int newoccupation; 360 361 if(!CanChangeOccupation(&twm_win)) { 362 return; 363 } 364 365 wlist1 = vs->wsw->currentwspc; 366 wlist2 = wlist1->next; 367 wlist2 = wlist2 ? wlist2 : Scr->workSpaceMgr.workSpaceList; 368 369 /* Out of (here), into (here+1) */ 370 newoccupation = (twm_win->occupation ^ (1 << wlist1->number)) 371 | (1 << wlist2->number); 372 ChangeOccupation(twm_win, newoccupation); 373} 374 375 376/* f.movetonextworkspaceandfollow */ 377void 378MoveToNextWorkSpaceAndFollow(VirtualScreen *vs, TwmWindow *twm_win) 379{ 380 if(!CanChangeOccupation(&twm_win)) { 381 return; 382 } 383 384 MoveToNextWorkSpace(vs, twm_win); 385 GotoNextWorkSpace(vs); 386#if 0 387 OtpRaise(twm_win, WinWin); /* XXX really do this? */ 388#endif 389} 390 391 392/* f.movetoprevworkspaceand */ 393void 394MoveToPrevWorkSpace(VirtualScreen *vs, TwmWindow *twm_win) 395{ 396 WorkSpace *wlist1, *wlist2; 397 int newoccupation; 398 399 if(!CanChangeOccupation(&twm_win)) { 400 return; 401 } 402 403 wlist1 = Scr->workSpaceMgr.workSpaceList; 404 wlist2 = vs->wsw->currentwspc; 405 if(wlist1 == NULL) { 406 return; 407 } 408 409 while(wlist1->next != wlist2 && wlist1->next != NULL) { 410 wlist1 = wlist1->next; 411 } 412 413 /* Out of (here), into (here-1) */ 414 newoccupation = (twm_win->occupation ^ (1 << wlist2->number)) 415 | (1 << wlist1->number); 416 ChangeOccupation(twm_win, newoccupation); 417} 418 419 420/* f.movetoprevworkspaceandfollow */ 421void 422MoveToPrevWorkSpaceAndFollow(VirtualScreen *vs, TwmWindow *twm_win) 423{ 424 if(!CanChangeOccupation(&twm_win)) { 425 return; 426 } 427 428 MoveToPrevWorkSpace(vs, twm_win); 429 GotoPrevWorkSpace(vs); 430#if 0 431 OtpRaise(twm_win, WinWin); /* XXX really do this? */ 432#endif 433} 434 435 436/* 437 * Set the occupation based on the window name. This is called if 438 * AutoOccupy is set, when we get a notification about a window name 439 * change. 440 */ 441void 442WmgrRedoOccupation(TwmWindow *win) 443{ 444 WorkSpace *ws; 445 int newoccupation; 446 447 if(LookInList(Scr->OccupyAll, win->name, &win->class)) { 448 newoccupation = fullOccupation; 449 } 450 else { 451 newoccupation = 0; 452 for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) { 453 if(LookInList(ws->clientlist, win->name, &win->class)) { 454 newoccupation |= 1 << ws->number; 455 } 456 } 457 } 458 if(newoccupation != 0) { 459 ChangeOccupation(win, newoccupation); 460 } 461} 462 463 464/* f.vanish */ 465void 466WMgrRemoveFromCurrentWorkSpace(VirtualScreen *vs, TwmWindow *win) 467{ 468 WorkSpace *ws; 469 int newoccupation; 470 471 ws = vs->wsw->currentwspc; 472 if(!ws) { 473 /* Impossible? */ 474 return; 475 } 476 if(! OCCUPY(win, ws)) { 477 return; 478 } 479 480 newoccupation = win->occupation & ~(1 << ws->number); 481 if(newoccupation == 0) { 482 return; 483 } 484 485 ChangeOccupation(win, newoccupation); 486} 487 488 489/* f.warphere */ 490void 491WMgrAddToCurrentWorkSpaceAndWarp(VirtualScreen *vs, char *winname) 492{ 493 TwmWindow *tw; 494 int newoccupation; 495 496 /* Find named window on this screen */ 497 for(tw = Scr->FirstWindow; tw != NULL; tw = tw->next) { 498 if(match(winname, tw->name)) { 499 break; 500 } 501 } 502 503 /* Didn't find it by name? Try by class */ 504 if(!tw) { 505 for(tw = Scr->FirstWindow; tw != NULL; tw = tw->next) { 506 if(match(winname, tw->class.res_name)) { 507 break; 508 } 509 } 510 } 511 if(!tw) { 512 for(tw = Scr->FirstWindow; tw != NULL; tw = tw->next) { 513 if(match(winname, tw->class.res_class)) { 514 break; 515 } 516 } 517 } 518 519 /* Still didn't find? Beep at the user and bail. */ 520 if(!tw) { 521 XBell(dpy, 0); 522 return; 523 } 524 525 /* If WarpUnmapped isn't set and this isn't mapped, beep and bail */ 526 if((! Scr->WarpUnmapped) && (! tw->mapped)) { 527 XBell(dpy, 0); 528 return; 529 } 530 531 /* Move it here if it's not */ 532 if(! OCCUPY(tw, vs->wsw->currentwspc)) { 533 newoccupation = tw->occupation | (1 << vs->wsw->currentwspc->number); 534 ChangeOccupation(tw, newoccupation); 535 } 536 537 /* If we get here, WarpUnmapped is set, so map it if we need to */ 538 if(! tw->mapped) { 539 DeIconify(tw); 540 } 541 542 /* And go */ 543 WarpToWindow(tw, Scr->RaiseOnWarp); 544} 545 546 547/* f.occupyall backend */ 548void 549OccupyAll(TwmWindow *twm_win) 550{ 551 IconMgr *save; 552 553 if(!CanChangeOccupation(&twm_win)) { 554 return; 555 } 556 557 /* 558 * Temporarily alter Scr->iconmgr because stuff down in 559 * ChangeOccupation winds up adding/removing bits, and that doesn't 560 * work right when we're setting all? XXX Investigate further. 561 */ 562 save = Scr->iconmgr; 563 Scr->iconmgr = Scr->workSpaceMgr.workSpaceList->iconmgr; 564 ChangeOccupation(twm_win, fullOccupation); 565 Scr->iconmgr = save; 566} 567 568 569 570/* 571 **************************************************************** 572 * 573 * Pieces related to the Occupy window 574 * 575 **************************************************************** 576 */ 577 578static ColorPair occupyButtoncp; 579 580static char *ok_string = "OK", 581 *cancel_string = "Cancel", 582 *everywhere_string = "All"; 583 584/* 585 * Create the Occupy window. Part of startup process. 586 * 587 * Do not do the layout of the parts, only calculate the initial total 588 * size. For the layout, call ResizeOccupyWindow() at the end. 589 * 590 * There is only one Occupy window (per Screen), it is reparented to each 591 * virtual screen as needed. 592 */ 593void 594CreateOccupyWindow(void) 595{ 596 int width; // Caculated and altered, unlike most others 597 int Dummy = 1; 598 TwmWindow *tmp_win; 599 /* Shorthands for the Occupy window */ 600 OccupyWindow *occwin = Scr->workSpaceMgr.occupyWindow; 601 Window w; // occwin->w 602 /* Misc other shorthands */ 603 const int lines = Scr->workSpaceMgr.lines; 604 const int columns = Scr->workSpaceMgr.columns; 605 const int bwidth = Scr->vScreenList->wsw->bwidth; 606 const int bheight = Scr->vScreenList->wsw->bheight; 607 const int vspace = occwin->vspace; 608 const int hspace = occwin->hspace; 609 const int height = ((bheight + vspace) * lines) + bheight + (2 * vspace); 610 611 /* Struct embedded in [struct embedded in] Scr, so memory's waiting */ 612 613 /* There isn't anything we should do without workspaces... */ 614 if(!Scr->workSpaceManagerActive) { 615 return; 616 } 617 618 /* Initialize font and colorpair bits */ 619 occwin->font = Scr->IconManagerFont; 620 occwin->cp = Scr->IconManagerC; 621#ifdef COLOR_BLIND_USER 622 occwin->cp.shadc = Scr->White; 623 occwin->cp.shadd = Scr->Black; 624#else 625 if(!Scr->BeNiceToColormap) { 626 GetShadeColors(&occwin->cp); 627 } 628#endif 629 630 /* We already know that these should be too */ 631 occwin->lines = lines; 632 occwin->columns = columns; 633 634 635 /* 636 * Work out the necessary size of the OK/Cancel/All buttons at the 637 * bottom. 638 */ 639 { 640 XRectangle inc_rect; 641 XRectangle logical_rect; 642 MyFont font = occwin->font; 643 int bbwidth; // Bottom button width 644 /* Window min width based on bottom vs. workspace btns */ 645 int bb_width, ws_width; 646 647 /* Buttons gotta be as wide as the biggest of the three strings */ 648 XmbTextExtents(font.font_set, ok_string, strlen(ok_string), 649 &inc_rect, &logical_rect); 650 bbwidth = logical_rect.width; 651 652 XmbTextExtents(font.font_set, cancel_string, strlen(cancel_string), 653 &inc_rect, &logical_rect); 654 bbwidth = MAX(bbwidth, logical_rect.width); 655 656 XmbTextExtents(font.font_set, everywhere_string, 657 strlen(everywhere_string), 658 &inc_rect, &logical_rect); 659 bbwidth = MAX(bbwidth, logical_rect.width); 660 661 /* Plus the padding width */ 662 bbwidth += hspace; 663 664 /* 665 * So, the final width of those bottom buttons is that, plus the 666 * 3d button look extra on both sides, plus a little extra. I 667 * guess that extra + 2 is similar to TitlePadding or 668 * ButtonIndent on titlebars, but we don't have a config param 669 * for it on the workspace manager (which is the config used for 670 * the occupy window), so leave it as a magic constant for now. 671 */ 672 occwin->owidth = bbwidth + 2 * Scr->WMgrButtonShadowDepth + 2; 673 674 /* 675 * The whole thing has to be at least triple the min width of 676 * those bottom buttons, since there are three of them. The 677 * layout is "hspace button hspace button [...] hspace", to pad 678 * between and on both sides. 679 */ 680 bb_width = 3 * (bbwidth + hspace) + hspace; 681 682 /* 683 * It also has to be the width of our per-WS buttons. Per-ws 684 * buttons are sized the same as in the button-state WSM, and 685 * then we add the padding to them as above. 686 */ 687 ws_width = columns * (bwidth + hspace) + hspace; 688 689 /* So the window has to be as wide as the wider of those */ 690 width = MAX(bb_width, ws_width); 691 } 692 693 694 /* Now we know the size, so make the window */ 695 w = occwin->w = XCreateSimpleWindow(dpy, Scr->Root, 0, 0, width, height, 696 1, Scr->Black, occwin->cp.back); 697 698 /* Take those base sizes as a minimum */ 699 occwin->minwidth = width; 700 occwin->minheight = height; 701 702 703 /* 704 * Make subwindows as buttons for the workspaces. They're laid out 705 * in a grid mirroring the workspace manager's. 706 */ 707 { 708 int i = 0, j = 0; 709 WorkSpace *ws; 710 711 occwin->obuttonw = calloc(Scr->workSpaceMgr.count, sizeof(Window)); 712 713 for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) { 714 int idx = (j * columns) + i; 715 716 /* 717 * Make and map. Note that we're not setting the size or 718 * location at all here; ResizeOccupyWindow() does all that. 719 * We just make 'em. 720 */ 721 occwin->obuttonw[idx] = XCreateSimpleWindow(dpy, w, 722 Dummy /* x */, 723 Dummy /* y */, 724 Dummy /* width */, 725 Dummy /* height */, 726 0, Scr->Black, ws->cp.back); 727 XMapWindow(dpy, occwin->obuttonw[idx]); 728 729 /* Inc around to the next location */ 730 i++; 731 if(i == columns) { 732 i = 0; 733 j++; 734 } 735 } 736 } 737 738 739 /* 740 * Now start putting together the OK/Cancel/All buttons 741 */ 742 743 /* Background for them is hardcoded */ 744 GetColor(Scr->Monochrome, &(occupyButtoncp.back), "gray50"); 745 746 /* Foreground (not used here) is too */ 747 occupyButtoncp.fore = Scr->White; 748 749 /* Override (probably historical */ 750 if(!Scr->BeNiceToColormap) { 751 GetShadeColors(&occupyButtoncp); 752 } 753 754 /* Make 'em */ 755 { 756 Window tw; 757 758 tw = XCreateSimpleWindow(dpy, w, Dummy, Dummy, Dummy, Dummy, 0, 759 Scr->Black, occupyButtoncp.back); 760 XMapWindow(dpy, tw); 761 occwin->OK = tw; 762 763 tw = XCreateSimpleWindow(dpy, w, Dummy, Dummy, Dummy, Dummy, 0, 764 Scr->Black, occupyButtoncp.back); 765 XMapWindow(dpy, tw); 766 occwin->cancel = tw; 767 768 tw = XCreateSimpleWindow(dpy, w, Dummy, Dummy, Dummy, Dummy, 0, 769 Scr->Black, occupyButtoncp.back); 770 XMapWindow(dpy, tw); 771 occwin->allworkspc = tw; 772 } 773 774 775 /* Setup various window properties */ 776 { 777 XSizeHints sizehints; 778 XWMHints wmhints; 779 780 sizehints.flags = PBaseSize | PMinSize | PMaxSize; 781 sizehints.min_width = width; 782 sizehints.min_height = height; 783 sizehints.base_width = width; 784 sizehints.base_height = height; 785 sizehints.max_width = width; 786 sizehints.max_height = height; 787 788 wmhints.flags = InputHint | StateHint; 789 wmhints.input = True; 790 wmhints.initial_state = NormalState; 791 792 XmbSetWMProperties(dpy, w, occwin->name, occwin->icon_name, 793 NULL, 0, &sizehints, &wmhints, NULL); 794 } 795 796 797 /* 798 * Create the TwmWindow wrapping around it, with decorations etc. We 799 * do this so early in startup that we're not listening for window 800 * creation events yet. 801 */ 802 tmp_win = AddWindow(w, AWT_OCCUPY, Scr->iconmgr, Scr->currentvs); 803 if(! tmp_win) { 804 fprintf(stderr, "cannot create occupy window, exiting...\n"); 805 exit(1); 806 } 807 tmp_win->vs = NULL; 808 tmp_win->occupation = 0; 809 810 /* tmp_win is more convenient the rest of the func, but put in place */ 811 occwin->twm_win = tmp_win; 812 813 814 /* 815 * Setup the window to have a button-pushing cursor and listen for 816 * clicks. 817 */ 818 { 819 unsigned long attrmask; 820 XSetWindowAttributes attr; 821 XWindowAttributes wattr; 822 823 attr.cursor = Scr->ButtonCursor; 824 attrmask = CWCursor; 825 XChangeWindowAttributes(dpy, w, attrmask, &attr); 826 827 XGetWindowAttributes(dpy, w, &wattr); 828 attrmask = wattr.your_event_mask | KeyPressMask | KeyReleaseMask 829 | ExposureMask; 830 XSelectInput(dpy, w, attrmask); 831 } 832 833 834 /* 835 * Now for each of the buttons (workspaces + OK/Cancel/All), we mark 836 * them as listening to click and exposure events. We also stash 837 * away the screen and wrapping TwmWindow in contexts so other code 838 * can dredge them up. 839 */ 840#define EVT (ButtonPressMask | ButtonReleaseMask | ExposureMask) 841#define BTN_IPT_CTX(win) \ 842 XSelectInput(dpy, (win), EVT); \ 843 XSaveContext(dpy, (win), TwmContext, (XPointer) tmp_win); \ 844 XSaveContext(dpy, (win), ScreenContext, (XPointer) Scr); 845 846 for(WorkSpace *ws = Scr->workSpaceMgr.workSpaceList 847 ; ws != NULL ; ws = ws->next) { 848 BTN_IPT_CTX(occwin->obuttonw[ws->number]); 849 } 850 851 BTN_IPT_CTX(occwin->OK); 852 BTN_IPT_CTX(occwin->cancel); 853 BTN_IPT_CTX(occwin->allworkspc); 854 855#undef BTN_IPT_CTX 856#undef EVT 857 858 859 /* Mark that we're not mapped */ 860 SetMapStateProp(tmp_win, WithdrawnState); 861 862 /* Now call that func that sizes all the buttons */ 863 ResizeOccupyWindow(tmp_win); 864} 865 866 867/* 868 * Slightly misleading name: layout the internals of the Occupy window 869 * based on its current size. That does happen when it's resized, but 870 * also when it's initially created. I guess you could call "creation" a 871 * resize of a sort... 872 */ 873void 874ResizeOccupyWindow(TwmWindow *win) 875{ 876 int bwidth, bheight, owidth, oheight; 877 int hspace, vspace; 878 int lines, columns; 879 int neww, newh; 880 WorkSpace *ws; 881 int i, j, x, y; 882 OccupyWindow *occwin = Scr->workSpaceMgr.occupyWindow; 883 884 /* Floor at the original size */ 885 neww = MAX(win->attr.width, occwin->minwidth); 886 newh = MAX(win->attr.height, occwin->minheight); 887 if(occwin->width == neww && occwin->height == newh) { 888 return; 889 } 890 891 /* Space between WS buttons. From WMgr{Horiz,Vert}ButtonIndent. */ 892 hspace = occwin->hspace; 893 vspace = occwin->vspace; 894 895 /* Lines/cols in the layout. Same as WorkspaceManager's */ 896 lines = Scr->workSpaceMgr.lines; 897 columns = Scr->workSpaceMgr.columns; 898 899 /* Width/height of each button, based on the above and window size */ 900 bwidth = (neww - columns * hspace) / columns; 901 bheight = (newh - (lines + 2) * vspace) / (lines + 1); 902 903 /* Width/height of the OK/Cancel/All buttons */ 904 owidth = occwin->owidth; 905 oheight = bheight; 906 907 908 /* 909 * Lay out the workspace buttons 910 */ 911 i = 0; 912 j = 0; 913 for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) { 914 XMoveResizeWindow(dpy, occwin->obuttonw [j * columns + i], 915 i * (bwidth + hspace) + (hspace / 2), 916 j * (bheight + vspace) + (vspace / 2), 917 bwidth, bheight); 918 i++; 919 if(i == columns) { 920 i = 0; 921 j++; 922 } 923 } 924 925 926 /* 927 * Now the action buttons 928 */ 929 hspace = (neww - 3 * owidth) / 4; // Padding between 930 x = hspace; 931 y = ((bheight + vspace) * lines) + ((3 * vspace) / 2); 932 XMoveResizeWindow(dpy, occwin->OK, x, y, owidth, oheight); 933 x += owidth + hspace; 934 XMoveResizeWindow(dpy, occwin->cancel, x, y, owidth, oheight); 935 x += owidth + hspace; 936 XMoveResizeWindow(dpy, occwin->allworkspc, x, y, owidth, oheight); 937 938 939 /* Save all those dimensions we figured */ 940 occwin->width = neww; 941 occwin->height = newh; 942 occwin->bwidth = bwidth; 943 occwin->bheight = bheight; 944 occwin->owidth = owidth; 945 946 /* Don't need to repaint it; it'll get expose events */ 947} 948 949 950/* 951 * Draw the window when we need to (e.g., on expose) 952 */ 953void 954PaintOccupyWindow(void) 955{ 956 WorkSpace *ws; 957 OccupyWindow *occwin; 958 int width, height; 959 960 occwin = Scr->workSpaceMgr.occupyWindow; 961 width = occwin->width; 962 height = occwin->height; 963 964 Draw3DBorder(occwin->w, 0, 0, width, height, 2, occwin->cp, off, true, false); 965 966 for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) { 967 Window bw = occwin->obuttonw [ws->number]; 968 ButtonState bs = (occwin->tmpOccupation & (1 << ws->number)) ? on : off; 969 970 PaintWsButton(OCCUPYWINDOW, NULL, bw, ws->label, ws->cp, bs); 971 } 972 PaintWsButton(OCCUPYBUTTON, NULL, occwin->OK, ok_string, 973 occupyButtoncp, off); 974 PaintWsButton(OCCUPYBUTTON, NULL, occwin->cancel, cancel_string, 975 occupyButtoncp, off); 976 PaintWsButton(OCCUPYBUTTON, NULL, occwin->allworkspc, everywhere_string, 977 occupyButtoncp, off); 978} 979 980 981/* 982 * Somebody clicked in the Occupy window 983 */ 984void 985OccupyHandleButtonEvent(XEvent *event) 986{ 987 WorkSpace *ws; 988 OccupyWindow *occupyW; 989 Window buttonW; 990 991 /* 992 * Doesn't make sense that this can even happen if there are no 993 * workspaces... 994 */ 995 if(! Scr->workSpaceManagerActive) { 996 return; 997 } 998 999 /* ... or if there's no Occupy window up for anything */ 1000 if(occupyWin == NULL) { 1001 return; 1002 } 1003 1004 /* Which sub-window (button) was clicked */ 1005 buttonW = event->xbutton.window; 1006 if(buttonW == 0) { 1007 return; /* icon */ 1008 } 1009 1010 /* Grab onto the pointer for the duration of our action */ 1011 XGrabPointer(dpy, Scr->Root, True, 1012 ButtonPressMask | ButtonReleaseMask, 1013 GrabModeAsync, GrabModeAsync, 1014 Scr->Root, None, CurrentTime); 1015 1016 /* Find the workspace button that was clicked */ 1017 occupyW = Scr->workSpaceMgr.occupyWindow; 1018 for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) { 1019 if(occupyW->obuttonw [ws->number] == buttonW) { 1020 break; 1021 } 1022 } 1023 1024 if(ws != NULL) { 1025 /* If one was, toggle it */ 1026 int mask = 1 << ws->number; 1027 ButtonState bs = (occupyW->tmpOccupation & mask) ? off : on; 1028 1029 PaintWsButton(OCCUPYWINDOW, NULL, occupyW->obuttonw [ws->number], 1030 ws->label, ws->cp, bs); 1031 occupyW->tmpOccupation ^= mask; 1032 } 1033 else if(buttonW == occupyW->OK) { 1034 /* Else if we clicked OK, set things and close the window */ 1035 if(occupyW->tmpOccupation == 0) { 1036 return; 1037 } 1038 ChangeOccupation(occupyWin, occupyW->tmpOccupation); 1039 XUnmapWindow(dpy, occupyW->twm_win->frame); 1040 occupyW->twm_win->mapped = false; 1041 occupyW->twm_win->occupation = 0; 1042 occupyWin = NULL; 1043 XSync(dpy, 0); 1044 } 1045 else if(buttonW == occupyW->cancel) { 1046 /* Or cancel, do nothing and close the window */ 1047 XUnmapWindow(dpy, occupyW->twm_win->frame); 1048 occupyW->twm_win->mapped = false; 1049 occupyW->twm_win->occupation = 0; 1050 occupyWin = NULL; 1051 XSync(dpy, 0); 1052 } 1053 else if(buttonW == occupyW->allworkspc) { 1054 /* Or All, set 'em all */ 1055 for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) { 1056 PaintWsButton(OCCUPYWINDOW, NULL, occupyW->obuttonw [ws->number], 1057 ws->label, ws->cp, on); 1058 } 1059 occupyW->tmpOccupation = fullOccupation; 1060 } 1061 1062 /* Release the pointer, if ??? */ 1063 if(ButtonPressed == -1) { 1064 XUngrabPointer(dpy, CurrentTime); 1065 } 1066} 1067 1068 1069/* 1070 * f.occupy backend - pop up Occupy control for some window 1071 */ 1072void 1073Occupy(TwmWindow *twm_win) 1074{ 1075 int x, y; 1076 unsigned int width, height; 1077 Window w; 1078 struct OccupyWindow *occupyWindow; 1079 TwmWindow *occupy_twm; 1080 1081 /* Don't pop up on stuff we can't change */ 1082 if(!CanChangeOccupation(&twm_win)) { 1083 return; 1084 } 1085 1086 /* Grab our one screen-wide f.occupy window */ 1087 occupyWindow = Scr->workSpaceMgr.occupyWindow; 1088 occupyWindow->tmpOccupation = twm_win->occupation; 1089 w = occupyWindow->w; 1090 1091 /* Figure where to put it so it's centered on the cursor */ 1092 XGetGeometry(dpy, w, &JunkRoot, &JunkX, &JunkY, &width, &height, 1093 &JunkBW, &JunkDepth); 1094 XQueryPointer(dpy, Scr->Root, &JunkRoot, &JunkRoot, &JunkX, &JunkY, 1095 &x, &y, &JunkMask); 1096 1097 occupy_twm = occupyWindow->twm_win; 1098 occupy_twm->occupation = twm_win->occupation; 1099 1100 width += 2 * (occupy_twm->frame_bw3D + occupy_twm->frame_bw); 1101 height += 2 * (occupy_twm->frame_bw3D + occupy_twm->frame_bw); 1102 x -= (width / 2); 1103 y -= (height / 2); 1104 1105 /* Clip to screen */ 1106 ConstrainByLayout(Scr->BorderedLayout, -1, &x, width, &y, height); 1107 1108 /* Move the occupy window to where it should be */ 1109 if(occupy_twm->parent_vs != twm_win->parent_vs) { 1110 occupy_twm->vs = twm_win->parent_vs; 1111 occupy_twm->frame_x = x; 1112 occupy_twm->frame_y = y; 1113 /* 1114 * XXX Should this be using DisplayWin() like everything else, 1115 * rather than manually grubbing beneath it? 1116 */ 1117 ReparentFrameAndIcon(occupy_twm); 1118 } 1119 else { 1120 XMoveWindow(dpy, occupyWindow->twm_win->frame, x, y); 1121 } 1122 1123 /* And show it */ 1124 SetMapStateProp(occupy_twm, NormalState); 1125 XMapWindow(dpy, occupyWindow->w); 1126 XMapWindow(dpy, occupy_twm->frame); 1127 1128 /* XXX Must be a better way to express "all the way on top" */ 1129 OtpSetPriority(occupy_twm, WinWin, 0, Above); 1130 1131 /* Mark it shown, and stash what window we're showing it for */ 1132 occupyWindow->twm_win->mapped = true; 1133 occupyWin = twm_win; 1134} 1135 1136 1137 1138 1139/* 1140 **************************************************************** 1141 * 1142 * Backend and misc 1143 * 1144 **************************************************************** 1145 */ 1146 1147 1148/* 1149 * The actual meat of occupation-changing; [re-]set the occupation for 1150 * the window. This is the func that actually sets and saves the new 1151 * occupation, moves the window where it should be, etc. Should maybe be 1152 * called something more like "SetOccupation()". 1153 */ 1154void 1155ChangeOccupation(TwmWindow *tmp_win, int newoccupation) 1156{ 1157 TwmWindow *t; 1158 WorkSpace *ws; 1159 int oldoccupation; 1160 int changedoccupation; 1161 1162 if((newoccupation == 0) 1163 || (newoccupation == tmp_win->occupation)) { 1164 /* 1165 * occupation=0 we interpret as "leave it alone". == current, 1166 * ditto. Go ahead and re-set the WM_OCCUPATION property though, 1167 * in case it's been broken by another client. 1168 */ 1169 char *namelist; 1170 int len; 1171 long eventMask; 1172 1173 /* Mask out the PropertyChange events while we change the prop */ 1174 eventMask = mask_out_event(tmp_win->w, PropertyChangeMask); 1175 1176 len = GetPropertyFromMask(tmp_win->occupation, &namelist); 1177 XChangeProperty(dpy, tmp_win->w, XA_WM_OCCUPATION, XA_STRING, 8, 1178 PropModeReplace, (unsigned char *) namelist, len); 1179 free(namelist); 1180#ifdef EWMH 1181 EwmhSet_NET_WM_DESKTOP(tmp_win); 1182#endif 1183 1184 /* Reset event mask */ 1185 restore_mask(tmp_win->w, eventMask); 1186 return; 1187 } 1188 1189 /* 1190 * OK, there's something to change. Stash the current state. 1191 */ 1192 oldoccupation = tmp_win->occupation; 1193 1194 /* 1195 * Add it to IconManager in the new WS[en], remove from old. We have 1196 * to do the rather odd dance because AddIconManager() loops through 1197 * workspaces, and will add it to any workspaces it occupies (even if 1198 * it's already there). RemoveIconManager() goes over the window's 1199 * list of what icon managers it's on and removes it from any that 1200 * don't match the current occupation, so it can just be told "here's 1201 * where I should be". 1202 */ 1203 tmp_win->occupation = newoccupation & ~oldoccupation; 1204 AddIconManager(tmp_win); 1205 tmp_win->occupation = newoccupation; 1206 RemoveIconManager(tmp_win); 1207 1208 /* If it shouldn't be "here", vanish it */ 1209 if(tmp_win->vs && !OCCUPY(tmp_win, tmp_win->vs->wsw->currentwspc)) { 1210 Vanish(tmp_win->vs, tmp_win); 1211 } 1212 1213 /* 1214 * Try to find an(other) virtual screen which shows a workspace 1215 * where the window has occupation, so that the window can be shown 1216 * there now. 1217 */ 1218 if(!tmp_win->vs) { 1219 VirtualScreen *vs; 1220 for(vs = Scr->vScreenList; vs != NULL; vs = vs->next) { 1221 if(OCCUPY(tmp_win, vs->wsw->currentwspc)) { 1222 DisplayWin(vs, tmp_win); 1223 break; 1224 } 1225 } 1226 } 1227 1228 /* 1229 * Loop over workspaces. Find the first one that it used to be in. 1230 * If it's not there anymore, take it out of the WindowRegion there 1231 * (RWFR() silently returns if we're not using WindowRegion's), and 1232 * add it the WindowRegion in the first WS it now occupies. 1233 * 1234 * XXX I'm not sure this is entirely sensible; it seems like just 1235 * unconditionally Remove/Place'ing would have the same effect? 1236 */ 1237 for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) { 1238 int mask = 1 << ws->number; 1239 if(oldoccupation & mask) { 1240 if(!(newoccupation & mask)) { 1241 int final_x, final_y; 1242 RemoveWindowFromRegion(tmp_win); 1243 if(PlaceWindowInRegion(tmp_win, &final_x, &final_y)) { 1244 XMoveWindow(dpy, tmp_win->frame, final_x, final_y); 1245 } 1246 } 1247 break; 1248 } 1249 } 1250 1251 /* Now set the WM_OCCUPATION property */ 1252 { 1253 char *namelist; 1254 int len; 1255 long eventMask; 1256 1257 eventMask = mask_out_event(tmp_win->w, PropertyChangeMask); 1258 1259 len = GetPropertyFromMask(newoccupation, &namelist); 1260 XChangeProperty(dpy, tmp_win->w, XA_WM_OCCUPATION, XA_STRING, 8, 1261 PropModeReplace, (unsigned char *) namelist, len); 1262 free(namelist); 1263#ifdef EWMH 1264 EwmhSet_NET_WM_DESKTOP(tmp_win); 1265#endif 1266 1267 restore_mask(tmp_win->w, eventMask); 1268 } 1269 1270 1271 /* 1272 * Handle showing it up in the workspace map in the appropriate 1273 * places. 1274 * 1275 * Note that this whole block messes with the {new,old}occupation 1276 * vars. That's "safe" because they're no longer used for their 1277 * original purposes, only for the WSmap changes, but it's still 1278 * kinda fugly. Change to local vars at the drop of a hat with later 1279 * changes... 1280 */ 1281 if(!WMapWindowMayBeAdded(tmp_win)) { 1282 /* Not showing in the map, so pretend it's nowhere */ 1283 newoccupation = 0; 1284 } 1285 if(Scr->workSpaceMgr.noshowoccupyall) { 1286 /* 1287 * Don't show OccupyAll. Note that this means both OccupyAll 1288 * window, AND windows manually set to occupy everything. We 1289 * don't have to adjust newoccupation, because the above 1290 * conditional would have caught it, so we only need to edit old. 1291 */ 1292 if(oldoccupation == fullOccupation) { 1293 oldoccupation = 0; 1294 } 1295 } 1296 1297 /* Flip the ones that need flipping */ 1298 changedoccupation = oldoccupation ^ newoccupation; 1299 for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) { 1300 int mask = 1 << ws->number; 1301 if(changedoccupation & mask) { 1302 if(newoccupation & mask) { 1303 /* Add to WS */ 1304 WMapAddWindowToWorkspace(tmp_win, ws); 1305 } 1306 else { 1307 /* 1308 * Remove from WS. We have to take it out of saved focus 1309 * if it were there. Maybe there are other places we 1310 * might need to remove it from (warpring?)? 1311 */ 1312 WMapRemoveWindowFromWorkspace(tmp_win, ws); 1313 if(Scr->SaveWorkspaceFocus && ws->save_focus == tmp_win) { 1314 ws->save_focus = NULL; 1315 } 1316 } 1317 } 1318 } 1319 1320 1321 /* 1322 * If transients don't have their own occupation, find any transients 1323 * of this window and move them with it. 1324 */ 1325 if(! Scr->TransientHasOccupation) { 1326 for(t = Scr->FirstWindow; t != NULL; t = t->next) { 1327 if(t != tmp_win && 1328 ((t->istransient && t->transientfor == tmp_win->w) || 1329 t->group == tmp_win->w)) { 1330 ChangeOccupation(t, tmp_win->occupation); 1331 } 1332 } 1333 } 1334 1335 /* All done */ 1336 return; 1337} 1338 1339 1340/* 1341 * There are various reasons you might not be able to change the 1342 * occupation of a window (either due to attributes of it, or the state 1343 * of your session/WM), so provide a function to check them all when we 1344 * try a change. 1345 * 1346 * Note that this is _not_ called from ChangeOccupation(); only from 1347 * other things that wrap it. Since CO() gets called from states where 1348 * this would [falsely] fail, it would be a bad idea to put it there. 1349 */ 1350static bool 1351CanChangeOccupation(TwmWindow **twm_winp) 1352{ 1353 TwmWindow *twm_win; 1354 1355 /* No workspaces config'd? Changing is nonsensical. */ 1356 if(!Scr->workSpaceManagerActive) { 1357 return false; 1358 } 1359 1360 /* 1361 * f.occupy window up? Can't change in the middle of changing. 1362 * Though if it's not mapped, still pull it up, else iconifying the 1363 * occupy window breaks it forever. 1364 */ 1365 if(occupyWin != NULL && Scr->workSpaceMgr.occupyWindow->twm_win->mapped) { 1366 return false; 1367 } 1368 1369 /* XXX Can we jut do this in the init? Check all callers. */ 1370 twm_win = *twm_winp; 1371 1372 /* Don't change occupation of icon managers */ 1373 if(twm_win->isiconmgr) { 1374 return false; 1375 } 1376 1377 /* XXX Should check iswspmgr here too? */ 1378 1379 /* 1380 * If transients don't have their own occupation, check 1381 * transient/group bits. 1382 */ 1383 if(!Scr->TransientHasOccupation) { 1384 if(twm_win->istransient) { 1385 return false; 1386 } 1387 if(twm_win->group != (Window) 0 && twm_win->group != twm_win->w) { 1388 /* 1389 * When trying to modify a group member window, 1390 * operate on the group leader instead 1391 * (and thereby on all group member windows as well). 1392 * If we can't find the group leader, pretend it isn't set. 1393 */ 1394 twm_win = GetTwmWindow(twm_win->group); 1395 if(!twm_win) { 1396 return true; 1397 } 1398 *twm_winp = twm_win; 1399 } 1400 } 1401 1402 /* Sure, go ahead, change it */ 1403 return true; 1404} 1405 1406 1407/* 1408 * Add a client name to a list determining which workspaces it will 1409 * occupy. Used in handling the Occupy { } block in config file. 1410 */ 1411bool 1412AddToClientsList(char *workspace, char *client) 1413{ 1414 WorkSpace *ws; 1415 1416 /* "all" is a magic workspace value which makes it occupy anywhere */ 1417 if(strcmp(workspace, "all") == 0) { 1418 for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) { 1419 AddToList(&ws->clientlist, client, ""); 1420 } 1421 return true; 1422 } 1423 1424 /* If prefixed with "ws:", strip the prefix and lookup by WS name */ 1425 if(strncmp(workspace, "ws:", 3) == 0) { 1426 if((ws = GetWorkspace(workspace + 3)) != NULL) { 1427 AddToList(&ws->clientlist, client, ""); 1428 return true; 1429 } 1430 } 1431 1432 /* Else find that named workspace and all this to it */ 1433 if((ws = GetWorkspace(workspace)) != NULL) { 1434 AddToList(&ws->clientlist, client, ""); 1435 return true; 1436 } 1437 1438 /* Couldn't figure where to put it */ 1439 return false; 1440} 1441 1442 1443/* 1444 * Turn a ctwm.workspace resource string into an occupation mask. n.b.; 1445 * this changes the 'res' arg in-place. 1446 */ 1447static int 1448GetMaskFromResource(TwmWindow *win, char *res) 1449{ 1450 WorkSpace *ws; 1451 int mask; 1452 enum { O_SET, O_ADD, O_REM } mode; 1453 char *wrkSpcName, *tokst; 1454 1455 /* 1456 * This can set the occupation to a specific set of workspaces ("ws1 1457 * ws3"), add to the set it woudl have otherwise ("+ws1 ws3"), or 1458 * remove from the set it would otherwise ("-ws1 ws3"). The +/- 1459 * apply to the whole expression, not to the individual entries in 1460 * it. So first, figure out what we're doing. 1461 */ 1462 mode = O_SET; 1463 if(*res == '+') { 1464 mode = O_ADD; 1465 res++; 1466 } 1467 else if(*res == '-') { 1468 mode = O_REM; 1469 res++; 1470 } 1471 1472 /* 1473 * Walk through the string adding the workspaces specified into the 1474 * mask of what we're doing. 1475 */ 1476 mask = 0; 1477 for(wrkSpcName = strtok_r(res, " ", &tokst) ; wrkSpcName 1478 ; wrkSpcName = strtok_r(NULL, " ", &tokst)) { 1479 if(strcmp(wrkSpcName, "all") == 0) { 1480 mask = fullOccupation; 1481 break; 1482 } 1483 if(strcmp(wrkSpcName, "current") == 0) { 1484 VirtualScreen *vs = Scr->currentvs; 1485 if(vs) { 1486 mask |= (1 << vs->wsw->currentwspc->number); 1487 } 1488 continue; 1489 } 1490 1491 ws = GetWorkspace(wrkSpcName); 1492 if(ws != NULL) { 1493 mask |= (1 << ws->number); 1494 } 1495 else { 1496 fprintf(stderr, "unknown workspace : %s\n", wrkSpcName); 1497 } 1498 } 1499 1500 /* 1501 * And return that mask, with necessary alterations depending on +/- 1502 * specified. 1503 */ 1504 switch(mode) { 1505 case O_SET: 1506 return (mask); 1507 case O_ADD: 1508 return (mask | win->occupation); 1509 case O_REM: 1510 return (win->occupation & ~mask); 1511 } 1512 1513 /* Can't get here */ 1514 fprintf(stderr, "%s(): Unreachable.\n", __func__); 1515 return 0; 1516} 1517 1518 1519/* 1520 * Turns a \0-separated buffer of workspace names into an occupation 1521 * bitmask. 1522 */ 1523unsigned int 1524GetMaskFromProperty(unsigned char *_prop, unsigned long len) 1525{ 1526 char wrkSpcName[256]; 1527 WorkSpace *ws; 1528 unsigned int mask; 1529 int l; 1530 char *prop; 1531 1532 mask = 0; 1533 l = 0; 1534 prop = (char *) _prop; 1535 while(l < len) { 1536 /* If you have WS names longer than 256 chars, that's just Too Bad */ 1537 safe_strncpy(wrkSpcName, prop, 256); 1538 l += strlen(prop) + 1; 1539 prop += strlen(prop) + 1; 1540 if(strcmp(wrkSpcName, "all") == 0) { 1541 mask = fullOccupation; 1542 break; 1543 } 1544 1545 ws = GetWorkspace(wrkSpcName); 1546 if(ws != NULL) { 1547 mask |= (1 << ws->number); 1548 } 1549 else { 1550 fprintf(stderr, "unknown workspace : %s\n", wrkSpcName); 1551 } 1552 } 1553 1554#if 0 1555 { 1556 char *dbs = mk_nullsep_string((char *)_prop, len); 1557 fprintf(stderr, "%s('%s') -> 0x%x\n", __func__, dbs, mask); 1558 free(dbs); 1559 } 1560#endif 1561 1562 return (mask); 1563} 1564 1565 1566/* 1567 * Turns an occupation mask into a \0-separated buffer (not really a 1568 * string) of the workspace names. 1569 */ 1570int 1571GetPropertyFromMask(unsigned int mask, char **prop) 1572{ 1573 WorkSpace *ws; 1574 int len; 1575 char *wss[MAXWORKSPACE]; 1576 int i; 1577 1578 /* If it's everything, just say 'all' */ 1579 if(mask == fullOccupation) { 1580 *prop = strdup("all"); 1581 return 3; 1582 } 1583 1584 /* Stash up pointers to all the labels for WSen it's in */ 1585 memset(wss, 0, sizeof(wss)); 1586 i = 0; 1587 len = 0; 1588 for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) { 1589 if(mask & (1 << ws->number)) { 1590 wss[i++] = ws->label; 1591 len += strlen(ws->label) + 1; 1592 } 1593 } 1594 1595 /* Assemble them into \0-separated string */ 1596 *prop = malloc(len); 1597 len = 0; 1598 for(i = 0 ; wss[i] != NULL ; i++) { 1599 strcpy((*prop + len), wss[i]); 1600 len += strlen(wss[i]) + 1; // Skip past \0 1601 } 1602 1603#if 0 1604 { 1605 char *dbs = mk_nullsep_string(*prop, len); 1606 fprintf(stderr, "%s(0x%x) -> %d:'%s'\n", __func__, mask, len, dbs); 1607 free(dbs); 1608 } 1609#endif 1610 1611 return len; 1612} 1613 1614 1615/* 1616 * Generate a printable variant of the null-separated strings we use for 1617 * stashing in XA_WM_OCCUPATION. Used for debugging 1618 * Get{Property,Mask}From{Mask,Property}(). 1619 */ 1620#ifdef __GNUC__ 1621# pragma GCC diagnostic push 1622# pragma GCC diagnostic ignored "-Wunused-function" 1623#endif 1624static char * 1625mk_nullsep_string(const char *prop, int len) 1626{ 1627 char *dbs; 1628 int i, j; 1629 1630 /* 1631 * '\0' => "\\0" means we need longer than input; *2 is overkill, 1632 * but always sufficient, and it's cheap. 1633 */ 1634 dbs = malloc(len * 2); 1635 i = j = 0; 1636 while(i < len) { 1637 size_t slen = strlen(prop + i); 1638 1639 strcpy(dbs + j, (prop + i)); 1640 i += slen + 1; 1641 strcpy(dbs + j + slen, "\\0"); 1642 j += slen + 2; 1643 } 1644 1645 return dbs; 1646} 1647#ifdef __GNUC__ 1648# pragma GCC diagnostic pop 1649#endif 1650