1/* 2 * Copyright 1988 by Evans & Sutherland Computer Corporation, 3 * Salt Lake City, Utah 4 * Portions Copyright 1989 by the Massachusetts Institute of Technology 5 * Cambridge, Massachusetts 6 * 7 * Copyright 1992 Claude Lecommandeur. 8 */ 9 10/*********************************************************************** 11 * 12 * $XConsortium: resize.c,v 1.80 91/05/11 17:35:42 dave Exp $ 13 * 14 * window resizing borrowed from the "wm" window manager 15 * 16 * 11-Dec-87 Thomas E. LaStrange File created 17 * 18 * Do the necessary modification to be integrated in ctwm. 19 * Can no longer be used for the standard twm. 20 * 21 * 22-April-92 Claude Lecommandeur. 22 * 23 * 24 ***********************************************************************/ 25 26#include "ctwm.h" 27 28#include <stdio.h> 29#include <stdlib.h> 30 31#include "events.h" 32#include "util.h" 33#include "otp.h" 34#include "functions_defs.h" 35#include "add_window.h" 36#include "colormaps.h" 37#include "screen.h" 38#include "drawing.h" 39#include "r_area.h" 40#include "r_area_list.h" 41#include "r_layout.h" 42#include "win_decorations.h" 43#include "win_ops.h" 44#include "win_resize.h" 45#include "win_utils.h" 46#include "workspace_manager.h" 47#include "iconmgr.h" 48 49#define MINHEIGHT 0 /* had been 32 */ 50#define MINWIDTH 0 /* had been 60 */ 51 52static int dragx; /* all these variables are used */ 53static int dragy; /* in resize operations */ 54static unsigned int dragWidth; 55static unsigned int dragHeight; 56 57static int origx; 58static int origy; 59static int origWidth; 60static int origHeight; 61 62static int clampTop; 63static int clampBottom; 64static int clampLeft; 65static int clampRight; 66static int clampDX; 67static int clampDY; 68 69static int last_width; 70static int last_height; 71 72static unsigned int resizeGrabMask; 73 74static void DisplaySize(TwmWindow *tmp_win, int width, int height); 75 76static void do_auto_clamp(TwmWindow *tmp_win, XEvent *evp) 77{ 78 Window junkRoot; 79 int x, y, h, v, junkbw; 80 unsigned int junkMask; 81 82 switch(evp->type) { 83 case ButtonPress: 84 x = evp->xbutton.x_root; 85 y = evp->xbutton.y_root; 86 break; 87 case KeyPress: 88 x = evp->xkey.x_root; 89 y = evp->xkey.y_root; 90 break; 91 default: 92 if(!XQueryPointer(dpy, Scr->Root, &junkRoot, &junkRoot, 93 &x, &y, &junkbw, &junkbw, &junkMask)) { 94 return; 95 } 96 } 97 98 /* 99 * Determine in which of the 9 "quadrants" of the window we are. 100 * Cast the values to signed int: if the numerator is negative 101 * we don't want them converted to unsigned due to the default 102 * promotion rules: that would produce a very large quotient. 103 */ 104 h = (int)(x - dragx) / (int)(dragWidth < 3 ? 1 : (dragWidth / 3)); 105 v = (int)(y - dragy - tmp_win->title_height) / 106 (int)(dragHeight < 3 ? 1 : (dragHeight / 3)); 107 108 if(h <= 0) { 109 clampLeft = 1; 110 clampDX = (x - dragx); 111 } 112 else if(h >= 2) { 113 clampRight = 1; 114 clampDX = (x - dragx - dragWidth); 115 } 116 117 if(v <= 0) { 118 clampTop = 1; 119 clampDY = (y - dragy); 120 } 121 else if(v >= 2) { 122 clampBottom = 1; 123 clampDY = (y - dragy - dragHeight); 124 } 125} 126 127/*********************************************************************** 128 * 129 * Procedure: 130 * OpaqueResizeSize - determine if window should be resized opaquely. 131 * 132 * Inputs: 133 * tmp_win - the TwmWindow pointer 134 * 135 *********************************************************************** 136 */ 137 138void OpaqueResizeSize(TwmWindow *tmp_win) 139{ 140 if(tmp_win->OpaqueResize) { 141 /* 142 * OpaqueResize defaults to a thousand. Assume that any number 143 * >= 1000 is "infinity" and don't bother calculating. 144 */ 145 if(Scr->OpaqueResizeThreshold >= 1000) { 146 Scr->OpaqueResize = true; 147 } 148 else { 149 /* 150 * scrsz will hold the number of pixels in your resolution, 151 * which can get big. [signed] int may not cut it. 152 */ 153 const unsigned long winsz = tmp_win->frame_width 154 * tmp_win->frame_height; 155 const unsigned long scrsz = Scr->rootw * Scr->rooth; 156 if(winsz > (scrsz * (Scr->OpaqueResizeThreshold / 100.0))) { 157 Scr->OpaqueResize = false; 158 } 159 else { 160 Scr->OpaqueResize = true; 161 } 162 } 163 } 164 else { 165 Scr->OpaqueResize = false; 166 } 167} 168 169 170/*********************************************************************** 171 * 172 * Procedure: 173 * StartResize - begin a window resize operation 174 * 175 * Inputs: 176 * ev - the event structure (button press) 177 * tmp_win - the TwmWindow pointer 178 * fromtitlebar - action invoked from titlebar button 179 * 180 *********************************************************************** 181 */ 182 183void StartResize(XEvent *evp, TwmWindow *tmp_win, 184 bool fromtitlebar, bool from3dborder) 185{ 186 Window junkRoot, grabwin; 187 unsigned int junkbw, junkDepth; 188 Cursor cursor; 189 190 cursor = (Scr->BorderCursors 191 && tmp_win->curcurs) ? tmp_win->curcurs : Scr->ResizeCursor; 192 ResizeWindow = tmp_win->frame; 193 if(! Scr->OpaqueResize || resizeWhenAdd) { 194 XGrabServer(dpy); 195 } 196 resizeGrabMask = ButtonPressMask | ButtonReleaseMask | 197 ButtonMotionMask | PointerMotionHintMask; 198 199 grabwin = Scr->Root; 200#ifdef WINBOX 201 if(tmp_win->winbox) { 202 grabwin = tmp_win->winbox->window; 203 } 204#endif 205 XGrabPointer(dpy, grabwin, True, resizeGrabMask, 206 GrabModeAsync, GrabModeAsync, grabwin, cursor, CurrentTime); 207 208 XGetGeometry(dpy, (Drawable) tmp_win->frame, &junkRoot, 209 &dragx, &dragy, &dragWidth, &dragHeight, &junkbw, 210 &junkDepth); 211 dragx += tmp_win->frame_bw; 212 dragy += tmp_win->frame_bw; 213 origx = dragx; 214 origy = dragy; 215 origWidth = dragWidth; 216 origHeight = dragHeight; 217 clampTop = clampBottom = clampLeft = clampRight = clampDX = clampDY = 0; 218 219 if(Scr->AutoRelativeResize && (from3dborder || !fromtitlebar)) { 220 do_auto_clamp(tmp_win, evp); 221 } 222 223 Scr->SizeStringOffset = SIZE_HINDENT; 224 MoveResizeSizeWindow(evp->xbutton.x_root, evp->xbutton.y_root, 225 Scr->SizeStringWidth + SIZE_HINDENT * 2, 226 Scr->SizeFont.height + SIZE_VINDENT * 2); 227 XMapRaised(dpy, Scr->SizeWindow); 228 InstallRootColormap(); 229 last_width = 0; 230 last_height = 0; 231 DisplaySize(tmp_win, origWidth, origHeight); 232 233 if(! Scr->OpaqueResize || resizeWhenAdd) 234 MoveOutline(Scr->Root, dragx - tmp_win->frame_bw, 235 dragy - tmp_win->frame_bw, dragWidth + 2 * tmp_win->frame_bw, 236 dragHeight + 2 * tmp_win->frame_bw, 237 tmp_win->frame_bw, tmp_win->title_height + tmp_win->frame_bw3D); 238} 239 240 241void MenuStartResize(TwmWindow *tmp_win, int x, int y, int w, int h) 242{ 243 if(! Scr->OpaqueResize) { 244 XGrabServer(dpy); 245 } 246 resizeGrabMask = ButtonPressMask | ButtonMotionMask | PointerMotionMask; 247 XGrabPointer(dpy, Scr->Root, True, resizeGrabMask, 248 GrabModeAsync, GrabModeAsync, 249 Scr->Root, Scr->ResizeCursor, CurrentTime); 250 dragx = x + tmp_win->frame_bw; 251 dragy = y + tmp_win->frame_bw; 252 origx = dragx; 253 origy = dragy; 254 dragWidth = origWidth = w; 255 dragHeight = origHeight = h; 256 clampTop = clampBottom = clampLeft = clampRight = clampDX = clampDY = 0; 257 last_width = 0; 258 last_height = 0; 259 Scr->SizeStringOffset = SIZE_HINDENT; 260 MoveResizeSizeWindow(dragx, dragy, 261 Scr->SizeStringWidth + SIZE_HINDENT * 2, 262 Scr->SizeFont.height + SIZE_VINDENT * 2); 263 XMapRaised(dpy, Scr->SizeWindow); 264 DisplaySize(tmp_win, origWidth, origHeight); 265 if(! Scr->OpaqueResize) 266 MoveOutline(Scr->Root, dragx - tmp_win->frame_bw, 267 dragy - tmp_win->frame_bw, 268 dragWidth + 2 * tmp_win->frame_bw, 269 dragHeight + 2 * tmp_win->frame_bw, 270 tmp_win->frame_bw, tmp_win->title_height + tmp_win->frame_bw3D); 271} 272 273/*********************************************************************** 274 * 275 * Procedure: 276 * AddStartResize - begin a windorew resize operation from AddWindow 277 * 278 * Inputs: 279 * tmp_win - the TwmWindow pointer 280 * 281 *********************************************************************** 282 */ 283 284void AddStartResize(TwmWindow *tmp_win, int x, int y, int w, int h) 285{ 286 XGrabServer(dpy); 287 resizeGrabMask = ButtonReleaseMask | ButtonMotionMask | PointerMotionHintMask; 288 XGrabPointer(dpy, Scr->Root, True, resizeGrabMask, 289 GrabModeAsync, GrabModeAsync, 290 Scr->Root, Scr->ResizeCursor, CurrentTime); 291 292 dragx = x + tmp_win->frame_bw; 293 dragy = y + tmp_win->frame_bw; 294 origx = dragx; 295 origy = dragy; 296 dragWidth = origWidth = w - 2 * tmp_win->frame_bw; 297 dragHeight = origHeight = h - 2 * tmp_win->frame_bw; 298 clampTop = clampBottom = clampLeft = clampRight = clampDX = clampDY = 0; 299 last_width = 0; 300 last_height = 0; 301 DisplaySize(tmp_win, origWidth, origHeight); 302} 303 304 305void MenuDoResize(int x_root, int y_root, TwmWindow *tmp_win) 306{ 307 int action; 308 Cursor cursor = 0; 309 310 action = 0; 311 312 x_root -= clampDX; 313 y_root -= clampDY; 314 315 if(clampTop) { 316 int delta = y_root - dragy; 317 if((int)(dragHeight - delta) < MINHEIGHT) { 318 delta = dragHeight - MINHEIGHT; 319 clampTop = 0; 320 } 321 dragy += delta; 322 dragHeight -= delta; 323 action = 1; 324 cursor = TopCursor; 325 } 326 else if(y_root <= dragy) { 327 dragy = y_root; 328 dragHeight = origy + origHeight - 329 y_root; 330 clampBottom = 0; 331 clampTop = 1; 332 clampDY = 0; 333 action = 1; 334 cursor = TopCursor; 335 } 336 if(clampLeft) { 337 int delta = x_root - dragx; 338 if((int)(dragWidth - delta) < MINWIDTH) { 339 delta = dragWidth - MINWIDTH; 340 clampLeft = 0; 341 } 342 dragx += delta; 343 dragWidth -= delta; 344 action = 1; 345 cursor = clampTop ? TopLeftCursor : LeftCursor; 346 } 347 else if(x_root <= dragx) { 348 dragx = x_root; 349 dragWidth = origx + origWidth - 350 x_root; 351 clampRight = 0; 352 clampLeft = 1; 353 clampDX = 0; 354 action = 1; 355 cursor = clampTop ? TopLeftCursor : LeftCursor; 356 } 357 if(clampBottom) { 358 int delta = y_root - dragy - dragHeight; 359 if((int)(dragHeight + delta) < MINHEIGHT) { 360 delta = MINHEIGHT - dragHeight; 361 clampBottom = 0; 362 } 363 dragHeight += delta; 364 action = 1; 365 cursor = clampLeft ? BottomLeftCursor : BottomCursor; 366 } 367 else if(y_root >= dragy + dragHeight) { 368 dragy = origy; 369 dragHeight = 1 + y_root - dragy; 370 clampTop = 0; 371 clampBottom = 1; 372 clampDY = 0; 373 action = 1; 374 cursor = clampLeft ? BottomLeftCursor : BottomCursor; 375 } 376 if(clampRight) { 377 int delta = x_root - dragx - dragWidth; 378 if((int)(dragWidth + delta) < MINWIDTH) { 379 delta = MINWIDTH - dragWidth; 380 clampRight = 0; 381 } 382 dragWidth += delta; 383 action = 1; 384 cursor = clampBottom ? BottomRightCursor : RightCursor; 385 cursor = clampTop ? TopRightCursor : cursor; 386 } 387 else if(x_root >= dragx + dragWidth) { 388 dragx = origx; 389 dragWidth = 1 + x_root - origx; 390 clampLeft = 0; 391 clampRight = 1; 392 clampDX = 0; 393 action = 1; 394 cursor = clampBottom ? BottomRightCursor : RightCursor; 395 cursor = clampTop ? TopRightCursor : cursor; 396 } 397 398 if(action) { 399 ConstrainSize(tmp_win, &dragWidth, &dragHeight); 400 if(clampLeft) { 401 dragx = origx + origWidth - dragWidth; 402 } 403 if(clampTop) { 404 dragy = origy + origHeight - dragHeight; 405 } 406 if(Scr->OpaqueResize) 407 SetupWindow(tmp_win, dragx - tmp_win->frame_bw, dragy - tmp_win->frame_bw, 408 dragWidth, dragHeight, -1); 409 else 410 MoveOutline(Scr->Root, 411 dragx - tmp_win->frame_bw, 412 dragy - tmp_win->frame_bw, 413 dragWidth + 2 * tmp_win->frame_bw, 414 dragHeight + 2 * tmp_win->frame_bw, 415 tmp_win->frame_bw, tmp_win->title_height + tmp_win->frame_bw3D); 416 if(Scr->BorderCursors && (cursor != tmp_win->curcurs)) { 417 tmp_win->curcurs = cursor; 418 XChangeActivePointerGrab(dpy, resizeGrabMask, cursor, CurrentTime); 419 } 420 } 421 422 DisplaySize(tmp_win, dragWidth, dragHeight); 423} 424 425/*********************************************************************** 426 * 427 * Procedure: 428 * DoResize - move the rubberband around. This is called for 429 * each motion event when we are resizing 430 * 431 * Inputs: 432 * x_root - the X corrdinate in the root window 433 * y_root - the Y corrdinate in the root window 434 * tmp_win - the current twm window 435 * 436 *********************************************************************** 437 */ 438 439void DoResize(int x_root, int y_root, TwmWindow *tmp_win) 440{ 441 int action; 442 Cursor cursor = 0; 443 444 action = 0; 445 446 x_root -= clampDX; 447 y_root -= clampDY; 448 449 if(clampTop) { 450 int delta = y_root - dragy; 451 if((int)(dragHeight - delta) < MINHEIGHT) { 452 delta = dragHeight - MINHEIGHT; 453 clampTop = 0; 454 } 455 dragy += delta; 456 dragHeight -= delta; 457 action = 1; 458 cursor = TopCursor; 459 } 460 else if(y_root <= dragy) { 461 dragy = y_root; 462 dragHeight = origy + origHeight - 463 y_root; 464 clampBottom = 0; 465 clampTop = 1; 466 clampDY = 0; 467 action = 1; 468 cursor = TopCursor; 469 } 470 if(clampLeft) { 471 int delta = x_root - dragx; 472 if((int)(dragWidth - delta) < MINWIDTH) { 473 delta = dragWidth - MINWIDTH; 474 clampLeft = 0; 475 } 476 dragx += delta; 477 dragWidth -= delta; 478 action = 1; 479 cursor = clampTop ? TopLeftCursor : LeftCursor; 480 } 481 else if(x_root <= dragx) { 482 dragx = x_root; 483 dragWidth = origx + origWidth - 484 x_root; 485 clampRight = 0; 486 clampLeft = 1; 487 clampDX = 0; 488 action = 1; 489 cursor = clampTop ? TopLeftCursor : LeftCursor; 490 } 491 if(clampBottom) { 492 int delta = y_root - dragy - dragHeight; 493 if((int)(dragHeight + delta) < MINHEIGHT) { 494 delta = MINHEIGHT - dragHeight; 495 clampBottom = 0; 496 } 497 dragHeight += delta; 498 action = 1; 499 cursor = clampLeft ? BottomLeftCursor : BottomCursor; 500 } 501 else if(y_root >= dragy + dragHeight - 1) { 502 dragy = origy; 503 dragHeight = 1 + y_root - dragy; 504 clampTop = 0; 505 clampBottom = 1; 506 clampDY = 0; 507 action = 1; 508 cursor = clampLeft ? BottomLeftCursor : BottomCursor; 509 } 510 if(clampRight) { 511 int delta = x_root - dragx - dragWidth; 512 if((int)(dragWidth + delta) < MINWIDTH) { 513 delta = MINWIDTH - dragWidth; 514 clampRight = 0; 515 } 516 dragWidth += delta; 517 action = 1; 518 cursor = clampBottom ? BottomRightCursor : RightCursor; 519 cursor = clampTop ? TopRightCursor : cursor; 520 } 521 else if(x_root >= dragx + dragWidth - 1) { 522 dragx = origx; 523 dragWidth = 1 + x_root - origx; 524 clampLeft = 0; 525 clampRight = 1; 526 clampDX = 0; 527 action = 1; 528 cursor = clampBottom ? BottomRightCursor : RightCursor; 529 cursor = clampTop ? TopRightCursor : cursor; 530 } 531 532 if(action) { 533 ConstrainSize(tmp_win, &dragWidth, &dragHeight); 534 if(clampLeft) { 535 dragx = origx + origWidth - dragWidth; 536 } 537 if(clampTop) { 538 dragy = origy + origHeight - dragHeight; 539 } 540 if(Scr->OpaqueResize && ! resizeWhenAdd) { 541 SetupWindow(tmp_win, dragx - tmp_win->frame_bw, dragy - tmp_win->frame_bw, 542 dragWidth, dragHeight, -1); 543 } 544 else { 545 MoveOutline(Scr->Root, 546 dragx - tmp_win->frame_bw, 547 dragy - tmp_win->frame_bw, 548 dragWidth + 2 * tmp_win->frame_bw, 549 dragHeight + 2 * tmp_win->frame_bw, 550 tmp_win->frame_bw, tmp_win->title_height + tmp_win->frame_bw3D); 551 } 552 if(Scr->BorderCursors && (cursor != tmp_win->curcurs)) { 553 tmp_win->curcurs = cursor; 554 XChangeActivePointerGrab(dpy, resizeGrabMask, cursor, CurrentTime); 555 } 556 } 557 558 DisplaySize(tmp_win, dragWidth, dragHeight); 559} 560 561/*********************************************************************** 562 * 563 * Procedure: 564 * DisplaySize - display the size in the dimensions window 565 * 566 * Inputs: 567 * tmp_win - the current twm window 568 * width - the width of the rubber band 569 * height - the height of the rubber band 570 * 571 *********************************************************************** 572 */ 573 574static void DisplaySize(TwmWindow *tmp_win, int width, int height) 575{ 576 char str[100]; 577 int dwidth; 578 int dheight; 579 580 if(last_width == width && last_height == height) { 581 return; 582 } 583 584 last_width = width; 585 last_height = height; 586 587 dheight = height - tmp_win->title_height - 2 * tmp_win->frame_bw3D; 588 dwidth = width - 2 * tmp_win->frame_bw3D; 589 590 /* 591 * ICCCM says that PMinSize is the default is no PBaseSize is given, 592 * and vice-versa. 593 */ 594 if(tmp_win->hints.flags & (PMinSize | PBaseSize) 595 && tmp_win->hints.flags & PResizeInc) { 596 if(tmp_win->hints.flags & PBaseSize) { 597 dwidth -= tmp_win->hints.base_width; 598 dheight -= tmp_win->hints.base_height; 599 } 600 else { 601 dwidth -= tmp_win->hints.min_width; 602 dheight -= tmp_win->hints.min_height; 603 } 604 } 605 606 if(tmp_win->hints.flags & PResizeInc) { 607 dwidth /= tmp_win->hints.width_inc; 608 dheight /= tmp_win->hints.height_inc; 609 } 610 611 sprintf(str, " %4d x %-4d ", dwidth, dheight); 612 XRaiseWindow(dpy, Scr->SizeWindow); 613 614 Draw3DBorder(Scr->SizeWindow, 0, 0, 615 Scr->SizeStringOffset + Scr->SizeStringWidth + SIZE_HINDENT, 616 Scr->SizeFont.height + SIZE_VINDENT * 2, 617 2, Scr->DefaultC, off, false, false); 618 619 FB(Scr->DefaultC.fore, Scr->DefaultC.back); 620 XmbDrawImageString(dpy, Scr->SizeWindow, Scr->SizeFont.font_set, 621 Scr->NormalGC, Scr->SizeStringOffset, 622 Scr->SizeFont.ascent + SIZE_VINDENT, str, 13); 623} 624 625/*********************************************************************** 626 * 627 * Procedure: 628 * EndResize - finish the resize operation 629 * 630 *********************************************************************** 631 */ 632 633void EndResize(void) 634{ 635 TwmWindow *tmp_win; 636 637#ifdef DEBUG 638 fprintf(stderr, "EndResize\n"); 639#endif 640 641 MoveOutline(Scr->Root, 0, 0, 0, 0, 0, 0); 642 XUnmapWindow(dpy, Scr->SizeWindow); 643 644 tmp_win = GetTwmWindow(ResizeWindow); 645 if(!tmp_win) { 646 return; 647 } 648 649 ConstrainSize(tmp_win, &dragWidth, &dragHeight); 650 651 if(dragWidth != tmp_win->frame_width || 652 dragHeight != tmp_win->frame_height) { 653 unzoom(tmp_win); 654 } 655 656 SetupWindow(tmp_win, dragx - tmp_win->frame_bw, dragy - tmp_win->frame_bw, 657 dragWidth, dragHeight, -1); 658 659 if(tmp_win->isiconmgr) { 660 int ncols = tmp_win->iconmgrp->cur_columns; 661 if(ncols == 0) { 662 ncols = 1; 663 } 664 665 tmp_win->iconmgrp->width = (int)(((dragWidth - 2 * tmp_win->frame_bw3D) * 666 (long) tmp_win->iconmgrp->columns) 667 / ncols); 668 PackIconManager(tmp_win->iconmgrp); 669 } 670 671 if(!Scr->NoRaiseResize) { 672 OtpRaise(tmp_win, WinWin); 673 WMapRaise(tmp_win); 674 } 675 676 UninstallRootColormap(); 677 678 ResizeWindow = None; 679} 680 681void MenuEndResize(TwmWindow *tmp_win) 682{ 683 MoveOutline(Scr->Root, 0, 0, 0, 0, 0, 0); 684 XUnmapWindow(dpy, Scr->SizeWindow); 685 ConstrainSize(tmp_win, &dragWidth, &dragHeight); 686 AddingX = dragx - tmp_win->frame_bw; 687 AddingY = dragy - tmp_win->frame_bw; 688 AddingW = dragWidth; 689 AddingH = dragHeight; 690 SetupWindow(tmp_win, AddingX, AddingY, AddingW, AddingH, -1); 691} 692 693 694/*********************************************************************** 695 * 696 * Procedure: 697 * AddEndResize - finish the resize operation for AddWindo<w 698 * 699 *********************************************************************** 700 */ 701 702void AddEndResize(TwmWindow *tmp_win) 703{ 704 705#ifdef DEBUG 706 fprintf(stderr, "AddEndResize\n"); 707#endif 708 709 ConstrainSize(tmp_win, &dragWidth, &dragHeight); 710 AddingX = dragx; 711 AddingY = dragy; 712 AddingW = dragWidth + (2 * tmp_win->frame_bw); 713 AddingH = dragHeight + (2 * tmp_win->frame_bw); 714} 715 716/*********************************************************************** 717 * 718 * Procedure: 719 * ConstrainSize - adjust the given width and height to account for the 720 * constraints imposed by size hints 721 * 722 * The general algorithm, especially the aspect ratio stuff, is 723 * borrowed from uwm's CheckConsistency routine. 724 * 725 ***********************************************************************/ 726 727void ConstrainSize(TwmWindow *tmp_win, 728 unsigned int *widthp, unsigned int *heightp) 729{ 730#define makemult(a,b) ((b==1) ? (a) : (((int)((a)/(b))) * (b)) ) 731 732 int minWidth, minHeight, maxWidth, maxHeight, xinc, yinc, delta; 733 int baseWidth, baseHeight; 734 int dwidth = *widthp, dheight = *heightp; 735 736 737 dwidth -= 2 * tmp_win->frame_bw3D; 738 dheight -= (tmp_win->title_height + 2 * tmp_win->frame_bw3D); 739 740 if(tmp_win->hints.flags & PMinSize) { 741 minWidth = tmp_win->hints.min_width; 742 minHeight = tmp_win->hints.min_height; 743 } 744 else if(tmp_win->hints.flags & PBaseSize) { 745 minWidth = tmp_win->hints.base_width; 746 minHeight = tmp_win->hints.base_height; 747 } 748 else { 749 minWidth = minHeight = 1; 750 } 751 752 if(tmp_win->hints.flags & PBaseSize) { 753 baseWidth = tmp_win->hints.base_width; 754 baseHeight = tmp_win->hints.base_height; 755 } 756 else if(tmp_win->hints.flags & PMinSize) { 757 baseWidth = tmp_win->hints.min_width; 758 baseHeight = tmp_win->hints.min_height; 759 } 760 else { 761 baseWidth = baseHeight = 0; 762 } 763 764 765 if(tmp_win->hints.flags & PMaxSize) { 766 maxWidth = min(Scr->MaxWindowWidth, tmp_win->hints.max_width); 767 maxHeight = min(Scr->MaxWindowHeight, tmp_win->hints.max_height); 768 } 769 else { 770 maxWidth = Scr->MaxWindowWidth; 771 maxHeight = Scr->MaxWindowHeight; 772 } 773 774 if(tmp_win->hints.flags & PResizeInc) { 775 xinc = tmp_win->hints.width_inc; 776 yinc = tmp_win->hints.height_inc; 777 if(xinc == 0) { 778 xinc = 1; 779 } 780 if(yinc == 0) { 781 yinc = 1; 782 } 783 } 784 else { 785 xinc = yinc = 1; 786 } 787 788 /* 789 * First, clamp to min and max values 790 */ 791 if(dwidth < minWidth) { 792 dwidth = minWidth; 793 } 794 if(dheight < minHeight) { 795 dheight = minHeight; 796 } 797 798 if(dwidth > maxWidth) { 799 dwidth = maxWidth; 800 } 801 if(dheight > maxHeight) { 802 dheight = maxHeight; 803 } 804 805 806 /* 807 * Second, fit to base + N * inc 808 */ 809 dwidth = ((dwidth - baseWidth) / xinc * xinc) + baseWidth; 810 dheight = ((dheight - baseHeight) / yinc * yinc) + baseHeight; 811 812 813 /* 814 * Third, adjust for aspect ratio 815 */ 816 /* 817 * The math looks like this: 818 * 819 * minAspectX dwidth maxAspectX 820 * ---------- <= ------- <= ---------- 821 * minAspectY dheight maxAspectY 822 * 823 * If that is multiplied out, then the width and height are 824 * invalid in the following situations: 825 * 826 * minAspectX * dheight > minAspectY * dwidth 827 * maxAspectX * dheight < maxAspectY * dwidth 828 * 829 */ 830 831 if(tmp_win->hints.flags & PAspect) { 832 int minAspectX = tmp_win->hints.min_aspect.x; 833 int minAspectY = tmp_win->hints.min_aspect.y; 834 int maxAspectX = tmp_win->hints.max_aspect.x; 835 int maxAspectY = tmp_win->hints.max_aspect.y; 836 837 if(minAspectX && minAspectY && maxAspectX && maxAspectY) { 838 if(minAspectX * dheight > minAspectY * dwidth) { 839 delta = makemult(minAspectX * dheight / minAspectY - dwidth, 840 xinc); 841 if(dwidth + delta <= maxWidth) { 842 dwidth += delta; 843 } 844 else { 845 delta = makemult(dheight - dwidth * minAspectY / minAspectX, 846 yinc); 847 if(dheight - delta >= minHeight) { 848 dheight -= delta; 849 } 850 } 851 } 852 853 if(maxAspectX * dheight < maxAspectY * dwidth) { 854 delta = makemult(dwidth * maxAspectY / maxAspectX - dheight, 855 yinc); 856 if(dheight + delta <= maxHeight) { 857 dheight += delta; 858 } 859 else { 860 delta = makemult(dwidth - maxAspectX * dheight / maxAspectY, 861 xinc); 862 if(dwidth - delta >= minWidth) { 863 dwidth -= delta; 864 } 865 } 866 } 867 } 868 } 869 870 871 /* 872 * Fourth, account for border width and title height 873 */ 874 *widthp = dwidth + 2 * tmp_win->frame_bw3D; 875 *heightp = dheight + tmp_win->title_height + 2 * tmp_win->frame_bw3D; 876} 877 878 879 880 881/********************************************************************** 882 * Rutgers mod #1 - rocky. 883 * Procedure: 884 * fullzoom - zooms window to full height of screen or 885 * to full height and width of screen. (Toggles 886 * so that it can undo the zoom - even when switching 887 * between fullzoom and vertical zoom.) 888 * 889 * Inputs: 890 * tmp_win - the TwmWindow pointer 891 * 892 * 893 ********************************************************************** 894 */ 895 896void fullzoom(TwmWindow *tmp_win, int func) 897{ 898 Window junkRoot; 899 unsigned int junkbw, junkDepth; 900 int tmpX, tmpY, tmpW, tmpH; 901 902 /* 903 * All our callers [need to] do this, so moving it here saves a few 904 * lines in some places around the calling, and when redundant it 905 * just wastes a comparison, so it's cheap. 906 */ 907 if(tmp_win->squeezed) { 908 XBell(dpy, 0); 909 return; 910 } 911 912 913 XGetGeometry(dpy, (Drawable) tmp_win->frame, &junkRoot, 914 &dragx, &dragy, (unsigned int *)&dragWidth, (unsigned int *)&dragHeight, 915 &junkbw, 916 &junkDepth); 917 918 /* 919 * Guard; if it was already not zoomed, and we're asking to unzoom 920 * it, just finish right away. This saves us work, but also avoids 921 * really bad side effects in some cases. e.g., if we try to 922 * ZOOM_NONE a window that's never been ZOOM'd, tmp_win->save_* will 923 * all be 0, so we'd wind up resizing it to a point. It's possible 924 * for that to happen via e.g. an EWMH message removing a _FULLSCREEN 925 * or similar attribute; that can then call into us telling us not to 926 * zoom, on a window that's never been zoomed. 927 * 928 * This wouldn't protect us if somehow it was zoomed but hadn't set 929 * that data, but I don't see how that can happen. Worry about that 930 * when it does. 931 */ 932 if(func == ZOOM_NONE && tmp_win->zoomed == ZOOM_NONE) { 933 return; 934 } 935 936 if(tmp_win->zoomed == func) { 937 /* It was already zoomed this way, unzoom it */ 938 dragHeight = tmp_win->save_frame_height; 939 dragWidth = tmp_win->save_frame_width; 940 dragx = tmp_win->save_frame_x; 941 dragy = tmp_win->save_frame_y; 942 943 unzoom(tmp_win); 944 945 /* XXX _should_ it be falling through here? */ 946 } 947 else { 948 RLayout *borderedLayout = NULL; 949 RArea area, finalArea = RAreaInvalid(); 950 int frame_bw_times_2; 951 952#ifdef WINBOX 953 if(tmp_win->winbox) { 954 XWindowAttributes winattrs; 955 if(XGetWindowAttributes(dpy, tmp_win->winbox->window, &winattrs)) { 956 borderedLayout = RLayoutNew( 957 RAreaListNew(1, 958 RAreaNew(winattrs.x, 959 winattrs.y, 960 winattrs.width, 961 winattrs.height), 962 NULL)); 963 } 964 } 965#endif 966 if(borderedLayout == NULL) { 967 borderedLayout = Scr->BorderedLayout; 968 } 969 970 if(tmp_win->zoomed == ZOOM_NONE) { 971 tmp_win->save_frame_x = dragx; 972 tmp_win->save_frame_y = dragy; 973 tmp_win->save_frame_width = dragWidth; 974 tmp_win->save_frame_height = dragHeight; 975 } 976 tmp_win->zoomed = func; 977 978 frame_bw_times_2 = 2 * tmp_win->frame_bw; 979 980 area = RAreaNew(dragx, dragy, dragWidth, dragHeight); 981 982 switch(func) { 983 case ZOOM_NONE: 984 break; 985 case F_XZOOM: 986 finalArea = RLayoutFullVert(borderedLayout, &area); 987 /* fall through */ 988 case F_ZOOM: 989 if(!RAreaIsValid(&finalArea)) { 990 finalArea = RLayoutFullVert1(borderedLayout, &area); 991 } 992 dragy = finalArea.y; 993 dragHeight = finalArea.height - frame_bw_times_2; 994 break; 995 case F_XHORIZOOM: 996 finalArea = RLayoutFullHoriz(borderedLayout, &area); 997 /* fall through */ 998 case F_HORIZOOM: 999 if(!RAreaIsValid(&finalArea)) { 1000 finalArea = RLayoutFullHoriz1(borderedLayout, &area); 1001 } 1002 dragx = finalArea.x; 1003 dragWidth = finalArea.width - frame_bw_times_2; 1004 break; 1005 case F_XFULLZOOM: 1006 finalArea = RLayoutFull(borderedLayout, &area); 1007 /* fall through */ 1008 case F_FULLZOOM: 1009 if(!RAreaIsValid(&finalArea)) { 1010 finalArea = RLayoutFull1(borderedLayout, &area); 1011 } 1012 dragx = finalArea.x; 1013 dragy = finalArea.y; 1014 dragWidth = finalArea.width - frame_bw_times_2; 1015 dragHeight = finalArea.height - frame_bw_times_2; 1016 break; 1017 case F_XLEFTZOOM: 1018 dragx = RLayoutFindLeftEdge(borderedLayout, &area); 1019 dragWidth += area.x - dragx; 1020 // TODO make it visible if hidden 1021 break; 1022 case F_LEFTZOOM: 1023 dragx = RLayoutFindMonitorLeftEdge(borderedLayout, &area); 1024 dragWidth += area.x - dragx; 1025 // TODO make it visible if hidden 1026 break; 1027 case F_XRIGHTZOOM: { 1028 int limit = RLayoutFindRightEdge(borderedLayout, &area); 1029 dragWidth = limit - area.x + 1 - frame_bw_times_2; 1030 // TODO make it visible if hidden 1031 } 1032 break; 1033 case F_RIGHTZOOM: { 1034 int limit = RLayoutFindMonitorRightEdge(borderedLayout, &area); 1035 dragWidth = limit - area.x + 1 - frame_bw_times_2; 1036 // TODO make it visible if hidden 1037 } 1038 break; 1039 case F_XTOPZOOM: 1040 dragy = RLayoutFindTopEdge(borderedLayout, &area); 1041 dragHeight += area.y - dragy; 1042 // TODO make it visible if hidden 1043 break; 1044 case F_TOPZOOM: 1045 dragy = RLayoutFindMonitorTopEdge(borderedLayout, &area); 1046 dragHeight += area.y - dragy; 1047 // TODO make it visible if hidden 1048 break; 1049 case F_XBOTTOMZOOM: { 1050 int limit = RLayoutFindBottomEdge(borderedLayout, &area); 1051 dragHeight = limit - area.y + 1 - frame_bw_times_2; 1052 // TODO make it visible if hidden 1053 } 1054 break; 1055 case F_BOTTOMZOOM: { 1056 int limit = RLayoutFindMonitorBottomEdge(borderedLayout, &area); 1057 dragHeight = limit - area.y + 1 - frame_bw_times_2; 1058 // TODO make it visible if hidden 1059 } 1060 break; 1061 case F_FULLSCREENZOOM: 1062 case F_XFULLSCREENZOOM: { 1063 int bw3D = tmp_win->frame_bw3D; 1064 int bw3D_times_2 = 2 * bw3D; 1065 int bw = tmp_win->frame_bw + bw3D; 1066 1067 finalArea = func == F_XFULLSCREENZOOM 1068 ? RLayoutFull(borderedLayout, &area) 1069 : RLayoutFull1(borderedLayout, &area); 1070 dragx = finalArea.x - bw; 1071 dragy = finalArea.y - tmp_win->title_height - bw; 1072 dragWidth = finalArea.width + bw3D_times_2; 1073 dragHeight = finalArea.height + tmp_win->title_height + bw3D_times_2; 1074 1075 /* and should ignore aspect ratio and size increments... */ 1076#ifdef EWMH 1077 /* x-ref HandleFocusIn() comments for why we need this */ 1078 OtpSetAflag(tmp_win, OTP_AFLAG_FULLSCREEN); 1079 OtpRestackWindow(tmp_win); 1080 /* the OtpRaise below is effectively already done here... */ 1081#endif 1082 } 1083 } 1084 1085 /* Temporary built layout? */ 1086 if(borderedLayout != Scr->BorderedLayout) { 1087 RLayoutFree(borderedLayout); 1088 } 1089 } 1090 1091 if(!Scr->NoRaiseResize && func != F_FULLSCREENZOOM) { 1092 OtpRaise(tmp_win, WinWin); 1093 } 1094 1095 if(func != F_FULLSCREENZOOM) { 1096 ConstrainSize(tmp_win, &dragWidth, &dragHeight); 1097 } 1098#ifdef BETTERZOOM 1099 if(func == F_ZOOM) { 1100 if(dragy + dragHeight < tmp_win->save_frame_y + tmp_win->save_frame_height) { 1101 dragy = tmp_win->save_frame_y + tmp_win->save_frame_height - dragHeight; 1102 } 1103 } 1104#endif 1105 SetupWindow(tmp_win, dragx, dragy, dragWidth, dragHeight, -1); 1106 /* I don't understand the reason of this. Claude. 1107 XUngrabPointer (dpy, CurrentTime); 1108 */ 1109 XUngrabServer(dpy); 1110 1111 XQueryPointer(dpy, 1112 tmp_win->w, 1113 &junkRoot, &junkRoot, 1114 &tmpX, &tmpY, &tmpW, &tmpH, &junkDepth); 1115 if(tmp_win->frame_x > tmpX || 1116 tmp_win->frame_x + tmp_win->frame_width < tmpX || 1117 tmp_win->frame_y > tmpY || 1118 tmp_win->frame_y + tmp_win->frame_height < tmpY) { 1119 XWarpPointer(dpy, Scr->Root, tmp_win->w, 0, 0, 0, 0, 0, 0); 1120 } 1121 1122#ifdef EWMH 1123 /* 1124 * Reset _NET_WM_STATE prop on the window. It sets whichever state 1125 * applies, not always the _MAXIMIZED_VERT we specify here. 1126 */ 1127 EwmhSet_NET_WM_STATE(tmp_win, EWMH_STATE_MAXIMIZED_VERT); 1128#endif 1129} 1130 1131/* 1132 * Forget about a window being zoomed. 1133 * This also needs to undo the special effects of F_FULLSCREENZOOM. 1134 */ 1135void unzoom(TwmWindow *tmp_win) 1136{ 1137 if(tmp_win->zoomed != ZOOM_NONE) { 1138#ifdef EWMH 1139 if(tmp_win->zoomed == F_FULLSCREENZOOM) { 1140 OtpClearAflag(tmp_win, OTP_AFLAG_FULLSCREEN); 1141 OtpRestackWindow(tmp_win); 1142 } 1143#endif 1144 1145 tmp_win->zoomed = ZOOM_NONE; 1146 } 1147} 1148 1149void savegeometry(TwmWindow *tmp_win) 1150{ 1151 if(!tmp_win) { 1152 return; 1153 } 1154 tmp_win->savegeometry.x = tmp_win->frame_x; 1155 tmp_win->savegeometry.y = tmp_win->frame_y; 1156 tmp_win->savegeometry.width = tmp_win->frame_width; 1157 tmp_win->savegeometry.height = tmp_win->frame_height; 1158} 1159 1160void restoregeometry(TwmWindow *tmp_win) 1161{ 1162 int x, y; 1163 unsigned int w, h; 1164 1165 if(!tmp_win) { 1166 return; 1167 } 1168 if(tmp_win->savegeometry.width == (unsigned int) - 1) { 1169 return; 1170 } 1171 x = tmp_win->savegeometry.x; 1172 y = tmp_win->savegeometry.y; 1173 w = tmp_win->savegeometry.width; 1174 h = tmp_win->savegeometry.height; 1175 SetupWindow(tmp_win, x, y, w, h, -1); 1176} 1177 1178 1179void ChangeSize(char *in_string, TwmWindow *tmp_win) 1180{ 1181 int change = 0, size = 0; 1182 char *endptr; 1183 int rx, ry, wx, wy, mr; 1184 Window rr, cr; 1185 1186 if(Isdigit(in_string[0])) { 1187 /* Handle the case f.changesize "640x480" */ 1188 wx = strtol(in_string, &endptr, 10); 1189 if(*endptr++ != 'x') { 1190 fprintf(stderr, 1191 "%s: Bad argument to f.changesize: \"%s\" (pattern \"640x480\")\n", 1192 ProgramName, in_string); 1193 return; 1194 } 1195 wy = strtol(endptr, &endptr, 10); 1196 1197 if(wy < tmp_win->title_height + 1) { 1198 wy = tmp_win->title_height + 1; 1199 } 1200 1201 SetupWindow(tmp_win, tmp_win->frame_x, tmp_win->frame_y, 1202 wx, wy + tmp_win->title_height, -1); 1203 } 1204 else { 1205 /* Handle the cases like f.changesize "right +10" */ 1206 int cmdlen = 0; 1207 1208 while(in_string[cmdlen] != ' ' && in_string[cmdlen] != '\0') { 1209 cmdlen++; 1210 } 1211 1212 if(in_string[cmdlen] != ' ') { 1213 fprintf(stderr, 1214 "%s: Bad argument to f.changesize: \"%s\" (sizechange missing)\n", 1215 ProgramName, in_string); 1216 return; 1217 } 1218 1219 change = strtol(in_string + cmdlen + 1, &endptr, 10); 1220 if(*endptr != 0) { 1221 fprintf(stderr, 1222 "%s: Bad argument to f.changesize: \"%s\" (sizechange not a number)\n", 1223 ProgramName, in_string); 1224 return; 1225 } 1226 1227 if(strncmp("bottom", in_string, cmdlen) == 0) { 1228 size = tmp_win->frame_height + change; 1229 1230 if(size < (tmp_win->title_height + 1)) { 1231 size = tmp_win->title_height + 1; 1232 } 1233 1234 SetupWindow(tmp_win, tmp_win->frame_x, tmp_win->frame_y, 1235 tmp_win->frame_width, size, 1236 -1); 1237 1238 XQueryPointer(dpy, tmp_win->w, &rr, &cr, &rx, &ry, &wx, &wy, 1239 (unsigned int *)&mr); 1240 1241 if((wy + tmp_win->title_height) > size) { 1242 XWarpPointer(dpy, None, tmp_win->w, 0, 0, 0, 0, 0, 0); 1243 } 1244 } 1245 else if(strncmp("top", in_string, cmdlen) == 0) { 1246 size = tmp_win->frame_height + change; 1247 1248 if(size < (tmp_win->title_height + 1)) { 1249 size = tmp_win->title_height + 1; 1250 } 1251 1252 SetupWindow(tmp_win, tmp_win->frame_x, (tmp_win->frame_y - change), 1253 tmp_win->frame_width, size, 1254 -1); 1255 1256 XQueryPointer(dpy, tmp_win->w, &rr, &cr, &rx, &ry, &wx, &wy, 1257 (unsigned int *)&mr); 1258 1259 if((wy + tmp_win->title_height) > size) { 1260 XWarpPointer(dpy, None, tmp_win->w, 0, 0, 0, 0, 0, 0); 1261 } 1262 1263 1264 } 1265 else if(strncmp("left", in_string, cmdlen) == 0) { 1266 size = tmp_win->frame_width + change; 1267 1268 if(size < 1) { 1269 size = 1; 1270 } 1271 1272 SetupWindow(tmp_win, (tmp_win->frame_x - change), tmp_win->frame_y, 1273 size, tmp_win->frame_height, 1274 -1); 1275 1276 XQueryPointer(dpy, tmp_win->w, &rr, &cr, &rx, &ry, &wx, &wy, 1277 (unsigned int *)&mr); 1278 1279 if(wx > size) { 1280 XWarpPointer(dpy, None, tmp_win->w, 0, 0, 0, 0, 0, 0); 1281 } 1282 1283 1284 } 1285 else if(strncmp("right", in_string, cmdlen) == 0) { 1286 size = tmp_win->frame_width + change; 1287 1288 if(size < 1) { 1289 size = 1; 1290 } 1291 1292 SetupWindow(tmp_win, tmp_win->frame_x, tmp_win->frame_y, 1293 size, tmp_win->frame_height, 1294 -1); 1295 1296 XQueryPointer(dpy, tmp_win->w, &rr, &cr, &rx, &ry, &wx, &wy, 1297 (unsigned int *)&mr); 1298 1299 if(wx > size) { 1300 XWarpPointer(dpy, None, tmp_win->w, 0, 0, 0, 0, 0, 0); 1301 } 1302 1303 } 1304 else { 1305 /* error */ 1306 fprintf(stderr, "%s: Bad argument to f.changesize: \"%s\"\n (unknown border)", 1307 ProgramName, in_string); 1308 return; 1309 } 1310 } 1311} 1312 1313 1314/*********************************************************************** 1315 * 1316 * Procedure: 1317 * resizeFromCenter - 1318 * 1319 *********************************************************************** 1320 */ 1321void 1322resizeFromCenter(Window w, TwmWindow *tmp_win) 1323{ 1324 int lastx, lasty, bw2; 1325 1326 bw2 = tmp_win->frame_bw * 2; 1327 AddingW = tmp_win->attr.width + bw2 + 2 * tmp_win->frame_bw3D; 1328 AddingH = tmp_win->attr.height + tmp_win->title_height + bw2 + 2 * 1329 tmp_win->frame_bw3D; 1330 1331 XGetGeometry(dpy, w, &JunkRoot, &origDragX, &origDragY, 1332 &DragWidth, &DragHeight, 1333 &JunkBW, &JunkDepth); 1334 1335 XWarpPointer(dpy, None, w, 1336 0, 0, 0, 0, DragWidth / 2, DragHeight / 2); 1337 XQueryPointer(dpy, Scr->Root, &JunkRoot, 1338 &JunkChild, &JunkX, &JunkY, 1339 &AddingX, &AddingY, &JunkMask); 1340 1341 lastx = -10000; 1342 lasty = -10000; 1343 1344 MenuStartResize(tmp_win, origDragX, origDragY, DragWidth, DragHeight); 1345 while(1) { 1346 XMaskEvent(dpy, 1347 ButtonPressMask | PointerMotionMask | ExposureMask, &Event); 1348 1349 if(Event.type == MotionNotify) { 1350 /* discard any extra motion events before a release */ 1351 while(XCheckMaskEvent(dpy, 1352 ButtonMotionMask | ButtonPressMask, &Event)) 1353 if(Event.type == ButtonPress) { 1354 break; 1355 } 1356 } 1357 1358 if(Event.type == ButtonPress) { 1359 MenuEndResize(tmp_win); 1360 // Next line should be unneeded, done by MenuEndResize() ? 1361 XMoveResizeWindow(dpy, w, AddingX, AddingY, AddingW, AddingH); 1362 break; 1363 } 1364 1365 if(Event.type != MotionNotify) { 1366 DispatchEvent2(); 1367 if(Cancel) { 1368 // ... 1369 MenuEndResize(tmp_win); 1370 return; 1371 } 1372 continue; 1373 } 1374 1375 /* 1376 * XXX - if we are going to do a loop, we ought to consider 1377 * using multiple GXxor lines so that we don't need to 1378 * grab the server. 1379 */ 1380 XQueryPointer(dpy, Scr->Root, &JunkRoot, &JunkChild, 1381 &JunkX, &JunkY, &AddingX, &AddingY, &JunkMask); 1382 1383 if(lastx != AddingX || lasty != AddingY) { 1384 MenuDoResize(AddingX, AddingY, tmp_win); 1385 1386 lastx = AddingX; 1387 lasty = AddingY; 1388 } 1389 1390 } 1391} 1392