/**************************************************************************/ /* */ /* 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 "scrnintstr.h" #include "miscstruct.h" #include "pixmapstr.h" #include "dixstruct.h" #include "regionstr.h" #include "../../include/gc.h" #include "servermd.h" #include "mi.h" #include "../../fb/fb.h" #include "Agent.h" #include "Display.h" #include "Screen.h" #include "Pixmaps.h" #include "Trap.h" #include "GCs.h" #include "GCOps.h" #include "Image.h" #include "Split.h" #include "Drawable.h" #include "Visual.h" #include "Client.h" #include "Events.h" #include "Args.h" #include "Utils.h" #include "compext/Compext.h" #include RESTYPE RT_NX_PIXMAP; /* * Set here the required log level. */ #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP #ifdef TEST #include "Font.h" #endif int nxagentPixmapPrivateIndex; /* * Force deallocation of the virtual pixmap. */ static Bool nxagentDestroyVirtualPixmap(PixmapPtr pPixmap); /* * This serves as a tool to check the synchronization * between pixmaps in framebuffer and the correspondent * pixmaps in the real X server. */ #ifdef TEST Bool nxagentCheckPixmapIntegrity(PixmapPtr pPixmap); #endif struct nxagentPixmapPair { Pixmap pixmap; PixmapPtr pMap; }; PixmapPtr nxagentCreatePixmap(ScreenPtr pScreen, int width, int height, int depth, unsigned usage_hint) { #ifdef DEBUG fprintf(stderr, "nxagentCreatePixmap: Creating pixmap with width [%d] " "height [%d] depth [%d] and allocation hint [%d].\n", width, height, depth, usage_hint); #endif /* * Create the pixmap structure but do not allocate memory for the * data. */ PixmapPtr pPixmap = AllocatePixmap(pScreen, 0); if (!pPixmap) { #ifdef WARNING fprintf(stderr, "nxagentCreatePixmap: WARNING! Failed to create pixmap with " "width [%d] height [%d] depth [%d] and allocation hint [%d].\n", width, height, depth, usage_hint); #endif return NullPixmap; } /* * Initialize the core members. */ pPixmap -> drawable.type = DRAWABLE_PIXMAP; pPixmap -> drawable.class = 0; pPixmap -> drawable.pScreen = pScreen; pPixmap -> drawable.depth = depth; pPixmap -> drawable.bitsPerPixel = BitsPerPixel(depth); pPixmap -> drawable.id = 0; pPixmap -> drawable.serialNumber = NEXT_SERIAL_NUMBER; pPixmap -> drawable.x = 0; pPixmap -> drawable.y = 0; pPixmap -> drawable.width = width; pPixmap -> drawable.height = height; pPixmap -> devKind = 0; pPixmap -> refcnt = 1; pPixmap -> devPrivate.ptr = NULL; pPixmap -> usage_hint = usage_hint; /* * Initialize the privates of the real picture. */ nxagentPrivPixmapPtr pPixmapPriv = nxagentPixmapPriv(pPixmap); pPixmapPriv -> isVirtual = False; pPixmapPriv -> isShared = nxagentShmPixmapTrap; /* * The shared memory pixmaps are never * synchronized with the remote X Server. */ if (pPixmapPriv -> isShared) { BoxRec box = { .x1 = 0, .y1 = 0, .x2 = width, .y2 = height }; pPixmapPriv -> corruptedRegion = RegionCreate(&box, 1); } else { pPixmapPriv -> corruptedRegion = RegionCreate((BoxRec *) NULL, 1); } pPixmapPriv -> corruptedBackground = 0; pPixmapPriv -> containGlyphs = 0; pPixmapPriv -> containTrapezoids = 0; /* * The lazy encoding policy generally does not send on remote X * server the off-screen images, by preferring to synchronize the * windows content. Anyway this behaviour may be inadvisable if a * pixmap is used, for example, for multiple copy areas on screen. * This counter serves the purpose, taking into account the number * of times the pixmap has been used as source for a deferred * operation. */ pPixmapPriv -> usageCounter = 0; pPixmapPriv -> corruptedBackgroundId = 0; pPixmapPriv -> corruptedId = 0; pPixmapPriv -> synchronizationBitmap = NullPixmap; pPixmapPriv -> corruptedTimestamp = 0; pPixmapPriv -> splitResource = NULL; pPixmapPriv -> isBackingPixmap = 0; /* * Create the pixmap based on the default windows. The proxy knows * this and uses this information to optimize encode the create * pixmap message by including the id of the drawable in the * checksum. */ if (width != 0 && height != 0 && !nxagentGCTrap) { pPixmapPriv -> id = XCreatePixmap(nxagentDisplay, nxagentDefaultWindows[pScreen -> myNum], width, height, depth); } else { pPixmapPriv -> id = 0; #ifdef TEST fprintf(stderr, "nxagentCreatePixmap: Skipping the creation of pixmap at [%p] on real " "X server with nxagentGCTrap [%d].\n", (void *) pPixmap, nxagentGCTrap); #endif } pPixmapPriv -> mid = FakeClientID(serverClient -> index); AddResource(pPixmapPriv -> mid, RT_NX_PIXMAP, pPixmap); pPixmapPriv -> pRealPixmap = pPixmap; pPixmapPriv -> pVirtualPixmap = NULL; pPixmapPriv -> pPicture = NULL; /* * Create the pixmap in the virtual framebuffer. */ PixmapPtr pVirtual = fbCreatePixmap(pScreen, width, height, depth, usage_hint); if (pVirtual == NULL) { #ifdef PANIC fprintf(stderr, "nxagentCreatePixmap: PANIC! Failed to create virtual pixmap with " "width [%d] height [%d] depth [%d] and allocation hint [%d].\n", width, height, depth, usage_hint); #endif nxagentDestroyPixmap(pPixmap); return NullPixmap; } #ifdef TEST fprintf(stderr, "nxagentCreatePixmap: Allocated memory for the Virtual %sPixmap %p of real Pixmap %p (%dx%d)," "allocation hint [%d].\n", nxagentShmPixmapTrap ? "Shm " : "", (void *) pVirtual, (void *) pPixmap, width, height, usage_hint); #endif pPixmapPriv -> pVirtualPixmap = pVirtual; /* * Initialize the privates of the virtual picture. We could avoid to * use a flag and just check the pointer to the virtual pixmap that, * if the pixmap is actually virtual, will be NULL. Unfortunately * the flag can be changed in nxagentValidateGC(). That code should * be removed in future. */ nxagentPrivPixmapPtr pVirtualPriv = nxagentPixmapPriv(pVirtual); pVirtualPriv -> isVirtual = True; pVirtualPriv -> isShared = nxagentShmPixmapTrap; pVirtualPriv -> corruptedRegion = RegionCreate((BoxRec *) NULL, 1); pVirtualPriv -> corruptedBackground = 0; pVirtualPriv -> containGlyphs = 0; pVirtualPriv -> containTrapezoids = 0; pVirtualPriv -> usageCounter = 0; pVirtualPriv -> corruptedBackgroundId = 0; pVirtualPriv -> corruptedId = 0; pVirtualPriv -> synchronizationBitmap = NullPixmap; pVirtualPriv -> corruptedTimestamp = 0; pVirtualPriv -> splitResource = NULL; /* * We might distinguish real and virtual pixmaps by checking the * pointers to pVirtualPixmap. We should also remove the copy of id * and use the one of the real pixmap. */ pVirtualPriv -> id = pPixmapPriv -> id; pVirtualPriv -> mid = 0; /* * Storing a pointer back to the real pixmap is silly. Unfortunately * this is the way it has been originally implemented. See also the * comment in destroy of the pixmap. */ pVirtualPriv -> pRealPixmap = pPixmap; pVirtualPriv -> pVirtualPixmap = NULL; pVirtualPriv -> pPicture = NULL; #ifdef TEST fprintf(stderr, "nxagentCreatePixmap: Created pixmap at [%p] virtual at [%p] with width [%d] " "height [%d] depth [%d] and allocation hint [%d].\n", (void *) pPixmap, (void *) pVirtual, width, height, depth, usage_hint); #endif return pPixmap; } Bool nxagentDestroyPixmap(PixmapPtr pPixmap) { if (!pPixmap) { #ifdef PANIC fprintf(stderr, "nxagentDestroyPixmap: PANIC! Invalid attempt to destroy " "a null pixmap pointer.\n"); #endif return False; } nxagentPrivPixmapPtr pPixmapPriv = nxagentPixmapPriv(pPixmap); PixmapPtr pVirtual = pPixmapPriv -> pVirtualPixmap; #ifdef TEST fprintf(stderr, "nxagentDestroyPixmap: Destroying pixmap at [%p] with virtual at [%p].\n", (void *) pPixmap, (void *) pVirtual); #endif if (pPixmapPriv -> isVirtual) { /* * For some pixmaps we receive the destroy only for the * virtual. Infact to draw in the framebuffer we can use the * virtual pixmap instead of the pointer to the real one. As the * virtual pixmap can collect references, we must transfer those * references to the real pixmap so we can continue as the destroy * had been requested for it. */ pVirtual = pPixmap; pPixmap = pPixmapPriv -> pRealPixmap; pPixmapPriv = nxagentPixmapPriv(pPixmap); /* * Move the references accumulated by the virtual pixmap into the * references of the real one. */ int refcnt = pVirtual -> refcnt - 1; #ifdef TEST fprintf(stderr, "nxagentDestroyPixmap: Adding [%d] references to pixmap at [%p].\n", refcnt, (void *) pPixmap); #endif pPixmap -> refcnt += refcnt; pVirtual -> refcnt -= refcnt; } --pPixmap -> refcnt; #ifdef TEST fprintf(stderr, "nxagentDestroyPixmap: Pixmap has now [%d] references with virtual pixmap [%d].\n", pPixmap -> refcnt, pVirtual -> refcnt); if (pVirtual != NULL && pVirtual -> refcnt != 1) { fprintf(stderr, "nxagentDestroyPixmap: PANIC! Virtual pixmap has [%d] references.\n", pVirtual -> refcnt); } #endif if (pPixmap -> refcnt > 0) { return True; } #ifdef TEST fprintf(stderr, "nxagentDestroyPixmap: Managing to destroy the pixmap at [%p]\n", (void *) pPixmap); #endif nxagentRemoveItemBSPixmapList(nxagentPixmap(pPixmap)); nxagentDestroyVirtualPixmap(pPixmap); if (pPixmapPriv -> corruptedRegion != NullRegion) { RegionDestroy(pPixmapPriv -> corruptedRegion); pPixmapPriv -> corruptedRegion = NullRegion; } if (nxagentSynchronization.pDrawable == (DrawablePtr) pPixmap) { nxagentSynchronization.pDrawable = NULL; #ifdef TEST fprintf(stderr, "nxagentDestroyPixmap: Synchronization drawable [%p] removed from resources.\n", (void *) pPixmap); #endif } nxagentDestroyCorruptedResource((DrawablePtr) pPixmap, RT_NX_CORR_BACKGROUND); nxagentDestroyCorruptedResource((DrawablePtr) pPixmap, RT_NX_CORR_PIXMAP); nxagentDestroyDrawableBitmap((DrawablePtr) pPixmap); if (pPixmapPriv -> splitResource != NULL) { nxagentReleaseSplit((DrawablePtr) pPixmap); } /* * A pixmap with width and height set to 0 is * created at the beginning. To this pixmap is * not assigned an id. This is likely a scratch * pixmap used by the X server. */ if (pPixmapPriv -> id) { XFreePixmap(nxagentDisplay, pPixmapPriv -> id); } if (pPixmapPriv -> mid) { FreeResource(pPixmapPriv -> mid, RT_NONE); } SAFE_free(pPixmap); return True; } Bool nxagentDestroyVirtualPixmap(PixmapPtr pPixmap) { PixmapPtr pVirtual = nxagentPixmapPriv(pPixmap) -> pVirtualPixmap; /* * Force the routine to get rid of the virtual * pixmap. */ if (pVirtual != NULL) { pVirtual -> refcnt = 1; nxagentPrivPixmapPtr pVirtualPriv = nxagentPixmapPriv(pVirtual); if (pVirtualPriv -> corruptedRegion != NullRegion) { RegionDestroy(pVirtualPriv -> corruptedRegion); pVirtualPriv -> corruptedRegion = NullRegion; } fbDestroyPixmap(pVirtual); } return True; } RegionPtr nxagentPixmapToRegion(PixmapPtr pPixmap) { #ifdef TEST fprintf(stderr, "nxagentPixmapToRegion: Pixmap = [%p] nxagentVirtualPixmap = [%p]\n", (void *) pPixmap, (void *) nxagentVirtualPixmap(pPixmap)); #endif return fbPixmapToRegion(nxagentVirtualPixmap(pPixmap)); } Bool nxagentModifyPixmapHeader(PixmapPtr pPixmap, int width, int height, int depth, int bitsPerPixel, int devKind, void * pPixData) { /* * See miModifyPixmapHeader() in miscrinit.c. This function is used * to recycle the scratch pixmap for this screen. We let it refer to * the virtual pixmap. */ if (!pPixmap) { return False; } if (nxagentPixmapIsVirtual(pPixmap)) { #ifdef PANIC fprintf(stderr, "nxagentModifyPixmapHeader: PANIC! Pixmap at [%p] is virtual.\n", (void *) pPixmap); #endif FatalError("nxagentModifyPixmapHeader: PANIC! Pixmap is virtual."); } PixmapPtr pVirtualPixmap = nxagentVirtualPixmap(pPixmap); #ifdef TEST fprintf(stderr, "nxagentModifyPixmapHeader: Pixmap at [%p] Virtual at [%p].\n", (void *) pPixmap, (void *) pVirtualPixmap); fprintf(stderr, "nxagentModifyPixmapHeader: Pixmap has width [%d] height [%d] depth [%d] " "bits-per-pixel [%d] devKind [%d] pPixData [%p].\n", pPixmap->drawable.width, pPixmap->drawable.height, pPixmap->drawable.depth, pPixmap->drawable.bitsPerPixel, pPixmap->devKind, (void *) pPixmap->devPrivate.ptr); fprintf(stderr, "nxagentModifyPixmapHeader: New parameters are width [%d] height [%d] depth [%d] " "bits-per-pixel [%d] devKind [%d] pPixData [%p].\n", width, height, depth, bitsPerPixel, devKind, (void *) pPixData); #endif /* * ignore return code, because the only case where this will return * FALSE is pPixmap == NULL, which we have already caught above. */ miModifyPixmapHeader(pPixmap, width, height, depth, bitsPerPixel, devKind, pPixData); miModifyPixmapHeader(pVirtualPixmap, width, height, depth, bitsPerPixel, devKind, pPixData); #ifdef PANIC if (!((width > 0) && (height > 0) && (depth > 0) && (bitsPerPixel > 0) && (devKind > 0) && pPixData)) { if (pPixmap->drawable.x != 0 || pPixmap->drawable.y != 0) { fprintf(stderr, "nxagentModifyPixmapHeader: PANIC! Pixmap at [%p] has x [%d] and y [%d].\n", (void *) pPixmap, pPixmap->drawable.x, pPixmap->drawable.y); FatalError("nxagentModifyPixmapHeader: PANIC! Pixmap has x or y greater than zero."); } } #endif return True; } static void nxagentPixmapMatchID(void *p0, XID x1, void *p2) { PixmapPtr pPixmap = (PixmapPtr)p0; struct nxagentPixmapPair *pPair = p2; if ((pPair -> pMap == NULL) && (nxagentPixmap(pPixmap) == pPair -> pixmap)) { pPair -> pMap = pPixmap; } } PixmapPtr nxagentPixmapPtr(Pixmap pixmap) { if (pixmap == None) { return NULL; } struct nxagentPixmapPair pair = { .pixmap = pixmap, .pMap = NULL }; FindClientResourcesByType(clients[serverClient -> index], RT_NX_PIXMAP, nxagentPixmapMatchID, &pair); for (int i = 0; (pair.pMap == NULL) && (i < MAXCLIENTS); i++) { if (clients[i]) { FindClientResourcesByType(clients[i], RT_PIXMAP, nxagentPixmapMatchID, &pair); } } #ifdef WARNING if (pair.pMap == NULL) { fprintf(stderr, "nxagentPixmapPtr: WARNING! Failed to find " "remote pixmap [%ld].\n", (long int) pair.pixmap); } else if (nxagentDrawableStatus((DrawablePtr) pair.pMap) == NotSynchronized) { fprintf(stderr, "WARNING! Rootless icon at [%p] [%d,%d] is not synchronized.\n", (void *) pair.pMap, pair.pMap -> drawable.width, pair.pMap -> drawable.height); } #endif return pair.pMap; } /* * Reconnection stuff. */ int nxagentDestroyNewPixmapResourceType(void * p, XID id) { /* * Address of the destructor is set in Init.c. */ #ifdef TEST fprintf(stderr, "nxagentDestroyNewPixmapResourceType: Destroying mirror id [%ld] for pixmap at [%p].\n", nxagentPixmapPriv((PixmapPtr) p) -> mid, (void *) p); #endif nxagentPixmapPriv((PixmapPtr) p) -> mid = None; return True; } void nxagentDisconnectPixmap(void *p0, XID x1, void *p2) { PixmapPtr pPixmap = (PixmapPtr) p0; #ifdef TEST Bool *pBool = (Bool*) p2; fprintf(stderr, "nxagentDisconnectPixmap: Called with bool [%d] and pixmap at [%p].\n", *pBool, (void *) pPixmap); fprintf(stderr, "nxagentDisconnectPixmap: Virtual pixmap is [%ld].\n", nxagentPixmap(pPixmap)); #endif nxagentPixmap(pPixmap) = None; if (nxagentDrawableStatus((DrawablePtr) pPixmap) == NotSynchronized) { nxagentDestroyCorruptedResource((DrawablePtr) pPixmap, RT_NX_CORR_BACKGROUND); nxagentDestroyCorruptedResource((DrawablePtr) pPixmap, RT_NX_CORR_PIXMAP); } } void nxagentDisconnectAllPixmaps(void) { int r; #ifdef TEST fprintf(stderr, "nxagentDisconnectAllPixmaps: Going to iterate through pixmap resources.\n"); #endif /* * The RT_NX_PIXMAP resource type is allocated only on the server * client, so we don't need to find it through the other clients * too. */ r = 1; FindClientResourcesByType(clients[serverClient -> index], RT_NX_PIXMAP, nxagentDisconnectPixmap, &r); #ifdef WARNING /* Note: nxagentDisconnectPixmap() does not modify r - so this check can never succeed */ if (r == 0) { fprintf(stderr, "nxagentDisconnectAllPixmaps: WARNING! Failed to disconnect " "pixmap for client [%d].\n", serverClient -> index); } #endif for (int i = 0; i < MAXCLIENTS; i++) { if (clients[i]) { #ifdef TEST fprintf(stderr, "nxagentDisconnectAllPixmaps: Going to disconnect pixmaps of client [%d].\n", i); #endif r = 1; FindClientResourcesByType(clients[i], RT_PIXMAP, nxagentDisconnectPixmap, &r); #ifdef WARNING /* Note: nxagentDisconnectPixmap() does not modify r - so this check can never succeed */ if (r == 0) { fprintf(stderr, "nxagentDisconnectAllPixmaps: WARNING! Failed to disconnect " "pixmap for client [%d].\n", i); } #endif } } #ifdef TEST fprintf(stderr, "nxagentDisconnectAllPixmaps: Pixmaps disconnection completed.\n"); #endif return; } void nxagentReconnectPixmap(void *p0, XID x1, void *p2) { PixmapPtr pPixmap = (PixmapPtr) p0; Bool *pBool = (Bool*) p2; if (!*pBool || pPixmap == NULL || NXDisplayError(nxagentDisplay) == 1) { *pBool = False; #ifdef TEST fprintf(stderr, "nxagentReconnectPixmap: Ignoring pixmap at [%p] while " "recovering from the error.\n", (void *) pPixmap); #endif return; } else if (pPixmap == nxagentDefaultScreen -> pScratchPixmap) { /* * Every time the scratch pixmap is used its data is changed, so * we don't need to reconnect it. */ #ifdef TEST fprintf(stderr, "nxagentReconnectPixmap: Ignoring scratch pixmap at [%p].\n", (void *) pPixmap); #endif return; } #ifdef TEST fprintf(stderr, "nxagentReconnectPixmap: Called with result [%d] and pixmap at [%p].\n", *pBool, (void *) pPixmap); fprintf(stderr, "nxagentReconnectPixmap: Virtual pixmap is at [%p] picture is at [%p].\n", (void *) nxagentPixmapPriv(pPixmap) -> pVirtualPixmap, (void *) nxagentPixmapPriv(pPixmap) -> pPicture); #endif nxagentPrivPixmapPtr pPixmapPriv = nxagentPixmapPriv(pPixmap); if (pPixmap -> drawable.width && pPixmap -> drawable.height) { pPixmapPriv -> id = XCreatePixmap(nxagentDisplay, nxagentDefaultWindows[pPixmap -> drawable.pScreen -> myNum], pPixmap -> drawable.width, pPixmap -> drawable.height, pPixmap -> drawable.depth); nxagentPixmap(pPixmapPriv -> pVirtualPixmap) = pPixmapPriv -> id; #ifdef TEST fprintf(stderr, "nxagentReconnectPixmap: Created virtual pixmap with id [%ld] for pixmap at [%p].\n", nxagentPixmap(pPixmap), (void *) pPixmap); #endif if (pPixmap == (PixmapPtr) nxagentDefaultScreen -> devPrivate) { #ifdef WARNING fprintf(stderr, "nxagentReconnectPixmap: WARNING! Pixmap is root screen. Returning.\n"); #endif return; } nxagentSplitTrap = True; *pBool = nxagentSynchronizeDrawableData((DrawablePtr) pPixmap, NEVER_BREAK, NULL); nxagentSplitTrap = False; if (!*pBool) { #ifdef PANIC fprintf(stderr, "nxagentReconnectPixmap: PANIC! Failed to synchronize the pixmap.\n"); #endif } if (nxagentDrawableStatus((DrawablePtr) pPixmap) == NotSynchronized) { if (nxagentIsCorruptedBackground(pPixmap) == 1) { nxagentAllocateCorruptedResource((DrawablePtr) pPixmap, RT_NX_CORR_BACKGROUND); nxagentFillRemoteRegion((DrawablePtr) pPixmap, nxagentCorruptedRegion((DrawablePtr) pPixmap)); } else { nxagentAllocateCorruptedResource((DrawablePtr) pPixmap, RT_NX_CORR_PIXMAP); } } } } Bool nxagentReconnectAllPixmaps(void *p0) { Bool result = 1; #ifdef TEST fprintf(stderr, "nxagentReconnectAllPixmaps: Going to recreate all pixmaps.\n"); #endif /* * Reset the geometry and alpha information * used by proxy to unpack the packed images. */ nxagentResetVisualCache(); nxagentResetAlphaCache(); /* * The RT_NX_PIXMAP resource type is allocated only on the server * client, so we don't need to find it through the other clients * too. */ FindClientResourcesByType(clients[serverClient -> index], RT_NX_PIXMAP, nxagentReconnectPixmap, &result); #ifdef WARNING if (result == 0) { fprintf(stderr, "nxagentReconnectAllPixmaps: WARNING! Failed to reconnect " "pixmap for client [%d].\n", serverClient -> index); } #endif /* * FIXME: This is a bit cumbersome: at the end of each iteration * result will be reset to 1. Therefore at loop exit result will * always be 1 meaning the whole function will always return 1... */ result = 1; for (int i = 0; i < MAXCLIENTS; result = 1, i++) { if (clients[i] != NULL) { #ifdef TEST fprintf(stderr, "nxagentReconnectAllPixmaps: Going to reconnect pixmaps of client [%d].\n", i); #endif /* * Let the pixmap be reconnected as it was an image request * issued by the client owning the resource. The client index is * used as a subscript by the image routines to cache the data * per-client. */ FindClientResourcesByType(clients[i], RT_PIXMAP, nxagentReconnectPixmap, &result); #ifdef WARNING if (result == 0) { fprintf(stderr, "nxagentReconnectAllPixmaps: WARNING! Failed to reconnect " "pixmap for client [%d].\n", serverClient -> index); } #endif } } #ifdef TEST fprintf(stderr, "nxagentReconnectAllPixmaps: Pixmaps reconnection completed.\n"); #endif return result; } #ifdef TEST static void nxagentCheckOnePixmapIntegrity(void *p0, XID x1, void *p2) { PixmapPtr pPixmap = (PixmapPtr) p0; Bool *pBool = (Bool*) p2; if (!*pBool) { return; } if (pPixmap == nxagentDefaultScreen -> devPrivate) { #ifdef TEST fprintf(stderr, "nxagentCheckOnePixmapIntegrity: pixmap %p is screen.\n", (void *) pPixmap); #endif return; } if (pPixmap == nxagentDefaultScreen -> PixmapPerDepth[0]) { #ifdef TEST fprintf(stderr, "nxagentCheckOnePixmapIntegrity: pixmap %p is default stipple of screen.\n", (void *) pPixmap); #endif return; } *pBool = nxagentCheckPixmapIntegrity(pPixmap); } Bool nxagentCheckPixmapIntegrity(PixmapPtr pPixmap) { Bool integrity = True; unsigned long plane_mask = AllPlanes; PixmapPtr pVirtual = nxagentVirtualPixmap(pPixmap); unsigned int width = pPixmap -> drawable.width; unsigned int height = pPixmap -> drawable.height; unsigned int depth = pPixmap -> drawable.depth; int format = (depth == 1) ? XYPixmap : ZPixmap; if (width && height) { unsigned int length = nxagentImageLength(width, height, format, 0, depth); char *data = malloc(length); if (data == NULL) { FatalError("nxagentCheckPixmapIntegrity: Failed to allocate a buffer of size %d.\n", length); } XImage *image = XGetImage(nxagentDisplay, nxagentPixmap(pPixmap), 0, 0, width, height, plane_mask, format); if (image == NULL) { FatalError("XGetImage: Failed.\n"); SAFE_free(data); return False; } #ifdef WARNING fprintf(stderr, "nxagentCheckPixmapIntegrity: Image from X has length [%d] and checksum [0x%s].\n", length, nxagentChecksum(image->data, length)); #endif NXCleanImage(image); #ifdef WARNING fprintf(stderr, "nxagentCheckPixmapIntegrity: Image after clean has checksum [0x%s].\n", nxagentChecksum(image->data, length)); #endif fbGetImage((DrawablePtr) pVirtual, 0, 0, width, height, format, plane_mask, data); #ifdef WARNING fprintf(stderr, "nxagentCheckPixmapIntegrity: Image from FB has length [%d] and checksum [0x%s].\n", length, nxagentChecksum(data, length)); #endif if (image != NULL && memcmp(image -> data, data, length) != 0) { integrity = False; } else { integrity = True; #ifdef TEST fprintf(stderr, "nxagentCheckPixmapIntegrity: Pixmap at [%p] has been realized. " "Now remote and framebuffer data are synchronized.\n", (void *) pPixmap); #endif } #ifdef WARNING if (!integrity) { char *p = image -> data; char *q = data; for (int i = 0; i < length; i++) { if (p[i] != q[i]) { fprintf(stderr, "nxagentCheckPixmapIntegrity: Byte [%d] image -> data [%d] data [%d]. " "Buffers differ!\n", i, p[i], q[i]); } else { fprintf(stderr, "nxagentCheckPixmapIntegrity: Byte [%d] image -> data [%d] data [%d].\n", i, p[i], q[i]); } } fprintf(stderr, "nxagentCheckPixmapIntegrity: Pixmap at [%p] width [%d], height [%d], has been realized " "but the data buffer still differs.\n", (void *) pPixmap, width, height); fprintf(stderr, "nxagentCheckPixmapIntegrity: bytes_per_line [%d] byte pad [%d] format [%d].\n", image -> bytes_per_line, nxagentImagePad(width, height, 0, depth), image -> format); FatalError("nxagentCheckPixmapIntegrity: Image is corrupted!!\n"); } #endif if (image != NULL) { XDestroyImage(image); } SAFE_free(data); } else { #ifdef WARNING fprintf(stderr, "nxagentCheckPixmapIntegrity: Ignored pixmap at [%p] with geometry [%d] [%d].\n", (void *) pPixmap, width, height); #endif } return integrity; } Bool nxagentCheckAllPixmapIntegrity(void) { Bool imageIsGood = True; #ifdef TEST fprintf(stderr, "nxagentCheckAllPixmapIntegrity\n"); #endif FindClientResourcesByType(clients[serverClient -> index], RT_NX_PIXMAP, nxagentCheckOnePixmapIntegrity, &imageIsGood); for (int i = 0; (i < MAXCLIENTS) && (imageIsGood); i++) { if (clients[i]) { FindClientResourcesByType(clients[i], RT_PIXMAP, nxagentCheckOnePixmapIntegrity, &imageIsGood); } } #ifdef TEST fprintf(stderr, "nxagentCheckAllPixmapIntegrity: pixmaps integrity = %d.\n", imageIsGood); #endif return imageIsGood; } #endif void nxagentSynchronizeShmPixmap(DrawablePtr pDrawable, int xPict, int yPict, int wPict, int hPict) { if (pDrawable -> type == DRAWABLE_PIXMAP && nxagentIsShmPixmap((PixmapPtr) pDrawable)) { #ifdef TEST fprintf(stderr, "nxagentSynchronizeShmPixmap: WARNING! Synchronizing shared pixmap at [%p].\n", (void *) pDrawable); #endif GCPtr pGC = nxagentGetScratchGC(pDrawable -> depth, pDrawable -> pScreen); CARD32 attributes[3]; attributes[0] = 0x228b22; attributes[1] = 0xffffff; attributes[2] = FillSolid; ChangeGC(pGC, GCForeground | GCBackground | GCFillStyle, attributes); ValidateGC(pDrawable, pGC); int width = (wPict != 0 && wPict <= pDrawable -> width) ? wPict : pDrawable -> width; int height = (hPict != 0 && hPict <= pDrawable -> height) ? hPict : pDrawable -> height; int depth = pDrawable -> depth; int format = (depth == 1) ? XYPixmap : ZPixmap; int length = nxagentImageLength(width, height, format, 0, depth); Bool saveTrap = nxagentGCTrap; nxagentGCTrap = False; nxagentSplitTrap = True; nxagentFBTrap = True; char *data = malloc(length); if (data) { fbGetImage(nxagentVirtualDrawable(pDrawable), xPict, yPict, width, height, format, 0xffffffff, data); nxagentPutImage(pDrawable, pGC, depth, xPict, yPict, width, height, 0, format, data); SAFE_free(data); } #ifdef WARNING else { fprintf(stderr, "nxagentSynchronizeShmPixmap: WARNING! Failed to allocate memory for the operation.\n"); } #endif nxagentGCTrap = saveTrap; nxagentSplitTrap = False; nxagentFBTrap = False; nxagentFreeScratchGC(pGC); } } #ifdef DUMP /* * This function is useful to visualize a pixmap and check * its data consistency. To avoid the creation of many * windows, one pixmap only can be monitored at a time. */ Bool nxagentPixmapOnShadowDisplay(PixmapPtr pMap) { static Display *shadow; static Window win; static int init = True; static int showTime; static PixmapPtr pPixmap; static int depth; static int width; static int height; static int length; static unsigned int format; if (init) { if (pMap == NULL) { return False; } else { pPixmap = pMap; } depth = pPixmap -> drawable.depth; width = pPixmap -> drawable.width; height = pPixmap -> drawable.height; format = (depth == 1) ? XYPixmap : ZPixmap; shadow = XOpenDisplay("localhost:0"); if (shadow == NULL) { #ifdef WARNING fprintf(stderr, "nxagentPixmapOnShadowDisplay: WARNING! Shadow display not opened.\n"); #endif return False; } init = False; win = XCreateSimpleWindow(shadow, DefaultRootWindow(shadow), 0, 0, width, height, 0, 0xFFCC33, 0xFF); XMapWindow(shadow, win); XClearWindow(shadow, win); } /* FIXME: If the pixmap has a different depth from the window, the XPutImage returns a BadMatch. For example this may happen if the Render extension is enabled. Can we fix this creating a new pixmap? */ if (DisplayPlanes(shadow, DefaultScreen(shadow)) != depth) { #ifdef WARNING fprintf(stderr, "nxagentPixmapOnShadowDisplay: Pixmap and Window depths [%d - %d] are not equals!\n", depth, DisplayPlanes(shadow, DefaultScreen(shadow))); #endif return False; } /* * If the framebuffer is updated continuously, the nxagent * visualization becomes much too slow. */ if ((GetTimeInMillis() - showTime) < 500) { return False; } showTime = GetTimeInMillis(); length = nxagentImageLength(width, height, format, 0, depth); char * data = malloc(length); if (data == NULL) { #ifdef WARNING fprintf(stderr, "nxagentPixmapOnShadowDisplay: WARNING! Failed to allocate memory for the operation.\n"); #endif return False; } fbGetImage((DrawablePtr) nxagentVirtualPixmap(pPixmap), 0, 0, width, height, format, AllPlanes, data); Visual *pVisual = nxagentImageVisual((DrawablePtr) pPixmap, depth); if (pVisual == NULL) { #ifdef WARNING fprintf(stderr, "nxagentPixmapOnShadowDisplay: WARNING! Visual not found. Using default visual.\n"); #endif pVisual = nxagentVisuals[nxagentDefaultVisualIndex].visual; } XImage *image = XCreateImage(nxagentDisplay, pVisual, depth, format, 0, (char *) data, width, height, BitmapPad(nxagentDisplay), nxagentImagePad(width, format, 0, depth)); if (image == NULL) { #ifdef WARNING fprintf(stderr, "nxagentPixmapOnShadowDisplay: XCreateImage failed.\n"); #endif SAFE_free(data); return False; } XGCValues value = { .foreground = 0xff0000, .background = 0x000000, .plane_mask = 0xffffff, .fill_style = FillSolid }; XlibGC gc = XCreateGC(shadow, win, GCBackground | GCForeground | GCFillStyle | GCPlaneMask, &value); NXCleanImage(image); XPutImage(shadow, win, gc, image, 0, 0, 0, 0, width, height); XFreeGC(shadow, gc); if (image != NULL) { XDestroyImage(image); } return True; } Bool nxagentFbOnShadowDisplay(void) { static Display *shadow; static Window win; static int init = True; static int showTime; static int prevWidth, prevHeight; WindowPtr pWin = screenInfo.screens[0]->root; if (pWin == NULL) { #ifdef WARNING fprintf(stderr, "nxagentFbOnShadowDisplay: The parent window is NULL.\n"); #endif return False; } int depth = pWin -> drawable.depth; int width = pWin -> drawable.width; int height = pWin -> drawable.height; unsigned int format = (depth == 1) ? XYPixmap : ZPixmap; if (init) { shadow = XOpenDisplay("localhost:0"); if (shadow == NULL) { #ifdef WARNING fprintf(stderr, "nxagentFbOnShadowDisplay: WARNING! Shadow display not opened.\n"); #endif return False; } init = False; prevWidth = width; prevHeight = height; win = XCreateSimpleWindow(shadow, DefaultRootWindow(shadow), 0, 0, width, height, 0, 0xFFCC33, 0xFF); XMapWindow(shadow, win); XClearWindow(shadow, win); } if (DisplayPlanes(shadow, DefaultScreen(shadow)) != depth) { #ifdef WARNING fprintf(stderr, "nxagentFbOnShadowDisplay: Depths [%d - %d] are not equals!\n", depth, DisplayPlanes(shadow, DefaultScreen(shadow))); #endif return False; } /* * If the framebuffer is updated continuously, the nxagent * visualization becomes too much slow. */ if ((GetTimeInMillis() - showTime) < 500) { return False; } showTime = GetTimeInMillis(); /* * If the root window is resized, also the window on shadow * display must be resized. */ if (prevWidth != width || prevHeight != height) { prevWidth = width; prevHeight = height; XWindowChanges values = { .width = width, .height = height }; XConfigureWindow(shadow, win, CWWidth | CWHeight, &values); } int length = nxagentImageLength(width, height, format, 0, depth); char *data = malloc(length); if (data == NULL) { #ifdef WARNING fprintf(stderr, "nxagentFbOnShadowDisplay: WARNING! Failed to allocate memory for the operation.\n"); #endif return False; } fbGetImage((DrawablePtr)pWin, 0, 0, width, height, format, AllPlanes, data); Visual *pVisual = nxagentImageVisual((DrawablePtr) pWin, depth); if (pVisual == NULL) { #ifdef WARNING fprintf(stderr, "nxagentFbOnShadowDisplay: WARNING! Visual not found. Using default visual.\n"); #endif pVisual = nxagentVisuals[nxagentDefaultVisualIndex].visual; } XImage *image = XCreateImage(nxagentDisplay, pVisual, depth, format, 0, (char *) data, width, height, BitmapPad(nxagentDisplay), nxagentImagePad(width, format, 0, depth)); if (image == NULL) { #ifdef WARNING fprintf(stderr, "nxagentFbOnShadowDisplay: XCreateImage failed.\n"); #endif SAFE_free(data); return False; } XGCValues value = { .foreground = 0xff0000, .background = 0x000000, .plane_mask = 0xffffff, .fill_style = FillSolid }; XlibGC gc = XCreateGC(shadow, win, GCBackground | GCForeground | GCFillStyle | GCPlaneMask, &value); NXCleanImage(image); XPutImage(shadow, win, gc, image, 0, 0, 0, 0, width, height); XFreeGC(shadow, gc); if (image != NULL) { XDestroyImage(image); } return True; } #endif #ifdef DEBUG void nxagentPrintResourceTypes(void) { fprintf(stderr, "nxagentPrintResourceTypes: RT_PIXMAP [%lu].\n", (unsigned long) RT_PIXMAP); fprintf(stderr, "nxagentPrintResourceTypes: RT_NX_PIXMAP [%lu].\n", (unsigned long) RT_NX_PIXMAP); fprintf(stderr, "nxagentPrintResourceTypes: RT_GC [%lu].\n", (unsigned long) RT_GC); fprintf(stderr, "nxagentPrintResourceTypes: RT_NX_GC [%lu].\n", (unsigned long) RT_NX_GC); fprintf(stderr, "nxagentPrintResourceTypes: RT_FONT [%lu].\n", (unsigned long) RT_FONT); fprintf(stderr, "nxagentPrintResourceTypes: RT_NX_FONT [%lu].\n", (unsigned long) RT_NX_FONT); fprintf(stderr, "nxagentPrintResourceTypes: RT_CURSOR [%lu].\n", (unsigned long) RT_CURSOR); fprintf(stderr, "nxagentPrintResourceTypes: RT_WINDOW [%lu].\n", (unsigned long) RT_WINDOW); fprintf(stderr, "nxagentPrintResourceTypes: RT_COLORMAP [%lu].\n", (unsigned long) RT_COLORMAP); } void nxagentPrintResourcePredicate(void *value, XID id, XID type, void *cdata) { fprintf(stderr, "nxagentPrintResourcePredicate: Resource [%p] id [%lu] type [%lu].\n", (void *) value, (unsigned long) id, (unsigned long) type); } void nxagentPrintResources(void) { nxagentPrintResourceTypes(); for (int i = 0; i < MAXCLIENTS; i++) { if (clients[i]) { Bool result; fprintf(stderr, "nxagentPrintResources: Printing resources for client [%d]:\n", i); FindAllClientResources(clients[i], nxagentPrintResourcePredicate, &result); } } } #endif