rootlessCommon.c revision 35c4bbdf
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 * TopLevelParent
60 *  Returns the top-level parent of pWindow.
61 *  The root is the top-level parent of itself, even though the root is
62 *  not otherwise considered to be a top-level window.
63 */
64WindowPtr
65TopLevelParent(WindowPtr pWindow)
66{
67    WindowPtr top;
68
69    if (IsRoot(pWindow))
70        return pWindow;
71
72    top = pWindow;
73    while (top && !IsTopLevel(top))
74        top = top->parent;
75
76    return top;
77}
78
79/*
80 * IsFramedWindow
81 *  Returns TRUE if this window is visible inside a frame
82 *  (e.g. it is visible and has a top-level or root parent)
83 */
84Bool
85IsFramedWindow(WindowPtr pWin)
86{
87    WindowPtr top;
88
89    if (!dixPrivateKeyRegistered(&rootlessWindowPrivateKeyRec))
90        return FALSE;
91
92    if (!pWin->realized)
93        return FALSE;
94    top = TopLevelParent(pWin);
95
96    return (top && WINREC(top));
97}
98
99Bool
100RootlessResolveColormap(ScreenPtr pScreen, int first_color,
101                        int n_colors, uint32_t * colors)
102{
103    int last, i;
104    ColormapPtr map;
105
106    map = RootlessGetColormap(pScreen);
107    if (map == NULL || map->class != PseudoColor)
108        return FALSE;
109
110    last = min(map->pVisual->ColormapEntries, first_color + n_colors);
111    for (i = max(0, first_color); i < last; i++) {
112        Entry *ent = map->red + i;
113        uint16_t red, green, blue;
114
115        if (!ent->refcnt)
116            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        }
122        else {
123            red = ent->co.local.red;
124            green = ent->co.local.green;
125            blue = ent->co.local.blue;
126        }
127
128        colors[i - first_color] = (0xFF000000UL
129                                   | ((uint32_t) red & 0xff00) << 8
130                                   | (green & 0xff00)
131                                   | (blue >> 8));
132    }
133
134    return TRUE;
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
144RootlessStartDrawing(WindowPtr pWindow)
145{
146    ScreenPtr pScreen = pWindow->drawable.pScreen;
147    WindowPtr top = TopLevelParent(pWindow);
148    RootlessWindowRec *winRec;
149    PixmapPtr curPixmap;
150
151    if (top == NULL)
152        return;
153    winRec = WINREC(top);
154    if (winRec == NULL)
155        return;
156
157    // Make sure the window's top-level parent is prepared for drawing.
158    if (!winRec->is_drawing) {
159        int bw = wBorderWidth(top);
160
161        SCREENREC(pScreen)->imp->StartDrawing(winRec->wid, &winRec->pixelData,
162                                              &winRec->bytesPerRow);
163
164        winRec->pixmap =
165            GetScratchPixmapHeader(pScreen, winRec->width, winRec->height,
166                                   top->drawable.depth,
167                                   top->drawable.bitsPerPixel,
168                                   winRec->bytesPerRow, 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        RL_DEBUG_MSG("Window %p already has winRec->pixmap %p; not pushing\n",
178                     pWindow, winRec->pixmap);
179    }
180    else {
181        PixmapPtr oldPixmap =
182            dixLookupPrivate(&pWindow->devPrivates,
183                             rootlessWindowOldPixmapPrivateKey);
184        if (oldPixmap != NULL) {
185            if (oldPixmap == curPixmap)
186                RL_DEBUG_MSG
187                    ("Window %p's curPixmap %p is the same as its oldPixmap; strange\n",
188                     pWindow, curPixmap);
189            else
190                RL_DEBUG_MSG("Window %p's existing oldPixmap %p being lost!\n",
191                             pWindow, oldPixmap);
192        }
193        dixSetPrivate(&pWindow->devPrivates, rootlessWindowOldPixmapPrivateKey,
194                      curPixmap);
195        pScreen->SetWindowPixmap(pWindow, winRec->pixmap);
196    }
197}
198
199/*
200 * RootlessStopDrawing
201 *  Stop drawing to a window's backing buffer. If flush is true,
202 *  damaged regions are flushed to the screen.
203 */
204static int
205RestorePreDrawingPixmapVisitor(WindowPtr pWindow, void *data)
206{
207    RootlessWindowRec *winRec = (RootlessWindowRec *) data;
208    ScreenPtr pScreen = pWindow->drawable.pScreen;
209    PixmapPtr exPixmap = pScreen->GetWindowPixmap(pWindow);
210    PixmapPtr oldPixmap =
211        dixLookupPrivate(&pWindow->devPrivates,
212                         rootlessWindowOldPixmapPrivateKey);
213    if (oldPixmap == NULL) {
214        if (exPixmap == winRec->pixmap)
215            RL_DEBUG_MSG
216                ("Window %p appears to be in drawing mode (ex-pixmap %p equals winRec->pixmap, which is being freed) but has no oldPixmap!\n",
217                 pWindow, exPixmap);
218    }
219    else {
220        if (exPixmap != winRec->pixmap)
221            RL_DEBUG_MSG
222                ("Window %p appears to be in drawing mode (oldPixmap %p) but ex-pixmap %p not winRec->pixmap %p!\n",
223                 pWindow, oldPixmap, exPixmap, winRec->pixmap);
224        if (oldPixmap == winRec->pixmap)
225            RL_DEBUG_MSG
226                ("Window %p's oldPixmap %p is winRec->pixmap, which has just been freed!\n",
227                 pWindow, oldPixmap);
228        pScreen->SetWindowPixmap(pWindow, oldPixmap);
229        dixSetPrivate(&pWindow->devPrivates, rootlessWindowOldPixmapPrivateKey,
230                      NULL);
231    }
232    return WT_WALKCHILDREN;
233}
234
235void
236RootlessStopDrawing(WindowPtr pWindow, Bool flush)
237{
238    ScreenPtr pScreen = pWindow->drawable.pScreen;
239    WindowPtr top = TopLevelParent(pWindow);
240    RootlessWindowRec *winRec;
241
242    if (top == NULL)
243        return;
244    winRec = WINREC(top);
245    if (winRec == NULL)
246        return;
247
248    if (winRec->is_drawing) {
249        SCREENREC(pScreen)->imp->StopDrawing(winRec->wid, flush);
250
251        FreeScratchPixmapHeader(winRec->pixmap);
252        TraverseTree(top, RestorePreDrawingPixmapVisitor, (void *) winRec);
253        winRec->pixmap = NULL;
254
255        winRec->is_drawing = FALSE;
256    }
257    else if (flush) {
258        SCREENREC(pScreen)->imp->UpdateRegion(winRec->wid, NULL);
259    }
260
261    if (flush && winRec->is_reorder_pending) {
262        winRec->is_reorder_pending = FALSE;
263        RootlessReorderWindow(pWindow);
264    }
265}
266
267/*
268 * RootlessDamageRegion
269 *  Mark a damaged region as requiring redisplay to screen.
270 *  pRegion is in GLOBAL coordinates.
271 */
272void
273RootlessDamageRegion(WindowPtr pWindow, RegionPtr pRegion)
274{
275    RootlessWindowRec *winRec;
276    RegionRec clipped;
277    WindowPtr pTop;
278    BoxPtr b1, b2;
279
280    RL_DEBUG_MSG("Damaged win 0x%x ", pWindow);
281
282    pTop = TopLevelParent(pWindow);
283    if (pTop == NULL)
284        return;
285
286    winRec = WINREC(pTop);
287    if (winRec == NULL)
288        return;
289
290    /* We need to intersect the drawn region with the clip of the window
291       to avoid marking places we didn't actually draw (which can cause
292       problems when the window has an extra client-side backing store)
293
294       But this is a costly operation and since we'll normally just be
295       drawing inside the clip, go to some lengths to avoid the general
296       case intersection. */
297
298    b1 = RegionExtents(&pWindow->borderClip);
299    b2 = RegionExtents(pRegion);
300
301    if (EXTENTCHECK(b1, b2)) {
302        /* Regions may overlap. */
303
304        if (RegionNumRects(pRegion) == 1) {
305            int in;
306
307            /* Damaged region only has a single rect, so we can
308               just compare that against the region */
309
310            in = RegionContainsRect(&pWindow->borderClip, RegionRects(pRegion));
311            if (in == rgnIN) {
312                /* clip totally contains pRegion */
313
314                SCREENREC(pWindow->drawable.pScreen)->imp->DamageRects(winRec->
315                                                                       wid,
316                                                                       RegionNumRects
317                                                                       (pRegion),
318                                                                       RegionRects
319                                                                       (pRegion),
320                                                                       -winRec->
321                                                                       x,
322                                                                       -winRec->
323                                                                       y);
324
325                RootlessQueueRedisplay(pTop->drawable.pScreen);
326                goto out;
327            }
328            else if (in == rgnOUT) {
329                /* clip doesn't contain pRegion */
330
331                goto out;
332            }
333        }
334
335        /* clip overlaps pRegion, need to intersect */
336
337        RegionNull(&clipped);
338        RegionIntersect(&clipped, &pWindow->borderClip, pRegion);
339
340        SCREENREC(pWindow->drawable.pScreen)->imp->DamageRects(winRec->wid,
341                                                               RegionNumRects
342                                                               (&clipped),
343                                                               RegionRects
344                                                               (&clipped),
345                                                               -winRec->x,
346                                                               -winRec->y);
347
348        RegionUninit(&clipped);
349
350        RootlessQueueRedisplay(pTop->drawable.pScreen);
351    }
352
353 out:
354#ifdef ROOTLESSDEBUG
355    {
356        BoxRec *box = RegionRects(pRegion), *end;
357        int numBox = RegionNumRects(pRegion);
358
359        for (end = box + numBox; box < end; box++) {
360            RL_DEBUG_MSG("Damage rect: %i, %i, %i, %i\n",
361                         box->x1, box->x2, box->y1, box->y2);
362        }
363    }
364#endif
365    return;
366}
367
368/*
369 * RootlessDamageBox
370 *  Mark a damaged box as requiring redisplay to screen.
371 *  pRegion is in GLOBAL coordinates.
372 */
373void
374RootlessDamageBox(WindowPtr pWindow, BoxPtr pBox)
375{
376    RegionRec region;
377
378    RegionInit(&region, pBox, 1);
379
380    RootlessDamageRegion(pWindow, &region);
381
382    RegionUninit(&region);      /* no-op */
383}
384
385/*
386 * RootlessDamageRect
387 *  Mark a damaged rectangle as requiring redisplay to screen.
388 *  (x, y, w, h) is in window-local coordinates.
389 */
390void
391RootlessDamageRect(WindowPtr pWindow, int x, int y, int w, int h)
392{
393    BoxRec box;
394    RegionRec region;
395
396    x += pWindow->drawable.x;
397    y += pWindow->drawable.y;
398
399    box.x1 = x;
400    box.x2 = x + w;
401    box.y1 = y;
402    box.y2 = y + h;
403
404    RegionInit(&region, &box, 1);
405
406    RootlessDamageRegion(pWindow, &region);
407
408    RegionUninit(&region);      /* no-op */
409}
410
411/*
412 * RootlessRedisplay
413 *  Stop drawing and redisplay the damaged region of a window.
414 */
415void
416RootlessRedisplay(WindowPtr pWindow)
417{
418    RootlessStopDrawing(pWindow, TRUE);
419}
420
421/*
422 * RootlessRepositionWindows
423 *  Reposition all windows on a screen to their correct positions.
424 */
425void
426RootlessRepositionWindows(ScreenPtr pScreen)
427{
428    WindowPtr root = pScreen->root;
429    WindowPtr win;
430
431    if (root != NULL) {
432        RootlessRepositionWindow(root);
433
434        for (win = root->firstChild; win; win = win->nextSib) {
435            if (WINREC(win) != NULL)
436                RootlessRepositionWindow(win);
437        }
438    }
439}
440
441/*
442 * RootlessRedisplayScreen
443 *  Walk every window on a screen and redisplay the damaged regions.
444 */
445void
446RootlessRedisplayScreen(ScreenPtr pScreen)
447{
448    WindowPtr root = pScreen->root;
449
450    if (root != NULL) {
451        WindowPtr win;
452
453        RootlessRedisplay(root);
454        for (win = root->firstChild; win; win = win->nextSib) {
455            if (WINREC(win) != NULL) {
456                RootlessRedisplay(win);
457            }
458        }
459    }
460}
461