/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ /* Copyright (c) 2008-2017 Oleksandr Shneyder */ /* Copyright (c) 2011-2022 Mike Gabriel */ /* Copyright (c) 2014-2019 Mihai Moldovan */ /* Copyright (c) 2014-2022 Ulrich Sibiller */ /* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ /* */ /* NXAGENT, NX protocol compression and NX extensions to this software */ /* are copyright of the aforementioned persons and companies. */ /* */ /* Redistribution and use of the present software is allowed according */ /* to terms specified in the file LICENSE which comes in the source */ /* distribution. */ /* */ /* All rights reserved. */ /* */ /* NOTE: This software has received contributions from various other */ /* contributors, only the core maintainers and supporters are listed as */ /* copyright holders. Please contact us, if you feel you should be listed */ /* as copyright holder, as well. */ /* */ /**************************************************************************/ #include "dixstruct.h" #include "../../fb/fb.h" #include "misc.h" #include "Agent.h" #include "Display.h" #include "Screen.h" #include "Trap.h" #include "Image.h" #include "Drawable.h" #include "Client.h" #include "Visual.h" #include "Events.h" #include "GCs.h" #include "Utils.h" #include "Handlers.h" #include "Pixels.h" #include "Reconnect.h" #include "GCOps.h" #include "Utils.h" #include "compext/Compext.h" #include "mibstorest.h" #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP /* * The list of rectangles composing a region s returned by * nxagentGetOptimizedRegion- Boxes() instead of RegionRects(). */ #define USE_OPTIMIZED_BOXES /* * The rectangles composing a region are de- fragmented to reduce the * number of synchronizing PutImage()s. */ #define ADVANCED_BOXES_DEFRAG /* * If defined, send the XClearArea at the end of the loop * synchronizing the shadow pixmap. In this way, large images can be * split but the user will see more updates together. */ #undef COLLECTED_UPDATES #ifdef ADVANCED_BOXES_DEFRAG #define INCLUDE_MARGIN 10 #endif struct nxagentExposeBackground { PixmapPtr pBackground; RegionPtr pExpose; }; RESTYPE RT_NX_CORR_BACKGROUND; RESTYPE RT_NX_CORR_WINDOW; RESTYPE RT_NX_CORR_PIXMAP; int nxagentCorruptedPixmaps = 0; /* * Number of windows which need synchronization. */ int nxagentCorruptedWindows = 0; int nxagentCorruptedBackgrounds = 0; Bool nxagentForceSynchronization = False; _nxagentSynchronizationRec nxagentSynchronization = { (DrawablePtr) NULL, 0, 0, 0, 0, 0 }; RegionPtr nxagentDeferredBackgroundExposures = NullRegion; /* * Predicate functions used to synchronize the content of the remote * drawable with the data stored in the virtual frame-buffer. */ void nxagentSynchronizeDrawablePredicate(void *p0, XID x1, void *p2); void nxagentExposeBackgroundPredicate(void *p0, XID x1, void *p2); /* * Imported from NXresource.c */ extern Bool nxagentFindClientResource(int, RESTYPE, void *); unsigned long nxagentGetColor(DrawablePtr pDrawable, int xPixel, int yPixel); unsigned long nxagentGetDrawableColor(DrawablePtr pDrawable); unsigned long nxagentGetRegionColor(DrawablePtr pDrawable, RegionPtr pRegion); Bool nxagentSkipImage = False; static int nxagentTooManyImageData(void) { unsigned int limit = nxagentOption(ImageRateLimit); unsigned int r = nxagentGetDataRate() / 1000; #ifdef TEST if (r > limit) { fprintf(stderr, "Warning: Current bit rate is: %u kB/s.\n", r); } #endif return (r > limit); } int nxagentSynchronizeDrawable(DrawablePtr pDrawable, int wait, unsigned int breakMask, WindowPtr owner) { pDrawable = nxagentSplitDrawable(pDrawable); if (!nxagentLosslessTrap) { if (nxagentDrawableStatus(pDrawable) == Synchronized) { #ifdef TEST fprintf(stderr, "nxagentSynchronizeDrawable: Drawable [%s][%p] with id [%ld] already " "synchronized.\n", nxagentDrawableType(pDrawable), (void *) pDrawable, pDrawable -> id); #endif return 0; } } /* * What we want here is to avoid drawing on the framebuffer and just * perform the operation on the real X server. This is the purpose * of the FB trap. At the same time we also want to avoid a split, * so that the image will be transferred in a single operation. */ nxagentFBTrap = True; nxagentSplitTrap = True; int result = nxagentSynchronizeDrawableData(pDrawable, breakMask, owner); nxagentSplitTrap = False; nxagentFBTrap = False; if (wait == DO_WAIT && nxagentSplitResource(pDrawable) != NULL) { nxagentWaitDrawable(pDrawable); } #ifdef TEST if (nxagentDrawableStatus(pDrawable) == Synchronized) { fprintf(stderr, "nxagentSynchronizeDrawable: Drawable %s [%p] with id [%ld] now synchronized.\n", nxagentDrawableType(pDrawable), (void *) pDrawable, pDrawable -> id); } else { fprintf(stderr, "nxagentSynchronizeDrawable: Drawable %s [%p] with id [%ld] not fully synchronized.\n", nxagentDrawableType(pDrawable), (void *) pDrawable, pDrawable -> id); } #endif return result; } static int reallySynchronizeDrawableData(DrawablePtr pDrawable) { GCPtr pGC = nxagentGetGraphicContext(pDrawable); if (pGC == NULL) { #ifdef WARNING fprintf(stderr, "%s: WARNING! Failed to get the temporary GC.\n", __func__); #endif return 0; } DrawablePtr pSrcDrawable = (pDrawable -> type == DRAWABLE_PIXMAP ? ((DrawablePtr) nxagentVirtualPixmap((PixmapPtr) pDrawable)) : pDrawable); int width = pDrawable -> width; int height = pDrawable -> height; int depth = pDrawable -> depth; #ifdef TEST fprintf(stderr, "%s: Synchronizing drawable (%s) with geometry [%d][%d][%d].\n", __func__, nxagentDrawableType(pDrawable), width, height, depth); #endif unsigned int format = (depth == 1) ? XYPixmap : ZPixmap; int length = nxagentImageLength(width, height, format, 0, depth); char *data = malloc(length); if (data == NULL) { #ifdef WARNING fprintf(stderr, "%s: WARNING! Failed to allocate memory for the operation.\n", __func__); #endif return 0; } ValidateGC(pDrawable, pGC); fbGetImage(pSrcDrawable, 0, 0, width, height, format, AllPlanes, data); nxagentPutImage(pDrawable, pGC, depth, 0, 0, width, height, 0, format, data); SAFE_free(data); return 1; } int nxagentSynchronizeDrawableData(DrawablePtr pDrawable, unsigned int breakMask, WindowPtr owner) { if (pDrawable -> type == DRAWABLE_PIXMAP) { /* * Synchronize the whole pixmap if we need to download a fresh * copy with lossless compression turned off. */ if (nxagentLosslessTrap) { #ifdef TEST fprintf(stderr, "%s: Forcing synchronization of pixmap at [%p] with lossless compression.\n", __func__, (void *) pDrawable); #endif return reallySynchronizeDrawableData(pDrawable); } else if (nxagentReconnectTrap) { /* * The pixmap data is not synchronized unless we need it. We * noticed we have to reconnect the pixmaps used by the GC's * clip mask. The other data will be synchronized on demand. */ if (pDrawable -> depth == 1) { #ifdef TEST if (nxagentReconnectTrap) { static int totalLength; static int totalReconnectedPixmaps; int width = pDrawable -> width; int height = pDrawable -> height; int depth = pDrawable -> depth; unsigned int format = (depth == 1) ? XYPixmap : ZPixmap; int length = nxagentImageLength(width, height, format, 0, depth); totalLength += length; totalReconnectedPixmaps++; fprintf(stderr, "%s: Reconnecting pixmap at [%p] [%dx%d] " "Depth [%d] Size [%d]. Total size [%d]. Total reconnected pixmaps [%d].\n", __func__, (void *) pDrawable, width, height, depth, length, totalLength, totalReconnectedPixmaps); } #endif return reallySynchronizeDrawableData(pDrawable); } else { #ifdef TEST fprintf(stderr, "%s: Skipping synchronization of pixmap at [%p][%p] during reconnection.\n", __func__, (void *) pDrawable, (void*) nxagentVirtualPixmap((PixmapPtr)pDrawable)); #endif nxagentMarkCorruptedRegion(pDrawable, NullRegion); return 1; } } } /* * By calling this function with the NullRegion as parameter we are * requesting to synchronize the full visible corrupted region of * the drawable. */ return nxagentSynchronizeRegion(pDrawable, NullRegion, breakMask, owner); } /* * If pRegion is NullRegion, all the viewable corrupted region will be * synchronized. */ int nxagentSynchronizeRegion(DrawablePtr pDrawable, RegionPtr pRegion, unsigned int breakMask, WindowPtr owner) { DrawablePtr pSrcDrawable; int leftPad = 0; int success = 0; char *data = NULL; GCPtr pGC = NULL; RegionPtr clipRegion = NullRegion; #ifdef COLLECTED_UPDATES RegionRec collectedUpdates; RegionInit(&collectedUpdates, NullBox, 1); #endif RegionRec exposeRegion; RegionInit(&exposeRegion, NullBox, 1); if (nxagentDrawableBitmap(pDrawable) != NullPixmap && nxagentDrawableStatus((DrawablePtr) nxagentDrawableBitmap(pDrawable)) == Synchronized) { #ifdef TEST fprintf(stderr, "nxagentSynchronizeRegion: WARNING! Drawable [%s] at [%p] has an already synchronized " "bitmap at [%p].\n", nxagentDrawableType(pDrawable), (void *) pDrawable, (void *) nxagentDrawableBitmap(pDrawable)); #endif nxagentDestroyDrawableBitmap(pDrawable); } /* * The stored bitmap may be used if we are going to synchronize the * full drawable. */ Bool useStoredBitmap = (nxagentDrawableBitmap(pDrawable) != NullPixmap && pRegion == NullRegion); if (useStoredBitmap) { #ifdef TEST fprintf(stderr, "nxagentSynchronizeRegion: Drawable [%s] at [%p] has a synchronization bitmap at [%p] " "[%d,%d,%d,%d] with [%ld] rects.\n", nxagentDrawableType(pDrawable), (void *) pDrawable, (void *) nxagentDrawableBitmap(pDrawable), nxagentCorruptedRegion((DrawablePtr) nxagentDrawableBitmap(pDrawable)) -> extents.x1, nxagentCorruptedRegion((DrawablePtr) nxagentDrawableBitmap(pDrawable)) -> extents.y1, nxagentCorruptedRegion((DrawablePtr) nxagentDrawableBitmap(pDrawable)) -> extents.x2, nxagentCorruptedRegion((DrawablePtr) nxagentDrawableBitmap(pDrawable)) -> extents.y2, RegionNumRects(nxagentCorruptedRegion((DrawablePtr) nxagentDrawableBitmap(pDrawable)))); #endif clipRegion = nxagentCreateRegion(pDrawable, NULL, 0, 0, pDrawable -> width, pDrawable -> height); /* * Intersecting the viewable region of the drawable with the * region remaining from a previous loop. */ RegionIntersect(clipRegion, clipRegion, nxagentCorruptedRegion((DrawablePtr) nxagentDrawableBitmap(pDrawable))); /* * The bitmap regions used in the synchronizations are only those * corrupted also on the drawable. In this way, if we put a tile * in a bad position (e.g. if the corrupted region moves), the * next synchronization will fix the error. */ RegionIntersect(clipRegion, clipRegion, nxagentCorruptedRegion(pDrawable)); /* * The bitmap to synchronize is clipped. */ if (RegionNil(clipRegion)) { #ifdef TEST fprintf(stderr, "nxagentSynchronizeRegion: The bitmap region [%d,%d,%d,%d] is not viewable. " "Destroying it.\n", clipRegion -> extents.x1, clipRegion -> extents.y1, clipRegion -> extents.x2, clipRegion -> extents.y2); #endif nxagentDestroyDrawableBitmap(pDrawable); goto nxagentSynchronizeRegionFree; } /* * Using the saved bitmap as source, instead of the drawable * itself. */ pSrcDrawable = ((DrawablePtr) nxagentVirtualPixmap(nxagentDrawableBitmap(pDrawable))); } else { if (pRegion != NullRegion && RegionNil(pRegion)) { #ifdef TEST fprintf(stderr, "nxagentSynchronizeRegion: Region [%d,%d,%d,%d] is nil. Skipping synchronization.\n", pRegion -> extents.x1, pRegion -> extents.y1, pRegion -> extents.x2, pRegion -> extents.y2); #endif goto nxagentSynchronizeRegionFree; } if (nxagentDrawableStatus(pDrawable) == Synchronized) { #ifdef TEST fprintf(stderr, "nxagentSynchronizeRegion: The [%s] at [%p] is already synchronized.\n", nxagentDrawableType(pDrawable), (void *) pDrawable); #endif goto nxagentSynchronizeRegionFree; } /* * Creating a region containing the viewable area of drawable. */ clipRegion = nxagentCreateRegion(pDrawable, NULL, 0, 0, pDrawable -> width, pDrawable -> height); /* * If the corrupted region is not viewable, we can skip the * synchronization. */ RegionIntersect(clipRegion, clipRegion, nxagentCorruptedRegion(pDrawable)); if (RegionNil(clipRegion)) { #ifdef TEST fprintf(stderr, "nxagentSynchronizeRegion: The corrupted region [%d,%d,%d,%d] is not viewable " "on [%s] at [%p]. Skipping the synchronization.\n", clipRegion -> extents.x1, clipRegion -> extents.y1, clipRegion -> extents.x2, clipRegion -> extents.y2, nxagentDrawableType(pDrawable), (void *) pDrawable); #endif goto nxagentSynchronizeRegionFree; } /* * We can skip the synchronization if the requested region is not * corrupted. Specifying a NullRegion as parameter, all the * viewable corrupted region will be synchronized. */ if (pRegion != NullRegion) { RegionIntersect(clipRegion, clipRegion, pRegion); if (RegionNil(clipRegion)) { #ifdef TEST fprintf(stderr, "nxagentSynchronizeRegion: Region requested [%d,%d,%d,%d] already " "synchronized on [%s] at [%p].\n", pRegion -> extents.x1, pRegion -> extents.y1, pRegion -> extents.x2, pRegion -> extents.y2, nxagentDrawableType(pDrawable), (void *) pDrawable); #endif goto nxagentSynchronizeRegionFree; } } pSrcDrawable = (pDrawable -> type == DRAWABLE_PIXMAP ? ((DrawablePtr) nxagentVirtualPixmap((PixmapPtr) pDrawable)) : pDrawable); } #ifdef TEST fprintf(stderr, "nxagentSynchronizeRegion: Synchronizing region with coordinates [%d,%d,%d,%d] " "on [%s] at [%p].\n", clipRegion -> extents.x1, clipRegion -> extents.y1, clipRegion -> extents.x2, clipRegion -> extents.y2, nxagentDrawableType(pDrawable), (void *) pDrawable); #endif int saveTrap = nxagentGCTrap; nxagentGCTrap = False; nxagentFBTrap = True; nxagentSplitTrap = True; pGC = nxagentGetGraphicContext(pDrawable); if (pGC == NULL) { #ifdef WARNING fprintf(stderr, "nxagentSynchronizeRegion: WARNING! Failed to create the temporary GC.\n"); #endif goto nxagentSynchronizeRegionFree; } ValidateGC(pDrawable, pGC); #ifdef TEST fprintf(stderr, "nxagentSynchronizeRegion: Going to synchronize [%ld] rects of [%s] at [%p].\n", RegionNumRects(clipRegion), nxagentDrawableType(pDrawable), (void *) pDrawable); fprintf(stderr, "nxagentSynchronizeRegion: Extents geometry [%d,%d,%d,%d].\n", clipRegion -> extents.x1, clipRegion -> extents.y1, clipRegion -> extents.x2, clipRegion -> extents.y2); fprintf(stderr, "nxagentSynchronizeRegion: Drawable geometry [%d,%d,%d,%d].\n", pDrawable -> x, pDrawable -> y, pDrawable -> width, pDrawable -> height); #endif /* * We are going to synchronize the corrupted area, so we use the * corrupted extents as maximum size of the image data. It's * important to avoid using the drawable size, because in case of a * huge window it had to result in a failed data memory allocation. */ int w, h, extentWidth, extentHeight, tileWidth, tileHeight; extentWidth = clipRegion -> extents.x2 - clipRegion -> extents.x1; extentHeight = clipRegion -> extents.y2 - clipRegion -> extents.y1; w = tileWidth = (nxagentOption(TileWidth) > extentWidth ? extentWidth : nxagentOption(TileWidth)); h = tileHeight = (nxagentOption(TileHeight) > extentHeight ? extentHeight : nxagentOption(TileHeight)); #ifdef DEBUG fprintf(stderr, "nxagentSynchronizeRegion: Using tiles of size [%dx%d].\n", tileWidth, tileHeight); #endif int length, format; data = nxagentAllocateImageData(w, h, pDrawable -> depth, &length, &format); if (data == NULL) { #ifdef WARNING fprintf(stderr, "nxagentSynchronizeRegion: WARNING! Failed to allocate memory for synchronization.\n"); /* * Print detailed information if the image length is zero. */ if (length == 0) { fprintf(stderr, "nxagentSynchronizeRegion: Drawable [%s] at [%p] with region geometry [%d][%d,%d,%d,%d].\n", nxagentDrawableType(pDrawable), (void *) pDrawable, RegionNumRects(clipRegion), clipRegion -> extents.x1, clipRegion -> extents.y1, clipRegion -> extents.x2, clipRegion -> extents.y2); } #endif goto nxagentSynchronizeRegionFree; } #ifndef USE_OPTIMIZED_BOXES BoxPtr pBox = RegionRects(clipRegion); #else BoxPtr pBox = nxagentGetOptimizedRegionBoxes(clipRegion); #endif /* USE_OPTIMIZED_BOXES */ int nBox = RegionNumRects(clipRegion); unsigned long now = GetTimeInMillis(); nxagentSynchronization.abort = False; /* * Going to split the updated region into small blocks. */ for (int i = 0; i < nBox; i++) { #ifdef USE_OPTIMIZED_BOXES if (pBox[i].x1 == 0 && pBox[i].y1 == 0 && pBox[i].x2 == 0 && pBox[i].y2 == 0) { continue; } #endif BoxRec box = pBox[i]; for (int y = box.y1; y < box.y2; y += h) { h = min(box.y2 - y, tileHeight); for (int x = box.x1; x < box.x2; x += w) { w = min(box.x2 - x, tileWidth); /* * FIXME: This should not occur. */ if (nxagentDrawableStatus(pDrawable) == Synchronized) { #ifdef WARNING if (pDrawable -> type == DRAWABLE_WINDOW && pSrcDrawable != pDrawable) { fprintf(stderr, "nxagentSynchronizeRegion: WARNING! Trying to synchronize " "the clean drawable type [%d] at [%p] with source at [%p].\n", pDrawable -> type, (void *) pDrawable, (void *) pSrcDrawable); } #endif goto nxagentSynchronizeRegionStop; } if (canBreakOnTimeout(breakMask)) { /* * Abort the synchronization loop if it lasts for more than * DeferTimeout milliseconds. */ unsigned long elapsedTime = GetTimeInMillis() - now; if (elapsedTime > nxagentOption(DeferTimeout)) { #ifdef TEST fprintf(stderr, "nxagentSynchronizeRegion: Synchronization break with " "[%lu] ms elapsed.\n", elapsedTime); #endif nxagentSynchronization.abort = True; goto nxagentSynchronizeRegionStop; } } /* * Abort the loop if we go out of bandwidth. */ if (breakOnCongestionDrawable(breakMask, pDrawable) == 1) { #ifdef TEST fprintf(stderr, "nxagentSynchronizeRegion: Synchronization break with " "congestion [%d] blocking [%d].\n", nxagentCongestion, nxagentBlocking); #endif nxagentSynchronization.abort = True; goto nxagentSynchronizeRegionStop; } /* * Abort the loop if the display blocks. */ if (breakOnBlocking(breakMask) == 1) { #ifdef TEST fprintf(stderr, "nxagentSynchronizeRegion: Synchronization break with " "blocking [%d] congestion [%d].\n", nxagentBlocking, nxagentCongestion); #endif nxagentSynchronization.abort = True; goto nxagentSynchronizeRegionStop; } BoxRec tileBox = {.x1 = x, .y1 = y, .x2 = x + w, .y2 = y + h}; #ifdef DEBUG fprintf(stderr, "nxagentSynchronizeRegion: Going to synchronize tile [%d,%d,%d,%d].\n", tileBox.x1, tileBox.y1, tileBox.x2, tileBox.y2); #endif nxagentGetImage(pSrcDrawable, x, y, w, h, format, AllPlanes, data); /* * Going to unmark the synchronized region. */ RegionRec tileRegion; RegionInit(&tileRegion, &tileBox, 1); RegionUnion(&exposeRegion, &exposeRegion, &tileRegion); #ifdef COLLECTED_UPDATES RegionAppend(&collectedUpdates, &tileRegion); #endif if (useStoredBitmap != 0) { /* * When a bitmap's tile is synchronized, we can clear the * corresponding region. We can't use the * nxagentUnmarkCorruptedRegion because we have not a * resource associated to this pixmap. */ RegionSubtract(nxagentPixmapCorruptedRegion(nxagentDrawableBitmap(pDrawable)), nxagentPixmapCorruptedRegion(nxagentDrawableBitmap(pDrawable)), &tileRegion); /* * The drawable's corrupted region can be cleared if the * bitmap's tile data matches the drawable's content at the * same position. */ if (nxagentDrawableStatus(pDrawable) == NotSynchronized) { int cmpLength, cmpFormat; char *cmpData = nxagentAllocateImageData(w, h, pDrawable -> depth, &cmpLength, &cmpFormat); if (cmpData != NULL) { nxagentGetImage(pDrawable, x, y, w, h, format, AllPlanes, cmpData); if (memcmp(data, cmpData, cmpLength) == 0) { #ifdef TEST fprintf(stderr, "nxagentSynchronizeRegion: Tile [%d,%d,%d,%d] matches drawable's data at same position.\n", x, y, x + w, y + h); #endif nxagentUnmarkCorruptedRegion(pDrawable, &tileRegion); } #ifdef TEST else { fprintf(stderr, "nxagentSynchronizeRegion: Tile [%d,%d,%d,%d] on drawable [%p] doesn't match.\n", x, y, x + w, y + h, (void *) pDrawable); } #endif } else { #ifdef WARNING fprintf(stderr, "nxagentSynchronizeRegion: WARNING! Failed to allocate memory to compare tiles.\n"); #endif } SAFE_free(cmpData); } } else { nxagentUnmarkCorruptedRegion(pDrawable, &tileRegion); if (nxagentDrawableBitmap(pDrawable) != NullPixmap) { #ifdef TEST fprintf(stderr, "nxagentSynchronizeRegion: Going to clean bitmap at [%p] with newer data.\n", (void *) nxagentDrawableBitmap(pDrawable)); #endif RegionSubtract(nxagentPixmapCorruptedRegion(nxagentDrawableBitmap(pDrawable)), nxagentPixmapCorruptedRegion(nxagentDrawableBitmap(pDrawable)), &tileRegion); } } /* * Realize the image after comparing the source data with the * bitmap data. */ nxagentRealizeImage(pDrawable, pGC, pDrawable -> depth, x, y, w, h, leftPad, format, data); RegionUninit(&tileRegion); #if !defined(COLLECTED_UPDATES) if (owner != NULL) { if (nxagentOption(Shadow) && (nxagentOption(XRatio) != DONT_SCALE || nxagentOption(YRatio) != DONT_SCALE)) { int scaledx = nxagentScale(x, nxagentOption(XRatio)); int scaledy = nxagentScale(y, nxagentOption(YRatio)); int scaledw = nxagentScale(x + w, nxagentOption(XRatio)) - scaledx; int scaledh = nxagentScale(y + h, nxagentOption(YRatio)) - scaledy; XClearArea(nxagentDisplay, nxagentWindow(owner), scaledx, scaledy, scaledw, scaledh, 0); } else { XClearArea(nxagentDisplay, nxagentWindow(owner), x, y, w, h, 0); } } #endif /* #if !defined(COLLECTED_UPDATES) */ /* * Abort the loop on the user's input. This is done here to * check for events read after the flush caused by the * PutImage. */ nxagentDispatchHandler((ClientPtr) 0, 0, 0); if (breakOnEvent(breakMask) == 1) { #ifdef TEST fprintf(stderr, "nxagentSynchronizeRegion: Synchronization break with " "new input events.\n"); #endif nxagentSynchronization.abort = True; goto nxagentSynchronizeRegionStop; } } } } nxagentSynchronizeRegionStop: nxagentSplitTrap = False; nxagentFBTrap = False; nxagentGCTrap = saveTrap; success = 1; if (!nxagentOption(Shadow)) { if (nxagentSynchronization.abort) { /* * Storing the pointer to the drawable we were synchronizing * when the loop aborted. It is used in * nxagentSynchronizeDrawablePredicate. */ nxagentSynchronization.pDrawable = pDrawable; nxagentSynchronization.drawableType = pDrawable -> type; if (nxagentDrawableBitmap(pDrawable) == NullPixmap) { #ifdef TEST fprintf(stderr, "nxagentSynchronizeRegion: Going to create the synchronization bitmap.\n"); #endif nxagentCreateDrawableBitmap(pDrawable); } } else { if (nxagentDrawableBitmap(pDrawable) != NullPixmap) { #ifdef TEST fprintf(stderr, "nxagentSynchronizeRegion: Synchronization loop finished. Going to destroy synchronization bitmap.\n"); #endif nxagentDestroyDrawableBitmap(pDrawable); } } if (pDrawable -> type == DRAWABLE_PIXMAP && nxagentIsCorruptedBackground((PixmapPtr) pDrawable) == 1 && !RegionNil(&exposeRegion)) { struct nxagentExposeBackground eb = { .pBackground = (PixmapPtr) pDrawable, .pExpose = &exposeRegion, }; for (int i = 0; i < MAXCLIENTS; i++) { if (clients[i] != NULL) { FindClientResourcesByType(clients[i], RT_WINDOW, nxagentExposeBackgroundPredicate, &eb); } } } } #ifdef COLLECTED_UPDATES else { if (owner != NULL) { int overlap = 0; RegionValidate(&collectedUpdates, &overlap); for (int i = 0; i < RegionNumRects(&collectedUpdates); i++) { int x = RegionRects(&collectedUpdates)[i].x1; int y = RegionRects(&collectedUpdates)[i].y1; int w = RegionRects(&collectedUpdates)[i].x2 - RegionRects(&collectedUpdates)[i].x1; int h = RegionRects(&collectedUpdates)[i].y2 - RegionRects(&collectedUpdates)[i].y1; if (nxagentOption(Shadow) && (nxagentOption(XRatio) != DONT_SCALE || nxagentOption(YRatio) != DONT_SCALE)) { int scaledx = nxagentScale(x, nxagentOption(XRatio)); int scaledy = nxagentScale(y, nxagentOption(YRatio)); int scaledw = nxagentScale(x + w, nxagentOption(XRatio)) - scaledx; int scaledh = nxagentScale(y + h, nxagentOption(YRatio)) - scaledy; XClearArea(nxagentDisplay, nxagentWindow(owner), scaledx, scaledy, scaledw, scaledh, 0); } else { XClearArea(nxagentDisplay, nxagentWindow(owner), x, y, w, h, 0); } } } } #endif /* #ifdef COLLECTED_UPDATES */ nxagentSynchronizeRegionFree: if (clipRegion != NullRegion) { nxagentFreeRegion(clipRegion); } SAFE_free(data); RegionUninit(&exposeRegion); #ifdef COLLECTED_UPDATES RegionUninit(&collectedUpdates); #endif /* #ifdef COLLECTED_UPDATES */ return success; } void nxagentSynchronizeBox(DrawablePtr pDrawable, BoxPtr pBox, unsigned int breakMask) { if (nxagentDrawableStatus(pDrawable) == Synchronized) { #ifdef TEST fprintf(stderr, "nxagentSynchronizeBox: The [%s] at [%p] is already synchronized.\n", nxagentDrawableType(pDrawable), (void *) pDrawable); #endif return; } if (pBox == NullBox) { #ifdef TEST fprintf(stderr, "nxagentSynchronizeBox: Going to synchronize the whole [%s] at [%p].\n", nxagentDrawableType(pDrawable), (void *) pDrawable); #endif nxagentSynchronizeRegion(pDrawable, NullRegion, breakMask, NULL); } else { #ifdef TEST fprintf(stderr, "nxagentSynchronizeBox: Going to create a region from box [%d,%d,%d,%d].\n", pBox -> x1, pBox -> y1, pBox -> x2, pBox -> y2); #endif RegionPtr pRegion = nxagentCreateRegion(pDrawable, NULL, pBox -> x1, pBox -> y1, pBox -> x2 - pBox -> x1, pBox -> y2 - pBox -> y1); if (RegionNil(pRegion)) { #ifdef TEST fprintf(stderr, "nxagentSynchronizeBox: Resulting region [%d,%d,%d,%d] is nil. Skipping synchronization.\n", pRegion -> extents.x1, pRegion -> extents.y1, pRegion -> extents.x2, pRegion -> extents.y2); #endif nxagentFreeRegion(pRegion); return; } #ifdef TEST fprintf(stderr, "nxagentSynchronizeBox: Going to synchronize the region [%d,%d,%d,%d] of " "[%s] at [%p].\n", pRegion -> extents.x1, pRegion -> extents.y1, pRegion -> extents.x2, pRegion -> extents.y2, nxagentDrawableType(pDrawable), (void *) pDrawable); #endif nxagentSynchronizeRegion(pDrawable, pRegion, breakMask, NULL); nxagentFreeRegion(pRegion); } } void nxagentSynchronizeDrawablePredicate(void *p0, XID x1, void *p2) { DrawablePtr pDrawable = (DrawablePtr) p0; unsigned int *breakMask = (unsigned int *) p2; Bool shouldClearHiddenRegion = True; /* * The nxagentSynchronization.abort propagates a break condition * across the resources loop, in order to block also the subsequent * synchronizations. */ if (nxagentSynchronization.abort || nxagentDrawableStatus(pDrawable) == Synchronized) { return; } /* * In order to implement a kind of round-robin synchronization, the * previous incomplete drawable synchronization is saved to jump to * the next resource available of same type. */ if (nxagentSynchronization.pDrawable != NULL && pDrawable -> type == nxagentSynchronization.drawableType) { if (nxagentSynchronization.pDrawable != pDrawable) { #ifdef TEST fprintf(stderr, "nxagentSynchronizeDrawablePredicate: Skipping drawable [%s][%p] while looking " "for last synchronized drawable [%p].\n", nxagentDrawableType(pDrawable), (void *) pDrawable, (void *) nxagentSynchronization.pDrawable); #endif return; } else { #ifdef TEST fprintf(stderr, "nxagentSynchronizeDrawablePredicate: Last synchronized drawable [%p] found. " "Skipping to the next resource.\n", (void *) nxagentSynchronization.pDrawable); #endif nxagentSynchronization.pDrawable = NULL; return; } } if (pDrawable -> type == DRAWABLE_PIXMAP) { /* * The pixmaps to be synchronized are those used as background or * used as source of any deferred operations for at least 2 times. */ if (NXAGENT_SHOULD_SYNCHRONIZE_PIXMAP(pDrawable) == 0) { #ifdef TEST fprintf(stderr, "nxagentSynchronizeDrawablePredicate: Skipping pixmap at [%p] " "with usage [%d] background [%d].\n", (void *) pDrawable, nxagentPixmapUsageCounter((PixmapPtr) pDrawable), nxagentIsCorruptedBackground((PixmapPtr) pDrawable)); #endif return; } #ifdef TEST else { fprintf(stderr, "nxagentSynchronizeDrawablePredicate: Synchronizing pixmap at [%p] " "with usage [%d] background [%d].\n", (void *) pDrawable, nxagentPixmapUsageCounter((PixmapPtr) pDrawable), nxagentIsCorruptedBackground((PixmapPtr) pDrawable)); } #endif } else if (NXAGENT_SHOULD_SYNCHRONIZE_WINDOW(pDrawable) == 0) { #ifdef TEST fprintf(stderr, "nxagentSynchronizeDrawablePredicate: Skipping not visible window at [%p].\n", (void *) pDrawable); #endif if (shouldClearHiddenRegion) { #ifdef TEST fprintf(stderr, "nxagentSynchronizeDrawablePredicate: Clearing out the not visible window " "at [%p].\n", (void *) pDrawable); #endif nxagentCleanCorruptedDrawable(pDrawable); } return; } /* * Postpone the synchronization if we went out of bandwidth or if * the display blocks. The pixmap synchronization is more careful * with bandwidth usage. */ /* FIXME: This condition sounds only as a complication, as the break parameters are already checked while synchronizing the drawable. if (breakOnCongestion(*breakMask) == 1 || (pDrawable -> type == DRAWABLE_PIXMAP && *breakMask != NEVER_BREAK && nxagentCongestion > 0)) { #ifdef TEST fprintf(stderr, "nxagentSynchronizeDrawablePredicate: WARNING! Breaking the " "synchronization with congestion [%d] blocking [%d].\n", nxagentCongestion, nxagentBlocking); #endif nxagentSynchronization.abort = True; return; } */ #ifdef TEST fprintf(stderr, "nxagentSynchronizeDrawablePredicate: Synchronizing drawable [%s][%p] " "with geometry (%dx%d).\n", nxagentDrawableType(pDrawable), (void *) pDrawable, pDrawable -> width, pDrawable -> height); fprintf(stderr, "nxagentSynchronizeDrawablePredicate: Corrupted extents [%d,%d,%d,%d] " "with [%ld] rects.\n", nxagentCorruptedRegion(pDrawable) -> extents.x1, nxagentCorruptedRegion(pDrawable) -> extents.y1, nxagentCorruptedRegion(pDrawable) -> extents.x2, nxagentCorruptedRegion(pDrawable) -> extents.y2, RegionNumRects(nxagentCorruptedRegion(pDrawable))); #endif /* * The stored bitmap is destroyed inside the synchronization loop, * so we have to check here its presence to know if we can clear the * dirty windows. */ shouldClearHiddenRegion = (nxagentDrawableBitmap(pDrawable) == NullPixmap); nxagentSynchronizeDrawable(pDrawable, DONT_WAIT, *breakMask, NULL); if (nxagentDrawableStatus(pDrawable) == NotSynchronized) { #ifdef TEST fprintf(stderr, "nxagentSynchronizeDrawablePredicate: Drawable [%s][%p] not fully synchronized.\n", nxagentDrawableType(pDrawable), (void *) pDrawable); #endif /* * If the remaining corrupted region is on an hidden section (not * viewable or outside of the pixmap's area) of a drawable, we can * clear it. */ if (!nxagentSynchronization.abort && shouldClearHiddenRegion) { #ifdef TEST fprintf(stderr, "nxagentSynchronizeDrawablePredicate: Clearing out the remaining corrupted " "[%s] at [%p].\n", nxagentDrawableType(pDrawable), (void *) pDrawable); #endif nxagentCleanCorruptedDrawable(pDrawable); } } } void nxagentSynchronizationLoop(unsigned int mask) { /* FIXME: All drawables should be set as synchronized and never marked as corrupted while the display is down. */ nxagentSkipImage = nxagentTooManyImageData(); if (nxagentOption(ImageRateLimit) && nxagentSkipImage) { #ifdef TEST fprintf(stderr, "nxagentSynchronizeDrawable: Skipping due to bit rate limit reached.\n"); #endif return; } if (NXDisplayError(nxagentDisplay) == 1) { #ifdef TEST fprintf(stderr, "nxagentSynchronizationLoop: WARNING! Not synchronizing the drawables " "with the display down.\n"); #endif return; } #ifdef TEST fprintf(stderr, "nxagentSynchronizationLoop: Synchronizing [%d] windows [%d] pixmaps " "and [%d] backgrounds with mask [%u].\n", nxagentCorruptedWindows, nxagentCorruptedPixmaps, nxagentCorruptedBackgrounds, mask); fprintf(stderr, "nxagentSynchronizationLoop: Stored bitmaps [%d] windows [%d] pixmaps " "and [%d] backgrounds.\n", nxagentSynchronization.windowBitmaps, nxagentSynchronization.pixmapBitmaps, nxagentSynchronization.backgroundBitmaps); fprintf(stderr, "nxagentSynchronizationLoop: Starting loops with congestion [%d] " "blocking [%d].\n", nxagentCongestion, nxagentBlocking); #endif unsigned int breakMask = mask; /* * The resource counter can be reset if we have not aborted the * synchronization loop, if we are not skipping resources to do * round-robin and if the bitmaps are all synchronized. */ Bool doRoundRobin = (nxagentSynchronization.pDrawable != NULL); nxagentSynchronization.abort = False; /* * Synchronize the windows. */ if (NXAGENT_SHOULD_SYNCHRONIZE_CORRUPTED_WINDOWS(mask)) { #ifdef TEST fprintf(stderr, "nxagentSynchronizationLoop: Going to loop through corrupted window resources.\n"); #endif FindClientResourcesByType(clients[serverClient -> index], RT_NX_CORR_WINDOW, nxagentSynchronizeDrawablePredicate, &breakMask); #ifdef TEST if (!nxagentSynchronization.abort && nxagentSynchronization.windowBitmaps == 0 && !doRoundRobin) { if (nxagentCorruptedWindows > 0) { fprintf(stderr, "nxagentSynchronizationLoop: Closing the loop with [%d] " "corrupted windows.\n", nxagentCorruptedWindows); } nxagentCorruptedWindows = 0; } #endif } /* * Synchronize the backgrounds. */ if (!nxagentSynchronization.abort && NXAGENT_SHOULD_SYNCHRONIZE_CORRUPTED_BACKGROUNDS(mask)) { #ifdef TEST fprintf(stderr, "nxagentSynchronizationLoop: Going to loop through corrupted background resources.\n"); #endif FindClientResourcesByType(clients[serverClient -> index], RT_NX_CORR_BACKGROUND, nxagentSynchronizeDrawablePredicate, &breakMask); #ifdef TEST if (!nxagentSynchronization.abort && nxagentSynchronization.backgroundBitmaps == 0 && !doRoundRobin) { if (nxagentCorruptedBackgrounds > 0) { fprintf(stderr, "nxagentSynchronizationLoop: Closing the loop with [%d] " "corrupted backgrounds.\n", nxagentCorruptedBackgrounds); } nxagentCorruptedBackgrounds = 0; } #endif } /* * If there is bandwidth remaining, synchronize the * pixmaps. Synchronizing a pixmap doesn't produce any visible * results. Better is to synchronize them on demand, before using * the pixmap in a copy or in a composite operation. */ if (!nxagentSynchronization.abort && NXAGENT_SHOULD_SYNCHRONIZE_CORRUPTED_PIXMAPS(mask)) { #ifdef TEST fprintf(stderr, "nxagentSynchronizationLoop: Going to loop through corrupted pixmap resources.\n"); #endif FindClientResourcesByType(clients[serverClient -> index], RT_NX_CORR_PIXMAP, nxagentSynchronizeDrawablePredicate, &breakMask); if (!nxagentSynchronization.abort && nxagentSynchronization.pixmapBitmaps == 0 && !doRoundRobin) { #ifdef TEST if (nxagentCorruptedPixmaps > 0) { fprintf(stderr, "nxagentSynchronizationLoop: Closing the loop with [%d] " "corrupted pixmaps.\n", nxagentCorruptedPixmaps); } #endif nxagentCorruptedPixmaps = 0; } } /* * If the last synchronized drawable has been removed, we have to * reset the variable sto- ring its pointer. */ if (nxagentSynchronization.pDrawable != NULL && !nxagentFindClientResource(serverClient -> index, RT_NX_CORR_WINDOW, nxagentSynchronization.pDrawable) && !nxagentFindClientResource(serverClient -> index, RT_NX_CORR_BACKGROUND, nxagentSynchronization.pDrawable) && !nxagentFindClientResource(serverClient -> index, RT_NX_CORR_PIXMAP, nxagentSynchronization.pDrawable)) { #ifdef TEST fprintf(stderr, "nxagentSynchronizationLoop: Synchronization drawable [%p] removed from resources.\n", (void *) nxagentSynchronization.pDrawable); #endif nxagentSynchronization.pDrawable = NULL; } #ifdef TEST fprintf(stderr, "nxagentSynchronizationLoop: Closing loops with congestion [%d] " "blocking [%d].\n", nxagentCongestion, nxagentBlocking); fprintf(stderr, "nxagentSynchronizationLoop: There are now [%d] windows [%d] pixmaps " "and [%d] backgrounds to synchronize.\n", nxagentCorruptedWindows, nxagentCorruptedPixmaps, nxagentCorruptedBackgrounds); #endif } RegionPtr nxagentCreateRegion(DrawablePtr pDrawable, GCPtr pGC, int x, int y, int width, int height) { BoxRec box = {.x1 = x, .y1 = y, .x2 = x + width, .y2 = y + height}; RegionPtr pRegion = RegionCreate(&box, 1); /* * Clipping the region. */ if (pDrawable -> type == DRAWABLE_PIXMAP) { /* * The region created doesn't need to be clipped if it has the * pixmap dimensions. */ if (x != 0 || y != 0 || width != pDrawable -> width || height != pDrawable -> height) { BoxRec tmpBox = {.x1 = 0, .y1 = 0, .x2 = pDrawable -> width, .y2 = pDrawable -> height}; RegionRec tmpRegion; RegionInit(&tmpRegion, &tmpBox, 1); RegionIntersect(pRegion, &tmpRegion, pRegion); RegionUninit(&tmpRegion); } } else { /* * We use the clipList because the borderClip contains also parts * of the window covered by its children. */ RegionTranslate(pRegion, pDrawable -> x, pDrawable -> y); if (nxagentWindowPriv((WindowPtr) pDrawable) -> hasTransparentChildren == 1) { RegionIntersect(pRegion, pRegion, &((WindowPtr) pDrawable) -> borderClip); } else { RegionIntersect(pRegion, pRegion, &((WindowPtr) pDrawable) -> clipList); } RegionTranslate(pRegion, -pDrawable -> x, -pDrawable -> y); } #ifdef TEST fprintf(stderr, "nxagentCreateRegion: New region created with coordinates [%d,%d,%d,%d].\n", pRegion -> extents.x1, pRegion -> extents.y1, pRegion -> extents.x2, pRegion -> extents.y2); #endif /* * If the pRegion is NIL we don't need to intersect it with the GC's * clipmask. */ if (!RegionNil(pRegion) && pGC != NULL && pGC -> clientClip != NULL && pGC -> clientClipType == CT_REGION) { RegionRec clipRegion; RegionInit(&clipRegion, NullBox, 1); RegionCopy(&clipRegion, (RegionPtr) pGC -> clientClip); /* * The clip origin is relative to the origin of the destination * drawable. The clip mask coor- dinates are relative to the clip * origin. */ if (pGC -> clipOrg.x != 0 || pGC -> clipOrg.y != 0) { RegionTranslate(&clipRegion, pGC -> clipOrg.x, pGC -> clipOrg.y); } #ifdef TEST fprintf(stderr, "nxagentCreateRegion: Clipping region to the clip mask with coordinates [%d,%d,%d,%d].\n", clipRegion.extents.x1, clipRegion.extents.y1, clipRegion.extents.x2, clipRegion.extents.y2); #endif RegionIntersect(pRegion, pRegion, &clipRegion); RegionUninit(&clipRegion); } return pRegion; } void nxagentMarkCorruptedRegion(DrawablePtr pDrawable, RegionPtr pRegion) { if (pRegion != NullRegion && RegionNil(pRegion)) { #ifdef TEST fprintf(stderr, "nxagentMarkCorruptedRegion: Region [%d,%d,%d,%d] is nil. Skipping operation.\n", pRegion -> extents.x1, pRegion -> extents.y1, pRegion -> extents.x2, pRegion -> extents.y2); #endif return; } /* * If the drawable was synchronized, the counter reporting the * number of corrupted drawables must be increased. Moreover the * corrupted ti- mestamp must be set. */ if (nxagentDrawableStatus(pDrawable) == Synchronized) { if (pDrawable -> type == DRAWABLE_WINDOW) { nxagentAllocateCorruptedResource(pDrawable, RT_NX_CORR_WINDOW); } nxagentSetCorruptedTimestamp(pDrawable); } if (pRegion == NullRegion) { int x = 0; int y = 0; int width = pDrawable -> width; int height = pDrawable -> height; #ifdef TEST fprintf(stderr, "nxagentMarkCorruptedRegion: Fully invalidating %s [%p] with " "coordinates [%d,%d][%d,%d].\n", nxagentDrawableType(pDrawable), (void *) pDrawable, x, y, width, height); #endif pRegion = nxagentCreateRegion(pDrawable, NULL, x, y, width, height); nxagentValidateSplit(pDrawable, pRegion); RegionUnion(nxagentCorruptedRegion(pDrawable), nxagentCorruptedRegion(pDrawable), pRegion); nxagentFreeRegion(pRegion); } else { #ifdef TEST int x = pRegion -> extents.x1; int y = pRegion -> extents.y1; int width = pRegion -> extents.x2 - pRegion -> extents.x1; int height = pRegion -> extents.y2 - pRegion -> extents.y1; fprintf(stderr, "nxagentMarkCorruptedRegion: Partly invalidating %s [%p] with " "coordinates [%d,%d][%d,%d].\n", nxagentDrawableType(pDrawable), (void *) pDrawable, x, y, width, height); #endif nxagentValidateSplit(pDrawable, pRegion); RegionUnion(nxagentCorruptedRegion(pDrawable), nxagentCorruptedRegion(pDrawable), pRegion); } } void nxagentUnmarkCorruptedRegion(DrawablePtr pDrawable, RegionPtr pRegion) { if (pRegion != NullRegion && RegionNil(pRegion)) { #ifdef TEST fprintf(stderr, "nxagentUnmarkCorruptedRegion: Region [%d,%d,%d,%d] is nil. Skipping operation.\n", pRegion -> extents.x1, pRegion -> extents.y1, pRegion -> extents.x2, pRegion -> extents.y2); #endif return; } int oldStatus = nxagentDrawableStatus(pDrawable); if (oldStatus == Synchronized) { #ifdef TEST fprintf(stderr, "nxagentUnmarkCorruptedRegion: Drawable %s [%p] already synchronized.\n", nxagentDrawableType(pDrawable), (void *) pDrawable); #endif return; } if (pRegion == NullRegion) { #ifdef TEST fprintf(stderr, "nxagentUnmarkCorruptedRegion: Fully validating %s [%p].\n", nxagentDrawableType(pDrawable), (void *) pDrawable); #endif nxagentValidateSplit(pDrawable, NULL); RegionEmpty(nxagentCorruptedRegion(pDrawable)); } else { #ifdef TEST fprintf(stderr, "nxagentUnmarkCorruptedRegion: Validating %s [%p] with region [%d,%d,%d,%d].\n", nxagentDrawableType(pDrawable), (void *) pDrawable, pRegion -> extents.x1, pRegion -> extents.y1, pRegion -> extents.x2, pRegion -> extents.y2); #endif nxagentValidateSplit(pDrawable, pRegion); RegionSubtract(nxagentCorruptedRegion(pDrawable), nxagentCorruptedRegion(pDrawable), pRegion); } /* * If the drawable becomes synchronized, the counter reporting the * number of corrupted drawables must be decreased. Moreover the * corrupted timestamp must be reset. * Note: oldstatus has been checked above and is always * "NotSynchronized" here. */ if (/*oldStatus == NotSynchronized &&*/ nxagentDrawableStatus(pDrawable) == Synchronized) { if (pDrawable -> type == DRAWABLE_PIXMAP) { nxagentDestroyCorruptedResource(pDrawable, RT_NX_CORR_BACKGROUND); nxagentDestroyCorruptedResource(pDrawable, RT_NX_CORR_PIXMAP); nxagentPixmapPriv(nxagentRealPixmap((PixmapPtr) pDrawable)) -> containTrapezoids = 0; } else { nxagentDestroyCorruptedResource(pDrawable, RT_NX_CORR_WINDOW); } nxagentResetCorruptedTimestamp(pDrawable); /* * If the resource is no longer dirty, the associated bitmap is * destroyed. */ if (nxagentDrawableBitmap(pDrawable) != NullPixmap) { nxagentDestroyDrawableBitmap(pDrawable); } } } void nxagentMoveCorruptedRegion(WindowPtr pWin, unsigned int mask) { /* * If a window is resized, its corrupted region is moved according * to the bit gravity. */ if (nxagentDrawableStatus((DrawablePtr) pWin) == NotSynchronized) { if (((mask & CWHeight) && nxagentWindowPriv(pWin) -> height != pWin -> drawable.height) || ((mask & CWWidth) && nxagentWindowPriv(pWin) -> width != pWin -> drawable.width)) { int nx, ny; GravityTranslate(0, 0, nxagentWindowPriv(pWin) -> x - pWin -> origin.x + wBorderWidth(pWin), nxagentWindowPriv(pWin) -> y - pWin -> origin.y + wBorderWidth(pWin), pWin -> drawable.width - nxagentWindowPriv(pWin) -> width, pWin -> drawable.height - nxagentWindowPriv(pWin) -> height, pWin -> bitGravity, &nx, &ny); #ifdef TEST fprintf(stderr, "nxagentMoveCorruptedRegion: Moving the corrupted region to [%d,%d] for window [%p].\n", nx, ny, (void *) pWin); #endif RegionTranslate(nxagentCorruptedRegion((DrawablePtr) pWin), nx, ny); /* * Having moved the corrupted region, we need to invalidate the * pending commits or otherwise the image will fall in the wrong * area. */ nxagentValidateSplit((DrawablePtr) pWin, NULL); /* * The window reconfiguration invalidates the synchronization * bitmap. */ nxagentDestroyDrawableBitmap((DrawablePtr) pWin); } } } /* * The DDX layer uses an 'Y-X banding' representation of regions: it * sorts all rectangles composing a region using first the * y-dimension, than the x-dimension; moreover it organizes the * rectangles in 'bands' sharing the same y-dimension. This * representation does not minimize the number of rectangles. For * example, the following region has 4 rectangles: * * +-----------+ * | | +---+ * | A | | B | * | | +---+ * +-----------+ * * The rectangle 'B' creates a band which splits the rectangle A in 3 * parts, for a total of 3 bands. The number of rectangles composing * the region is 4. * * This kind of representation is not advisable for the lazy * synchronization because, in the example above, the nxagent had to * send 4 put images instead of 2. * * To minimize the problem we use the following function: by * traversing the list of rectangles we merge all boxes with same x * coordinates and coincident y, in order to create an X-Y banding. * * Be careful: all the coordinates of boxes merged are set to 0, so * take care of this when looping through the box list returned by * this function. */ BoxPtr nxagentGetOptimizedRegionBoxes(RegionPtr pRegion) { BoxRec boxExtents; BoxPtr pBox = RegionRects(pRegion); int nBox = RegionNumRects(pRegion); #ifdef TEST fprintf(stderr, "nxagentGetOptimizedRegionBoxes: Going to optimize region at [%p] with [%d] rects.\n", (void *) pRegion, nBox); #endif if (nBox <= 1) { return pBox; } #ifdef DEBUG int nBoxOptim = nBox; #endif /* * The boxes are now grouped to grown as much as possible, using * their overlapping vertex as rule. */ for (int i = 0; i < nBox; i++) { /* * If the coordinates are (0,0) the box has been already merged, * so we can skip it. */ if (pBox[i].x1 == 0 && pBox[i].y1 == 0 && pBox[i].x2 == 0 && pBox[i].y2 == 0) { continue; } #ifdef DEBUG fprintf(stderr, "nxagentGetOptimizedRegionBoxes: Referential box [%d] has coordinates [%d,%d,%d,%d].\n", i, pBox[i].x1, pBox[i].y1, pBox[i].x2, pBox[i].y2); #endif #ifdef ADVANCED_BOXES_DEFRAG boxExtents.x1 = pBox[i].x1; boxExtents.y1 = pBox[i].y1; boxExtents.x2 = pBox[i].x2; #endif boxExtents.y2 = pBox[i].y2; for (int j = i+1; j < nBox; j++) { if (pBox[j].x1 == 0 && pBox[j].y1 == 0 && pBox[j].x2 == 0 && pBox[j].y2 == 0) { continue; } #ifdef DEBUG fprintf(stderr, "nxagentGetOptimizedRegionBoxes: Mergeable box [%d] has coordinates [%d,%d,%d,%d].\n", j, pBox[j].x1, pBox[j].y1, pBox[j].x2, pBox[j].y2); #endif /* * Each consequent box is merged if its higher side overlaps the * lower side of current box. In case of ADVANCED_BOXES_DEFRAG * the higher side must be included within a range defined by * INCLUDE_MARGIN. */ #ifndef ADVANCED_BOXES_DEFRAG if (pBox[j].y1 == boxExtents.y2 && pBox[j].x1 == pBox[i].x1 && pBox[j].x2 == pBox[i].x2) #else if (pBox[j].x1 > boxExtents.x1 - INCLUDE_MARGIN && pBox[j].x1 < boxExtents.x1 + INCLUDE_MARGIN && pBox[j].y1 > boxExtents.y2 - INCLUDE_MARGIN && pBox[j].y1 < boxExtents.y2 + INCLUDE_MARGIN && pBox[j].x2 > boxExtents.x2 - INCLUDE_MARGIN && pBox[j].x2 < boxExtents.x2 + INCLUDE_MARGIN) #endif { #ifdef DEBUG fprintf(stderr, "nxagentGetOptimizedRegionBoxes: Going to merge box at [%d] with box at [%d].\n", j, i); #endif #ifdef ADVANCED_BOXES_DEFRAG if (pBox[j].x1 < boxExtents.x1) { boxExtents.x1 = pBox[j].x1; } if (pBox[j].x2 > boxExtents.x2) { boxExtents.x2 = pBox[j].x2; } if (pBox[j].y1 < boxExtents.y1) { boxExtents.y1 = pBox[j].y1; } #endif if (pBox[j].y2 > boxExtents.y2) { boxExtents.y2 = pBox[j].y2; } /* * By appending a box to another, we have to remove it from * the box list. We do this by setting its coordinates to * (0,0) and by checking their value in the main loop. */ pBox[j].x1 = pBox[j].y1 = pBox[j].x2 = pBox[j].y2 = 0; #ifdef DEBUG nBoxOptim--; #endif } } /* * Extend the box height. */ #ifdef ADVANCED_BOXES_DEFRAG pBox[i].x1 = boxExtents.x1; pBox[i].y1 = boxExtents.y1; pBox[i].x2 = boxExtents.x2; #endif pBox[i].y2 = boxExtents.y2; } #ifdef ADVANCED_BOXES_DEFRAG /* * The new list need to be validated to avoid boxes * overlapping. This code may be improved to remove also the * partial- ly overlapping boxes. */ for (int i = 0; i < nBox; i++) { if (pBox[i].x1 == 0 && pBox[i].y1 == 0 && pBox[i].x2 == 0 && pBox[i].y2 == 0) { continue; } #ifdef DEBUG fprintf(stderr, "nxagentGetOptimizedRegionBoxes: Referential box [%d] has coordinates [%d,%d,%d,%d].\n", i, pBox[i].x1, pBox[i].y1, pBox[i].x2, pBox[i].y2); #endif boxExtents.x1 = pBox[i].x1; boxExtents.y1 = pBox[i].y1; boxExtents.x2 = pBox[i].x2; boxExtents.y2 = pBox[i].y2; for (int j = i+1; j < nBox; j++) { if (pBox[j].x1 == 0 && pBox[j].y1 == 0 && pBox[j].x2 == 0 && pBox[j].y2 == 0) { continue; } #ifdef DEBUG fprintf(stderr, "nxagentGetOptimizedRegionBoxes: Mergeable box [%d] has coordinates [%d,%d,%d,%d].\n", j, pBox[j].x1, pBox[j].y1, pBox[j].x2, pBox[j].y2); #endif if ((boxExtents.x1 <= pBox[j].x1 && boxExtents.x2 >= pBox[j].x2 && boxExtents.y1 <= pBox[j].y1 && boxExtents.y2 >= pBox[j].y2)) { /* * If a box is completely inside another, we set its * coordinates to 0 to consider it as merged. */ #ifdef DEBUG fprintf(stderr, "nxagentGetOptimizedRegionBoxes: Going to merge box [%d,%d,%d,%d] " "with its box container [%d,%d,%d,%d].\n", pBox[j].x1, pBox[j].y1, pBox[j].x2, pBox[j].y2, boxExtents.x1, boxExtents.y1, boxExtents.y1, boxExtents.y2); #endif pBox[j].x1 = pBox[j].y1 = pBox[j].x2 = pBox[j].y2 = 0; #ifdef DEBUG nBoxOptim--; #endif } } } #endif #ifdef DEBUG fprintf(stderr, "nxagentGetOptimizedRegionBoxes: Original boxes number [%d] Optimized boxes number [%d].\n", nBox, nBoxOptim); #endif return pBox; } unsigned long nxagentGetColor(DrawablePtr pDrawable, int xPixel, int yPixel) { int leftPad = 0; int depth = pDrawable -> depth; int format = (depth == 1) ? XYPixmap : ZPixmap; int length = nxagentImageLength(1, 1, format, leftPad, depth); char * data = malloc(length); if (data == NULL) { #ifdef WARNING fprintf(stderr, "nxagentGetColor: WARNING! Failed to allocate memory for the operation.\n"); #endif return -1; } Visual *pVisual = nxagentImageVisual(pDrawable, depth); if (pVisual == NULL) { #ifdef WARNING fprintf(stderr, "nxagentGetColor: WARNING! Visual not found. Using default visual.\n"); #endif pVisual = nxagentVisuals[nxagentDefaultVisualIndex].visual; } fbGetImage(pDrawable, xPixel, yPixel, 1, 1, format, AllPlanes, data); XImage *ximage = XCreateImage(nxagentDisplay, pVisual, depth, format, leftPad, (char *) data, 1, 1, BitmapPad(nxagentDisplay), nxagentImagePad(1, format, leftPad, 1)); if (ximage == NULL) { #ifdef WARNING fprintf(stderr, "nxagentGetColor: WARNING! Failed to create the XImage.\n"); #endif SAFE_free(data); return -1; } unsigned long pixel = XGetPixel(ximage, 0, 0); XDestroyImage(ximage); return pixel; } /* * This function could be used to determine the ClearArea color of * corrupted regions on screen. */ unsigned long nxagentGetRegionColor(DrawablePtr pDrawable, RegionPtr pRegion) { if (RegionNil(pRegion)) { return nxagentGetDrawableColor(pDrawable); } /* * The pixel used as reference is the first outer pixel at the * bottom right corner of corrupted region extents. */ int xPicker = pRegion -> extents.x2 + 1; if (xPicker > pDrawable -> width) { xPicker = pDrawable -> width; } int yPicker = pRegion -> extents.y2 + 1; if (yPicker > pDrawable -> height) { yPicker = pDrawable -> height; } return nxagentGetColor(pDrawable, xPicker, yPicker); } unsigned long nxagentGetDrawableColor(DrawablePtr pDrawable) { /* * The pixel used to determine the color of a drawable is at * coordinates (x + width - 4, y + 4). */ return nxagentGetColor(pDrawable, pDrawable -> width - 4, 4); } void nxagentClearRegion(DrawablePtr pDrawable, RegionPtr pRegion) { unsigned long backupPixel = 0; #ifdef DEBUG static int nBoxCleared; #endif if (pDrawable -> type != DRAWABLE_WINDOW) { #ifdef TEST fprintf(stderr, "nxagentClearRegion: Cannot clear a pixmap. Exiting.\n"); #endif return; } if (pRegion == NullRegion || RegionNil(pRegion)) { #ifdef TEST fprintf(stderr, "nxagentClearRegion: The region is empty. Exiting.\n"); #endif return; } WindowPtr pWin = (WindowPtr) pDrawable; int restore = 0; /* * If the window has already a background, we can hope it will be * nice. */ if (pWin -> backgroundState != None) { #ifdef DEBUG fprintf(stderr, "nxagentClearRegion: Window at [%p] has background state [%u].\n", (void *) pWin, pWin -> backgroundState); #endif } else { /* * Save the original state. */ backupPixel = pWin -> background.pixel; unsigned long color = nxagentGetDrawableColor((DrawablePtr) pWin); if (color == -1) { color = 0xffffff; } pWin -> backgroundState = BackgroundPixel; pWin -> background.pixel = color; nxagentChangeWindowAttributes(pWin, CWBackPixel); #ifdef DEBUG fprintf(stderr, "nxagentClearRegion: Window at [%p] now has pixel background [%ld].\n", (void *) pWin, color); #endif restore = 1; } BoxPtr pBox = nxagentGetOptimizedRegionBoxes(pRegion); int nBox = RegionNumRects(pRegion); for (int i = 0; i < nBox; i++) { if (pBox[i].x1 == 0 && pBox[i].y1 == 0 && pBox[i].x2 == 0 && pBox[i].y2 == 0) { continue; } XClearArea(nxagentDisplay, nxagentWindow(pWin), pBox[i].x1, pBox[i].y1, pBox[i].x2 - pBox[i].x1, pBox[i].y2 - pBox[i].y1, False); #ifdef DEBUG nBoxCleared++; #endif } /* * Restore the old state. */ if (restore == 1) { pWin -> backgroundState = None; pWin -> background.pixel = backupPixel; } #ifdef DEBUG fprintf(stderr, "nxagentClearRegion: Number of cleared boxes is [%d].\n", nBoxCleared); #endif } void nxagentFillRemoteRegion(DrawablePtr pDrawable, RegionPtr pRegion) { if (RegionNil(pRegion)) { return; } GCPtr pGC = nxagentGetGraphicContext(pDrawable); int nrects = RegionNumRects(pRegion); #ifdef TEST fprintf(stderr, "nxagentFillRemoteRegion: Going to fill remote region [%d,%d,%d,%d] rects [%d] with color [%lu].\n", pRegion -> extents.x1, pRegion -> extents.y1, pRegion -> extents.x2, pRegion -> extents.y2, nrects, pGC -> fgPixel); #endif if (nrects == 1) { XFillRectangle(nxagentDisplay, nxagentDrawable(pDrawable), nxagentGC(pGC), pRegion -> extents.x1, pRegion -> extents.y1, pRegion -> extents.x2 - pRegion -> extents.x1, pRegion -> extents.y2 - pRegion -> extents.y1); } else { BoxPtr pBox = RegionRects(pRegion); XRectangle *pRects = malloc(nrects * sizeof(XRectangle)); for (int i = 0; i < nrects; i++) { pRects[i].x = pBox[i].x1; pRects[i].y = pBox[i].y1; pRects[i].width = pBox[i].x2 - pBox[i].x1; pRects[i].height = pBox[i].y2 - pBox[i].y1; } XFillRectangles(nxagentDisplay, nxagentDrawable(pDrawable), nxagentGC(pGC), pRects, nrects); SAFE_free(pRects); } } int nxagentDestroyCorruptedWindowResource(void * p, XID id) { #ifdef TEST fprintf(stderr, "nxagentDestroyCorruptedWindowResource: Removing corrupted window [%p] from resources.\n", (void *) p); #endif nxagentWindowPriv((WindowPtr) p) -> corruptedId = None; return 1; } int nxagentDestroyCorruptedPixmapResource(void * p, XID id) { #ifdef TEST fprintf(stderr, "nxagentDestroyCorruptedPixmapResource: Removing corrupted pixmap [%p] from resources.\n", (void *) p); #endif nxagentPixmapPriv((PixmapPtr) p) -> corruptedId = None; return 1; } int nxagentDestroyCorruptedBackgroundResource(void * p, XID id) { #ifdef TEST fprintf(stderr, "nxagentDestroyCorruptedBackgroundResource: Removing corrupted pixmap background [%p] from resources.\n", (void *) p); #endif nxagentPixmapPriv((PixmapPtr) p) -> corruptedBackgroundId = None; return 1; } void nxagentPointsToDirtyRegion(DrawablePtr pDrawable, int mode, int nPoints, xPoint *pPoints) { RegionPtr pRegion = RegionCreate(NullBox, 1); xPoint *xp = pPoints; for (int np = nPoints; np--; xp++) { BoxRec box; if (CoordModePrevious) { box.x1 = box.x2 = (xp-1) -> x + xp -> x; box.y1 = box.y2 = (xp-1) -> y + xp -> y; } else { box.x1 = box.x2 = xp -> x; box.y1 = box.y2 = xp -> y; } #ifdef TEST fprintf(stderr, "nxagentPointsToDirtyRegion: Adding the point (%d,%d) to the dirty region.\n", box.x1, box.y1); #endif /* * By using REGION_APPEND() and REGION_VALIDATE() * this loop could become less expensive. */ RegionRec tmpRegion; RegionInit(&tmpRegion, &box, 1); RegionUnion(pRegion, pRegion, &tmpRegion); RegionUninit(&tmpRegion); } BoxRec extents = *RegionExtents(pRegion); RegionReset(pRegion, &extents); #ifdef TEST fprintf(stderr, "nxagentPointsToDirtyRegion: The resulting dirty region has [%ld] rects and" " extents (%d,%d,%d,%d).\n", RegionNumRects(pRegion), extents.x1, extents.y1, extents.x2, extents.y2); #endif nxagentMarkCorruptedRegion(pDrawable, pRegion); RegionDestroy(pRegion); } #ifdef DUMP #define USE_MULTIPLE_COLORS void nxagentCorruptedRegionOnWindow(void *p0, XID x, void *p2) { WindowPtr pWin = (WindowPtr) p0; RegionPtr clipRegion; RegionRec visRegion; BoxPtr pBox; XlibGC gc; XGCValues value; static unsigned long color = 0xff000000; int nrectangles; int i; /* * There are no regions to draw. */ if (nxagentDrawableStatus((DrawablePtr) pWin) == Synchronized) { return; } /* * The window is not visible. */ if (!nxagentWindowIsVisible(pWin)) { return; } #ifdef TEST fprintf(stderr, "nxagentCorruptedRegionOnWindow: Going to draw on window at [%p].\n", (void *) pWin); #endif clipRegion = nxagentCreateRegion((DrawablePtr) pWin, NULL, 0, 0, pWin -> drawable.width, pWin -> drawable.height); RegionInit(&visRegion, NullBox, 1); RegionIntersect(&visRegion, clipRegion, nxagentCorruptedRegion((DrawablePtr) pWin)); nxagentFreeRegion(clipRegion); if (RegionNil(&visRegion)) { #ifdef TEST fprintf(stderr, "nxagentCorruptedRegionOnWindow: The corrupted region of window at [%p] is hidden.\n", (void *) pWin); #endif RegionUninit(&visRegion); return; } nxagentClearRegion((DrawablePtr) pWin, &visRegion); #ifdef USE_MULTIPLE_COLORS color += nxagentWindow(pWin) * 5; if (color == 0 || color == 0xffffffff) { color = 0xff000000; } #endif value.foreground = color; value.subwindow_mode = IncludeInferiors; gc = XCreateGC(nxagentDisplay, nxagentWindow(pWin), GCForeground | GCSubwindowMode, &value); nrectangles = RegionNumRects(&visRegion); #ifdef TEST fprintf(stderr, "nxagentCorruptedRegionOnWindow: Going to draw the region with extents [%d,%d,%d,%d] and [%d] rects.\n", visRegion.extents.x1, visRegion.extents.y1, visRegion.extents.x2, visRegion.extents.y2, nrectangles); #endif pBox = nxagentGetOptimizedRegionBoxes(&visRegion); for (i = 0; i < nrectangles; i++) { if (pBox[i].x1 == 0 && pBox[i].y1 == 0 && pBox[i].x2 == 0 && pBox[i].y2 == 0) { continue; } XDrawRectangle(nxagentDisplay, nxagentWindow(pWin), gc, pBox[i].x1, pBox[i].y1, pBox[i].x2 - pBox[i].x1 - 1, pBox[i].y2 - pBox[i].y1 - 1); } XFreeGC(nxagentDisplay, gc); RegionUninit(&visRegion); } void nxagentRegionsOnScreen(void) { FindClientResourcesByType(clients[serverClient -> index], RT_NX_CORR_WINDOW, nxagentCorruptedRegionOnWindow, NULL); } #endif /* * If the synchronization loop breaks and the drawable synchronization * cannot be completed, the remaining data is stored in a bitmap. The * synchronization loop is then restarted using the bitmap as source * instead of the drawable. */ void nxagentCreateDrawableBitmap(DrawablePtr pDrawable) { GCPtr pGC = NULL; RegionPtr pClipRegion = NullRegion; #ifdef TEST fprintf(stderr, "nxagentCreateDrawableBitmap: Creating synchronization bitmap for [%s] at [%p].\n", nxagentDrawableType(pDrawable), (void *) pDrawable); #endif /* * The bitmap is created only in the nxagent. */ int saveTrap = nxagentGCTrap; nxagentGCTrap = True; if (nxagentDrawableStatus(pDrawable) == Synchronized) { #ifdef TEST fprintf(stderr, "nxagentCreateDrawableBitmap: The drawable is already synchronized. Skipping bitmap creation.\n"); #endif goto nxagentCreateDrawableBitmapEnd; } /* * Should create a function to append a bitmap to another, instead * of destroying the old one. */ if (nxagentDrawableBitmap(pDrawable) != NullPixmap) { #ifdef WARNING fprintf(stderr, "nxagentCreateDrawableBitmap: WARNING! Going to replace the bitmap at [%p] with corrupted [%d,%d,%d,%d].\n", (void *) nxagentDrawableBitmap(pDrawable), nxagentCorruptedRegion((DrawablePtr) nxagentDrawableBitmap(pDrawable)) -> extents.x1, nxagentCorruptedRegion((DrawablePtr) nxagentDrawableBitmap(pDrawable)) -> extents.y1, nxagentCorruptedRegion((DrawablePtr) nxagentDrawableBitmap(pDrawable)) -> extents.x2, nxagentCorruptedRegion((DrawablePtr) nxagentDrawableBitmap(pDrawable)) -> extents.y2); #endif nxagentDestroyDrawableBitmap(pDrawable); } /* * Clipping to the visible area. */ pClipRegion = nxagentCreateRegion(pDrawable, NULL, 0, 0, pDrawable -> width, pDrawable -> height); RegionIntersect(pClipRegion, pClipRegion, nxagentCorruptedRegion(pDrawable)); if (RegionNil(pClipRegion)) { #ifdef TEST fprintf(stderr, "nxagentCreateDrawableBitmap: The corrupted region is not visible. Skipping bitmap creation.\n"); #endif goto nxagentCreateDrawableBitmapEnd; } /* * FIXME: A better way it would be create the bitmap with the same * extents of the clipRegion. This requires to save the offset with * respect to the drawable origin like in the backing store. This * becomes particularly important when the drawable is a huge * window, because the pixmap creation would fail. */ PixmapPtr pBitmap; pBitmap = nxagentCreatePixmap(pDrawable -> pScreen, pDrawable -> width, pDrawable -> height, pDrawable -> depth, 0); if (pBitmap == NULL) { #ifdef WARNING fprintf(stderr, "nxagentCreateDrawableBitmap: Cannot create pixmap for the bitmap data.\n"); #endif goto nxagentCreateDrawableBitmapEnd; } pGC = GetScratchGC(pBitmap -> drawable.depth, pBitmap -> drawable.pScreen); ValidateGC((DrawablePtr) pBitmap, pGC); int x = pClipRegion -> extents.x1; int y = pClipRegion -> extents.y1; int w = pClipRegion -> extents.x2 - pClipRegion -> extents.x1; int h = pClipRegion -> extents.y2 - pClipRegion -> extents.y1; nxagentCopyArea(pDrawable, (DrawablePtr) pBitmap, pGC, x, y, w, h, x, y); RegionUnion(nxagentCorruptedRegion((DrawablePtr) pBitmap), nxagentCorruptedRegion((DrawablePtr) pBitmap), pClipRegion); if (pDrawable -> type == DRAWABLE_PIXMAP) { nxagentPixmapPriv(nxagentRealPixmap((PixmapPtr) pDrawable)) -> synchronizationBitmap = pBitmap; if (nxagentIsCorruptedBackground((PixmapPtr) pDrawable) == 1) { nxagentSynchronization.backgroundBitmaps++; } else { nxagentSynchronization.pixmapBitmaps++; } } else { nxagentWindowPriv((WindowPtr) pDrawable) -> synchronizationBitmap = pBitmap; nxagentSynchronization.windowBitmaps++; } #ifdef TEST fprintf(stderr, "nxagentCreateDrawableBitmap: Drawable [%p] has bitmap at [%p] with corrupted [%d,%d,%d,%d].\n", (void *) pDrawable, (void *) nxagentDrawableBitmap(pDrawable), nxagentCorruptedRegion((DrawablePtr) nxagentDrawableBitmap(pDrawable)) -> extents.x1, nxagentCorruptedRegion((DrawablePtr) nxagentDrawableBitmap(pDrawable)) -> extents.y1, nxagentCorruptedRegion((DrawablePtr) nxagentDrawableBitmap(pDrawable)) -> extents.x2, nxagentCorruptedRegion((DrawablePtr) nxagentDrawableBitmap(pDrawable)) -> extents.y2); #endif nxagentCreateDrawableBitmapEnd: nxagentGCTrap = saveTrap; if (pClipRegion != NullRegion) { nxagentFreeRegion(pClipRegion); } if (pGC != NULL) { FreeScratchGC(pGC); } } void nxagentDestroyDrawableBitmap(DrawablePtr pDrawable) { if (nxagentDrawableBitmap(pDrawable) != NullPixmap) { #ifdef TEST fprintf(stderr, "nxagentDestroyDrawableBitmap: Destroying bitmap for drawable at [%p].\n", (void *) pDrawable); #endif nxagentDestroyPixmap(nxagentDrawableBitmap(pDrawable)); if (pDrawable -> type == DRAWABLE_PIXMAP) { nxagentPixmapPriv(nxagentRealPixmap((PixmapPtr) pDrawable)) -> synchronizationBitmap = NullPixmap; if (nxagentIsCorruptedBackground((PixmapPtr) pDrawable) == 1) { nxagentSynchronization.backgroundBitmaps--; } else { nxagentSynchronization.pixmapBitmaps--; } } else { nxagentWindowPriv((WindowPtr) pDrawable) -> synchronizationBitmap = NullPixmap; nxagentSynchronization.windowBitmaps--; } } } void nxagentIncreasePixmapUsageCounter(PixmapPtr pPixmap) { if (nxagentDrawableStatus((DrawablePtr) pPixmap) == Synchronized) { return; } #ifdef TEST fprintf(stderr, "nxagentIncreasePixmapUsageCounter: Pixmap usage counter was [%d].\n", nxagentPixmapUsageCounter(pPixmap)); #endif nxagentPixmapUsageCounter(pPixmap) += 1; nxagentAllocateCorruptedResource((DrawablePtr) pPixmap, RT_NX_CORR_PIXMAP); } void nxagentAllocateCorruptedResource(DrawablePtr pDrawable, RESTYPE type) { PixmapPtr pRealPixmap; if (nxagentSessionState == SESSION_DOWN) { #ifdef TEST fprintf(stderr, "nxagentAllocateCorruptedResource: WARNING! Not allocated corrupted resource " "[%s][%p] with the display down.\n", nxagentDrawableType(pDrawable), (void *) pDrawable); #endif return; } if (type == RT_NX_CORR_WINDOW) { if (nxagentWindowPriv((WindowPtr) pDrawable) -> corruptedId == 0) { #ifdef TEST fprintf(stderr, "nxagentAllocateCorruptedResource: New resource at [%p]. Corrupted " "windows counter was [%d].\n", (void *) pDrawable, nxagentCorruptedWindows); #endif nxagentCorruptedWindows++; nxagentWindowPriv((WindowPtr) pDrawable) -> corruptedId = FakeClientID(serverClient -> index); AddResource(nxagentWindowPriv((WindowPtr) pDrawable) -> corruptedId, RT_NX_CORR_WINDOW, (void *) pDrawable); } } else if (type == RT_NX_CORR_BACKGROUND) { pRealPixmap = nxagentRealPixmap((PixmapPtr) pDrawable); if (nxagentPixmapPriv(pRealPixmap) -> corruptedBackgroundId == 0) { /* * When a pixmap is added to the background corrupted resources, * it must be removed from the pixmap corrupted resources. */ nxagentDestroyCorruptedResource(pDrawable, RT_NX_CORR_PIXMAP); #ifdef TEST fprintf(stderr, "nxagentAllocateCorruptedResource: New resource at [%p]. Corrupted " "backgrounds counter was [%d].\n", (void *) pDrawable, nxagentCorruptedBackgrounds); #endif nxagentCorruptedBackgrounds++; nxagentPixmapPriv(pRealPixmap) -> corruptedBackgroundId = FakeClientID(serverClient -> index); AddResource(nxagentPixmapPriv(pRealPixmap) -> corruptedBackgroundId, RT_NX_CORR_BACKGROUND, (void *) pRealPixmap); } } else if (type == RT_NX_CORR_PIXMAP) { /* * The shared memory pixmaps are always dirty and shouldn't be * synchronized. */ if (nxagentPixmapUsageCounter((PixmapPtr) pDrawable) >= MINIMUM_PIXMAP_USAGE_COUNTER && !nxagentIsShmPixmap((PixmapPtr) pDrawable)) { pRealPixmap = nxagentRealPixmap((PixmapPtr) pDrawable); if (nxagentPixmapPriv(pRealPixmap) -> corruptedId == 0) { #ifdef TEST fprintf(stderr, "nxagentAllocateCorruptedResource: New resource at [%p]. Corrupted " "pixmaps counter was [%d].\n", (void *) pDrawable, nxagentCorruptedPixmaps); #endif nxagentCorruptedPixmaps++; nxagentPixmapPriv(pRealPixmap) -> corruptedId = FakeClientID(serverClient -> index); AddResource(nxagentPixmapPriv(pRealPixmap) -> corruptedId, RT_NX_CORR_PIXMAP, (void *) pRealPixmap); } } } } void nxagentDestroyCorruptedResource(DrawablePtr pDrawable, RESTYPE type) { PixmapPtr pRealPixmap; if (type == RT_NX_CORR_WINDOW) { if (nxagentWindowPriv((WindowPtr) pDrawable) -> corruptedId != 0) { #ifdef TEST fprintf(stderr, "nxagentDestroyCorruptedResource: Removing resource at [%p]. Corrupted " "windows counter was [%d].\n", (void *) pDrawable, nxagentCorruptedWindows); #endif if (nxagentCorruptedWindows > 0) { nxagentCorruptedWindows--; } FreeResource(nxagentWindowPriv((WindowPtr) pDrawable) -> corruptedId, RT_NONE); if (nxagentSynchronization.pDrawable == pDrawable) { nxagentSynchronization.pDrawable = NULL; } } } else if (type == RT_NX_CORR_BACKGROUND) { pRealPixmap = nxagentRealPixmap((PixmapPtr) pDrawable); if (nxagentPixmapPriv(pRealPixmap) -> corruptedBackgroundId != 0) { #ifdef TEST fprintf(stderr, "nxagentDestroyCorruptedResource: Removing resource at [%p]. Corrupted " "backgrounds counter was [%d].\n", (void *) pDrawable, nxagentCorruptedBackgrounds); #endif if (nxagentCorruptedBackgrounds > 0) { nxagentCorruptedBackgrounds--; } FreeResource(nxagentPixmapPriv(pRealPixmap) -> corruptedBackgroundId, RT_NONE); if (nxagentSynchronization.pDrawable == pDrawable) { nxagentSynchronization.pDrawable = NULL; } } } else if (type == RT_NX_CORR_PIXMAP) { pRealPixmap = nxagentRealPixmap((PixmapPtr) pDrawable); if (nxagentPixmapPriv(pRealPixmap) -> corruptedId != 0) { #ifdef TEST fprintf(stderr, "nxagentDestroyCorruptedResource: Removing resource at [%p]. Corrupted " "pixmaps counter was [%d].\n", (void *) pDrawable, nxagentCorruptedPixmaps); #endif if (nxagentCorruptedPixmaps > 0) { nxagentCorruptedPixmaps--; } FreeResource(nxagentPixmapPriv(pRealPixmap) -> corruptedId, RT_NONE); if (nxagentSynchronization.pDrawable == pDrawable) { nxagentSynchronization.pDrawable = NULL; } } } } void nxagentCleanCorruptedDrawable(DrawablePtr pDrawable) { if (nxagentDrawableStatus(pDrawable) == Synchronized) { return; } #ifdef TEST fprintf(stderr, "nxagentCleanCorruptedDrawable: Clearing out the corrupted drawable [%s][%p].\n", nxagentDrawableType(pDrawable), (void *) pDrawable); #endif nxagentUnmarkCorruptedRegion(pDrawable, NullRegion); if (nxagentDrawableBitmap(pDrawable) != NullPixmap) { nxagentDestroyDrawableBitmap(pDrawable); } } void nxagentUnmarkExposedRegion(WindowPtr pWin, RegionPtr pRegion, RegionPtr pOther) { RegionRec clipRegion; if (pRegion != NullRegion && !RegionNil(pRegion) && nxagentDrawableStatus((DrawablePtr) pWin) == NotSynchronized) { RegionInit(&clipRegion, NullBox, 1); RegionCopy(&clipRegion, pRegion); if (pOther != NullRegion && !RegionNil(pOther)) { RegionUnion(&clipRegion, &clipRegion, pOther); } RegionTranslate(&clipRegion, -pWin -> drawable.x, -pWin -> drawable.y); #ifdef TEST fprintf(stderr, "nxagentUnmarkExposedRegion: Validating expose region [%d,%d,%d,%d] " "on window [%p].\n", clipRegion.extents.x1, clipRegion.extents.y1, clipRegion.extents.x2, clipRegion.extents.y2, (void *) pWin); #endif nxagentUnmarkCorruptedRegion((DrawablePtr) pWin, &clipRegion); RegionUninit(&clipRegion); } } int nxagentSynchronizationPredicate(void) { if (nxagentCorruptedWindows == 0 && nxagentCorruptedBackgrounds == 0 && nxagentCorruptedPixmaps == 0) { return NotNeeded; } if (!nxagentBlocking && nxagentCongestion <= 4 && nxagentReady == 0 && nxagentUserInput(NULL) == 0) { return Needed; } /* * If there are resources to synchronize but the conditions to start * the loop are not satisfied, a little delay is requested to check * for a new loop as soon as possible. */ return Delayed; } void nxagentSendBackgroundExpose(WindowPtr pWin, PixmapPtr pBackground, RegionPtr pExpose) { RegionRec expose; miBSWindowPtr pBackingStore; RegionInit(&expose, NullBox, 1); #ifdef DEBUG fprintf(stderr, "nxagentSendBackgroundExpose: Original expose region is [%d,%d,%d,%d].\n", pExpose -> extents.x1, pExpose -> extents.y1, pExpose -> extents.x2, pExpose -> extents.y2); fprintf(stderr, "nxagentSendBackgroundExpose: Window clipList is [%d,%d,%d,%d].\n", pWin -> clipList.extents.x1, pWin -> clipList.extents.y1, pWin -> clipList.extents.x2, pWin -> clipList.extents.y2); #endif if (nxagentDrawableStatus((DrawablePtr) pBackground) == Synchronized && (pBackground -> drawable.width < pWin -> drawable.width || pBackground -> drawable.height < pWin -> drawable.height)) { #ifdef TEST fprintf(stderr, "nxagentSendBackgroundExpose: Pixmap background [%dx%d] is " "smaller than window [%dx%d]. Going to expose the winSize.\n", pBackground -> drawable.width, pBackground -> drawable.height, pWin -> drawable.width, pWin -> drawable.height); #endif RegionCopy(&expose, &pWin -> winSize); } else { RegionCopy(&expose, pExpose); RegionTranslate(&expose, pWin -> drawable.x, pWin -> drawable.y); } RegionSubtract(&expose, &expose, nxagentCorruptedRegion((DrawablePtr) pWin)); if (RegionNil(&pWin -> clipList)) { #ifdef TEST fprintf(stderr, "nxagentSendBackgroundExpose: Exposures deferred because the window " "is hidden.\n"); #endif RegionUnion(nxagentDeferredBackgroundExposures, nxagentDeferredBackgroundExposures, &expose); nxagentWindowPriv(pWin) -> deferredBackgroundExpose = 1; goto nxagentSendBackgroundExposeEnd; } #ifdef TEST fprintf(stderr, "nxagentSendBackgroundExpose: Sending expose [%d,%d,%d,%d].\n", expose.extents.x1, expose.extents.y1, expose.extents.x2, expose.extents.y2); #endif /* * This prevents hidden region to be exposed. */ pBackingStore = (miBSWindowPtr)pWin->backStorage; if ((pBackingStore != NULL) && !RegionNil(&pBackingStore->SavedRegion)) { RegionTranslate(&expose, -pWin -> drawable.x, -pWin -> drawable.y); RegionSubtract(&expose, &expose, &pBackingStore -> SavedRegion); RegionTranslate(&expose, pWin -> drawable.x, pWin -> drawable.y); } RegionIntersect(&expose, &expose, &pWin -> clipList); /* * Reduce the overall region to expose. */ RegionTranslate(&expose, -pWin -> drawable.x, -pWin -> drawable.y); RegionSubtract(pExpose, pExpose, &expose); RegionTranslate(&expose, pWin -> drawable.x, pWin -> drawable.y); miWindowExposures(pWin, &expose, &expose); nxagentSendBackgroundExposeEnd: RegionUninit(&expose); } void nxagentExposeBackgroundPredicate(void *p0, XID x1, void *p2) { WindowPtr pWin = (WindowPtr) p0; struct nxagentExposeBackground *pPair = p2; if (RegionNil(pPair -> pExpose)) { return; } if (pWin -> backgroundState == BackgroundPixmap && pWin -> background.pixmap == pPair -> pBackground) { #ifdef TEST fprintf(stderr, "nxagentExposeBackgroundPredicate: Window at [%p] uses pixmap [%p] " "as background.\n", (void *) pWin, (void *) pPair -> pBackground); #endif nxagentSendBackgroundExpose(pWin, pPair -> pBackground, pPair -> pExpose); } else if (pWin -> backgroundState == ParentRelative) { #ifdef TEST fprintf(stderr, "nxagentExposeBackgroundPredicate: Window [%p] uses parent's background.\n", (void *) pWin); #endif WindowPtr pParent = pWin -> parent; while (pParent != NULL) { if (pParent -> backgroundState == BackgroundPixmap && pParent -> background.pixmap == pPair -> pBackground) { #ifdef TEST fprintf(stderr, "nxagentExposeBackgroundPredicate: Parent window at [%p] uses pixmap [%p] " "as background.\n", (void *) pParent, (void *) pPair -> pBackground); #endif nxagentSendBackgroundExpose(pWin, pPair -> pBackground, pPair -> pExpose); break; } pParent = pParent -> parent; } } } /* * This function is similar to nxagentClipAndSendExpose(). */ int nxagentClipAndSendClearExpose(WindowPtr pWin, void * ptr) { RegionPtr remoteExposeRgn = (RegionRec *) ptr; if (nxagentWindowPriv(pWin) -> deferredBackgroundExpose == 1) { RegionPtr exposeRgn = RegionCreate(NULL, 1); #ifdef DEBUG BoxRec box = *RegionExtents(remoteExposeRgn); fprintf(stderr, "nxagentClipAndSendClearExpose: Background expose extents: [%d,%d,%d,%d].\n", box.x1, box.y1, box.x2, box.y2); box = *RegionExtents(&pWin -> clipList); fprintf(stderr, "nxagentClipAndSendClearExpose: Clip list extents for window at [%p]: [%d,%d,%d,%d].\n", (void *) pWin, box.x1, box.y1, box.x2, box.y2); #endif RegionIntersect(exposeRgn, remoteExposeRgn, &pWin -> clipList); /* * If the region will be synchronized, the expose on corrupted * regions can be ignored. */ RegionSubtract(exposeRgn, exposeRgn, nxagentCorruptedRegion((DrawablePtr) pWin)); if (RegionNotEmpty(exposeRgn)) { #ifdef DEBUG box = *RegionExtents(exposeRgn); fprintf(stderr, "nxagentClipAndSendClearExpose: Forwarding expose [%d,%d,%d,%d] to window at [%p] pWin.\n", box.x1, box.y1, box.x2, box.y2, (void *) pWin); #endif RegionSubtract(remoteExposeRgn, remoteExposeRgn, exposeRgn); miWindowExposures(pWin, exposeRgn, exposeRgn); } RegionDestroy(exposeRgn); nxagentWindowPriv(pWin) -> deferredBackgroundExpose = 0; } if (RegionNotEmpty(remoteExposeRgn)) { #ifdef DEBUG fprintf(stderr, "nxagentClipAndSendClearExpose: Region not empty. Walk children.\n"); #endif return WT_WALKCHILDREN; } else { #ifdef DEBUG fprintf(stderr, "nxagentClipAndSendClearExpose: Region empty. Stop walking.\n"); #endif return WT_STOPWALKING; } } void nxagentSendDeferredBackgroundExposures(void) { if (nxagentDeferredBackgroundExposures == NullRegion) { nxagentDeferredBackgroundExposures = RegionCreate(NullBox, 1); } if (RegionNotEmpty(nxagentDeferredBackgroundExposures) != 0) { #ifdef TEST fprintf(stderr, "nxagentSendDeferredBackgroundExposures: Going to send deferred exposures to the root window.\n"); #endif TraverseTree(screenInfo.screens[0]->root, nxagentClipAndSendClearExpose, (void *) nxagentDeferredBackgroundExposures); RegionEmpty(nxagentDeferredBackgroundExposures); } }