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