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