Unverified Commit 443b5272 authored by Mike Gabriel's avatar Mike Gabriel

Merge branch 'Ionic-feature/reconnect-checks' into 3.6.x

Attributes GH PR #183: https://github.com/ArcticaProject/nx-libs/pull/183 Together with PR #143 this fixes ArcticaProject/nx-libs#132. t merges an updated upstream into a topic branch.
parents e1c85caa 64a83d1e
...@@ -1405,6 +1405,82 @@ static void nxagentParseOptions(char *name, char *value) ...@@ -1405,6 +1405,82 @@ static void nxagentParseOptions(char *name, char *value)
return; 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, "none") == 0)
{
nxagentChangeOption(ReconnectTolerance, ToleranceChecksBypass);
}
else
{
/*
* Check for a matching integer. Or any integer, really.
*/
long tolerance_parse = 0;
errno = 0;
tolerance_parse = strtol(value, NULL, 10);
if ((errno) && (0 == tolerance_parse))
{
fprintf(stderr, "nxagentParseOptions: 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, "nxagentParseOptions: Warning: value [%s] of option [%s] "
"out of range, clamped to [%u].\n",
validateString(value), validateString(name), tolerance_parse);
}
if (0 > tolerance_parse)
{
tolerance_parse = 0;
fprintf(stderr, "nxagentParseOptions: Warning: value [%s] of option [%s] "
"out of range, clamped to [%u].\n",
validateString(value), validateString(name), tolerance_parse);
}
#ifdef TEST
switch (tolerance_parse) {
case ToleranceChecksStrict:
case ToleranceChecksSafe:
case ToleranceChecksRisky:
case ToleranceChecksBypass:
break;
default:
fprintf(stderr, "nxagentParseOptions: Warning: value [%s] of "
"option [%s] unknown, will be mapped to "
"\"Bypass\" [%u] value internally.\n",
validateString(value), validateString(name),
(unsigned int)ToleranceChecksBypass);
}
#endif
nxagentChangeOption(ReconnectTolerance, tolerance_parse);
}
return;
}
else else
{ {
#ifdef DEBUG #ifdef DEBUG
......
...@@ -45,6 +45,7 @@ is" without express or implied warranty. ...@@ -45,6 +45,7 @@ is" without express or implied warranty.
#include <unistd.h> #include <unistd.h>
#include <time.h> #include <time.h>
#include <errno.h> #include <errno.h>
#include <stdbool.h>
#include <nx-X11/X.h> #include <nx-X11/X.h>
#include <nx-X11/Xproto.h> #include <nx-X11/Xproto.h>
...@@ -178,7 +179,7 @@ static void nxagentInitDepths(void); ...@@ -178,7 +179,7 @@ static void nxagentInitDepths(void);
static void nxagentInitPixmapFormats(void); static void nxagentInitPixmapFormats(void);
static int nxagentCheckForDefaultDepthCompatibility(void); static int nxagentCheckForDefaultDepthCompatibility(void);
static int nxagentCheckForDepthsCompatibility(int flexibility); static int nxagentCheckForDepthsCompatibility(void);
static int nxagentCheckForPixmapFormatsCompatibility(void); static int nxagentCheckForPixmapFormatsCompatibility(void);
static int nxagentInitAndCheckVisuals(int flexibility); static int nxagentInitAndCheckVisuals(int flexibility);
static int nxagentCheckForColormapsCompatibility(int flexibility); static int nxagentCheckForColormapsCompatibility(int flexibility);
...@@ -1346,6 +1347,7 @@ FIXME: Use of nxagentParentWindow is strongly deprecated. ...@@ -1346,6 +1347,7 @@ FIXME: Use of nxagentParentWindow is strongly deprecated.
nxagentInitDepths(); nxagentInitDepths();
nxagentInitPixmapFormats(); nxagentInitPixmapFormats();
(void) nxagentCheckForPixmapFormatsCompatibility();
/* /*
* Create a pixmap for each depth matching the * Create a pixmap for each depth matching the
...@@ -1726,8 +1728,6 @@ XXX: Some X server doesn't list 1 among available depths... ...@@ -1726,8 +1728,6 @@ XXX: Some X server doesn't list 1 among available depths...
} }
} }
#endif #endif
nxagentCheckForPixmapFormatsCompatibility();
} }
void nxagentSetDefaultDrawables() void nxagentSetDefaultDrawables()
...@@ -2175,10 +2175,40 @@ void nxagentDisconnectDisplay(void) ...@@ -2175,10 +2175,40 @@ void nxagentDisconnectDisplay(void)
static int nxagentCheckForDefaultDepthCompatibility() static int nxagentCheckForDefaultDepthCompatibility()
{ {
/*
* Depending on the (reconnect) tolerance checks value, this
* function checks stricter or looser:
* - Strict means that the old and new default depth values
* must match exactly.
* - Safe or Risky means that the default depth values might differ,
* but the new default depth value must be at least as
* high as the former default depth value. This is
* recommended, because it allows clients with a
* higher default depth value to still connect, but
* not lose functionality.
* - Bypass or higher means that all of these checks are
* essentially deactivated. This is probably a very
* bad idea.
*/
int dDepth; int dDepth;
dDepth = DefaultDepth(nxagentDisplay, DefaultScreen(nxagentDisplay)); dDepth = DefaultDepth(nxagentDisplay, DefaultScreen(nxagentDisplay));
const unsigned int tolerance = nxagentOption(ReconnectTolerance);
if (ToleranceChecksBypass <= tolerance)
{
#ifdef WARNING
fprintf(stderr, "nxagentCheckForDefaultDepthCompatibility: WARNING! Not proceeding with any checks, "
"because tolerance [%u] higher than or equal [%u]. New default depth value "
"is [%d], former default depth value is [%d].\n", tolerance,
ToleranceChecksBypass, dDepth, nxagentDefaultDepthRecBackup);
#endif
return 1;
}
if (nxagentDefaultDepthRecBackup == dDepth) if (nxagentDefaultDepthRecBackup == dDepth)
{ {
#ifdef TEST #ifdef TEST
...@@ -2188,142 +2218,326 @@ static int nxagentCheckForDefaultDepthCompatibility() ...@@ -2188,142 +2218,326 @@ static int nxagentCheckForDefaultDepthCompatibility()
return 1; return 1;
} }
else if ((ToleranceChecksSafe <= tolerance) && (nxagentDefaultDepthRecBackup < dDepth))
{
#ifdef WARNING
fprintf(stderr, "nxagentCheckForDefaultDepthCompatibility: WARNING! New default depth [%d] "
"higher than the old default depth [%d] at tolerance [%u].\n", dDepth,
nxagentDefaultDepthRecBackup, tolerance);
#endif
return 1;
}
else else
{ {
#ifdef WARNING #ifdef WARNING
fprintf(stderr, "nxagentCheckForDefaultDepthCompatibility: WARNING! New default depth [%d] " fprintf(stderr, "nxagentCheckForDefaultDepthCompatibility: WARNING! New default depth [%d] "
"doesn't match with old default depth [%d].\n", dDepth, nxagentDefaultDepthRecBackup); "doesn't match with old default depth [%d] at tolerance [%u].\n", dDepth,
nxagentDefaultDepthRecBackup, tolerance);
#endif #endif
return 0; return 0;
} }
} }
static int nxagentCheckForDepthsCompatibility(int flexibility) static int nxagentCheckForDepthsCompatibility()
{ {
int i, j; /*
int matched; * Depending on the (reconnect) tolerance checks value, this
int compatible; * function checks stricter or looser:
* - Strict means that the number of old and new depths must
* match exactly and every old depth value must be
* available in the new depth array.
* - Safe means that the number of depths might diverge,
* but all former depth must also be included in the
* new depth array. This is recommended, because
* it allows clients with more depths to still
* connect, but not lose functionality.
* - Risky means that the new depths array is allowed to be
* smaller than the old depths array, but at least
* one depth value must be included in both.
* This is potentially unsafe.
* - Bypass or higher means that all of these checks are
* essentially deactivated. This is a very bad idea.
*/
const unsigned int tolerance = nxagentOption(ReconnectTolerance);
if (ToleranceChecksBypass <= tolerance)
{
#ifdef WARNING
fprintf(stderr, "nxagentCheckForDepthsCompatibility: WARNING! Not proceeding with any checks, "
"because tolerance [%u] higher than or equal [%u]. Number of newly available depths "
"is [%d], number of old depths is [%d].\n", tolerance, ToleranceChecksBypass,
nxagentNumDepths, nxagentNumDepthsRecBackup);
#endif
if (nxagentNumDepths != nxagentNumDepthsRecBackup) return 1;
}
if ((ToleranceChecksStrict == tolerance) && (nxagentNumDepths != nxagentNumDepthsRecBackup))
{ {
#ifdef WARNING #ifdef WARNING
fprintf(stderr, "nxagentCheckForDepthsCompatibility: WARNING! Number of new available depths [%d] " fprintf(stderr, "nxagentCheckForDepthsCompatibility: WARNING! No tolerance allowed and "
"doesn't match with old depths [%d].\n", nxagentNumDepths, "number of new available depths [%d] doesn't match with number of old "
"depths [%d].\n", nxagentNumDepths,
nxagentNumDepthsRecBackup); nxagentNumDepthsRecBackup);
#endif #endif
return 0; return 0;
} }
compatible = 1; if ((ToleranceChecksSafe == tolerance) && (nxagentNumDepths < nxagentNumDepthsRecBackup))
{
#ifdef WARNING
fprintf(stderr, "nxagentCheckForDepthsCompatibility: WARNING! Tolerance [%u] not "
"high enough and number of new available depths [%d] "
"lower than number of old depths [%d].\n", tolerance,
nxagentNumDepths, nxagentNumDepthsRecBackup);
#endif
for (i = 0; i < nxagentNumDepths; i++) return 0;
}
/*
* By now the tolerance is either:
* - Strict and both depth numbers match
* - Safe and:
* o the number of old and new depths matches exactly, or
* o the number of old depths is lower than the number
* of new depths
* - Risky
*/
bool compatible = true;
bool one_match = false;
bool matched = false;
int total_matches = 0;
/*
* FIXME: within this loop, we try to match all "new" depths
* against the "old" depths. Depending upon the flexibility
* value, either all "new" depths must have a corresponding
* counterpart in the "old" array, or at least one value
* must be included in both.
* Is this safe enough though?
* Shouldn't we better try to match entries in the "old"
* depths array against the "new" depths array, such that
* we know that all "old" values are covered by "new"
* values? Or is it more important that "new" values are
* covered by "old" ones, with potentially more "old"
* values lingering around that cannot be displayed by the
* connected client?
*
* This section probably needs a revisit at some point in time.
*/
for (int i = 0; i < nxagentNumDepths; ++i)
{ {
matched = 0; matched = false;
for (j = 0; j < nxagentNumDepthsRecBackup; j++) for (int j = 0; j < nxagentNumDepthsRecBackup; ++j)
{ {
if (nxagentDepths[i] == nxagentDepthsRecBackup[j]) if (nxagentDepths[i] == nxagentDepthsRecBackup[j])
{ {
matched = 1; matched = true;
one_match = true;
++total_matches;
break; break;
} }
} }
if (matched == 0) if ((ToleranceChecksRisky > tolerance) && (!matched))
{ {
#ifdef WARNING #ifdef WARNING
fprintf(stderr, "nxagentCheckForDepthsCompatibility: WARNING! Failed to match available depth [%d].\n", fprintf(stderr, "nxagentCheckForDepthsCompatibility: WARNING! Tolerance [%u] too low and "
nxagentDepths[i]); "failed to match available depth [%d].\n", tolerance, nxagentDepths[i]);
#endif #endif
compatible = 0; compatible = false;
break; break;
} }
} }
/*
* At Risky tolerance, only one match is necessary to be "compatible".
*/
if (ToleranceChecksRisky == tolerance)
{
compatible = one_match;
}
if (compatible == 1) int ret = (!(!compatible));
if (compatible)
{ {
#ifdef TEST #ifdef TEST
fprintf(stderr, "nxagentCheckForDepthsCompatibility: Internal depths match with " fprintf(stderr, "nxagentCheckForDepthsCompatibility: Internal depths match with "
"remote depths.\n"); "remote depths at tolerance [%u].\n", tolerance);
#endif
if (total_matches != nxagentNumDepths)
{
#ifdef WARNING
fprintf(stderr, "nxagentCheckForDepthsCompatibility: only some [%d] of the new depths [%d] "
"match with old depths [%d] at tolerance [%u].\n", total_matches, nxagentNumDepths,
nxagentNumDepthsRecBackup, tolerance);
#endif #endif
} }
}
else else
{ {
#ifdef WARNING #ifdef WARNING
fprintf(stderr, "nxagentCheckForDepthsCompatibility: WARNING! New available depths don't match with " fprintf(stderr, "nxagentCheckForDepthsCompatibility: WARNING! New available depths [%d] don't match "
"old depths.\n"); "with old depths [%d] at tolerance [%u]. Only [%d] depth values matched.\n",
nxagentNumDepths, nxagentNumDepthsRecBackup, tolerance, total_matches);
#endif #endif
} }
return compatible; return (ret);
} }
static int nxagentCheckForPixmapFormatsCompatibility() static int nxagentCheckForPixmapFormatsCompatibility()
{ {
int i, j; /*
int matched; * Depending on the (reconnect) tolerance checks value, this
int compatible; * function checks stricter or looser:
* - Strict means that the number of internal and external
* pixmap formats must match exactly and every
* internal pixmap format must be available in the
* external pixmap format array.
* - Safe means that the number of pixmap formats might
* diverge, but all internal pixmap formats must
* also be included in the external pixmap formats
* array. This is recommended, because it allows
* clients with more pixmap formats to still connect,
* but not lose functionality.
* - Risky means that the internal pixmap formats array is
* allowed to be smaller than the external pixmap
* formats array, but at least one pixmap format must
* be included in both. This is potentially unsafe.
* - Bypass or higher means that all of these checks are
* essentially deactivated. This is a very bad idea.
*/
const unsigned int tolerance = nxagentOption(ReconnectTolerance);
if (ToleranceChecksBypass <= tolerance)
{
#ifdef WARNING
fprintf(stderr, "nxagentCheckForPixmapFormatsCompatibility: WARNING! Not proceeding with any checks, "
"because tolerance [%u] higher than or equal [%u]. Number of internally available "
"pixmap formats is [%d], number of externally available pixmap formats is [%d].\n",
tolerance, ToleranceChecksBypass, nxagentNumPixmapFormats, nxagentRemoteNumPixmapFormats);
#endif
compatible = 1; return 1;
}
if (nxagentNumPixmapFormats != nxagentRemoteNumPixmapFormats) if ((ToleranceChecksStrict == tolerance) && (nxagentNumPixmapFormats != nxagentRemoteNumPixmapFormats))
{ {
#ifdef DEBUG #ifdef DEBUG
fprintf(stderr, "nxagentCheckForPixmapFormatsCompatibility: WARNING! Number of internal pixmap formats [%d] " fprintf(stderr, "nxagentCheckForPixmapFormatsCompatibility: WARNING! No tolerance allowed and number "
"doesn't match with remote formats [%d].\n", nxagentNumPixmapFormats, "of internal pixmap formats [%d] doesn't match with number of remote formats [%d].\n",
nxagentRemoteNumPixmapFormats); nxagentNumPixmapFormats, nxagentRemoteNumPixmapFormats);
#endif #endif
return 0;
} }
for (i = 0; i < nxagentNumPixmapFormats; i++) if ((ToleranceChecksSafe == tolerance) && (nxagentNumPixmapFormats > nxagentRemoteNumPixmapFormats))
{ {
matched = 0; #ifdef DEBUG
fprintf(stderr, "nxagentCheckForPixmapFormatsCompatibility: WARNING! Tolerance [%u] too low "
"and number of internal pixmap formats [%d] higher than number of external formats [%d].\n",
tolerance, nxagentNumPixmapFormats, nxagentRemoteNumPixmapFormats);
#endif
return 0;
}
/*
* By now the tolerance is either:
* - Strict
* - Safe and:
* o the number of internal and external pixmap formats
* matches exactly, or
* o the number of external pixmap formats is higher than
* the number of internal pixmap formats,
* - Risky
*/
for (j = 0; j < nxagentRemoteNumPixmapFormats; j++) bool compatible = true;
bool one_match = false;
bool matched = false;
int total_matches = 0;
for (int i = 0; i < nxagentNumPixmapFormats; ++i)
{
matched = false;
for (int j = 0; j < nxagentRemoteNumPixmapFormats; ++j)
{ {
if (nxagentPixmapFormats[i].depth == nxagentRemotePixmapFormats[j].depth && if (nxagentPixmapFormats[i].depth == nxagentRemotePixmapFormats[j].depth &&
nxagentPixmapFormats[i].bits_per_pixel == nxagentRemotePixmapFormats[j].bits_per_pixel && nxagentPixmapFormats[i].bits_per_pixel == nxagentRemotePixmapFormats[j].bits_per_pixel &&
nxagentPixmapFormats[i].scanline_pad == nxagentRemotePixmapFormats[j].scanline_pad) nxagentPixmapFormats[i].scanline_pad == nxagentRemotePixmapFormats[j].scanline_pad)
{ {
matched = 1; matched = true;
one_match = true;
++total_matches;
break; break;
} }
} }
if (matched == 0) if ((ToleranceChecksRisky > tolerance) && (!matched))
{ {
#ifdef WARNING #ifdef WARNING
fprintf(stderr, "nxagentCheckForPixmapFormatsCompatibility: WARNING! Failed to match internal " fprintf(stderr, "nxagentCheckForPixmapFormatsCompatibility: WARNING! Tolerance [%u] too low "
"pixmap format depth [%d] bpp [%d] pad [%d].\n", nxagentPixmapFormats[i].depth, "and failed to match internal pixmap format (depth [%d] bpp [%d] pad [%d]).\n",
nxagentPixmapFormats[i].bits_per_pixel, nxagentPixmapFormats[i].scanline_pad); tolerance, nxagentPixmapFormats[i].depth, nxagentPixmapFormats[i].bits_per_pixel,
nxagentPixmapFormats[i].scanline_pad);
#endif #endif
compatible = 0; compatible = false;
} }
} }
#ifdef TEST int ret = !(!(compatible));
if (compatible == 1) if (compatible)
{ {
#ifdef TEST
fprintf(stderr, "nxagentCheckForPixmapFormatsCompatibility: Internal pixmap formats match with " fprintf(stderr, "nxagentCheckForPixmapFormatsCompatibility: Internal pixmap formats match with "
"remote pixmap formats.\n"); "remote pixmap formats at tolerance [%u].\n", tolerance);
} #endif
if (total_matches != nxagentNumPixmapFormats)
{
#ifdef WARNING
fprintf(stderr, "nxagentCheckForPixmapFormatsCompatibility: Only some [%d] of the internal "
"pixmap formats [%d] match with external pixmap formats [%d] at tolerance [%u].\n",
total_matches, nxagentNumPixmapFormats, nxagentRemoteNumPixmapFormats, tolerance);
#endif #endif
}
}
else
{
#ifdef WARNING
fprintf(stderr, "nxagentCheckForPixmapFormatsCompatibility: WARNING! Internally available "
"pixmap formats [%d] don't match with external pixmap formats [%d] "
"at tolerance [%u]. Only [%d] depth values matched.\n",
nxagentNumPixmapFormats, nxagentRemoteNumPixmapFormats, tolerance, total_matches);
#endif
}
return compatible; return (ret);
} }
static int nxagentInitAndCheckVisuals(int flexibility) static int nxagentInitAndCheckVisuals(int flexibility)
{ {
/* FIXME: does this also need work? */
XVisualInfo viTemplate; XVisualInfo viTemplate;
XVisualInfo *viList; XVisualInfo *viList;
XVisualInfo *newVisuals; XVisualInfo *newVisuals;
...@@ -2433,6 +2647,7 @@ FIXME: Should the visual be ignored in this case? ...@@ -2433,6 +2647,7 @@ FIXME: Should the visual be ignored in this case?
static int nxagentCheckForColormapsCompatibility(int flexibility) static int nxagentCheckForColormapsCompatibility(int flexibility)
{ {
/* FIXME: does this also need work? */
if (nxagentNumDefaultColormaps == nxagentNumDefaultColormapsRecBackup) if (nxagentNumDefaultColormaps == nxagentNumDefaultColormapsRecBackup)
{ {
#ifdef TEST #ifdef TEST
...@@ -2592,7 +2807,7 @@ Bool nxagentReconnectDisplay(void *p0) ...@@ -2592,7 +2807,7 @@ Bool nxagentReconnectDisplay(void *p0)
reconnectDisplayState = GOT_DEPTH_LIST; reconnectDisplayState = GOT_DEPTH_LIST;
if (nxagentCheckForDepthsCompatibility(flexibility) == 0) if (nxagentCheckForDepthsCompatibility() == 0)
{ {
nxagentSetReconnectError(FAILED_RESUME_DEPTHS_ALERT, nxagentSetReconnectError(FAILED_RESUME_DEPTHS_ALERT,
"Couldn't restore all the required depths."); "Couldn't restore all the required depths.");
...@@ -2626,6 +2841,14 @@ Bool nxagentReconnectDisplay(void *p0) ...@@ -2626,6 +2841,14 @@ Bool nxagentReconnectDisplay(void *p0)
nxagentInitPixmapFormats(); nxagentInitPixmapFormats();
if (nxagentCheckForPixmapFormatsCompatibility() == 0)
{
nxagentSetReconnectError(FAILED_RESUME_PIXMAPS_ALERT,
"Couldn't restore all the required pixmap formats.");
return False;
}
reconnectDisplayState = GOT_PIXMAP_FORMAT_LIST; reconnectDisplayState = GOT_PIXMAP_FORMAT_LIST;
/* /*
......
...@@ -166,6 +166,8 @@ void nxagentInitOptions() ...@@ -166,6 +166,8 @@ void nxagentInitOptions()
nxagentOptions.Xinerama = 1; nxagentOptions.Xinerama = 1;
nxagentOptions.SleepTime = DEFAULT_SLEEP_TIME; nxagentOptions.SleepTime = DEFAULT_SLEEP_TIME;
nxagentOptions.ReconnectTolerance = DEFAULT_TOLERANCE;
} }
/* /*
......
...@@ -67,6 +67,17 @@ typedef enum _ClientOsType ...@@ -67,6 +67,17 @@ typedef enum _ClientOsType
} ClientOsType; } ClientOsType;
typedef enum _ToleranceChecksMode
{
ToleranceChecksStrict = 0,
ToleranceChecksSafe = 1,
ToleranceChecksRisky = 2,
ToleranceChecksBypass = 3
} ToleranceChecksMode;
#define DEFAULT_TOLERANCE ToleranceChecksStrict
/* /*
* Set of options affecting agent operations. * Set of options affecting agent operations.
*/ */
...@@ -414,6 +425,12 @@ typedef struct _AgentOptions ...@@ -414,6 +425,12 @@ typedef struct _AgentOptions
unsigned int SleepTime; unsigned int SleepTime;
/*
* Tolerance - tightens or loosens reconnect checks.
*/
ToleranceChecksMode ReconnectTolerance;
} AgentOptionsRec; } AgentOptionsRec;
typedef AgentOptionsRec *AgentOptionsPtr; typedef AgentOptionsRec *AgentOptionsPtr;
......
...@@ -9063,7 +9063,8 @@ int ParseEnvironmentOptions(const char *env, int force) ...@@ -9063,7 +9063,8 @@ int ParseEnvironmentOptions(const char *env, int force)
strcasecmp(name, "clipboard") == 0 || strcasecmp(name, "clipboard") == 0 ||
strcasecmp(name, "streaming") == 0 || strcasecmp(name, "streaming") == 0 ||
strcasecmp(name, "backingstore") == 0 || strcasecmp(name, "backingstore") == 0 ||
strcasecmp(name, "sleep") == 0) strcasecmp(name, "sleep") == 0 ||
strcasecmp(name, "tolerancechecks") == 0)
{ {
#ifdef DEBUG #ifdef DEBUG
*logofs << "Loop: Ignoring agent option '" << name *logofs << "Loop: Ignoring agent option '" << name
......
...@@ -325,7 +325,9 @@ shadowmode=s\n\ ...@@ -325,7 +325,9 @@ shadowmode=s\n\
defer=n\n\ defer=n\n\
tile=s\n\ tile=s\n\
menu=n\n\ menu=n\n\
sleep=n These options are interpreted by the NX agent. They\n\ sleep=n\n\
tolerancecehcks=s\n\
These options are interpreted by the NX agent. They\n\
are ignored by the proxy.\n\ are ignored by the proxy.\n\
\n\ \n\
Environment:\n\ Environment:\n\
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment