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
137unsigned long RootlessWID(WindowPtr pWindow) {
138    ScreenPtr pScreen = pWindow->drawable.pScreen;
139    WindowPtr top = TopLevelParent(pWindow);
140    RootlessWindowRec *winRec;
141    PixmapPtr curPixmap;
142
143    if (top == NULL) {
144        return 0;
145    }
146    winRec = WINREC(top);
147    if (winRec == NULL) {
148        return 0;
149    }
150
151    return (unsigned long)(uintptr_t)winRec->wid;
152}
153
154/*
155 * RootlessStartDrawing
156 *  Prepare a window for direct access to its backing buffer.
157 *  Each top-level parent has a Pixmap representing its backing buffer,
158 *  which all of its children inherit.
159 */
160void
161RootlessStartDrawing(WindowPtr pWindow)
162{
163    ScreenPtr pScreen = pWindow->drawable.pScreen;
164    WindowPtr top = TopLevelParent(pWindow);
165    RootlessWindowRec *winRec;
166    PixmapPtr curPixmap;
167
168    if (top == NULL) {
169        RL_DEBUG_MSG("RootlessStartDrawing is a no-op because top == NULL.\n");
170        return;
171    }
172    winRec = WINREC(top);
173    if (winRec == NULL) {
174        RL_DEBUG_MSG("RootlessStartDrawing is a no-op because winRec == NULL.\n");
175        return;
176    }
177
178    // Make sure the window's top-level parent is prepared for drawing.
179    if (!winRec->is_drawing) {
180        int bw = wBorderWidth(top);
181
182        SCREENREC(pScreen)->imp->StartDrawing(winRec->wid, &winRec->pixelData,
183                                              &winRec->bytesPerRow);
184
185        winRec->pixmap =
186            GetScratchPixmapHeader(pScreen, winRec->width, winRec->height,
187                                   top->drawable.depth,
188                                   top->drawable.bitsPerPixel,
189                                   winRec->bytesPerRow, winRec->pixelData);
190
191        RL_DEBUG_MSG("GetScratchPixmapHeader gave us %p %p (%d,%d %dx%d %d) for wid=%lu\n",
192                     winRec->pixmap, winRec->pixmap->devPrivate.ptr, winRec->pixmap->drawable.x,
193                     winRec->pixmap->drawable.y, winRec->pixmap->drawable.width, winRec->pixmap->drawable.height,
194                     winRec->pixmap->drawable.bitsPerPixel, RootlessWID(pWindow));
195
196        SetPixmapBaseToScreen(winRec->pixmap,
197                              top->drawable.x - bw, top->drawable.y - bw);
198
199        RL_DEBUG_MSG("After SetPixmapBaseToScreen(%d %d %d): %p (%d,%d %dx%d %d) for wid=%lu\n",
200                     top->drawable.x, top->drawable.y, bw, winRec->pixmap->devPrivate.ptr, winRec->pixmap->drawable.x,
201                     winRec->pixmap->drawable.y, winRec->pixmap->drawable.width, winRec->pixmap->drawable.height,
202                     winRec->pixmap->drawable.bitsPerPixel, RootlessWID(pWindow));
203
204        winRec->is_drawing = TRUE;
205    } else {
206        RL_DEBUG_MSG("Skipped call to xprStartDrawing (wid: %lu) because winRec->is_drawing says we already did.\n", RootlessWID(pWindow));
207    }
208
209    curPixmap = pScreen->GetWindowPixmap(pWindow);
210    if (curPixmap == winRec->pixmap) {
211        RL_DEBUG_MSG("Window %p already has winRec->pixmap %p; not pushing\n",
212                     pWindow, winRec->pixmap);
213    }
214    else {
215        PixmapPtr oldPixmap =
216            dixLookupPrivate(&pWindow->devPrivates,
217                             rootlessWindowOldPixmapPrivateKey);
218
219        RL_DEBUG_MSG("curPixmap is %p %p for wid=%lu\n", curPixmap, curPixmap ? curPixmap->devPrivate.ptr : NULL, RootlessWID(pWindow));
220        RL_DEBUG_MSG("oldPixmap is %p %p for wid=%lu\n", oldPixmap, oldPixmap ? oldPixmap->devPrivate.ptr : NULL, RootlessWID(pWindow));
221
222        if (oldPixmap != NULL) {
223            if (oldPixmap == curPixmap)
224                RL_DEBUG_MSG
225                    ("Window %p's curPixmap %p is the same as its oldPixmap; strange\n",
226                     pWindow, curPixmap);
227            else
228                RL_DEBUG_MSG("Window %p's existing oldPixmap %p being lost!\n",
229                             pWindow, oldPixmap);
230        }
231        dixSetPrivate(&pWindow->devPrivates, rootlessWindowOldPixmapPrivateKey,
232                      curPixmap);
233        pScreen->SetWindowPixmap(pWindow, winRec->pixmap);
234    }
235}
236
237/*
238 * RootlessStopDrawing
239 *  Stop drawing to a window's backing buffer. If flush is true,
240 *  damaged regions are flushed to the screen.
241 */
242static int
243RestorePreDrawingPixmapVisitor(WindowPtr pWindow, void *data)
244{
245    RootlessWindowRec *winRec = (RootlessWindowRec *) data;
246    ScreenPtr pScreen = pWindow->drawable.pScreen;
247    PixmapPtr exPixmap = pScreen->GetWindowPixmap(pWindow);
248    PixmapPtr oldPixmap =
249        dixLookupPrivate(&pWindow->devPrivates,
250                         rootlessWindowOldPixmapPrivateKey);
251    if (oldPixmap == NULL) {
252        if (exPixmap == winRec->pixmap)
253            RL_DEBUG_MSG
254                ("Window %p appears to be in drawing mode (ex-pixmap %p equals winRec->pixmap, which is being freed) but has no oldPixmap!\n",
255                 pWindow, exPixmap);
256    }
257    else {
258        if (exPixmap != winRec->pixmap)
259            RL_DEBUG_MSG
260                ("Window %p appears to be in drawing mode (oldPixmap %p) but ex-pixmap %p not winRec->pixmap %p!\n",
261                 pWindow, oldPixmap, exPixmap, winRec->pixmap);
262        if (oldPixmap == winRec->pixmap)
263            RL_DEBUG_MSG
264                ("Window %p's oldPixmap %p is winRec->pixmap, which has just been freed!\n",
265                 pWindow, oldPixmap);
266        pScreen->SetWindowPixmap(pWindow, oldPixmap);
267        dixSetPrivate(&pWindow->devPrivates, rootlessWindowOldPixmapPrivateKey,
268                      NULL);
269    }
270    return WT_WALKCHILDREN;
271}
272
273void
274RootlessStopDrawing(WindowPtr pWindow, Bool flush)
275{
276    ScreenPtr pScreen = pWindow->drawable.pScreen;
277    WindowPtr top = TopLevelParent(pWindow);
278    RootlessWindowRec *winRec;
279
280    if (top == NULL)
281        return;
282    winRec = WINREC(top);
283    if (winRec == NULL)
284        return;
285
286    if (winRec->is_drawing) {
287        SCREENREC(pScreen)->imp->StopDrawing(winRec->wid, flush);
288
289        FreeScratchPixmapHeader(winRec->pixmap);
290        TraverseTree(top, RestorePreDrawingPixmapVisitor, (void *) winRec);
291        winRec->pixmap = NULL;
292
293        winRec->is_drawing = FALSE;
294    }
295    else if (flush) {
296        SCREENREC(pScreen)->imp->UpdateRegion(winRec->wid, NULL);
297    }
298
299    if (flush && winRec->is_reorder_pending) {
300        winRec->is_reorder_pending = FALSE;
301        RootlessReorderWindow(pWindow);
302    }
303}
304
305/*
306 * RootlessDamageRegion
307 *  Mark a damaged region as requiring redisplay to screen.
308 *  pRegion is in GLOBAL coordinates.
309 */
310void
311RootlessDamageRegion(WindowPtr pWindow, RegionPtr pRegion)
312{
313    RootlessWindowRec *winRec;
314    RegionRec clipped;
315    WindowPtr pTop;
316    BoxPtr b1, b2;
317
318    RL_DEBUG_MSG("Damaged win %p\n", pWindow);
319
320    pTop = TopLevelParent(pWindow);
321    if (pTop == NULL)
322        return;
323
324    winRec = WINREC(pTop);
325    if (winRec == NULL)
326        return;
327
328    /* We need to intersect the drawn region with the clip of the window
329       to avoid marking places we didn't actually draw (which can cause
330       problems when the window has an extra client-side backing store)
331
332       But this is a costly operation and since we'll normally just be
333       drawing inside the clip, go to some lengths to avoid the general
334       case intersection. */
335
336    b1 = RegionExtents(&pWindow->borderClip);
337    b2 = RegionExtents(pRegion);
338
339    if (EXTENTCHECK(b1, b2)) {
340        /* Regions may overlap. */
341
342        if (RegionNumRects(pRegion) == 1) {
343            int in;
344
345            /* Damaged region only has a single rect, so we can
346               just compare that against the region */
347
348            in = RegionContainsRect(&pWindow->borderClip, RegionRects(pRegion));
349            if (in == rgnIN) {
350                /* clip totally contains pRegion */
351
352                SCREENREC(pWindow->drawable.pScreen)->imp->DamageRects(winRec->
353                                                                       wid,
354                                                                       RegionNumRects
355                                                                       (pRegion),
356                                                                       RegionRects
357                                                                       (pRegion),
358                                                                       -winRec->
359                                                                       x,
360                                                                       -winRec->
361                                                                       y);
362
363                RootlessQueueRedisplay(pTop->drawable.pScreen);
364                goto out;
365            }
366            else if (in == rgnOUT) {
367                /* clip doesn't contain pRegion */
368
369                goto out;
370            }
371        }
372
373        /* clip overlaps pRegion, need to intersect */
374
375        RegionNull(&clipped);
376        RegionIntersect(&clipped, &pWindow->borderClip, pRegion);
377
378        SCREENREC(pWindow->drawable.pScreen)->imp->DamageRects(winRec->wid,
379                                                               RegionNumRects
380                                                               (&clipped),
381                                                               RegionRects
382                                                               (&clipped),
383                                                               -winRec->x,
384                                                               -winRec->y);
385
386        RegionUninit(&clipped);
387
388        RootlessQueueRedisplay(pTop->drawable.pScreen);
389    }
390
391 out:
392#ifdef ROOTLESSDEBUG
393    {
394        BoxRec *box = RegionRects(pRegion), *end;
395        int numBox = RegionNumRects(pRegion);
396
397        for (end = box + numBox; box < end; box++) {
398            RL_DEBUG_MSG("Damage rect: %i, %i, %i, %i\n",
399                         box->x1, box->x2, box->y1, box->y2);
400        }
401    }
402#endif
403    return;
404}
405
406/*
407 * RootlessDamageBox
408 *  Mark a damaged box as requiring redisplay to screen.
409 *  pRegion is in GLOBAL coordinates.
410 */
411void
412RootlessDamageBox(WindowPtr pWindow, BoxPtr pBox)
413{
414    RegionRec region;
415
416    RegionInit(&region, pBox, 1);
417
418    RootlessDamageRegion(pWindow, &region);
419
420    RegionUninit(&region);      /* no-op */
421}
422
423/*
424 * RootlessDamageRect
425 *  Mark a damaged rectangle as requiring redisplay to screen.
426 *  (x, y, w, h) is in window-local coordinates.
427 */
428void
429RootlessDamageRect(WindowPtr pWindow, int x, int y, int w, int h)
430{
431    BoxRec box;
432    RegionRec region;
433
434    x += pWindow->drawable.x;
435    y += pWindow->drawable.y;
436
437    box.x1 = x;
438    box.x2 = x + w;
439    box.y1 = y;
440    box.y2 = y + h;
441
442    RegionInit(&region, &box, 1);
443
444    RootlessDamageRegion(pWindow, &region);
445
446    RegionUninit(&region);      /* no-op */
447}
448
449/*
450 * RootlessRedisplay
451 *  Stop drawing and redisplay the damaged region of a window.
452 */
453void
454RootlessRedisplay(WindowPtr pWindow)
455{
456    RootlessStopDrawing(pWindow, TRUE);
457}
458
459/*
460 * RootlessRepositionWindows
461 *  Reposition all windows on a screen to their correct positions.
462 */
463void
464RootlessRepositionWindows(ScreenPtr pScreen)
465{
466    WindowPtr root = pScreen->root;
467    WindowPtr win;
468
469    if (root != NULL) {
470        RootlessRepositionWindow(root);
471
472        for (win = root->firstChild; win; win = win->nextSib) {
473            if (WINREC(win) != NULL)
474                RootlessRepositionWindow(win);
475        }
476    }
477}
478
479/*
480 * RootlessRedisplayScreen
481 *  Walk every window on a screen and redisplay the damaged regions.
482 */
483void
484RootlessRedisplayScreen(ScreenPtr pScreen)
485{
486    WindowPtr root = pScreen->root;
487
488    if (root != NULL) {
489        WindowPtr win;
490
491        RootlessRedisplay(root);
492        for (win = root->firstChild; win; win = win->nextSib) {
493            if (WINREC(win) != NULL) {
494                RootlessRedisplay(win);
495            }
496        }
497    }
498}
499