aboutsummaryrefslogtreecommitdiff
path: root/nx-X11/programs/Xserver/hw/nxagent/Display.c
diff options
context:
space:
mode:
Diffstat (limited to 'nx-X11/programs/Xserver/hw/nxagent/Display.c')
-rw-r--r--nx-X11/programs/Xserver/hw/nxagent/Display.c2963
1 files changed, 2963 insertions, 0 deletions
diff --git a/nx-X11/programs/Xserver/hw/nxagent/Display.c b/nx-X11/programs/Xserver/hw/nxagent/Display.c
new file mode 100644
index 000000000..c764f50ae
--- /dev/null
+++ b/nx-X11/programs/Xserver/hw/nxagent/Display.c
@@ -0,0 +1,2963 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* 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. */
+/* */
+/**************************************************************************/
+
+/*
+
+Copyright 1993 by Davor Matic
+
+Permission to use, copy, modify, distribute, and sell this software
+and its documentation for any purpose is hereby granted without fee,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation. Davor Matic makes no representations about
+the suitability of this software for any purpose. It is provided "as
+is" without express or implied warranty.
+
+*/
+
+#include <signal.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <time.h>
+#include <errno.h>
+#include <stdbool.h>
+
+#include <nx-X11/X.h>
+#include <nx-X11/Xproto.h>
+#include "screenint.h"
+#include "input.h"
+#include "misc.h"
+#include "scrnintstr.h"
+#include "servermd.h"
+#include "windowstr.h"
+#include "dixstruct.h"
+
+#ifdef WATCH
+#include "unistd.h"
+#endif
+
+#include <nx/NXalert.h>
+
+#include "Agent.h"
+#include "Display.h"
+#include "Visual.h"
+#include "Options.h"
+#include "Error.h"
+#include "Init.h"
+#include "Args.h"
+#include "Image.h"
+
+#define Pixmap XlibPixmap
+#include "Icons.h"
+#undef Pixmap
+
+#include "Render.h"
+#include "Font.h"
+#include "Reconnect.h"
+#include "Events.h"
+#include "Dialog.h"
+#include "Client.h"
+#include "Splash.h"
+#include "Screen.h"
+#include "Handlers.h"
+
+#include <nx/NX.h>
+#include "compext/Compext.h"
+
+#include NXAGENT_ICON_NAME
+#include X2GOAGENT_ICON_NAME
+
+/*
+ * Set here the required log level.
+ */
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef WATCH
+
+Display *nxagentDisplay = NULL;
+XVisualInfo *nxagentVisuals = NULL;
+Bool nxagentTrue24 = False;
+
+int nxagentNumVisuals;
+int nxagentXConnectionNumber;
+
+int nxagentIOErrorHandler(Display *display);
+
+static Bool nxagentDisplayInfoSaved = False;
+static Display *nxagentDisplayBackup = NULL;
+static XlibGC nxagentBitmapGCBackup = NULL;
+static XVisualInfo *nxagentVisualsRecBackup;
+static int nxagentNumVisualsRecBackup;
+static int nxagentNumDefaultColormapsRecBackup;
+static int *nxagentDepthsRecBackup;
+static int nxagentNumDepthsRecBackup;
+static int nxagentDefaultDepthRecBackup;
+static int nxagentDisplayWidthRecBackup;
+static int nxagentDisplayHeightRecBackup;
+static Bool nxagentRenderEnableRecBackup;
+static Bool *nxagentVisualHasBeenIgnored;
+
+static enum
+{
+ NOTHING = 0,
+ OPENED,
+ GOT_VISUAL_INFO,
+ ALLOC_DEF_COLORMAP,
+ GOT_DEPTH_LIST,
+ GOT_PIXMAP_FORMAT_LIST,
+ EVERYTHING_DONE
+} reconnectDisplayState;
+
+int nxagentDefaultVisualIndex;
+Colormap *nxagentDefaultColormaps = NULL;
+int nxagentNumDefaultColormaps;
+int *nxagentDepths = NULL;
+int nxagentNumDepths;
+XPixmapFormatValues *nxagentPixmapFormats = NULL;
+XPixmapFormatValues *nxagentRemotePixmapFormats = NULL;
+int nxagentNumPixmapFormats;
+int nxagentRemoteNumPixmapFormats;
+Pixel nxagentBlackPixel;
+Pixel nxagentWhitePixel;
+Drawable nxagentDefaultDrawables[MAXDEPTH + 1];
+Pixmap nxagentScreenSaverPixmap;
+
+/*
+ * Also used in Cursor.c. There are huge problems
+ * using GC definition. This is to be reworked.
+ */
+
+XlibGC nxagentBitmapGC;
+
+/*
+ * The "confine" window is used in the nxagentConstrainCursor
+ * procedure. We are currently overriding the original Xnest
+ * behaviour. It is unclear what this window is used for.
+ */
+
+Window nxagentConfineWindow;
+
+Pixmap nxagentIconPixmap;
+Pixmap nxagentIconShape;
+Bool useXpmIcon = False;
+
+Bool nxagentMakeIcon(Display *display, Pixmap *nxIcon, Pixmap *nxMask);
+
+
+static void nxagentInitVisuals(void);
+static void nxagentSetDefaultVisual(void);
+static void nxagentInitDepths(void);
+static void nxagentInitPixmapFormats(void);
+
+static int nxagentCheckForDefaultDepthCompatibility(void);
+static int nxagentCheckForDepthsCompatibility(void);
+static int nxagentCheckForPixmapFormatsCompatibility(void);
+static int nxagentInitAndCheckVisuals(int flexibility);
+static int nxagentCheckForColormapsCompatibility(int flexibility);
+
+/*
+ * Save Internal implementation Also called in Reconnect.c.
+ */
+
+Display *nxagentInternalOpenDisplay(char *display);
+
+#ifdef NXAGENT_TIMESTAMP
+unsigned long startTime;
+#endif
+
+/*
+ * This is located in connection.c.
+ */
+
+extern void RejectWellKnownSockets(void);
+
+int nxagentServerOrder()
+{
+ int whichbyte = 1;
+
+ if (*((char *) &whichbyte))
+ return LSBFirst;
+
+ return MSBFirst;
+}
+
+/*
+ * FIXME: This error handler is not printing anything
+ * in the session log. This is OK once the session is
+ * started, because the error is handled by the other
+ * layers, but not before that point, as the agent
+ * would die without giving any feedback to the user
+ * (or, worse, to the NX server). We should check how
+ * many requests have been handled for this display
+ * and print a message if the display dies before the
+ * session is up and running.
+ */
+
+/*
+ * FIXME: This should be moved to Error.c, The other
+ * handlers should be probably moved to Handlers.c.
+ */
+
+int nxagentIOErrorHandler(Display *display)
+{
+ #ifdef TEST
+ fprintf(stderr, "nxagentIOErrorHandler: Got I/O error with nxagentException.ioError [%d].\n",
+ nxagentException.ioError);
+ #endif
+
+ nxagentException.ioError++;
+
+ #ifdef TEST
+ fprintf(stderr, "nxagentIOErrorHandler: Set nxagentException.ioError to [%d].\n",
+ nxagentException.ioError);
+ #endif
+
+ return 1;
+}
+
+/*
+ * Force a shutdown of any connection attempt
+ * while connecting to the remote display.
+ * This is needed to avoid a hang up in case
+ * of loopback connections to our own listen-
+ * ing sockets.
+ */
+
+static void nxagentRejectConnection(int signal)
+{
+ #ifdef TEST
+ fprintf(stderr, "nxagentRejectConnection: Going to reject client connections.\n");
+ #endif
+
+ RejectWellKnownSockets();
+
+ #ifdef TEST
+ fprintf(stderr, "nxagentRejectConnection: Setting new alarm to 5 seconds from now.\n");
+ #endif
+
+ /*
+ * A further timeout is unlikely to happen
+ * in the case of loopback connections.
+ */
+
+ alarm(5);
+}
+
+/*
+ * Ignore the signal if the NX transport is
+ * not running.
+ */
+
+static void nxagentSigusrHandler(int signal)
+{
+ #ifdef TEST
+ fprintf(stderr, "nxagentSigusrHandler: Nothing to do with signal [%d].\n",
+ signal);
+ #endif
+}
+
+static void nxagentSighupHandler(int signal)
+{
+ #ifdef TEST
+ fprintf(stderr, "nxagentSighupHandler: Handling signal with state [%s] transport [%d] server "
+ "generation [%ld].\n", DECODE_SESSION_STATE(nxagentSessionState),
+ NXTransRunning(NX_FD_ANY), serverGeneration);
+ #endif
+
+ if (signal != SIGHUP)
+ {
+ #ifdef PANIC
+ fprintf(stderr, "nxagentSighupHandler: PANIC! Invalid signal [%d] received in state [%s].\n",
+ signal, DECODE_SESSION_STATE(nxagentSessionState));
+ #endif
+
+ return;
+ }
+
+ if (dispatchException & DE_TERMINATE)
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentSighupHandler: Ignoring the signal while terminating the session.\n");
+ #endif
+
+ return;
+ }
+ else if (nxagentSessionState == SESSION_UP)
+ {
+ if (nxagentOption(Persistent) == 1)
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentSighupHandler: Handling the signal by disconnecting the agent.\n");
+ #endif
+
+ nxagentException.sigHup++;
+ }
+ else
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentSighupHandler: Ignoring the signal with persistency disabled.\n");
+ #endif
+ }
+
+ return;
+ }
+ else if (nxagentSessionState == SESSION_STARTING)
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentSighupHandler: Handling the signal by aborting the session.\n");
+ #endif
+
+ nxagentException.sigHup++;
+
+ return;
+ }
+ else if (nxagentSessionState == SESSION_DOWN)
+ {
+ if (NXTransRunning(NX_FD_ANY) == 1)
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentSighupHandler: Handling the signal by aborting the reconnection.\n");
+ #endif
+ }
+ else
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentSighupHandler: Handling the signal by resuming the session.\n");
+ #endif
+ }
+
+ nxagentException.sigHup++;
+
+ return;
+ }
+
+ #ifdef WARNING
+ fprintf(stderr, "nxagentSighupHandler: WARNING! Ignoring the signal in state [%s].\n",
+ DECODE_SESSION_STATE(nxagentSessionState));
+ #endif
+}
+
+static void nxagentSigchldHandler(int signal)
+{
+ int pid = 0;
+ int status;
+ int options;
+
+ #ifdef TEST
+ fprintf(stderr, "nxagentSigchldHandler: Going to check the children processes.\n");
+ #endif
+
+ options = WNOHANG | WUNTRACED;
+
+ /*
+ * Try with the pid of the dialog process.
+ * Leave the other children unaffected.
+ */
+
+ if (nxagentRootlessDialogPid)
+ {
+ pid = waitpid(nxagentRootlessDialogPid, &status, options);
+
+ if (pid == -1 && errno == ECHILD)
+ {
+ #ifdef WARNING
+ fprintf(stderr, "nxagentSigchldHandler: Got ECHILD waiting for child %d (Rootless dialog).\n",
+ nxagentRootlessDialogPid);
+ #endif
+
+ pid = nxagentRootlessDialogPid = 0;
+ }
+ }
+
+ if (pid == 0 && nxagentPulldownDialogPid)
+ {
+ pid = waitpid(nxagentPulldownDialogPid, &status, options);
+
+ if (pid == -1 && errno == ECHILD)
+ {
+ #ifdef WARNING
+ fprintf(stderr, "nxagentSigchldHandler: Got ECHILD waiting for child %d (Pulldown dialog).\n",
+ nxagentPulldownDialogPid);
+ #endif
+
+ pid = nxagentPulldownDialogPid = 0;
+ }
+ }
+
+ if (pid == 0 && nxagentKillDialogPid)
+ {
+ pid = waitpid(nxagentKillDialogPid, &status, options);
+
+ if (pid == -1 && errno == ECHILD)
+ {
+ #ifdef WARNING
+ fprintf(stderr, "nxagentSigchldHandler: Got ECHILD waiting for child %d (Kill dialog).\n",
+ nxagentKillDialogPid);
+ #endif
+
+ pid = nxagentKillDialogPid = 0;
+ }
+ }
+
+ if (pid == 0 && nxagentSuspendDialogPid)
+ {
+ pid = waitpid(nxagentSuspendDialogPid, &status, options);
+
+ if (pid == -1 && errno == ECHILD)
+ {
+ #ifdef WARNING
+ fprintf(stderr, "nxagentSigchldHandler: Got ECHILD waiting for child %d (Suspend dialog).\n",
+ nxagentSuspendDialogPid);
+ #endif
+
+ pid = nxagentSuspendDialogPid = 0;
+ }
+ }
+
+ if (pid == 0 && nxagentFontsReplacementDialogPid)
+ {
+ pid = waitpid(nxagentFontsReplacementDialogPid, &status, options);
+
+ if (pid == -1 && errno == ECHILD)
+ {
+ #ifdef WARNING
+ fprintf(stderr, "nxagentSigchldHandler: Got ECHILD waiting for child %d (Fonts replacement).\n",
+ nxagentFontsReplacementDialogPid);
+ #endif
+
+ pid = nxagentFontsReplacementDialogPid = 0;
+ }
+ }
+
+ if (pid == 0 && nxagentEnableRandRModeDialogPid)
+ {
+ pid = waitpid(nxagentEnableRandRModeDialogPid, &status, options);
+
+ if (pid == -1 && errno == ECHILD)
+ {
+ #ifdef WARNING
+ fprintf(stderr, "nxagentSigchldHandler: Got ECHILD waiting for child %d (EnableRandRMode dialog).\n",
+ nxagentEnableRandRModeDialogPid);
+ #endif
+
+ pid = nxagentEnableRandRModeDialogPid = 0;
+ }
+ }
+
+ if (pid == 0 && nxagentDisableRandRModeDialogPid)
+ {
+ pid = waitpid(nxagentDisableRandRModeDialogPid, &status, options);
+
+ if (pid == -1 && errno == ECHILD)
+ {
+ #ifdef WARNING
+ fprintf(stderr, "nxagentSigchldHandler: Got ECHILD waiting for child %d (DisableRandRMode dialog).\n",
+ nxagentDisableRandRModeDialogPid);
+ #endif
+
+ pid = nxagentDisableRandRModeDialogPid = 0;
+ }
+ }
+
+ if (pid == 0 && nxagentEnableDeferModePid)
+ {
+ pid = waitpid(nxagentEnableDeferModePid, &status, options);
+
+ if (pid == -1 && errno == ECHILD)
+ {
+ #ifdef WARNING
+ fprintf(stderr, "nxagentSigchldHandler: Got ECHILD waiting for child %d (EnableDeferMode dialog).\n",
+ nxagentEnableDeferModePid);
+ #endif
+
+ pid = nxagentEnableDeferModePid = 0;
+ }
+ }
+
+ if (pid == 0 && nxagentDisableDeferModePid)
+ {
+ pid = waitpid(nxagentDisableDeferModePid, &status, options);
+
+ if (pid == -1 && errno == ECHILD)
+ {
+ #ifdef WARNING
+ fprintf(stderr, "nxagentSigchldHandler: Got ECHILD waiting for child %d (DisableDeferMode dialog).\n",
+ nxagentDisableDeferModePid);
+ #endif
+
+ pid = nxagentDisableDeferModePid = 0;
+ }
+ }
+
+ if (pid == -1)
+ {
+ FatalError("Got error '%s' waiting for the child.\n", strerror(errno));
+ }
+
+ if (pid > 0)
+ {
+ if (WIFSTOPPED(status))
+ {
+ #ifdef WARNING
+ fprintf(stderr, "nxagentSigchldHandler: Child process [%d] was stopped "
+ "with signal [%d].\n", pid, (WSTOPSIG(status)));
+ #endif
+ }
+ else
+ {
+ #ifdef TEST
+
+ if (WIFEXITED(status))
+ {
+ fprintf(stderr, "nxagentSigchldHandler: Child process [%d] exited "
+ "with status [%d].\n", pid, (WEXITSTATUS(status)));
+ }
+ else if (WIFSIGNALED(status))
+ {
+ fprintf(stderr, "nxagentSigchldHandler: Child process [%d] died "
+ "because of signal [%d].\n", pid, (WTERMSIG(status)));
+ }
+
+ #endif
+
+ nxagentResetDialog(pid);
+ }
+ }
+ else if (pid == 0)
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentSigchldHandler: Forwarding the signal to the NX transport.\n");
+ #endif
+
+ NXTransSignal(SIGCHLD, NX_SIGNAL_RAISE);
+ }
+
+ return;
+}
+
+Display *nxagentInternalOpenDisplay(char *display)
+{
+ Display *newDisplay;
+
+ struct sigaction oldAction;
+ struct sigaction newAction;
+
+ int result;
+
+ /*
+ * Stop the smart schedule timer since
+ * it uses SIGALRM as we do.
+ */
+
+ nxagentStopTimer();
+
+ /*
+ * Install the handler rejecting a possible
+ * loopback connection.
+ */
+/*
+FIXME: Should print a warning if the user tries to let
+ the agent impersonate the same display as the
+ display where the agent is supposed to connect.
+ We actually handle this by means of RejectWell-
+ KnownSockets() but without giving to the user
+ a friendly explaination for the error.
+*/
+
+ newAction.sa_handler = nxagentRejectConnection;
+
+ sigfillset(&newAction.sa_mask);
+
+ newAction.sa_flags = 0;
+
+ while (((result = sigaction(SIGALRM, &newAction,
+ &oldAction)) == -1) && (errno == EINTR));
+
+ if (result == -1)
+ {
+ FatalError("Can't set alarm for rejecting connections.");
+ }
+
+ alarm(10);
+
+ #ifdef TEST
+ fprintf(stderr, "nxagentInternalOpenDisplay: Going to open the display [%s].\n",
+ display);
+ #endif
+
+ newDisplay = XOpenDisplay(display);
+
+ alarm(0);
+
+ while (((result = sigaction(SIGALRM, &oldAction,
+ NULL)) == -1) && (errno == EINTR));
+
+ if (result == -1)
+ {
+ FatalError("Can't restore alarm for rejecting connections.");
+ }
+
+ #ifdef TEST
+ fprintf(stderr, "nxagentInternalOpenDisplay: Setting the NX flush policy to immediate.\n");
+ #endif
+
+ NXSetDisplayPolicy(nxagentDisplay, NXPolicyImmediate);
+
+ #ifdef TEST
+ fprintf(stderr, "nxagentInternalOpenDisplay: Function returned display at [%p].\n",
+ (void *) newDisplay);
+ #endif
+
+ return newDisplay;
+}
+
+static void nxagentDisplayBlockHandler(Display *display, int reason)
+{
+ if (nxagentDisplay != NULL)
+ {
+ /*
+ * Don't allow the smart schedule to
+ * interrupt the agent while waiting
+ * for the remote display.
+ */
+
+ #ifdef DEBUG
+ fprintf(stderr, "nxagentDisplayBlockHandler: BLOCK! Stopping the smart schedule timer.\n");
+ #endif
+
+ nxagentStopTimer();
+
+ if (reason == NXBlockRead)
+ {
+ #ifdef DEBUG
+ fprintf(stderr, "nxagentDisplayBlockHandler: BLOCK! Display is blocking for [read].\n");
+ #endif
+ }
+ else
+ {
+ #ifdef DEBUG
+ fprintf(stderr, "nxagentDisplayBlockHandler: BLOCK! Display is blocking for [write].\n");
+ #endif
+
+ nxagentBlocking = 1;
+
+ if (!SmartScheduleSignalEnable)
+ {
+
+ /*
+ * Let the dispatch attend the next
+ * client.
+ */
+
+ #ifdef DEBUG
+ fprintf(stderr, "nxagentDisplayBlockHandler: BLOCK! Yielding with agent blocked.\n");
+ #endif
+
+ nxagentDispatch.start = GetTimeInMillis();
+
+ nxagentDispatch.in = nxagentBytesIn;
+ nxagentDispatch.out = nxagentBytesOut;
+
+ }
+
+ /*
+ * Give a chance to the next client.
+ */
+
+ isItTimeToYield = 1;
+ }
+ }
+}
+
+static void nxagentDisplayWriteHandler(Display *display, int length)
+{
+ if (nxagentDisplay != NULL)
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentDisplayWriteHandler: WRITE! Called with [%d] bytes written.\n",
+ length);
+ #endif
+
+ /*
+ * Notify the dispatch handler.
+ */
+
+ nxagentDispatchHandler(NULL, 0, length);
+
+ if (nxagentOption(LinkType) == LINK_TYPE_NONE)
+ {
+ nxagentFlush = GetTimeInMillis();
+ }
+ }
+}
+
+static CARD32 nxagentRateTime = 5000;
+static CARD32 nxagentLastTime;
+static unsigned int nxagentRate = 0;
+
+int nxagentGetDataRate(void)
+{
+ return nxagentRate;
+}
+
+static void nxagentDisplayFlushHandler(Display *display, int length)
+{
+ CARD32 time;
+
+ if (nxagentDisplay != NULL)
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentDisplayFlushHandler: FLUSH! Called with [%d] bytes flushed.\n",
+ length);
+ #endif
+
+ nxagentCongestion = NXDisplayCongestion(nxagentDisplay);
+
+ #ifdef TEST
+ fprintf(stderr, "nxagentDisplayFlushHandler: FLUSH! Current congestion level is [%d].\n",
+ nxagentCongestion);
+ #endif
+
+ if (nxagentOption(LinkType) != LINK_TYPE_NONE)
+ {
+ nxagentFlush = GetTimeInMillis();
+
+ time = nxagentFlush;
+
+ time = time - nxagentLastTime;
+
+ if (time < nxagentRateTime)
+ {
+ nxagentRate = ((nxagentRate * (nxagentRateTime - time) +
+ length) * 1000) / nxagentRateTime;
+ }
+ else
+ {
+ nxagentRate = (length * 1000) / nxagentRateTime;
+ }
+
+ nxagentLastTime = nxagentFlush;
+ }
+ }
+}
+
+static int nxagentDisplayErrorPredicate(Display *display, int error)
+{
+ #ifdef TEST
+ fprintf(stderr, "nxagentDisplayErrorPredicate: CHECK! Error is [%d] with [%d][%d][%d][%d][%d].\n",
+ ((error == 1) || (dispatchException & DE_RESET) != 0 ||
+ (dispatchException & DE_TERMINATE) != 0 || nxagentException.sigHup > 0 ||
+ nxagentException.ioError > 0), error, (dispatchException & DE_RESET) != 0,
+ (dispatchException & DE_TERMINATE), nxagentException.sigHup > 0,
+ nxagentException.ioError > 0);
+ #endif
+
+ if (error == 0)
+ {
+ if ((dispatchException & DE_RESET) != 0 ||
+ (dispatchException & DE_TERMINATE))
+ {
+ return 1;
+ }
+ else if (nxagentException.sigHup > 0 ||
+ nxagentException.ioError > 0)
+ {
+ NXForceDisplayError(display);
+
+ return 1;
+ }
+ }
+
+ return error;
+}
+
+void nxagentInstallDisplayHandlers()
+{
+ /*
+ * If the display was already opened, be sure
+ * all structures are freed.
+ */
+
+ nxagentResetDisplayHandlers();
+
+ /*
+ * We want the Xlib I/O error handler to return,
+ * instead of quitting the application. Using
+ * setjmp()/longjmp() leaves the door open to
+ * unexpected bugs when dealing with interaction
+ * with the other X server layers.
+ */
+
+ NXHandleDisplayError(1);
+
+ NXSetDisplayBlockHandler(nxagentDisplayBlockHandler);
+
+ NXSetDisplayWriteHandler(nxagentDisplayWriteHandler);
+
+ NXSetDisplayFlushHandler(nxagentDisplayFlushHandler, NULL);
+
+ /*
+ * Override the default Xlib error handler.
+ */
+
+ XSetIOErrorHandler(nxagentIOErrorHandler);
+
+ /*
+ * Let Xlib become aware of our interrupts. In theory
+ * we don't need to have the error handler installed
+ * during the normal operations and could simply let
+ * the dispatcher handle the interrupts. In practice
+ * it's better to have Xlib invalidating the display
+ * as soon as possible rather than incurring in the
+ * risk of entering a loop that doesn't care checking
+ * the display errors explicitly.
+ */
+
+ #ifdef TEST
+ fprintf(stderr, "nxagentInstallDisplayHandlers: Installing the error function predicate.\n");
+ #endif
+
+ NXSetDisplayErrorPredicate(nxagentDisplayErrorPredicate);
+}
+
+void nxagentPostInstallDisplayHandlers()
+{
+ /*
+ * This is executed after having opened the
+ * display, once we know the display address.
+ */
+
+ if (nxagentDisplay != NULL)
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentPostInstallDisplayHandlers: Initializing the NX display internals.\n");
+ #endif
+
+ NXInitDisplay(nxagentDisplay);
+/*
+FIXME: What is the most appropriate number of elements?
+
+ NXInitCache(nxagentDisplay, 128);
+*/
+ NXInitCache(nxagentDisplay, 256);
+
+ NXSetDisplayFlushHandler(nxagentDisplayFlushHandler, nxagentDisplay);
+ }
+
+ /*
+ * Handler for the Xlib protocol errors.
+ */
+
+ XSetErrorHandler(nxagentErrorHandler);
+}
+
+void nxagentResetDisplayHandlers()
+{
+ if (nxagentDisplay != NULL)
+ {
+ /*
+ * Free the internal nxcompext
+ * structures.
+ */
+
+ NXResetDisplay(nxagentDisplay);
+
+ /*
+ * Remove the display descriptor
+ * from the listened sockets.
+ */
+
+ nxagentRemoveXConnection();
+
+ /*
+ * Restart the suspended clients.
+ */
+
+ nxagentWakeupByReconnect();
+
+ nxagentReleaseAllSplits();
+ }
+
+ /*
+ * Reset the display to a healty state.
+ */
+
+ nxagentBuffer = 0;
+ nxagentBlocking = 0;
+ nxagentCongestion = 0;
+
+ /*
+ * Reset the counter of synchronization
+ * requests pending.
+ */
+
+ nxagentTokens.soft = 0;
+ nxagentTokens.hard = 0;
+ nxagentTokens.pending = 0;
+
+ /*
+ * Reset the current dispatch information.
+ */
+
+ nxagentDispatch.client = UNDEFINED;
+ nxagentDispatch.in = 0;
+ nxagentDispatch.out = 0;
+ nxagentDispatch.start = 0;
+}
+
+void nxagentInstallSignalHandlers()
+{
+ #ifdef TEST
+ fprintf(stderr, "nxagentInstallSignalHandlers: Installing the agent signal handlers.\n");
+ #endif
+
+ /*
+ * Keep the default X server's handlers for
+ * SIGINT and SIGTERM and restore the other
+ * signals of interest to our defaults.
+ */
+
+ struct sigaction newAction;
+
+ int result;
+
+ /*
+ * By default nxcomp installs its signal handlers.
+ * We need to ensure that SIGUSR1 and SIGUSR2 are
+ * ignored if the NX transport is not running.
+ */
+
+ newAction.sa_handler = nxagentSigusrHandler;
+
+ sigfillset(&newAction.sa_mask);
+
+ newAction.sa_flags = 0;
+
+ while (((result = sigaction(SIGUSR1, &newAction,
+ NULL)) == -1) && (errno == EINTR));
+
+ if (result == -1)
+ {
+ FatalError("Can't set the handler for user signal 1.");
+ }
+
+ while (((result = sigaction(SIGUSR2, &newAction,
+ NULL)) == -1) && (errno == EINTR));
+
+ if (result == -1)
+ {
+ FatalError("Can't set the handler for user signal 2.");
+ }
+
+ /*
+ * Reset the SIGALRM to the default.
+ */
+
+ nxagentStopTimer();
+
+ newAction.sa_handler = SIG_DFL;
+
+ sigfillset(&newAction.sa_mask);
+
+ while (((result = sigaction(SIGALRM, &newAction,
+ NULL)) == -1) && (errno == EINTR));
+
+ if (result == -1)
+ {
+ FatalError("Can't set the handler for alarm signal.");
+ }
+
+ /*
+ * Let the smart schedule set the SIGALRM
+ * handler again.
+ */
+
+ nxagentInitTimer();
+
+ /*
+ * Install our own handler for the SIGHUP.
+ */
+
+ newAction.sa_handler = nxagentSighupHandler;
+
+ sigfillset(&newAction.sa_mask);
+
+ newAction.sa_flags = 0;
+
+ while (((result = sigaction(SIGHUP, &newAction,
+ NULL)) == -1) && (errno == EINTR));
+
+ if (result == -1)
+ {
+ FatalError("Can't set the handler for session suspend.");
+ }
+
+ /*
+ * We need to be notified about our children.
+ */
+
+ newAction.sa_handler = nxagentSigchldHandler;
+
+ sigfillset(&newAction.sa_mask);
+
+ newAction.sa_flags = 0;
+
+ while (((result = sigaction(SIGCHLD, &newAction,
+ NULL)) == -1) && (errno == EINTR));
+
+ if (result == -1)
+ {
+ FatalError("Can't set the handler for children.");
+ }
+}
+
+void nxagentPostInstallSignalHandlers()
+{
+ #ifdef TEST
+ fprintf(stderr, "nxagentPostInstallSignalHandlers: Dealing with the proxy signal handlers.\n");
+ #endif
+
+ /*
+ * Reconfigure our signal handlers to work well
+ * with the NX transport.
+ *
+ * Let our handlers manage the SIGINT and SIGTERM.
+ * The following calls will tell the NX transport
+ * to restore the old handlers (those originally
+ * installed by us or the X server).
+ */
+
+ NXTransSignal(SIGINT, NX_SIGNAL_DISABLE);
+ NXTransSignal(SIGTERM, NX_SIGNAL_DISABLE);
+
+ /*
+ * Also tell the proxy to ignore the SIGHUP.
+ */
+
+ NXTransSignal(SIGHUP, NX_SIGNAL_DISABLE);
+
+ /*
+ * Both the proxy and the agent need to catch
+ * their children, so we'll have to send the
+ * signal to transport.
+ */
+
+ NXTransSignal(SIGCHLD, NX_SIGNAL_DISABLE);
+
+ /*
+ * Let the NX transport take care of SIGUSR1
+ * and SIGUSR2.
+ */
+}
+
+void nxagentResetSignalHandlers()
+{
+ struct sigaction newAction;
+
+ int result;
+
+ memset(&newAction, 0, sizeof(newAction));
+
+ /*
+ * Reset the signal handlers
+ * to a well known state.
+ */
+
+ #ifdef TEST
+ fprintf(stderr, "nxagentResetSignalHandlers: Resetting the agent the signal handlers.\n");
+ #endif
+
+ /*
+ * Reset the SIGALRM to the default.
+ */
+
+ nxagentStopTimer();
+
+ newAction.sa_handler = SIG_DFL;
+
+ sigfillset(&newAction.sa_mask);
+
+ while (((result = sigaction(SIGALRM, &newAction,
+ NULL)) == -1) && (errno == EINTR));
+
+ if (result == -1)
+ {
+ FatalError("Can't set the handler for alarm signal.");
+ }
+
+ /*
+ * Let the smart schedule set the SIGALRM
+ * handler again.
+ */
+
+ nxagentInitTimer();
+
+}
+
+void nxagentOpenDisplay(int argc, char *argv[])
+{
+ int i;
+
+ if (!nxagentDoFullGeneration) return;
+
+ #ifdef NXAGENT_TIMESTAMP
+
+ startTime = GetTimeInMillis();
+
+ fprintf(stderr, "Display: Opening the display on real X server with time [%d] ms.\n",
+ GetTimeInMillis() - startTime);
+
+ #endif
+
+ /*
+ * Initialize the reconnector only in the case
+ * of persistent sessions.
+ */
+
+ if (nxagentOption(Persistent))
+ {
+ nxagentInitReconnector();
+ }
+
+ if (*nxagentDisplayName == '\0')
+ {
+ strncpy(nxagentDisplayName, XDisplayName(NULL), 1023);
+
+ nxagentDisplayName[1023] = '\0';
+ }
+
+ nxagentCloseDisplay();
+
+ nxagentInstallSignalHandlers();
+
+ nxagentInstallDisplayHandlers();
+
+ nxagentDisplay = nxagentInternalOpenDisplay(nxagentDisplayName);
+
+ nxagentPostInstallSignalHandlers();
+
+ nxagentPostInstallDisplayHandlers();
+
+ if (nxagentDisplay == NULL)
+ {
+/*
+FIXME: The agent should never exit the program with a FatalError()
+ but rather use a specific function that may eventually call
+ FatalError() on its turn.
+*/
+ FatalError("Unable to open display '%s'.\n", nxagentDisplayName);
+ }
+
+ if (nxagentSynchronize)
+ XSynchronize(nxagentDisplay, True);
+
+ nxagentXConnectionNumber = XConnectionNumber(nxagentDisplay);
+
+ #ifdef TEST
+ fprintf(stderr, "nxagentOpenDisplay: Display image order is [%d] bitmap order is [%d].\n",
+ ImageByteOrder(nxagentDisplay), BitmapBitOrder(nxagentDisplay));
+
+ fprintf(stderr, "nxagentOpenDisplay: Display scanline unit is [%d] scanline pad is [%d].\n",
+ BitmapUnit(nxagentDisplay), BitmapPad(nxagentDisplay));
+ #endif
+
+ #ifdef WATCH
+
+ fprintf(stderr, "nxagentOpenDisplay: Watchpoint 1.\n");
+
+/*
+Reply Total Cached Bits In Bits Out Bits/Reply Ratio
+------- ----- ------ ------- -------- ---------- -----
+#1 U 1 1 256 bits (0 KB) -> 150 bits (0 KB) -> 256/1 -> 150/1 = 1.707:1
+#20 1 1 119104 bits (15 KB) -> 28 bits (0 KB) -> 119104/1 -> 28/1 = 4253.714:1
+#98 2 512 bits (0 KB) -> 84 bits (0 KB) -> 256/1 -> 42/1 = 6.095:1
+*/
+
+ sleep(60);
+
+ #endif
+
+ #ifdef NXAGENT_TIMESTAMP
+
+ fprintf(stderr, "Display: Display on real X server opened with time [%d] ms.\n",
+ GetTimeInMillis() - startTime);
+
+ #endif
+
+ nxagentUseNXTrans =
+ nxagentPostProcessArgs(nxagentDisplayName, nxagentDisplay,
+ DefaultScreenOfDisplay(nxagentDisplay));
+
+ /*
+ * Processing the arguments all the timeouts
+ * have been set. Now we have to change the
+ * screen-saver timeout.
+ */
+
+ nxagentSetScreenSaverTime();
+
+ nxagentInitVisuals();
+
+ nxagentNumDefaultColormaps = nxagentNumVisuals;
+ nxagentDefaultColormaps = (Colormap *)malloc(nxagentNumDefaultColormaps *
+ sizeof(Colormap));
+
+ for (i = 0; i < nxagentNumDefaultColormaps; i++)
+ {
+ nxagentDefaultColormaps[i] = XCreateColormap(nxagentDisplay,
+ DefaultRootWindow(nxagentDisplay),
+ nxagentVisuals[i].visual,
+ AllocNone);
+ }
+
+ #ifdef WATCH
+
+ fprintf(stderr, "nxagentOpenDisplay: Watchpoint 4.\n");
+
+/*
+Reply Total Cached Bits In Bits Out Bits/Reply Ratio
+------- ----- ------ ------- -------- ---------- -----
+N/A
+*/
+
+ sleep(30);
+
+ #endif
+
+ nxagentBlackPixel = BlackPixel(nxagentDisplay, DefaultScreen(nxagentDisplay));
+ nxagentWhitePixel = WhitePixel(nxagentDisplay, DefaultScreen(nxagentDisplay));
+
+ #ifdef WATCH
+
+ fprintf(stderr, "nxagentOpenDisplay: Watchpoint 5.\n");
+
+/*
+Reply Total Cached Bits In Bits Out Bits/Reply Ratio
+------- ----- ------ ------- -------- ---------- -----
+N/A
+*/
+
+ sleep(30);
+
+ #endif
+
+ /*
+ * Initialize the agent's event mask that will be requested
+ * for the root and all the top level windows. If the nested
+ * window is a child of an existing window, we will need to
+ * receive StructureNotify events. If we are going to manage
+ * the changes in root window's visibility we'll also need
+ * VisibilityChange events.
+ */
+
+/*
+FIXME: Use of nxagentParentWindow is strongly deprecated.
+ We need also to clarify which events are selected
+ in the diferent operating modes.
+*/
+
+ nxagentInitDefaultEventMask();
+
+ /*
+ * Initialize the pixmap depths and formats.
+ */
+
+ nxagentInitDepths();
+
+ nxagentInitPixmapFormats();
+ (void) nxagentCheckForPixmapFormatsCompatibility();
+
+ /*
+ * Create a pixmap for each depth matching the
+ * local supported formats with format available
+ * on the remote display.
+ */
+
+ nxagentSetDefaultDrawables();
+
+ #ifdef RENDER
+ if (nxagentRenderEnable)
+ {
+ nxagentRenderExtensionInit();
+ }
+ #endif
+
+ /*
+ * This GC is referenced in Cursor.c. It can be
+ * probably removed.
+ */
+
+ nxagentBitmapGC = XCreateGC(nxagentDisplay, nxagentDefaultDrawables[1], 0L, NULL);
+
+ /*
+ * Note that this "confine window" is useless at the
+ * moment as we reimplement nxagentConstrainCursor()
+ * to skip the "constrain" stuff.
+ */
+
+ #ifdef TEST
+ fprintf(stderr, "nxagentOpenDisplay: Going to create agent's confine window.\n");
+ #endif
+
+ nxagentConfineWindow = XCreateWindow(nxagentDisplay,
+ DefaultRootWindow(nxagentDisplay),
+ 0, 0, 1, 1, 0, 0,
+ InputOnly,
+ CopyFromParent,
+ 0L, NULL);
+
+ #ifdef TEST
+ fprintf(stderr, "nxagentOpenDisplay: Created agent's confine window with id [%ld].\n",
+ nxagentConfineWindow);
+ #endif
+
+ if (!(nxagentUserGeometry.flag & XValue))
+ {
+ nxagentChangeOption(RootX, 0);
+ }
+
+ if (!(nxagentUserGeometry.flag & YValue))
+ {
+ nxagentChangeOption(RootY, 0);
+ }
+
+ if (nxagentParentWindow == 0)
+ {
+ if (!(nxagentUserGeometry.flag & WidthValue))
+ {
+ if (nxagentOption(Fullscreen))
+ {
+ nxagentChangeOption(RootWidth, DisplayWidth(nxagentDisplay, DefaultScreen(nxagentDisplay)));
+ }
+ else
+ {
+ nxagentChangeOption(RootWidth, 3 * DisplayWidth(nxagentDisplay,
+ DefaultScreen(nxagentDisplay)) / 4);
+ }
+ }
+
+ if (!(nxagentUserGeometry.flag & HeightValue))
+ {
+ if (nxagentOption(Fullscreen))
+ {
+ nxagentChangeOption(RootHeight, DisplayHeight(nxagentDisplay, DefaultScreen(nxagentDisplay)));
+ }
+ else
+ {
+ nxagentChangeOption(RootHeight, 3 * DisplayHeight(nxagentDisplay,
+ DefaultScreen(nxagentDisplay)) / 4);
+ }
+ }
+ }
+
+ if (!nxagentUserBorderWidth)
+ {
+ nxagentChangeOption(BorderWidth, 1);
+ }
+
+ nxagentLogoDepth = DefaultDepth(nxagentDisplay,
+ DefaultScreen(nxagentDisplay)
+ );
+
+ nxagentLogoBlack = 0x000000;
+ nxagentLogoRed = 0xff0000;
+ nxagentLogoWhite = 0xffffff;
+ nxagentLogoGray = 0x222222;
+
+ #ifdef WATCH
+
+ fprintf(stderr, "nxagentOpenDisplay: Watchpoint 5.1.\n");
+
+/*
+Reply Total Cached Bits In Bits Out Bits/Reply Ratio
+------- ----- ------ ------- -------- ---------- -----
+N/A
+*/
+
+ sleep(30);
+
+ #endif
+
+ useXpmIcon = nxagentMakeIcon(nxagentDisplay, &nxagentIconPixmap, &nxagentIconShape);
+
+ #ifdef WATCH
+
+ fprintf(stderr, "nxagentOpenDisplay: Watchpoint 5.2.\n");
+
+/*
+Reply Total Cached Bits In Bits Out Bits/Reply Ratio
+------- ----- ------ ------- -------- ---------- -----
+#84 2 512 bits (0 KB) -> 76 bits (0 KB) -> 256/1 -> 38/1 = 6.737:1
+*/
+
+ sleep(30);
+
+ #endif
+
+ #ifdef WATCH
+
+ fprintf(stderr, "nxagentOpenDisplay: Watchpoint 6.\n");
+
+/*
+Reply Total Cached Bits In Bits Out Bits/Reply Ratio
+------- ----- ------ ------- -------- ---------- -----
+N/A
+*/
+
+ sleep(30);
+
+ #endif
+
+ #ifdef NXAGENT_TIMESTAMP
+
+ fprintf(stderr, "Display: Open of the display finished with time [%d] ms.\n",
+ GetTimeInMillis() - startTime);
+
+ #endif
+
+ if (nxagentOption(Persistent))
+ {
+ reconnectDisplayState = EVERYTHING_DONE;
+ }
+}
+
+void nxagentSetDefaultVisual(void)
+{
+ XVisualInfo vi;
+
+ int i;
+
+ if (nxagentUserDefaultClass || nxagentUserDefaultDepth)
+ {
+ nxagentDefaultVisualIndex = UNDEFINED;
+
+ for (i = 0; i < nxagentNumVisuals; i++)
+ {
+ if ((!nxagentUserDefaultClass ||
+ nxagentVisuals[i].class == nxagentDefaultClass)
+ &&
+ (!nxagentUserDefaultDepth ||
+ nxagentVisuals[i].depth == nxagentDefaultDepth))
+ {
+ nxagentDefaultVisualIndex = i;
+
+ break;
+ }
+ }
+
+ if (nxagentDefaultVisualIndex == UNDEFINED)
+ {
+ FatalError("Unable to find desired default visual.\n");
+ }
+ }
+ else
+ {
+ vi.visualid = XVisualIDFromVisual(DefaultVisual(nxagentDisplay,
+ DefaultScreen(nxagentDisplay)));
+ nxagentDefaultVisualIndex = 0;
+
+ for (i = 0; i < nxagentNumVisuals; i++)
+ {
+ if (vi.visualid == nxagentVisuals[i].visualid)
+ {
+ nxagentDefaultVisualIndex = i;
+ }
+ }
+ }
+}
+
+void nxagentInitVisuals(void)
+{
+ XVisualInfo vi;
+ XVisualInfo *viList = NULL;
+
+ long mask;
+ int i, viNumList;
+
+ mask = VisualScreenMask;
+ vi.screen = DefaultScreen(nxagentDisplay);
+ vi.depth = DefaultDepth(nxagentDisplay, DefaultScreen(nxagentDisplay));
+ viList = XGetVisualInfo(nxagentDisplay, mask, &vi, &viNumList);
+ nxagentVisuals = (XVisualInfo *) malloc(viNumList * sizeof(XVisualInfo));
+ nxagentNumVisuals = 0;
+
+ for (i = 0; i < viNumList; i++)
+ {
+ if (viList[i].depth == vi.depth)
+ {
+ if (nxagentVisuals != NULL)
+ {
+ memcpy(nxagentVisuals + nxagentNumVisuals, viList + i, sizeof(XVisualInfo));
+ }
+
+ #ifdef DEBUG
+ fprintf(stderr, "nxagentInitVisuals: Visual:\n");
+ fprintf(stderr, "\tdepth = %d\n", nxagentVisuals[nxagentNumVisuals].depth);
+ fprintf(stderr, "\tclass = %d\n", nxagentVisuals[nxagentNumVisuals].class);
+ fprintf(stderr, "\tmask = (%lu,%lu,%lu)\n",
+ nxagentVisuals[nxagentNumVisuals].red_mask,
+ nxagentVisuals[nxagentNumVisuals].green_mask,
+ nxagentVisuals[nxagentNumVisuals].blue_mask);
+ fprintf(stderr, "\tcolormap size = %d\n", nxagentVisuals[nxagentNumVisuals].colormap_size);
+ fprintf(stderr, "\tbits_per_rgb = %d\n", nxagentVisuals[nxagentNumVisuals].bits_per_rgb);
+ #endif
+
+ nxagentNumVisuals++;
+ }
+ }
+
+ if (nxagentVisuals != NULL)
+ {
+ nxagentVisuals = (XVisualInfo *) realloc(nxagentVisuals,
+ nxagentNumVisuals * sizeof(XVisualInfo));
+ }
+
+ XFree(viList);
+
+ if (nxagentNumVisuals == 0 || nxagentVisuals == NULL)
+ {
+ FatalError("Unable to find any visuals.\n");
+ }
+
+ nxagentSetDefaultVisual();
+}
+
+void nxagentInitDepths()
+{
+ #ifdef TEST
+ int i;
+ #endif
+
+ nxagentDepths = XListDepths(nxagentDisplay, DefaultScreen(nxagentDisplay),
+ &nxagentNumDepths);
+
+ if (nxagentDepths == NULL)
+ {
+ #ifdef PANIC
+ fprintf(stderr, "nxagentInitDepths: PANIC! Failed to get available depths.\n");
+ #endif
+
+ FatalError("Failed to get available depths and pixmap formats.");
+ }
+ #ifdef TEST
+ else
+ {
+ fprintf(stderr, "nxagentInitDepths: Got [%d] available depths:\n",
+ nxagentNumDepths);
+
+ for (i = 0; i < nxagentNumDepths; i++)
+ {
+ fprintf(stderr, " [%d]", nxagentDepths[i]);
+ }
+
+ fprintf(stderr, ".\n");
+ }
+ #endif
+}
+
+void nxagentInitPixmapFormats()
+{
+ int i, j;
+ int depth;
+
+ /*
+ * Formats are created with no care of which are supported
+ * on the real display. Creating only formats supported
+ * by the remote end makes troublesome handling migration
+ * of session from a display to another.
+ */
+
+ nxagentNumPixmapFormats = 0;
+
+/*
+XXX: Some X server doesn't list 1 among available depths...
+*/
+
+ nxagentPixmapFormats = malloc((nxagentNumDepths + 1) * sizeof(XPixmapFormatValues));
+
+ for (i = 1; i <= MAXDEPTH; i++)
+ {
+ depth = 0;
+
+ if (i == 1)
+ {
+ depth = 1;
+ }
+ else
+ {
+ for (j = 0; j < nxagentNumDepths; j++)
+ {
+ if (nxagentDepths[j] == i)
+ {
+ depth = i;
+
+ break;
+ }
+ }
+ }
+
+ if (depth != 0)
+ {
+ if (nxagentNumPixmapFormats >= MAXFORMATS)
+ {
+ FatalError("nxagentInitPixmapFormats: MAXFORMATS is too small for this server.\n");
+ }
+
+ nxagentPixmapFormats[nxagentNumPixmapFormats].depth = depth;
+ nxagentPixmapFormats[nxagentNumPixmapFormats].bits_per_pixel = nxagentBitsPerPixel(depth);
+ nxagentPixmapFormats[nxagentNumPixmapFormats].scanline_pad = BITMAP_SCANLINE_PAD;
+
+ #ifdef TEST
+ fprintf(stderr, "nxagentInitPixmapFormats: Set format [%d] to depth [%d] "
+ "bits per pixel [%d] scanline pad [%d].\n", nxagentNumPixmapFormats,
+ depth, nxagentPixmapFormats[nxagentNumPixmapFormats].bits_per_pixel,
+ BITMAP_SCANLINE_PAD);
+ #endif
+
+ nxagentNumPixmapFormats++;
+ }
+ }
+
+ nxagentRemotePixmapFormats = XListPixmapFormats(nxagentDisplay, &nxagentRemoteNumPixmapFormats);
+
+ if (nxagentRemotePixmapFormats == NULL)
+ {
+ #ifdef WARNING
+ fprintf(stderr, "nxagentInitPixmapFormats: WARNING! Failed to get available remote pixmap formats.\n");
+ #endif
+ }
+ #ifdef TEST
+ else
+ {
+ fprintf(stderr, "nxagentInitPixmapFormats: Got [%d] available remote pixmap formats:\n",
+ nxagentRemoteNumPixmapFormats);
+
+ for (i = 0; i < nxagentRemoteNumPixmapFormats; i++)
+ {
+ fprintf(stderr, "nxagentInitPixmapFormats: Remote pixmap format [%d]: depth [%d] "
+ "bits_per_pixel [%d] scanline_pad [%d].\n", i, nxagentRemotePixmapFormats[i].depth,
+ nxagentRemotePixmapFormats[i].bits_per_pixel, nxagentRemotePixmapFormats[i].scanline_pad);
+ }
+ }
+ #endif
+}
+
+void nxagentSetDefaultDrawables()
+{
+ int i, j;
+
+ for (i = 0; i <= MAXDEPTH; i++)
+ {
+ nxagentDefaultDrawables[i] = None;
+ }
+
+ for (i = 0; i < nxagentNumPixmapFormats; i++)
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentSetDefaultDrawables: Checking remote pixmap format [%d] with depth [%d] "
+ "bits per pixel [%d] scanline pad [%d].\n", i, nxagentPixmapFormats[i].depth,
+ nxagentPixmapFormats[i].bits_per_pixel, nxagentPixmapFormats[i].scanline_pad);
+ #endif
+
+ if (nxagentPixmapFormats[i].depth == 24)
+ {
+ if (nxagentPixmapFormats[i].bits_per_pixel == 24)
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentSetDefaultDrawables: WARNING! Assuming remote pixmap "
+ "format [%d] as true 24 bits.\n", i);
+ #endif
+
+ nxagentTrue24 = True;
+ }
+ }
+
+ for (j = 0; j < nxagentNumDepths; j++)
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentSetDefaultDrawables: Checking depth at index [%d] with pixmap depth [%d] "
+ "and display depth [%d].\n", j, nxagentPixmapFormats[i].depth, nxagentDepths[j]);
+ #endif
+
+ if ((nxagentPixmapFormats[i].depth == 1 ||
+ nxagentPixmapFormats[i].depth == nxagentDepths[j]) &&
+ nxagentDefaultDrawables[nxagentPixmapFormats[i].depth] == None)
+ {
+ nxagentDefaultDrawables[nxagentPixmapFormats[i].depth] =
+ XCreatePixmap(nxagentDisplay, DefaultRootWindow(nxagentDisplay),
+ 1, 1, nxagentPixmapFormats[i].depth);
+
+ #ifdef TEST
+ fprintf(stderr, "nxagentSetDefaultDrawables: Created default drawable [%lu] for depth [%d].\n",
+ nxagentDefaultDrawables[nxagentPixmapFormats[i].depth], nxagentPixmapFormats[i].depth);
+ #endif
+ }
+ }
+
+ if (nxagentDefaultDrawables[nxagentPixmapFormats[i].depth] == None)
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentSetDefaultDrawables: WARNING! Forcing default drawable for depth [%d].\n",
+ nxagentPixmapFormats[i].depth);
+ #endif
+
+ nxagentDefaultDrawables[nxagentPixmapFormats[i].depth] =
+ XCreatePixmap(nxagentDisplay, DefaultRootWindow(nxagentDisplay),
+ 1, 1, nxagentPixmapFormats[i].depth);
+
+ #ifdef TEST
+ fprintf(stderr, "nxagentSetDefaultDrawables: Created default drawable [%lu] for depth [%d].\n",
+ nxagentDefaultDrawables[nxagentPixmapFormats[i].depth], nxagentPixmapFormats[i].depth);
+ #endif
+ }
+ }
+}
+
+void nxagentCloseDisplay()
+{
+ #ifdef TEST
+ fprintf(stderr, "nxagentCloseDisplay: Called with full generation [%d] and display [%p].\n",
+ nxagentDoFullGeneration, (void *) nxagentDisplay);
+ #endif
+
+ if (nxagentDoFullGeneration == 0 ||
+ nxagentDisplay == NULL)
+ {
+ return;
+ }
+
+ /*
+ * If nxagentDoFullGeneration is true, all
+ * the X resources will be destroyed upon
+ * closing the display connection, so there
+ * is no real need to generate additional
+ * traffic
+ */
+
+ free(nxagentDefaultColormaps);
+ nxagentDefaultColormaps = NULL;
+
+ XFree(nxagentVisuals);
+ nxagentVisuals = NULL;
+
+ free(nxagentDepths);
+ nxagentDepths = NULL;
+
+ XFree(nxagentPixmapFormats);
+ nxagentPixmapFormats = NULL;
+
+ XFree(nxagentRemotePixmapFormats);
+ nxagentRemotePixmapFormats = NULL;
+
+ nxagentFreeFontCache();
+/*
+FIXME: Is this needed?
+
+ nxagentFreeFontMatchStuff();
+*/
+
+ /*
+ * Free the image cache. This is useful
+ * for detecting memory leaks.
+ */
+
+ if (nxagentDisplay != NULL)
+ {
+ NXFreeCache(nxagentDisplay);
+
+ NXResetDisplay(nxagentDisplay);
+ }
+
+ /*
+ * Kill all the running dialogs.
+ */
+
+ nxagentTerminateDialogs();
+
+ #ifdef TEST
+ fprintf(stderr, "nxagentCloseDisplay: Setting the display to NULL.\n");
+ #endif
+
+ XCloseDisplay(nxagentDisplay);
+
+ nxagentDisplay = NULL;
+}
+
+static FILE *nxagentLookForIconFile(char *iconName, const char *permission,
+ char *return_path)
+{
+ char *path;
+ char *end;
+ char singlePath[PATH_MAX];
+ int breakLoop;
+ FILE *fptr = NULL;
+
+ #ifdef WIN32
+ const char separator = ';';
+ const char *slash = "\\";
+ #else
+ const char separator = ':';
+ const char *slash = "/";
+ #endif
+
+ if ((path = getenv("PATH")) == NULL)
+ {
+ return NULL;
+ }
+
+ for(breakLoop = 0; breakLoop == 0 && fptr == NULL; )
+ {
+ end = strchr(path, separator);
+
+ if (end != NULL)
+ {
+ strncpy(singlePath, path, (unsigned long)(end - path));
+
+ singlePath[(unsigned long)(end - path)] = 0;
+
+ path = end + 1;
+ }
+ else
+ {
+ strcpy(singlePath, path);
+
+ breakLoop = 1;
+ }
+
+ if (singlePath[strlen(singlePath)- 1] == slash[0])
+ {
+ singlePath[strlen(singlePath)- 1] = 0;
+ }
+
+ if (strlen(singlePath) + strlen(iconName) + 1 < PATH_MAX)
+ {
+ strncat(singlePath, slash, 1);
+ strcat(singlePath, iconName);
+
+ if ((fptr = fopen(singlePath, permission)) != NULL)
+ {
+ strcpy(return_path, singlePath);
+ }
+ }
+ else
+ {
+ fprintf(stderr, "Error: Path too long.\n");
+ }
+ }
+
+ return fptr;
+}
+
+Bool nxagentMakeIcon(Display *display, Pixmap *nxIcon, Pixmap *nxMask)
+{
+ char default_path [PATH_MAX];
+ char icon_path [PATH_MAX];
+ FILE *icon_fp;
+ int status;
+ Bool success = False;
+ XlibPixmap IconPixmap;
+ XlibPixmap IconShape;
+ char* agent_icon_name;
+ char** agentIconData;
+
+ /*
+ * selecting x2go icon when running as X2Go agent
+ */
+ if(nxagentX2go)
+ {
+ agent_icon_name=X2GOAGENT_ICON_NAME;
+ agentIconData=x2goagentIconData;
+ }
+ else
+ {
+ agent_icon_name=NXAGENT_ICON_NAME;
+ agentIconData=nxagentIconData;
+ }
+
+
+ snprintf(default_path, PATH_MAX-1, "/usr/NX/share/images/%s", agent_icon_name);
+
+ if ((icon_fp = fopen(default_path, "r")) == NULL)
+ {
+ icon_fp = nxagentLookForIconFile(agent_icon_name, "r", icon_path);
+
+ if (icon_fp != NULL)
+ {
+ fclose (icon_fp);
+ success = True;
+ }
+ }
+ else
+ {
+ fclose (icon_fp);
+ success = True;
+ strcpy(icon_path, default_path);
+ }
+
+ if (success)
+ {
+ status = XpmReadFileToPixmap(display,
+ DefaultRootWindow(display),
+ icon_path,
+ &IconPixmap,
+ &IconShape,
+ NULL);
+
+ if (status != XpmSuccess)
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentMakeIcon: Xpm operation failed with error '%s'.\n",
+ XpmGetErrorString(status));
+ #endif
+
+ success = False;
+ }
+ }
+
+ if (!success)
+ {
+ status = XpmCreatePixmapFromData(display,
+ DefaultRootWindow(display),
+ agentIconData,
+ &IconPixmap,
+ &IconShape,
+ NULL);
+
+ if (status != XpmSuccess)
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentMakeIcon: Xpm operation failed with error '%s'.\n",
+ XpmGetErrorString(status));
+ #endif
+
+ success = False;
+ }
+ else
+ {
+ success = True;
+ }
+ }
+
+
+ *nxIcon = IconPixmap;
+ *nxMask = IconShape;
+
+ return success;
+}
+
+Bool nxagentXServerGeometryChanged()
+{
+ return (WidthOfScreen(DefaultScreenOfDisplay(nxagentDisplay)) !=
+ WidthOfScreen(DefaultScreenOfDisplay(nxagentDisplayBackup))) ||
+ (HeightOfScreen(DefaultScreenOfDisplay(nxagentDisplay)) !=
+ HeightOfScreen(DefaultScreenOfDisplay(nxagentDisplayBackup)));
+}
+
+void nxagentBackupDisplayInfo(void)
+{
+ if (nxagentDisplayInfoSaved)
+ {
+ return;
+ }
+
+ /*
+ * Since we need the display structure
+ * in order to behave correctly when no X
+ * connection is available, we must always
+ * have a good display record.
+ * It can be discarded only when a new X
+ * connection is available, so we store it
+ * in order to destroy whenever the recon-
+ * nection succed.
+ */
+
+ nxagentDisplayBackup = nxagentDisplay;
+ nxagentBitmapGCBackup = nxagentBitmapGC;
+ nxagentDepthsRecBackup = nxagentDepths;
+ nxagentNumDepthsRecBackup = nxagentNumDepths;
+ nxagentNumDefaultColormapsRecBackup = nxagentNumDefaultColormaps;
+ nxagentVisualsRecBackup = nxagentVisuals;
+ nxagentNumVisualsRecBackup = nxagentNumVisuals;
+ if (nxagentVisualHasBeenIgnored)
+ {
+ free(nxagentVisualHasBeenIgnored);
+ nxagentVisualHasBeenIgnored = NULL;
+ }
+ nxagentVisualHasBeenIgnored = malloc(nxagentNumVisuals * sizeof(Bool));
+ nxagentDefaultDepthRecBackup = DefaultDepth(nxagentDisplay, DefaultScreen(nxagentDisplay));
+ nxagentDisplayWidthRecBackup = DisplayWidth(nxagentDisplay, DefaultScreen(nxagentDisplay));
+ nxagentDisplayHeightRecBackup = DisplayHeight(nxagentDisplay, DefaultScreen(nxagentDisplay));
+ nxagentRenderEnableRecBackup = nxagentRenderEnable;
+
+ nxagentDisplayInfoSaved = True;
+}
+
+void nxagentCleanupBackupDisplayInfo(void)
+{
+ free(nxagentDepthsRecBackup);
+ nxagentNumDepthsRecBackup = 0;
+
+ nxagentNumDefaultColormapsRecBackup = 0;
+
+ free(nxagentVisualsRecBackup);
+ nxagentNumVisualsRecBackup = 0;
+
+ if (nxagentVisualHasBeenIgnored)
+ {
+ free(nxagentVisualHasBeenIgnored);
+ nxagentVisualHasBeenIgnored = NULL;
+ }
+
+ nxagentDefaultDepthRecBackup = 0;
+ nxagentDisplayWidthRecBackup = 0;
+ nxagentDisplayHeightRecBackup = 0;
+
+ if (nxagentDisplayBackup)
+ {
+ XCloseDisplay(nxagentDisplayBackup);
+
+ nxagentDisplayBackup = NULL;
+ }
+
+ if (nxagentBitmapGCBackup)
+ {
+ if (nxagentDisplayBackup)
+ {
+ XFreeGC(nxagentDisplayBackup, nxagentBitmapGCBackup);
+ }
+ else
+ {
+ free(nxagentBitmapGCBackup);
+ }
+
+ nxagentBitmapGCBackup = NULL;
+ }
+
+ nxagentDisplayInfoSaved = False;
+}
+
+void nxagentDisconnectDisplay(void)
+{
+ switch (reconnectDisplayState)
+ {
+ case EVERYTHING_DONE:
+ if (nxagentBitmapGC &&
+ nxagentBitmapGCBackup &&
+ (nxagentBitmapGC != nxagentBitmapGCBackup))
+ {
+ XFreeGC(nxagentDisplay, nxagentBitmapGC);
+ }
+
+ nxagentBitmapGC = nxagentBitmapGCBackup;
+ case GOT_PIXMAP_FORMAT_LIST:
+ case GOT_DEPTH_LIST:
+ case ALLOC_DEF_COLORMAP:
+ if (nxagentDefaultColormaps)
+ {
+ int i;
+
+ for (i = 0; i < nxagentNumDefaultColormaps; i++)
+ {
+ nxagentDefaultColormaps[i] = None;
+ }
+ }
+ case GOT_VISUAL_INFO:
+ case OPENED:
+ /*
+ * Actually we need the nxagentDisplay
+ * structure in order to let the agent
+ * go when no X connection is available.
+ */
+
+ if (nxagentDisplay &&
+ nxagentDisplayBackup &&
+ (nxagentDisplay != nxagentDisplayBackup))
+ {
+ XCloseDisplay(nxagentDisplay);
+ }
+ case NOTHING:
+ nxagentDisplay = nxagentDisplayBackup;
+ break;
+ default:
+ FatalError("Display is in unknown state. Can't continue.");
+ }
+
+ reconnectDisplayState = NOTHING;
+}
+
+static int nxagentCheckForDefaultDepthCompatibility()
+{
+ /*
+ * Depending on the (reconnect) tolerance checks value, this
+ * function checks stricter or looser:
+ * - Strict means that the old and new default depth values
+ * must match exactly.
+ * - Safe or Risky means that the default depth values might differ,
+ * but the new default depth value must be at least as
+ * high as the former default depth value. This is
+ * recommended, because it allows clients with a
+ * higher default depth value to still connect, but
+ * not lose functionality.
+ * - Bypass means that all of these checks are essentially
+ * deactivated. This is probably a very bad idea.
+ */
+
+ int dDepth;
+
+ dDepth = DefaultDepth(nxagentDisplay, DefaultScreen(nxagentDisplay));
+
+ const unsigned int tolerance = nxagentOption(ReconnectTolerance);
+
+ if (ToleranceChecksBypass <= tolerance)
+ {
+ #ifdef WARNING
+ fprintf(stderr, "nxagentCheckForDefaultDepthCompatibility: WARNING! Not proceeding with any checks, "
+ "because tolerance [%u] higher than or equal [%u]. New default depth value "
+ "is [%d], former default depth value is [%d].\n", tolerance,
+ ToleranceChecksBypass, dDepth, nxagentDefaultDepthRecBackup);
+ #endif
+
+ return 1;
+ }
+
+ if (nxagentDefaultDepthRecBackup == dDepth)
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentCheckForDefaultDepthCompatibility: New default depth [%d] "
+ "matches with old default depth.\n", dDepth);
+ #endif
+
+ return 1;
+ }
+ else if ((ToleranceChecksSafe <= tolerance) && (nxagentDefaultDepthRecBackup < dDepth))
+ {
+ #ifdef WARNING
+ fprintf(stderr, "nxagentCheckForDefaultDepthCompatibility: WARNING! New default depth [%d] "
+ "higher than the old default depth [%d] at tolerance [%u].\n", dDepth,
+ nxagentDefaultDepthRecBackup, tolerance);
+ #endif
+
+ return 1;
+ }
+ else
+ {
+ #ifdef WARNING
+ fprintf(stderr, "nxagentCheckForDefaultDepthCompatibility: WARNING! New default depth [%d] "
+ "doesn't match with old default depth [%d] at tolerance [%u].\n", dDepth,
+ nxagentDefaultDepthRecBackup, tolerance);
+ #endif
+
+ return 0;
+ }
+}
+
+static int nxagentCheckForDepthsCompatibility()
+{
+ /*
+ * Depending on the (reconnect) tolerance checks value, this
+ * function checks stricter or looser:
+ * - Strict means that the number of old and new depths must
+ * match exactly and every old depth value must be
+ * available in the new depth array.
+ * - Safe means that the number of depths might diverge,
+ * but all former depth must also be included in the
+ * new depth array. This is recommended, because
+ * it allows clients with more depths to still
+ * connect, but not lose functionality.
+ * - Risky means that the new depths array is allowed to be
+ * smaller than the old depths array, but at least
+ * one depth value must be included in both.
+ * This is potentially unsafe.
+ * - Bypass or higher means that all of these checks are
+ * essentially deactivated. This is a very bad idea.
+ */
+
+ const unsigned int tolerance = nxagentOption(ReconnectTolerance);
+
+ if (ToleranceChecksBypass <= tolerance)
+ {
+ #ifdef WARNING
+ fprintf(stderr, "nxagentCheckForDepthsCompatibility: WARNING! Not proceeding with any checks, "
+ "because tolerance [%u] higher than or equal [%u]. Number of newly available depths "
+ "is [%d], number of old depths is [%d].\n", tolerance, ToleranceChecksBypass,
+ nxagentNumDepths, nxagentNumDepthsRecBackup);
+ #endif
+
+ return 1;
+ }
+
+ if ((ToleranceChecksStrict == tolerance) && (nxagentNumDepths != nxagentNumDepthsRecBackup))
+ {
+ #ifdef WARNING
+ fprintf(stderr, "nxagentCheckForDepthsCompatibility: WARNING! No tolerance allowed and "
+ "number of new available depths [%d] doesn't match with number of old "
+ "depths [%d].\n", nxagentNumDepths,
+ nxagentNumDepthsRecBackup);
+ #endif
+
+ return 0;
+ }
+
+ if ((ToleranceChecksSafe == tolerance) && (nxagentNumDepths < nxagentNumDepthsRecBackup))
+ {
+ #ifdef WARNING
+ fprintf(stderr, "nxagentCheckForDepthsCompatibility: WARNING! Tolerance [%u] not "
+ "high enough and number of new available depths [%d] "
+ "lower than number of old depths [%d].\n", tolerance,
+ nxagentNumDepths, nxagentNumDepthsRecBackup);
+ #endif
+
+ return 0;
+ }
+
+ /*
+ * By now the tolerance is either:
+ * - Strict and both depth numbers match
+ * - Safe and:
+ * o the number of old and new depths matches exactly, or
+ * o the number of old depths is lower than the number
+ * of new depths
+ * - Risky
+ */
+
+ bool compatible = true;
+ bool one_match = false;
+ bool matched = false;
+ int total_matches = 0;
+
+ /*
+ * FIXME: within this loop, we try to match all "new" depths
+ * against the "old" depths. Depending upon the flexibility
+ * value, either all "new" depths must have a corresponding
+ * counterpart in the "old" array, or at least one value
+ * must be included in both.
+ * Is this safe enough though?
+ * Shouldn't we better try to match entries in the "old"
+ * depths array against the "new" depths array, such that
+ * we know that all "old" values are covered by "new"
+ * values? Or is it more important that "new" values are
+ * covered by "old" ones, with potentially more "old"
+ * values lingering around that cannot be displayed by the
+ * connected client?
+ *
+ * This section probably needs a revisit at some point in time.
+ */
+ for (int i = 0; i < nxagentNumDepths; ++i)
+ {
+ matched = false;
+
+ for (int j = 0; j < nxagentNumDepthsRecBackup; ++j)
+ {
+ if (nxagentDepths[i] == nxagentDepthsRecBackup[j])
+ {
+ matched = true;
+ one_match = true;
+ ++total_matches;
+
+ break;
+ }
+ }
+
+ if ((ToleranceChecksRisky > tolerance) && (!matched))
+ {
+ #ifdef WARNING
+ fprintf(stderr, "nxagentCheckForDepthsCompatibility: WARNING! Tolerance [%u] too low and "
+ "failed to match available depth [%d].\n", tolerance, nxagentDepths[i]);
+ #endif
+
+ compatible = false;
+
+ break;
+ }
+ }
+
+ /*
+ * At Risky tolerance, only one match is necessary to be "compatible".
+ */
+ if (ToleranceChecksRisky == tolerance)
+ {
+ compatible = one_match;
+ }
+
+ int ret = (!(!compatible));
+
+ if (compatible)
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentCheckForDepthsCompatibility: Internal depths match with "
+ "remote depths at tolerance [%u].\n", tolerance);
+ #endif
+
+ if (total_matches != nxagentNumDepths)
+ {
+ #ifdef WARNING
+ fprintf(stderr, "nxagentCheckForDepthsCompatibility: only some [%d] of the new depths [%d] "
+ "match with old depths [%d] at tolerance [%u].\n", total_matches, nxagentNumDepths,
+ nxagentNumDepthsRecBackup, tolerance);
+ #endif
+ }
+ }
+ else
+ {
+ #ifdef WARNING
+ fprintf(stderr, "nxagentCheckForDepthsCompatibility: WARNING! New available depths [%d] don't match "
+ "with old depths [%d] at tolerance [%u]. Only [%d] depth values matched.\n",
+ nxagentNumDepths, nxagentNumDepthsRecBackup, tolerance, total_matches);
+ #endif
+ }
+
+ return (ret);
+}
+
+static int nxagentCheckForPixmapFormatsCompatibility()
+{
+ /*
+ * Depending on the (reconnect) tolerance checks value, this
+ * function checks stricter or looser:
+ * - Strict means that the number of internal and external
+ * pixmap formats must match exactly and every
+ * internal pixmap format must be available in the
+ * external pixmap format array.
+ * - Safe means that the number of pixmap formats might
+ * diverge, but all internal pixmap formats must
+ * also be included in the external pixmap formats
+ * array. This is recommended, because it allows
+ * clients with more pixmap formats to still connect,
+ * but not lose functionality.
+ * - Risky means that the internal pixmap formats array is
+ * allowed to be smaller than the external pixmap
+ * formats array, but at least one pixmap format must
+ * be included in both. This is potentially unsafe.
+ * - Bypass or higher means that all of these checks are
+ * essentially deactivated. This is a very bad idea.
+ */
+
+ const unsigned int tolerance = nxagentOption(ReconnectTolerance);
+
+ if (ToleranceChecksBypass <= tolerance)
+ {
+ #ifdef WARNING
+ fprintf(stderr, "nxagentCheckForPixmapFormatsCompatibility: WARNING! Not proceeding with any checks, "
+ "because tolerance [%u] higher than or equal [%u]. Number of internally available "
+ "pixmap formats is [%d], number of externally available pixmap formats is [%d].\n",
+ tolerance, ToleranceChecksBypass, nxagentNumPixmapFormats, nxagentRemoteNumPixmapFormats);
+ #endif
+
+ return 1;
+ }
+
+ if ((ToleranceChecksStrict == tolerance) && (nxagentNumPixmapFormats != nxagentRemoteNumPixmapFormats))
+ {
+ #ifdef DEBUG
+ fprintf(stderr, "nxagentCheckForPixmapFormatsCompatibility: WARNING! No tolerance allowed and number "
+ "of internal pixmap formats [%d] doesn't match with number of remote formats [%d].\n",
+ nxagentNumPixmapFormats, nxagentRemoteNumPixmapFormats);
+ #endif
+
+ return 0;
+ }
+
+ if ((ToleranceChecksSafe == tolerance) && (nxagentNumPixmapFormats > nxagentRemoteNumPixmapFormats))
+ {
+ #ifdef DEBUG
+ fprintf(stderr, "nxagentCheckForPixmapFormatsCompatibility: WARNING! Tolerance [%u] too low "
+ "and number of internal pixmap formats [%d] higher than number of external formats [%d].\n",
+ tolerance, nxagentNumPixmapFormats, nxagentRemoteNumPixmapFormats);
+ #endif
+
+ return 0;
+ }
+
+ /*
+ * By now the tolerance is either:
+ * - Strict
+ * - Safe and:
+ * o the number of internal and external pixmap formats
+ * matches exactly, or
+ * o the number of external pixmap formats is higher than
+ * the number of internal pixmap formats,
+ * - Risky
+ */
+
+ bool compatible = true;
+ bool matched = false;
+ int total_matches = 0;
+
+ for (int i = 0; i < nxagentNumPixmapFormats; ++i)
+ {
+ matched = false;
+
+ for (int j = 0; j < nxagentRemoteNumPixmapFormats; ++j)
+ {
+ if (nxagentPixmapFormats[i].depth == nxagentRemotePixmapFormats[j].depth &&
+ nxagentPixmapFormats[i].bits_per_pixel == nxagentRemotePixmapFormats[j].bits_per_pixel &&
+ nxagentPixmapFormats[i].scanline_pad == nxagentRemotePixmapFormats[j].scanline_pad)
+ {
+ matched = true;
+ ++total_matches;
+
+ break;
+ }
+ }
+
+ if ((ToleranceChecksRisky > tolerance) && (!matched))
+ {
+ #ifdef WARNING
+ fprintf(stderr, "nxagentCheckForPixmapFormatsCompatibility: WARNING! Tolerance [%u] too low "
+ "and failed to match internal pixmap format (depth [%d] bpp [%d] pad [%d]).\n",
+ tolerance, nxagentPixmapFormats[i].depth, nxagentPixmapFormats[i].bits_per_pixel,
+ nxagentPixmapFormats[i].scanline_pad);
+ #endif
+
+ compatible = false;
+ }
+ }
+
+ int ret = !(!(compatible));
+
+ if (compatible)
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentCheckForPixmapFormatsCompatibility: Internal pixmap formats match with "
+ "remote pixmap formats at tolerance [%u].\n", tolerance);
+ #endif
+
+ if (total_matches != nxagentNumPixmapFormats)
+ {
+ #ifdef WARNING
+ fprintf(stderr, "nxagentCheckForPixmapFormatsCompatibility: Only some [%d] of the internal "
+ "pixmap formats [%d] match with external pixmap formats [%d] at tolerance [%u].\n",
+ total_matches, nxagentNumPixmapFormats, nxagentRemoteNumPixmapFormats, tolerance);
+ #endif
+ }
+ }
+ else
+ {
+ #ifdef WARNING
+ fprintf(stderr, "nxagentCheckForPixmapFormatsCompatibility: WARNING! Internally available "
+ "pixmap formats [%d] don't match with external pixmap formats [%d] "
+ "at tolerance [%u]. Only [%d] depth values matched.\n",
+ nxagentNumPixmapFormats, nxagentRemoteNumPixmapFormats, tolerance, total_matches);
+ #endif
+ }
+
+ return (ret);
+}
+
+static int nxagentInitAndCheckVisuals(int flexibility)
+{
+ /* FIXME: does this also need work? */
+ XVisualInfo viTemplate;
+ XVisualInfo *viList;
+ XVisualInfo *newVisuals;
+
+ long viMask;
+ int i, n;
+ int matched;
+ int compatible;
+ int viNumList;
+
+ compatible = 1;
+
+ viMask = VisualScreenMask;
+ viTemplate.screen = DefaultScreen(nxagentDisplay);
+ viTemplate.depth = DefaultDepth(nxagentDisplay, DefaultScreen(nxagentDisplay));
+ viList = XGetVisualInfo(nxagentDisplay, viMask, &viTemplate, &viNumList);
+
+ newVisuals = malloc(sizeof(XVisualInfo) * nxagentNumVisuals);
+
+ for (i = 0; i < nxagentNumVisuals; i++)
+ {
+ matched = 0;
+
+ for (n = 0; n < viNumList; n++)
+ {
+ if (nxagentCompareVisuals(nxagentVisuals[i], viList[n]) == 1)
+ {
+/*
+FIXME: Should the visual be ignored in this case?
+ We can flag the visuals with inverted masks,
+ and use this information to switch the masks
+ when contacting the remote X server.
+*/
+ if (nxagentVisuals[i].red_mask == viList[n].blue_mask &&
+ nxagentVisuals[i].blue_mask == viList[n].red_mask)
+ {
+ #ifdef WARNING
+ fprintf(stderr, "nxagentInitAndCheckVisuals: WARNING! Red and blue mask inverted. "
+ "Forcing matching.\n");
+ #endif
+ }
+
+ matched = 1;
+
+ nxagentVisualHasBeenIgnored[i] = FALSE;
+
+ memcpy(newVisuals + i, viList + n, sizeof(XVisualInfo));
+
+ break;
+ }
+ }
+
+ if (matched == 0)
+ {
+ if (nxagentVisuals[i].class == DirectColor)
+ {
+ #ifdef WARNING
+ fprintf(stderr, "nxagentInitAndCheckVisuals: WARNING! Ignoring not matched DirectColor visual.\n");
+ #endif
+
+ nxagentVisualHasBeenIgnored[i] = TRUE;
+
+ memcpy(newVisuals + i, nxagentVisuals + i, sizeof(XVisualInfo));
+ }
+ else
+ {
+ #ifdef DEBUG
+ fprintf(stderr, "nxagentInitAndCheckVisuals: WARNING! Failed to match this visual:\n");
+ fprintf(stderr, "\tdepth = %d\n", nxagentVisuals[i].depth);
+ fprintf(stderr, "\tclass = %d\n", nxagentVisuals[i].class);
+ fprintf(stderr, "\tmask = (%ld,%ld,%ld)\n",
+ nxagentVisuals[i].red_mask,
+ nxagentVisuals[i].green_mask,
+ nxagentVisuals[i].blue_mask);
+ fprintf(stderr, "\tcolormap size = %d\n", nxagentVisuals[i].colormap_size);
+ fprintf(stderr, "\tbits_per_rgb = %d\n", nxagentVisuals[i].bits_per_rgb);
+ #endif
+
+ compatible = 0;
+
+ break;
+ }
+ }
+ }
+
+ XFree(viList);
+
+ if (compatible == 1)
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentInitAndCheckVisuals: New visuals match with old visuals.\n");
+ #endif
+
+ nxagentVisuals = newVisuals;
+ }
+ else
+ {
+ #ifdef WARNING
+ fprintf(stderr, "nxagentInitAndCheckVisuals: New visuals don't match with old visuals.\n");
+ #endif
+
+ free(newVisuals);
+ }
+
+ return compatible;
+}
+
+static int nxagentCheckForColormapsCompatibility(int flexibility)
+{
+ /* FIXME: does this also need work? */
+ if (nxagentNumDefaultColormaps == nxagentNumDefaultColormapsRecBackup)
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentCheckForColormapsCompatibility: Number of new colormaps [%d] "
+ "matches with old colormaps.\n", nxagentNumDefaultColormaps);
+ #endif
+
+ return 1;
+ }
+ else
+ {
+ #ifdef WARNING
+ fprintf(stderr, "nxagentCheckForColormapsCompatibility: WARNING! Number of new colormaps [%d] "
+ "doesn't match with old colormaps [%d].\n", nxagentNumDefaultColormaps,
+ nxagentNumDefaultColormapsRecBackup);
+ #endif
+
+ return 0;
+ }
+}
+
+Bool nxagentReconnectDisplay(void *p0)
+{
+ int i;
+ int flexibility = *(int*)p0;
+
+ #if defined(NXAGENT_RECONNECT_DEBUG) || defined(NXAGENT_RECONNECT_DISPLAY_DEBUG)
+ fprintf(stderr, "nxagentReconnectDisplay\n");
+ #endif
+
+ if (reconnectDisplayState)
+ {
+ fprintf(stderr, "nxagentReconnectDisplay: Trying to reconnect a session "
+ "uncleanly disconnected\n");
+
+ return False;
+ }
+
+ /*
+ * Reset the values to their defaults.
+ */
+
+ nxagentPackMethod = -1;
+ nxagentPackQuality = -1;
+ nxagentSplitThreshold = -1;
+
+ nxagentRemoteMajor = -1;
+
+ nxagentInstallSignalHandlers();
+
+ nxagentInstallDisplayHandlers();
+
+ nxagentDisplay = nxagentInternalOpenDisplay(nxagentDisplayName);
+
+ nxagentPostInstallSignalHandlers();
+
+ nxagentPostInstallDisplayHandlers();
+
+ if (nxagentDisplay == NULL)
+ {
+ nxagentSetReconnectError(FAILED_RESUME_DISPLAY_ALERT,
+ "Couldn't open the display.");
+
+ return FALSE;
+ }
+
+ nxagentAddXConnection();
+
+ /*
+ * Display is now open.
+ */
+
+ reconnectDisplayState = OPENED;
+
+ #ifdef NXAGENT_TIMESTAMP
+
+ fprintf(stderr, "Display: Open of the display finished with time [%d] ms.\n",
+ GetTimeInMillis() - startTime);
+
+ #endif
+
+ if (nxagentCheckForDefaultDepthCompatibility() == 0)
+ {
+ nxagentSetReconnectError(FAILED_RESUME_DISPLAY_ALERT,
+ "Default display depth doesn't match.");
+
+ return FALSE;
+ }
+
+ nxagentUseNXTrans = nxagentPostProcessArgs(nxagentDisplayName, nxagentDisplay,
+ DefaultScreenOfDisplay(nxagentDisplay));
+
+ /*
+ * After processing the arguments all the
+ * timeout values have been set. Now we
+ * have to change the screen-saver timeout.
+ */
+
+ nxagentSetScreenSaverTime();
+
+ /*
+ * Init and compare the visuals.
+ */
+
+ if (nxagentInitAndCheckVisuals(flexibility) == FALSE)
+ {
+ nxagentSetReconnectError(FAILED_RESUME_VISUALS_ALERT,
+ "Couldn't restore the required visuals.");
+
+ return FALSE;
+ }
+
+ reconnectDisplayState = GOT_VISUAL_INFO;
+
+ nxagentSetDefaultVisual();
+
+ nxagentInitAlphaVisual();
+
+ /*
+ * Re-allocate the colormaps.
+ */
+
+ nxagentNumDefaultColormaps = nxagentNumVisuals;
+
+ nxagentDefaultColormaps = (Colormap *) realloc(nxagentDefaultColormaps,
+ nxagentNumDefaultColormaps * sizeof(Colormap));
+
+ if (nxagentDefaultColormaps == NULL)
+ {
+ FatalError("Can't allocate memory for the default colormaps\n");
+ }
+
+ reconnectDisplayState = ALLOC_DEF_COLORMAP;
+
+ for (i = 0; i < nxagentNumDefaultColormaps; i++)
+ {
+ if (nxagentVisualHasBeenIgnored[i])
+ {
+ nxagentDefaultColormaps[i] = (XID)0;
+ }
+ else
+ {
+ nxagentDefaultColormaps[i] = XCreateColormap(nxagentDisplay,
+ DefaultRootWindow(nxagentDisplay),
+ nxagentVisuals[i].visual,
+ AllocNone);
+ }
+ }
+
+ nxagentCheckForColormapsCompatibility(flexibility);
+
+ /*
+ * Check the display depth.
+ */
+
+ nxagentInitDepths();
+
+ reconnectDisplayState = GOT_DEPTH_LIST;
+
+ if (nxagentCheckForDepthsCompatibility() == 0)
+ {
+ nxagentSetReconnectError(FAILED_RESUME_DEPTHS_ALERT,
+ "Couldn't restore all the required depths.");
+
+ return False;
+ }
+
+ /*
+ * nxagentPixmapFormats and nxagentRemotePixmapFormats
+ * will be reallocated in nxagentInitPixmapFormats().
+ */
+
+ if (nxagentPixmapFormats != NULL)
+ {
+ XFree(nxagentPixmapFormats);
+
+ nxagentPixmapFormats = NULL;
+ }
+
+ if (nxagentRemotePixmapFormats != NULL)
+ {
+ XFree(nxagentRemotePixmapFormats);
+
+ nxagentRemotePixmapFormats = NULL;
+ }
+
+ /*
+ * Check if all the required pixmap
+ * formats are supported.
+ */
+
+ nxagentInitPixmapFormats();
+
+ if (nxagentCheckForPixmapFormatsCompatibility() == 0)
+ {
+ nxagentSetReconnectError(FAILED_RESUME_PIXMAPS_ALERT,
+ "Couldn't restore all the required pixmap formats.");
+
+ return False;
+ }
+
+ reconnectDisplayState = GOT_PIXMAP_FORMAT_LIST;
+
+ /*
+ * Create a pixmap for each depth matching the
+ * local supported formats with format available
+ * on the remote display.
+ */
+
+ nxagentSetDefaultDrawables();
+
+ #ifdef RENDER
+
+ if (nxagentRenderEnable)
+ {
+ nxagentRenderExtensionInit();
+ }
+
+ if (nxagentRenderEnableRecBackup != nxagentRenderEnable)
+ {
+ nxagentRenderEnable = nxagentRenderEnableRecBackup;
+
+ nxagentSetReconnectError(FAILED_RESUME_RENDER_ALERT,
+ "Render extension not available or incompatible version.");
+
+ return False;
+ }
+
+ #endif
+
+ nxagentBlackPixel = BlackPixel(nxagentDisplay, DefaultScreen(nxagentDisplay));
+ nxagentWhitePixel = WhitePixel(nxagentDisplay, DefaultScreen(nxagentDisplay));
+
+ /*
+ * Initialize the agent's event mask that will be requested
+ * for the root or all the top level windows. If the nested
+ * window is a child of an existing window we will need to
+ * receive StructureNotify events. If we are going to manage
+ * the changes in root window's visibility we'll also need
+ * VisibilityChange events.
+ */
+
+ nxagentInitDefaultEventMask();
+
+ nxagentBitmapGC = XCreateGC(nxagentDisplay, nxagentDefaultDrawables[1], 0L, NULL);
+
+ #ifdef TEST
+ fprintf(stderr, "nxagentReconnectDisplay: Going to create agent's confine window.\n");
+ #endif
+
+ nxagentConfineWindow = XCreateWindow(nxagentDisplay,
+ DefaultRootWindow(nxagentDisplay),
+ 0, 0, 1, 1, 0, 0,
+ InputOnly,
+ CopyFromParent,
+ 0L, NULL);
+
+ #ifdef TEST
+ fprintf(stderr, "nxagentReconnectDisplay: Created agent's confine window with id [%ld].\n",
+ nxagentConfineWindow);
+ #endif
+
+ nxagentLogoDepth = DefaultDepth(nxagentDisplay, DefaultScreen(nxagentDisplay));
+
+ nxagentLogoBlack = 0x000000;
+ nxagentLogoRed = 0xff0000;
+ nxagentLogoWhite = 0xffffff;
+ nxagentLogoGray = 0x222222;
+
+ useXpmIcon = nxagentMakeIcon(nxagentDisplay, &nxagentIconPixmap, &nxagentIconShape);
+
+ /*
+ * All went fine. We can continue
+ * handling our clients.
+ */
+
+ reconnectDisplayState = EVERYTHING_DONE;
+
+ return True;
+}
+
+void nxagentAddXConnection()
+{
+ int fd = XConnectionNumber(nxagentDisplay);
+
+ nxagentXConnectionNumber = fd;
+
+ #ifdef TEST
+ fprintf(stderr, "nxagentAddXConnection: Adding the X connection [%d] "
+ "to the device set.\n", nxagentXConnectionNumber);
+ #endif
+
+ SetNotifyFd(XConnectionNumber(nxagentDisplay), nxagentNotifyConnection, X_NOTIFY_READ, NULL);
+}
+
+void nxagentRemoveXConnection()
+{
+ #ifdef TEST
+ fprintf(stderr, "nxagentRemoveXConnection: Removing the X connection [%d] "
+ "from the device set.\n", nxagentXConnectionNumber);
+ #endif
+
+ RemoveNotifyFd(nxagentXConnectionNumber);
+}
+
+/*
+ * Force an I/O error and wait until the NX trans-
+ * port is gone. It must be called before suspend-
+ * ing or terminating a session to ensure that the
+ * NX transport is terminated first.
+ */
+
+void nxagentWaitDisplay()
+{
+ /*
+ * Disable the smart scheduler's interrupts.
+ */
+
+ #ifdef DEBUG
+ fprintf(stderr, "nxagentWaitDisplay: Stopping the smart schedule timer.\n");
+ #endif
+
+ nxagentStopTimer();
+
+ if (nxagentDisplay != NULL)
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentWaitDisplay: Going to shutdown the X connection [%d].\n",
+ nxagentXConnectionNumber);
+ #endif
+
+ NXForceDisplayError(nxagentDisplay);
+
+ XSync(nxagentDisplay, 0);
+ }
+
+ #ifdef TEST
+ fprintf(stderr, "nxagentWaitDisplay: Going to wait for the NX transport.\n");
+ #endif
+
+ NXTransDestroy(NX_FD_ANY);
+
+ #ifdef TEST
+ fprintf(stderr, "nxagentWaitDisplay: The NX transport is not running.\n");
+ #endif
+
+ /*
+ * Be sure the signal handlers are
+ * in a known state.
+ */
+
+ nxagentResetSignalHandlers();
+}
+
+/*
+ * This has not to do with the remote display but
+ * with the X server that the agent is impersonating.
+ * We have it here to be consistent with the other
+ * cleanup procedures which have mainly to do with
+ * the Xlib display connection.
+ */
+
+void nxagentAbortDisplay()
+{
+ /*
+ * Be sure the X server socket in .X11-unix is
+ * deleted otherwise other users may to become
+ * unable to run a session on the same display.
+ */
+
+ #ifdef TEST
+ fprintf(stderr, "nxagentAbortDisplay: Cleaning up the X server sockets.\n");
+ #endif
+
+ CloseWellKnownConnections();
+}