1/*
2 * Common rootless definitions and code
3 */
4/*
5 * Copyright (c) 2001 Greg Parker. All Rights Reserved.
6 * Copyright (c) 2002-2003 Torrey T. Lyons. All Rights Reserved.
7 * Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the "Software"),
11 * to deal in the Software without restriction, including without limitation
12 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 * and/or sell copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included in
17 * all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22 * THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
23 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
24 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 * DEALINGS IN THE SOFTWARE.
26 *
27 * Except as contained in this notice, the name(s) of the above copyright
28 * holders shall not be used in advertising or otherwise to promote the sale,
29 * use or other dealings in this Software without prior written authorization.
30 */
31
32#ifdef HAVE_DIX_CONFIG_H
33#include <dix-config.h>
34#endif
35
36#include <stddef.h> /* For NULL */
37#include <limits.h> /* For CHAR_BIT */
38
39#include "rootlessCommon.h"
40#include "colormapst.h"
41
42unsigned int rootless_CopyBytes_threshold = 0;
43unsigned int rootless_CopyWindow_threshold = 0;
44int rootlessGlobalOffsetX = 0;
45int rootlessGlobalOffsetY = 0;
46
47RegionRec rootlessHugeRoot = {{-32767, -32767, 32767, 32767}, NULL};
48
49/* Following macro from miregion.c */
50
51/*  true iff two Boxes overlap */
52#define EXTENTCHECK(r1,r2) \
53      (!( ((r1)->x2 <= (r2)->x1)  || \
54          ((r1)->x1 >= (r2)->x2)  || \
55          ((r1)->y2 <= (r2)->y1)  || \
56          ((r1)->y1 >= (r2)->y2) ) )
57
58
59/*
60 * TopLevelParent
61 *  Returns the top-level parent of pWindow.
62 *  The root is the top-level parent of itself, even though the root is
63 *  not otherwise considered to be a top-level window.
64 */
65WindowPtr
66TopLevelParent(WindowPtr pWindow)
67{
68    WindowPtr top;
69
70    if (IsRoot(pWindow))
71        return pWindow;
72
73    top = pWindow;
74    while (top && ! IsTopLevel(top))
75        top = top->parent;
76
77    return top;
78}
79
80
81/*
82 * IsFramedWindow
83 *  Returns TRUE if this window is visible inside a frame
84 *  (e.g. it is visible and has a top-level or root parent)
85 */
86Bool
87IsFramedWindow(WindowPtr pWin)
88{
89    WindowPtr top;
90
91    if (!dixPrivateKeyRegistered(&rootlessWindowPrivateKeyRec))
92        return FALSE;
93
94    if (!pWin->realized)
95        return FALSE;
96    top = TopLevelParent(pWin);
97
98    return (top && WINREC(top));
99}
100
101Bool
102RootlessResolveColormap (ScreenPtr pScreen, int first_color,
103                         int n_colors, uint32_t *colors)
104{
105  int last, i;
106  ColormapPtr map;
107
108  map = RootlessGetColormap (pScreen);
109  if (map == NULL || map->class != PseudoColor) return FALSE;
110
111  last = min (map->pVisual->ColormapEntries, first_color + n_colors);
112  for (i = max (0, first_color); i < last; i++) {
113    Entry *ent = map->red + i;
114    uint16_t red, green, blue;
115
116      if (!ent->refcnt)	continue;
117      if (ent->fShared) {
118	red = ent->co.shco.red->color;
119	green = ent->co.shco.green->color;
120	blue = ent->co.shco.blue->color;
121      } else {
122	red = ent->co.local.red;
123	green = ent->co.local.green;
124	blue = ent->co.local.blue;
125      }
126
127      colors[i - first_color] = (0xFF000000UL
128				 | ((uint32_t) red & 0xff00) << 8
129				 | (green & 0xff00)
130				 | (blue >> 8));
131    }
132
133  return TRUE;
134}
135
136
137/*
138 * RootlessStartDrawing
139 *  Prepare a window for direct access to its backing buffer.
140 *  Each top-level parent has a Pixmap representing its backing buffer,
141 *  which all of its children inherit.
142 */
143void RootlessStartDrawing(WindowPtr pWindow)
144{
145    ScreenPtr pScreen = pWindow->drawable.pScreen;
146    WindowPtr top = TopLevelParent(pWindow);
147    RootlessWindowRec *winRec;
148    PixmapPtr curPixmap;
149
150    if (top == NULL)
151        return;
152    winRec = WINREC(top);
153    if (winRec == NULL)
154        return;
155
156    // Make sure the window's top-level parent is prepared for drawing.
157    if (!winRec->is_drawing) {
158        int bw = wBorderWidth(top);
159
160        SCREENREC(pScreen)->imp->StartDrawing(winRec->wid, &winRec->pixelData,
161                                              &winRec->bytesPerRow);
162
163        winRec->pixmap =
164            GetScratchPixmapHeader(pScreen, winRec->width, winRec->height,
165                                   top->drawable.depth,
166                                   top->drawable.bitsPerPixel,
167                                   winRec->bytesPerRow,
168                                   winRec->pixelData);
169        SetPixmapBaseToScreen(winRec->pixmap,
170                              top->drawable.x - bw, top->drawable.y - bw);
171
172        winRec->is_drawing = TRUE;
173    }
174
175    curPixmap = pScreen->GetWindowPixmap(pWindow);
176    if (curPixmap == winRec->pixmap)
177    {
178        RL_DEBUG_MSG("Window %p already has winRec->pixmap %p; not pushing\n", pWindow, winRec->pixmap);
179    }
180    else
181    {
182        PixmapPtr oldPixmap = dixLookupPrivate(&pWindow->devPrivates, rootlessWindowOldPixmapPrivateKey);
183        if (oldPixmap != NULL)
184        {
185            if (oldPixmap == curPixmap)
186                RL_DEBUG_MSG("Window %p's curPixmap %p is the same as its oldPixmap; strange\n", pWindow, curPixmap);
187            else
188                RL_DEBUG_MSG("Window %p's existing oldPixmap %p being lost!\n", pWindow, oldPixmap);
189        }
190	dixSetPrivate(&pWindow->devPrivates, rootlessWindowOldPixmapPrivateKey, curPixmap);
191        pScreen->SetWindowPixmap(pWindow, winRec->pixmap);
192    }
193}
194
195
196/*
197 * RootlessStopDrawing
198 *  Stop drawing to a window's backing buffer. If flush is true,
199 *  damaged regions are flushed to the screen.
200 */
201static int RestorePreDrawingPixmapVisitor(WindowPtr pWindow, pointer data)
202{
203    RootlessWindowRec *winRec = (RootlessWindowRec*)data;
204    ScreenPtr pScreen = pWindow->drawable.pScreen;
205    PixmapPtr exPixmap = pScreen->GetWindowPixmap(pWindow);
206    PixmapPtr oldPixmap = dixLookupPrivate(&pWindow->devPrivates, rootlessWindowOldPixmapPrivateKey);
207    if (oldPixmap == NULL)
208    {
209        if (exPixmap == winRec->pixmap)
210            RL_DEBUG_MSG("Window %p appears to be in drawing mode (ex-pixmap %p equals winRec->pixmap, which is being freed) but has no oldPixmap!\n", pWindow, exPixmap);
211    }
212    else
213    {
214        if (exPixmap != winRec->pixmap)
215            RL_DEBUG_MSG("Window %p appears to be in drawing mode (oldPixmap %p) but ex-pixmap %p not winRec->pixmap %p!\n", pWindow, oldPixmap, exPixmap, winRec->pixmap);
216        if (oldPixmap == winRec->pixmap)
217            RL_DEBUG_MSG("Window %p's oldPixmap %p is winRec->pixmap, which has just been freed!\n", pWindow, oldPixmap);
218        pScreen->SetWindowPixmap(pWindow, oldPixmap);
219        dixSetPrivate(&pWindow->devPrivates, rootlessWindowOldPixmapPrivateKey, NULL);
220    }
221    return WT_WALKCHILDREN;
222}
223
224void RootlessStopDrawing(WindowPtr pWindow, Bool flush)
225{
226    ScreenPtr pScreen = pWindow->drawable.pScreen;
227    WindowPtr top = TopLevelParent(pWindow);
228    RootlessWindowRec *winRec;
229
230    if (top == NULL)
231        return;
232    winRec = WINREC(top);
233    if (winRec == NULL)
234        return;
235
236    if (winRec->is_drawing) {
237        SCREENREC(pScreen)->imp->StopDrawing(winRec->wid, flush);
238
239        FreeScratchPixmapHeader(winRec->pixmap);
240        TraverseTree(top, RestorePreDrawingPixmapVisitor, (pointer)winRec);
241        winRec->pixmap = NULL;
242
243        winRec->is_drawing = FALSE;
244    }
245    else if (flush) {
246        SCREENREC(pScreen)->imp->UpdateRegion(winRec->wid, NULL);
247    }
248
249    if (flush && winRec->is_reorder_pending) {
250        winRec->is_reorder_pending = FALSE;
251        RootlessReorderWindow(pWindow);
252    }
253}
254
255
256/*
257 * RootlessDamageRegion
258 *  Mark a damaged region as requiring redisplay to screen.
259 *  pRegion is in GLOBAL coordinates.
260 */
261void
262RootlessDamageRegion(WindowPtr pWindow, RegionPtr pRegion)
263{
264    RootlessWindowRec *winRec;
265    RegionRec clipped;
266    WindowPtr pTop;
267    BoxPtr b1, b2;
268
269    RL_DEBUG_MSG("Damaged win 0x%x ", pWindow);
270
271    pTop = TopLevelParent(pWindow);
272    if (pTop == NULL)
273        return;
274
275    winRec = WINREC(pTop);
276    if (winRec == NULL)
277        return;
278
279    /* We need to intersect the drawn region with the clip of the window
280       to avoid marking places we didn't actually draw (which can cause
281       problems when the window has an extra client-side backing store)
282
283       But this is a costly operation and since we'll normally just be
284       drawing inside the clip, go to some lengths to avoid the general
285       case intersection. */
286
287    b1 = RegionExtents(&pWindow->borderClip);
288    b2 = RegionExtents(pRegion);
289
290    if (EXTENTCHECK(b1, b2)) {
291        /* Regions may overlap. */
292
293        if (RegionNumRects(pRegion) == 1) {
294            int in;
295
296            /* Damaged region only has a single rect, so we can
297               just compare that against the region */
298
299            in = RegionContainsRect(&pWindow->borderClip,
300                                RegionRects (pRegion));
301            if (in == rgnIN) {
302            /* clip totally contains pRegion */
303
304                SCREENREC(pWindow->drawable.pScreen)->imp->
305                    DamageRects(winRec->wid,
306                                RegionNumRects(pRegion),
307                                RegionRects(pRegion),
308                                -winRec->x, -winRec->y);
309
310                RootlessQueueRedisplay(pTop->drawable.pScreen);
311                goto out;
312            }
313            else if (in == rgnOUT) {
314                /* clip doesn't contain pRegion */
315
316                goto out;
317            }
318        }
319
320        /* clip overlaps pRegion, need to intersect */
321
322        RegionNull(&clipped);
323        RegionIntersect(&clipped, &pWindow->borderClip, pRegion);
324
325        SCREENREC(pWindow->drawable.pScreen)->imp->
326            DamageRects(winRec->wid,
327                        RegionNumRects(&clipped),
328                        RegionRects(&clipped),
329                        -winRec->x, -winRec->y);
330
331        RegionUninit(&clipped);
332
333        RootlessQueueRedisplay(pTop->drawable.pScreen);
334    }
335
336out:
337#ifdef ROOTLESSDEBUG
338    {
339        BoxRec *box = RegionRects(pRegion), *end;
340        int numBox = RegionNumRects(pRegion);
341
342        for (end = box+numBox; box < end; box++) {
343            RL_DEBUG_MSG("Damage rect: %i, %i, %i, %i\n",
344                         box->x1, box->x2, box->y1, box->y2);
345        }
346    }
347#endif
348    return;
349}
350
351
352/*
353 * RootlessDamageBox
354 *  Mark a damaged box as requiring redisplay to screen.
355 *  pRegion is in GLOBAL coordinates.
356 */
357void
358RootlessDamageBox(WindowPtr pWindow, BoxPtr pBox)
359{
360    RegionRec region;
361
362    RegionInit(&region, pBox, 1);
363
364    RootlessDamageRegion(pWindow, &region);
365
366    RegionUninit(&region);  /* no-op */
367}
368
369
370/*
371 * RootlessDamageRect
372 *  Mark a damaged rectangle as requiring redisplay to screen.
373 *  (x, y, w, h) is in window-local coordinates.
374 */
375void
376RootlessDamageRect(WindowPtr pWindow, int x, int y, int w, int h)
377{
378    BoxRec box;
379    RegionRec region;
380
381    x += pWindow->drawable.x;
382    y += pWindow->drawable.y;
383
384    box.x1 = x;
385    box.x2 = x + w;
386    box.y1 = y;
387    box.y2 = y + h;
388
389    RegionInit(&region, &box, 1);
390
391    RootlessDamageRegion(pWindow, &region);
392
393    RegionUninit(&region);  /* no-op */
394}
395
396
397/*
398 * RootlessRedisplay
399 *  Stop drawing and redisplay the damaged region of a window.
400 */
401void
402RootlessRedisplay(WindowPtr pWindow)
403{
404    RootlessStopDrawing(pWindow, TRUE);
405}
406
407
408/*
409 * RootlessRepositionWindows
410 *  Reposition all windows on a screen to their correct positions.
411 */
412void
413RootlessRepositionWindows(ScreenPtr pScreen)
414{
415    WindowPtr root = pScreen->root;
416    WindowPtr win;
417
418    if (root != NULL) {
419        RootlessRepositionWindow(root);
420
421        for (win = root->firstChild; win; win = win->nextSib) {
422            if (WINREC(win) != NULL)
423                RootlessRepositionWindow(win);
424        }
425    }
426}
427
428
429/*
430 * RootlessRedisplayScreen
431 *  Walk every window on a screen and redisplay the damaged regions.
432 */
433void
434RootlessRedisplayScreen(ScreenPtr pScreen)
435{
436    WindowPtr root = pScreen->root;
437
438    if (root != NULL) {
439        WindowPtr win;
440
441        RootlessRedisplay(root);
442        for (win = root->firstChild; win; win = win->nextSib) {
443            if (WINREC(win) != NULL) {
444                RootlessRedisplay(win);
445            }
446        }
447    }
448}
449