functions_win_moveresize.c revision 0bbfda8a
1/* 2 * Functions related to moving/resizing windows. 3 */ 4 5#include "ctwm.h" 6 7#include <stdio.h> 8#include <stdlib.h> 9 10#include "colormaps.h" 11#include "events.h" 12#include "event_handlers.h" 13#include "functions.h" 14#include "functions_defs.h" 15#include "functions_internal.h" 16#include "icons.h" 17#include "otp.h" 18#include "parse.h" 19#include "screen.h" 20#include "util.h" 21#include "vscreen.h" 22#include "win_decorations.h" 23#include "win_ops.h" 24#include "win_resize.h" 25#include "win_utils.h" 26#include "workspace_manager.h" 27 28 29/* 30 * MoveFillDir-ectional specifiers, used in jump/pack/fill 31 */ 32typedef enum { 33 MFD_BOTTOM, 34 MFD_LEFT, 35 MFD_RIGHT, 36 MFD_TOP, 37} MoveFillDir; 38static int FindConstraint(TwmWindow *tmp_win, MoveFillDir direction); 39 40/* Internal util */ 41static bool belongs_to_twm_window(TwmWindow *t, Window w); 42 43 44/* 45 * Constrained move variables 46 * 47 * Used in the resize handling, but needed over in event code for 48 * ButtonRelease as well. 49 */ 50bool ConstMove = false; 51CMoveDir ConstMoveDir; 52int ConstMoveX; 53int ConstMoveY; 54int ConstMoveXL; 55int ConstMoveXR; 56int ConstMoveYT; 57int ConstMoveYB; 58 59/* 60 * Which move-ish function is in progress. This is _almost_ really a 61 * local var in the movewindow() function, but we also reference it in 62 * the HandleButtonRelease() event handler because that has to know 63 * which move variant we're doing to figure out whether it has to 64 * constrain the final coordinates in various ways. 65 */ 66int MoveFunction; 67 68/* 69 * Globals used to keep track of whether the mouse has moved during a 70 * resize function. 71 */ 72int ResizeOrigX; 73int ResizeOrigY; 74 75 76 77/* 78 * Now, on to the actual handlers. 79 */ 80 81 82/* 83 ********************************************************* 84 * 85 * First, the various methods of moving windows around. 86 * 87 ********************************************************* 88 */ 89 90/* 91 * Simple f.move and related 92 */ 93static void movewindow(EF_FULLPROTO); 94DFHANDLER(move) 95{ 96 movewindow(EF_ARGS); 97} 98DFHANDLER(forcemove) 99{ 100 movewindow(EF_ARGS); 101} 102DFHANDLER(movepack) 103{ 104 movewindow(EF_ARGS); 105} 106DFHANDLER(movepush) 107{ 108 movewindow(EF_ARGS); 109} 110 111/* f.move and friends backend */ 112static void 113movewindow(EF_FULLPROTO) 114{ 115 int origX, origY; 116 bool moving_icon; 117 bool fromtitlebar; 118 const Window dragroot = Scr->XineramaRoot; 119 const Window rootw = eventp->xbutton.root; 120 121 /* Better not be a menu open */ 122 PopDownMenu(); 123 124 /* Stash up just which f.move* variant we are */ 125 MoveFunction = func; 126 127 /* 128 * Figure whether we're moving opaquely. 129 */ 130 if(tmp_win->OpaqueMove) { 131 if(Scr->OpaqueMoveThreshold >= 200) { 132 Scr->OpaqueMove = true; 133 } 134 else { 135 const unsigned long sw = tmp_win->frame_width 136 * tmp_win->frame_height; 137 const unsigned long ss = Scr->rootw * Scr->rooth; 138 const float sf = Scr->OpaqueMoveThreshold / 100.0; 139 140 if(sw > (ss * sf)) { 141 Scr->OpaqueMove = false; 142 } 143 else { 144 Scr->OpaqueMove = true; 145 } 146 } 147 } 148 else { 149 Scr->OpaqueMove = false; 150 } 151 152 /* If it's in a WindowBox, adjust coordinates as necessary */ 153 if(tmp_win->winbox) { 154 XTranslateCoordinates(dpy, dragroot, tmp_win->winbox->window, 155 eventp->xbutton.x_root, eventp->xbutton.y_root, 156 &(eventp->xbutton.x_root), &(eventp->xbutton.y_root), &JunkChild); 157 } 158 159 /* 160 * XXX pulldown=true only when we're triggering from a ButtonRelease 161 * in a menu, and this warp should only be going somewhere if we hit 162 * the winbox case above and had to translate the coordinates? But, 163 * in that case, the coordinates would be changed to be relative to 164 * the winbox window, and here we're positioning relative to Root? 165 */ 166 if(pulldown) 167 XWarpPointer(dpy, None, Scr->Root, 168 0, 0, 0, 0, eventp->xbutton.x_root, eventp->xbutton.y_root); 169 170 /* 171 * Stub out handlers for enter/leave notifications while we do stuff. 172 * They get reset toward the end of the ButtonRelease handler. 173 */ 174 EventHandler[EnterNotify] = HandleUnknown; 175 EventHandler[LeaveNotify] = HandleUnknown; 176 177 if(!Scr->NoGrabServer || !Scr->OpaqueMove) { 178 XGrabServer(dpy); 179 } 180 181 /* 182 * Setup size for the window showing current location as we move it. 183 * The same window is used for resize ops too, where it might be a 184 * different size. 185 */ 186 Scr->SizeStringOffset = SIZE_HINDENT; 187 XResizeWindow(dpy, Scr->SizeWindow, 188 Scr->SizeStringWidth + SIZE_HINDENT * 2, 189 Scr->SizeFont.height + SIZE_VINDENT * 2); 190 XMapRaised(dpy, Scr->SizeWindow); 191 192 /* 193 * Use XGrabPointer() to configure how we get events locations 194 * reported relative to what root. 195 */ 196 { 197 const Window grabwin = (tmp_win->winbox ? tmp_win->winbox->window 198 : Scr->XineramaRoot); 199 200 XGrabPointer(dpy, grabwin, True, 201 ButtonPressMask | ButtonReleaseMask | 202 ButtonMotionMask | PointerMotionMask, 203 GrabModeAsync, GrabModeAsync, grabwin, Scr->MoveCursor, 204 CurrentTime); 205 } 206 207 /* 208 * Set w to what we're actually moving. If it's an icon, we always 209 * move it opaquely anyway. If it's a window (that's not iconofied), 210 * we move the frame. 211 */ 212 moving_icon = false; 213 if(context == C_ICON && tmp_win->icon && tmp_win->icon->w) { 214 w = tmp_win->icon->w; 215 DragX = eventp->xbutton.x; 216 DragY = eventp->xbutton.y; 217 moving_icon = true; 218 if(tmp_win->OpaqueMove) { 219 Scr->OpaqueMove = true; 220 } 221 } 222 else if(! tmp_win->icon || w != tmp_win->icon->w) { 223 XTranslateCoordinates(dpy, w, tmp_win->frame, 224 eventp->xbutton.x, 225 eventp->xbutton.y, 226 &DragX, &DragY, &JunkChild); 227 228 w = tmp_win->frame; 229 } 230 231 DragWindow = None; 232 233 /* Get x/y relative to parent window, i.e. the virtual screen, Root. 234 * XMoveWindow() moves are relative to this. 235 * MoveOutline()s however are drawn from the XineramaRoot since they 236 * may cross virtual screens. 237 */ 238 XGetGeometry(dpy, w, &JunkRoot, &origDragX, &origDragY, 239 &DragWidth, &DragHeight, &DragBW, 240 &JunkDepth); 241 242 origX = eventp->xbutton.x_root; 243 origY = eventp->xbutton.y_root; 244 CurrentDragX = origDragX; 245 CurrentDragY = origDragY; 246 247 /* 248 * Setup ConstrainedMove if this is a double-click. That means 249 * setting the flags, and moving the pointer off to the middle of the 250 * window. 251 * 252 * Only do the constrained move if timer is set; need to check it 253 * in case of stupid or wicked fast servers 254 */ 255 if(ConstrainedMoveTime && 256 (eventp->xbutton.time - last_time) < ConstrainedMoveTime) { 257 int width, height; 258 259 ConstMove = true; 260 ConstMoveDir = MOVE_NONE; 261 ConstMoveX = eventp->xbutton.x_root - DragX - DragBW; 262 ConstMoveY = eventp->xbutton.y_root - DragY - DragBW; 263 width = DragWidth + 2 * DragBW; 264 height = DragHeight + 2 * DragBW; 265 ConstMoveXL = ConstMoveX + width / 3; 266 ConstMoveXR = ConstMoveX + 2 * (width / 3); 267 ConstMoveYT = ConstMoveY + height / 3; 268 ConstMoveYB = ConstMoveY + 2 * (height / 3); 269 270 XWarpPointer(dpy, None, w, 271 0, 0, 0, 0, DragWidth / 2, DragHeight / 2); 272 273 XQueryPointer(dpy, w, &JunkRoot, &JunkChild, 274 &JunkX, &JunkY, &DragX, &DragY, &JunkMask); 275 } 276 last_time = eventp->xbutton.time; 277 278 /* If not moving opaquely, setup the outline bits */ 279 if(!Scr->OpaqueMove) { 280 InstallRootColormap(); 281 if(!Scr->MoveDelta) { 282 /* 283 * Draw initial outline. This was previously done the 284 * first time though the outer loop by dropping out of 285 * the XCheckMaskEvent inner loop down to one of the 286 * MoveOutline's below. 287 */ 288 MoveOutline(dragroot, 289 origDragX - DragBW + Scr->currentvs->x, 290 origDragY - DragBW + Scr->currentvs->y, 291 DragWidth + 2 * DragBW, DragHeight + 2 * DragBW, 292 tmp_win->frame_bw, 293 moving_icon ? 0 : tmp_win->title_height + tmp_win->frame_bw3D); 294 /* 295 * This next line causes HandleReleaseNotify to call 296 * XRaiseWindow(). This is solely to preserve the 297 * previous behaviour that raises a window being moved 298 * on button release even if you never actually moved 299 * any distance (unless you move less than MoveDelta or 300 * NoRaiseMove is set or OpaqueMove is set). 301 */ 302 DragWindow = w; 303 } 304 } 305 306 /* 307 * Init whether triggered from something on the titlebar (e.g., a 308 * TitleButton bound to f.move). We need to keep this var in a scope 309 * outside the event loop below because the resetting of it in there 310 * is supposed to have effect on future loops. 311 */ 312 fromtitlebar = belongs_to_twm_window(tmp_win, eventp->xbutton.window); 313 314 if(menuFromFrameOrWindowOrTitlebar) { 315 /* warp the pointer to the middle of the window */ 316 XWarpPointer(dpy, None, Scr->Root, 0, 0, 0, 0, 317 origDragX + DragWidth / 2, 318 origDragY + DragHeight / 2); 319 XFlush(dpy); 320 } 321 322 /* Fill in the position window with where we're starting */ 323 DisplayPosition(tmp_win, CurrentDragX, CurrentDragY); 324 325 /* 326 * Internal event loop for doing the moving. 327 */ 328 while(1) { 329 const long releaseEvent = menuFromFrameOrWindowOrTitlebar ? 330 ButtonPress : ButtonRelease; 331 const long movementMask = menuFromFrameOrWindowOrTitlebar ? 332 PointerMotionMask : ButtonMotionMask; 333 334 /* block until there is an interesting event */ 335 XMaskEvent(dpy, ButtonPressMask | ButtonReleaseMask | 336 EnterWindowMask | LeaveWindowMask | 337 ExposureMask | movementMask | 338 VisibilityChangeMask, &Event); 339 340 /* throw away enter and leave events until release */ 341 if(Event.xany.type == EnterNotify || 342 Event.xany.type == LeaveNotify) { 343 continue; 344 } 345 346 /* discard any extra motion events before a logical release */ 347 if(Event.type == MotionNotify) { 348 while(XCheckMaskEvent(dpy, movementMask | releaseEvent, &Event)) 349 if(Event.type == releaseEvent) { 350 break; 351 } 352 } 353 354 /* test to see if we have a second button press to abort move */ 355 if(!menuFromFrameOrWindowOrTitlebar) { 356 if(Event.type == ButtonPress && DragWindow != None) { 357 Cursor cur; 358 if(Scr->OpaqueMove) { 359 XMoveWindow(dpy, DragWindow, origDragX, origDragY); 360 if(moving_icon) { 361 tmp_win->icon->w_x = origDragX; 362 tmp_win->icon->w_y = origDragY; 363 } 364 } 365 else { 366 MoveOutline(dragroot, 0, 0, 0, 0, 0, 0); 367 } 368 DragWindow = None; 369 370 XUnmapWindow(dpy, Scr->SizeWindow); 371 cur = LeftButt; 372 if(Event.xbutton.button == Button2) { 373 cur = MiddleButt; 374 } 375 else if(Event.xbutton.button >= Button3) { 376 cur = RightButt; 377 } 378 379 XGrabPointer(dpy, Scr->Root, True, 380 ButtonReleaseMask | ButtonPressMask, 381 GrabModeAsync, GrabModeAsync, 382 Scr->Root, cur, CurrentTime); 383 func_reset_cursor = false; // Leave cursor alone 384 return; 385 } 386 } 387 388 if(fromtitlebar && Event.type == ButtonPress) { 389 fromtitlebar = false; 390 CurrentDragX = origX = Event.xbutton.x_root; 391 CurrentDragY = origY = Event.xbutton.y_root; 392 XTranslateCoordinates(dpy, rootw, tmp_win->frame, 393 origX, origY, 394 &DragX, &DragY, &JunkChild); 395 continue; 396 } 397 398 if(!DispatchEvent2()) { 399 continue; 400 } 401 402 if(Cancel) { 403 WindowMoved = false; 404 if(!Scr->OpaqueMove) { 405 UninstallRootColormap(); 406 } 407 func_reset_cursor = false; // Leave cursor alone 408 return; 409 } 410 if(Event.type == releaseEvent) { 411 MoveOutline(dragroot, 0, 0, 0, 0, 0, 0); 412 if(moving_icon && 413 ((CurrentDragX != origDragX || 414 CurrentDragY != origDragY))) { 415 tmp_win->icon_moved = true; 416 } 417 if(!Scr->OpaqueMove && menuFromFrameOrWindowOrTitlebar) { 418 int xl = Event.xbutton.x_root - (DragWidth / 2), 419 yt = Event.xbutton.y_root - (DragHeight / 2); 420 if(!moving_icon && 421 (MoveFunction == F_MOVEPACK || MoveFunction == F_MOVEPUSH)) { 422 TryToPack(tmp_win, &xl, &yt); 423 } 424 XMoveWindow(dpy, DragWindow, xl, yt); 425 } 426 if(menuFromFrameOrWindowOrTitlebar) { 427 DragWindow = None; 428 } 429 break; 430 } 431 432 /* something left to do only if the pointer moved */ 433 if(Event.type != MotionNotify) { 434 continue; 435 } 436 437 /* Get info about where the pointer is */ 438 XQueryPointer(dpy, rootw, &(eventp->xmotion.root), &JunkChild, 439 &(eventp->xmotion.x_root), &(eventp->xmotion.y_root), 440 &JunkX, &JunkY, &JunkMask); 441 442 /* 443 * Tweak up for root. XXX Is this even right? There are too 444 * many Root's, and this corrects for a specific one, but I'm not 445 * sure it's the right one... 446 */ 447 FixRootEvent(eventp); 448 449 /* Tweak for window box, if this is in one */ 450 if(tmp_win->winbox) { 451 XTranslateCoordinates(dpy, dragroot, tmp_win->winbox->window, 452 eventp->xmotion.x_root, eventp->xmotion.y_root, 453 &(eventp->xmotion.x_root), &(eventp->xmotion.y_root), &JunkChild); 454 } 455 456 /* 457 * If we haven't moved MoveDelta yet, we're not yet sure we're 458 * doing anything, so just loop back around. 459 */ 460 if(DragWindow == None && 461 abs(eventp->xmotion.x_root - origX) < Scr->MoveDelta && 462 abs(eventp->xmotion.y_root - origY) < Scr->MoveDelta) { 463 continue; 464 } 465 466 /* 467 * Now we know we're moving whatever the window is. 468 */ 469 DragWindow = w; 470 471 /* Raise when the move starts if we should */ 472 if(!Scr->NoRaiseMove && Scr->OpaqueMove && !WindowMoved) { 473 TwmWindow *t; 474 475 /* 476 * XXX In several of the error cases listed in here, it's 477 * seems almost that we should just abort the whole move 478 * process immediately if any of them are hit, because things 479 * get nonsensical. 480 */ 481 482 /* Find TwmWindow bits related to what we're dragging */ 483 if(XFindContext(dpy, DragWindow, TwmContext, (XPointer *) &t) == XCNOENT) { 484 fprintf(stderr, "%s(): Can't find TwmWindow.\n", __func__); 485 /* XXX abort? */ 486 t = NULL; 487 } 488 489 if(t != tmp_win) { 490 fprintf(stderr, "%s(): DragWindow isn't tmp_win!\n", __func__); 491 /* XXX abort? */ 492 } 493 494 if(t == NULL) { 495 /* Don't try doing this stuff... */ 496 } 497 else if(DragWindow == t->frame) { 498 if(moving_icon) { 499 fprintf(stderr, "%s(): moving_icon is true incorrectly!\n", 500 __func__); 501 } 502 OtpRaise(t, WinWin); 503 } 504 else if(t->icon && DragWindow == t->icon->w) { 505 if(!moving_icon) { 506 fprintf(stderr, "%s(): moving_icon is false incorrectly!\n", 507 __func__); 508 } 509 OtpRaise(t, IconWin); 510 } 511 else { 512 fprintf(stderr, "%s(): Couldn't figure what to raise.\n", 513 __func__); 514 } 515 } 516 517 WindowMoved = true; 518 519 /* 520 * Handle moving the step 521 */ 522 if(ConstMove) { 523 /* Did we already decide it's constrained? Do that. */ 524 switch(ConstMoveDir) { 525 case MOVE_NONE: { 526 /* Haven't figured direction yet, so do so */ 527 if(eventp->xmotion.x_root < ConstMoveXL || 528 eventp->xmotion.x_root > ConstMoveXR) { 529 ConstMoveDir = MOVE_HORIZ; 530 } 531 532 if(eventp->xmotion.y_root < ConstMoveYT || 533 eventp->xmotion.y_root > ConstMoveYB) { 534 ConstMoveDir = MOVE_VERT; 535 } 536 537 XQueryPointer(dpy, DragWindow, &JunkRoot, &JunkChild, 538 &JunkX, &JunkY, &DragX, &DragY, &JunkMask); 539 break; 540 } 541 542 /* We know which dir it's contrained to, so figure amount */ 543 case MOVE_VERT: 544 ConstMoveY = eventp->xmotion.y_root - DragY - DragBW; 545 break; 546 547 case MOVE_HORIZ: 548 ConstMoveX = eventp->xmotion.x_root - DragX - DragBW; 549 break; 550 } 551 552 /* We've got a move to do, so do it */ 553 if(ConstMoveDir != MOVE_NONE) { 554 int xl, yt, width, height; 555 556 xl = ConstMoveX; 557 yt = ConstMoveY; 558 width = DragWidth + 2 * DragBW; 559 height = DragHeight + 2 * DragBW; 560 561 if(Scr->DontMoveOff && MoveFunction != F_FORCEMOVE) { 562 TryToGrid(tmp_win, &xl, &yt); 563 } 564 if(!moving_icon && MoveFunction == F_MOVEPUSH && Scr->OpaqueMove) { 565 TryToPush(tmp_win, xl, yt); 566 } 567 568 if(!moving_icon && 569 (MoveFunction == F_MOVEPACK || MoveFunction == F_MOVEPUSH)) { 570 TryToPack(tmp_win, &xl, &yt); 571 } 572 573 if(Scr->DontMoveOff && MoveFunction != F_FORCEMOVE) { 574 ConstrainByBorders(tmp_win, &xl, width, &yt, height); 575 } 576 CurrentDragX = xl; 577 CurrentDragY = yt; 578 if(Scr->OpaqueMove) { 579 if(MoveFunction == F_MOVEPUSH && !moving_icon) { 580 SetupWindow(tmp_win, xl, yt, 581 tmp_win->frame_width, tmp_win->frame_height, -1); 582 } 583 else { 584 XMoveWindow(dpy, DragWindow, xl, yt); 585 if(moving_icon) { 586 tmp_win->icon->w_x = xl; 587 tmp_win->icon->w_y = yt; 588 } 589 } 590 WMapSetupWindow(tmp_win, xl, yt, -1, -1); 591 } 592 else { 593 MoveOutline(dragroot, xl + Scr->currentvs->x, 594 yt + Scr->currentvs->y, width, height, 595 tmp_win->frame_bw, 596 moving_icon ? 0 : tmp_win->title_height + tmp_win->frame_bw3D); 597 } 598 } 599 } 600 else if(DragWindow != None) { 601 /* 602 * There's a non-constrained move to process 603 * 604 * This is split out for virtual screens. In that case, it's 605 * possible to drag windows from one workspace to another, and 606 * as such, these need to be adjusted to the root, rather 607 * than this virtual screen... 608 */ 609 const int xroot = eventp->xmotion.x_root; 610 const int yroot = eventp->xmotion.y_root; 611 const int width = DragWidth + 2 * DragBW; 612 const int height = DragHeight + 2 * DragBW; 613 int xl, yt; 614 615 if(!menuFromFrameOrWindowOrTitlebar) { 616 xl = xroot - DragX - DragBW; 617 yt = yroot - DragY - DragBW; 618 } 619 else { 620 xl = xroot - (DragWidth / 2); 621 yt = yroot - (DragHeight / 2); 622 } 623 624 if(Scr->DontMoveOff && MoveFunction != F_FORCEMOVE) { 625 TryToGrid(tmp_win, &xl, &yt); 626 } 627 if(!moving_icon && MoveFunction == F_MOVEPUSH && Scr->OpaqueMove) { 628 TryToPush(tmp_win, xl, yt); 629 } 630 631 if(!moving_icon && 632 (MoveFunction == F_MOVEPACK || MoveFunction == F_MOVEPUSH)) { 633 TryToPack(tmp_win, &xl, &yt); 634 } 635 636 if(Scr->DontMoveOff && MoveFunction != F_FORCEMOVE) { 637 ConstrainByBorders(tmp_win, &xl, width, &yt, height); 638 } 639 640 CurrentDragX = xl; 641 CurrentDragY = yt; 642 if(Scr->OpaqueMove) { 643 if(MoveFunction == F_MOVEPUSH && !moving_icon) { 644 SetupWindow(tmp_win, xl, yt, 645 tmp_win->frame_width, tmp_win->frame_height, -1); 646 } 647 else { 648 XMoveWindow(dpy, DragWindow, xl, yt); 649 if(moving_icon) { 650 tmp_win->icon->w_x = xl; 651 tmp_win->icon->w_y = yt; 652 } 653 } 654 if(! moving_icon) { 655 WMapSetupWindow(tmp_win, xl, yt, -1, -1); 656 } 657 } 658 else { 659 MoveOutline(dragroot, xl + Scr->currentvs->x, 660 yt + Scr->currentvs->y, width, height, 661 tmp_win->frame_bw, 662 moving_icon ? 0 : tmp_win->title_height + tmp_win->frame_bw3D); 663 } 664 } 665 666 /* We've moved a step, so update the displayed position */ 667 DisplayPosition(tmp_win, CurrentDragX, CurrentDragY); 668 } 669 670 /* Done, so hide away the position display window */ 671 XUnmapWindow(dpy, Scr->SizeWindow); 672 673 /* Restore colormap if we replaced it */ 674 if(!Scr->OpaqueMove && DragWindow == None) { 675 UninstallRootColormap(); 676 } 677 678 return; 679} 680 681 682/* 683 * f.pack -- moving until collision 684 * 685 * XXX Collapse this down; no need for an extra level of indirection on 686 * the function calling. 687 */ 688static void packwindow(TwmWindow *tmp_win, const char *direction); 689DFHANDLER(pack) 690{ 691 if(tmp_win->squeezed) { 692 XBell(dpy, 0); 693 return; 694 } 695 packwindow(tmp_win, action); 696} 697 698static void 699packwindow(TwmWindow *tmp_win, const char *direction) 700{ 701 int cons, newx, newy; 702 int x, y, px, py, junkX, junkY; 703 unsigned int junkK; 704 Window junkW; 705 706 if(!strcmp(direction, "left")) { 707 cons = FindConstraint(tmp_win, MFD_LEFT); 708 if(cons == -1) { 709 return; 710 } 711 newx = cons; 712 newy = tmp_win->frame_y; 713 } 714 else if(!strcmp(direction, "right")) { 715 cons = FindConstraint(tmp_win, MFD_RIGHT); 716 if(cons == -1) { 717 return; 718 } 719 newx = cons; 720 newx -= tmp_win->frame_width + 2 * tmp_win->frame_bw; 721 newy = tmp_win->frame_y; 722 } 723 else if(!strcmp(direction, "top")) { 724 cons = FindConstraint(tmp_win, MFD_TOP); 725 if(cons == -1) { 726 return; 727 } 728 newx = tmp_win->frame_x; 729 newy = cons; 730 } 731 else if(!strcmp(direction, "bottom")) { 732 cons = FindConstraint(tmp_win, MFD_BOTTOM); 733 if(cons == -1) { 734 return; 735 } 736 newx = tmp_win->frame_x; 737 newy = cons; 738 newy -= tmp_win->frame_height + 2 * tmp_win->frame_bw; 739 } 740 else { 741 return; 742 } 743 744 XQueryPointer(dpy, Scr->Root, &junkW, &junkW, &junkX, &junkY, &x, &y, &junkK); 745 px = x - tmp_win->frame_x + newx; 746 py = y - tmp_win->frame_y + newy; 747 XWarpPointer(dpy, Scr->Root, Scr->Root, 0, 0, 0, 0, px, py); 748 OtpRaise(tmp_win, WinWin); 749 XMoveWindow(dpy, tmp_win->frame, newx, newy); 750 SetupWindow(tmp_win, newx, newy, tmp_win->frame_width, 751 tmp_win->frame_height, -1); 752} 753 754 755/* 756 * f.jump* -- moving incrementally in various directions 757 */ 758static void jump(TwmWindow *tmp_win, MoveFillDir direction, const char *action); 759DFHANDLER(jumpleft) 760{ 761 jump(tmp_win, MFD_LEFT, action); 762} 763DFHANDLER(jumpright) 764{ 765 jump(tmp_win, MFD_RIGHT, action); 766} 767DFHANDLER(jumpdown) 768{ 769 jump(tmp_win, MFD_BOTTOM, action); 770} 771DFHANDLER(jumpup) 772{ 773 jump(tmp_win, MFD_TOP, action); 774} 775 776static void 777jump(TwmWindow *tmp_win, MoveFillDir direction, const char *action) 778{ 779 int fx, fy, px, py, step, status, cons; 780 int fwidth, fheight; 781 int junkX, junkY; 782 unsigned int junkK; 783 Window junkW; 784 785 if(tmp_win->squeezed) { 786 XBell(dpy, 0); 787 return; 788 } 789 790 if(! action) { 791 return; 792 } 793 status = sscanf(action, "%d", &step); 794 if(status != 1) { 795 return; 796 } 797 if(step < 1) { 798 return; 799 } 800 801 fx = tmp_win->frame_x; 802 fy = tmp_win->frame_y; 803 XQueryPointer(dpy, Scr->Root, &junkW, &junkW, &junkX, &junkY, &px, &py, &junkK); 804 px -= fx; 805 py -= fy; 806 807 fwidth = tmp_win->frame_width + 2 * tmp_win->frame_bw; 808 fheight = tmp_win->frame_height + 2 * tmp_win->frame_bw; 809 switch(direction) { 810 case MFD_LEFT: 811 cons = FindConstraint(tmp_win, MFD_LEFT); 812 if(cons == -1) { 813 return; 814 } 815 fx -= step * Scr->XMoveGrid; 816 if(fx < cons) { 817 fx = cons; 818 } 819 break; 820 case MFD_RIGHT: 821 cons = FindConstraint(tmp_win, MFD_RIGHT); 822 if(cons == -1) { 823 return; 824 } 825 fx += step * Scr->XMoveGrid; 826 if(fx + fwidth > cons) { 827 fx = cons - fwidth; 828 } 829 break; 830 case MFD_TOP: 831 cons = FindConstraint(tmp_win, MFD_TOP); 832 if(cons == -1) { 833 return; 834 } 835 fy -= step * Scr->YMoveGrid; 836 if(fy < cons) { 837 fy = cons; 838 } 839 break; 840 case MFD_BOTTOM: 841 cons = FindConstraint(tmp_win, MFD_BOTTOM); 842 if(cons == -1) { 843 return; 844 } 845 fy += step * Scr->YMoveGrid; 846 if(fy + fheight > cons) { 847 fy = cons - fheight; 848 } 849 break; 850 } 851 /* Pebl Fixme: don't warp if jump happens through iconmgr */ 852 XWarpPointer(dpy, Scr->Root, Scr->Root, 0, 0, 0, 0, fx + px, fy + py); 853 if(!Scr->NoRaiseMove) { 854 OtpRaise(tmp_win, WinWin); 855 } 856 SetupWindow(tmp_win, fx, fy, tmp_win->frame_width, tmp_win->frame_height, -1); 857} 858 859 860 861/* 862 ********************************************************* 863 * 864 * Next up, straightforward resizing operations 865 * 866 ********************************************************* 867 */ 868 869/* 870 * Standard f.resize 871 */ 872DFHANDLER(resize) 873{ 874 PopDownMenu(); 875 if(tmp_win->squeezed) { 876 XBell(dpy, 0); 877 return; 878 } 879 EventHandler[EnterNotify] = HandleUnknown; 880 EventHandler[LeaveNotify] = HandleUnknown; 881 882 OpaqueResizeSize(tmp_win); 883 884 if(pulldown) 885 XWarpPointer(dpy, None, Scr->Root, 886 0, 0, 0, 0, eventp->xbutton.x_root, eventp->xbutton.y_root); 887 888 if(!tmp_win->icon || (w != tmp_win->icon->w)) { /* can't resize icons */ 889 890 /* fromMenu = False; ????? */ 891 if((Context == C_FRAME || Context == C_WINDOW || Context == C_TITLE 892 || Context == C_ROOT) 893 && cur_fromMenu()) { 894 resizeFromCenter(w, tmp_win); 895 } 896 else { 897 /* 898 * see if this is being done from the titlebar 899 */ 900 bool from3dborder = (eventp->xbutton.window == tmp_win->frame); 901 bool fromtitlebar = !from3dborder && 902 belongs_to_twm_window(tmp_win, eventp->xbutton.window); 903 904 /* Save pointer position so we can tell if it was moved or 905 not during the resize. */ 906 ResizeOrigX = eventp->xbutton.x_root; 907 ResizeOrigY = eventp->xbutton.y_root; 908 909 StartResize(eventp, tmp_win, fromtitlebar, from3dborder); 910 func_reset_cursor = false; // Leave special cursor alone 911 912 do { 913 XMaskEvent(dpy, 914 ButtonPressMask | ButtonReleaseMask | 915 EnterWindowMask | LeaveWindowMask | 916 ButtonMotionMask | VisibilityChangeMask | ExposureMask, &Event); 917 918 if(fromtitlebar && Event.type == ButtonPress) { 919 fromtitlebar = false; 920 continue; 921 } 922 923 if(Event.type == MotionNotify) { 924 /* discard any extra motion events before a release */ 925 while 926 (XCheckMaskEvent 927 (dpy, ButtonMotionMask | ButtonReleaseMask, &Event)) 928 if(Event.type == ButtonRelease) { 929 break; 930 } 931 } 932 933 if(!DispatchEvent2()) { 934 continue; 935 } 936 937 } 938 while(!(Event.type == ButtonRelease || Cancel)); 939 } 940 } 941 return; 942} 943 944 945/* 946 * The various zoom resizes 947 */ 948DFHANDLER(zoom) 949{ 950 fullzoom(tmp_win, func); 951} 952DFHANDLER(horizoom) 953{ 954 fullzoom(tmp_win, func); 955} 956DFHANDLER(fullzoom) 957{ 958 fullzoom(tmp_win, func); 959} 960DFHANDLER(fullscreenzoom) 961{ 962 fullzoom(tmp_win, func); 963} 964DFHANDLER(leftzoom) 965{ 966 fullzoom(tmp_win, func); 967} 968DFHANDLER(rightzoom) 969{ 970 fullzoom(tmp_win, func); 971} 972DFHANDLER(topzoom) 973{ 974 fullzoom(tmp_win, func); 975} 976DFHANDLER(bottomzoom) 977{ 978 fullzoom(tmp_win, func); 979} 980 981 982/* 983 * f.fill - resizing until collision 984 * 985 * XXX Similar to f.pack's, collapse away this extra level of function. 986 */ 987static void fillwindow(TwmWindow *tmp_win, const char *direction); 988DFHANDLER(fill) 989{ 990 if(tmp_win->squeezed) { 991 XBell(dpy, 0); 992 return; 993 } 994 fillwindow(tmp_win, action); 995} 996 997static void 998fillwindow(TwmWindow *tmp_win, const char *direction) 999{ 1000 int cons, newx, newy, save; 1001 unsigned int neww, newh; 1002 int i; 1003 const int winx = tmp_win->frame_x; 1004 const int winy = tmp_win->frame_y; 1005 const int winw = tmp_win->frame_width + 2 * tmp_win->frame_bw; 1006 const int winh = tmp_win->frame_height + 2 * tmp_win->frame_bw; 1007 1008 if(!strcmp(direction, "left")) { 1009 cons = FindConstraint(tmp_win, MFD_LEFT); 1010 if(cons == -1) { 1011 return; 1012 } 1013 newx = cons; 1014 newy = tmp_win->frame_y; 1015 neww = winw + winx - newx; 1016 newh = winh; 1017 neww -= 2 * tmp_win->frame_bw; 1018 newh -= 2 * tmp_win->frame_bw; 1019 ConstrainSize(tmp_win, &neww, &newh); 1020 } 1021 else if(!strcmp(direction, "right")) { 1022 for(i = 0; i < 2; i++) { 1023 cons = FindConstraint(tmp_win, MFD_RIGHT); 1024 if(cons == -1) { 1025 return; 1026 } 1027 newx = tmp_win->frame_x; 1028 newy = tmp_win->frame_y; 1029 neww = cons - winx; 1030 newh = winh; 1031 save = neww; 1032 neww -= 2 * tmp_win->frame_bw; 1033 newh -= 2 * tmp_win->frame_bw; 1034 ConstrainSize(tmp_win, &neww, &newh); 1035 if((neww != winw) || (newh != winh) || 1036 (cons == Scr->rootw - Scr->BorderRight)) { 1037 break; 1038 } 1039 neww = save; 1040 SetupWindow(tmp_win, newx, newy, neww, newh, -1); 1041 } 1042 } 1043 else if(!strcmp(direction, "top")) { 1044 cons = FindConstraint(tmp_win, MFD_TOP); 1045 if(cons == -1) { 1046 return; 1047 } 1048 newx = tmp_win->frame_x; 1049 newy = cons; 1050 neww = winw; 1051 newh = winh + winy - newy; 1052 neww -= 2 * tmp_win->frame_bw; 1053 newh -= 2 * tmp_win->frame_bw; 1054 ConstrainSize(tmp_win, &neww, &newh); 1055 } 1056 else if(!strcmp(direction, "bottom")) { 1057 for(i = 0; i < 2; i++) { 1058 cons = FindConstraint(tmp_win, MFD_BOTTOM); 1059 if(cons == -1) { 1060 return; 1061 } 1062 newx = tmp_win->frame_x; 1063 newy = tmp_win->frame_y; 1064 neww = winw; 1065 newh = cons - winy; 1066 save = newh; 1067 neww -= 2 * tmp_win->frame_bw; 1068 newh -= 2 * tmp_win->frame_bw; 1069 ConstrainSize(tmp_win, &neww, &newh); 1070 if((neww != winw) || (newh != winh) || 1071 (cons == Scr->rooth - Scr->BorderBottom)) { 1072 break; 1073 } 1074 newh = save; 1075 SetupWindow(tmp_win, newx, newy, neww, newh, -1); 1076 } 1077 } 1078 else if(!strcmp(direction, "vertical")) { 1079 if(tmp_win->zoomed == ZOOM_NONE) { 1080 tmp_win->save_frame_height = tmp_win->frame_height; 1081 tmp_win->save_frame_width = tmp_win->frame_width; 1082 tmp_win->save_frame_y = tmp_win->frame_y; 1083 tmp_win->save_frame_x = tmp_win->frame_x; 1084 1085 tmp_win->frame_y++; 1086 newy = FindConstraint(tmp_win, MFD_TOP); 1087 tmp_win->frame_y--; 1088 newh = FindConstraint(tmp_win, MFD_BOTTOM) - newy; 1089 newh -= 2 * tmp_win->frame_bw; 1090 1091 newx = tmp_win->frame_x; 1092 neww = tmp_win->frame_width; 1093 1094 ConstrainSize(tmp_win, &neww, &newh); 1095 1096 /* if the bottom of the window has moved up 1097 * it will be pushed down */ 1098 if(newy + newh < tmp_win->save_frame_y + tmp_win->save_frame_height) { 1099 newy = tmp_win->save_frame_y + 1100 tmp_win->save_frame_height - newh; 1101 } 1102 tmp_win->zoomed = F_ZOOM; 1103 SetupWindow(tmp_win, newx, newy, neww, newh, -1); 1104 } 1105 else { 1106 fullzoom(tmp_win, tmp_win->zoomed); 1107 } 1108 return; 1109 } 1110 else { 1111 return; 1112 } 1113 SetupWindow(tmp_win, newx, newy, neww, newh, -1); 1114} 1115 1116 1117 1118/* 1119 ********************************************************* 1120 * 1121 * Resizing/moving to specified geometries 1122 * 1123 ********************************************************* 1124 */ 1125 1126/* 1127 * Resizing to a window's idea of its "normal" size, from WM_NORMAL_HINTS 1128 * property. 1129 */ 1130DFHANDLER(initsize) 1131{ 1132 int grav, x, y; 1133 unsigned int width, height, swidth, sheight; 1134 1135 grav = ((tmp_win->hints.flags & PWinGravity) 1136 ? tmp_win->hints.win_gravity : NorthWestGravity); 1137 1138 if(!(tmp_win->hints.flags & USSize) && !(tmp_win->hints.flags & PSize)) { 1139 return; 1140 } 1141 1142 width = tmp_win->hints.width + 2 * tmp_win->frame_bw3D; 1143 height = tmp_win->hints.height + 2 * tmp_win->frame_bw3D + 1144 tmp_win->title_height; 1145 ConstrainSize(tmp_win, &width, &height); 1146 1147 x = tmp_win->frame_x; 1148 y = tmp_win->frame_y; 1149 swidth = tmp_win->frame_width; 1150 sheight = tmp_win->frame_height; 1151 1152 switch(grav) { 1153 case ForgetGravity: 1154 case StaticGravity: 1155 case NorthWestGravity: 1156 case NorthGravity: 1157 case WestGravity: 1158 case CenterGravity: 1159 break; 1160 1161 case NorthEastGravity: 1162 case EastGravity: 1163 x += swidth - width; 1164 break; 1165 1166 case SouthWestGravity: 1167 case SouthGravity: 1168 y += sheight - height; 1169 break; 1170 1171 case SouthEastGravity: 1172 x += swidth - width; 1173 y += sheight - height; 1174 break; 1175 } 1176 1177 SetupWindow(tmp_win, x, y, width, height, -1); 1178 return; 1179} 1180 1181 1182/* 1183 * Setting a window to a specific specified geometry string. 1184 */ 1185DFHANDLER(moveresize) 1186{ 1187 int x, y, mask; 1188 unsigned int width, height; 1189 int px = 20, py = 30; 1190 1191 mask = XParseGeometry(action, &x, &y, &width, &height); 1192 if(!(mask & WidthValue)) { 1193 width = tmp_win->frame_width; 1194 } 1195 else { 1196 width += 2 * tmp_win->frame_bw3D; 1197 } 1198 if(!(mask & HeightValue)) { 1199 height = tmp_win->frame_height; 1200 } 1201 else { 1202 height += 2 * tmp_win->frame_bw3D + tmp_win->title_height; 1203 } 1204 ConstrainSize(tmp_win, &width, &height); 1205 if(mask & XValue) { 1206 if(mask & XNegative) { 1207 x += Scr->rootw - width; 1208 } 1209 } 1210 else { 1211 x = tmp_win->frame_x; 1212 } 1213 if(mask & YValue) { 1214 if(mask & YNegative) { 1215 y += Scr->rooth - height; 1216 } 1217 } 1218 else { 1219 y = tmp_win->frame_y; 1220 } 1221 1222 { 1223 int junkX, junkY; 1224 unsigned int junkK; 1225 Window junkW; 1226 XQueryPointer(dpy, Scr->Root, &junkW, &junkW, &junkX, &junkY, &px, &py, &junkK); 1227 } 1228 px -= tmp_win->frame_x; 1229 if(px > width) { 1230 px = width / 2; 1231 } 1232 py -= tmp_win->frame_y; 1233 if(py > height) { 1234 px = height / 2; 1235 } 1236 1237 SetupWindow(tmp_win, x, y, width, height, -1); 1238 XWarpPointer(dpy, Scr->Root, Scr->Root, 0, 0, 0, 0, x + px, y + py); 1239 return; 1240} 1241 1242 1243/* 1244 * Making a specified alteration to a window's size 1245 */ 1246DFHANDLER(changesize) 1247{ 1248 /* XXX Only use of this func; should we collapse? */ 1249 ChangeSize(action, tmp_win); 1250} 1251 1252 1253/* 1254 * Stashing and flipping back to a geometry 1255 */ 1256DFHANDLER(savegeometry) 1257{ 1258 savegeometry(tmp_win); 1259} 1260 1261DFHANDLER(restoregeometry) 1262{ 1263 restoregeometry(tmp_win); 1264} 1265 1266 1267 1268 1269/* 1270 ********************************************************* 1271 * 1272 * Misc utils used in the above 1273 * 1274 ********************************************************* 1275 */ 1276 1277/* 1278 * Used in the various move/fill/pack/etc bits 1279 */ 1280static int 1281FindConstraint(TwmWindow *tmp_win, MoveFillDir direction) 1282{ 1283 TwmWindow *t; 1284 int ret; 1285 const int winx = tmp_win->frame_x; 1286 const int winy = tmp_win->frame_y; 1287 const int winw = tmp_win->frame_width + 2 * tmp_win->frame_bw; 1288 const int winh = tmp_win->frame_height + 2 * tmp_win->frame_bw; 1289 1290 switch(direction) { 1291 case MFD_LEFT: 1292 if(winx < Scr->BorderLeft) { 1293 return -1; 1294 } 1295 ret = Scr->BorderLeft; 1296 break; 1297 case MFD_RIGHT: 1298 if(winx + winw > Scr->rootw - Scr->BorderRight) { 1299 return -1; 1300 } 1301 ret = Scr->rootw - Scr->BorderRight; 1302 break; 1303 case MFD_TOP: 1304 if(winy < Scr->BorderTop) { 1305 return -1; 1306 } 1307 ret = Scr->BorderTop; 1308 break; 1309 case MFD_BOTTOM: 1310 if(winy + winh > Scr->rooth - Scr->BorderBottom) { 1311 return -1; 1312 } 1313 ret = Scr->rooth - Scr->BorderBottom; 1314 break; 1315 default: 1316 return -1; 1317 } 1318 for(t = Scr->FirstWindow; t != NULL; t = t->next) { 1319 const int w = t->frame_width + 2 * t->frame_bw; 1320 const int h = t->frame_height + 2 * t->frame_bw; 1321 1322 if(t == tmp_win) { 1323 continue; 1324 } 1325 if(!visible(t)) { 1326 continue; 1327 } 1328 if(!t->mapped) { 1329 continue; 1330 } 1331 1332 switch(direction) { 1333 case MFD_LEFT: 1334 if(winx <= t->frame_x + w) { 1335 continue; 1336 } 1337 if(winy >= t->frame_y + h) { 1338 continue; 1339 } 1340 if(winy + winh <= t->frame_y) { 1341 continue; 1342 } 1343 ret = MAX(ret, t->frame_x + w); 1344 break; 1345 case MFD_RIGHT: 1346 if(winx + winw >= t->frame_x) { 1347 continue; 1348 } 1349 if(winy >= t->frame_y + h) { 1350 continue; 1351 } 1352 if(winy + winh <= t->frame_y) { 1353 continue; 1354 } 1355 ret = MIN(ret, t->frame_x); 1356 break; 1357 case MFD_TOP: 1358 if(winy <= t->frame_y + h) { 1359 continue; 1360 } 1361 if(winx >= t->frame_x + w) { 1362 continue; 1363 } 1364 if(winx + winw <= t->frame_x) { 1365 continue; 1366 } 1367 ret = MAX(ret, t->frame_y + h); 1368 break; 1369 case MFD_BOTTOM: 1370 if(winy + winh >= t->frame_y) { 1371 continue; 1372 } 1373 if(winx >= t->frame_x + w) { 1374 continue; 1375 } 1376 if(winx + winw <= t->frame_x) { 1377 continue; 1378 } 1379 ret = MIN(ret, t->frame_y); 1380 break; 1381 } 1382 } 1383 return ret; 1384} 1385 1386 1387/* 1388 * Is Window w part of the conglomerate of metawindows we put around the 1389 * real window for TwmWindow t? Note that this does _not_ check if w is 1390 * the actual window we built the TwmWindow t around. 1391 */ 1392static bool 1393belongs_to_twm_window(TwmWindow *t, Window w) 1394{ 1395 /* Safety */ 1396 if(!t) { 1397 return false; 1398 } 1399 1400 /* Part of the framing we put around the window? */ 1401 if(w == t->frame || w == t->title_w 1402 || w == t->hilite_wl || w == t->hilite_wr) { 1403 return true; 1404 } 1405 1406 /* Part of the icon bits? */ 1407 if(t->icon && (w == t->icon->w || w == t->icon->bm_w)) { 1408 return true; 1409 } 1410 1411 /* One of the title button windows? */ 1412 if(t->titlebuttons) { 1413 TBWindow *tbw; 1414 int nb = Scr->TBInfo.nleft + Scr->TBInfo.nright; 1415 for(tbw = t->titlebuttons ; nb > 0 ; tbw++, nb--) { 1416 if(tbw->window == w) { 1417 return true; 1418 } 1419 } 1420 } 1421 1422 /* Then no */ 1423 return false; 1424} 1425