rootlessCommon.c revision 4642e01f
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_FillBytes_threshold = 0;
44unsigned int rootless_CompositePixels_threshold = 0;
45unsigned int rootless_CopyWindow_threshold = 0;
46#ifdef ROOTLESS_GLOBAL_COORDS
47int rootlessGlobalOffsetX = 0;
48int rootlessGlobalOffsetY = 0;
49#endif
50
51RegionRec rootlessHugeRoot = {{-32767, -32767, 32767, 32767}, NULL};
52
53/* Following macro from miregion.c */
54
55/*  true iff two Boxes overlap */
56#define EXTENTCHECK(r1,r2) \
57      (!( ((r1)->x2 <= (r2)->x1)  || \
58          ((r1)->x1 >= (r2)->x2)  || \
59          ((r1)->y2 <= (r2)->y1)  || \
60          ((r1)->y1 >= (r2)->y2) ) )
61
62
63/*
64 * TopLevelParent
65 *  Returns the top-level parent of pWindow.
66 *  The root is the top-level parent of itself, even though the root is
67 *  not otherwise considered to be a top-level window.
68 */
69WindowPtr
70TopLevelParent(WindowPtr pWindow)
71{
72    WindowPtr top;
73
74    if (IsRoot(pWindow))
75        return pWindow;
76
77    top = pWindow;
78    while (top && ! IsTopLevel(top))
79        top = top->parent;
80
81    return top;
82}
83
84
85/*
86 * IsFramedWindow
87 *  Returns TRUE if this window is visible inside a frame
88 *  (e.g. it is visible and has a top-level or root parent)
89 */
90Bool
91IsFramedWindow(WindowPtr pWin)
92{
93    WindowPtr top;
94
95    if (!pWin->realized)
96        return FALSE;
97    top = TopLevelParent(pWin);
98
99    return (top && WINREC(top));
100}
101
102Bool
103RootlessResolveColormap (ScreenPtr pScreen, int first_color,
104                         int n_colors, uint32_t *colors)
105{
106  int last, i;
107  ColormapPtr map;
108
109  map = RootlessGetColormap (pScreen);
110  if (map == NULL || map->class != PseudoColor) return FALSE;
111
112  last = MIN (map->pVisual->ColormapEntries, first_color + n_colors);
113  for (i = MAX (0, first_color); i < last; i++) {
114    Entry *ent = map->red + i;
115    uint16_t red, green, blue;
116
117      if (!ent->refcnt)	continue;
118      if (ent->fShared) {
119	red = ent->co.shco.red->color;
120	green = ent->co.shco.green->color;
121	blue = ent->co.shco.blue->color;
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/*
139 * RootlessStartDrawing
140 *  Prepare a window for direct access to its backing buffer.
141 *  Each top-level parent has a Pixmap representing its backing buffer,
142 *  which all of its children inherit.
143 */
144void RootlessStartDrawing(WindowPtr pWindow)
145{
146    ScreenPtr pScreen = pWindow->drawable.pScreen;
147    WindowPtr top = TopLevelParent(pWindow);
148    RootlessWindowRec *winRec;
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    PixmapPtr 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    ScreenPtr pScreen = pWindow->drawable.pScreen;
265    RootlessWindowRec *winRec;
266    RegionRec clipped;
267    WindowPtr pTop;
268    BoxPtr b1, b2;
269
270    RL_DEBUG_MSG("Damaged win 0x%x ", pWindow);
271
272    pTop = TopLevelParent(pWindow);
273    if (pTop == NULL)
274        return;
275
276    winRec = WINREC(pTop);
277    if (winRec == NULL)
278        return;
279
280    /* We need to intersect the drawn region with the clip of the window
281       to avoid marking places we didn't actually draw (which can cause
282       problems when the window has an extra client-side backing store)
283
284       But this is a costly operation and since we'll normally just be
285       drawing inside the clip, go to some lengths to avoid the general
286       case intersection. */
287
288    b1 = REGION_EXTENTS(pScreen, &pWindow->borderClip);
289    b2 = REGION_EXTENTS(pScreen, pRegion);
290
291    if (EXTENTCHECK(b1, b2)) {
292        /* Regions may overlap. */
293
294        if (REGION_NUM_RECTS(pRegion) == 1) {
295            int in;
296
297            /* Damaged region only has a single rect, so we can
298               just compare that against the region */
299
300            in = RECT_IN_REGION(pScreen, &pWindow->borderClip,
301                                REGION_RECTS (pRegion));
302            if (in == rgnIN) {
303            /* clip totally contains pRegion */
304
305#ifdef ROOTLESS_TRACK_DAMAGE
306                REGION_UNION(pScreen, &winRec->damage,
307                                 &winRec->damage, (pRegion));
308#else
309                SCREENREC(pScreen)->imp->DamageRects(winRec->wid,
310                                REGION_NUM_RECTS(pRegion),
311                                REGION_RECTS(pRegion),
312                                -winRec->x, -winRec->y);
313#endif
314
315                RootlessQueueRedisplay(pTop->drawable.pScreen);
316                goto out;
317            }
318            else if (in == rgnOUT) {
319                /* clip doesn't contain pRegion */
320
321                goto out;
322            }
323        }
324
325        /* clip overlaps pRegion, need to intersect */
326
327        REGION_NULL(pScreen, &clipped);
328        REGION_INTERSECT(pScreen, &clipped, &pWindow->borderClip, pRegion);
329
330#ifdef ROOTLESS_TRACK_DAMAGE
331        REGION_UNION(pScreen, &winRec->damage,
332                     &winRec->damage, (pRegion));
333#else
334        SCREENREC(pScreen)->imp->DamageRects(winRec->wid,
335                        REGION_NUM_RECTS(&clipped),
336                        REGION_RECTS(&clipped),
337                        -winRec->x, -winRec->y);
338#endif
339
340        REGION_UNINIT(pScreen, &clipped);
341
342        RootlessQueueRedisplay(pTop->drawable.pScreen);
343    }
344
345out:
346#ifdef ROOTLESSDEBUG
347    {
348        BoxRec *box = REGION_RECTS(pRegion), *end;
349        int numBox = REGION_NUM_RECTS(pRegion);
350
351        for (end = box+numBox; box < end; box++) {
352            RL_DEBUG_MSG("Damage rect: %i, %i, %i, %i\n",
353                         box->x1, box->x2, box->y1, box->y2);
354        }
355    }
356#endif
357    return;
358}
359
360
361/*
362 * RootlessDamageBox
363 *  Mark a damaged box as requiring redisplay to screen.
364 *  pRegion is in GLOBAL coordinates.
365 */
366void
367RootlessDamageBox(WindowPtr pWindow, BoxPtr pBox)
368{
369    RegionRec region;
370
371    REGION_INIT(pWindow->drawable.pScreen, &region, pBox, 1);
372
373    RootlessDamageRegion(pWindow, &region);
374
375    REGION_UNINIT(pWindow->drawable.pScreen, &region);  /* no-op */
376}
377
378
379/*
380 * RootlessDamageRect
381 *  Mark a damaged rectangle as requiring redisplay to screen.
382 *  (x, y, w, h) is in window-local coordinates.
383 */
384void
385RootlessDamageRect(WindowPtr pWindow, int x, int y, int w, int h)
386{
387    BoxRec box;
388    RegionRec region;
389
390    x += pWindow->drawable.x;
391    y += pWindow->drawable.y;
392
393    box.x1 = x;
394    box.x2 = x + w;
395    box.y1 = y;
396    box.y2 = y + h;
397
398    REGION_INIT(pWindow->drawable.pScreen, &region, &box, 1);
399
400    RootlessDamageRegion(pWindow, &region);
401
402    REGION_UNINIT(pWindow->drawable.pScreen, &region);  /* no-op */
403}
404
405
406/*
407 * RootlessRedisplay
408 *  Stop drawing and redisplay the damaged region of a window.
409 */
410void
411RootlessRedisplay(WindowPtr pWindow)
412{
413#ifdef ROOTLESS_TRACK_DAMAGE
414
415    RootlessWindowRec *winRec = WINREC(pWindow);
416    ScreenPtr pScreen = pWindow->drawable.pScreen;
417
418    RootlessStopDrawing(pWindow, FALSE);
419
420    if (REGION_NOTEMPTY(pScreen, &winRec->damage)) {
421        RL_DEBUG_MSG("Redisplay Win 0x%x, %i x %i @ (%i, %i)\n",
422                     pWindow, winRec->width, winRec->height,
423                     winRec->x, winRec->y);
424
425        // move region to window local coords
426        REGION_TRANSLATE(pScreen, &winRec->damage,
427                         -winRec->x, -winRec->y);
428
429        SCREENREC(pScreen)->imp->UpdateRegion(winRec->wid, &winRec->damage);
430
431        REGION_EMPTY(pScreen, &winRec->damage);
432    }
433
434#else   /* !ROOTLESS_TRACK_DAMAGE */
435
436    RootlessStopDrawing(pWindow, TRUE);
437
438#endif
439}
440
441
442/*
443 * RootlessRepositionWindows
444 *  Reposition all windows on a screen to their correct positions.
445 */
446void
447RootlessRepositionWindows(ScreenPtr pScreen)
448{
449    WindowPtr root = WindowTable[pScreen->myNum];
450    WindowPtr win;
451
452    if (root != NULL) {
453        RootlessRepositionWindow(root);
454
455        for (win = root->firstChild; win; win = win->nextSib) {
456            if (WINREC(win) != NULL)
457                RootlessRepositionWindow(win);
458        }
459    }
460}
461
462
463/*
464 * RootlessRedisplayScreen
465 *  Walk every window on a screen and redisplay the damaged regions.
466 */
467void
468RootlessRedisplayScreen(ScreenPtr pScreen)
469{
470    WindowPtr root = WindowTable[pScreen->myNum];
471
472    if (root != NULL) {
473        WindowPtr win;
474
475        RootlessRedisplay(root);
476        for (win = root->firstChild; win; win = win->nextSib) {
477            if (WINREC(win) != NULL) {
478                RootlessRedisplay(win);
479            }
480        }
481    }
482}
483