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