461 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			461 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
| /*
 | |
|  * Common rootless definitions and code
 | |
|  */
 | |
| /*
 | |
|  * Copyright (c) 2001 Greg Parker. All Rights Reserved.
 | |
|  * Copyright (c) 2002-2003 Torrey T. Lyons. All Rights Reserved.
 | |
|  * Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
 | |
|  *
 | |
|  * Permission is hereby granted, free of charge, to any person obtaining a
 | |
|  * copy of this software and associated documentation files (the "Software"),
 | |
|  * to deal in the Software without restriction, including without limitation
 | |
|  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 | |
|  * and/or sell copies of the Software, and to permit persons to whom the
 | |
|  * Software is furnished to do so, subject to the following conditions:
 | |
|  *
 | |
|  * The above copyright notice and this permission notice shall be included in
 | |
|  * all copies or substantial portions of the Software.
 | |
|  *
 | |
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | |
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | |
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 | |
|  * THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
 | |
|  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 | |
|  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 | |
|  * DEALINGS IN THE SOFTWARE.
 | |
|  *
 | |
|  * Except as contained in this notice, the name(s) of the above copyright
 | |
|  * holders shall not be used in advertising or otherwise to promote the sale,
 | |
|  * use or other dealings in this Software without prior written authorization.
 | |
|  */
 | |
| 
 | |
| #ifdef HAVE_DIX_CONFIG_H
 | |
| #include <dix-config.h>
 | |
| #endif
 | |
| 
 | |
| #include <stddef.h>             /* For NULL */
 | |
| #include <limits.h>             /* For CHAR_BIT */
 | |
| 
 | |
| #include "rootlessCommon.h"
 | |
| #include "colormapst.h"
 | |
| 
 | |
| unsigned int rootless_CopyBytes_threshold = 0;
 | |
| unsigned int rootless_CopyWindow_threshold = 0;
 | |
| int rootlessGlobalOffsetX = 0;
 | |
| int rootlessGlobalOffsetY = 0;
 | |
| 
 | |
| RegionRec rootlessHugeRoot = { {-32767, -32767, 32767, 32767}, NULL };
 | |
| 
 | |
| /* Following macro from miregion.c */
 | |
| 
 | |
| /*  true iff two Boxes overlap */
 | |
| #define EXTENTCHECK(r1,r2) \
 | |
|       (!( ((r1)->x2 <= (r2)->x1)  || \
 | |
|           ((r1)->x1 >= (r2)->x2)  || \
 | |
|           ((r1)->y2 <= (r2)->y1)  || \
 | |
|           ((r1)->y1 >= (r2)->y2) ) )
 | |
| 
 | |
| /*
 | |
|  * TopLevelParent
 | |
|  *  Returns the top-level parent of pWindow.
 | |
|  *  The root is the top-level parent of itself, even though the root is
 | |
|  *  not otherwise considered to be a top-level window.
 | |
|  */
 | |
| WindowPtr
 | |
| TopLevelParent(WindowPtr pWindow)
 | |
| {
 | |
|     WindowPtr top;
 | |
| 
 | |
|     if (IsRoot(pWindow))
 | |
|         return pWindow;
 | |
| 
 | |
|     top = pWindow;
 | |
|     while (top && !IsTopLevel(top))
 | |
|         top = top->parent;
 | |
| 
 | |
|     return top;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * IsFramedWindow
 | |
|  *  Returns TRUE if this window is visible inside a frame
 | |
|  *  (e.g. it is visible and has a top-level or root parent)
 | |
|  */
 | |
| Bool
 | |
| IsFramedWindow(WindowPtr pWin)
 | |
| {
 | |
|     WindowPtr top;
 | |
| 
 | |
|     if (!dixPrivateKeyRegistered(&rootlessWindowPrivateKeyRec))
 | |
|         return FALSE;
 | |
| 
 | |
|     if (!pWin->realized)
 | |
|         return FALSE;
 | |
|     top = TopLevelParent(pWin);
 | |
| 
 | |
|     return (top && WINREC(top));
 | |
| }
 | |
| 
 | |
| Bool
 | |
| RootlessResolveColormap(ScreenPtr pScreen, int first_color,
 | |
|                         int n_colors, uint32_t * colors)
 | |
| {
 | |
|     int last, i;
 | |
|     ColormapPtr map;
 | |
| 
 | |
|     map = RootlessGetColormap(pScreen);
 | |
|     if (map == NULL || map->class != PseudoColor)
 | |
|         return FALSE;
 | |
| 
 | |
|     last = min(map->pVisual->ColormapEntries, first_color + n_colors);
 | |
|     for (i = max(0, first_color); i < last; i++) {
 | |
|         Entry *ent = map->red + i;
 | |
|         uint16_t red, green, blue;
 | |
| 
 | |
|         if (!ent->refcnt)
 | |
|             continue;
 | |
|         if (ent->fShared) {
 | |
|             red = ent->co.shco.red->color;
 | |
|             green = ent->co.shco.green->color;
 | |
|             blue = ent->co.shco.blue->color;
 | |
|         }
 | |
|         else {
 | |
|             red = ent->co.local.red;
 | |
|             green = ent->co.local.green;
 | |
|             blue = ent->co.local.blue;
 | |
|         }
 | |
| 
 | |
|         colors[i - first_color] = (0xFF000000UL
 | |
|                                    | ((uint32_t) red & 0xff00) << 8
 | |
|                                    | (green & 0xff00)
 | |
|                                    | (blue >> 8));
 | |
|     }
 | |
| 
 | |
|     return TRUE;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * RootlessStartDrawing
 | |
|  *  Prepare a window for direct access to its backing buffer.
 | |
|  *  Each top-level parent has a Pixmap representing its backing buffer,
 | |
|  *  which all of its children inherit.
 | |
|  */
 | |
| void
 | |
| RootlessStartDrawing(WindowPtr pWindow)
 | |
| {
 | |
|     ScreenPtr pScreen = pWindow->drawable.pScreen;
 | |
|     WindowPtr top = TopLevelParent(pWindow);
 | |
|     RootlessWindowRec *winRec;
 | |
|     PixmapPtr curPixmap;
 | |
| 
 | |
|     if (top == NULL)
 | |
|         return;
 | |
|     winRec = WINREC(top);
 | |
|     if (winRec == NULL)
 | |
|         return;
 | |
| 
 | |
|     // Make sure the window's top-level parent is prepared for drawing.
 | |
|     if (!winRec->is_drawing) {
 | |
|         int bw = wBorderWidth(top);
 | |
| 
 | |
|         SCREENREC(pScreen)->imp->StartDrawing(winRec->wid, &winRec->pixelData,
 | |
|                                               &winRec->bytesPerRow);
 | |
| 
 | |
|         winRec->pixmap =
 | |
|             GetScratchPixmapHeader(pScreen, winRec->width, winRec->height,
 | |
|                                    top->drawable.depth,
 | |
|                                    top->drawable.bitsPerPixel,
 | |
|                                    winRec->bytesPerRow, winRec->pixelData);
 | |
|         SetPixmapBaseToScreen(winRec->pixmap,
 | |
|                               top->drawable.x - bw, top->drawable.y - bw);
 | |
| 
 | |
|         winRec->is_drawing = TRUE;
 | |
|     }
 | |
| 
 | |
|     curPixmap = pScreen->GetWindowPixmap(pWindow);
 | |
|     if (curPixmap == winRec->pixmap) {
 | |
|         RL_DEBUG_MSG("Window %p already has winRec->pixmap %p; not pushing\n",
 | |
|                      pWindow, winRec->pixmap);
 | |
|     }
 | |
|     else {
 | |
|         PixmapPtr oldPixmap =
 | |
|             dixLookupPrivate(&pWindow->devPrivates,
 | |
|                              rootlessWindowOldPixmapPrivateKey);
 | |
|         if (oldPixmap != NULL) {
 | |
|             if (oldPixmap == curPixmap)
 | |
|                 RL_DEBUG_MSG
 | |
|                     ("Window %p's curPixmap %p is the same as its oldPixmap; strange\n",
 | |
|                      pWindow, curPixmap);
 | |
|             else
 | |
|                 RL_DEBUG_MSG("Window %p's existing oldPixmap %p being lost!\n",
 | |
|                              pWindow, oldPixmap);
 | |
|         }
 | |
|         dixSetPrivate(&pWindow->devPrivates, rootlessWindowOldPixmapPrivateKey,
 | |
|                       curPixmap);
 | |
|         pScreen->SetWindowPixmap(pWindow, winRec->pixmap);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * RootlessStopDrawing
 | |
|  *  Stop drawing to a window's backing buffer. If flush is true,
 | |
|  *  damaged regions are flushed to the screen.
 | |
|  */
 | |
| static int
 | |
| RestorePreDrawingPixmapVisitor(WindowPtr pWindow, void *data)
 | |
| {
 | |
|     RootlessWindowRec *winRec = (RootlessWindowRec *) data;
 | |
|     ScreenPtr pScreen = pWindow->drawable.pScreen;
 | |
|     PixmapPtr exPixmap = pScreen->GetWindowPixmap(pWindow);
 | |
|     PixmapPtr oldPixmap =
 | |
|         dixLookupPrivate(&pWindow->devPrivates,
 | |
|                          rootlessWindowOldPixmapPrivateKey);
 | |
|     if (oldPixmap == NULL) {
 | |
|         if (exPixmap == winRec->pixmap)
 | |
|             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);
 | |
|     }
 | |
|     else {
 | |
|         if (exPixmap != winRec->pixmap)
 | |
|             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);
 | |
|         if (oldPixmap == winRec->pixmap)
 | |
|             RL_DEBUG_MSG
 | |
|                 ("Window %p's oldPixmap %p is winRec->pixmap, which has just been freed!\n",
 | |
|                  pWindow, oldPixmap);
 | |
|         pScreen->SetWindowPixmap(pWindow, oldPixmap);
 | |
|         dixSetPrivate(&pWindow->devPrivates, rootlessWindowOldPixmapPrivateKey,
 | |
|                       NULL);
 | |
|     }
 | |
|     return WT_WALKCHILDREN;
 | |
| }
 | |
| 
 | |
| void
 | |
| RootlessStopDrawing(WindowPtr pWindow, Bool flush)
 | |
| {
 | |
|     ScreenPtr pScreen = pWindow->drawable.pScreen;
 | |
|     WindowPtr top = TopLevelParent(pWindow);
 | |
|     RootlessWindowRec *winRec;
 | |
| 
 | |
|     if (top == NULL)
 | |
|         return;
 | |
|     winRec = WINREC(top);
 | |
|     if (winRec == NULL)
 | |
|         return;
 | |
| 
 | |
|     if (winRec->is_drawing) {
 | |
|         SCREENREC(pScreen)->imp->StopDrawing(winRec->wid, flush);
 | |
| 
 | |
|         FreeScratchPixmapHeader(winRec->pixmap);
 | |
|         TraverseTree(top, RestorePreDrawingPixmapVisitor, (void *) winRec);
 | |
|         winRec->pixmap = NULL;
 | |
| 
 | |
|         winRec->is_drawing = FALSE;
 | |
|     }
 | |
|     else if (flush) {
 | |
|         SCREENREC(pScreen)->imp->UpdateRegion(winRec->wid, NULL);
 | |
|     }
 | |
| 
 | |
|     if (flush && winRec->is_reorder_pending) {
 | |
|         winRec->is_reorder_pending = FALSE;
 | |
|         RootlessReorderWindow(pWindow);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * RootlessDamageRegion
 | |
|  *  Mark a damaged region as requiring redisplay to screen.
 | |
|  *  pRegion is in GLOBAL coordinates.
 | |
|  */
 | |
| void
 | |
| RootlessDamageRegion(WindowPtr pWindow, RegionPtr pRegion)
 | |
| {
 | |
|     RootlessWindowRec *winRec;
 | |
|     RegionRec clipped;
 | |
|     WindowPtr pTop;
 | |
|     BoxPtr b1, b2;
 | |
| 
 | |
|     RL_DEBUG_MSG("Damaged win 0x%x ", pWindow);
 | |
| 
 | |
|     pTop = TopLevelParent(pWindow);
 | |
|     if (pTop == NULL)
 | |
|         return;
 | |
| 
 | |
|     winRec = WINREC(pTop);
 | |
|     if (winRec == NULL)
 | |
|         return;
 | |
| 
 | |
|     /* We need to intersect the drawn region with the clip of the window
 | |
|        to avoid marking places we didn't actually draw (which can cause
 | |
|        problems when the window has an extra client-side backing store)
 | |
| 
 | |
|        But this is a costly operation and since we'll normally just be
 | |
|        drawing inside the clip, go to some lengths to avoid the general
 | |
|        case intersection. */
 | |
| 
 | |
|     b1 = RegionExtents(&pWindow->borderClip);
 | |
|     b2 = RegionExtents(pRegion);
 | |
| 
 | |
|     if (EXTENTCHECK(b1, b2)) {
 | |
|         /* Regions may overlap. */
 | |
| 
 | |
|         if (RegionNumRects(pRegion) == 1) {
 | |
|             int in;
 | |
| 
 | |
|             /* Damaged region only has a single rect, so we can
 | |
|                just compare that against the region */
 | |
| 
 | |
|             in = RegionContainsRect(&pWindow->borderClip, RegionRects(pRegion));
 | |
|             if (in == rgnIN) {
 | |
|                 /* clip totally contains pRegion */
 | |
| 
 | |
|                 SCREENREC(pWindow->drawable.pScreen)->imp->DamageRects(winRec->
 | |
|                                                                        wid,
 | |
|                                                                        RegionNumRects
 | |
|                                                                        (pRegion),
 | |
|                                                                        RegionRects
 | |
|                                                                        (pRegion),
 | |
|                                                                        -winRec->
 | |
|                                                                        x,
 | |
|                                                                        -winRec->
 | |
|                                                                        y);
 | |
| 
 | |
|                 RootlessQueueRedisplay(pTop->drawable.pScreen);
 | |
|                 goto out;
 | |
|             }
 | |
|             else if (in == rgnOUT) {
 | |
|                 /* clip doesn't contain pRegion */
 | |
| 
 | |
|                 goto out;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /* clip overlaps pRegion, need to intersect */
 | |
| 
 | |
|         RegionNull(&clipped);
 | |
|         RegionIntersect(&clipped, &pWindow->borderClip, pRegion);
 | |
| 
 | |
|         SCREENREC(pWindow->drawable.pScreen)->imp->DamageRects(winRec->wid,
 | |
|                                                                RegionNumRects
 | |
|                                                                (&clipped),
 | |
|                                                                RegionRects
 | |
|                                                                (&clipped),
 | |
|                                                                -winRec->x,
 | |
|                                                                -winRec->y);
 | |
| 
 | |
|         RegionUninit(&clipped);
 | |
| 
 | |
|         RootlessQueueRedisplay(pTop->drawable.pScreen);
 | |
|     }
 | |
| 
 | |
|  out:
 | |
| #ifdef ROOTLESSDEBUG
 | |
|     {
 | |
|         BoxRec *box = RegionRects(pRegion), *end;
 | |
|         int numBox = RegionNumRects(pRegion);
 | |
| 
 | |
|         for (end = box + numBox; box < end; box++) {
 | |
|             RL_DEBUG_MSG("Damage rect: %i, %i, %i, %i\n",
 | |
|                          box->x1, box->x2, box->y1, box->y2);
 | |
|         }
 | |
|     }
 | |
| #endif
 | |
|     return;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * RootlessDamageBox
 | |
|  *  Mark a damaged box as requiring redisplay to screen.
 | |
|  *  pRegion is in GLOBAL coordinates.
 | |
|  */
 | |
| void
 | |
| RootlessDamageBox(WindowPtr pWindow, BoxPtr pBox)
 | |
| {
 | |
|     RegionRec region;
 | |
| 
 | |
|     RegionInit(®ion, pBox, 1);
 | |
| 
 | |
|     RootlessDamageRegion(pWindow, ®ion);
 | |
| 
 | |
|     RegionUninit(®ion);      /* no-op */
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * RootlessDamageRect
 | |
|  *  Mark a damaged rectangle as requiring redisplay to screen.
 | |
|  *  (x, y, w, h) is in window-local coordinates.
 | |
|  */
 | |
| void
 | |
| RootlessDamageRect(WindowPtr pWindow, int x, int y, int w, int h)
 | |
| {
 | |
|     BoxRec box;
 | |
|     RegionRec region;
 | |
| 
 | |
|     x += pWindow->drawable.x;
 | |
|     y += pWindow->drawable.y;
 | |
| 
 | |
|     box.x1 = x;
 | |
|     box.x2 = x + w;
 | |
|     box.y1 = y;
 | |
|     box.y2 = y + h;
 | |
| 
 | |
|     RegionInit(®ion, &box, 1);
 | |
| 
 | |
|     RootlessDamageRegion(pWindow, ®ion);
 | |
| 
 | |
|     RegionUninit(®ion);      /* no-op */
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * RootlessRedisplay
 | |
|  *  Stop drawing and redisplay the damaged region of a window.
 | |
|  */
 | |
| void
 | |
| RootlessRedisplay(WindowPtr pWindow)
 | |
| {
 | |
|     RootlessStopDrawing(pWindow, TRUE);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * RootlessRepositionWindows
 | |
|  *  Reposition all windows on a screen to their correct positions.
 | |
|  */
 | |
| void
 | |
| RootlessRepositionWindows(ScreenPtr pScreen)
 | |
| {
 | |
|     WindowPtr root = pScreen->root;
 | |
|     WindowPtr win;
 | |
| 
 | |
|     if (root != NULL) {
 | |
|         RootlessRepositionWindow(root);
 | |
| 
 | |
|         for (win = root->firstChild; win; win = win->nextSib) {
 | |
|             if (WINREC(win) != NULL)
 | |
|                 RootlessRepositionWindow(win);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * RootlessRedisplayScreen
 | |
|  *  Walk every window on a screen and redisplay the damaged regions.
 | |
|  */
 | |
| void
 | |
| RootlessRedisplayScreen(ScreenPtr pScreen)
 | |
| {
 | |
|     WindowPtr root = pScreen->root;
 | |
| 
 | |
|     if (root != NULL) {
 | |
|         WindowPtr win;
 | |
| 
 | |
|         RootlessRedisplay(root);
 | |
|         for (win = root->firstChild; win; win = win->nextSib) {
 | |
|             if (WINREC(win) != NULL) {
 | |
|                 RootlessRedisplay(win);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 |