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 * window resizing borrowed from the "wm" window manager
55 *
56 * 11-Dec-87 Thomas E. LaStrange                File created
57 *
58 ***********************************************************************/
59
60#include <stdio.h>
61#include "twm.h"
62#include "parse.h"
63#include "util.h"
64#include "resize.h"
65#include "iconmgr.h"
66#include "add_window.h"
67#include "screen.h"
68#include "events.h"
69
70static void DisplaySize(TwmWindow *tmp_win, int width, int height);
71
72#define MINHEIGHT 0             /* had been 32 */
73#define MINWIDTH 0              /* had been 60 */
74
75static int dragx;               /* all these variables are used */
76static int dragy;               /* in resize operations */
77static int dragWidth;
78static int dragHeight;
79
80static int origx;
81static int origy;
82static int origWidth;
83static int origHeight;
84
85static int clampTop;
86static int clampBottom;
87static int clampLeft;
88static int clampRight;
89static int clampDX;
90static int clampDY;
91
92static int last_width;
93static int last_height;
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    }
125    else if (h >= 2) {
126        clampRight = 1;
127        clampDX = (x - dragx - dragWidth);
128    }
129
130    if (v <= 0) {
131        clampTop = 1;
132        clampDY = (y - dragy);
133    }
134    else if (v >= 2) {
135        clampBottom = 1;
136        clampDY = (y - dragy - dragHeight);
137    }
138}
139
140/**
141 * begin a window resize operation
142 *  \param ev           the event structure (button press)
143 *  \param tmp_win      the TwmWindow pointer
144 *  \param fromtitlebar action invoked from titlebar button
145 */
146void
147StartResize(XEvent *evp, TwmWindow *tmp_win, Bool fromtitlebar)
148{
149    Window junkRoot;
150    unsigned int junkbw, junkDepth;
151
152    ResizeWindow = tmp_win->frame;
153    XGrabServer(dpy);
154    XGrabPointer(dpy, Scr->Root, True,
155                 ButtonPressMask | ButtonReleaseMask |
156                 ButtonMotionMask | PointerMotionHintMask,
157                 GrabModeAsync, GrabModeAsync,
158                 Scr->Root, Scr->ResizeCursor, CurrentTime);
159
160    XGetGeometry(dpy, (Drawable) tmp_win->frame, &junkRoot,
161                 &dragx, &dragy, (unsigned int *) &dragWidth,
162                 (unsigned int *) &dragHeight, &junkbw, &junkDepth);
163    dragx += tmp_win->frame_bw;
164    dragy += tmp_win->frame_bw;
165    origx = dragx;
166    origy = dragy;
167    origWidth = dragWidth;
168    origHeight = dragHeight;
169    clampTop = clampBottom = clampLeft = clampRight = clampDX = clampDY = 0;
170
171    if (Scr->AutoRelativeResize && !fromtitlebar)
172        do_auto_clamp(tmp_win, evp);
173
174    Scr->SizeStringOffset = SIZE_HINDENT;
175    XResizeWindow(dpy, Scr->SizeWindow,
176                  (unsigned) (Scr->SizeStringWidth + SIZE_HINDENT * 2),
177                  (unsigned) (Scr->SizeFont.height + SIZE_VINDENT * 2));
178    XMapRaised(dpy, Scr->SizeWindow);
179    InstallRootColormap();
180    last_width = 0;
181    last_height = 0;
182    DisplaySize(tmp_win, origWidth, origHeight);
183    MoveOutline(Scr->Root, dragx - tmp_win->frame_bw,
184                dragy - tmp_win->frame_bw, dragWidth + 2 * tmp_win->frame_bw,
185                dragHeight + 2 * tmp_win->frame_bw,
186                tmp_win->frame_bw, tmp_win->title_height);
187}
188
189void
190MenuStartResize(TwmWindow *tmp_win, int x, int y, int w, int h)
191{
192    XGrabServer(dpy);
193    XGrabPointer(dpy, Scr->Root, True,
194                 ButtonPressMask | ButtonMotionMask | PointerMotionMask,
195                 GrabModeAsync, GrabModeAsync,
196                 Scr->Root, Scr->ResizeCursor, CurrentTime);
197    dragx = x + tmp_win->frame_bw;
198    dragy = y + tmp_win->frame_bw;
199    origx = dragx;
200    origy = dragy;
201    dragWidth = origWidth = w;  /* - 2 * tmp_win->frame_bw; */
202    dragHeight = origHeight = h;        /* - 2 * tmp_win->frame_bw; */
203    clampTop = clampBottom = clampLeft = clampRight = clampDX = clampDY = 0;
204    last_width = 0;
205    last_height = 0;
206    Scr->SizeStringOffset = SIZE_HINDENT;
207    XResizeWindow(dpy, Scr->SizeWindow,
208                  (unsigned) (Scr->SizeStringWidth + SIZE_HINDENT * 2),
209                  (unsigned) (Scr->SizeFont.height + SIZE_VINDENT * 2));
210    XMapRaised(dpy, Scr->SizeWindow);
211    DisplaySize(tmp_win, origWidth, origHeight);
212    MoveOutline(Scr->Root, dragx - tmp_win->frame_bw,
213                dragy - tmp_win->frame_bw,
214                dragWidth + 2 * tmp_win->frame_bw,
215                dragHeight + 2 * tmp_win->frame_bw,
216                tmp_win->frame_bw, tmp_win->title_height);
217}
218
219/**
220 * begin a windorew resize operation from AddWindow
221 *  \param tmp_win the TwmWindow pointer
222 */
223void
224AddStartResize(TwmWindow *tmp_win, int x, int y, int w, int h)
225{
226    XGrabServer(dpy);
227    XGrabPointer(dpy, Scr->Root, True,
228                 ButtonReleaseMask | ButtonMotionMask | PointerMotionHintMask,
229                 GrabModeAsync, GrabModeAsync,
230                 Scr->Root, Scr->ResizeCursor, CurrentTime);
231
232    dragx = x + tmp_win->frame_bw;
233    dragy = y + tmp_win->frame_bw;
234    origx = dragx;
235    origy = dragy;
236    dragWidth = origWidth = w - 2 * tmp_win->frame_bw;
237    dragHeight = origHeight = h - 2 * tmp_win->frame_bw;
238    clampTop = clampBottom = clampLeft = clampRight = clampDX = clampDY = 0;
239/*****
240    if (Scr->AutoRelativeResize) {
241        clampRight = clampBottom = 1;
242    }
243*****/
244    last_width = 0;
245    last_height = 0;
246    DisplaySize(tmp_win, origWidth, origHeight);
247}
248
249void
250MenuDoResize(int x_root, int y_root, TwmWindow *tmp_win)
251{
252    int action;
253
254    action = 0;
255
256    x_root -= clampDX;
257    y_root -= clampDY;
258
259    if (clampTop) {
260        int delta = y_root - dragy;
261
262        if (dragHeight - delta < MINHEIGHT) {
263            delta = dragHeight - MINHEIGHT;
264            clampTop = 0;
265        }
266        dragy += delta;
267        dragHeight -= delta;
268        action = 1;
269    }
270    else if (y_root <= dragy    /* ||
271                                   y_root == findRootInfo(root)->rooty */ ) {
272        dragy = y_root;
273        dragHeight = origy + origHeight - y_root;
274        clampBottom = 0;
275        clampTop = 1;
276        clampDY = 0;
277        action = 1;
278    }
279    if (clampLeft) {
280        int delta = x_root - dragx;
281
282        if (dragWidth - delta < MINWIDTH) {
283            delta = dragWidth - MINWIDTH;
284            clampLeft = 0;
285        }
286        dragx += delta;
287        dragWidth -= delta;
288        action = 1;
289    }
290    else if (x_root <= dragx    /* ||
291                                   x_root == findRootInfo(root)->rootx */ ) {
292        dragx = x_root;
293        dragWidth = origx + origWidth - x_root;
294        clampRight = 0;
295        clampLeft = 1;
296        clampDX = 0;
297        action = 1;
298    }
299    if (clampBottom) {
300        int delta = y_root - dragy - dragHeight;
301
302        if (dragHeight + delta < MINHEIGHT) {
303            delta = MINHEIGHT - dragHeight;
304            clampBottom = 0;
305        }
306        dragHeight += delta;
307        action = 1;
308    }
309    else if (y_root >= dragy + dragHeight) {
310        dragy = origy;
311        dragHeight = 1 + y_root - dragy;
312        clampTop = 0;
313        clampBottom = 1;
314        clampDY = 0;
315        action = 1;
316    }
317    if (clampRight) {
318        int delta = x_root - dragx - dragWidth;
319
320        if (dragWidth + delta < MINWIDTH) {
321            delta = MINWIDTH - dragWidth;
322            clampRight = 0;
323        }
324        dragWidth += delta;
325        action = 1;
326    }
327    else if (x_root >= dragx + dragWidth) {
328        dragx = origx;
329        dragWidth = 1 + x_root - origx;
330        clampLeft = 0;
331        clampRight = 1;
332        clampDX = 0;
333        action = 1;
334    }
335
336    if (action) {
337        ConstrainSize(tmp_win, &dragWidth, &dragHeight);
338        if (clampLeft)
339            dragx = origx + origWidth - dragWidth;
340        if (clampTop)
341            dragy = origy + origHeight - dragHeight;
342        MoveOutline(Scr->Root,
343                    dragx - tmp_win->frame_bw,
344                    dragy - tmp_win->frame_bw,
345                    dragWidth + 2 * tmp_win->frame_bw,
346                    dragHeight + 2 * tmp_win->frame_bw,
347                    tmp_win->frame_bw, tmp_win->title_height);
348    }
349
350    DisplaySize(tmp_win, dragWidth, dragHeight);
351}
352
353/**
354 * move the rubberband around.  This is called for each motion event when
355 * we are resizing
356 *
357 *  \param x_root  the X coordinate in the root window
358 *  \param y_root  the Y coordinate in the root window
359 *  \param tmp_win the current twm window
360 */
361void
362DoResize(int x_root, int y_root, TwmWindow *tmp_win)
363{
364    int action;
365
366    action = 0;
367
368    x_root -= clampDX;
369    y_root -= clampDY;
370
371    if (clampTop) {
372        int delta = y_root - dragy;
373
374        if (dragHeight - delta < MINHEIGHT) {
375            delta = dragHeight - MINHEIGHT;
376            clampTop = 0;
377        }
378        dragy += delta;
379        dragHeight -= delta;
380        action = 1;
381    }
382    else if (y_root <= dragy    /* ||
383                                   y_root == findRootInfo(root)->rooty */ ) {
384        dragy = y_root;
385        dragHeight = origy + origHeight - y_root;
386        clampBottom = 0;
387        clampTop = 1;
388        clampDY = 0;
389        action = 1;
390    }
391    if (clampLeft) {
392        int delta = x_root - dragx;
393
394        if (dragWidth - delta < MINWIDTH) {
395            delta = dragWidth - MINWIDTH;
396            clampLeft = 0;
397        }
398        dragx += delta;
399        dragWidth -= delta;
400        action = 1;
401    }
402    else if (x_root <= dragx    /* ||
403                                   x_root == findRootInfo(root)->rootx */ ) {
404        dragx = x_root;
405        dragWidth = origx + origWidth - x_root;
406        clampRight = 0;
407        clampLeft = 1;
408        clampDX = 0;
409        action = 1;
410    }
411    if (clampBottom) {
412        int delta = y_root - dragy - dragHeight;
413
414        if (dragHeight + delta < MINHEIGHT) {
415            delta = MINHEIGHT - dragHeight;
416            clampBottom = 0;
417        }
418        dragHeight += delta;
419        action = 1;
420    }
421    else if (y_root >= dragy + dragHeight - 1   /* ||
422                                                   y_root == findRootInfo(root)->rooty
423                                                   + findRootInfo(root)->rootheight - 1 */ ) {
424        dragy = origy;
425        dragHeight = 1 + y_root - dragy;
426        clampTop = 0;
427        clampBottom = 1;
428        clampDY = 0;
429        action = 1;
430    }
431    if (clampRight) {
432        int delta = x_root - dragx - dragWidth;
433
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 */
476static void
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) &&
497        tmp_win->hints.flags & PResizeInc) {
498        if (tmp_win->hints.flags & PBaseSize) {
499            dwidth -= tmp_win->hints.base_width;
500            dheight -= tmp_win->hints.base_height;
501        }
502        else {
503            dwidth -= tmp_win->hints.min_width;
504            dheight -= tmp_win->hints.min_height;
505        }
506    }
507
508    if (tmp_win->hints.flags & PResizeInc) {
509        dwidth /= tmp_win->hints.width_inc;
510        dheight /= tmp_win->hints.height_inc;
511    }
512
513    (void) snprintf(str, sizeof(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, str, 13);
519}
520
521/**
522 * finish the resize operation
523 */
524void
525EndResize(void)
526{
527    TwmWindow *tmp_win;
528
529#ifdef DEBUG
530    fprintf(stderr, "EndResize\n");
531#endif
532
533    MoveOutline(Scr->Root, 0, 0, 0, 0, 0, 0);
534    XUnmapWindow(dpy, Scr->SizeWindow);
535
536    XFindContext(dpy, ResizeWindow, TwmContext, (XPointer *) &tmp_win);
537
538    ConstrainSize(tmp_win, &dragWidth, &dragHeight);
539
540    if (dragWidth != tmp_win->frame_width ||
541        dragHeight != tmp_win->frame_height)
542        tmp_win->zoomed = ZOOM_NONE;
543
544    SetupWindow(tmp_win, dragx - tmp_win->frame_bw, dragy - tmp_win->frame_bw,
545                dragWidth, dragHeight, -1);
546
547    if (tmp_win->iconmgr) {
548        int ncols = tmp_win->iconmgrp->cur_columns;
549
550        if (ncols == 0)
551            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 * finish the resize operation for AddWindo<w
582 */
583void
584AddEndResize(TwmWindow *tmp_win)
585{
586
587#ifdef DEBUG
588    fprintf(stderr, "AddEndResize\n");
589#endif
590
591    ConstrainSize(tmp_win, &dragWidth, &dragHeight);
592    AddingX = dragx - tmp_win->frame_bw;
593    AddingY = dragy - tmp_win->frame_bw;
594    AddingW = dragWidth + (2 * tmp_win->frame_bw);
595    AddingH = dragHeight + (2 * tmp_win->frame_bw);
596}
597
598/**
599 * adjust the given width and height to account for the constraints imposed
600 * by size hints.
601 *
602 *      The general algorithm, especially the aspect ratio stuff, is
603 *      borrowed from uwm's CheckConsistency routine.
604 */
605void
606ConstrainSize(TwmWindow *tmp_win, int *widthp, int *heightp)
607{
608#define makemult(a,b) ((b==1) ? (a) : (((int)((a)/(b))) * (b)) )
609#define _min(a,b) (((a) < (b)) ? (a) : (b))
610
611    int minWidth, minHeight, maxWidth, maxHeight, xinc, yinc;
612    int baseWidth, baseHeight;
613    int dwidth = *widthp, dheight = *heightp;
614
615    dheight -= tmp_win->title_height;
616
617    if (tmp_win->hints.flags & PMinSize) {
618        minWidth = tmp_win->hints.min_width;
619        minHeight = tmp_win->hints.min_height;
620    }
621    else if (tmp_win->hints.flags & PBaseSize) {
622        minWidth = tmp_win->hints.base_width;
623        minHeight = tmp_win->hints.base_height;
624    }
625    else
626        minWidth = minHeight = 1;
627
628    if (tmp_win->hints.flags & PBaseSize) {
629        baseWidth = tmp_win->hints.base_width;
630        baseHeight = tmp_win->hints.base_height;
631    }
632    else if (tmp_win->hints.flags & PMinSize) {
633        baseWidth = tmp_win->hints.min_width;
634        baseHeight = tmp_win->hints.min_height;
635    }
636    else
637        baseWidth = baseHeight = 0;
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    }
643    else {
644        maxWidth = Scr->MaxWindowWidth;
645        maxHeight = Scr->MaxWindowHeight;
646    }
647
648    if (tmp_win->hints.flags & PResizeInc) {
649        xinc = tmp_win->hints.width_inc;
650        yinc = tmp_win->hints.height_inc;
651    }
652    else
653        xinc = yinc = 1;
654
655    /*
656     * First, clamp to min and max values
657     */
658    if (dwidth < minWidth)
659        dwidth = minWidth;
660    if (dheight < minHeight)
661        dheight = minHeight;
662
663    if (dwidth > maxWidth)
664        dwidth = maxWidth;
665    if (dheight > maxHeight)
666        dheight = maxHeight;
667
668    /*
669     * Second, fit to base + N * inc
670     */
671    dwidth = ((dwidth - baseWidth) / xinc * xinc) + baseWidth;
672    dheight = ((dheight - baseHeight) / yinc * yinc) + baseHeight;
673
674    /*
675     * Third, adjust for aspect ratio
676     */
677#define maxAspectX tmp_win->hints.max_aspect.x
678#define maxAspectY tmp_win->hints.max_aspect.y
679#define minAspectX tmp_win->hints.min_aspect.x
680#define minAspectY tmp_win->hints.min_aspect.y
681    /*
682     * The math looks like this:
683     *
684     * minAspectX    dwidth     maxAspectX
685     * ---------- <= ------- <= ----------
686     * minAspectY    dheight    maxAspectY
687     *
688     * If that is multiplied out, then the width and height are
689     * invalid in the following situations:
690     *
691     * minAspectX * dheight > minAspectY * dwidth
692     * maxAspectX * dheight < maxAspectY * dwidth
693     *
694     */
695
696    if (tmp_win->hints.flags & PAspect) {
697        int delta;
698
699        if (minAspectX * dheight > minAspectY * dwidth) {
700            delta = makemult(minAspectX * dheight / minAspectY - dwidth, xinc);
701            if (dwidth + delta <= maxWidth)
702                dwidth += delta;
703            else {
704                delta = makemult(dheight - dwidth * minAspectY / minAspectX,
705                                 yinc);
706                if (dheight - delta >= minHeight)
707                    dheight -= delta;
708            }
709        }
710
711        if (maxAspectX * dheight < maxAspectY * dwidth) {
712            delta = makemult(dwidth * maxAspectY / maxAspectX - dheight, yinc);
713            if (dheight + delta <= maxHeight)
714                dheight += delta;
715            else {
716                delta = makemult(dwidth - maxAspectX * dheight / maxAspectY,
717                                 xinc);
718                if (dwidth - delta >= minWidth)
719                    dwidth -= delta;
720            }
721        }
722    }
723
724    /*
725     * Fourth, account for border width and title height
726     */
727    *widthp = dwidth;
728    *heightp = dheight + tmp_win->title_height;
729}
730
731/**
732 * set window sizes, this was called from either AddWindow, EndResize, or
733 * HandleConfigureNotify.
734 *
735 *  Special Considerations:
736 * This routine will check to make sure the window is not completely off the
737 * display, if it is, it'll bring some of it back on.
738 *
739 * The tmp_win->frame_XXX variables should NOT be updated with the values of
740 * x,y,w,h prior to calling this routine, since the new values are compared
741 * against the old to see whether a synthetic ConfigureNotify event should be
742 * sent.  (It should be sent if the window was moved but not resized.)
743 *
744 *  \param tmp_win the TwmWindow pointer
745 *  \param x       the x coordinate of the upper-left outer corner of the frame
746 *  \param y       the y coordinate of the upper-left outer corner of the frame
747 *  \param w       the width of the frame window w/o border
748 *  \param h       the height of the frame window w/o border
749 *  \param bw      the border width of the frame window or -1 not to change
750 */
751void
752SetupWindow(TwmWindow *tmp_win, int x, int y, int w, int h, int bw)
753{
754    SetupFrame(tmp_win, x, y, w, h, bw, False);
755}
756
757/**
758 *  \param sendEvent whether or not to force a send
759 */
760void
761SetupFrame(TwmWindow *tmp_win, int x, int y, int w, int h, int bw,
762           Bool sendEvent)
763{
764    XEvent client_event;
765    XWindowChanges frame_wc, xwc;
766    unsigned long frame_mask, xwcm;
767    int title_width, title_height;
768    int reShape;
769
770#ifdef DEBUG
771    fprintf(stderr, "SetupWindow: x=%d, y=%d, w=%d, h=%d, bw=%d\n",
772            x, y, w, h, bw);
773#endif
774
775    if (x >= Scr->MyDisplayWidth)
776        x = Scr->MyDisplayWidth - 16;   /* one "average" cursor width */
777    if (y >= Scr->MyDisplayHeight)
778        y = Scr->MyDisplayHeight - 16;  /* one "average" cursor width */
779    if (bw < 0)
780        bw = tmp_win->frame_bw; /* -1 means current frame width */
781
782    if (tmp_win->iconmgr) {
783        tmp_win->iconmgrp->width = w;
784        h = tmp_win->iconmgrp->height + tmp_win->title_height;
785    }
786
787    /*
788     * According to the July 27, 1988 ICCCM draft, we should send a
789     * "synthetic" ConfigureNotify event to the client if the window
790     * was moved but not resized.
791     */
792    if (((x != tmp_win->frame_x || y != tmp_win->frame_y) &&
793         (w == tmp_win->frame_width && h == tmp_win->frame_height)) ||
794        (bw != tmp_win->frame_bw))
795        sendEvent = TRUE;
796
797    xwcm = CWWidth;
798    title_width = xwc.width = w;
799    title_height = Scr->TitleHeight + bw;
800
801    ComputeWindowTitleOffsets(tmp_win, xwc.width, True);
802
803    reShape = (tmp_win->wShaped ? TRUE : FALSE);
804    if (tmp_win->squeeze_info) {        /* check for title shaping */
805        title_width = tmp_win->rightx + Scr->TBInfo.rightoff;
806        if (title_width < xwc.width) {
807            xwc.width = title_width;
808            if (tmp_win->frame_height != h ||
809                tmp_win->frame_width != w ||
810                tmp_win->frame_bw != bw || title_width != tmp_win->title_width)
811                reShape = TRUE;
812        }
813        else {
814            if (!tmp_win->wShaped)
815                reShape = TRUE;
816            title_width = xwc.width;
817        }
818    }
819
820    tmp_win->title_width = title_width;
821    if (tmp_win->title_height)
822        tmp_win->title_height = title_height;
823
824    if (tmp_win->title_w) {
825        if (bw != tmp_win->frame_bw) {
826            xwc.border_width = bw;
827            tmp_win->title_x = xwc.x = -bw;
828            tmp_win->title_y = xwc.y = -bw;
829            xwcm |= (CWX | CWY | CWBorderWidth);
830        }
831
832        XConfigureWindow(dpy, tmp_win->title_w, (unsigned) xwcm, &xwc);
833    }
834
835    if (tmp_win->attr.width != w)
836        tmp_win->widthEverChangedByUser = True;
837
838    if (tmp_win->attr.height != (h - tmp_win->title_height))
839        tmp_win->heightEverChangedByUser = True;
840
841    tmp_win->attr.width = w;
842    tmp_win->attr.height = h - tmp_win->title_height;
843
844    XMoveResizeWindow(dpy, tmp_win->w, 0, tmp_win->title_height,
845                      (unsigned) w, (unsigned) (h - tmp_win->title_height));
846
847    /*
848     * fix up frame and assign size/location values in tmp_win
849     */
850    frame_mask = 0;
851    if (bw != tmp_win->frame_bw) {
852        frame_wc.border_width = tmp_win->frame_bw = bw;
853        frame_mask |= CWBorderWidth;
854    }
855    frame_wc.x = tmp_win->frame_x = x;
856    frame_wc.y = tmp_win->frame_y = y;
857    frame_wc.width = tmp_win->frame_width = w;
858    frame_wc.height = tmp_win->frame_height = h;
859    frame_mask |= (CWX | CWY | CWWidth | CWHeight);
860    XConfigureWindow(dpy, tmp_win->frame, (unsigned) frame_mask, &frame_wc);
861
862    /*
863     * fix up highlight window
864     */
865    if (tmp_win->title_height && tmp_win->hilite_w) {
866        xwc.width = (tmp_win->rightx - tmp_win->highlightx);
867        if (Scr->TBInfo.nright > 0)
868            xwc.width -= Scr->TitlePadding;
869        if (xwc.width <= 0) {
870            xwc.x = Scr->MyDisplayWidth;        /* move offscreen */
871            xwc.width = 1;
872        }
873        else {
874            xwc.x = tmp_win->highlightx;
875        }
876
877        xwcm = CWX | CWWidth;
878        XConfigureWindow(dpy, tmp_win->hilite_w, (unsigned) xwcm, &xwc);
879    }
880
881    if (HasShape && reShape) {
882        SetFrameShape(tmp_win);
883    }
884
885    if (sendEvent) {
886        client_event.type = ConfigureNotify;
887        client_event.xconfigure.display = dpy;
888
889        client_event.xconfigure.event = tmp_win->w;
890        client_event.xconfigure.window = tmp_win->w;
891        client_event.xconfigure.x = (x + tmp_win->frame_bw - tmp_win->old_bw);
892        client_event.xconfigure.y = (y + tmp_win->frame_bw +
893                                     tmp_win->title_height - tmp_win->old_bw);
894        client_event.xconfigure.width = tmp_win->frame_width;
895        client_event.xconfigure.height = tmp_win->frame_height -
896            tmp_win->title_height;
897        client_event.xconfigure.border_width = tmp_win->old_bw;
898        /* Real ConfigureNotify events say we're above title window, so ... */
899        /* what if we don't have a title ????? */
900        client_event.xconfigure.above = tmp_win->frame;
901        client_event.xconfigure.override_redirect = False;
902        XSendEvent(dpy, tmp_win->w, False, StructureNotifyMask, &client_event);
903    }
904}
905
906/**
907 * zooms window to full height of screen or to full height and width of screen.
908 * (Toggles so that it can undo the zoom - even when switching between fullzoom
909 * and vertical zoom.)
910 *
911 *  \param tmp_win  the TwmWindow pointer
912 */
913void
914fullzoom(TwmWindow *tmp_win, int flag)
915{
916    Window junkRoot;
917    unsigned int junkbw, junkDepth;
918    int basex, basey;
919
920    XGetGeometry(dpy, (Drawable) tmp_win->frame, &junkRoot,
921                 &dragx, &dragy, (unsigned int *) &dragWidth,
922                 (unsigned int *) &dragHeight, &junkbw, &junkDepth);
923
924    basex = 0;
925    basey = 0;
926
927    if (tmp_win->zoomed == flag) {
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        int frame_bw_times_2;
936
937        if (tmp_win->zoomed == ZOOM_NONE) {
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 = (short) flag;
943        }
944        else
945            tmp_win->zoomed = (short) flag;
946
947        frame_bw_times_2 = 2 * tmp_win->frame_bw;
948
949        switch (flag) {
950        case ZOOM_NONE:
951            break;
952        case F_ZOOM:
953            dragHeight = Scr->MyDisplayHeight - frame_bw_times_2;
954            dragy = basey;
955            break;
956        case F_HORIZOOM:
957            dragx = basex;
958            dragWidth = Scr->MyDisplayWidth - frame_bw_times_2;
959            break;
960        case F_FULLZOOM:
961            dragx = basex;
962            dragy = basey;
963            dragHeight = Scr->MyDisplayHeight - frame_bw_times_2;
964            dragWidth = Scr->MyDisplayWidth - frame_bw_times_2;
965            break;
966        case F_LEFTZOOM:
967            dragx = basex;
968            dragy = basey;
969            dragHeight = Scr->MyDisplayHeight - frame_bw_times_2;
970            dragWidth = Scr->MyDisplayWidth / 2 - frame_bw_times_2;
971            break;
972        case F_RIGHTZOOM:
973            dragx = basex + Scr->MyDisplayWidth / 2;
974            dragy = basey;
975            dragHeight = Scr->MyDisplayHeight - frame_bw_times_2;
976            dragWidth = Scr->MyDisplayWidth / 2 - frame_bw_times_2;
977            break;
978        case F_TOPZOOM:
979            dragx = basex;
980            dragy = basey;
981            dragHeight = Scr->MyDisplayHeight / 2 - frame_bw_times_2;
982            dragWidth = Scr->MyDisplayWidth - frame_bw_times_2;
983            break;
984        case F_BOTTOMZOOM:
985            dragx = basex;
986            dragy = basey + Scr->MyDisplayHeight / 2;
987            dragHeight = Scr->MyDisplayHeight / 2 - frame_bw_times_2;
988            dragWidth = Scr->MyDisplayWidth - frame_bw_times_2;
989            break;
990        }
991    }
992
993    if (!Scr->NoRaiseResize)
994        XRaiseWindow(dpy, tmp_win->frame);
995
996    ConstrainSize(tmp_win, &dragWidth, &dragHeight);
997
998    SetupWindow(tmp_win, dragx, dragy, dragWidth, dragHeight, -1);
999    XUngrabPointer(dpy, CurrentTime);
1000    XUngrabServer(dpy);
1001}
1002
1003void
1004SetFrameShape(TwmWindow *tmp)
1005{
1006    /*
1007     * see if the titlebar needs to move
1008     */
1009    if (tmp->title_w) {
1010        int oldx = tmp->title_x, oldy = tmp->title_y;
1011
1012        ComputeTitleLocation(tmp);
1013        if (oldx != tmp->title_x || oldy != tmp->title_y)
1014            XMoveWindow(dpy, tmp->title_w, tmp->title_x, tmp->title_y);
1015    }
1016
1017    /*
1018     * The frame consists of the shape of the contents window offset by
1019     * title_height or'ed with the shape of title_w (which is always
1020     * rectangular).
1021     */
1022    if (tmp->wShaped) {
1023        /*
1024         * need to do general case
1025         */
1026        XShapeCombineShape(dpy, tmp->frame, ShapeBounding,
1027                           0, tmp->title_height, tmp->w,
1028                           ShapeBounding, ShapeSet);
1029        if (tmp->title_w) {
1030            XShapeCombineShape(dpy, tmp->frame, ShapeBounding,
1031                               tmp->title_x + tmp->frame_bw,
1032                               tmp->title_y + tmp->frame_bw,
1033                               tmp->title_w, ShapeBounding, ShapeUnion);
1034        }
1035    }
1036    else {
1037        /*
1038         * can optimize rectangular contents window
1039         */
1040        if (tmp->squeeze_info) {
1041            XRectangle newBounding[2];
1042            XRectangle newClip[2];
1043            int fbw2 = 2 * tmp->frame_bw;
1044
1045            /*
1046             * Build the border clipping rectangles; one around title, one
1047             * around window.  The title_[xy] field already have had frame_bw
1048             * subtracted off them so that they line up properly in the frame.
1049             *
1050             * The frame_width and frame_height do *not* include borders.
1051             */
1052            /* border */
1053            newBounding[0].x = (short) tmp->title_x;
1054            newBounding[0].y = (short) tmp->title_y;
1055            newBounding[0].width = (unsigned short) (tmp->title_width + fbw2);
1056            newBounding[0].height = (unsigned short) tmp->title_height;
1057            newBounding[1].x = (short) -tmp->frame_bw;
1058            newBounding[1].y = (short) Scr->TitleHeight;
1059            newBounding[1].width = (unsigned short) (tmp->attr.width + fbw2);
1060            newBounding[1].height = (unsigned short) (tmp->attr.height + fbw2);
1061            XShapeCombineRectangles(dpy, tmp->frame, ShapeBounding, 0, 0,
1062                                    newBounding, 2, ShapeSet, YXBanded);
1063            /* insides */
1064            newClip[0].x = (short) (tmp->title_x + tmp->frame_bw);
1065            newClip[0].y = 0;
1066            newClip[0].width = (unsigned short) tmp->title_width;
1067            newClip[0].height = (unsigned short) Scr->TitleHeight;
1068            newClip[1].x = 0;
1069            newClip[1].y = (short) tmp->title_height;
1070            newClip[1].width = (unsigned short) tmp->attr.width;
1071            newClip[1].height = (unsigned short) tmp->attr.height;
1072            XShapeCombineRectangles(dpy, tmp->frame, ShapeClip, 0, 0,
1073                                    newClip, 2, ShapeSet, YXBanded);
1074        }
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 origin                (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