diff options
author | Reinhard Tartler <siretart@tauware.de> | 2011-10-10 17:58:30 +0200 |
---|---|---|
committer | Reinhard Tartler <siretart@tauware.de> | 2011-10-10 17:58:30 +0200 |
commit | 9e3371021541dbb7d8428b419c2e77156b166f1a (patch) | |
tree | 752906c36559aa58c53945824724222f2646d850 /nx-X11/programs/Xserver/hw/nxagent/Display.c | |
parent | edddbe8765d46b5040fdde7b04eeee8e21282114 (diff) | |
download | nx-libs-9e3371021541dbb7d8428b419c2e77156b166f1a.tar.gz nx-libs-9e3371021541dbb7d8428b419c2e77156b166f1a.tar.bz2 nx-libs-9e3371021541dbb7d8428b419c2e77156b166f1a.zip |
Imported nxagent-3.1.0-2.tar.gznxagent/3.1.0-2
Summary: Imported nxagent-3.1.0-2.tar.gz
Keywords:
Imported nxagent-3.1.0-2.tar.gz
into Git repository
Diffstat (limited to 'nx-X11/programs/Xserver/hw/nxagent/Display.c')
-rw-r--r-- | nx-X11/programs/Xserver/hw/nxagent/Display.c | 2705 |
1 files changed, 2705 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..c2e131954 --- /dev/null +++ b/nx-X11/programs/Xserver/hw/nxagent/Display.c @@ -0,0 +1,2705 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2007 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXAGENT, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of NoMachine S.r.l. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +/* + +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 <X11/X.h> +#include <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 "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" +#include "Icons.h" +#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.h" +#include "NXlib.h" + +#include NXAGENT_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; +int nxagentNumDefaultColormaps; +int *nxagentDepths; +int nxagentNumDepths; +XPixmapFormatValues *nxagentPixmapFormats; +XPixmapFormatValues *nxagentRemotePixmapFormats; +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; + +unsigned int nxagentLogoColor(unsigned int value); +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(int flexibility); +static int nxagentCheckForPixmapFormatsCompatibility(void); +static int nxagentInitAndCheckVisuals(int flexibility); +static int nxagentCheckForColormapsCompatibility(int flexibility); + +/* + * FIXME: These must definitely become local. + */ + +XVisualInfo pV; +unsigned int r, b, g, or, ob, og, off; + +/* + * 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; +} + +unsigned int nxagentLogoColor(unsigned int value) +{ + /* + * Takes a color value in RGB24 (0xff0000, 0x00ff00, + * 0x0000ff) and converts it into an equivalent for + * the current visual. + */ + + int cr=0, cg=0, cb=0; + + cr = (value >> or) &r; + cg = (value >> (og - 8)) &g; + cb = (value >> (ob - 16)) &b; + + return (cr | cg | cb); +} + +/* + * 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 (pid == 0 && 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 && 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; + + #ifdef SMART_SCHEDULE + + /* + * Stop the smart schedule timer since + * it uses SIGALRM as we do. + */ + + nxagentStopTimer(); + + #endif + + /* + * 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 SMART_SCHEDULE + + #ifdef DEBUG + fprintf(stderr, "nxagentDisplayBlockHandler: BLOCK! Stopping the smart schedule timer.\n"); + #endif + + nxagentStopTimer(); + + #endif + + 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; + + #ifdef SMART_SCHEDULE + + if (SmartScheduleDisable == 1) + { + + #endif + + /* + * 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; + + #ifdef SMART_SCHEDULE + + } + + #endif + + /* + * 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 void nxagentDisplayFlushHandler(Display *display, int length) +{ + 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(); + } + } +} + +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. + */ + + #ifdef SMART_SCHEDULE + + nxagentStopTimer(); + + #endif + + 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."); + } + + #ifdef SMART_SCHEDULE + + /* + * Let the smart schedule set the SIGALRM + * handler again. + */ + + nxagentInitTimer(); + + #endif + + /* + * 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; + + /* + * 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. + */ + + #ifdef SMART_SCHEDULE + + nxagentStopTimer(); + + #endif + + 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."); + } + + #ifdef SMART_SCHEDULE + + /* + * Let the smart schedule set the SIGALRM + * handler again. + */ + + nxagentInitTimer(); + + #endif +} + +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); + } + + 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 *)xalloc(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(); + + /* + * 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) + ); + + pV = nxagentVisuals[nxagentDefaultVisualIndex]; + + r = pV.red_mask; + g = pV.green_mask; + b = pV.blue_mask; + + if (!pV.red_mask || !pV.green_mask || !pV.blue_mask) + { + nxagentLogoBlack = 0x000000; + nxagentLogoRed = 0xff0000; + nxagentLogoWhite = 0xffffff; + } + else + { + for (or=0, off=0x800000; (r&(off>>or)) == 0; or++); + for (og=0, off=0x800000; (g&(off>>og)) == 0; og++); + for (ob=0, off=0x800000; (b&(off>>ob)) == 0; ob++); + + nxagentLogoRed = nxagentLogoColor(0xff0000); + nxagentLogoBlack = nxagentLogoColor(0x000000); + nxagentLogoWhite = 0xffffff; + } + + #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; + + nxagentPixmapFormats = xalloc(nxagentNumDepths * sizeof(XPixmapFormatValues)); + + for (i = 1; i <= MAXDEPTH; i++) + { + depth = 0; + + 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 + + nxagentCheckForPixmapFormatsCompatibility(); +} + +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 + */ + + xfree(nxagentDefaultColormaps); + nxagentDefaultColormaps = NULL; + + XFree(nxagentVisuals); + nxagentVisuals = NULL; + + xfree(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; +} + +Bool nxagentMakeIcon(Display *display, Pixmap *nxIcon, Pixmap *nxMask) +{ + char *env_path = getenv("PATH"); + int lenght_env_path = 0; + char icon_filename [256]; + char default_path [256]; + char *icon_path = malloc( strlen(env_path) + sizeof(icon_filename) ); + FILE *icon_fp; + int status; + Bool success = False; + XlibPixmap IconPixmap; + XlibPixmap IconShape; + + if (env_path == NULL) + lenght_env_path = 0; + else + lenght_env_path = strlen(env_path) + 1; + strncpy(icon_filename, "", 255); + strncpy(default_path, "", 255); + + strcat(icon_filename, NXAGENT_ICON_NAME); + strcat(default_path,"/usr/NX/share/images/"); + strcat(default_path,icon_filename); + + if ((icon_fp = fopen(default_path, "r")) == NULL) + { + char *s; + char *temp_path = malloc(lenght_env_path + strlen(icon_filename) ); + char *temp_path1 = malloc(lenght_env_path + strlen(icon_filename) ); + + strncpy(temp_path, env_path, strlen(env_path)); + strncpy(temp_path1, "", lenght_env_path + strlen(icon_filename) ); + + while (strlen(temp_path) > 0) + { + s = strpbrk (temp_path, ":"); + if (s == NULL) break; + + strncpy (temp_path1, temp_path , strlen(temp_path) - strlen(s) ); + strncat (temp_path1, "/", 1); + strncat (temp_path1, icon_filename, strlen(icon_filename)); + if ((icon_fp = fopen(temp_path1, "r")) != NULL) + { + fclose (icon_fp); + success = True; + strcpy(icon_path,temp_path1); + break; + } + strncpy(temp_path1, "", lenght_env_path + strlen(icon_filename) ); + strncpy(temp_path1, s + 1, strlen(s)-1); + strncpy(temp_path, "", lenght_env_path + strlen(icon_filename) ); + strcpy(temp_path, temp_path1 ); + strncpy(temp_path1, "", lenght_env_path + strlen(icon_filename) ); + } + free(temp_path); + free(temp_path1); + } + 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), + nxagentIconData, + &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; + } + } + + free(icon_path); + + *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) + { + xfree(nxagentVisualHasBeenIgnored); + nxagentVisualHasBeenIgnored = NULL; + } + nxagentVisualHasBeenIgnored = xalloc(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) +{ + xfree(nxagentDepthsRecBackup); + nxagentNumDepthsRecBackup = 0; + + nxagentNumDefaultColormapsRecBackup = 0; + + xfree(nxagentVisualsRecBackup); + nxagentNumVisualsRecBackup = 0; + + if (nxagentVisualHasBeenIgnored) + { + xfree(nxagentVisualHasBeenIgnored); + nxagentVisualHasBeenIgnored = NULL; + } + + nxagentDefaultDepthRecBackup = 0; + nxagentDisplayWidthRecBackup = 0; + nxagentDisplayHeightRecBackup = 0; + + if (nxagentDisplayBackup) + { + XCloseDisplay(nxagentDisplayBackup); + + nxagentDisplayBackup = NULL; + } + + if (nxagentBitmapGCBackup) + { + if (nxagentDisplayBackup) + { + XFreeGC(nxagentDisplayBackup, nxagentBitmapGCBackup); + } + else + { + xfree(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() +{ + int dDepth; + + dDepth = DefaultDepth(nxagentDisplay, DefaultScreen(nxagentDisplay)); + + if (nxagentDefaultDepthRecBackup == dDepth) + { + #ifdef TEST + fprintf(stderr, "nxagentCheckForDefaultDepthCompatibility: New default depth [%d] " + "matches with old default depth.\n", dDepth); + #endif + + return 1; + } + else + { + #ifdef WARNING + fprintf(stderr, "nxagentCheckForDefaultDepthCompatibility: WARNING! New default depth [%d] " + "doesn't match with old default depth [%d].\n", dDepth, nxagentDefaultDepthRecBackup); + #endif + + return 0; + } +} + +static int nxagentCheckForDepthsCompatibility(int flexibility) +{ + int i, j; + int matched; + int compatible; + + if (nxagentNumDepths != nxagentNumDepthsRecBackup) + { + #ifdef WARNING + fprintf(stderr, "nxagentCheckForDepthsCompatibility: WARNING! Number of new available depths [%d] " + "doesn't match with old depths [%d].\n", nxagentNumDepths, + nxagentNumDepthsRecBackup); + #endif + + return 0; + } + + compatible = 1; + + for (i = 0; i < nxagentNumDepths; i++) + { + matched = 0; + + for (j = 0; j < nxagentNumDepthsRecBackup; j++) + { + if (nxagentDepths[i] == nxagentDepthsRecBackup[j]) + { + matched = 1; + + break; + } + } + + if (matched == 0) + { + #ifdef WARNING + fprintf(stderr, "nxagentCheckForDepthsCompatibility: WARNING! Failed to match available depth [%d].\n", + nxagentDepths[i]); + #endif + + compatible = 0; + + break; + } + } + + + if (compatible == 1) + { + #ifdef TEST + fprintf(stderr, "nxagentCheckForDepthsCompatibility: Internal depths match with " + "remote depths.\n"); + #endif + } + else + { + #ifdef WARNING + fprintf(stderr, "nxagentCheckForDepthsCompatibility: WARNING! New available depths don't match with " + "old depths.\n"); + #endif + } + + return compatible; +} + +static int nxagentCheckForPixmapFormatsCompatibility() +{ + int i, j; + int matched; + int compatible; + + compatible = 1; + + if (nxagentNumPixmapFormats != nxagentRemoteNumPixmapFormats) + { + #ifdef DEBUG + fprintf(stderr, "nxagentCheckForPixmapFormatsCompatibility: WARNING! Number of internal pixmap formats [%d] " + "doesn't match with remote formats [%d].\n", nxagentNumPixmapFormats, + nxagentRemoteNumPixmapFormats); + #endif + } + + for (i = 0; i < nxagentNumPixmapFormats; i++) + { + matched = 0; + + for (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 = 1; + + break; + } + } + + if (matched == 0) + { + #ifdef WARNING + fprintf(stderr, "nxagentCheckForPixmapFormatsCompatibility: WARNING! Failed to match internal " + "pixmap format depth [%d] bpp [%d] pad [%d].\n", nxagentPixmapFormats[i].depth, + nxagentPixmapFormats[i].bits_per_pixel, nxagentPixmapFormats[i].scanline_pad); + #endif + + compatible = 0; + } + } + + #ifdef TEST + + if (compatible == 1) + { + fprintf(stderr, "nxagentCheckForPixmapFormatsCompatibility: Internal pixmap formats match with " + "remote pixmap formats.\n"); + } + + #endif + + return compatible; +} + +static int nxagentInitAndCheckVisuals(int flexibility) +{ + 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) +{ + 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; + int packMethod, packQuality; + + #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. + */ + + packMethod = nxagentPackMethod; + packQuality = nxagentPackQuality; + + nxagentPackMethod = -1; + nxagentPackQuality = -1; + nxagentSplitThreshold = -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 *) xrealloc(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(flexibility) == 0) + { + nxagentSetReconnectError(FAILED_RESUME_DEPTHS_ALERT, + "Couldn't restore all the required depths."); + + return False; + } + + /* + * Check if all the required pixmap + * formats are supported. + */ + + nxagentInitPixmapFormats(); + + 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)); + + pV = nxagentVisuals[nxagentDefaultVisualIndex]; + + r = pV.red_mask; + g = pV.green_mask; + b = pV.blue_mask; + + if (!pV.red_mask || !pV.green_mask || !pV.blue_mask) + { + nxagentLogoBlack = 0x000000; + nxagentLogoRed = 0xff0000; + nxagentLogoWhite = 0xffffff; + } + else + { + for (or=0, off=0x800000; (r&(off>>or)) == 0; or++); + for (og=0, off=0x800000; (g&(off>>og)) == 0; og++); + for (ob=0, off=0x800000; (b&(off>>ob)) == 0; ob++); + + nxagentLogoRed = nxagentLogoColor(0xff0000); + nxagentLogoBlack = nxagentLogoColor(0x000000); + nxagentLogoWhite = 0xffffff; + } + + 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 + + AddEnabledDevice(nxagentXConnectionNumber); +} + +void nxagentRemoveXConnection() +{ + #ifdef TEST + fprintf(stderr, "nxagentRemoveXConnection: Removing the X connection [%d] " + "from the device set.\n", nxagentXConnectionNumber); + #endif + + RemoveEnabledDevice(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 SMART_SCHEDULE + + #ifdef DEBUG + fprintf(stderr, "nxagentWaitDisplay: Stopping the smart schedule timer.\n"); + #endif + + nxagentStopTimer(); + + #endif + + 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(); +} |