resize.c revision 7a2631fc
1/*****************************************************************************/
2/*
3
4Copyright 1989, 1998  The Open Group
5
6Permission to use, copy, modify, distribute, and sell this software and its
7documentation for any purpose is hereby granted without fee, provided that
8the above copyright notice appear in all copies and that both that
9copyright notice and this permission notice appear in supporting
10documentation.
11
12The above copyright notice and this permission notice shall be included in
13all copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
18OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
19AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22Except as contained in this notice, the name of The Open Group shall not be
23used in advertising or otherwise to promote the sale, use or other dealings
24in this Software without prior written authorization from The Open Group.
25
26*/
27/**       Copyright 1988 by Evans & Sutherland Computer Corporation,        **/
28/**                          Salt Lake City, Utah                           **/
29/**                        Cambridge, Massachusetts                         **/
30/**                                                                         **/
31/**                           All Rights Reserved                           **/
32/**                                                                         **/
33/**    Permission to use, copy, modify, and distribute this software and    **/
34/**    its documentation  for  any  purpose  and  without  fee is hereby    **/
35/**    granted, provided that the above copyright notice appear  in  all    **/
36/**    copies and that both  that  copyright  notice  and  this  permis-    **/
37/**    sion  notice appear in supporting  documentation,  and  that  the    **/
38/**    name of Evans & Sutherland not be used in advertising    **/
39/**    in publicity pertaining to distribution of the  software  without    **/
40/**    specific, written prior permission.                                  **/
41/**                                                                         **/
42/**    EVANS & SUTHERLAND DISCLAIMs ALL WARRANTIES WITH REGARD    **/
43/**    TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES  OF  MERCHANT-    **/
44/**    ABILITY  AND  FITNESS,  IN  NO  EVENT SHALL EVANS & SUTHERLAND    **/
45/**    BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL  DAM-    **/
46/**    AGES OR  ANY DAMAGES WHATSOEVER  RESULTING FROM LOSS OF USE, DATA    **/
47/**    OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER    **/
48/**    TORTIOUS ACTION, ARISING OUT OF OR IN  CONNECTION  WITH  THE  USE    **/
49/**    OR PERFORMANCE OF THIS SOFTWARE.                                     **/
50/*****************************************************************************/
51
52
53/***********************************************************************
54 *
55 * window resizing borrowed from the "wm" window manager
56 *
57 * 11-Dec-87 Thomas E. LaStrange                File created
58 *
59 ***********************************************************************/
60
61#include <stdio.h>
62#include "twm.h"
63#include "parse.h"
64#include "util.h"
65#include "resize.h"
66#include "iconmgr.h"
67#include "add_window.h"
68#include "screen.h"
69#include "events.h"
70
71#define MINHEIGHT 0     /* had been 32 */
72#define MINWIDTH 0      /* had been 60 */
73
74static int dragx;       /* all these variables are used */
75static int dragy;       /* in resize operations */
76static int dragWidth;
77static int dragHeight;
78
79static int origx;
80static int origy;
81static int origWidth;
82static int origHeight;
83
84static int clampTop;
85static int clampBottom;
86static int clampLeft;
87static int clampRight;
88static int clampDX;
89static int clampDY;
90
91static int last_width;
92static int last_height;
93
94
95static void
96do_auto_clamp (TwmWindow *tmp_win, XEvent *evp)
97{
98    Window junkRoot;
99    int x, y, h, v, junkbw;
100    unsigned int junkMask;
101
102    switch (evp->type) {
103      case ButtonPress:
104	x = evp->xbutton.x_root;
105	y = evp->xbutton.y_root;
106	break;
107      case KeyPress:
108	x = evp->xkey.x_root;
109	y = evp->xkey.y_root;
110	break;
111      default:
112	if (!XQueryPointer (dpy, Scr->Root, &junkRoot, &junkRoot,
113			    &x, &y, &junkbw, &junkbw, &junkMask))
114	  return;
115    }
116
117    h = ((x - dragx) / (dragWidth < 3 ? 1 : (dragWidth / 3)));
118    v = ((y - dragy - tmp_win->title_height) /
119	 (dragHeight < 3 ? 1 : (dragHeight / 3)));
120
121    if (h <= 0) {
122	clampLeft = 1;
123	clampDX = (x - dragx);
124    } else if (h >= 2) {
125	clampRight = 1;
126	clampDX = (x - dragx - dragWidth);
127    }
128
129    if (v <= 0) {
130	clampTop = 1;
131	clampDY = (y - dragy);
132    } else if (v >= 2) {
133	clampBottom = 1;
134	clampDY = (y - dragy - dragHeight);
135    }
136}
137
138
139/**
140 * begin a window resize operation
141 *  \param ev           the event structure (button press)
142 *  \param tmp_win      the TwmWindow pointer
143 *  \param fromtitlebar action invoked from titlebar button
144 */
145void
146StartResize(XEvent *evp, TwmWindow *tmp_win, Bool fromtitlebar)
147{
148    Window      junkRoot;
149    unsigned int junkbw, junkDepth;
150
151    ResizeWindow = tmp_win->frame;
152    XGrabServer(dpy);
153    XGrabPointer(dpy, Scr->Root, True,
154        ButtonPressMask | ButtonReleaseMask |
155	ButtonMotionMask | PointerMotionHintMask,
156        GrabModeAsync, GrabModeAsync,
157        Scr->Root, Scr->ResizeCursor, CurrentTime);
158
159    XGetGeometry(dpy, (Drawable) tmp_win->frame, &junkRoot,
160        &dragx, &dragy, (unsigned int *)&dragWidth, (unsigned int *)&dragHeight, &junkbw,
161                 &junkDepth);
162    dragx += tmp_win->frame_bw;
163    dragy += tmp_win->frame_bw;
164    origx = dragx;
165    origy = dragy;
166    origWidth = dragWidth;
167    origHeight = dragHeight;
168    clampTop = clampBottom = clampLeft = clampRight = clampDX = clampDY = 0;
169
170    if (Scr->AutoRelativeResize && !fromtitlebar)
171      do_auto_clamp (tmp_win, evp);
172
173    Scr->SizeStringOffset = SIZE_HINDENT;
174    XResizeWindow (dpy, Scr->SizeWindow,
175		   Scr->SizeStringWidth + SIZE_HINDENT * 2,
176		   Scr->SizeFont.height + SIZE_VINDENT * 2);
177    XMapRaised(dpy, Scr->SizeWindow);
178    InstallRootColormap();
179    last_width = 0;
180    last_height = 0;
181    DisplaySize(tmp_win, origWidth, origHeight);
182    MoveOutline (Scr->Root, dragx - tmp_win->frame_bw,
183		 dragy - tmp_win->frame_bw, dragWidth + 2 * tmp_win->frame_bw,
184		 dragHeight + 2 * tmp_win->frame_bw,
185		 tmp_win->frame_bw, tmp_win->title_height);
186}
187
188
189
190void
191MenuStartResize(TwmWindow *tmp_win, int x, int y, int w, int h)
192{
193    XGrabServer(dpy);
194    XGrabPointer(dpy, Scr->Root, True,
195        ButtonPressMask | ButtonMotionMask | PointerMotionMask,
196        GrabModeAsync, GrabModeAsync,
197        Scr->Root, Scr->ResizeCursor, CurrentTime);
198    dragx = x + tmp_win->frame_bw;
199    dragy = y + tmp_win->frame_bw;
200    origx = dragx;
201    origy = dragy;
202    dragWidth = origWidth = w; /* - 2 * tmp_win->frame_bw; */
203    dragHeight = origHeight = h; /* - 2 * tmp_win->frame_bw; */
204    clampTop = clampBottom = clampLeft = clampRight = clampDX = clampDY = 0;
205    last_width = 0;
206    last_height = 0;
207    Scr->SizeStringOffset = SIZE_HINDENT;
208    XResizeWindow (dpy, Scr->SizeWindow,
209		   Scr->SizeStringWidth + SIZE_HINDENT * 2,
210		   Scr->SizeFont.height + SIZE_VINDENT * 2);
211    XMapRaised(dpy, Scr->SizeWindow);
212    DisplaySize(tmp_win, origWidth, origHeight);
213    MoveOutline (Scr->Root, dragx - tmp_win->frame_bw,
214		 dragy - tmp_win->frame_bw,
215		 dragWidth + 2 * tmp_win->frame_bw,
216		 dragHeight + 2 * tmp_win->frame_bw,
217		 tmp_win->frame_bw, tmp_win->title_height);
218}
219
220/**
221 * begin a windorew resize operation from AddWindow
222 *  \param tmp_win the TwmWindow pointer
223 */
224void
225AddStartResize(TwmWindow *tmp_win, int x, int y, int w, int h)
226{
227    XGrabServer(dpy);
228    XGrabPointer(dpy, Scr->Root, True,
229        ButtonReleaseMask | ButtonMotionMask | PointerMotionHintMask,
230        GrabModeAsync, GrabModeAsync,
231        Scr->Root, Scr->ResizeCursor, CurrentTime);
232
233    dragx = x + tmp_win->frame_bw;
234    dragy = y + tmp_win->frame_bw;
235    origx = dragx;
236    origy = dragy;
237    dragWidth = origWidth = w - 2 * tmp_win->frame_bw;
238    dragHeight = origHeight = h - 2 * tmp_win->frame_bw;
239    clampTop = clampBottom = clampLeft = clampRight = clampDX = clampDY = 0;
240/*****
241    if (Scr->AutoRelativeResize) {
242	clampRight = clampBottom = 1;
243    }
244*****/
245    last_width = 0;
246    last_height = 0;
247    DisplaySize(tmp_win, origWidth, origHeight);
248}
249
250
251
252void
253MenuDoResize(int x_root, int y_root, TwmWindow *tmp_win)
254{
255    int action;
256
257    action = 0;
258
259    x_root -= clampDX;
260    y_root -= clampDY;
261
262    if (clampTop) {
263        int         delta = y_root - dragy;
264        if (dragHeight - delta < MINHEIGHT) {
265            delta = dragHeight - MINHEIGHT;
266            clampTop = 0;
267        }
268        dragy += delta;
269        dragHeight -= delta;
270        action = 1;
271    }
272    else if (y_root <= dragy/* ||
273             y_root == findRootInfo(root)->rooty*/) {
274        dragy = y_root;
275        dragHeight = origy + origHeight -
276            y_root;
277        clampBottom = 0;
278        clampTop = 1;
279	clampDY = 0;
280        action = 1;
281    }
282    if (clampLeft) {
283        int         delta = x_root - dragx;
284        if (dragWidth - delta < MINWIDTH) {
285            delta = dragWidth - MINWIDTH;
286            clampLeft = 0;
287        }
288        dragx += delta;
289        dragWidth -= delta;
290        action = 1;
291    }
292    else if (x_root <= dragx/* ||
293             x_root == findRootInfo(root)->rootx*/) {
294        dragx = x_root;
295        dragWidth = origx + origWidth -
296            x_root;
297        clampRight = 0;
298        clampLeft = 1;
299	clampDX = 0;
300        action = 1;
301    }
302    if (clampBottom) {
303        int         delta = y_root - dragy - dragHeight;
304        if (dragHeight + delta < MINHEIGHT) {
305            delta = MINHEIGHT - dragHeight;
306            clampBottom = 0;
307        }
308        dragHeight += delta;
309        action = 1;
310    }
311    else if (y_root >= dragy + dragHeight) {
312        dragy = origy;
313        dragHeight = 1 + y_root - dragy;
314        clampTop = 0;
315        clampBottom = 1;
316	clampDY = 0;
317        action = 1;
318    }
319    if (clampRight) {
320        int         delta = x_root - dragx - dragWidth;
321        if (dragWidth + delta < MINWIDTH) {
322            delta = MINWIDTH - dragWidth;
323            clampRight = 0;
324        }
325        dragWidth += delta;
326        action = 1;
327    }
328    else if (x_root >= dragx + dragWidth) {
329        dragx = origx;
330        dragWidth = 1 + x_root - origx;
331        clampLeft = 0;
332        clampRight = 1;
333	clampDX = 0;
334        action = 1;
335    }
336
337    if (action) {
338        ConstrainSize (tmp_win, &dragWidth, &dragHeight);
339        if (clampLeft)
340            dragx = origx + origWidth - dragWidth;
341        if (clampTop)
342            dragy = origy + origHeight - dragHeight;
343        MoveOutline(Scr->Root,
344            dragx - tmp_win->frame_bw,
345            dragy - tmp_win->frame_bw,
346            dragWidth + 2 * tmp_win->frame_bw,
347            dragHeight + 2 * tmp_win->frame_bw,
348	    tmp_win->frame_bw, tmp_win->title_height);
349    }
350
351    DisplaySize(tmp_win, dragWidth, dragHeight);
352}
353
354
355/**
356 * move the rubberband around.  This is called for each motion event when
357 * we are resizing
358 *
359 *  \param x_root  the X corrdinate in the root window
360 *  \param y_root  the Y corrdinate in the root window
361 *  \param tmp_win the current twm window
362 */
363void
364DoResize(int x_root, int y_root, TwmWindow *tmp_win)
365{
366    int action;
367
368    action = 0;
369
370    x_root -= clampDX;
371    y_root -= clampDY;
372
373    if (clampTop) {
374        int         delta = y_root - dragy;
375        if (dragHeight - delta < MINHEIGHT) {
376            delta = dragHeight - MINHEIGHT;
377            clampTop = 0;
378        }
379        dragy += delta;
380        dragHeight -= delta;
381        action = 1;
382    }
383    else if (y_root <= dragy/* ||
384             y_root == findRootInfo(root)->rooty*/) {
385        dragy = y_root;
386        dragHeight = origy + origHeight -
387            y_root;
388        clampBottom = 0;
389        clampTop = 1;
390	clampDY = 0;
391        action = 1;
392    }
393    if (clampLeft) {
394        int         delta = x_root - dragx;
395        if (dragWidth - delta < MINWIDTH) {
396            delta = dragWidth - MINWIDTH;
397            clampLeft = 0;
398        }
399        dragx += delta;
400        dragWidth -= delta;
401        action = 1;
402    }
403    else if (x_root <= dragx/* ||
404             x_root == findRootInfo(root)->rootx*/) {
405        dragx = x_root;
406        dragWidth = origx + origWidth -
407            x_root;
408        clampRight = 0;
409        clampLeft = 1;
410	clampDX = 0;
411        action = 1;
412    }
413    if (clampBottom) {
414        int         delta = y_root - dragy - dragHeight;
415        if (dragHeight + delta < MINHEIGHT) {
416            delta = MINHEIGHT - dragHeight;
417            clampBottom = 0;
418        }
419        dragHeight += delta;
420        action = 1;
421    }
422    else if (y_root >= dragy + dragHeight - 1/* ||
423           y_root == findRootInfo(root)->rooty
424           + findRootInfo(root)->rootheight - 1*/) {
425        dragy = origy;
426        dragHeight = 1 + y_root - dragy;
427        clampTop = 0;
428        clampBottom = 1;
429	clampDY = 0;
430        action = 1;
431    }
432    if (clampRight) {
433        int         delta = x_root - dragx - dragWidth;
434        if (dragWidth + delta < MINWIDTH) {
435            delta = MINWIDTH - dragWidth;
436            clampRight = 0;
437        }
438        dragWidth += delta;
439        action = 1;
440    }
441    else if (x_root >= dragx + dragWidth - 1/* ||
442             x_root == findRootInfo(root)->rootx +
443             findRootInfo(root)->rootwidth - 1*/) {
444        dragx = origx;
445        dragWidth = 1 + x_root - origx;
446        clampLeft = 0;
447        clampRight = 1;
448	clampDX = 0;
449        action = 1;
450    }
451
452    if (action) {
453        ConstrainSize (tmp_win, &dragWidth, &dragHeight);
454        if (clampLeft)
455            dragx = origx + origWidth - dragWidth;
456        if (clampTop)
457            dragy = origy + origHeight - dragHeight;
458        MoveOutline(Scr->Root,
459            dragx - tmp_win->frame_bw,
460            dragy - tmp_win->frame_bw,
461            dragWidth + 2 * tmp_win->frame_bw,
462            dragHeight + 2 * tmp_win->frame_bw,
463	    tmp_win->frame_bw, tmp_win->title_height);
464    }
465
466    DisplaySize(tmp_win, dragWidth, dragHeight);
467}
468
469/**
470 * display the size in the dimensions window.
471 *
472 *  \param tmp_win the current twm window
473 *  \param width   the width of the rubber band
474 *  \param height  the height of the rubber band
475 */
476void
477DisplaySize(TwmWindow *tmp_win, int width, int height)
478{
479    char str[100];
480    int dwidth;
481    int dheight;
482
483    if (last_width == width && last_height == height)
484        return;
485
486    last_width = width;
487    last_height = height;
488
489    dheight = height - tmp_win->title_height;
490    dwidth = width;
491
492    /*
493     * ICCCM says that PMinSize is the default is no PBaseSize is given,
494     * and vice-versa.
495     */
496    if (tmp_win->hints.flags&(PMinSize|PBaseSize) && tmp_win->hints.flags & PResizeInc)
497    {
498	if (tmp_win->hints.flags & PBaseSize) {
499	    dwidth -= tmp_win->hints.base_width;
500	    dheight -= tmp_win->hints.base_height;
501	} else {
502	    dwidth -= tmp_win->hints.min_width;
503	    dheight -= tmp_win->hints.min_height;
504	}
505    }
506
507    if (tmp_win->hints.flags & PResizeInc)
508    {
509        dwidth /= tmp_win->hints.width_inc;
510        dheight /= tmp_win->hints.height_inc;
511    }
512
513    (void) sprintf (str, " %4d x %-4d ", dwidth, dheight);
514    XRaiseWindow(dpy, Scr->SizeWindow);
515    MyFont_ChangeGC(Scr->DefaultC.fore, Scr->DefaultC.back, &Scr->SizeFont);
516    MyFont_DrawImageString (dpy, Scr->SizeWindow, &Scr->SizeFont,
517			    Scr->NormalGC, Scr->SizeStringOffset,
518			    Scr->SizeFont.ascent + SIZE_VINDENT,
519			    str, 13);
520}
521
522/**
523 * finish the resize operation
524 */
525void
526EndResize()
527{
528    TwmWindow *tmp_win;
529
530#ifdef DEBUG
531    fprintf(stderr, "EndResize\n");
532#endif
533
534    MoveOutline(Scr->Root, 0, 0, 0, 0, 0, 0);
535    XUnmapWindow(dpy, Scr->SizeWindow);
536
537    XFindContext(dpy, ResizeWindow, TwmContext, (caddr_t *)&tmp_win);
538
539    ConstrainSize (tmp_win, &dragWidth, &dragHeight);
540
541    if (dragWidth != tmp_win->frame_width ||
542        dragHeight != tmp_win->frame_height)
543            tmp_win->zoomed = ZOOM_NONE;
544
545    SetupWindow (tmp_win, dragx - tmp_win->frame_bw, dragy - tmp_win->frame_bw,
546		 dragWidth, dragHeight, -1);
547
548    if (tmp_win->iconmgr)
549    {
550	int ncols = tmp_win->iconmgrp->cur_columns;
551	if (ncols == 0) ncols = 1;
552
553	tmp_win->iconmgrp->width = (int) ((dragWidth *
554					   (long) tmp_win->iconmgrp->columns)
555					  / ncols);
556        PackIconManager(tmp_win->iconmgrp);
557    }
558
559    if (!Scr->NoRaiseResize)
560        XRaiseWindow(dpy, tmp_win->frame);
561
562    UninstallRootColormap();
563
564    ResizeWindow = None;
565}
566
567void
568MenuEndResize(TwmWindow *tmp_win)
569{
570    MoveOutline(Scr->Root, 0, 0, 0, 0, 0, 0);
571    XUnmapWindow(dpy, Scr->SizeWindow);
572    ConstrainSize (tmp_win, &dragWidth, &dragHeight);
573    AddingX = dragx - tmp_win->frame_bw;
574    AddingY = dragy - tmp_win->frame_bw;
575    AddingW = dragWidth;/* + (2 * tmp_win->frame_bw);*/
576    AddingH = dragHeight;/* + (2 * tmp_win->frame_bw);*/
577    SetupWindow (tmp_win, AddingX, AddingY, AddingW, AddingH, -1);
578}
579
580
581
582/**
583 * finish the resize operation for AddWindo<w
584 */
585void
586AddEndResize(TwmWindow *tmp_win)
587{
588
589#ifdef DEBUG
590    fprintf(stderr, "AddEndResize\n");
591#endif
592
593    ConstrainSize (tmp_win, &dragWidth, &dragHeight);
594    AddingX = dragx - tmp_win->frame_bw;
595    AddingY = dragy - tmp_win->frame_bw;
596    AddingW = dragWidth + (2 * tmp_win->frame_bw);
597    AddingH = dragHeight + (2 * tmp_win->frame_bw);
598}
599
600/**
601 * adjust the given width and height to account for the constraints imposed
602 * by size hints.
603 *
604 *      The general algorithm, especially the aspect ratio stuff, is
605 *      borrowed from uwm's CheckConsistency routine.
606 */
607void
608ConstrainSize (TwmWindow *tmp_win, int *widthp, int *heightp)
609{
610#define makemult(a,b) ((b==1) ? (a) : (((int)((a)/(b))) * (b)) )
611#define _min(a,b) (((a) < (b)) ? (a) : (b))
612
613    int minWidth, minHeight, maxWidth, maxHeight, xinc, yinc, delta;
614    int baseWidth, baseHeight;
615    int dwidth = *widthp, dheight = *heightp;
616
617
618    dheight -= tmp_win->title_height;
619
620    if (tmp_win->hints.flags & PMinSize) {
621        minWidth = tmp_win->hints.min_width;
622        minHeight = tmp_win->hints.min_height;
623    } else if (tmp_win->hints.flags & PBaseSize) {
624        minWidth = tmp_win->hints.base_width;
625        minHeight = tmp_win->hints.base_height;
626    } else
627        minWidth = minHeight = 1;
628
629    if (tmp_win->hints.flags & PBaseSize) {
630	baseWidth = tmp_win->hints.base_width;
631	baseHeight = tmp_win->hints.base_height;
632    } else if (tmp_win->hints.flags & PMinSize) {
633	baseWidth = tmp_win->hints.min_width;
634	baseHeight = tmp_win->hints.min_height;
635    } else
636	baseWidth = baseHeight = 0;
637
638
639    if (tmp_win->hints.flags & PMaxSize) {
640        maxWidth = _min (Scr->MaxWindowWidth, tmp_win->hints.max_width);
641        maxHeight = _min (Scr->MaxWindowHeight, tmp_win->hints.max_height);
642    } else {
643        maxWidth = Scr->MaxWindowWidth;
644	maxHeight = Scr->MaxWindowHeight;
645    }
646
647    if (tmp_win->hints.flags & PResizeInc) {
648        xinc = tmp_win->hints.width_inc;
649        yinc = tmp_win->hints.height_inc;
650    } else
651        xinc = yinc = 1;
652
653    /*
654     * First, clamp to min and max values
655     */
656    if (dwidth < minWidth) dwidth = minWidth;
657    if (dheight < minHeight) dheight = minHeight;
658
659    if (dwidth > maxWidth) dwidth = maxWidth;
660    if (dheight > maxHeight) dheight = maxHeight;
661
662
663    /*
664     * Second, fit to base + N * inc
665     */
666    dwidth = ((dwidth - baseWidth) / xinc * xinc) + baseWidth;
667    dheight = ((dheight - baseHeight) / yinc * yinc) + baseHeight;
668
669
670    /*
671     * Third, adjust for aspect ratio
672     */
673#define maxAspectX tmp_win->hints.max_aspect.x
674#define maxAspectY tmp_win->hints.max_aspect.y
675#define minAspectX tmp_win->hints.min_aspect.x
676#define minAspectY tmp_win->hints.min_aspect.y
677    /*
678     * The math looks like this:
679     *
680     * minAspectX    dwidth     maxAspectX
681     * ---------- <= ------- <= ----------
682     * minAspectY    dheight    maxAspectY
683     *
684     * If that is multiplied out, then the width and height are
685     * invalid in the following situations:
686     *
687     * minAspectX * dheight > minAspectY * dwidth
688     * maxAspectX * dheight < maxAspectY * dwidth
689     *
690     */
691
692    if (tmp_win->hints.flags & PAspect)
693    {
694        if (minAspectX * dheight > minAspectY * dwidth)
695        {
696            delta = makemult(minAspectX * dheight / minAspectY - dwidth,
697                             xinc);
698            if (dwidth + delta <= maxWidth) dwidth += delta;
699            else
700            {
701                delta = makemult(dheight - dwidth*minAspectY/minAspectX,
702                                 yinc);
703                if (dheight - delta >= minHeight) dheight -= delta;
704            }
705        }
706
707        if (maxAspectX * dheight < maxAspectY * dwidth)
708        {
709            delta = makemult(dwidth * maxAspectY / maxAspectX - dheight,
710                             yinc);
711            if (dheight + delta <= maxHeight) dheight += delta;
712            else
713            {
714                delta = makemult(dwidth - maxAspectX*dheight/maxAspectY,
715                                 xinc);
716                if (dwidth - delta >= minWidth) dwidth -= delta;
717            }
718        }
719    }
720
721
722    /*
723     * Fourth, account for border width and title height
724     */
725    *widthp = dwidth;
726    *heightp = dheight + tmp_win->title_height;
727}
728
729
730/**
731 * set window sizes, this was called from either AddWindow, EndResize, or
732 * HandleConfigureNotify.
733 *
734 *  Special Considerations:
735 * This routine will check to make sure the window is not completely off the
736 * display, if it is, it'll bring some of it back on.
737 *
738 * The tmp_win->frame_XXX variables should NOT be updated with the values of
739 * x,y,w,h prior to calling this routine, since the new values are compared
740 * against the old to see whether a synthetic ConfigureNotify event should be
741 * sent.  (It should be sent if the window was moved but not resized.)
742 *
743 *  \param tmp_win the TwmWindow pointer
744 *  \param x       the x coordinate of the upper-left outer corner of the frame
745 *  \param y       the y coordinate of the upper-left outer corner of the frame
746 *  \param w       the width of the frame window w/o border
747 *  \param h       the height of the frame window w/o border
748 *  \param bw      the border width of the frame window or -1 not to change
749 */
750void SetupWindow (TwmWindow *tmp_win, int x, int y, int w, int h, int bw)
751{
752    SetupFrame (tmp_win, x, y, w, h, bw, False);
753}
754
755/**
756 *  \param sendEvent whether or not to force a send
757 */
758void SetupFrame (TwmWindow *tmp_win, int x, int y, int w, int h, int bw, Bool sendEvent)
759{
760    XEvent client_event;
761    XWindowChanges frame_wc, xwc;
762    unsigned long frame_mask, xwcm;
763    int title_width, title_height;
764    int reShape;
765
766#ifdef DEBUG
767    fprintf (stderr, "SetupWindow: x=%d, y=%d, w=%d, h=%d, bw=%d\n",
768	     x, y, w, h, bw);
769#endif
770
771    if (x >= Scr->MyDisplayWidth)
772      x = Scr->MyDisplayWidth - 16;	/* one "average" cursor width */
773    if (y >= Scr->MyDisplayHeight)
774      y = Scr->MyDisplayHeight - 16;	/* one "average" cursor width */
775    if (bw < 0)
776      bw = tmp_win->frame_bw;		/* -1 means current frame width */
777
778    if (tmp_win->iconmgr) {
779	tmp_win->iconmgrp->width = w;
780        h = tmp_win->iconmgrp->height + tmp_win->title_height;
781    }
782
783    /*
784     * According to the July 27, 1988 ICCCM draft, we should send a
785     * "synthetic" ConfigureNotify event to the client if the window
786     * was moved but not resized.
787     */
788    if (((x != tmp_win->frame_x || y != tmp_win->frame_y) &&
789	 (w == tmp_win->frame_width && h == tmp_win->frame_height)) ||
790	(bw != tmp_win->frame_bw))
791      sendEvent = TRUE;
792
793    xwcm = CWWidth;
794    title_width = xwc.width = w;
795    title_height = Scr->TitleHeight + bw;
796
797    ComputeWindowTitleOffsets (tmp_win, xwc.width, True);
798
799    reShape = (tmp_win->wShaped ? TRUE : FALSE);
800    if (tmp_win->squeeze_info)		/* check for title shaping */
801    {
802	title_width = tmp_win->rightx + Scr->TBInfo.rightoff;
803	if (title_width < xwc.width)
804	{
805	    xwc.width = title_width;
806	    if (tmp_win->frame_height != h ||
807	    	tmp_win->frame_width != w ||
808		tmp_win->frame_bw != bw ||
809	    	title_width != tmp_win->title_width)
810	    	reShape = TRUE;
811	}
812	else
813	{
814	    if (!tmp_win->wShaped) reShape = TRUE;
815	    title_width = xwc.width;
816	}
817    }
818
819    tmp_win->title_width = title_width;
820    if (tmp_win->title_height) tmp_win->title_height = title_height;
821
822    if (tmp_win->title_w) {
823	if (bw != tmp_win->frame_bw) {
824	    xwc.border_width = bw;
825	    tmp_win->title_x = xwc.x = -bw;
826	    tmp_win->title_y = xwc.y = -bw;
827	    xwcm |= (CWX | CWY | CWBorderWidth);
828	}
829
830	XConfigureWindow(dpy, tmp_win->title_w, xwcm, &xwc);
831    }
832
833    if (tmp_win->attr.width != w)
834	tmp_win->widthEverChangedByUser = True;
835
836    if (tmp_win->attr.height != (h - tmp_win->title_height))
837	tmp_win->heightEverChangedByUser = True;
838
839    tmp_win->attr.width = w;
840    tmp_win->attr.height = h - tmp_win->title_height;
841
842    XMoveResizeWindow (dpy, tmp_win->w, 0, tmp_win->title_height,
843		       w, h - tmp_win->title_height);
844
845    /*
846     * fix up frame and assign size/location values in tmp_win
847     */
848    frame_mask = 0;
849    if (bw != tmp_win->frame_bw) {
850	frame_wc.border_width = tmp_win->frame_bw = bw;
851	frame_mask |= CWBorderWidth;
852    }
853    frame_wc.x = tmp_win->frame_x = x;
854    frame_wc.y = tmp_win->frame_y = y;
855    frame_wc.width = tmp_win->frame_width = w;
856    frame_wc.height = tmp_win->frame_height = h;
857    frame_mask |= (CWX | CWY | CWWidth | CWHeight);
858    XConfigureWindow (dpy, tmp_win->frame, frame_mask, &frame_wc);
859
860    /*
861     * fix up highlight window
862     */
863    if (tmp_win->title_height && tmp_win->hilite_w)
864    {
865	xwc.width = (tmp_win->rightx - tmp_win->highlightx);
866	if (Scr->TBInfo.nright > 0) xwc.width -= Scr->TitlePadding;
867        if (xwc.width <= 0) {
868            xwc.x = Scr->MyDisplayWidth;	/* move offscreen */
869            xwc.width = 1;
870        } else {
871            xwc.x = tmp_win->highlightx;
872        }
873
874        xwcm = CWX | CWWidth;
875        XConfigureWindow(dpy, tmp_win->hilite_w, xwcm, &xwc);
876    }
877
878    if (HasShape && reShape) {
879	SetFrameShape (tmp_win);
880    }
881
882    if (sendEvent)
883    {
884        client_event.type = ConfigureNotify;
885        client_event.xconfigure.display = dpy;
886        client_event.xconfigure.event = tmp_win->w;
887        client_event.xconfigure.window = tmp_win->w;
888        client_event.xconfigure.x = (x + tmp_win->frame_bw - tmp_win->old_bw);
889        client_event.xconfigure.y = (y + tmp_win->frame_bw +
890				     tmp_win->title_height - tmp_win->old_bw);
891        client_event.xconfigure.width = tmp_win->frame_width;
892        client_event.xconfigure.height = tmp_win->frame_height -
893                tmp_win->title_height;
894        client_event.xconfigure.border_width = tmp_win->old_bw;
895        /* Real ConfigureNotify events say we're above title window, so ... */
896	/* what if we don't have a title ????? */
897        client_event.xconfigure.above = tmp_win->frame;
898        client_event.xconfigure.override_redirect = False;
899        XSendEvent(dpy, tmp_win->w, False, StructureNotifyMask, &client_event);
900    }
901}
902
903
904/**
905 * zooms window to full height of screen or to full height and width of screen.
906 * (Toggles so that it can undo the zoom - even when switching between fullzoom
907 * and vertical zoom.)
908 *
909 *  \param tmp_win  the TwmWindow pointer
910 */
911void
912fullzoom(TwmWindow *tmp_win, int flag)
913{
914    Window      junkRoot;
915    unsigned int junkbw, junkDepth;
916    int basex, basey;
917    int frame_bw_times_2;
918
919	XGetGeometry(dpy, (Drawable) tmp_win->frame, &junkRoot,
920	        &dragx, &dragy, (unsigned int *)&dragWidth, (unsigned int *)&dragHeight, &junkbw,
921	        &junkDepth);
922
923	basex = 0;
924	basey = 0;
925
926        if (tmp_win->zoomed == flag)
927        {
928            dragHeight = tmp_win->save_frame_height;
929            dragWidth = tmp_win->save_frame_width;
930            dragx = tmp_win->save_frame_x;
931            dragy = tmp_win->save_frame_y;
932            tmp_win->zoomed = ZOOM_NONE;
933        }
934        else
935        {
936                if (tmp_win->zoomed == ZOOM_NONE)
937                {
938                        tmp_win->save_frame_x = dragx;
939                        tmp_win->save_frame_y = dragy;
940                        tmp_win->save_frame_width = dragWidth;
941                        tmp_win->save_frame_height = dragHeight;
942                        tmp_win->zoomed = flag;
943                 }
944                  else
945                            tmp_win->zoomed = flag;
946
947
948	frame_bw_times_2 = 2*tmp_win->frame_bw;
949
950        switch (flag)
951        {
952        case ZOOM_NONE:
953            break;
954        case F_ZOOM:
955            dragHeight = Scr->MyDisplayHeight - frame_bw_times_2;
956            dragy=basey;
957            break;
958        case F_HORIZOOM:
959            dragx = basex;
960            dragWidth = Scr->MyDisplayWidth - frame_bw_times_2;
961            break;
962        case F_FULLZOOM:
963            dragx = basex;
964            dragy = basey;
965            dragHeight = Scr->MyDisplayHeight - frame_bw_times_2;
966            dragWidth = Scr->MyDisplayWidth - frame_bw_times_2;
967            break;
968        case F_LEFTZOOM:
969            dragx = basex;
970            dragy = basey;
971            dragHeight = Scr->MyDisplayHeight - frame_bw_times_2;
972            dragWidth = Scr->MyDisplayWidth/2 - frame_bw_times_2;
973            break;
974        case F_RIGHTZOOM:
975            dragx = basex + Scr->MyDisplayWidth/2;
976            dragy = basey;
977            dragHeight = Scr->MyDisplayHeight - frame_bw_times_2;
978            dragWidth = Scr->MyDisplayWidth/2 - frame_bw_times_2;
979            break;
980        case F_TOPZOOM:
981            dragx = basex;
982            dragy = basey;
983            dragHeight = Scr->MyDisplayHeight/2 - frame_bw_times_2;
984            dragWidth = Scr->MyDisplayWidth - frame_bw_times_2;
985            break;
986        case F_BOTTOMZOOM:
987            dragx = basex;
988            dragy = basey + Scr->MyDisplayHeight/2;
989            dragHeight = Scr->MyDisplayHeight/2 - frame_bw_times_2;
990            dragWidth = Scr->MyDisplayWidth - frame_bw_times_2;
991            break;
992         }
993      }
994
995    if (!Scr->NoRaiseResize)
996        XRaiseWindow(dpy, tmp_win->frame);
997
998    ConstrainSize(tmp_win, &dragWidth, &dragHeight);
999
1000    SetupWindow (tmp_win, dragx , dragy , dragWidth, dragHeight, -1);
1001    XUngrabPointer (dpy, CurrentTime);
1002    XUngrabServer (dpy);
1003}
1004
1005void
1006SetFrameShape (TwmWindow *tmp)
1007{
1008    /*
1009     * see if the titlebar needs to move
1010     */
1011    if (tmp->title_w) {
1012	int oldx = tmp->title_x, oldy = tmp->title_y;
1013	ComputeTitleLocation (tmp);
1014	if (oldx != tmp->title_x || oldy != tmp->title_y)
1015	  XMoveWindow (dpy, tmp->title_w, tmp->title_x, tmp->title_y);
1016    }
1017
1018    /*
1019     * The frame consists of the shape of the contents window offset by
1020     * title_height or'ed with the shape of title_w (which is always
1021     * rectangular).
1022     */
1023    if (tmp->wShaped) {
1024	/*
1025	 * need to do general case
1026	 */
1027	XShapeCombineShape (dpy, tmp->frame, ShapeBounding,
1028			    0, tmp->title_height, tmp->w,
1029			    ShapeBounding, ShapeSet);
1030	if (tmp->title_w) {
1031	    XShapeCombineShape (dpy, tmp->frame, ShapeBounding,
1032				tmp->title_x + tmp->frame_bw,
1033				tmp->title_y + tmp->frame_bw,
1034				tmp->title_w, ShapeBounding,
1035				ShapeUnion);
1036	}
1037    } else {
1038	/*
1039	 * can optimize rectangular contents window
1040	 */
1041	if (tmp->squeeze_info) {
1042	    XRectangle  newBounding[2];
1043	    XRectangle  newClip[2];
1044	    int fbw2 = 2 * tmp->frame_bw;
1045
1046	    /*
1047	     * Build the border clipping rectangles; one around title, one
1048	     * around window.  The title_[xy] field already have had frame_bw
1049	     * subtracted off them so that they line up properly in the frame.
1050	     *
1051	     * The frame_width and frame_height do *not* include borders.
1052	     */
1053	    /* border */
1054	    newBounding[0].x = tmp->title_x;
1055	    newBounding[0].y = tmp->title_y;
1056	    newBounding[0].width = tmp->title_width + fbw2;
1057	    newBounding[0].height = tmp->title_height;
1058	    newBounding[1].x = -tmp->frame_bw;
1059	    newBounding[1].y = Scr->TitleHeight;
1060	    newBounding[1].width = tmp->attr.width + fbw2;
1061	    newBounding[1].height = tmp->attr.height + fbw2;
1062	    XShapeCombineRectangles (dpy, tmp->frame, ShapeBounding, 0, 0,
1063				     newBounding, 2, ShapeSet, YXBanded);
1064	    /* insides */
1065	    newClip[0].x = tmp->title_x + tmp->frame_bw;
1066	    newClip[0].y = 0;
1067	    newClip[0].width = tmp->title_width;
1068	    newClip[0].height = Scr->TitleHeight;
1069	    newClip[1].x = 0;
1070	    newClip[1].y = tmp->title_height;
1071	    newClip[1].width = tmp->attr.width;
1072	    newClip[1].height = tmp->attr.height;
1073	    XShapeCombineRectangles (dpy, tmp->frame, ShapeClip, 0, 0,
1074				     newClip, 2, ShapeSet, YXBanded);
1075	} else {
1076	    (void) XShapeCombineMask (dpy, tmp->frame, ShapeBounding, 0, 0,
1077 				      None, ShapeSet);
1078	    (void) XShapeCombineMask (dpy, tmp->frame, ShapeClip, 0, 0,
1079				      None, ShapeSet);
1080	}
1081    }
1082}
1083
1084/*
1085 * Squeezed Title:
1086 *
1087 *                         tmp->title_x
1088 *                   0     |
1089 *  tmp->title_y   ........+--------------+.........  -+,- tmp->frame_bw
1090 *             0   : ......| +----------+ |....... :  -++
1091 *                 : :     | |          | |      : :   ||-Scr->TitleHeight
1092 *                 : :     | |          | |      : :   ||
1093 *                 +-------+ +----------+ +--------+  -+|-tmp->title_height
1094 *                 | +---------------------------+ |  --+
1095 *                 | |                           | |
1096 *                 | |                           | |
1097 *                 | |                           | |
1098 *                 | |                           | |
1099 *                 | |                           | |
1100 *                 | +---------------------------+ |
1101 *                 +-------------------------------+
1102 *
1103 *
1104 * Unsqueezed Title:
1105 *
1106 *                 tmp->title_x
1107 *                 | 0
1108 *  tmp->title_y   +-------------------------------+  -+,tmp->frame_bw
1109 *             0   | +---------------------------+ |  -+'
1110 *                 | |                           | |   |-Scr->TitleHeight
1111 *                 | |                           | |   |
1112 *                 + +---------------------------+ +  -+
1113 *                 |-+---------------------------+-|
1114 *                 | |                           | |
1115 *                 | |                           | |
1116 *                 | |                           | |
1117 *                 | |                           | |
1118 *                 | |                           | |
1119 *                 | +---------------------------+ |
1120 *                 +-------------------------------+
1121 *
1122 *
1123 *
1124 * Dimensions and Positions:
1125 *
1126 *     frame orgin                 (0, 0)
1127 *     frame upper left border     (-tmp->frame_bw, -tmp->frame_bw)
1128 *     frame size w/o border       tmp->frame_width , tmp->frame_height
1129 *     frame/title border width    tmp->frame_bw
1130 *     extra title height w/o bdr  tmp->title_height = TitleHeight + frame_bw
1131 *     title window height         Scr->TitleHeight
1132 *     title origin w/o border     (tmp->title_x, tmp->title_y)
1133 *     client origin               (0, Scr->TitleHeight + tmp->frame_bw)
1134 *     client size                 tmp->attr.width , tmp->attr.height
1135 *
1136 * When shaping, need to remember that the width and height of rectangles
1137 * are really deltax and deltay to lower right handle corner, so they need
1138 * to have -1 subtracted from would normally be the actual extents.
1139 */
1140