workspace_manager.c revision 0bbfda8a
1/* 2 * Copyright 1992 Claude Lecommandeur. 3 */ 4 5#include "ctwm.h" 6 7#include <stdio.h> 8#include <string.h> 9#include <stdlib.h> 10 11#include <X11/Xatom.h> 12 13#include "ctwm_atoms.h" 14#include "util.h" 15#include "animate.h" 16#include "screen.h" 17#include "add_window.h" 18#include "events.h" 19#include "otp.h" 20#include "cursor.h" 21#include "image.h" 22#include "drawing.h" 23#include "list.h" 24#include "occupation.h" 25#include "vscreen.h" 26#include "win_decorations.h" 27#include "win_iconify.h" 28#include "win_ops.h" 29#include "win_utils.h" 30#include "workspace_manager.h" 31#include "workspace_utils.h" 32 33#include "gram.tab.h" 34 35 36// Temp; x-ref desc in workspace_utils 37extern bool useBackgroundInfo; 38 39 40static void CreateWorkSpaceManagerWindow(VirtualScreen *vs); 41static void ResizeWorkSpaceManager(VirtualScreen *vs, TwmWindow *win); 42static void PaintWorkSpaceManagerBorder(VirtualScreen *vs); 43 44static void wmap_mapwin_backend(TwmWindow *win, bool handleraise); 45 46static void WMapRedrawWindow(Window window, int width, int height, 47 ColorPair cp, const char *label); 48 49static void InvertColorPair(ColorPair *cp); 50 51 52static XContext MapWListContext = None; 53static Cursor handCursor = None; 54 55 56 57/* 58 **************************************************************** 59 * 60 * First, functions related to general creation and drawing of the WSM 61 * window and its backing structs 62 * 63 **************************************************************** 64 */ 65 66/* 67 * Allocate an X Context for WSM stuff. 68 */ 69void 70InitWorkSpaceManagerContext(void) 71{ 72 if(MapWListContext == None) { 73 MapWListContext = XUniqueContext(); 74 } 75} 76 77 78/* 79 * Prep up structures for WSM windows in each VS. Called (for each 80 * screen) in startup after InitVirtualScreens() has setup the VS stuff 81 * (and after config file processing). 82 */ 83void 84ConfigureWorkSpaceManager(void) 85{ 86 VirtualScreen *vs; 87 88 for(vs = Scr->vScreenList; vs != NULL; vs = vs->next) { 89 /* 90 * Make sure this is all properly initialized to nothing. Otherwise 91 * bad and undefined behavior can show up in certain cases (e.g., 92 * with no Workspaces {} defined in .ctwmrc, the only defined 93 * workspace will be random memory bytes, which can causes crashes on 94 * e.g. f.menu "TwmWindows".) 95 */ 96 WorkSpaceWindow *wsw = calloc(1, sizeof(WorkSpaceWindow)); 97 wsw->state = Scr->workSpaceMgr.initialstate; 98 vs->wsw = wsw; 99 } 100} 101 102 103/* 104 * Create workspace manager windows for each vscreen. Called (for each 105 * screen) late in startup, after the preceeding funcs have run their 106 * course. 107 */ 108void 109CreateWorkSpaceManager(void) 110{ 111 if(! Scr->workSpaceManagerActive) { 112 return; 113 } 114 115 /* Setup basic fonts/colors/cursors */ 116 Scr->workSpaceMgr.windowFont.basename = 117 "-adobe-courier-medium-r-normal--10-100-75-75-m-60-iso8859-1"; 118 Scr->workSpaceMgr.buttonFont = Scr->IconManagerFont; 119 Scr->workSpaceMgr.cp = Scr->IconManagerC; 120 if(!Scr->BeNiceToColormap) { 121 GetShadeColors(&Scr->workSpaceMgr.cp); 122 } 123 if(handCursor == None) { 124 NewFontCursor(&handCursor, "top_left_arrow"); 125 } 126 127 128 /* 129 * Create a WSM window for each vscreen. We don't need one for each 130 * workspace (we just reuse the same one), but we do need one for 131 * each vscreen (since they have to be displayed simultaneously). 132 */ 133 { 134 WorkSpace *ws, *fws; 135 char *vsmapbuf, *vsmap; 136 137 vsmapbuf = CtwmGetVScreenMap(dpy, Scr->Root); 138 if(vsmapbuf != NULL) { 139 vsmap = strtok(vsmapbuf, ","); 140 } 141 else { 142 vsmap = NULL; 143 } 144 145 /* 146 * weird things can happen if the config file is changed or the 147 * atom returned above is messed with. Sometimes windows may 148 * disappear in that case depending on what's changed. 149 * (depending on where they were on the actual screen. 150 */ 151 ws = Scr->workSpaceMgr.workSpaceList; 152 for(VirtualScreen *vs = Scr->vScreenList; vs != NULL; vs = vs->next) { 153 WorkSpaceWindow *wsw = vs->wsw; 154 if(vsmap) { 155 fws = GetWorkspace(vsmap); 156 } 157 else { 158 fws = NULL; 159 } 160 if(fws) { 161 wsw->currentwspc = fws; 162 vsmap = strtok(NULL, ","); 163 } 164 else { 165 wsw->currentwspc = ws; 166 ws = ws->next; 167 } 168 CreateWorkSpaceManagerWindow(vs); 169 } 170 171 free(vsmapbuf); 172 } 173 174 175 /* 176 * Init background in the WSM workspace subwindow and potentially the 177 * root window to the settings for the active workspace 178 * 179 * XXX CTAG_BGDRAW This process is also done in similar fashion 180 * during CreateWorkSpaceManagerWindow(), and the two parts are done 181 * split well apart during GotoWorkSpace(). The details of the 182 * process should be factored out into helper functions instead of 183 * being reimplemented in each place. That will require a little 184 * shuffling of code, and careful thinking on the apparent 185 * differences (which seem like they may be cosmetic). Todo. 186 */ 187 for(VirtualScreen *vs = Scr->vScreenList; vs != NULL; vs = vs->next) { 188 WorkSpaceWindow *wsw = vs->wsw; // Our WSW 189 WorkSpace *ws2 = wsw->currentwspc; // Active WS 190 MapSubwindow *msw = wsw->mswl[ws2->number]; // Active WS's subwin 191 192 /* Setup the background/border on the active workspace */ 193 if(Scr->workSpaceMgr.curImage == NULL) { 194 if(Scr->workSpaceMgr.curPaint) { 195 XSetWindowBackground(dpy, msw->w, Scr->workSpaceMgr.curColors.back); 196 } 197 } 198 else { 199 XSetWindowBackgroundPixmap(dpy, msw->w, Scr->workSpaceMgr.curImage->pixmap); 200 } 201 XSetWindowBorder(dpy, msw->w, Scr->workSpaceMgr.curBorderColor); 202 XClearWindow(dpy, msw->w); 203 204 /* Set the root window to the color/image of that WS if we should */ 205 if(useBackgroundInfo && ! Scr->DontPaintRootWindow) { 206 if(ws2->image == NULL) { 207 XSetWindowBackground(dpy, vs->window, ws2->backcp.back); 208 } 209 else { 210 XSetWindowBackgroundPixmap(dpy, vs->window, ws2->image->pixmap); 211 } 212 XClearWindow(dpy, vs->window); 213 } 214 } 215 216 217 /* 218 * Set the property we use to store the full list of workspaces. 219 * 220 * XXX This isn't really part of creating the WSM windows, so doesn't 221 * strictly belong here. It does need to happen after the config 222 * file parsing setup the workspaces, so couldn't go into 223 * InitWorkSpaceManager(). It could probably move into 224 * ConfigureWorkSpaceManager() though, or could move into a separate 225 * hypotehtical ConfigureWorkSpaces() sort of thing... 226 */ 227 { 228 char *wrkSpcList; 229 int len; 230 231 len = GetPropertyFromMask(0xFFFFFFFFu, &wrkSpcList); 232 XChangeProperty(dpy, Scr->Root, XA_WM_WORKSPACESLIST, XA_STRING, 8, 233 PropModeReplace, (unsigned char *) wrkSpcList, len); 234 free(wrkSpcList); 235 } 236} 237 238 239/* 240 * Put together the actual window for the workspace manager. Called as 241 * part of CreateWorkSpaceManager() during startup, once per vscreen 242 * (since there's a separate window for each). 243 */ 244static void 245CreateWorkSpaceManagerWindow(VirtualScreen *vs) 246{ 247 unsigned int width, height; 248 TwmWindow *tmp_win; 249 int x, y, gravity; 250 /* Shortcuts */ 251 const int vspace = Scr->workSpaceMgr.vspace; 252 const int hspace = Scr->workSpaceMgr.hspace; 253 const long count = Scr->workSpaceMgr.count; 254 255 /* No workspaces? Nothing to do. */ 256 if(count == 0) { 257 return; 258 } 259 260 /* 261 * Work out grid. wSM.columns will be filled if specified in 262 * WorkSpaceManageGeometry, or uninitialized (0) if not. 263 */ 264 { 265 int lines, columns; 266 columns = Scr->workSpaceMgr.columns; 267 if(columns == 0) { 268 lines = 2; 269 columns = ((count - 1) / lines) + 1; 270 } 271 else { 272 lines = ((count - 1) / columns) + 1; 273 } 274 Scr->workSpaceMgr.lines = lines; 275 Scr->workSpaceMgr.columns = columns; 276 } 277 278 279 /* Work out dimensions of stuff */ 280 { 281 unsigned int bwidth, bheight; 282 unsigned short strWid; 283 WorkSpace *ws; 284 const char *geometry = Scr->workSpaceMgr.geometry; 285 const int lines = Scr->workSpaceMgr.lines; 286 const int columns = Scr->workSpaceMgr.columns; 287 288 /* Figure longest label */ 289 strWid = 0; 290 for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) { 291 XRectangle inc_rect; 292 XRectangle logical_rect; 293 unsigned short wid; 294 const MyFont font = Scr->workSpaceMgr.buttonFont; 295 296 XmbTextExtents(font.font_set, ws->label, strlen(ws->label), 297 &inc_rect, &logical_rect); 298 wid = logical_rect.width; 299 if(wid > strWid) { 300 strWid = wid; 301 } 302 } 303 304 /* 305 * If WorkSpaceManagerGeometry is given, work from that. Else, 306 * create a workable minimum ourselves. 307 * */ 308 if(geometry != NULL) { 309 int mask; 310 311 /* Base button/subwindow sizes */ 312 bwidth = strWid + 10; 313 bheight = 22; 314 315 /* Adjust to WSMGeometry if specified */ 316 mask = XParseGeometry(geometry, &x, &y, &width, &height); 317 if(mask & WidthValue) { 318 bwidth = (width - (columns * hspace)) / columns; 319 } 320 if(mask & HeightValue) { 321 bheight = (height - (lines * vspace)) / lines; 322 } 323 324 /* Size of the whole thing is based off those */ 325 width = columns * (bwidth + hspace); 326 height = lines * (bheight + vspace); 327 328 /* 329 * If no Y given, put it at the bottom of the screen. If one 330 * is, just accept it. If it's a negative, we have to figure 331 * out where that actually is on this vscreen. 332 */ 333 if(!(mask & YValue)) { 334 y = 0; 335 mask |= YNegative; 336 } 337 if(mask & YNegative) { 338 y += vs->h - height; 339 } 340 341 /* 342 * If X is given, tweak as necessary for the vscreen 343 * location. Otherwise, put it in in something like the 344 * middle. 345 */ 346 if(mask & XValue) { 347 if(mask & XNegative) { 348 x += vs->w - width; 349 gravity = (mask & YNegative) ? SouthEastGravity : NorthEastGravity; 350 } 351 else { 352 gravity = (mask & YNegative) ? SouthWestGravity : NorthWestGravity; 353 } 354 } 355 else { 356 x = (vs->w - width) / 2; 357 gravity = (mask & YValue) ? ((mask & YNegative) ? 358 SouthGravity : NorthGravity) : SouthGravity; 359 } 360 } 361 else { 362 /* No geom specified, come up with one */ 363 bwidth = strWid + 2 * Scr->WMgrButtonShadowDepth + 6; 364 bheight = 22; 365 width = columns * (bwidth + hspace); 366 height = lines * (bheight + vspace); 367 x = (vs->w - width) / 2; 368 y = vs->h - height; 369 gravity = NorthWestGravity; 370 } 371 } 372 373 /* Set w/h to dummy values; ResizeWorkSpaceManager() writes real ones */ 374 vs->wsw->width = 1; 375 vs->wsw->height = 1; 376 377 /* Allocate structs for map/button subwindows */ 378 vs->wsw->bswl = calloc(count, sizeof(ButtonSubwindow *)); 379 vs->wsw->mswl = calloc(count, sizeof(MapSubwindow *)); 380 381 /* Create main window */ 382 vs->wsw->w = XCreateSimpleWindow(dpy, Scr->Root, x, y, width, height, 0, 383 Scr->Black, Scr->workSpaceMgr.cp.back); 384 385 386 /* 387 * Create the map and button subwindows for each workspace 388 */ 389 { 390 WorkSpace *ws; 391 392 for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) { 393 MapSubwindow *msw; 394 ButtonSubwindow *bsw; 395 const int Dummy = 1; 396 const unsigned long border = Scr->workSpaceMgr.defBorderColor; 397 398 /* Alloc structs */ 399 vs->wsw->bswl[ws->number] = bsw 400 = calloc(1, sizeof(ButtonSubwindow)); 401 vs->wsw->mswl[ws->number] = msw = calloc(1, sizeof(MapSubwindow)); 402 403 /* 404 * Create windows for button/map. ResizeWorkSpaceManager() 405 * sets the real sizes and positions, so we dummy 'em. 406 */ 407 bsw->w = XCreateSimpleWindow(dpy, vs->wsw->w, 408 Dummy, Dummy, Dummy, Dummy, 409 0, Scr->Black, ws->cp.back); 410 411 msw->w = XCreateSimpleWindow(dpy, vs->wsw->w, 412 Dummy, Dummy, Dummy, Dummy, 413 1, border, ws->cp.back); 414 415 /* Map whichever is up by default */ 416 if(vs->wsw->state == WMS_buttons) { 417 XMapWindow(dpy, bsw->w); 418 } 419 else { 420 XMapWindow(dpy, msw->w); 421 } 422 423 /* Setup background on map-state window */ 424 /* XXX X-ref CTAG_BGDRAW in CreateWorkSpaceManager() */ 425 if(useBackgroundInfo) { 426 if(ws->image == NULL || Scr->NoImagesInWorkSpaceManager) { 427 XSetWindowBackground(dpy, msw->w, ws->backcp.back); 428 } 429 else { 430 XSetWindowBackgroundPixmap(dpy, msw->w, ws->image->pixmap); 431 } 432 } 433 else { 434 if(Scr->workSpaceMgr.defImage == NULL || Scr->NoImagesInWorkSpaceManager) { 435 XSetWindowBackground(dpy, msw->w, Scr->workSpaceMgr.defColors.back); 436 } 437 else { 438 XSetWindowBackgroundPixmap(dpy, msw->w, Scr->workSpaceMgr.defImage->pixmap); 439 } 440 } 441 442 /* 443 * Clear out button subwin; PaintWorkSpaceManager() fills it 444 * in. Is this really necessary? 445 */ 446 XClearWindow(dpy, bsw->w); 447 } 448 } 449 450 451 /* Set WM properties */ 452 { 453 XSizeHints sizehints; 454 XWMHints wmhints; 455 const int lines = Scr->workSpaceMgr.lines; 456 const int columns = Scr->workSpaceMgr.columns; 457 const char *name = Scr->workSpaceMgr.name; 458 const char *icon_name = Scr->workSpaceMgr.icon_name; 459 460 sizehints.flags = USPosition | PBaseSize | PMinSize | PResizeInc 461 | PWinGravity; 462 sizehints.x = x; 463 sizehints.y = y; 464 sizehints.base_width = columns * hspace; 465 sizehints.base_height = lines * vspace; 466 sizehints.width_inc = columns; 467 sizehints.height_inc = lines; 468 sizehints.min_width = columns * (hspace + 2); 469 sizehints.min_height = lines * (vspace + 2); 470 sizehints.win_gravity = gravity; 471 472 wmhints.flags = InputHint | StateHint; 473 wmhints.input = True; 474 wmhints.initial_state = NormalState; 475 476 XmbSetWMProperties(dpy, vs->wsw->w, name, icon_name, NULL, 0, 477 &sizehints, &wmhints, NULL); 478 } 479 480 481 /* Create our TwmWindow wrapping around it */ 482 tmp_win = AddWindow(vs->wsw->w, AWT_WORKSPACE_MANAGER, 483 Scr->iconmgr, vs); 484 if(! tmp_win) { 485 fprintf(stderr, "cannot create workspace manager window, exiting...\n"); 486 exit(1); 487 } 488 tmp_win->occupation = fullOccupation; 489 tmp_win->attr.width = width; 490 tmp_win->attr.height = height; 491 vs->wsw->twm_win = tmp_win; 492 493 494 /* Do the figuring to size and internal-layout it */ 495 ResizeWorkSpaceManager(vs, tmp_win); 496 497 498 /* Setup cursor/gravity and listen for events */ 499 { 500 XWindowAttributes wattr; 501 XSetWindowAttributes attr; 502 unsigned long attrmask; 503 504 attr.cursor = Scr->ButtonCursor; 505 attr.win_gravity = gravity; 506 attrmask = CWCursor | CWWinGravity; 507 XChangeWindowAttributes(dpy, vs->wsw->w, attrmask, &attr); 508 509 XGetWindowAttributes(dpy, vs->wsw->w, &wattr); 510 attrmask = wattr.your_event_mask | KeyPressMask | KeyReleaseMask 511 | ExposureMask; 512 XSelectInput(dpy, vs->wsw->w, attrmask); 513 } 514 515 516 /* 517 * Mark the buttons as listening to click and exposure events, and 518 * stash away some pointers in contexts. We stash the overall WSM 519 * window in TwmContext, which means that when an event looks up the 520 * window, it finds the WSM rather than the subwindow, and then falls 521 * into the WMgrHandle*Event()'s, which then dig down into the event 522 * to find where it happened in there. 523 * 524 * The map window doesn't listen to expose events; it's just empty 525 * and background colored. The individual subwindows in the map 526 * listen for exposes for drawing themselves. 527 * 528 * Dragging windows around to move or re-occupy in the map window 529 * does rely on motion events, but we don't listen for them here. 530 * That happens in WMgrHandleButtonEvent() after getting the initial 531 * click. It changes the listen and runs through the action 532 * internally; those motions never run through our main event loop. 533 */ 534 for(WorkSpace *ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; 535 ws = ws->next) { 536 Window buttonw = vs->wsw->bswl[ws->number]->w; 537 Window mapsubw = vs->wsw->mswl[ws->number]->w; 538 539 XSelectInput(dpy, buttonw, ButtonPressMask | ButtonReleaseMask 540 | ExposureMask); 541 XSaveContext(dpy, buttonw, TwmContext, (XPointer) tmp_win); 542 XSaveContext(dpy, buttonw, ScreenContext, (XPointer) Scr); 543 544 XSelectInput(dpy, mapsubw, ButtonPressMask | ButtonReleaseMask); 545 XSaveContext(dpy, mapsubw, TwmContext, (XPointer) tmp_win); 546 XSaveContext(dpy, mapsubw, ScreenContext, (XPointer) Scr); 547 } 548 549 550 /* Set WM_STATE prop */ 551 SetMapStateProp(tmp_win, WithdrawnState); 552 553 554 /* Setup root window if necessary */ 555 /* XXX X-ref CTAG_BGDRAW in CreateWorkSpaceManager() */ 556 if(useBackgroundInfo && ! Scr->DontPaintRootWindow) { 557 WorkSpace *ws = Scr->workSpaceMgr.workSpaceList; 558 if(ws->image == NULL) { 559 XSetWindowBackground(dpy, Scr->Root, ws->backcp.back); 560 } 561 else { 562 XSetWindowBackgroundPixmap(dpy, Scr->Root, ws->image->pixmap); 563 } 564 XClearWindow(dpy, Scr->Root); 565 } 566 567 568 /* 569 * Don't have to PaintWorkSpaceManager(vs) here, because 570 * ResizeWorkSpaceManager() already called it for us. 571 */ 572} 573 574 575/* 576 * Size and layout a WSM. Mostly an internal bit in the process of 577 * setting it up. 578 */ 579static void 580ResizeWorkSpaceManager(VirtualScreen *vs, TwmWindow *win) 581{ 582 WorkSpace *ws; 583 int i, j; 584 /* Lots of shortcuts to ease reading */ 585 const int neww = win->attr.width; 586 const int newh = win->attr.height; 587 const int hspace = Scr->workSpaceMgr.hspace; 588 const int vspace = Scr->workSpaceMgr.vspace; 589 const int lines = Scr->workSpaceMgr.lines; 590 const int columns = Scr->workSpaceMgr.columns; 591 const int bwidth = (neww - (columns * hspace)) / columns; 592 const int bheight = (newh - (lines * vspace)) / lines; 593 const int wwidth = neww / columns; 594 const int wheight = newh / lines; 595 const float wf = (float)(wwidth - 2) / (float) vs->w; 596 const float hf = (float)(wheight - 2) / (float) vs->h; 597 598 /* If nothing's changed since our last run, there's nothing to change */ 599 if(neww == vs->wsw->width && newh == vs->wsw->height) { 600 return; 601 } 602 603 /* Set the new overall vals */ 604 vs->wsw->bwidth = bwidth; 605 vs->wsw->bheight = bheight; 606 vs->wsw->width = neww; 607 vs->wsw->height = newh; 608 vs->wsw->wwidth = wwidth; 609 vs->wsw->wheight = wheight; 610 611 /* Iterate over the WS's */ 612 i = 0; 613 j = 0; 614 for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) { 615 MapSubwindow *msw = vs->wsw->mswl[ws->number]; 616 ButtonSubwindow *bsw = vs->wsw->bswl[ws->number]; 617 618 /* Move button window to its place in the grid and size appropriately */ 619 XMoveResizeWindow(dpy, bsw->w, 620 i * (bwidth + hspace) + (hspace / 2), 621 j * (bheight + vspace) + (vspace / 2), 622 bwidth, bheight); 623 624 /* Move the map window as well */ 625 msw->x = i * wwidth; 626 msw->y = j * wheight; 627 XMoveResizeWindow(dpy, msw->w, msw->x, msw->y, wwidth - 2, wheight - 2); 628 629 /* 630 * Redo interior sizing and placement of all the windows in the 631 * WS in the map window 632 */ 633 for(WinList *wl = msw->wl; wl != NULL; wl = wl->next) { 634 TwmWindow *tmp_win = wl->twm_win; 635 wl->x = (int)(tmp_win->frame_x * wf); 636 wl->y = (int)(tmp_win->frame_y * hf); 637 wl->width = (unsigned int)((tmp_win->frame_width * wf) + 0.5); 638 wl->height = (unsigned int)((tmp_win->frame_height * hf) + 0.5); 639 XMoveResizeWindow(dpy, wl->w, wl->x, wl->y, wl->width, wl->height); 640 } 641 642 /* And around to the next WS */ 643 i++; 644 if(i == columns) { 645 i = 0; 646 j++; 647 }; 648 } 649 650 651 /* Draw it */ 652 PaintWorkSpaceManager(vs); 653} 654 655 656/* 657 * Draw up the button-state pieces of a WSM window. 658 * 659 * Note: this is currently stubbed out and does nothing. Historically 660 * it's been called during startup when the WSM window is put together, 661 * and when the screen is unmasked. However, the only apparent result is 662 * that the border and buttons get drawn a little earlier; they already 663 * get expose events that get picked up when we start the event loop. 664 * 665 * If we don't find any reason to reinstate it, we should remove this in 666 * the future. 667 */ 668void 669PaintWorkSpaceManager(VirtualScreen *vs) 670{ 671 WorkSpace *ws; 672 673 /* x-ref header comment */ 674 return; 675 676 PaintWorkSpaceManagerBorder(vs); 677 678 for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) { 679 Window buttonw = vs->wsw->bswl[ws->number]->w; 680 ButtonState bs = (ws == vs->wsw->currentwspc) ? on : off; 681 682 PaintWsButton(WSPCWINDOW, vs, buttonw, ws->label, ws->cp, bs); 683 } 684} 685 686 687/* 688 * Border around the WSM 689 */ 690static void 691PaintWorkSpaceManagerBorder(VirtualScreen *vs) 692{ 693 int width, height; 694 695 width = vs->wsw->width; 696 height = vs->wsw->height; 697 Draw3DBorder(vs->wsw->w, 0, 0, width, height, 2, Scr->workSpaceMgr.cp, off, 698 true, false); 699} 700 701 702/* 703 * Draw a workspace manager window on expose. X-ref comment on 704 * PaintWorkSpaceManager(). 705 */ 706void 707WMgrHandleExposeEvent(VirtualScreen *vs, XEvent *event) 708{ 709 if(vs->wsw->state == WMS_buttons) { 710 Window buttonw; 711 WorkSpace *ws; 712 713 /* Find the button we're exposing */ 714 for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) { 715 buttonw = vs->wsw->bswl[ws->number]->w; 716 if(event->xexpose.window == buttonw) { 717 break; 718 } 719 } 720 721 /* If none, just paint the border. Else paint the button. */ 722 if(ws == NULL) { 723 PaintWorkSpaceManagerBorder(vs); 724 } 725 else { 726 ButtonState bs = (ws == vs->wsw->currentwspc) ? on : off; 727 PaintWsButton(WSPCWINDOW, vs, buttonw, ws->label, ws->cp, bs); 728 } 729 } 730 else { 731 WinList *wl; 732 733 /* 734 * This is presumably exposing some individual window in the WS 735 * subwindow; find it from the stashed context on the window, and 736 * redraw it. 737 */ 738 if(XFindContext(dpy, event->xexpose.window, MapWListContext, 739 (XPointer *) &wl) == XCNOENT) { 740 return; 741 } 742 if(wl && wl->twm_win && wl->twm_win->mapped) { 743 WMapRedrawName(vs, wl); 744 } 745 } 746} 747 748 749 750/* 751 * Moving the WSM between button and map state 752 */ 753void 754WMgrToggleState(VirtualScreen *vs) 755{ 756 if(vs->wsw->state == WMS_buttons) { 757 WMgrSetMapState(vs); 758 } 759 else { 760 WMgrSetButtonsState(vs); 761 } 762} 763 764void 765WMgrSetMapState(VirtualScreen *vs) 766{ 767 WorkSpace *ws; 768 769 if(vs->wsw->state == WMS_map) { 770 return; 771 } 772 for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) { 773 XUnmapWindow(dpy, vs->wsw->bswl [ws->number]->w); 774 XMapWindow(dpy, vs->wsw->mswl [ws->number]->w); 775 } 776 vs->wsw->state = WMS_map; 777 MaybeAnimate = true; 778} 779 780void 781WMgrSetButtonsState(VirtualScreen *vs) 782{ 783 WorkSpace *ws; 784 785 if(vs->wsw->state == WMS_buttons) { 786 return; 787 } 788 for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) { 789 XUnmapWindow(dpy, vs->wsw->mswl [ws->number]->w); 790 XMapWindow(dpy, vs->wsw->bswl [ws->number]->w); 791 } 792 vs->wsw->state = WMS_buttons; 793} 794 795 796 797 798/* 799 **************************************************************** 800 * 801 * Handlers for mouse/key actions in the WSM 802 * 803 **************************************************************** 804 */ 805 806/* 807 * Key press/release events in the WSM. A major use (and only for 808 * release) is the Ctrl-key switching between map and button state. The 809 * other use is on-the-fly renaming of workspaces by typing in the 810 * button-state WSM. 811 */ 812void 813WMgrHandleKeyReleaseEvent(VirtualScreen *vs, XEvent *event) 814{ 815 KeySym keysym; 816 817 keysym = XLookupKeysym((XKeyEvent *) event, 0); 818 if(! keysym) { 819 return; 820 } 821 if(keysym == XK_Control_L || keysym == XK_Control_R) { 822 /* DontToggleWorkSpaceManagerState added 20040607 by dl*/ 823 if(!Scr->DontToggleWorkspaceManagerState) { 824 WMgrToggleState(vs); 825 } 826 return; 827 } 828} 829 830void 831WMgrHandleKeyPressEvent(VirtualScreen *vs, XEvent *event) 832{ 833 WorkSpace *ws; 834 835 /* Check if we're using Control to toggle the state */ 836 { 837 KeySym keysym = XLookupKeysym((XKeyEvent *) event, 0); 838 if(! keysym) { 839 return; 840 } 841 if(keysym == XK_Control_L || keysym == XK_Control_R) { 842 /* DontToggleWorkSpaceManagerState added 20040607 by dl*/ 843 if(!Scr->DontToggleWorkspaceManagerState) { 844 WMgrToggleState(vs); 845 } 846 return; 847 } 848 } 849 850 /* Otherwise, key presses do nothing in map state */ 851 if(vs->wsw->state == WMS_map) { 852 return; 853 } 854 855 /* 856 * If we're typing in a button-state WSM, and the mouse is on one of 857 * the buttons, that means we're changing the name, so do that dance. 858 */ 859 for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) { 860 if(vs->wsw->bswl[ws->number]->w == event->xkey.subwindow) { 861 break; 862 } 863 } 864 if(ws == NULL) { 865 /* Not on a button, nothing to do */ 866 return; 867 } 868 869 870 /* 871 * Edit the label. 872 */ 873 { 874 int nkeys; 875 char keys[16]; 876 size_t nlen; 877 char *newname; 878 879 /* Look up what keystrokes are queued. Arbitrary buf size */ 880 nkeys = XLookupString(&(event->xkey), keys, 16, NULL, NULL); 881 882 /* Label length can't grow to more than cur+nkeys */ 883 nlen = strlen(ws->label); 884 newname = malloc(nlen + nkeys + 1); 885 strcpy(newname, ws->label); 886 887 /* Iterate over the passed keystrokes */ 888 for(int i = 0 ; i < nkeys ; i++) { 889 unsigned char k = keys[i]; 890 891 if(isprint(k)) { 892 /* Printable chars append to the string */ 893 newname[nlen++] = k; 894 } 895 else if((k == 127) || (k == 8)) { 896 /* 897 * DEL or BS back up a char. 898 * 899 * XXX Would it be more generally correct to do this via 900 * keysyms, in the face of changed keyboard mappings or 901 * significantly differing locales? 902 */ 903 if(nlen != 0) { 904 nlen--; 905 } 906 } 907 else { 908 /* Any other char stops the process dead */ 909 break; 910 } 911 } 912 /* Now ends where it ends */ 913 newname[nlen] = '\0'; 914 915 /* Swap it in */ 916 free(ws->label); 917 ws->label = newname; 918 } 919 920 921 /* Redraw the button with the new label */ 922 { 923 ButtonState bs = (ws == vs->wsw->currentwspc) ? on : off; 924 925 PaintWsButton(WSPCWINDOW, vs, vs->wsw->bswl[ws->number]->w, ws->label, 926 ws->cp, bs); 927 } 928} 929 930 931/* 932 * Mouse clicking in WSM. Gets called on button press (not release). In 933 * the simple case, that's just switching workspaces. In the more 934 * complex, it's changing window occupation in various different ways, or 935 * even moving windows. When that's happening, it internally hijacks 936 * button/motion/exposure events and implements them for the moving, with 937 * magic escapes if it gets them for something else. Ew. 938 */ 939void 940WMgrHandleButtonEvent(VirtualScreen *vs, XEvent *event) 941{ 942 WorkSpace *oldws, *newws; 943 WinList *wl; 944 TwmWindow *win; 945 unsigned int W0, H0; 946 XEvent lastev; 947 Window w = 0; 948 Position newX = 0, newY = 0, winX = 0, winY = 0; 949 bool alreadyvivible, realmovemode; 950 const WorkSpaceWindow *mw = vs->wsw; 951 952 /* Shortcuts into the event */ 953 const Window parent = event->xbutton.window; // Map/button for WS 954 const Window sw = event->xbutton.subwindow; // Map mini-win 955 const Time etime = event->xbutton.time; 956 const unsigned int button = event->xbutton.button; 957 const unsigned int modifier = event->xbutton.state; 958 959 960 /* If we're in button state, we're just clicking to change */ 961 if(vs->wsw->state == WMS_buttons) { 962 WorkSpace *ws; 963 for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) { 964 if(vs->wsw->bswl[ws->number]->w == parent) { 965 GotoWorkSpace(vs, ws); 966 break; 967 } 968 } 969 return; 970 } 971 972 /* 973 * If we get this far, we're in map state, where things are more 974 * complicated. A simple click/release means change here too, but 975 * there's also the possibility of dragging a subwindow around to 976 * change its window's occupation. 977 */ 978 979 /* Find what workspace we're clicking in */ 980 for(oldws = Scr->workSpaceMgr.workSpaceList ; oldws != NULL ; 981 oldws = oldws->next) { 982 if(vs->wsw->mswl[oldws->number]->w == parent) { 983 break; 984 } 985 } 986 if(oldws == NULL) { 987 /* None? We're done here. */ 988 return; 989 } 990 991 /* 992 * If clicked in the workspace but outside a window, we can only be 993 * switching workspaces. So just do that, and we're done. 994 */ 995 if(sw == (Window) 0) { 996 GotoWorkSpace(vs, oldws); 997 return; 998 } 999 1000 /* Use the context to find the winlist entry for this window */ 1001 if(XFindContext(dpy, sw, MapWListContext, (XPointer *) &wl) == XCNOENT) { 1002 return; 1003 } 1004 win = wl->twm_win; 1005 1006 /* 1007 * Sometimes we skip transients, so do so. XXX Should this 1008 * GotoWorkSpace()? 1009 */ 1010 if((! Scr->TransientHasOccupation) && win->istransient) { 1011 return; 1012 } 1013 1014 /* 1015 * Are we trying to actually move the window, by moving its avatar in 1016 * the WSM? If ReallyMoveInWorkspaceManager is set, we're moving on 1017 * click, but not in shift-click. If it's not, it's the reverse. 1018 * 1019 * XXX This interacts really oddly and badly when you're also moving 1020 * the window from WS to WS. 1021 */ 1022 realmovemode = false; 1023 if(Scr->ReallyMoveInWorkspaceManager) { 1024 if(!(modifier & ShiftMask)) { 1025 realmovemode = true; 1026 } 1027 } 1028 else if(modifier & ShiftMask) { 1029 realmovemode = true; 1030 } 1031 1032 /* 1033 * Frob screen-wide OpaqueMove as necessary for this window's 1034 * details. 1035 * XXX Really? 1036 */ 1037 if(win->OpaqueMove) { 1038 if(Scr->OpaqueMoveThreshold >= 200) { 1039 Scr->OpaqueMove = true; 1040 } 1041 else { 1042 const unsigned long winsz = win->frame_width * win->frame_height; 1043 const unsigned long scrsz = vs->w * vs->h; 1044 const float sf = Scr->OpaqueMoveThreshold / 100.0; 1045 if(winsz > (scrsz * sf)) { 1046 Scr->OpaqueMove = false; 1047 } 1048 else { 1049 Scr->OpaqueMove = true; 1050 } 1051 } 1052 } 1053 else { 1054 Scr->OpaqueMove = false; 1055 } 1056 1057 /* 1058 * Buttons inside the workspace manager, when clicking on the 1059 * representation of a window: 1060 * 1: drag window to a different workspace 1061 * 2: drag a copy of the window to a different workspace 1062 * (show it in both workspaces) 1063 * 3: remove the window from this workspace (if it isn't the last). 1064 * 1065 * XXX If you move between workspaces while also doing realmovemode, 1066 * really messed up things happen. 1067 */ 1068 switch(button) { 1069 case 1 : { 1070 /* 1071 * Moving from one to another; get rid of the old location, 1072 * then fall through to the "duplicating" case below. 1073 */ 1074 XUnmapWindow(dpy, sw); 1075 /* FALLTHRU */ 1076 } 1077 1078 case 2 : { 1079 /* 1080 * Duplicating window to another WS. We create a copy of the 1081 * window, and start moving that. The 'moving' case above 1082 * falls through to us after unmapping that old window, 1083 * leaving just our copy, which is good enough. This is 1084 * really just setting up the visual stuff for the move; the 1085 * actual occupation changes etc. come at the end of the 1086 * motion, much lower down. 1087 */ 1088 int X0, Y0, X1, Y1; 1089 unsigned int bw; 1090 XSetWindowAttributes attrs; 1091 Window junkW; 1092 1093 /* [XYWH]0 = size/location of the avatar in the map */ 1094 XGetGeometry(dpy, sw, &junkW, &X0, &Y0, &W0, &H0, &bw, &JunkDepth); 1095 1096 /* 1097 * [XY]0 are the coordinates of the avatar subwindow inside 1098 * the individual workspace window in the map. Turn those 1099 * into [XY]1 as the coordinates of it relative to the whole 1100 * WSM window. 1101 */ 1102 XTranslateCoordinates(dpy, vs->wsw->mswl[oldws->number]->w, 1103 mw->w, X0, Y0, &X1, &Y1, &junkW); 1104 1105 /* 1106 * Create the copy window to drag around, as a child of the 1107 * whole WSM (so we can move it between workspaces), and map 1108 * it. 1109 */ 1110 attrs.event_mask = ExposureMask; 1111 attrs.background_pixel = wl->cp.back; 1112 attrs.border_pixel = wl->cp.back; 1113 w = XCreateWindow(dpy, mw->w, X1, Y1, W0, H0, bw, 1114 CopyFromParent, CopyFromParent, CopyFromParent, 1115 CWEventMask | CWBackPixel | CWBorderPixel, 1116 &attrs); 1117 XMapRaised(dpy, w); 1118 1119 /* Do our dance on it to draw the name/color/etc */ 1120 WMapRedrawWindow(w, W0, H0, wl->cp, wl->twm_win->icon_name); 1121 1122 /* 1123 * If we're moving the real window and 1124 * AlwaysShowWindowWhenMovingFromWorkspaceManager is set in 1125 * config, we need to be sure the real window is visible 1126 * while we move it. 1127 */ 1128 if(realmovemode && Scr->ShowWinWhenMovingInWmgr) { 1129 if(Scr->OpaqueMove) { 1130 DisplayWin(vs, win); 1131 } 1132 else { 1133 MoveOutline(Scr->Root, 1134 win->frame_x - win->frame_bw, 1135 win->frame_y - win->frame_bw, 1136 win->frame_width + 2 * win->frame_bw, 1137 win->frame_height + 2 * win->frame_bw, 1138 win->frame_bw, 1139 win->title_height + win->frame_bw3D); 1140 } 1141 } 1142 1143 /* Move onward */ 1144 break; 1145 } 1146 1147 /* 1148 * For the button 3 or anything else case, there's no dragging or 1149 * anything, so they do their thing and just immediately return. 1150 */ 1151 case 3 : { 1152 int newocc = win->occupation & ~(1 << oldws->number); 1153 if(newocc != 0) { 1154 ChangeOccupation(win, newocc); 1155 } 1156 return; 1157 } 1158 1159 default : 1160 return; 1161 } 1162 1163 /* 1164 * Keep dragging the representation of the window 1165 * 1166 * XXX Look back at this and see if we can move it to an inner 1167 * function for readability... 1168 */ 1169 { 1170 const float wf = (float)(mw->wwidth - 1) / (float) vs->w; 1171 const float hf = (float)(mw->wheight - 1) / (float) vs->h; 1172 int XW, YW; 1173 bool cont; 1174 Window junkW; 1175 1176 /* Figure where in the subwindow the click was, and stash in XW/YW */ 1177 XTranslateCoordinates(dpy, Scr->Root, sw, event->xbutton.x_root, 1178 event->xbutton.y_root, 1179 &XW, &YW, &junkW); 1180 1181 /* 1182 * Grab the pointer, lock it into the WSM, and get the events 1183 * related to buttons and movement. We don't need 1184 * PointerMotionMask, since that would only happen after buttons 1185 * are released, and we'll be done by then. 1186 */ 1187 XGrabPointer(dpy, mw->w, False, 1188 ButtonPressMask | ButtonMotionMask | ButtonReleaseMask, 1189 GrabModeAsync, GrabModeAsync, mw->w, Scr->MoveCursor, 1190 CurrentTime); 1191 1192 /* Start handling events until we're done */ 1193 alreadyvivible = false; 1194 cont = true; 1195 while(cont) { 1196 XEvent ev; 1197 1198 /* Grab the next event and handle */ 1199 XMaskEvent(dpy, ButtonPressMask | ButtonMotionMask | 1200 ButtonReleaseMask | ExposureMask, &ev); 1201 switch(ev.xany.type) { 1202 case Expose : { 1203 /* Something got exposed */ 1204 if(ev.xexpose.window == w) { 1205 /* 1206 * The win we're working with? We know how to do 1207 * that. 1208 */ 1209 WMapRedrawWindow(w, W0, H0, wl->cp, 1210 wl->twm_win->icon_name); 1211 break; 1212 } 1213 1214 /* Else, delegate to our global dispatcher */ 1215 Event = ev; 1216 DispatchEvent(); 1217 break; 1218 } 1219 1220 case ButtonPress : 1221 case ButtonRelease : { 1222 /* 1223 * Events for buttons other than the one whose press 1224 * started this activity are totally ignored. 1225 */ 1226 if(ev.xbutton.button != button) { 1227 break; 1228 } 1229 1230 /* 1231 * Otherwise, this is a press/release of the button 1232 * that started things. Though I'm not sure how it 1233 * could be a press, since it was already pressed. 1234 * Regardless, this is our exit condition. Fall 1235 * through into the Motion case to handle any 1236 * remaining movement. 1237 */ 1238 lastev = ev; 1239 cont = false; 1240 newX = ev.xbutton.x; 1241 newY = ev.xbutton.y; 1242 } 1243 1244 /* Everything remaining is motion handling */ 1245 case MotionNotify : { 1246 /* If we fell through from above, no new movement */ 1247 if(cont) { 1248 newX = ev.xmotion.x; 1249 newY = ev.xmotion.y; 1250 } 1251 1252 /* Lots to do if we're moving the window for real */ 1253 if(realmovemode) { 1254 int XSW, YSW; 1255 WorkSpace *cws; 1256 MapSubwindow *msw; 1257 1258 /* Did the move start in the currently visible WS? */ 1259 bool startincurrent = (oldws == vs->wsw->currentwspc); 1260 1261 /* Find the workspace we wound up in */ 1262 for(cws = Scr->workSpaceMgr.workSpaceList ; 1263 cws != NULL ; 1264 cws = cws->next) { 1265 msw = vs->wsw->mswl [cws->number]; 1266 if((newX >= msw->x) 1267 && (newX < msw->x + mw->wwidth) 1268 && (newY >= msw->y) 1269 && (newY < msw->y + mw->wheight)) { 1270 break; 1271 } 1272 } 1273 if(!cws) { 1274 /* None? Ignore. */ 1275 break; 1276 } 1277 1278 /* 1279 * Mouse is wherever it started inside the 1280 * subwindow, so figure the X/Y of the top left 1281 * of the subwindow from there. (coords relative 1282 * to the whole WSM because of grab) 1283 */ 1284 winX = newX - XW; 1285 winY = newY - YW; 1286 1287 /* XXX redundant w/previous? */ 1288 msw = vs->wsw->mswl[cws->number]; 1289 1290 /* 1291 * Figure where those coords are relative to the 1292 * per-workspace window. 1293 */ 1294 XTranslateCoordinates(dpy, mw->w, msw->w, 1295 winX, winY, &XSW, &YSW, &junkW); 1296 1297 /* 1298 * Now rework the win[XY] to be the coordinates 1299 * the window would be in the whole screen, based 1300 * on the [XY]SW inside the map, using our scale 1301 * factors. 1302 */ 1303 winX = (int)(XSW / wf); 1304 winY = (int)(YSW / hf); 1305 1306 /* 1307 * Clip to the screen if DontMoveOff is set. 1308 * 1309 * XXX Can we use the Constrain*() functions for 1310 * this, instead of implementing icky magic 1311 * internally? 1312 */ 1313 if(Scr->DontMoveOff) { 1314 const int width = win->frame_width; 1315 const int height = win->frame_height; 1316 1317 if((winX < Scr->BorderLeft) && ((Scr->MoveOffResistance < 0) || 1318 (winX > Scr->BorderLeft - Scr->MoveOffResistance))) { 1319 winX = Scr->BorderLeft; 1320 newX = msw->x + XW + Scr->BorderLeft * mw->wwidth / vs->w; 1321 } 1322 if(((winX + width) > vs->w - Scr->BorderRight) && 1323 ((Scr->MoveOffResistance < 0) || 1324 ((winX + width) < vs->w - Scr->BorderRight + Scr->MoveOffResistance))) { 1325 winX = vs->w - Scr->BorderRight - width; 1326 newX = msw->x + mw->wwidth * 1327 (1 - Scr->BorderRight / (double) vs->w) - wl->width + XW - 2; 1328 } 1329 if((winY < Scr->BorderTop) && ((Scr->MoveOffResistance < 0) || 1330 (winY > Scr->BorderTop - Scr->MoveOffResistance))) { 1331 winY = Scr->BorderTop; 1332 newY = msw->y + YW + Scr->BorderTop * mw->height / vs->h; 1333 } 1334 if(((winY + height) > vs->h - Scr->BorderBottom) && 1335 ((Scr->MoveOffResistance < 0) || 1336 ((winY + height) < vs->h - Scr->BorderBottom + Scr->MoveOffResistance))) { 1337 winY = vs->h - Scr->BorderBottom - height; 1338 newY = msw->y + mw->wheight 1339 * (1 - Scr->BorderBottom / (double) vs->h) 1340 * - wl->height + YW - 2; 1341 } 1342 } 1343 1344 1345 /* Setup the avatar for the new position of win */ 1346 WMapSetupWindow(win, winX, winY, -1, -1); 1347 1348 /* 1349 * If SWWMIW, we already made sure above the 1350 * window is always visible on the screen. So we 1351 * don't need to do any of the following steps to 1352 * maybe show it or maybe un-show it, since we 1353 * know it's already always shown. 1354 */ 1355 if(Scr->ShowWinWhenMovingInWmgr) { 1356 goto movewin; 1357 } 1358 1359 /* 1360 * If we wound up in the same workspace as is 1361 * being displayed on the screen, we need to make 1362 * sure that window is showing up on the screen 1363 * as a "about to be occupied here if you release 1364 * the button" indication. 1365 */ 1366 if(cws == vs->wsw->currentwspc) { 1367 if(alreadyvivible) { 1368 /* Unless it's already there */ 1369 goto movewin; 1370 } 1371 if(Scr->OpaqueMove) { 1372 XMoveWindow(dpy, win->frame, winX, winY); 1373 DisplayWin(vs, win); 1374 } 1375 else { 1376 MoveOutline(Scr->Root, 1377 winX - win->frame_bw, 1378 winY - win->frame_bw, 1379 win->frame_width + 2 * win->frame_bw, 1380 win->frame_height + 2 * win->frame_bw, 1381 win->frame_bw, 1382 win->title_height + win->frame_bw3D); 1383 } 1384 alreadyvivible = true; 1385 1386 /* 1387 * We already moved it, skip past the other 1388 * code trying to move it in other cases. 1389 */ 1390 goto move; 1391 } 1392 1393 /* 1394 * If it's for whatever reason not being shown on 1395 * the screen, then we don't need to move it; hop 1396 * straight to moving the avatar. 1397 */ 1398 if(!alreadyvivible) { 1399 goto move; 1400 } 1401 1402 /* 1403 * So it _is_ being shown. If it's not supposed 1404 * to be here, hide it away (don't need to worry 1405 * about AlwaysShow, it would have skipped past 1406 * us from ab0ve). Also, if we started moving 1407 * the window out of the visible workspace with 1408 * button 1, it's gonna not be here if you 1409 * release, so hide it away. 1410 */ 1411 if(!OCCUPY(win, vs->wsw->currentwspc) || 1412 (startincurrent && (button == 1))) { 1413 if(Scr->OpaqueMove) { 1414 Vanish(vs, win); 1415 XMoveWindow(dpy, win->frame, winX, winY); 1416 } 1417 else { 1418 MoveOutline(Scr->Root, 0, 0, 0, 0, 0, 0); 1419 } 1420 alreadyvivible = false; 1421 goto move; 1422 } 1423 1424movewin: 1425 /* 1426 * However we get here, the real window is 1427 * visible and needs to be moved. So move it. 1428 */ 1429 if(Scr->OpaqueMove) { 1430 XMoveWindow(dpy, win->frame, winX, winY); 1431 } 1432 else { 1433 MoveOutline(Scr->Root, 1434 winX - win->frame_bw, 1435 winY - win->frame_bw, 1436 win->frame_width + 2 * win->frame_bw, 1437 win->frame_height + 2 * win->frame_bw, 1438 win->frame_bw, 1439 win->title_height + win->frame_bw3D); 1440 } 1441 } 1442 1443move: 1444 /* 1445 * Just move the subwindow in the map to the new 1446 * location. 1447 */ 1448 XMoveWindow(dpy, w, newX - XW, newY - YW); 1449 1450 /* And we're done. Next event! */ 1451 break; 1452 } 1453 } 1454 } 1455 } 1456 1457 /* 1458 * Finished with dragging (button released). 1459 */ 1460 if(realmovemode) { 1461 /* Moving the real window? Move the real window. */ 1462 if(Scr->ShowWinWhenMovingInWmgr || alreadyvivible) { 1463 if(Scr->OpaqueMove && !OCCUPY(win, vs->wsw->currentwspc)) { 1464 Vanish(vs, win); 1465 } 1466 if(!Scr->OpaqueMove) { 1467 MoveOutline(Scr->Root, 0, 0, 0, 0, 0, 0); 1468 WMapRedrawName(vs, wl); 1469 } 1470 } 1471 SetupWindow(win, winX, winY, win->frame_width, win->frame_height, -1); 1472 } 1473 1474 /* 1475 * Last event that caused us to escape is other code's 1476 * responsibility, put it back in the queue. 1477 */ 1478 lastev.xbutton.subwindow = (Window) 0; 1479 lastev.xbutton.window = parent; 1480 XPutBackEvent(dpy, &lastev); 1481 1482 /* End our grab */ 1483 XUngrabPointer(dpy, CurrentTime); 1484 1485 /* 1486 * If you wanted to change workspaces, and clicked _outside_ a 1487 * window, it would have just switched way up near the top of the 1488 * function. But if you clicked _in_ a window [in the WSM map], it 1489 * would have to go through the whole fun to find out whether you 1490 * wanted to move/reoccupy the window, or were just wanting to change 1491 * WSen. 1492 * 1493 * So if it's been <250ms (completely arbitrary and non-configgable, 1494 * probably should be rethought) since you started, assume you just 1495 * wanted to switch workspaces. Don't do any occupation change, And 1496 * just switch. Also do some magic related to the map/button 1497 * toggling. 1498 * 1499 * XXX This still leaves any window _moves_ done. It seems like it 1500 * should probably revert those too? 1501 */ 1502 if((lastev.xbutton.time - etime) < 250) { 1503 KeyCode control_L_code, control_R_code; 1504 char keys [32]; 1505 1506 /* Re-show old miniwindow, destroy the temp, and warp to WS */ 1507 XMapWindow(dpy, sw); 1508 XDestroyWindow(dpy, w); 1509 GotoWorkSpace(vs, oldws); 1510 if(!Scr->DontWarpCursorInWMap) { 1511 WarpToWindow(win, Scr->RaiseOnWarp); 1512 } 1513 1514 /* 1515 * The control keys toggle between map and button state. If we 1516 * did a short move, and ctrl is being held at the end, flip the 1517 * state. This has several possible causes and effects. 1518 * 1519 * One is that _during_ a move, we don't do look through KeyPress 1520 * events, so we wouldn't see it happen yet. But after we're 1521 * done and return back into the event loop, that press will come 1522 * in and cause the state to change. It may be weird to the user 1523 * to see that happen, not when they hit ctrl, but _later_, after 1524 * they release the mouse button. The "best" solution may be to 1525 * find that press in the event queue and empty it out, but a 1526 * cheap solution is just to pre-flip it and then let the event 1527 * code flip it back. Flickery maybe, but easy. Now, _should_ we be 1528 * doing that? I'm a little doubtful... 1529 * 1530 * A second is that if the WSM is "naturally" in button mode, and 1531 * you temporarily ctrl-flip it into map mode and then click in a 1532 * window. This code will cause it to automatically flip back 1533 * after you release the mouse if you haven't released ctrl yet. 1534 * This is apparently needed because, unless you have 1535 * DontWarpCursorInWMap set, the previous few lines of code would 1536 * have shifted the cursor to the window you clicked, which means 1537 * you don't get a chance to release it in the WSM and flip the 1538 * state back. It seems a reasonable assumption that the user 1539 * wanted a temporary change of state just for the purposes of 1540 * the change. 1541 * 1542 * (if you had released the ctrl before the mouse, the release 1543 * event would already be queued up up on the WSM, so would flip 1544 * it back after we return) 1545 * 1546 * XXX Should we be checking DontToggleWorkSpaceManagerState 1547 * here? 1548 */ 1549 control_L_code = XKeysymToKeycode(dpy, XStringToKeysym("Control_L")); 1550 control_R_code = XKeysymToKeycode(dpy, XStringToKeysym("Control_R")); 1551 XQueryKeymap(dpy, keys); 1552 if((keys [control_L_code / 8] & ((char) 0x80 >> (control_L_code % 8))) || 1553 keys [control_R_code / 8] & ((char) 0x80 >> (control_R_code % 8))) { 1554 WMgrToggleState(vs); 1555 } 1556 return; 1557 } 1558 1559 1560 /* Find the workspace we finally found up in */ 1561 for(newws = Scr->workSpaceMgr.workSpaceList ; newws != NULL ; 1562 newws = newws->next) { 1563 MapSubwindow *msw = vs->wsw->mswl[newws->number]; 1564 if((newX >= msw->x) && (newX < msw->x + mw->wwidth) && 1565 (newY >= msw->y) && (newY < msw->y + mw->wheight)) { 1566 break; 1567 } 1568 } 1569 1570 /* And finish off whatever we're supposed to be doing */ 1571 switch(button) { 1572 case 1 : { /* moving to another workspace */ 1573 int occupation; 1574 1575 /* If nothing to change, re-map avatar and we're done */ 1576 if((newws == NULL) || (newws == oldws) || 1577 OCCUPY(wl->twm_win, newws)) { 1578 XMapWindow(dpy, sw); 1579 break; 1580 } 1581 1582 /* Out of the old, into the new */ 1583 occupation = win->occupation; 1584 occupation |= (1 << newws->number); 1585 occupation &= ~(1 << oldws->number); 1586 ChangeOccupation(win, occupation); 1587 1588 /* 1589 * Raise it to the top if it's in our current ws, and 1590 * properly slot it into the WSM map stack if not. 1591 */ 1592 if(newws == vs->wsw->currentwspc) { 1593 OtpRaise(win, WinWin); 1594 WMapRaise(win); 1595 } 1596 else { 1597 WMapRestack(newws); 1598 } 1599 1600 break; 1601 } 1602 1603 case 2 : { /* putting in extra workspace */ 1604 int occupation; 1605 1606 /* Nothing to do if it's going nowhere or places it already is */ 1607 if((newws == NULL) || (newws == oldws) || 1608 OCCUPY(wl->twm_win, newws)) { 1609 break; 1610 } 1611 1612 /* Move */ 1613 occupation = win->occupation | (1 << newws->number); 1614 ChangeOccupation(win, occupation); 1615 1616 /* Raise/stack */ 1617 if(newws == vs->wsw->currentwspc) { 1618 OtpRaise(win, WinWin); 1619 WMapRaise(win); 1620 } 1621 else { 1622 WMapRestack(newws); 1623 } 1624 break; 1625 } 1626 1627 /* 1628 * Should actually never hit this state; only 1/2 would have 1629 * gotten this far into the code... 1630 */ 1631 default : 1632 return; 1633 } 1634 1635 /* Clean up our temporary moving-around-in the WSM window */ 1636 XDestroyWindow(dpy, w); 1637} 1638 1639 1640 1641 1642/* 1643 **************************************************************** 1644 * 1645 * Functions for doing things with subwindows in the WSM in map state 1646 * 1647 **************************************************************** 1648 */ 1649 1650/* 1651 * Backend for mapping up windows in the WSM map. 1652 */ 1653static void 1654wmap_mapwin_backend(TwmWindow *win, bool handleraise) 1655{ 1656 VirtualScreen *vs; 1657 WorkSpace *ws; 1658 WinList *wl; 1659 1660 for(vs = Scr->vScreenList; vs != NULL; vs = vs->next) { 1661 for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) { 1662 for(wl = vs->wsw->mswl[ws->number]->wl; wl != NULL; wl = wl->next) { 1663 if(win == wl->twm_win) { 1664 /* 1665 * When called via deiconify, we might have to do 1666 * stuff related to auto-raising the window while we 1667 * de-iconify. When called via a map request, the 1668 * window is always wherever it previously was in the 1669 * stack. 1670 */ 1671 if(!handleraise || Scr->NoRaiseDeicon) { 1672 XMapWindow(dpy, wl->w); 1673 } 1674 else { 1675 XMapRaised(dpy, wl->w); 1676 } 1677 WMapRedrawName(vs, wl); 1678 break; 1679 } 1680 } 1681 } 1682 } 1683} 1684 1685/* 1686 * Map up a window's subwindow in the map-mode WSM. Happens as a result 1687 * of getting (or faking) a Map request event. Notably, _not_ in the 1688 * process of de-iconifying a window; mostly as a result of _creating_ 1689 * windows, or when a client maps itself without a request from us. 1690 * 1691 * x-ref comment on WMapDeIconify() for some subtle distinctions between 1692 * the two... 1693 */ 1694void 1695WMapMapWindow(TwmWindow *win) 1696{ 1697 /* We don't do raise handling */ 1698 wmap_mapwin_backend(win, false); 1699} 1700 1701 1702/* 1703 * De-iconify a window in the WSM map. The opposite of WMapIconify(), 1704 * and different from WMapMapWindow() in complicated ways. This function 1705 * winds up getting called when a window is de-iconified via a ctwm 1706 * function. 1707 */ 1708void 1709WMapDeIconify(TwmWindow *win) 1710{ 1711 /* If it's not showing anywhere, nothing to do. Is this possible? */ 1712 if(!win->vs) { 1713 return; 1714 } 1715 1716 /* Loop and map, handling raises if necessary */ 1717 wmap_mapwin_backend(win, true); 1718} 1719 1720 1721/* 1722 * Hide away a window in the WSM map. Happens when win is iconified; 1723 * different from destruction. 1724 */ 1725void 1726WMapIconify(TwmWindow *win) 1727{ 1728 VirtualScreen *vs; 1729 WorkSpace *ws; 1730 WinList *wl; 1731 1732 if(!win->vs) { 1733 return; 1734 } 1735 1736 for(vs = Scr->vScreenList; vs != NULL; vs = vs->next) { 1737 for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) { 1738 for(wl = vs->wsw->mswl[ws->number]->wl; wl != NULL; wl = wl->next) { 1739 if(win == wl->twm_win) { 1740 XUnmapWindow(dpy, wl->w); 1741 break; 1742 } 1743 } 1744 } 1745 } 1746} 1747 1748 1749/* 1750 * Position a window in the WSM. Happens as a result of moving things. 1751 */ 1752void 1753WMapSetupWindow(TwmWindow *win, int x, int y, int w, int h) 1754{ 1755 VirtualScreen *vs; 1756 1757 /* If it's an icon manager, or not on a vscreen, nothing to do */ 1758 if(win->isiconmgr || !win->vs) { 1759 return; 1760 } 1761 1762 /* If it's a WSM, we may be resetting size/position, but that's it */ 1763 if(win->iswspmgr) { 1764 if(w == -1) { 1765 return; 1766 } 1767 ResizeWorkSpaceManager(win->vs, win); 1768 return; 1769 } 1770 1771 /* If it's an occupy window, ditto */ 1772 if(win == Scr->workSpaceMgr.occupyWindow->twm_win) { 1773 if(w == -1) { 1774 return; 1775 } 1776 ResizeOccupyWindow(win); 1777 return; 1778 } 1779 1780 1781 /* For anything else, we're potentially showing something */ 1782 for(vs = Scr->vScreenList; vs != NULL; vs = vs->next) { 1783 WorkSpaceWindow *wsw = vs->wsw; 1784 WorkSpace *ws; 1785 1786 /* Scale factors for windows in the map */ 1787 float wf = (float)(wsw->wwidth - 2) / (float) vs->w; 1788 float hf = (float)(wsw->wheight - 2) / (float) vs->h; 1789 1790 /* 1791 * Loop around windows in each WS to find all the places the 1792 * requested window should show up. 1793 */ 1794 for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) { 1795 MapSubwindow *msw = wsw->mswl[ws->number]; 1796 1797 for(WinList *wl = msw->wl; wl != NULL; wl = wl->next) { 1798 if(win == wl->twm_win) { 1799 /* New positions */ 1800 wl->x = (int)(x * wf); 1801 wl->y = (int)(y * hf); 1802 1803 /* Rescale if necessary and move */ 1804 if(w == -1) { 1805 XMoveWindow(dpy, wl->w, wl->x, wl->y); 1806 } 1807 else { 1808 wl->width = (unsigned int)((w * wf) + 0.5); 1809 wl->height = (unsigned int)((h * hf) + 0.5); 1810 if(!Scr->use3Dwmap) { 1811 wl->width -= 2; 1812 wl->height -= 2; 1813 } 1814 if(wl->width < 1) { 1815 wl->width = 1; 1816 } 1817 if(wl->height < 1) { 1818 wl->height = 1; 1819 } 1820 XMoveResizeWindow(dpy, wl->w, wl->x, wl->y, 1821 wl->width, wl->height); 1822 } 1823 break; 1824 } 1825 } 1826 } 1827 } 1828} 1829 1830 1831/* 1832 * Frontends for changing the stacking of windows in the WSM. 1833 * 1834 * Strictly speaker, we have no ability to raise or lower a window in the 1835 * map; we only draw the whole stack. And these functions don't actually 1836 * change the stacking of a window, they're called as a _result_ of 1837 * changing the stacking, to notify the WSM to re-check and update 1838 * itself. So while conceptually we maintain a separation for our 1839 * callers between various reasons this is being called, the 1840 * implementations are identical. 1841 */ 1842void 1843WMapRaiseLower(TwmWindow *win) 1844{ 1845 WorkSpace *ws; 1846 1847 for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) { 1848 if(OCCUPY(win, ws)) { 1849 WMapRestack(ws); 1850 } 1851 } 1852} 1853 1854void 1855WMapLower(TwmWindow *win) 1856{ 1857 WMapRaiseLower(win); 1858} 1859 1860void 1861WMapRaise(TwmWindow *win) 1862{ 1863 WMapRaiseLower(win); 1864} 1865 1866 1867/* 1868 * Backend for redoing the stacking of a window in the WSM. 1869 * 1870 * XXX Since this tends to get called iteratively, there's probably 1871 * something better we can do than doing all this relatively expensive 1872 * stuff over and over... 1873 */ 1874void 1875WMapRestack(WorkSpace *ws) 1876{ 1877 WinList *wl; 1878 Window root, parent; // Dummy 1879 Window *children, *smallws; 1880 unsigned int nchildren; 1881 1882 /* Get a whole list of the windows on the screen */ 1883 nchildren = 0; 1884 XQueryTree(dpy, Scr->Root, &root, &parent, &children, &nchildren); 1885 1886 /* 1887 * We're presumably dealing with a [often very small] subset of them, 1888 * but just allocate space for the whole list; easier than trying to 1889 * shrink down, and really, how big can it possibly be? 1890 */ 1891 smallws = calloc(nchildren, sizeof(Window)); 1892 1893 /* Work it up per vscreen */ 1894 for(VirtualScreen *vs = Scr->vScreenList; vs != NULL; vs = vs->next) { 1895 int j = 0; 1896 const MapSubwindow *msw = vs->wsw->mswl[ws->number]; 1897 1898 /* Loop backward (from top to bottom of stack) */ 1899 for(int i = nchildren - 1; i >= 0; i--) { 1900 TwmWindow *win = GetTwmWindow(children[i]); 1901 1902 /* 1903 * Only care about certain windows. If it's not a window we 1904 * know about, or not a frame (e.g., an icon), or doesn't 1905 * occupy this workspace, skip it. 1906 */ 1907 if(win == NULL || win->frame != children[i] || !OCCUPY(win, ws)) { 1908 continue; 1909 } 1910 1911 /* Debug */ 1912 if(tracefile) { 1913 fprintf(tracefile, "WMapRestack : w = %lx, win = %p\n", 1914 children[i], (void *)win); 1915 fflush(tracefile); 1916 } 1917 1918 /* Find this window in the list for the map of this WS */ 1919 for(wl = msw->wl; wl != NULL; wl = wl->next) { 1920 /* Debug */ 1921 if(tracefile) { 1922 fprintf(tracefile, "WMapRestack : wl = %p, twm_win = %p\n", 1923 (void *)wl, (void *)wl->twm_win); 1924 fflush(tracefile); 1925 } 1926 1927 if(win == wl->twm_win) { 1928 /* There you are. Add into our list to restack. */ 1929 smallws[j++] = wl->w; 1930 break; 1931 } 1932 } 1933 } 1934 1935 /* 1936 * Restack the windows in the map. Note that the order is 1937 * reversed from earlier; XQueryTree() returns bottom->top, 1938 * XRestackWindows() is passed top->bottom. 1939 */ 1940 XRestackWindows(dpy, smallws, j); 1941 } 1942 1943 /* Cleanup */ 1944 XFree(children); 1945 free(smallws); 1946 return; 1947} 1948 1949 1950 1951 1952/* 1953 **************************************************************** 1954 * 1955 * Bits related to the actual drawing of the windows in the map. 1956 * 1957 **************************************************************** 1958 */ 1959 1960/* 1961 * Update stuff in the WSM when win's icon name changes 1962 */ 1963void 1964WMapUpdateIconName(TwmWindow *win) 1965{ 1966 VirtualScreen *vs; 1967 WorkSpace *ws; 1968 WinList *wl; 1969 1970 for(vs = Scr->vScreenList; vs != NULL; vs = vs->next) { 1971 for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) { 1972 for(wl = vs->wsw->mswl[ws->number]->wl; wl != NULL; wl = wl->next) { 1973 if(win == wl->twm_win) { 1974 WMapRedrawName(vs, wl); 1975 break; 1976 } 1977 } 1978 } 1979 } 1980} 1981 1982 1983/* 1984 * Draw a window name into the window's representation in the map-state 1985 * WSM. 1986 */ 1987void 1988WMapRedrawName(VirtualScreen *vs, WinList *wl) 1989{ 1990 ColorPair cp = wl->cp; 1991 1992 if(Scr->ReverseCurrentWorkspace && wl->wlist == vs->wsw->currentwspc) { 1993 InvertColorPair(&cp); 1994 } 1995 WMapRedrawWindow(wl->w, wl->width, wl->height, cp, wl->twm_win->icon_name); 1996} 1997 1998 1999/* 2000 * Draw up a window's representation in the map-state WSM, with the 2001 * window name. 2002 * 2003 * The drawing of the window name could probably be done a bit better. 2004 * The font size is based on a tiny fraction of the window's height, so 2005 * is probably usually too small to be useful, and often appears just as 2006 * some odd colored pixels at the top of the window. 2007 */ 2008static void 2009WMapRedrawWindow(Window window, int width, int height, 2010 ColorPair cp, const char *label) 2011{ 2012 int x, y; 2013 const MyFont font = Scr->workSpaceMgr.windowFont; 2014 2015 /* Blank out window background color */ 2016 XClearWindow(dpy, window); 2017 2018 /* Figure out where to position the name */ 2019 { 2020 XRectangle inc_rect; 2021 XRectangle logical_rect; 2022 int strhei, strwid; 2023 int i, descent; 2024 XFontStruct **xfonts; 2025 char **font_names; 2026 int fnum; 2027 2028 XmbTextExtents(font.font_set, label, strlen(label), 2029 &inc_rect, &logical_rect); 2030 strwid = logical_rect.width; 2031 strhei = logical_rect.height; 2032 2033 /* 2034 * If it's too tall to fit, just give up now. 2035 * XXX Probably should still do border stuff below... 2036 */ 2037 if(strhei > height) { 2038 return; 2039 } 2040 2041 x = (width - strwid) / 2; 2042 if(x < 1) { 2043 x = 1; 2044 } 2045 2046 fnum = XFontsOfFontSet(font.font_set, &xfonts, &font_names); 2047 for(i = 0, descent = 0; i < fnum; i++) { 2048 /* xf = xfonts[i]; */ 2049 descent = ((descent < (xfonts[i]->max_bounds.descent)) ? 2050 (xfonts[i]->max_bounds.descent) : descent); 2051 } 2052 2053 y = ((height + strhei) / 2) - descent; 2054 } 2055 2056 /* Draw up borders around the win */ 2057 if(Scr->use3Dwmap) { 2058 Draw3DBorder(window, 0, 0, width, height, 1, cp, off, true, false); 2059 FB(cp.fore, cp.back); 2060 } 2061 else { 2062 FB(cp.back, cp.fore); 2063 XFillRectangle(dpy, window, Scr->NormalGC, 0, 0, width, height); 2064 FB(cp.fore, cp.back); 2065 } 2066 2067 /* Write in the name */ 2068 if(Scr->Monochrome != COLOR) { 2069 XmbDrawImageString(dpy, window, font.font_set, Scr->NormalGC, x, y, 2070 label, strlen(label)); 2071 } 2072 else { 2073 XmbDrawString(dpy, window, font.font_set, Scr->NormalGC, x, y, 2074 label, strlen(label)); 2075 } 2076} 2077 2078 2079 2080 2081/* 2082 * Processes for adding/removing windows from the WSM 2083 */ 2084 2085/* 2086 * Add a window into any appropriate WSMs' maps. Called during 2087 * AddWindow(). 2088 */ 2089void 2090WMapAddWindow(TwmWindow *win) 2091{ 2092 WorkSpace *ws; 2093 2094 if(!WMapWindowMayBeAdded(win)) { 2095 return; 2096 } 2097 2098 for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) { 2099 if(OCCUPY(win, ws)) { 2100 WMapAddWindowToWorkspace(win, ws); 2101 } 2102 } 2103} 2104 2105 2106/* 2107 * Create WSM representation of a given window in a given WS. Called 2108 * when windows get added to a workspace, either via WMapAddWindow() 2109 * during the AddWindow() process, or via an occupation change. 2110 * 2111 * (previously: WMapAddToList()) 2112 */ 2113void 2114WMapAddWindowToWorkspace(TwmWindow *win, WorkSpace *ws) 2115{ 2116 ColorPair cp; 2117 2118 /* Setup coloring for the window */ 2119 cp.back = win->title.back; 2120 cp.fore = win->title.fore; 2121 if(Scr->workSpaceMgr.windowcpgiven) { 2122 cp.back = Scr->workSpaceMgr.windowcp.back; 2123 GetColorFromList(Scr->workSpaceMgr.windowBackgroundL, 2124 win->name, &win->class, &cp.back); 2125 cp.fore = Scr->workSpaceMgr.windowcp.fore; 2126 GetColorFromList(Scr->workSpaceMgr.windowForegroundL, 2127 win->name, &win->class, &cp.fore); 2128 } 2129 if(Scr->use3Dwmap && !Scr->BeNiceToColormap) { 2130 GetShadeColors(&cp); 2131 } 2132 2133 /* We need a copy in each VS */ 2134 for(VirtualScreen *vs = Scr->vScreenList; vs != NULL; vs = vs->next) { 2135 unsigned int bw; 2136 WinList *wl; 2137 const float wf = (float)(vs->wsw->wwidth - 2) / (float) vs->w; 2138 const float hf = (float)(vs->wsw->wheight - 2) / (float) vs->h; 2139 MapSubwindow *msw = vs->wsw->mswl[ws->number]; 2140 2141 /* Put together its winlist entry */ 2142 wl = malloc(sizeof(struct winList)); 2143 wl->wlist = ws; 2144 wl->x = (int)(win->frame_x * wf); 2145 wl->y = (int)(win->frame_y * hf); 2146 wl->width = (unsigned int)((win->frame_width * wf) + 0.5); 2147 wl->height = (unsigned int)((win->frame_height * hf) + 0.5); 2148 wl->cp = cp; 2149 wl->twm_win = win; 2150 2151 /* Size the window bits */ 2152 bw = 0; 2153 if(!Scr->use3Dwmap) { 2154 bw = 1; 2155 wl->width -= 2; 2156 wl->height -= 2; 2157 } 2158 if(wl->width < 1) { 2159 wl->width = 1; 2160 } 2161 if(wl->height < 1) { 2162 wl->height = 1; 2163 } 2164 2165 /* Create its little window */ 2166 wl->w = XCreateSimpleWindow(dpy, msw->w, wl->x, wl->y, 2167 wl->width, wl->height, bw, 2168 Scr->Black, cp.back); 2169 2170 /* Setup cursor and attributes for it */ 2171 { 2172 XSetWindowAttributes attr; 2173 unsigned long attrmask; 2174 2175 attr.cursor = handCursor; 2176 attrmask = CWCursor; 2177 2178 if(Scr->BackingStore) { 2179 attr.backing_store = WhenMapped; 2180 attrmask |= CWBackingStore; 2181 } 2182 2183 XChangeWindowAttributes(dpy, wl->w, attrmask, &attr); 2184 } 2185 2186 /* Setup events and stash context bits */ 2187 XSelectInput(dpy, wl->w, ExposureMask); 2188 XSaveContext(dpy, wl->w, TwmContext, (XPointer) vs->wsw->twm_win); 2189 XSaveContext(dpy, wl->w, ScreenContext, (XPointer) Scr); 2190 XSaveContext(dpy, wl->w, MapWListContext, (XPointer) wl); 2191 2192 /* Link it onto the front of the list */ 2193 wl->next = msw->wl; 2194 msw->wl = wl; 2195 2196 /* 2197 * And map it, if its window is mapped. That'll kick an expose 2198 * event, which will work its way down to WMapRedrawWindow(), and 2199 * fill things in. 2200 */ 2201 if(win->mapped) { 2202 XMapWindow(dpy, wl->w); 2203 } 2204 } // And around to the next vscreen 2205} 2206 2207 2208/* 2209 * Remove a window from any WSM maps it's in. Called during window 2210 * destruction process. 2211 * 2212 * (previously: WMapDestroyWindow()) 2213 */ 2214void 2215WMapRemoveWindow(TwmWindow *win) 2216{ 2217 WorkSpace *ws; 2218 2219 for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) { 2220 if(OCCUPY(win, ws)) { 2221 WMapRemoveWindowFromWorkspace(win, ws); 2222 } 2223 } 2224 2225 /* 2226 * If it's a mapped occupy window, manually hide aways its bits in 2227 * here. 2228 * 2229 * XXX Better belongs inline in caller or separate func? This is the 2230 * only thing exposing occupyWin out of occupation.c. 2231 */ 2232 if(win == occupyWin) { 2233 OccupyWindow *occwin = Scr->workSpaceMgr.occupyWindow; 2234 XUnmapWindow(dpy, occwin->twm_win->frame); 2235 occwin->twm_win->mapped = false; 2236 occwin->twm_win->occupation = 0; 2237 occupyWin = NULL; 2238 } 2239} 2240 2241 2242/* 2243 * Remove window's WSM representation. Happens from WMapRemoveWindow() 2244 * as part of the window destruction process, and in the occupation 2245 * change process. 2246 * 2247 * (previously: WMapRemoveFromList()) 2248 */ 2249void 2250WMapRemoveWindowFromWorkspace(TwmWindow *win, WorkSpace *ws) 2251{ 2252 VirtualScreen *vs; 2253 2254 for(vs = Scr->vScreenList; vs != NULL; vs = vs->next) { 2255 WinList **prev = &vs->wsw->mswl[ws->number]->wl; 2256 2257 /* Pull it out of the list and destroy it */ 2258 for(WinList *wl = *prev ; wl != NULL ; wl = wl->next) { 2259 if(win != wl->twm_win) { 2260 /* Not it */ 2261 prev = &wl->next; 2262 continue; 2263 } 2264 2265 /* There you are. Unlink and kill */ 2266 *prev = wl->next; 2267 2268 XDeleteContext(dpy, wl->w, TwmContext); 2269 XDeleteContext(dpy, wl->w, ScreenContext); 2270 XDeleteContext(dpy, wl->w, MapWListContext); 2271 XDestroyWindow(dpy, wl->w); 2272 free(wl); 2273 2274 /* Around to the next vscreen */ 2275 break; 2276 } 2277 } 2278} 2279 2280 2281 2282 2283/* 2284 **************************************************************** 2285 * 2286 * Utils-ish funcs 2287 * 2288 **************************************************************** 2289 */ 2290 2291/* 2292 * This is really more util.c fodder, but leaving it here for now because 2293 * it's only used once in WMapRedrawName(). If we start finding external 2294 * uses for it, it should be moved. 2295 */ 2296static void 2297InvertColorPair(ColorPair *cp) 2298{ 2299 Pixel save; 2300 2301 save = cp->fore; 2302 cp->fore = cp->back; 2303 cp->back = save; 2304 save = cp->shadc; 2305 cp->shadc = cp->shadd; 2306 cp->shadd = save; 2307} 2308 2309 2310/* 2311 * Verify if a window may be added to the workspace map. 2312 * This is not allowed for 2313 * - icon managers 2314 * - the occupy window 2315 * - workspace manager windows 2316 * - or, optionally, windows with full occupation. 2317 */ 2318bool 2319WMapWindowMayBeAdded(TwmWindow *win) 2320{ 2321 if(win->isiconmgr) { 2322 return false; 2323 } 2324 if(win == Scr->workSpaceMgr.occupyWindow->twm_win) { 2325 return false; 2326 } 2327 if(win->iswspmgr) { 2328 return false; 2329 } 2330 if(Scr->workSpaceMgr.noshowoccupyall && 2331 win->occupation == fullOccupation) { 2332 return false; 2333 } 2334 return true; 2335} 2336