rootlessValTree.c revision 4642e01f
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 RootlessShapedWindowIn (ScreenPtr pScreen, RegionPtr universe,
108			RegionPtr bounding, BoxPtr rect, int x, int y);
109
110int RootlessMiValidateTree (WindowPtr pRoot, WindowPtr pChild, VTKind kind);
111
112/*
113 * Compute the visibility of a shaped window
114 */
115int
116RootlessShapedWindowIn (ScreenPtr pScreen, RegionPtr universe,
117			RegionPtr bounding, BoxPtr rect, int x, int y)
118{
119    BoxRec  box;
120    register BoxPtr  boundBox;
121    int	    nbox;
122    Bool    someIn, someOut;
123    register int t, x1, y1, x2, y2;
124
125    nbox = REGION_NUM_RECTS (bounding);
126    boundBox = REGION_RECTS (bounding);
127    someIn = someOut = FALSE;
128    x1 = rect->x1;
129    y1 = rect->y1;
130    x2 = rect->x2;
131    y2 = rect->y2;
132    while (nbox--)
133    {
134	if ((t = boundBox->x1 + x) < x1)
135	    t = x1;
136	box.x1 = t;
137	if ((t = boundBox->y1 + y) < y1)
138	    t = y1;
139	box.y1 = t;
140	if ((t = boundBox->x2 + x) > x2)
141	    t = x2;
142	box.x2 = t;
143	if ((t = boundBox->y2 + y) > y2)
144	    t = y2;
145	box.y2 = t;
146	if (box.x1 > box.x2)
147	    box.x2 = box.x1;
148	if (box.y1 > box.y2)
149	    box.y2 = box.y1;
150	switch (RECT_IN_REGION(pScreen, universe, &box))
151	{
152	case rgnIN:
153	    if (someOut)
154		return rgnPART;
155	    someIn = TRUE;
156	    break;
157	case rgnOUT:
158	    if (someIn)
159		return rgnPART;
160	    someOut = TRUE;
161	    break;
162	default:
163	    return rgnPART;
164	}
165	boundBox++;
166    }
167    if (someIn)
168	return rgnIN;
169    return rgnOUT;
170}
171
172#define HasParentRelativeBorder(w) (!(w)->borderIsPixel && \
173				    HasBorder(w) && \
174				    (w)->backgroundState == ParentRelative)
175
176
177/*
178 *-----------------------------------------------------------------------
179 * RootlessComputeClips --
180 *	Recompute the clipList, borderClip, exposed and borderExposed
181 *	regions for pParent and its children. Only viewable windows are
182 *	taken into account.
183 *
184 * Results:
185 *	None.
186 *
187 * Side Effects:
188 *	clipList, borderClip, exposed and borderExposed are altered.
189 *	A VisibilityNotify event may be generated on the parent window.
190 *
191 *-----------------------------------------------------------------------
192 */
193static void
194RootlessComputeClips (WindowPtr pParent, ScreenPtr pScreen,
195		      RegionPtr universe, VTKind kind, RegionPtr exposed)
196{
197    int			dx,
198			dy;
199    RegionRec		childUniverse;
200    register WindowPtr	pChild;
201    int     	  	oldVis, newVis;
202    BoxRec		borderSize;
203    RegionRec		childUnion;
204    Bool		overlap;
205    RegionPtr		borderVisible;
206    Bool		resized;
207    /*
208     * Figure out the new visibility of this window.
209     * The extent of the universe should be the same as the extent of
210     * the borderSize region. If the window is unobscured, this rectangle
211     * will be completely inside the universe (the universe will cover it
212     * completely). If the window is completely obscured, none of the
213     * universe will cover the rectangle.
214     */
215    borderSize.x1 = pParent->drawable.x - wBorderWidth(pParent);
216    borderSize.y1 = pParent->drawable.y - wBorderWidth(pParent);
217    dx = (int) pParent->drawable.x + (int) pParent->drawable.width + wBorderWidth(pParent);
218    if (dx > 32767)
219	dx = 32767;
220    borderSize.x2 = dx;
221    dy = (int) pParent->drawable.y + (int) pParent->drawable.height + wBorderWidth(pParent);
222    if (dy > 32767)
223	dy = 32767;
224    borderSize.y2 = dy;
225
226    oldVis = pParent->visibility;
227    switch (RECT_IN_REGION( pScreen, universe, &borderSize))
228    {
229    case rgnIN:
230	    newVis = VisibilityUnobscured;
231	    break;
232	case rgnPART:
233	    newVis = VisibilityPartiallyObscured;
234	    {
235		RegionPtr   pBounding;
236
237		if ((pBounding = wBoundingShape (pParent)))
238		{
239		    switch (RootlessShapedWindowIn (pScreen, universe,
240						    pBounding, &borderSize,
241						    pParent->drawable.x,
242						    pParent->drawable.y))
243		    {
244		    case rgnIN:
245			newVis = VisibilityUnobscured;
246			break;
247		    case rgnOUT:
248			newVis = VisibilityFullyObscured;
249			break;
250		    }
251		}
252	    }
253	    break;
254	default:
255	    newVis = VisibilityFullyObscured;
256	    break;
257    }
258
259    pParent->visibility = newVis;
260    if (oldVis != newVis &&
261	((pParent->eventMask | wOtherEventMasks(pParent)) & VisibilityChangeMask))
262	SendVisibilityNotify(pParent);
263
264    dx = pParent->drawable.x - pParent->valdata->before.oldAbsCorner.x;
265    dy = pParent->drawable.y - pParent->valdata->before.oldAbsCorner.y;
266
267    /*
268     * avoid computations when dealing with simple operations
269     */
270
271    switch (kind) {
272    case VTMap:
273    case VTStack:
274    case VTUnmap:
275	break;
276    case VTMove:
277	if ((oldVis == newVis) &&
278	    ((oldVis == VisibilityFullyObscured) ||
279	     (oldVis == VisibilityUnobscured)))
280	{
281	    pChild = pParent;
282	    while (1)
283	    {
284		if (pChild->viewable)
285		{
286		    if (pChild->visibility != VisibilityFullyObscured)
287		    {
288			REGION_TRANSLATE( pScreen, &pChild->borderClip,
289						      dx, dy);
290			REGION_TRANSLATE( pScreen, &pChild->clipList,
291						      dx, dy);
292			pChild->drawable.serialNumber = NEXT_SERIAL_NUMBER;
293			if (pScreen->ClipNotify)
294			    (* pScreen->ClipNotify) (pChild, dx, dy);
295
296		    }
297		    if (pChild->valdata)
298		    {
299			REGION_NULL(pScreen,
300				    &pChild->valdata->after.borderExposed);
301			if (HasParentRelativeBorder(pChild))
302			  {
303			    REGION_SUBTRACT(pScreen,
304					 &pChild->valdata->after.borderExposed,
305					 &pChild->borderClip,
306					 &pChild->winSize);
307			}
308			REGION_NULL(pScreen, &pChild->valdata->after.exposed);
309		    }
310		    if (pChild->firstChild)
311		    {
312			pChild = pChild->firstChild;
313			continue;
314		    }
315		}
316		while (!pChild->nextSib && (pChild != pParent))
317		    pChild = pChild->parent;
318		if (pChild == pParent)
319		    break;
320		pChild = pChild->nextSib;
321	    }
322	    return;
323	}
324	/* fall through */
325    default:
326    	/*
327     	 * To calculate exposures correctly, we have to translate the old
328     	 * borderClip and clipList regions to the window's new location so there
329     	 * is a correspondence between pieces of the new and old clipping regions.
330     	 */
331    	if (dx || dy)
332    	{
333	    /*
334	     * We translate the old clipList because that will be exposed or copied
335	     * if gravity is right.
336	     */
337	    REGION_TRANSLATE( pScreen, &pParent->borderClip, dx, dy);
338	    REGION_TRANSLATE( pScreen, &pParent->clipList, dx, dy);
339    	}
340	break;
341    case VTBroken:
342	REGION_EMPTY (pScreen, &pParent->borderClip);
343	REGION_EMPTY (pScreen, &pParent->clipList);
344	break;
345    }
346
347    borderVisible = pParent->valdata->before.borderVisible;
348    resized = pParent->valdata->before.resized;
349    REGION_NULL(pScreen, &pParent->valdata->after.borderExposed);
350    REGION_NULL(pScreen, &pParent->valdata->after.exposed);
351
352    /*
353     * Since the borderClip must not be clipped by the children, we do
354     * the border exposure first...
355     *
356     * 'universe' is the window's borderClip. To figure the exposures, remove
357     * the area that used to be exposed from the new.
358     * This leaves a region of pieces that weren't exposed before.
359     */
360
361    if (HasBorder (pParent))
362    {
363    	if (borderVisible)
364    	{
365	    /*
366	     * when the border changes shape, the old visible portions
367	     * of the border will be saved by DIX in borderVisible --
368	     * use that region and destroy it
369	     */
370	    REGION_SUBTRACT( pScreen, exposed, universe, borderVisible);
371	    REGION_DESTROY( pScreen, borderVisible);
372    	}
373    	else
374    	{
375	    REGION_SUBTRACT( pScreen, exposed, universe, &pParent->borderClip);
376    	}
377	if (HasParentRelativeBorder(pParent) && (dx || dy)) {
378	    REGION_SUBTRACT( pScreen, &pParent->valdata->after.borderExposed,
379				  universe,
380				  &pParent->winSize);
381	} else {
382	    REGION_SUBTRACT( pScreen, &pParent->valdata->after.borderExposed,
383			       exposed, &pParent->winSize);
384	}
385
386    	REGION_COPY( pScreen, &pParent->borderClip, universe);
387
388    	/*
389     	 * To get the right clipList for the parent, and to make doubly sure
390     	 * that no child overlaps the parent's border, we remove the parent's
391     	 * border from the universe before proceeding.
392     	 */
393
394    	REGION_INTERSECT( pScreen, universe, universe, &pParent->winSize);
395    }
396    else
397    	REGION_COPY( pScreen, &pParent->borderClip, universe);
398
399    if ((pChild = pParent->firstChild) && pParent->mapped)
400    {
401	REGION_NULL(pScreen, &childUniverse);
402	REGION_NULL(pScreen, &childUnion);
403	if ((pChild->drawable.y < pParent->lastChild->drawable.y) ||
404	    ((pChild->drawable.y == pParent->lastChild->drawable.y) &&
405	     (pChild->drawable.x < pParent->lastChild->drawable.x)))
406	{
407	    for (; pChild; pChild = pChild->nextSib)
408	    {
409		if (pChild->viewable)
410		    REGION_APPEND( pScreen, &childUnion, &pChild->borderSize);
411	    }
412	}
413	else
414	{
415	    for (pChild = pParent->lastChild; pChild; pChild = pChild->prevSib)
416	    {
417		if (pChild->viewable)
418		    REGION_APPEND( pScreen, &childUnion, &pChild->borderSize);
419	    }
420	}
421	REGION_VALIDATE( pScreen, &childUnion, &overlap);
422
423	for (pChild = pParent->firstChild;
424	     pChild;
425	     pChild = pChild->nextSib)
426 	{
427	    if (pChild->viewable) {
428		/*
429		 * If the child is viewable, we want to remove its extents
430		 * from the current universe, but we only re-clip it if
431		 * it's been marked.
432		 */
433		if (pChild->valdata) {
434		    /*
435		     * Figure out the new universe from the child's
436		     * perspective and recurse.
437		     */
438		    REGION_INTERSECT( pScreen, &childUniverse,
439					    universe,
440					    &pChild->borderSize);
441		    RootlessComputeClips (pChild, pScreen, &childUniverse,
442					  kind, exposed);
443		}
444		/*
445		 * Once the child has been processed, we remove its extents
446		 * from the current universe, thus denying its space to any
447		 * other sibling.
448		 */
449		if (overlap)
450		    REGION_SUBTRACT( pScreen, universe, universe,
451					  &pChild->borderSize);
452	    }
453	}
454	if (!overlap)
455	    REGION_SUBTRACT( pScreen, universe, universe, &childUnion);
456	REGION_UNINIT( pScreen, &childUnion);
457	REGION_UNINIT( pScreen, &childUniverse);
458    } /* if any children */
459
460    /*
461     * 'universe' now contains the new clipList for the parent window.
462     *
463     * To figure the exposure of the window we subtract the old clip from the
464     * new, just as for the border.
465     */
466
467    if (oldVis == VisibilityFullyObscured ||
468	oldVis == VisibilityNotViewable)
469    {
470	REGION_COPY( pScreen, &pParent->valdata->after.exposed, universe);
471    }
472    else if (newVis != VisibilityFullyObscured &&
473	     newVis != VisibilityNotViewable)
474    {
475    	REGION_SUBTRACT( pScreen, &pParent->valdata->after.exposed,
476			       universe, &pParent->clipList);
477    }
478
479    /*
480     * One last thing: backing storage. We have to try to save what parts of
481     * the window are about to be obscured. We can just subtract the universe
482     * from the old clipList and get the areas that were in the old but aren't
483     * in the new and, hence, are about to be obscured.
484     */
485    if (pParent->backStorage && !resized)
486    {
487	REGION_SUBTRACT( pScreen, exposed, &pParent->clipList, universe);
488	(* pScreen->SaveDoomedAreas)(pParent, exposed, dx, dy);
489    }
490
491    /* HACK ALERT - copying contents of regions, instead of regions */
492    {
493	RegionRec   tmp;
494
495	tmp = pParent->clipList;
496	pParent->clipList = *universe;
497	*universe = tmp;
498    }
499
500#ifdef NOTDEF
501    REGION_COPY( pScreen, &pParent->clipList, universe);
502#endif
503
504    pParent->drawable.serialNumber = NEXT_SERIAL_NUMBER;
505
506    if (pScreen->ClipNotify)
507	(* pScreen->ClipNotify) (pParent, dx, dy);
508}
509
510static void
511RootlessTreeObscured(WindowPtr pParent)
512{
513    register WindowPtr pChild;
514    register int    oldVis;
515
516    pChild = pParent;
517    while (1)
518    {
519	if (pChild->viewable)
520	{
521	    oldVis = pChild->visibility;
522	    if (oldVis != (pChild->visibility = VisibilityFullyObscured) &&
523		((pChild->eventMask | wOtherEventMasks(pChild)) & VisibilityChangeMask))
524		SendVisibilityNotify(pChild);
525	    if (pChild->firstChild)
526	    {
527		pChild = pChild->firstChild;
528		continue;
529	    }
530	}
531	while (!pChild->nextSib && (pChild != pParent))
532	    pChild = pChild->parent;
533	if (pChild == pParent)
534	    break;
535	pChild = pChild->nextSib;
536    }
537}
538
539/*
540 *-----------------------------------------------------------------------
541 * RootlessMiValidateTree --
542 *	Recomputes the clip list for pParent and all its inferiors.
543 *
544 * Results:
545 *	Always returns 1.
546 *
547 * Side Effects:
548 *	The clipList, borderClip, exposed, and borderExposed regions for
549 *	each marked window are altered.
550 *
551 * Notes:
552 *	This routine assumes that all affected windows have been marked
553 *	(valdata created) and their winSize and borderSize regions
554 *	adjusted to correspond to their new positions. The borderClip and
555 *	clipList regions should not have been touched.
556 *
557 *	The top-most level is treated differently from all lower levels
558 *	because pParent is unchanged. For the top level, we merge the
559 *	regions taken up by the marked children back into the clipList
560 *	for pParent, thus forming a region from which the marked children
561 *	can claim their areas. For lower levels, where the old clipList
562 *	and borderClip are invalid, we can't do this and have to do the
563 *	extra operations done in miComputeClips, but this is much faster
564 *	e.g. when only one child has moved...
565 *
566 *-----------------------------------------------------------------------
567 */
568/*
569   Quartz version: used for validate from root in rootless mode.
570   We need to make sure top-level windows don't clip each other,
571   and that top-level windows aren't clipped to the root window.
572*/
573/*ARGSUSED*/
574// fixme this is ugly
575// Xprint/ValTree.c doesn't work, but maybe that method can?
576int
577RootlessMiValidateTree (WindowPtr pRoot, /* Parent to validate */
578			WindowPtr pChild, /* First child of pRoot that was
579					   * affected */
580			VTKind kind /* What kind of configuration caused call */)
581{
582    RegionRec	  	childClip;  /* The new borderClip for the current
583				     * child */
584    RegionRec		exposed;    /* For intermediate calculations */
585    register ScreenPtr	pScreen;
586    register WindowPtr	pWin;
587
588    pScreen = pRoot->drawable.pScreen;
589    if (pChild == NullWindow)
590	pChild = pRoot->firstChild;
591
592    REGION_NULL(pScreen, &childClip);
593    REGION_NULL(pScreen, &exposed);
594
595    if (REGION_BROKEN (pScreen, &pRoot->clipList) &&
596	!REGION_BROKEN (pScreen, &pRoot->borderClip))
597    {
598        // fixme this might not work, but hopefully doesn't happen anyway.
599        kind = VTBroken;
600        REGION_EMPTY (pScreen, &pRoot->clipList);
601        ErrorF("ValidateTree: BUSTED!\n");
602    }
603
604    /*
605     * Recursively compute the clips for all children of the root.
606     * They don't clip against each other or the root itself, so
607     * childClip is always reset to that child's size.
608     */
609
610    for (pWin = pChild;
611	 pWin != NullWindow;
612	 pWin = pWin->nextSib)
613    {
614        if (pWin->viewable) {
615            if (pWin->valdata) {
616                REGION_COPY( pScreen, &childClip, &pWin->borderSize);
617                RootlessComputeClips (pWin, pScreen, &childClip, kind, &exposed);
618            } else if (pWin->visibility == VisibilityNotViewable) {
619                RootlessTreeObscured(pWin);
620            }
621        } else {
622            if (pWin->valdata) {
623                REGION_EMPTY( pScreen, &pWin->clipList);
624                if (pScreen->ClipNotify)
625                    (* pScreen->ClipNotify) (pWin, 0, 0);
626                REGION_EMPTY( pScreen, &pWin->borderClip);
627                pWin->valdata = (ValidatePtr)NULL;
628            }
629        }
630    }
631
632    REGION_UNINIT(pScreen, &childClip);
633
634    /* The root is never clipped by its children, so nothing on the root
635       is ever exposed by moving or mapping its children. */
636    REGION_NULL(pScreen, &pRoot->valdata->after.exposed);
637    REGION_NULL(pScreen, &pRoot->valdata->after.borderExposed);
638
639    return 1;
640}
641