2025-08-08 20:00:36 +02:00

2534 lines
60 KiB
C

/**************************************************************************/
/* */
/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
/* Copyright (c) 2008-2017 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
/* Copyright (c) 2011-2022 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
/* Copyright (c) 2014-2019 Mihai Moldovan <ionic@ionic.de> */
/* Copyright (c) 2014-2022 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 <stdio.h>
#include <string.h>
#include <errno.h>
#include <libgen.h>
#ifdef __sun
#include <strings.h>
#endif
#include "X.h"
#include "Xproto.h"
#include "screenint.h"
#include "input.h"
#include "misc.h"
#include "globals.h"
#include "scrnintstr.h"
#include "dixstruct.h"
#include "servermd.h"
#include "opaque.h"
#include "Init.h"
#include "Agent.h"
#include "Display.h"
#include "Args.h"
#include "Options.h"
#include "Binder.h"
#include "Trap.h"
#include "Screen.h"
#include "Image.h"
#ifdef RENDER
#include "Render.h"
#endif
#include "Handlers.h"
#include "Error.h"
#include "Reconnect.h"
#include "Utils.h"
/*
* NX includes and definitions.
*/
#include "compext/Compext.h"
#include <nx/NXpack.h>
/*
* Set here the required log level.
*/
#define PANIC
#define WARNING
#undef TEST
#undef DEBUG
#undef WATCH
#ifdef WATCH
#include "unistd.h"
#endif
#ifdef PANORAMIX
#define PANORAMIX_DISABLED_COND (noPanoramiXExtension || PanoramiXExtensionDisabledHack)
#else
#define PANORAMIX_DISABLED_COND TRUE
#endif
#ifdef RANDR
#define RRXINERAMA_DISABLED_COND noRRXineramaExtension
#else
#define RRXINERAMA_DISABLED_COND TRUE
#endif
/*
* Define this to force the dispatcher
* to always use the dumb scheduler.
*/
#undef DISABLE_SMART_SCHEDULE
Bool nxagentUserDefinedFontPath = False;
/*
* From X11/ImUtil.c.
*/
extern int _XGetBitsPerPixel(Display *dpy, int depth);
extern char dispatchExceptionAtReset;
const char *nxagentProgName = NULL;
char nxagentDisplayName[NXAGENTDISPLAYNAMELENGTH];
Bool nxagentSynchronize = False;
Bool nxagentRealWindowProp = False;
Bool nxagentAutoDPI = False;
char nxagentShadowDisplayName[NXAGENTSHADOWDISPLAYNAMELENGTH] = {0};
char nxagentWindowName[NXAGENTWINDOWNAMELENGTH];
char nxagentDialogName[NXAGENTDIALOGNAMELENGTH];
char nxagentSessionId[NXAGENTSESSIONIDLENGTH] = {0};
char *nxagentOptionsFilenameOrString;
Bool nxagentFullGeneration = False;
int nxagentDefaultClass = TrueColor;
Bool nxagentUserDefaultClass = False;
int nxagentDefaultDepth;
Bool nxagentUserDefaultDepth = False;
struct UserGeometry nxagentUserGeometry = {0, 0, 0, 0, 0};
Bool nxagentUserBorderWidth = False;
int nxagentNumScreens = 0;
Bool nxagentReportWindowIds = False;
Bool nxagentReportPrivateWindowIds = False;
Bool nxagentDoDirectColormaps = False;
Window nxagentParentWindow = 0;
int nxagentLockDeferLevel = 0;
Bool nxagentResizeDesktopAtStartup = False;
Bool nxagentUseNXTrans = False;
Bool nxagentForceNXTrans = False;
int nxagentMaxAllowedResets = 0;
char *nxagentKeyboard = NULL;
Bool nxagentOnce = True;
int nxagentRemoteMajor = -1;
static void nxagentParseOptionString(char*);
/*
* Get the caption to be used for helper dialogs
* from agent's window name passed as parameter.
*/
static int nxagentGetDialogName(void);
Bool nxagentVerbose = False;
char *nxagentKeystrokeFile = NULL;
int ddxProcessArgument(int argc, char *argv[], int i)
{
/*
* The flavour can never change, so only set it once.
*
* ddxProcessArgument() is called once for every command line
* argument, with argv[i] being the argument, i > 0. argv[0] is the program name.
*/
if (nxagentProgName == NULL)
{
char *basec = strdup(argv[0]);
nxagentProgName = strdup(basename(basec));
SAFE_free(basec);
#ifdef X2GO
/*
* Check if we are running as X2Go Agent
*/
checkX2goAgent(argc, argv);
#endif
}
#ifdef TEST
fprintf(stderr, "%s: argv[0] [%s] nxagentProgName [%s]\n", __func__, argv[0], nxagentProgName);
#endif
/*
* Ensure that the options are set to their defaults.
*/
static Bool resetOptions = True;
if (resetOptions == True)
{
char *envOptions = NULL;
nxagentInitOptions();
resetOptions = False;
/*
* Ensure the correct order of options evaluation: the environment
* first, then those included in the options file and, last, the
* command line options.
*/
char *envDisplay = getenv("DISPLAY");
if (envDisplay != NULL && strlen(envDisplay) == 0)
{
envDisplay = NULL;
}
for (int j = 0; j < argc; j++)
{
if ((!strcmp(argv[j], "-display")) && (j + 1 < argc))
{
envOptions = strdup(argv[j + 1]);
#ifdef WARNING
if (envOptions == NULL)
{
fprintf(stderr, "ddxProcessArgument: WARNING! failed string allocation.\n");
}
#endif
break;
}
}
if ((envOptions == NULL) && (envDisplay != NULL))
{
envOptions = strdup(envDisplay);
#ifdef WARNING
if (envOptions == NULL)
{
fprintf(stderr, "ddxProcessArgument: WARNING! failed string allocation.\n");
}
#endif
}
if (envOptions != NULL)
{
nxagentParseOptionString(envOptions);
SAFE_free(envOptions);
}
for (int j = 0; j < argc; j++)
{
if ((!strcmp(argv[j], "-options") || !strcmp(argv[j], "-option")) && j + 1 < argc)
{
SAFE_free(nxagentOptionsFilenameOrString);
if (-1 == asprintf(&nxagentOptionsFilenameOrString, "%s", argv[j + 1]))
{
FatalError("malloc failed");
}
break;
}
}
nxagentProcessOptions(nxagentOptionsFilenameOrString);
} /* if (resetOptions == True) */
if (!strcmp(argv[i], "-B"))
{
#ifdef TEST
fprintf(stderr, "ddxProcessArgument: Checking the NX binder option.\n");
#endif
if (nxagentCheckBinder(argc, argv, i) > 0)
{
/*
* We are going to run the agent with the 'binder' option. Go
* straight to the proxy loop.
*/
#ifdef TEST
fprintf(stderr, "ddxProcessArgument: Entering the NX proxy binder loop.\n");
#endif
nxagentBinderLoop();
/*
* This will make an exit.
*/
nxagentBinderExit(0);
}
else
{
/*
* Exit with an error.
*/
nxagentBinderExit(1);
}
}
if (!strcmp(argv[i], "-display"))
{
if (++i < argc)
{
snprintf(nxagentDisplayName, NXAGENTDISPLAYNAMELENGTH, "%s", argv[i]);
return 2;
}
return 0;
}
if (!strcmp(argv[i], "-id"))
{
if (++i < argc)
{
snprintf(nxagentSessionId, NXAGENTSESSIONIDLENGTH, "%s", argv[i]);
return 2;
}
return 0;
}
if (!strcmp(argv[i], "-version"))
{
nxagentShowVersionInfo();
exit(0);
}
/*
* This had to be '-options' since the beginning
* but was '-option' by mistake. Now we have to
* handle the backward compatibility.
*/
if (!strcmp(argv[i], "-options") || !strcmp(argv[i], "-option"))
{
if (++i < argc)
{
SAFE_free(nxagentOptionsFilenameOrString);
if (-1 == asprintf(&nxagentOptionsFilenameOrString, "%s", argv[i]))
{
FatalError("malloc failed");
}
return 2;
}
return 0;
}
if (!strcmp(argv[i], "-sync")) {
nxagentSynchronize = True;
return 1;
}
if (!strcmp(argv[i], "-nxrealwindowprop")) {
nxagentRealWindowProp = True;
return 1;
}
if (!strcmp(argv[i], "-reportwids")) {
nxagentReportWindowIds = True;
return 1;
}
if (!strcmp(argv[i], "-reportprivatewids")) {
nxagentReportPrivateWindowIds = True;
return 1;
}
if (!strcmp(argv[i], "-full")) {
nxagentFullGeneration = True;
return 1;
}
if (!strcmp(argv[i], "-class")) {
if (++i < argc) {
if (!strcmp(argv[i], "StaticGray")) {
nxagentDefaultClass = StaticGray;
nxagentUserDefaultClass = True;
return 2;
}
else if (!strcmp(argv[i], "GrayScale")) {
nxagentDefaultClass = GrayScale;
nxagentUserDefaultClass = True;
return 2;
}
else if (!strcmp(argv[i], "StaticColor")) {
nxagentDefaultClass = StaticColor;
nxagentUserDefaultClass = True;
return 2;
}
else if (!strcmp(argv[i], "PseudoColor")) {
nxagentDefaultClass = PseudoColor;
nxagentUserDefaultClass = True;
return 2;
}
else if (!strcmp(argv[i], "TrueColor")) {
nxagentDefaultClass = TrueColor;
nxagentUserDefaultClass = True;
return 2;
}
else if (!strcmp(argv[i], "DirectColor")) {
nxagentDefaultClass = DirectColor;
nxagentUserDefaultClass = True;
return 2;
}
}
return 0;
}
if (!strcmp(argv[i], "-cc")) {
if (++i < argc && sscanf(argv[i], "%i", &nxagentDefaultClass) == 1) {
if (nxagentDefaultClass >= 0 && nxagentDefaultClass <= 5) {
nxagentUserDefaultClass = True;
/* let the OS layer process it as well, so return 0 */
}
}
return 0;
}
if (!strcmp(argv[i], "-depth")) {
if (++i < argc && sscanf(argv[i], "%i", &nxagentDefaultDepth) == 1) {
if (nxagentDefaultDepth > 0) {
nxagentUserDefaultDepth = True;
return 2;
}
}
return 0;
}
/*
* These options are now useless and are parsed only for
* compatibility with older versions.
*/
if (!strcmp(argv[i], "-fast") ||
!strcmp(argv[i], "-slow") ||
!strcmp(argv[i], "-hint") ||
!strcmp(argv[i], "-sss"))
{
fprintf(stderr, "Warning: Ignoring deprecated command line option '%s'.\n",
argv[i]);
return 1;
}
if (!strcmp(argv[i], "-backingstore"))
{
if (++i < argc)
{
if (!strcmp(argv[i], "0"))
{
nxagentChangeOption(BackingStore, BackingStoreNever);
}
else
{
nxagentChangeOption(BackingStore, BackingStoreForce);
}
return 2;
}
return 0;
}
if (!strcmp(argv[i], "-streaming"))
{
if (++i < argc)
{
if (!strcmp(argv[i], "0"))
{
nxagentChangeOption(Streaming, False);
}
else
{
nxagentChangeOption(Streaming, True);
}
return 2;
}
return 0;
}
if (!strcmp(argv[i], "-defer"))
{
int level;
if (++i < argc &&
sscanf(argv[i], "%i", &level) == 1 &&
level >= 0 && level <= 2)
{
if (!nxagentOption(Shadow))
{
nxagentChangeOption(DeferLevel, level);
/*
* The defer level set with the command line is not changed
* when the session is resumed.
*/
nxagentLockDeferLevel = 1;
}
return 2;
}
return 0;
}
if (!strcmp(argv[i], "-irlimit"))
{
int limit;
if (++i < argc &&
sscanf(argv[i], "%i", &limit) == 1)
{
nxagentChangeOption(ImageRateLimit, limit);
return 2;
}
return 0;
}
if (!strcmp(argv[i], "-tile"))
{
int width, height;
if (++i < argc &&
sscanf(argv[i], "%ix%i", &width, &height) == 2 &&
width >= 32 && height >= 32)
{
nxagentChangeOption(TileWidth, width);
nxagentChangeOption(TileHeight, height);
return 2;
}
return 0;
}
if (strcmp(argv[i], "-fp") == 0)
{
if(++i < argc)
{
#ifdef TEST
fprintf(stderr, "ddxProcessArgument: User defined font path [%s].\n", argv[i]);
#endif
nxagentUserDefinedFontPath = True;
defaultFontPath = argv[i];
}
else
{
UseMsg();
}
}
if (!strcmp(argv[i], "-geometry"))
{
if (++i < argc)
{
if (!strcmp(argv[i],"fullscreen") || !strcmp(argv[i],"allscreens"))
{
nxagentChangeOption(Fullscreen, True);
nxagentChangeOption(AllScreens, True);
}
else if (!strcmp(argv[i],"onescreen"))
{
nxagentChangeOption(Fullscreen, True);
nxagentChangeOption(AllScreens, False);
}
else
{
if (nxagentUserGeometry.flag == 0)
{
nxagentUserGeometry.flag = XParseGeometry(argv[i],
&nxagentUserGeometry.X,
&nxagentUserGeometry.Y,
&nxagentUserGeometry.Width,
&nxagentUserGeometry.Height);
}
}
if (nxagentUserGeometry.flag || (nxagentOption(Fullscreen)))
{
return 2;
}
}
return 0;
}
if (!strcmp(argv[i], "-bw"))
{
int bw;
if (++i < argc && sscanf(argv[i], "%i", &bw) == 1)
{
nxagentChangeOption(BorderWidth, bw);
if (bw >= 0)
{
nxagentUserBorderWidth = True;
return 2;
}
}
return 0;
}
if (!strcmp(argv[i], "-name"))
{
if (++i < argc)
{
snprintf(nxagentWindowName, NXAGENTWINDOWNAMELENGTH, "%s", argv[i]);
return 2;
}
return 0;
}
if (!strcmp(argv[i], "-scrns"))
{
if (++i < argc && sscanf(argv[i], "%i", &nxagentNumScreens) == 1)
{
if (nxagentNumScreens > 0)
{
if (nxagentNumScreens > MAXSCREENS)
{
ErrorF("Maximum number of screens is %d.\n", MAXSCREENS);
nxagentNumScreens = MAXSCREENS;
}
return 2;
}
}
return 0;
}
if (!strcmp(argv[i], "-install"))
{
nxagentDoDirectColormaps = True;
return 1;
}
if (!strcmp(argv[i], "-parent"))
{
if (++i < argc)
{
nxagentParentWindow = (XID) strtol (argv[i], (char**)NULL, 0);
return 2;
}
}
if (!strcmp(argv[i], "-forcenx"))
{
nxagentForceNXTrans = True;
return 1;
}
if (!strcmp(argv[i], "-norootlessexit"))
{
nxagentChangeOption(NoRootlessExit, True);
return 1;
}
if (!strcmp(argv[i], "-nomagicpixel"))
{
nxagentChangeOption(MagicPixel, False);
return 1;
}
if (!strcmp(argv[i], "-noonce"))
{
nxagentOnce = False;
return 1;
}
if (!strcmp(argv[i], "-kbtype") ||
!strcmp(argv[i], "-keyboard"))
{
if (++i < argc)
{
SAFE_free(nxagentKeyboard);
#ifdef X2GO
if (nxagentX2go && strcmp(argv[i], "null/null") == 0)
{
#ifdef TEST
fprintf(stderr, "%s: changing nxagentKeyboard from [null/null] to [clone].\n", __func__);
#endif
nxagentKeyboard = strdup("clone");
}
else
#endif
{
nxagentKeyboard = strdup(argv[i]);
}
if (nxagentKeyboard == NULL)
{
FatalError("malloc failed");
}
return 2;
}
return 0;
}
if (!strcmp(argv[i], "-extensions"))
{
return 1;
}
#ifdef RENDER
if (!strcmp(argv[i], "-norender"))
{
nxagentRenderEnable = False;
return 1;
}
#endif
if (!strcmp(argv[i], "-nocomposite"))
{
nxagentChangeOption(Composite, False);
return 1;
}
/* the composite extension is disabled by default so we provide a
way to enable it */
if (!strcmp(argv[i], "-composite"))
{
nxagentChangeOption(Composite, True);
return 1;
}
if (!strcmp(argv[i], "-nodamage"))
{
nxagentChangeOption(UseDamage, False);
return 1;
}
if (!strcmp(argv[i], "-autodpi")) {
nxagentAutoDPI = True;
return 1;
}
/*
* The original -noreset option, disabling dispatchExceptionAtReset,
* is the default. Use this option to restore the original
* behaviour.
*/
if (!strcmp(argv[i], "-reset"))
{
nxagentChangeOption(Reset, True);
return 1;
}
if (!strcmp(argv[i], "-persistent"))
{
nxagentChangeOption(Persistent, True);
return 1;
}
if (!strcmp(argv[i], "-nopersistent"))
{
nxagentChangeOption(Persistent, False);
return 1;
}
if (!strcmp(argv[i], "-noshmem"))
{
nxagentChangeOption(SharedMemory, False);
return 1;
}
if (!strcmp(argv[i], "-shmem"))
{
nxagentChangeOption(SharedMemory, True);
return 1;
}
if (!strcmp(argv[i], "-noignore"))
{
nxagentChangeOption(DeviceControl, True);
nxagentChangeOption(DeviceControlUserDefined, True);
return 1;
}
if (!strcmp(argv[i], "-nokbreset"))
{
nxagentChangeOption(ResetKeyboardAtResume, False);
return 1;
}
if (!strcmp(argv[i], "-noxkblock"))
{
nxagentChangeOption(InhibitXkb, False);
return 1;
}
/*
* Enable pseudo-rootless mode.
*/
if (!strcmp(argv[i], "-R"))
{
nxagentChangeOption(Binder, False);
nxagentChangeOption(Desktop, False);
nxagentChangeOption(Rootless, True);
return 1;
}
/*
* Enable the "desktop" mode. This is the default.
*/
if (!strcmp(argv[i], "-D"))
{
nxagentChangeOption(Binder, False);
nxagentChangeOption(Rootless, False);
nxagentChangeOption(Desktop, True);
return 1;
}
/*
* Enable the "shadow" mode.
*/
if (!strcmp(argv[i], "-S"))
{
nxagentChangeOption(Shadow, True);
nxagentChangeOption(DeferLevel, 0);
nxagentChangeOption(Persistent, False);
return 1;
}
if (!strcmp(argv[i], "-shadow"))
{
if (++i < argc)
{
snprintf(nxagentShadowDisplayName, NXAGENTSHADOWDISPLAYNAMELENGTH, "%s", argv[i]);
if (strcmp(nxagentShadowDisplayName, "") == 0)
{
FatalError("Invalid shadow display option");
}
return 2;
}
return 0;
}
if (!strcmp(argv[i], "-shadowmode"))
{
if (++i < argc)
{
if (!strcmp(argv[i], "0"))
{
nxagentChangeOption(ViewOnly, True);
}
else
{
nxagentChangeOption(ViewOnly, False);
}
return 2;
}
return 0;
}
/*
* Enable the auto-disconnect timeout.
*/
if (!strcmp(argv[i], "-timeout"))
{
int seconds;
if (++i < argc && sscanf(argv[i], "%i", &seconds) == 1)
{
if (seconds >= 0)
{
if (seconds > 0 && seconds < 60)
{
seconds = 60;
}
nxagentChangeOption(Timeout, seconds);
return 2;
}
}
return 0;
}
/*
* The return value for -query, -broadcast and -indirect must be 0
* to let the dix layer process these options.
*/
if (!strcmp(argv[i], "-query"))
{
nxagentChangeOption(Desktop, True);
nxagentChangeOption(Xdmcp, True);
nxagentMaxAllowedResets = 0;
return 0;
}
if (!strcmp(argv[i], "-broadcast"))
{
nxagentChangeOption(Desktop, True);
nxagentChangeOption(Xdmcp, True);
nxagentMaxAllowedResets = 0;
return 0;
}
if (!strcmp(argv[i], "-indirect"))
{
nxagentChangeOption(Desktop, True);
nxagentChangeOption(Xdmcp, True);
nxagentMaxAllowedResets = 1;
return 0;
}
if (!strcmp(argv[i], "-noshpix"))
{
nxagentChangeOption(SharedPixmaps, False);
return 1;
}
if (!strcmp(argv[i], "-shpix"))
{
nxagentChangeOption(SharedPixmaps, True);
return 1;
}
if (!strcmp(argv[i], "-clipboard"))
{
if ((!strcmp(argv[i+1], "both")) || (!strcmp(argv[i+1], "1")))
{
nxagentChangeOption(Clipboard, ClipboardBoth);
}
else if (!strcmp(argv[i+1], "client"))
{
nxagentChangeOption(Clipboard, ClipboardClient);
}
else if (!strcmp(argv[i+1], "server"))
{
nxagentChangeOption(Clipboard, ClipboardServer);
}
else if ((!strcmp(argv[i+1], "none")) || (!strcmp(argv[i+1], "0")))
{
nxagentChangeOption(Clipboard, ClipboardNone);
}
else
{
nxagentChangeOption(Clipboard, ClipboardBoth);
}
return 2;
}
if (!strcmp(argv[i], "-textclipboard"))
{
nxagentChangeOption(TextClipboard, True);
return 1;
}
if (!strcmp(argv[i], "-bs"))
{
nxagentChangeOption(BackingStore, BackingStoreNever);
return 1;
}
if (!strcmp(argv[i], "-verbose"))
{
nxagentVerbose = True;
return 1;
}
if (!strcmp(argv[i], "-keystrokefile"))
{
if (i + 1 < argc)
{
if (NULL != (nxagentKeystrokeFile = strdup(argv[i + 1])))
{
return 2;
} else {
FatalError("malloc failed");
}
}
return 0;
}
if (!strcmp(argv[i], "-autograb"))
{
nxagentChangeOption(AutoGrab, True);
return 1;
}
/*
* Disable Xinerama (i.e. fake it in Screen.c) if somehow Xinerama support
* has been disabled on the cmdline.
*/
if (PANORAMIX_DISABLED_COND && RRXINERAMA_DISABLED_COND)
nxagentChangeOption(Xinerama, False);
return 0;
}
/* copy from nxcomp's Loop.cpp */
static int hexval(char c)
{
if ((c >= '0') && (c <= '9'))
return c - '0';
if ((c >= 'a') && (c <= 'f'))
return c - 'a' + 10;
if ((c >= 'A') && (c <= 'F'))
return c - 'A' + 10;
return -1;
}
static void URLDecodeInPlace(char *str)
{
if (str)
{
char *to = str;
while (str[0])
{
if ((str[0] == '%') &&
(hexval(str[1]) >= 0) &&
(hexval(str[2]) >= 0))
{
*(to++) = hexval(str[1]) * 16 + hexval(str[2]);
str += 3;
}
else
*(to++) = *(str++);
}
*to = '\0';
}
}
static void nxagentParseSingleOption(char *name, char *value)
{
int argc;
char *argv[2] = { NULL, NULL };
#ifdef TEST
fprintf(stderr, "nxagentParseSingleOption: Processing option '%s' = '%s'.\n",
validateString(name), validateString(value));
#endif
URLDecodeInPlace(value);
if (!value)
value = "";
if (!strcmp(name, "kbtype") ||
!strcmp(name, "keyboard") ||
!strcmp(name, "id") ||
!strcmp(name, "display") ||
!strcmp(name, "clipboard") ||
!strcmp(name, "geometry") ||
!strcmp(name, "option") ||
!strcmp(name, "options") ||
!strcmp(name, "shadow") ||
!strcmp(name, "shadowmode") ||
!strcmp(name, "streaming") ||
!strcmp(name, "defer") ||
!strcmp(name, "tile"))
{
argv[1] = value;
argc = 2;
}
else if (!strcmp(name, "R") && !strcmp(value, "1"))
{
argc = 1;
}
else if (!strcmp(name, "fast") || !strcmp(name, "slow"))
{
fprintf(stderr, "Warning: Ignoring deprecated option '%s'.\n", name);
return;
}
else if (!strcmp(name, "render"))
{
if (nxagentReconnectTrap)
{
#ifdef DEBUG
fprintf(stderr, "nxagentParseSingleOption: Ignoring option 'render' at reconnection.\n");
#endif
}
else if (nxagentRenderEnable == UNDEFINED)
{
if (!strcmp(value, "1"))
{
nxagentRenderEnable = True;
}
else if (!strcmp(value, "0"))
{
nxagentRenderEnable = False;
}
else
{
fprintf(stderr, "Warning: Ignoring bad value '%s' for option 'render'.\n",
validateString(value));
}
}
return;
}
else if (!strcmp(name, "state"))
{
setStatePath(value);
return;
}
else if (!strcmp(name, "fullscreen"))
{
if (nxagentReconnectTrap)
{
#ifdef DEBUG
fprintf(stderr, "nxagentParseSingleOption: Ignoring option 'fullscreen' at reconnection.\n");
#endif
}
else if (!strcmp(value, "2"))
{
nxagentChangeOption(Fullscreen, True);
nxagentChangeOption(AllScreens, False);
}
else if (!strcmp(value, "1"))
{
nxagentChangeOption(Fullscreen, True);
nxagentChangeOption(AllScreens, True);
}
else if (!strcmp(value, "0"))
{
nxagentChangeOption(Fullscreen, False);
nxagentChangeOption(AllScreens, False);
}
else
{
fprintf(stderr, "Warning: Ignoring bad value '%s' for option 'fullscreen'.\n",
validateString(value));
}
return;
}
else if (!strcmp(name, "shpix"))
{
if (!strcmp(value, "1"))
{
nxagentChangeOption(SharedPixmaps, True);
}
else if (!strcmp(value, "0"))
{
nxagentChangeOption(SharedPixmaps, False);
}
else
{
fprintf(stderr, "Warning: Ignoring bad value '%s' for option 'shpix'.\n",
validateString(value));
}
return;
}
else if (!strcmp(name, "shmem"))
{
if (!strcmp(value, "1"))
{
nxagentChangeOption(SharedMemory, True);
}
else if (!strcmp(value, "0"))
{
nxagentChangeOption(SharedMemory, False);
}
else
{
fprintf(stderr, "Warning: Ignoring bad value '%s' for option 'shmem'.\n",
validateString(value));
}
return;
}
else if (!strcmp(name, "composite"))
{
if (!strcmp(value, "1"))
{
nxagentChangeOption(Composite, True);
}
else if (!strcmp(value, "0"))
{
nxagentChangeOption(Composite, False);
}
else
{
fprintf(stderr, "Warning: Ignoring bad value '%s' for option 'composite'.\n",
validateString(value));
}
return;
}
else if (!strcmp(name, "xinerama"))
{
#if !defined(PANORAMIX) && !defined(RANDR)
nxagentChangeOption(Xinerama, False);
fprintf(stderr, "Warning: No Xinerama support compiled into %s.\n", nxagentProgName);
return;
#else
if (PANORAMIX_DISABLED_COND && RRXINERAMA_DISABLED_COND)
{
nxagentChangeOption(Xinerama, False);
fprintf(stderr, "Warning: XINERAMA extension has been disabled on %s startup.\n", nxagentProgName);
return;
}
if (!strcmp(value, "1"))
{
nxagentChangeOption(Xinerama, True);
return;
}
else if (!strcmp(value, "0"))
{
nxagentChangeOption(Xinerama, False);
}
else
{
fprintf(stderr, "Warning: Ignoring bad value '%s' for option 'xinerama'.\n",
validateString(value));
}
return;
#endif
}
else if (!strcmp(name, "resize"))
{
if (!nxagentOption(DesktopResize) || strcmp(value, "0") == 0)
{
nxagentResizeDesktopAtStartup = False;
}
else if (strcmp(value, "1") == 0)
{
nxagentResizeDesktopAtStartup = True;
}
else
{
fprintf(stderr, "Warning: Ignoring bad value '%s' for option 'resize'.\n",
validateString(value));
}
return;
}
else if (!strcmp(name, "backingstore"))
{
if (!strcmp(value, "0"))
{
nxagentChangeOption(BackingStore, BackingStoreNever);
}
else
{
nxagentChangeOption(BackingStore, BackingStoreForce);
}
return;
}
else if (!strcmp(name, "menu"))
{
if (!strcmp(value, "0"))
{
nxagentChangeOption(Menu, False);
}
else
{
nxagentChangeOption(Menu, True);
}
return;
}
else if (!strcmp(name, "magicpixel"))
{
if (!strcmp(value, "0"))
{
nxagentChangeOption(MagicPixel, False);
}
else
{
nxagentChangeOption(MagicPixel, True);
}
return;
}
else if (!strcmp(name, "autodpi"))
{
if (nxagentReconnectTrap)
{
#ifdef DEBUG
fprintf(stderr, "nxagentParseSingleOption: Ignoring option 'autodpi' at reconnection.\n");
#endif
}
else if (!strcmp(value, "0"))
{
nxagentAutoDPI = False;
}
else
{
nxagentAutoDPI = True;
}
return;
}
else if (strcmp(name, "shadowuid") == 0)
{
nxagentShadowUid = atoi(value);
return;
}
else if (strcmp(name, "clients") == 0)
{
char *new = strdup(value);
if (new)
{
SAFE_free(nxagentClientsLogName);
nxagentClientsLogName = new;
}
else
{
fprintf(stderr, "Warning: Ignoring option [%s] because of memory problems\n",
validateString(name));
}
return;
}
else if (strcmp(name, "client") == 0)
{
if (strcmp(value, "winnt") == 0 || strcmp(value, "windows") == 0)
{
nxagentChangeOption(ClientOs, ClientOsWinnt);
}
else if (strcmp(value, "linux") == 0)
{
nxagentChangeOption(ClientOs, ClientOsLinux);
}
else if (strcmp(value, "solaris") == 0)
{
nxagentChangeOption(ClientOs, ClientOsSolaris);
}
else if (strcmp(value, "macosx") == 0)
{
nxagentChangeOption(ClientOs, ClientOsMac);
}
return;
}
else if (strcmp(name, "copysize") == 0)
{
nxagentChangeOption(CopyBufferSize, atoi(value));
return;
}
else if (!strcmp(name, "sleep"))
{
errno = 0;
long sleep_parse = strtol(value, NULL, 10);
if ((errno) && (0 == sleep_parse))
{
fprintf(stderr, "Unable to convert value [%s] of option [%s]. "
"Ignoring option.\n",
validateString(value), validateString(name));
return;
}
if ((long) UINT_MAX < sleep_parse)
{
sleep_parse = UINT_MAX;
fprintf(stderr, "Warning: value [%s] of option [%s] "
"out of range, clamped to [%lu].\n",
validateString(value), validateString(name), sleep_parse);
}
if (0 > sleep_parse)
{
sleep_parse = 0;
fprintf(stderr, "Warning: value [%s] of option [%s] "
"out of range, clamped to [%lu].\n",
validateString(value), validateString(name), sleep_parse);
}
nxagentChangeOption(SleepTimeMillis, sleep_parse);
return;
}
else if (!strcmp(name, "tolerancechecks"))
{
if (strcmp(value, "strict") == 0)
{
nxagentChangeOption(ReconnectTolerance, ToleranceChecksStrict);
}
else if (strcmp(value, "safe") == 0)
{
nxagentChangeOption(ReconnectTolerance, ToleranceChecksSafe);
}
else if (strcmp(value, "risky") == 0)
{
nxagentChangeOption(ReconnectTolerance, ToleranceChecksRisky);
}
else if (strcmp(value, "bypass") == 0)
{
nxagentChangeOption(ReconnectTolerance, ToleranceChecksBypass);
}
else
{
/*
* Check for a matching integer. Or any integer, really.
*/
errno = 0;
long tolerance_parse = strtol(value, NULL, 10);
if ((errno) && (0 == tolerance_parse))
{
fprintf(stderr, "Unable to convert value [%s] of option [%s]. "
"Ignoring option.\n",
validateString(value), validateString(name));
return;
}
if ((long) UINT_MAX < tolerance_parse)
{
tolerance_parse = UINT_MAX;
fprintf(stderr, "Warning: value [%s] of option [%s] "
"out of range, clamped to [%lu].\n",
validateString(value), validateString(name), tolerance_parse);
}
if (0 > tolerance_parse)
{
tolerance_parse = 0;
fprintf(stderr, "Warning: value [%s] of option [%s] "
"out of range, clamped to [%lu].\n",
validateString(value), validateString(name), tolerance_parse);
}
#ifdef TEST
switch (tolerance_parse) {
case ToleranceChecksStrict:
case ToleranceChecksSafe:
case ToleranceChecksRisky:
case ToleranceChecksBypass:
break;
default:
fprintf(stderr, "%s: Warning: value [%s] of "
"option [%s] unknown, will be mapped to "
"\"Bypass\" [%u] value internally.\n",
__func__, validateString(value), validateString(name),
(unsigned int)ToleranceChecksBypass);
}
#endif
nxagentChangeOption(ReconnectTolerance, tolerance_parse);
}
return;
}
else if (!strcmp(name, "keyconv"))
{
if (!strcmp(value, "off")) {
nxagentChangeOption(KeycodeConversion, KeycodeConversionOff);
}
else if (!strcmp(value, "on")) {
nxagentChangeOption(KeycodeConversion, KeycodeConversionOn);
}
else if (!strcmp(value, "auto")) {
nxagentChangeOption(KeycodeConversion, KeycodeConversionAuto);
}
else
{
fprintf(stderr, "Warning: Ignoring bad value '%s' for option 'keyconv'.\n",
validateString(value));
}
return;
}
else if (!strcmp(name, "autograb"))
{
if (!strcmp(value, "0"))
{
nxagentChangeOption(AutoGrab, False);
}
else
{
nxagentChangeOption(AutoGrab, True);
}
return;
}
else if (!strcmp(name, "textclipboard"))
{
if (!strcmp(value, "0"))
{
nxagentChangeOption(TextClipboard, False);
}
else
{
nxagentChangeOption(TextClipboard, True);
}
return;
}
else
{
#ifdef DEBUG
fprintf(stderr, "nxagentParseSingleOption: Ignored option [%s] with value [%s].\n",
validateString(name), validateString(value));
#endif
return;
}
/*
* Before passing the not yet evaluated options to
* ddxProcessArgument(), we have to add a dash as prefix.
*/
int size = strlen(name) + 1;
if (size > 1)
{
if ((argv[0] = malloc(size + 1)) == NULL)
{
fprintf(stderr, "Warning: Ignoring option '%s' due to lack of memory.\n",
name);
return;
}
*argv[0] = '-';
memcpy(argv[0] + 1, name, size);
}
ddxProcessArgument(argc, argv, 0);
SAFE_free(argv[0]);
}
static void nxagentParseOptionString(char *string)
{
char *value = NULL;
char *option = NULL;
/*
* we must not modify string, but strtok will insert \0. So let's
* work with a copy
*/
char *dup = strdup(string);
/*
* Remove the port specification.
*/
char *delimiter = rindex(dup, ':');
if (delimiter)
{
#ifdef DEBUG
fprintf(stderr, "%s: stripping port specification [%s]\n", __func__, delimiter);
#endif
*delimiter = '\0';
}
else
{
fprintf(stderr, "Warning: Option file doesn't contain a port specification.\n");
}
while ((option = strtok(option ? NULL : dup, ",")))
{
delimiter = rindex(option, '=');
if (delimiter)
{
*delimiter = '\0';
value = delimiter + 1;
}
else
{
value = NULL;
}
nxagentParseSingleOption(option, value);
}
SAFE_free(dup);
}
char *nxagentSkipNXMarker(char *string)
{
if (strncasecmp(string, "nx/nx,", 6) == 0 ||
strncasecmp(string, "nx/nx:", 6) == 0)
{
#ifdef DEBUG
fprintf(stderr, "%s: skipping [%6.6s]\n", __func__, string);
#endif
return string + 6;
}
else if (strncasecmp(string, "nx,", 3) == 0 ||
strncasecmp(string, "nx:", 3) == 0)
{
#ifdef DEBUG
fprintf(stderr, "%s: skipping [%3.3s]\n", __func__, string);
#endif
return string + 3;
}
else
{
return string;
}
}
void nxagentProcessOptions(char * string)
{
if (!string)
return;
#ifdef DEBUG
fprintf(stderr, "%s: Going to process option string/filename [%s].\n",
__func__, validateString(string));
#endif
/* if the "filename" starts with an nx marker treat it
as an option _string_ instead of a filename */
char *skipped = nxagentSkipNXMarker(string);
if (skipped != string)
{
nxagentParseOptionString(skipped);
}
else
{
nxagentProcessOptionsFile(string);
}
}
void nxagentProcessOptionsFile(char * filename)
{
FILE *file = NULL;
char *data = NULL;
int sizeOfFile;
int maxFileSize = 1024;
#ifdef DEBUG
fprintf(stderr, "nxagentProcessOptionsFile: Going to process option file [%s].\n",
validateString(filename));
#endif
/*
* Init statePath
*/
setStatePath("");
if (filename == NULL)
{
return;
}
if ((file = fopen(filename, "r")) == NULL)
{
fprintf(stderr, "Warning: Couldn't open option file '%s'. Error is '%s'.\n",
validateString(filename), strerror(errno));
goto nxagentProcessOptionsFileExit;
}
if (fseek(file, 0, SEEK_END) != 0)
{
fprintf(stderr, "Warning: Couldn't position inside option file '%s'. Error is '%s'.\n",
validateString(filename), strerror(errno));
goto nxagentProcessOptionsFileExit;
}
if ((sizeOfFile = ftell(file)) == -1)
{
fprintf(stderr, "Warning: Couldn't get the size of option file '%s'. Error is '%s'.\n",
validateString(filename), strerror(errno));
goto nxagentProcessOptionsFileExit;
}
#ifdef DEBUG
fprintf(stderr, "nxagentProcessOptionsFile: Processing option file [%s].\n",
validateString(filename));
#endif
rewind(file);
if (sizeOfFile > maxFileSize)
{
fprintf(stderr, "Warning: Maximum file size exceeded for options '%s'.\n",
validateString(filename));
goto nxagentProcessOptionsFileExit;
}
if ((data = malloc(sizeOfFile + 1)) == NULL)
{
fprintf(stderr, "Warning: Memory allocation failed processing file '%s'.\n",
validateString(filename));
goto nxagentProcessOptionsFileExit;
}
int offset = 0;
int size = 0;
for (;;)
{
size_t result = fread(data + offset, 1, sizeOfFile, file);
if (ferror(file) != 0)
{
fprintf(stderr, "Warning: Error reading the option file '%s'.\n",
validateString(filename));
goto nxagentProcessOptionsFileExit;
}
size += result;
offset += result;
if (feof(file) != 0 || (size == sizeOfFile))
{
break;
}
}
if (size != sizeOfFile)
{
fprintf(stderr, "Warning: Premature end of option file '%s' while reading.\n",
validateString(filename));
goto nxagentProcessOptionsFileExit;
}
/*
* Truncate the buffer to the first line.
*/
for (offset = 0; (offset < sizeOfFile) && (data[offset] != '\n'); offset++);
data[offset] = '\0';
#ifdef DEBUG
fprintf(stderr, "%s: first line of options file [%s]\n", __func__, data);
#endif
nxagentParseOptionString(nxagentSkipNXMarker(data));
nxagentProcessOptionsFileExit:
SAFE_free(data);
if (file)
{
if (fclose(file) != 0)
{
fprintf(stderr, "Warning: Couldn't close option file '%s'. Error is '%s'.\n",
validateString(filename), strerror(errno));
}
}
return;
}
/*
* FIXME: Transport initialization, shouldn't depend upon
* argv[], because we call it at every reconnection.
*/
Bool nxagentPostProcessArgs(char* name, Display* dpy, Screen* scr)
{
Bool useNXTrans = False;
#ifdef WATCH
fprintf(stderr, "nxagentPostProcessArgs: Watchpoint 2.\n");
/*
Reply Total Cached Bits In Bits Out Bits/Reply Ratio
------- ----- ------ ------- -------- ---------- -----
N/A
*/
sleep(30);
#endif
if (nxagentOption(Rootless) && nxagentOption(Fullscreen))
{
#ifdef TEST
fprintf(stderr, "WARNING: Ignoring fullscreen option for rootless session.\n");
#endif
nxagentChangeOption(Fullscreen, False);
nxagentChangeOption(AllScreens, False);
}
/*
* Ensure we have a valid name for children dialogs.
*/
nxagentGetDialogName();
/*
* Ensure we have a valid name for window name.
*/
if (*nxagentWindowName == '\0')
{
#ifdef X2GO
if (nxagentX2go)
{
snprintf(nxagentWindowName, NXAGENTWINDOWNAMELENGTH, "X2Go Agent");
}
else
#endif
{
snprintf(nxagentWindowName, NXAGENTWINDOWNAMELENGTH, "NX Agent");
}
}
/*
* Note that use of NX packed images as well as
* render extension could be later disabled due
* to the fact that session is running nested
* in a nxagent server.
*/
if (nxagentForceNXTrans)
{
useNXTrans = True;
}
else if ((strncasecmp(name, "nx/", 3) == 0) ||
(strncasecmp(name, "nx:", 3) == 0) ||
(strncasecmp(name, "nx,", 3) == 0) ||
(strcasecmp(name, "nx") == 0))
{
useNXTrans = True;
}
if (useNXTrans == True)
{
unsigned int linkType = LINK_TYPE_NONE;
unsigned int localMajor = 0;
unsigned int localMinor = 0;
unsigned int localPatch = 0;
unsigned int remoteMajor = 0;
unsigned int remoteMinor = 0;
unsigned int remotePatch = 0;
int splitTimeout = 0;
int motionTimeout = 0;
int splitMode = 0;
int splitSize = 0;
unsigned int packMethod = PACK_NONE;
unsigned int packQuality = 9;
int dataLevel = 0;
int streamLevel = 0;
int deltaLevel = 0;
unsigned int loadCache = 0;
unsigned int saveCache = 0;
unsigned int startupCache = 0;
unsigned int clientSegment = 0;
unsigned int serverSegment = 0;
unsigned int clientSize = 0;
unsigned int serverSize = 0;
if (NXGetControlParameters(dpy, &linkType, &localMajor, &localMinor,
&localPatch, &remoteMajor, &remoteMinor, &remotePatch,
&splitTimeout, &motionTimeout, &splitMode, &splitSize,
&packMethod, &packQuality, &dataLevel, &streamLevel,
&deltaLevel, &loadCache, &saveCache, &startupCache) == 0)
{
fprintf(stderr, "Warning: Failed to get the control parameters.\n");
}
nxagentChangeOption(LinkType, linkType);
#ifdef TEST
fprintf(stderr, "nxagentPostProcessArgs: Got local version [%d.%d.%d] remote version [%d.%d.%d].\n",
localMajor, localMinor, localPatch, remoteMajor, remoteMinor, remotePatch);
fprintf(stderr, "nxagentPostProcessArgs: Got split timeout [%d] motion timeout [%d].\n",
splitTimeout, motionTimeout);
fprintf(stderr, "nxagentPostProcessArgs: Got split mode [%d] split size [%d].\n",
splitMode, splitSize);
fprintf(stderr, "nxagentPostProcessArgs: Got preferred pack method [%d] and quality [%d].\n",
packMethod, packQuality);
#endif
if (remoteMajor < 2)
{
#ifdef TEST
fprintf(stderr, "nxagentPostProcessArgs: WARNING! Using backward compatible alpha encoding.\n");
#endif
nxagentAlphaCompat = True;
}
else
{
nxagentAlphaCompat = False;
}
nxagentRemoteMajor = remoteMajor;
if (nxagentPackMethod == -1)
{
nxagentPackMethod = packMethod;
}
if (nxagentPackQuality == -1)
{
nxagentPackQuality = packQuality;
}
/*
* Set the minimum size of images being streamed.
*/
if (nxagentSplitThreshold == -1)
{
nxagentSplitThreshold = splitSize;
}
/*
* Let the remote proxy use the shared memory extension, if
* supported by the X server. The client part is not useful and
* not implemented. The size of the segment is chosen by the
* user. The only purpose of the message is to reserve the XID
* that will be used by the remote.
*/
unsigned int enableClient = 0;
unsigned int enableServer = 1;
if (NXGetShmemParameters(dpy, &enableClient, &enableServer, &clientSegment,
&serverSegment, &clientSize, &serverSize) == 0)
{
fprintf(stderr, "Warning: Failed to get the shared memory parameters.\n");
}
if (enableServer == 1)
{
fprintf(stderr, "Info: Using shared memory parameters %d/%d/%d/%dK.\n",
nxagentOption(SharedMemory), nxagentOption(SharedPixmaps),
enableServer, serverSize / 1024);
}
else
{
fprintf(stderr, "Info: Using shared memory parameters %d/%d/0/0K.\n",
nxagentOption(SharedMemory), nxagentOption(SharedPixmaps));
}
/*
* We don't need the NoExpose events. Block them at the proxy
* side.
*/
NXSetExposeParameters(nxagentDisplay, 1, 1, 0);
}
else
{
/*
* We don't have a proxy on the remote side.
*/
nxagentChangeOption(LinkType, LINK_TYPE_NONE);
}
/*
* Set the lossless and lossy pack methods based on the user's
* preferences and the selected link type.
*/
nxagentSetPackMethod();
/*
* If not set, set the defer level and the synchronization timeout
* based on the link type.
*/
nxagentSetDeferLevel();
/*
* Also set the display output buffer size.
*/
nxagentSetBufferSize();
/*
* Select the preferred scheduler.
*/
nxagentSetScheduler();
/*
* Select the buffer coalescence timeout.
*/
nxagentSetCoalescence();
/*
* The enableBackingStore flag is defined
* in window.c in the dix.
*/
/*
FIXME: In rootless mode the backing-store support is not functional yet.
*/
if (nxagentOption(Rootless))
{
enableBackingStore = FALSE;
}
else if (nxagentOption(BackingStore) == BackingStoreUndefined ||
nxagentOption(BackingStore) == BackingStoreForce)
{
enableBackingStore = TRUE;
}
else if (nxagentOption(BackingStore) == BackingStoreNever)
{
enableBackingStore = FALSE;
}
/*
* need to check if this was set on the command line as this has
* the priority over the option file.
*/
if (nxagentRenderEnable == UNDEFINED)
{
nxagentRenderEnable = True;
}
if (nxagentRenderEnable == True)
{
nxagentAlphaEnabled = True;
}
else
{
nxagentAlphaEnabled = False;
}
if (nxagentOption(Rootless) && nxagentOption(Xdmcp))
{
FatalError("PANIC! Cannot start a XDMCP session in rootless mode.\n");
}
/*
* We enable server reset only for indirect XDMCP sessions.
*/
if (nxagentOption(Reset) && nxagentMaxAllowedResets == 0)
{
#ifdef WARNING
fprintf(stderr, "nxagentPostProcessArgs: Disabling the server reset.\n");
#endif
nxagentChangeOption(Reset, False);
dispatchExceptionAtReset = 0;
}
/*
* We skip server reset by default. This should be equivalent to
* passing the -noreset option to a standard XFree86 server.
*/
if (!nxagentOption(Reset))
{
#ifdef TEST
fprintf(stderr, "nxagentPostProcessArgs: Disabling dispatch of exception at server reset.\n");
#endif
dispatchExceptionAtReset = 0;
}
/*
* Check if the user activated the auto-disconect feature.
*/
if (nxagentOption(Timeout) > 0)
{
fprintf(stderr, "Info: Using auto-disconnect timeout of %d seconds.\n",
nxagentOption(Timeout));
}
#ifdef WATCH
fprintf(stderr, "nxagentPostProcessArgs: Watchpoint 3.\n");
/*
Reply Total Cached Bits In Bits Out Bits/Reply Ratio
------- ----- ------ ------- -------- ---------- -----
#16 1 256 bits (0 KB) -> 12 bits (0 KB) -> 256/1 -> 12/1 = 21.333:1
#233 A 1 256 bits (0 KB) -> 131 bits (0 KB) -> 256/1 -> 131/1 = 1.954:1
#245 A 2 512 bits (0 KB) -> 19 bits (0 KB) -> 256/1 -> 10/1 = 26.947:1
*/
sleep(30);
#endif
return useNXTrans;
}
void ddxUseMsg(void)
{
ErrorF("-full utilize full regeneration\n");
ErrorF("-class string default visual class\n");
ErrorF("-depth int default depth\n");
ErrorF("-geometry string window size and position (WXH+X+Y) or 'allscreens' / 'onescreen'\n");
ErrorF("-bw int window border width\n");
ErrorF("-name string window name\n");
ErrorF("-scrns int number of screens to generate\n");
ErrorF("-install install colormaps directly\n");
ErrorF("\nThe NX system adds the following arguments:\n");
ErrorF("-options file|string file or string containing nx/nx options\n");
ErrorF("-forcenx force use of NX protocol messages assuming communication through nxproxy\n");
ErrorF("-timeout int auto-disconnect timeout in seconds (minimum allowed: 60)\n");
ErrorF("-norootlessexit don't exit if there are no clients in rootless mode\n");
ErrorF("-nomagicpixel disable nxagent's magic pixel\n");
ErrorF("-autodpi detect real server's DPI and use that in the session\n");
ErrorF("-display string display name of the real server\n");
ErrorF("-sync synchronize with the real server\n");
ErrorF("-nxrealwindowprop set property NX_REAL_WINDOW for each X11 window inside nxagent\n");
ErrorF("-reportwids report externally exposed X11 window IDs to the session log\n");
ErrorF("-reportprivatewids report internal X11 window ID to the session log\n");
#ifdef RENDER
ErrorF("-norender disable the use of the render extension\n");
ErrorF("-nocomposite disable the use of the composite extension (default)\n");
ErrorF("-composite enable the use of the composite extension\n");
#endif
ErrorF("-nopersistent disable disconnection/reconnection to the X display on SIGHUP\n");
ErrorF("-noshmem disable use of shared memory extension\n");
ErrorF("-shmem enable use of shared memory extension\n");
ErrorF("-noshpix disable use of shared pixmaps\n");
ErrorF("-shpix enable use of shared pixmaps\n");
ErrorF("-noignore don't ignore pointer and keyboard configuration changes mandated by clients\n");
ErrorF("-nokbreset don't reset keyboard device if the session is resumed\n");
ErrorF("-noxkblock always allow applications to change layout through XKEYBOARD\n");
ErrorF("-autograb enable autograb\n");
ErrorF("-textclipboard limit clipboard data to text only\n");
ErrorF("-irlimit maximum image data rate to the encoder input in kB/s.\n");
ErrorF("-tile WxH maximum size of image tiles (minimum allowed: 32x32)\n");
ErrorF("-keystrokefile file file with keyboard shortcut definitions\n");
ErrorF("-options file|string path to an options file or an option string (for testing/debugging)\n");
ErrorF("-verbose print more warning and error messages\n");
ErrorF("-D enable desktop mode\n");
ErrorF("-R enable rootless mode\n");
ErrorF("-S enable shadow mode\n");
ErrorF("-B enable proxy binding mode\n");
ErrorF("-version show version information and exit\n");
}
static int nxagentGetDialogName(void)
{
if (*nxagentSessionId != '\0')
{
int length = strlen(nxagentSessionId);
/* if the session id contains an MD5 hash in a well-known format cut it off */
if (length > (MD5_LENGTH * 2 + 1) &&
*(nxagentSessionId + (length - (MD5_LENGTH * 2 + 1))) == '-')
{
length -= (MD5_LENGTH * 2 + 1);
}
snprintf(nxagentDialogName, NXAGENTDIALOGNAMELENGTH, "NX - %.*s", length, nxagentSessionId);
return 1;
}
snprintf(nxagentDialogName, NXAGENTDIALOGNAMELENGTH, "NX");
return 0;
}
void nxagentSetPackMethod(void)
{
unsigned char supportedMethods[NXNumberOfPackMethods];
unsigned int entries = NXNumberOfPackMethods;
if (nxagentOption(LinkType) == LINK_TYPE_NONE)
{
nxagentChangeOption(Streaming, False);
nxagentPackMethod = PACK_NONE;
nxagentPackLossless = PACK_NONE;
nxagentSplitThreshold = 0;
return;
}
/*
* Check if we need to select the lossy and lossless pack methods
* based on the link type.
*/
int method = nxagentPackMethod;
if (method == PACK_ADAPTIVE)
{
#ifdef TEST
fprintf(stderr, "nxagentSetPackMethod: Using adaptive mode for image compression.\n");
#endif
nxagentChangeOption(Adaptive, True);
}
else
{
#ifdef TEST
fprintf(stderr, "nxagentSetPackMethod: Not using adaptive mode for image compression.\n");
#endif
nxagentChangeOption(Adaptive, False);
}
if (method == PACK_LOSSY || method == PACK_ADAPTIVE)
{
nxagentPackMethod = PACK_JPEG_16M_COLORS;
}
else if (method == PACK_LOSSLESS)
{
switch (nxagentOption(LinkType))
{
case LINK_TYPE_MODEM:
case LINK_TYPE_ISDN:
case LINK_TYPE_ADSL:
case LINK_TYPE_WAN:
{
nxagentPackMethod = PACK_BITMAP_16M_COLORS;
break;
}
case LINK_TYPE_LAN:
{
nxagentPackMethod = PACK_RLE_16M_COLORS;
break;
}
default:
{
fprintf(stderr, "Warning: Unknown link type '%d' while setting the pack method.\n",
nxagentOption(LinkType));
break;
}
}
}
/*
* Query the remote proxy to determine whether the selected methods
* are supported.
*/
if (NXGetUnpackParameters(nxagentDisplay, &entries, supportedMethods) == 0 ||
entries != NXNumberOfPackMethods)
{
fprintf(stderr, "Warning: Unable to retrieve the supported pack methods.\n");
nxagentPackMethod = PACK_NONE;
nxagentPackLossless = PACK_NONE;
}
else
{
if (nxagentPackMethod == PACK_BITMAP_16M_COLORS ||
nxagentPackMethod == PACK_RLE_16M_COLORS ||
nxagentPackMethod == PACK_RGB_16M_COLORS ||
nxagentPackMethod == PACK_NONE)
{
nxagentPackLossless = nxagentPackMethod;
}
else
{
if (nxagentOption(LinkType) == LINK_TYPE_LAN)
{
nxagentPackLossless = PACK_RLE_16M_COLORS;
}
else
{
nxagentPackLossless = PACK_BITMAP_16M_COLORS;
}
}
if (supportedMethods[nxagentPackLossless] == 0)
{
nxagentPackLossless = PACK_NONE;
}
#ifdef TEST
fprintf(stderr, "nxagentSetPackMethod: Using method [%d] for lossless compression.\n",
nxagentPackLossless);
#endif
if (supportedMethods[nxagentPackMethod] == 0)
{
fprintf(stderr, "Warning: Pack method '%d' not supported by the proxy.\n",
nxagentPackMethod);
fprintf(stderr, "Warning: Replacing with lossless pack method '%d'.\n",
nxagentPackLossless);
nxagentPackMethod = nxagentPackLossless;
}
}
if (nxagentPackMethod == nxagentPackLossless)
{
nxagentPackQuality = 9;
}
#ifdef TEST
fprintf(stderr, "nxagentSetPackMethod: Assuming pack methods [%d] and [%d] with "
"quality [%d].\n", nxagentPackMethod, nxagentPackLossless, nxagentPackQuality);
#endif
}
/*
* Each defer level adds the following rules to the previous ones:
*
* Level 0 Eager encoding.
*
* Level 1 No data is put or copied on pixmaps, marking them always
* as corrupted and synchronizing them on demand, i.e. when
* a copy area to a window is requested, the source is syn-
* chronized before copying it.
*
* Level 2 The put images over the windows are skipped marking the
* destination as corrupted. The same happens for copy area
* and composite operations, spreading the corrupted regions
* of involved drawables.
*/
void nxagentSetDeferLevel(void)
{
/* defaults */
int deferLevel = 0;
int tileWidth = 64;
int tileHeight = 64;
int deferTimeout = 200;
/*
* Streaming is only partly implemented and is not available in this
* version of the agent.
*/
if (nxagentOption(Streaming))
{
fprintf(stderr, "Warning: Streaming of images not available in this agent.\n");
nxagentChangeOption(Streaming, False);
}
switch (nxagentOption(LinkType))
{
case LINK_TYPE_MODEM:
case LINK_TYPE_ISDN: { deferLevel = 2; tileWidth = 64; tileHeight = 64; break; }
case LINK_TYPE_ADSL: { deferLevel = 2; tileWidth = 4096; tileHeight = 4096; break; }
case LINK_TYPE_WAN: { deferLevel = 1; tileWidth = 4096; tileHeight = 4096; break; }
case LINK_TYPE_NONE:
case LINK_TYPE_LAN: { deferLevel = 0; tileWidth = 4096; tileHeight = 4096; break; }
default:
{
fprintf(stderr, "Warning: Unknown link type '%d' processing the defer option.\n",
nxagentOption(LinkType));
break;
}
}
/*
* Set the defer timeout.
*/
if (nxagentOption(Shadow))
{
#ifdef TEST
fprintf(stderr, "nxagentSetDeferLevel: Ignoring defer timeout parameter in shadow mode.\n");
#endif
}
else
{
nxagentChangeOption(DeferTimeout, deferTimeout);
}
/*
* Set the defer level.
*/
if (nxagentOption(Shadow))
{
#ifdef TEST
fprintf(stderr, "nxagentSetDeferLevel: Ignoring defer parameter in shadow mode.\n");
#endif
}
else if (nxagentOption(DeferLevel) != UNDEFINED)
{
#ifdef TEST
fprintf(stderr, "nxagentSetDeferLevel: Not overriding the [defer] option "
"with value [%d]. Defer timeout is [%ld] ms.\n", nxagentOption(DeferLevel),
nxagentOption(DeferTimeout));
#endif
}
else
{
nxagentChangeOption(DeferLevel, deferLevel);
#ifdef TEST
fprintf(stderr, "nxagentSetDeferLevel: Assuming defer level [%d] with timeout of [%ld] ms.\n",
nxagentOption(DeferLevel), nxagentOption(DeferTimeout));
#endif
}
/*
* Set the tile width.
*/
if (nxagentOption(TileWidth) != UNDEFINED)
{
#ifdef TEST
fprintf(stderr, "nxagentSetDeferLevel: Not overriding the [tile] option "
"width value [%d].\n", nxagentOption(TileWidth));
#endif
}
else
{
nxagentChangeOption(TileWidth, tileWidth);
#ifdef TEST
fprintf(stderr, "nxagentSetDeferLevel: Assuming tile width [%d].\n",
nxagentOption(TileWidth));
#endif
}
/*
* Set the tile height.
*/
if (nxagentOption(TileHeight) != UNDEFINED)
{
#ifdef TEST
fprintf(stderr, "nxagentSetDeferLevel: Not overriding the [tile] option "
"height value [%d].\n", nxagentOption(TileHeight));
#endif
}
else
{
nxagentChangeOption(TileHeight, tileHeight);
#ifdef TEST
fprintf(stderr, "nxagentSetDeferLevel: Assuming tile height [%d].\n",
nxagentOption(TileHeight));
#endif
}
}
void nxagentSetBufferSize(void)
{
int size = 16384;
switch (nxagentOption(LinkType))
{
case LINK_TYPE_MODEM: { size = 4096; break; }
case LINK_TYPE_ISDN: { size = 4096; break; }
case LINK_TYPE_ADSL: { size = 8192; break; }
case LINK_TYPE_WAN: { size = 16384; break; }
case LINK_TYPE_NONE:
case LINK_TYPE_LAN: { size = 16384; break; }
default:
{
fprintf(stderr, "Warning: Unknown link type '%d' while setting the display buffer size.\n",
nxagentOption(LinkType));
break;
}
}
nxagentChangeOption(DisplayBuffer, size);
nxagentBuffer = size;
if (NXSetDisplayBuffer(nxagentDisplay, nxagentBuffer) < 0)
{
fprintf(stderr, "Warning: Can't set the display buffer size to [%d].\n",
nxagentBuffer);
}
}
void nxagentSetScheduler(void)
{
/*
* The smart scheduler is the default.
*/
if (nxagentOption(Shadow))
{
#ifdef TEST
fprintf(stderr, "nxagentSetScheduler: Using the dumb scheduler in shadow mode.\n");
#endif
nxagentDisableTimer();
}
}
void nxagentSetCoalescence(void)
{
int timeout = 0;
switch (nxagentOption(LinkType))
{
case LINK_TYPE_MODEM: { timeout = 50; break; }
case LINK_TYPE_ISDN: { timeout = 20; break; }
case LINK_TYPE_ADSL: { timeout = 10; break; }
case LINK_TYPE_WAN: { timeout = 5; break; }
case LINK_TYPE_NONE:
case LINK_TYPE_LAN: { timeout = 0; break; }
default:
{
fprintf(stderr, "Warning: Unknown link type '%d' while setting the display coalescence.\n",
nxagentOption(LinkType));
break;
}
}
#ifdef TEST
fprintf(stderr, "nxagentSetCoalescence: Using coalescence timeout of [%d] ms.\n",
timeout);
#endif
nxagentChangeOption(DisplayCoalescence, timeout);
}
void nxagentShowVersionInfo(void)
{
ErrorF("NXAGENT - Version " NX_VERSION_CURRENT_STRING "\n");
}