1/*
2 * Calculate window clip lists for rootless mode
3 *
4 * This file is very closely based on mivaltree.c.
5 */
6
7/*
8 * mivaltree.c --
9 *	Functions for recalculating window clip lists. Main function
10 *	is miValidateTree.
11 *
12
13Copyright 1987, 1988, 1989, 1998  The Open Group
14
15All Rights Reserved.
16
17The above copyright notice and this permission notice shall be included in
18all copies or substantial portions of the Software.
19
20THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
23OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
24AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
25CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26
27Except as contained in this notice, the name of The Open Group shall not be
28used in advertising or otherwise to promote the sale, use or other dealings
29in this Software without prior written authorization from The Open Group.
30
31 *
32 * Copyright 1987, 1988, 1989 by
33 * Digital Equipment Corporation, Maynard, Massachusetts,
34 *
35 *                         All Rights Reserved
36 *
37 * Permission to use, copy, modify, and distribute this software and its
38 * documentation for any purpose and without fee is hereby granted,
39 * provided that the above copyright notice appear in all copies and that
40 * both that copyright notice and this permission notice appear in
41 * supporting documentation, and that the name of Digital not be
42 * used in advertising or publicity pertaining to distribution of the
43 * software without specific, written prior permission.
44 *
45 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
46 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
47 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
48 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
49 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
50 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
51 * SOFTWARE.
52 *
53 ******************************************************************/
54
55/* The panoramix components contained the following notice */
56/*****************************************************************
57
58Copyright (c) 1991, 1997 Digital Equipment Corporation, Maynard, Massachusetts.
59
60Permission is hereby granted, free of charge, to any person obtaining a copy
61of this software and associated documentation files (the "Software"), to deal
62in the Software without restriction, including without limitation the rights
63to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
64copies of the Software.
65
66The above copyright notice and this permission notice shall be included in
67all copies or substantial portions of the Software.
68
69THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
70IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
71FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
72DIGITAL EQUIPMENT CORPORATION BE LIABLE FOR ANY CLAIM, DAMAGES, INCLUDING,
73BUT NOT LIMITED TO CONSEQUENTIAL OR INCIDENTAL DAMAGES, OR OTHER LIABILITY,
74WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
75IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
76
77Except as contained in this notice, the name of Digital Equipment Corporation
78shall not be used in advertising or otherwise to promote the sale, use or other
79dealings in this Software without prior written authorization from Digital
80Equipment Corporation.
81
82******************************************************************/
83 /*
84  * Aug '86: Susan Angebranndt -- original code
85  * July '87: Adam de Boor -- substantially modified and commented
86  * Summer '89: Joel McCormack -- so fast you wouldn't believe it possible.
87  *             In particular, much improved code for window mapping and
88  *             circulating.
89  *             Bob Scheifler -- avoid miComputeClips for unmapped windows,
90  *                              valdata changes
91  */
92#ifdef HAVE_DIX_CONFIG_H
93#include <dix-config.h>
94#endif
95
96#include <stddef.h>             /* For NULL */
97#include    <X11/X.h>
98#include    "scrnintstr.h"
99#include    "validate.h"
100#include    "windowstr.h"
101#include    "mi.h"
102#include    "regionstr.h"
103#include    "mivalidate.h"
104
105#include    "globals.h"
106
107int RootlessMiValidateTree(WindowPtr pRoot, WindowPtr pChild, VTKind kind);
108
109#define HasParentRelativeBorder(w) (!(w)->borderIsPixel && \
110				    HasBorder(w) && \
111				    (w)->backgroundState == ParentRelative)
112
113/*
114 *-----------------------------------------------------------------------
115 * RootlessComputeClips --
116 *	Recompute the clipList, borderClip, exposed and borderExposed
117 *	regions for pParent and its children. Only viewable windows are
118 *	taken into account.
119 *
120 * Results:
121 *	None.
122 *
123 * Side Effects:
124 *	clipList, borderClip, exposed and borderExposed are altered.
125 *	A VisibilityNotify event may be generated on the parent window.
126 *
127 *-----------------------------------------------------------------------
128 */
129static void
130RootlessComputeClips(WindowPtr pParent, ScreenPtr pScreen,
131                     RegionPtr universe, VTKind kind, RegionPtr exposed)
132{
133    int dx, dy;
134    RegionRec childUniverse;
135    register WindowPtr pChild;
136    int oldVis, newVis;
137    BoxRec borderSize;
138    RegionRec childUnion;
139    Bool overlap;
140    RegionPtr borderVisible;
141
142    /*
143     * Figure out the new visibility of this window.
144     * The extent of the universe should be the same as the extent of
145     * the borderSize region. If the window is unobscured, this rectangle
146     * will be completely inside the universe (the universe will cover it
147     * completely). If the window is completely obscured, none of the
148     * universe will cover the rectangle.
149     */
150    borderSize.x1 = pParent->drawable.x - wBorderWidth(pParent);
151    borderSize.y1 = pParent->drawable.y - wBorderWidth(pParent);
152    dx = (int) pParent->drawable.x + (int) pParent->drawable.width +
153        wBorderWidth(pParent);
154    if (dx > 32767)
155        dx = 32767;
156    borderSize.x2 = dx;
157    dy = (int) pParent->drawable.y + (int) pParent->drawable.height +
158        wBorderWidth(pParent);
159    if (dy > 32767)
160        dy = 32767;
161    borderSize.y2 = dy;
162
163    oldVis = pParent->visibility;
164    switch (RegionContainsRect(universe, &borderSize)) {
165    case rgnIN:
166        newVis = VisibilityUnobscured;
167        break;
168    case rgnPART:
169        newVis = VisibilityPartiallyObscured;
170        {
171            RegionPtr pBounding;
172
173            if ((pBounding = wBoundingShape(pParent))) {
174                switch (miShapedWindowIn(universe, pBounding, &borderSize,
175                                         pParent->drawable.x,
176                                         pParent->drawable.y)) {
177                case rgnIN:
178                    newVis = VisibilityUnobscured;
179                    break;
180                case rgnOUT:
181                    newVis = VisibilityFullyObscured;
182                    break;
183                }
184            }
185        }
186        break;
187    default:
188        newVis = VisibilityFullyObscured;
189        break;
190    }
191
192    pParent->visibility = newVis;
193    if (oldVis != newVis &&
194        ((pParent->
195          eventMask | wOtherEventMasks(pParent)) & VisibilityChangeMask))
196        SendVisibilityNotify(pParent);
197
198    dx = pParent->drawable.x - pParent->valdata->before.oldAbsCorner.x;
199    dy = pParent->drawable.y - pParent->valdata->before.oldAbsCorner.y;
200
201    /*
202     * avoid computations when dealing with simple operations
203     */
204
205    switch (kind) {
206    case VTMap:
207    case VTStack:
208    case VTUnmap:
209        break;
210    case VTMove:
211        if ((oldVis == newVis) &&
212            ((oldVis == VisibilityFullyObscured) ||
213             (oldVis == VisibilityUnobscured))) {
214            pChild = pParent;
215            while (1) {
216                if (pChild->viewable) {
217                    if (pChild->visibility != VisibilityFullyObscured) {
218                        RegionTranslate(&pChild->borderClip, dx, dy);
219                        RegionTranslate(&pChild->clipList, dx, dy);
220                        pChild->drawable.serialNumber = NEXT_SERIAL_NUMBER;
221                        if (pScreen->ClipNotify)
222                            (*pScreen->ClipNotify) (pChild, dx, dy);
223
224                    }
225                    if (pChild->valdata) {
226                        RegionNull(&pChild->valdata->after.borderExposed);
227                        if (HasParentRelativeBorder(pChild)) {
228                            RegionSubtract(&pChild->valdata->after.
229                                           borderExposed, &pChild->borderClip,
230                                           &pChild->winSize);
231                        }
232                        RegionNull(&pChild->valdata->after.exposed);
233                    }
234                    if (pChild->firstChild) {
235                        pChild = pChild->firstChild;
236                        continue;
237                    }
238                }
239                while (!pChild->nextSib && (pChild != pParent))
240                    pChild = pChild->parent;
241                if (pChild == pParent)
242                    break;
243                pChild = pChild->nextSib;
244            }
245            return;
246        }
247        /* fall through */
248    default:
249        /*
250         * To calculate exposures correctly, we have to translate the old
251         * borderClip and clipList regions to the window's new location so there
252         * is a correspondence between pieces of the new and old clipping regions.
253         */
254        if (dx || dy) {
255            /*
256             * We translate the old clipList because that will be exposed or copied
257             * if gravity is right.
258             */
259            RegionTranslate(&pParent->borderClip, dx, dy);
260            RegionTranslate(&pParent->clipList, dx, dy);
261        }
262        break;
263    case VTBroken:
264        RegionEmpty(&pParent->borderClip);
265        RegionEmpty(&pParent->clipList);
266        break;
267    }
268
269    borderVisible = pParent->valdata->before.borderVisible;
270    RegionNull(&pParent->valdata->after.borderExposed);
271    RegionNull(&pParent->valdata->after.exposed);
272
273    /*
274     * Since the borderClip must not be clipped by the children, we do
275     * the border exposure first...
276     *
277     * 'universe' is the window's borderClip. To figure the exposures, remove
278     * the area that used to be exposed from the new.
279     * This leaves a region of pieces that weren't exposed before.
280     */
281
282    if (HasBorder(pParent)) {
283        if (borderVisible) {
284            /*
285             * when the border changes shape, the old visible portions
286             * of the border will be saved by DIX in borderVisible --
287             * use that region and destroy it
288             */
289            RegionSubtract(exposed, universe, borderVisible);
290            RegionDestroy(borderVisible);
291        }
292        else {
293            RegionSubtract(exposed, universe, &pParent->borderClip);
294        }
295        if (HasParentRelativeBorder(pParent) && (dx || dy)) {
296            RegionSubtract(&pParent->valdata->after.borderExposed,
297                           universe, &pParent->winSize);
298        }
299        else {
300            RegionSubtract(&pParent->valdata->after.borderExposed,
301                           exposed, &pParent->winSize);
302        }
303
304        RegionCopy(&pParent->borderClip, universe);
305
306        /*
307         * To get the right clipList for the parent, and to make doubly sure
308         * that no child overlaps the parent's border, we remove the parent's
309         * border from the universe before proceeding.
310         */
311
312        RegionIntersect(universe, universe, &pParent->winSize);
313    }
314    else
315        RegionCopy(&pParent->borderClip, universe);
316
317    if ((pChild = pParent->firstChild) && pParent->mapped) {
318        RegionNull(&childUniverse);
319        RegionNull(&childUnion);
320        if ((pChild->drawable.y < pParent->lastChild->drawable.y) ||
321            ((pChild->drawable.y == pParent->lastChild->drawable.y) &&
322             (pChild->drawable.x < pParent->lastChild->drawable.x))) {
323            for (; pChild; pChild = pChild->nextSib) {
324                if (pChild->viewable)
325                    RegionAppend(&childUnion, &pChild->borderSize);
326            }
327        }
328        else {
329            for (pChild = pParent->lastChild; pChild; pChild = pChild->prevSib) {
330                if (pChild->viewable)
331                    RegionAppend(&childUnion, &pChild->borderSize);
332            }
333        }
334        RegionValidate(&childUnion, &overlap);
335
336        for (pChild = pParent->firstChild; pChild; pChild = pChild->nextSib) {
337            if (pChild->viewable) {
338                /*
339                 * If the child is viewable, we want to remove its extents
340                 * from the current universe, but we only re-clip it if
341                 * it's been marked.
342                 */
343                if (pChild->valdata) {
344                    /*
345                     * Figure out the new universe from the child's
346                     * perspective and recurse.
347                     */
348                    RegionIntersect(&childUniverse,
349                                    universe, &pChild->borderSize);
350                    RootlessComputeClips(pChild, pScreen, &childUniverse,
351                                         kind, exposed);
352                }
353                /*
354                 * Once the child has been processed, we remove its extents
355                 * from the current universe, thus denying its space to any
356                 * other sibling.
357                 */
358                if (overlap)
359                    RegionSubtract(universe, universe, &pChild->borderSize);
360            }
361        }
362        if (!overlap)
363            RegionSubtract(universe, universe, &childUnion);
364        RegionUninit(&childUnion);
365        RegionUninit(&childUniverse);
366    }                           /* if any children */
367
368    /*
369     * 'universe' now contains the new clipList for the parent window.
370     *
371     * To figure the exposure of the window we subtract the old clip from the
372     * new, just as for the border.
373     */
374
375    if (oldVis == VisibilityFullyObscured || oldVis == VisibilityNotViewable) {
376        RegionCopy(&pParent->valdata->after.exposed, universe);
377    }
378    else if (newVis != VisibilityFullyObscured &&
379             newVis != VisibilityNotViewable) {
380        RegionSubtract(&pParent->valdata->after.exposed,
381                       universe, &pParent->clipList);
382    }
383
384    /* HACK ALERT - copying contents of regions, instead of regions */
385    {
386        RegionRec tmp;
387
388        tmp = pParent->clipList;
389        pParent->clipList = *universe;
390        *universe = tmp;
391    }
392
393#ifdef NOTDEF
394    RegionCopy(&pParent->clipList, universe);
395#endif
396
397    pParent->drawable.serialNumber = NEXT_SERIAL_NUMBER;
398
399    if (pScreen->ClipNotify)
400        (*pScreen->ClipNotify) (pParent, dx, dy);
401}
402
403static void
404RootlessTreeObscured(WindowPtr pParent)
405{
406    register WindowPtr pChild;
407    register int oldVis;
408
409    pChild = pParent;
410    while (1) {
411        if (pChild->viewable) {
412            oldVis = pChild->visibility;
413            if (oldVis != (pChild->visibility = VisibilityFullyObscured) &&
414                ((pChild->
415                  eventMask | wOtherEventMasks(pChild)) & VisibilityChangeMask))
416                SendVisibilityNotify(pChild);
417            if (pChild->firstChild) {
418                pChild = pChild->firstChild;
419                continue;
420            }
421        }
422        while (!pChild->nextSib && (pChild != pParent))
423            pChild = pChild->parent;
424        if (pChild == pParent)
425            break;
426        pChild = pChild->nextSib;
427    }
428}
429
430/*
431 *-----------------------------------------------------------------------
432 * RootlessMiValidateTree --
433 *	Recomputes the clip list for pParent and all its inferiors.
434 *
435 * Results:
436 *	Always returns 1.
437 *
438 * Side Effects:
439 *	The clipList, borderClip, exposed, and borderExposed regions for
440 *	each marked window are altered.
441 *
442 * Notes:
443 *	This routine assumes that all affected windows have been marked
444 *	(valdata created) and their winSize and borderSize regions
445 *	adjusted to correspond to their new positions. The borderClip and
446 *	clipList regions should not have been touched.
447 *
448 *	The top-most level is treated differently from all lower levels
449 *	because pParent is unchanged. For the top level, we merge the
450 *	regions taken up by the marked children back into the clipList
451 *	for pParent, thus forming a region from which the marked children
452 *	can claim their areas. For lower levels, where the old clipList
453 *	and borderClip are invalid, we can't do this and have to do the
454 *	extra operations done in miComputeClips, but this is much faster
455 *	e.g. when only one child has moved...
456 *
457 *-----------------------------------------------------------------------
458 */
459/*
460   Quartz version: used for validate from root in rootless mode.
461   We need to make sure top-level windows don't clip each other,
462   and that top-level windows aren't clipped to the root window.
463*/
464 /*ARGSUSED*/
465// fixme this is ugly
466// Xprint/ValTree.c doesn't work, but maybe that method can?
467    int
468RootlessMiValidateTree(WindowPtr pRoot, /* Parent to validate */
469                       WindowPtr pChild,        /* First child of pRoot that was
470                                                 * affected */
471                       VTKind kind /* What kind of configuration caused call */
472                       )
473{
474    RegionRec childClip;        /* The new borderClip for the current
475                                 * child */
476    RegionRec exposed;          /* For intermediate calculations */
477    register ScreenPtr pScreen;
478    register WindowPtr pWin;
479
480    pScreen = pRoot->drawable.pScreen;
481    if (pChild == NullWindow)
482        pChild = pRoot->firstChild;
483
484    RegionNull(&childClip);
485    RegionNull(&exposed);
486
487    if (RegionBroken(&pRoot->clipList) && !RegionBroken(&pRoot->borderClip)) {
488        // fixme this might not work, but hopefully doesn't happen anyway.
489        kind = VTBroken;
490        RegionNull(&pRoot->clipList);
491        ErrorF("ValidateTree: BUSTED!\n");
492    }
493
494    /*
495     * Recursively compute the clips for all children of the root.
496     * They don't clip against each other or the root itself, so
497     * childClip is always reset to that child's size.
498     */
499
500    for (pWin = pChild; pWin != NullWindow; pWin = pWin->nextSib) {
501        if (pWin->viewable) {
502            if (pWin->valdata) {
503                RegionCopy(&childClip, &pWin->borderSize);
504                RootlessComputeClips(pWin, pScreen, &childClip, kind, &exposed);
505            }
506            else if (pWin->visibility == VisibilityNotViewable) {
507                RootlessTreeObscured(pWin);
508            }
509        }
510        else {
511            if (pWin->valdata) {
512                RegionEmpty(&pWin->clipList);
513                if (pScreen->ClipNotify)
514                    (*pScreen->ClipNotify) (pWin, 0, 0);
515                RegionEmpty(&pWin->borderClip);
516                pWin->valdata = NULL;
517            }
518        }
519    }
520
521    RegionUninit(&childClip);
522
523    /* The root is never clipped by its children, so nothing on the root
524       is ever exposed by moving or mapping its children. */
525    RegionNull(&pRoot->valdata->after.exposed);
526    RegionNull(&pRoot->valdata->after.borderExposed);
527
528    return 1;
529}
530