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(®ion, pBox, 1); 417 418 RootlessDamageRegion(pWindow, ®ion); 419 420 RegionUninit(®ion); /* 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(®ion, &box, 1); 443 444 RootlessDamageRegion(pWindow, ®ion); 445 446 RegionUninit(®ion); /* 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