428261197a
Tested by ajacoutot@, krw@, shadchin@ and jasper@ on various configurations including multihead with both zaphod and xrandr.
488 lines
14 KiB
C
488 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_FillBytes_threshold = 0;
|
|
unsigned int rootless_CompositePixels_threshold = 0;
|
|
unsigned int rootless_CopyWindow_threshold = 0;
|
|
#ifdef ROOTLESS_GLOBAL_COORDS
|
|
int rootlessGlobalOffsetX = 0;
|
|
int rootlessGlobalOffsetY = 0;
|
|
#endif
|
|
|
|
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, pointer 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, (pointer)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 */
|
|
|
|
#ifdef ROOTLESS_TRACK_DAMAGE
|
|
RegionUnion(&winRec->damage,
|
|
&winRec->damage, (pRegion));
|
|
#else
|
|
SCREENREC(pWindow->drawable.pScreen)->imp->
|
|
DamageRects(winRec->wid,
|
|
RegionNumRects(pRegion),
|
|
RegionRects(pRegion),
|
|
-winRec->x, -winRec->y);
|
|
#endif
|
|
|
|
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);
|
|
|
|
#ifdef ROOTLESS_TRACK_DAMAGE
|
|
RegionUnion(&winRec->damage,
|
|
&winRec->damage, (pRegion));
|
|
#else
|
|
SCREENREC(pWindow->drawable.pScreen)->imp->
|
|
DamageRects(winRec->wid,
|
|
RegionNumRects(&clipped),
|
|
RegionRects(&clipped),
|
|
-winRec->x, -winRec->y);
|
|
#endif
|
|
|
|
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)
|
|
{
|
|
#ifdef ROOTLESS_TRACK_DAMAGE
|
|
|
|
RootlessWindowRec *winRec = WINREC(pWindow);
|
|
ScreenPtr pScreen = pWindow->drawable.pScreen;
|
|
|
|
RootlessStopDrawing(pWindow, FALSE);
|
|
|
|
if (RegionNotEmpty(&winRec->damage)) {
|
|
RL_DEBUG_MSG("Redisplay Win 0x%x, %i x %i @ (%i, %i)\n",
|
|
pWindow, winRec->width, winRec->height,
|
|
winRec->x, winRec->y);
|
|
|
|
// move region to window local coords
|
|
RegionTranslate(&winRec->damage,
|
|
-winRec->x, -winRec->y);
|
|
|
|
SCREENREC(pScreen)->imp->UpdateRegion(winRec->wid, &winRec->damage);
|
|
|
|
RegionEmpty(&winRec->damage);
|
|
}
|
|
|
|
#else /* !ROOTLESS_TRACK_DAMAGE */
|
|
|
|
RootlessStopDrawing(pWindow, TRUE);
|
|
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
* 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);
|
|
}
|
|
}
|
|
}
|
|
}
|