1263 lines
33 KiB
C
1263 lines
33 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. */
|
|
/* */
|
|
/**************************************************************************/
|
|
|
|
#include "dixstruct.h"
|
|
#include "scrnintstr.h"
|
|
#include "windowstr.h"
|
|
#include "osdep.h"
|
|
|
|
#include "Agent.h"
|
|
#include "Handlers.h"
|
|
#include "Display.h"
|
|
#include "Events.h"
|
|
#include "Client.h"
|
|
#include "Reconnect.h"
|
|
#include "Dialog.h"
|
|
#include "Drawable.h"
|
|
#include "Xdmcp.h"
|
|
#include "Screen.h"
|
|
#include "Millis.h"
|
|
|
|
#define Window XlibWindow
|
|
#include "compext/Compext.h"
|
|
#undef Window
|
|
|
|
#include <nx/Shadow.h>
|
|
|
|
/*
|
|
* Set here the required log level.
|
|
*/
|
|
|
|
#define PANIC
|
|
#define WARNING
|
|
#undef TEST
|
|
#undef DEBUG
|
|
#undef DUMP
|
|
|
|
/*
|
|
* Log begin and end of the important handlers.
|
|
*/
|
|
|
|
#undef BLOCKS
|
|
|
|
/*
|
|
* If not defined, flush immediately upon entering the block handler.
|
|
*/
|
|
|
|
#define FLUSH_AFTER_MULTIPLE_READS
|
|
|
|
/*
|
|
* The soft limit should roughly match the size of the Xlib I/O
|
|
* buffer.
|
|
*/
|
|
|
|
#define BYTES_BEFORE_SOFT_TOKEN 2048
|
|
#define BYTES_BEFORE_HARD_TOKEN 65536
|
|
|
|
/*
|
|
* Maximum number of synchronization requests before waiting for the
|
|
* remote.
|
|
*/
|
|
|
|
#define TOKENS_PENDING_LIMIT 8
|
|
|
|
/*
|
|
* Limits are very unobtrusive. We don't want to interfere with the
|
|
* normal dispatching.
|
|
*/
|
|
|
|
#define BYTES_BEFORE_YIELD 1048576
|
|
#define TIME_BEFORE_YIELD 500
|
|
|
|
/*
|
|
* Dynamically reduce the display buffer size after a congestion.
|
|
*/
|
|
|
|
#undef DYNAMIC_DISPLAY_BUFFER
|
|
|
|
/*
|
|
* Minimum display buffer size.
|
|
*/
|
|
|
|
#define MINIMUM_DISPLAY_BUFFER 512
|
|
|
|
#ifdef NX_DEBUG_INPUT
|
|
extern int nxagentDebugInputDevices;
|
|
extern unsigned long nxagentLastInputDevicesDumpTime;
|
|
|
|
extern void nxagentDumpInputDevicesState(void);
|
|
#endif
|
|
|
|
/*
|
|
* Used in the handling of the X desktop manager protocol.
|
|
*/
|
|
|
|
Bool nxagentXdmcpUp = False;
|
|
Bool nxagentXdmcpAlertUp = False;
|
|
|
|
/*
|
|
* Also used in the block, wakeup and sync handlers.
|
|
*/
|
|
|
|
int nxagentBuffer;
|
|
Bool nxagentBlocking;
|
|
|
|
/* FIXME: nxagentCongestion is checked as a Boolean and as an Integer
|
|
(>= 4) at the same time which is weird at least */
|
|
int nxagentCongestion;
|
|
|
|
double nxagentBytesIn;
|
|
double nxagentBytesOut;
|
|
|
|
/*
|
|
* Total number of descriptors ready as reported by the wakeup
|
|
* handler.
|
|
*/
|
|
|
|
int nxagentReady;
|
|
|
|
/*
|
|
* Timestamp of the last write to the remote display.
|
|
*/
|
|
|
|
int nxagentFlush;
|
|
|
|
/*
|
|
* Arbitrate the bandwidth among our clients.
|
|
*/
|
|
|
|
struct _TokensRec nxagentTokens = { 0, 0, 0 };
|
|
struct _DispatchRec nxagentDispatch = { UNDEFINED, 0, 0, 0 };
|
|
|
|
/*
|
|
* Called just before blocking, waiting for our clients or the X
|
|
* server.
|
|
*/
|
|
|
|
extern Bool nxagentSkipImage;
|
|
|
|
void nxagentBlockHandler(void * data, struct timeval **timeout, void * mask)
|
|
{
|
|
/*
|
|
* Zero timeout.
|
|
*/
|
|
|
|
static struct timeval zero;
|
|
|
|
/*
|
|
* Current timestamp.
|
|
*/
|
|
|
|
static int now;
|
|
|
|
/*
|
|
* Pending bytes to write to the network.
|
|
*/
|
|
|
|
static int flushable;
|
|
|
|
/*
|
|
* Set if we need to synchronize any drawable.
|
|
*/
|
|
|
|
static Bool synchronize;
|
|
|
|
#ifdef BLOCKS
|
|
fprintf(stderr, "[Begin block]\n");
|
|
#endif
|
|
|
|
now = GetTimeInMillis();
|
|
|
|
#ifdef NX_DEBUG_INPUT
|
|
|
|
if (nxagentDebugInputDevices == 1 &&
|
|
now - nxagentLastInputDevicesDumpTime > 5000)
|
|
{
|
|
nxagentLastInputDevicesDumpTime = now;
|
|
|
|
nxagentDumpInputDevicesState();
|
|
}
|
|
|
|
#endif
|
|
|
|
if (nxagentNeedConnectionChange())
|
|
{
|
|
#ifdef TEST
|
|
fprintf(stderr, "nxagentBlockHandler: Calling nxagentHandleConnectionChanges "
|
|
"with ioError [%d] sigHup [%d].\n", nxagentException.ioError, nxagentException.sigHup);
|
|
#endif
|
|
|
|
nxagentHandleConnectionChanges();
|
|
}
|
|
|
|
if (nxagentOption(Rootless) &&
|
|
nxagentLastWindowDestroyed && nxagentRootlessDialogPid == 0 &&
|
|
now > nxagentLastWindowDestroyedTime + 30 * 1000 && !nxagentOption(NoRootlessExit))
|
|
{
|
|
#ifdef WARNING
|
|
fprintf(stderr, "nxagentBlockHandler: No application running. Closing the session.\n");
|
|
#endif
|
|
|
|
nxagentTerminateSession();
|
|
}
|
|
|
|
#ifdef TEST
|
|
|
|
if (nxagentLastWindowDestroyed)
|
|
{
|
|
fprintf(stderr, "nxagentBlockHandler: Elapsed time [%lu].\n",
|
|
now - nxagentLastWindowDestroyedTime);
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
* Slow down the agent if the session is not connected to a valid
|
|
* display.
|
|
*/
|
|
|
|
if (NXDisplayError(nxagentDisplay) == 1 && nxagentShadowCounter == 0 && nxagentOption(SleepTimeMillis) > 0)
|
|
{
|
|
#ifdef TEST
|
|
fprintf(stderr, "nxagentBlockHandler: sleeping for %d milliseconds for slowdown.\n",
|
|
nxagentOption(SleepTimeMillis));
|
|
#endif
|
|
usleep(nxagentOption(SleepTimeMillis) * 1000);
|
|
|
|
now = GetTimeInMillis();
|
|
}
|
|
#ifdef TEST
|
|
else if (0 == nxagentOption(SleepTimeMillis)) {
|
|
fprintf(stderr, "nxagentBlockHandler: not sleeping for slowdown.\n");
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Update the shadow display. This is only for test purposes.
|
|
*/
|
|
|
|
#ifdef DUMP
|
|
|
|
nxagentPixmapOnShadowDisplay(NULL);
|
|
|
|
nxagentFbOnShadowDisplay();
|
|
|
|
#endif
|
|
|
|
/*
|
|
* We need this here because some window configuration changes can
|
|
* be generated by the X server outside the control of the DIX.
|
|
*/
|
|
|
|
nxagentFlushConfigureWindow();
|
|
|
|
/*
|
|
* Check whether there is any drawable to synchronize.
|
|
*/
|
|
|
|
#ifdef TEST
|
|
fprintf(stderr, "nxagentBlockHandler: Checking synchronization at %s with "
|
|
"[%d][%d][%d].\n", GetTimeInMillisAsString(), nxagentCorruptedWindows,
|
|
nxagentCorruptedBackgrounds, nxagentCorruptedPixmaps);
|
|
#endif
|
|
|
|
synchronize = (nxagentCorruptedWindows > 0 ||
|
|
nxagentCorruptedBackgrounds > 0 ||
|
|
nxagentCorruptedPixmaps > 0);
|
|
|
|
/*
|
|
* The synchronization function requires a mask as parameter:
|
|
*
|
|
* EVENT_BREAK Breaks if an user input, like a key press
|
|
* or a mouse move, is detected.
|
|
*
|
|
* CONGESTION_BREAK Breaks if the congestion becomes greater
|
|
* than 4.
|
|
*
|
|
* BLOCKING_BREAK Breaks if the display descriptor becomes
|
|
* blocked for write during the loop.
|
|
*
|
|
* ALWAYS_BREAK Any of the previous conditions is met.
|
|
*
|
|
* NEVER_BREAK The loop continues until all the drawables
|
|
* are synchronized.
|
|
*/
|
|
|
|
if (synchronize)
|
|
{
|
|
/*
|
|
* We should not enter the synchronization loop if there is any
|
|
* user input pending, i.e. if we are in the middle of a scroll
|
|
* operation.
|
|
*/
|
|
|
|
if (nxagentForceSynchronization)
|
|
{
|
|
#ifdef TEST
|
|
fprintf(stderr, "nxagentBlockHandler: Going to force a synchronization at %s.\n",
|
|
GetTimeInMillisAsString());
|
|
#endif
|
|
|
|
nxagentSynchronizationLoop(NEVER_BREAK);
|
|
|
|
nxagentForceSynchronization = False;
|
|
}
|
|
else if (nxagentUserInput(NULL) == 0 &&
|
|
!nxagentBlocking &&
|
|
nxagentCongestion <= 4)
|
|
{
|
|
#ifdef TEST
|
|
fprintf(stderr, "nxagentBlockHandler: Going to synchronize at %s.\n",
|
|
GetTimeInMillisAsString());
|
|
#endif
|
|
|
|
nxagentSynchronizationLoop(ALWAYS_BREAK);
|
|
}
|
|
#ifdef TEST
|
|
else
|
|
{
|
|
fprintf(stderr, "nxagentBlockHandler: Not synchronizing with [%d][%d].\n",
|
|
nxagentBlocking, nxagentCongestion);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Check if we have more corrupted resources and whether the
|
|
* conditions are satisfied to continue with the synchronization.
|
|
*/
|
|
|
|
synchronize = (nxagentCongestion <= 4 &&
|
|
(nxagentCorruptedWindows > 0 ||
|
|
nxagentCorruptedBackgrounds > 0 ||
|
|
nxagentCorruptedPixmaps > 0));
|
|
|
|
if (!nxagentSkipImage && synchronize)
|
|
{
|
|
#ifdef TEST
|
|
fprintf(stderr, "nxagentBlockHandler: Setting a zero timeout with [%d][%d][%d] and "
|
|
"congestion [%d].\n", nxagentCorruptedWindows, nxagentCorruptedBackgrounds,
|
|
nxagentCorruptedPixmaps, nxagentCongestion);
|
|
#endif
|
|
|
|
zero.tv_sec = 0;
|
|
zero.tv_usec = 0;
|
|
|
|
*timeout = &zero;
|
|
}
|
|
#ifdef TEST
|
|
else
|
|
{
|
|
if (nxagentCorruptedWindows == 0 &&
|
|
nxagentCorruptedBackgrounds == 0 &&
|
|
nxagentCorruptedPixmaps == 0)
|
|
{
|
|
fprintf(stderr, "nxagentBlockHandler: Nothing more to synchronize at %s.\n",
|
|
GetTimeInMillisAsString());
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "nxagentBlockHandler: Delaying synchronization with [%d][%d][%d] and "
|
|
"congestion [%d].\n", nxagentCorruptedWindows, nxagentCorruptedBackgrounds,
|
|
nxagentCorruptedPixmaps, nxagentCongestion);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* If the remote X server is blocking, reduce the amount of data
|
|
* sent in a single display update by reducing the size of the
|
|
* display buffer.
|
|
*/
|
|
|
|
#ifdef DYNAMIC_DISPLAY_BUFFER
|
|
|
|
if (nxagentBlocking &&
|
|
nxagentBuffer > MINIMUM_DISPLAY_BUFFER)
|
|
{
|
|
nxagentBuffer >>= 1;
|
|
|
|
if (nxagentBuffer < MINIMUM_DISPLAY_BUFFER)
|
|
{
|
|
nxagentBuffer = MINIMUM_DISPLAY_BUFFER;
|
|
}
|
|
|
|
#ifdef TEST
|
|
fprintf(stderr, "nxagentBlockHandler: Reducing the display buffer to [%d] bytes.\n",
|
|
nxagentBuffer);
|
|
#endif
|
|
|
|
NXSetDisplayBuffer(nxagentDisplay, nxagentBuffer);
|
|
}
|
|
else if (nxagentBuffer < nxagentOption(DisplayBuffer) &&
|
|
nxagentCongestion == 0)
|
|
{
|
|
nxagentBuffer = nxagentOption(DisplayBuffer);
|
|
|
|
#ifdef TEST
|
|
fprintf(stderr, "nxagentBlockHandler: Increasing the display buffer to [%d] bytes.\n",
|
|
nxagentBuffer);
|
|
#endif
|
|
|
|
NXSetDisplayBuffer(nxagentDisplay, nxagentBuffer);
|
|
}
|
|
|
|
#endif /* #ifdef DYNAMIC_DISPLAY_BUFFER */
|
|
|
|
/*
|
|
* Dispatch to the clients the events that may have become
|
|
* available.
|
|
*/
|
|
|
|
if (nxagentPendingEvents(nxagentDisplay) > 0)
|
|
{
|
|
#ifdef TEST
|
|
fprintf(stderr, "nxagentBlockHandler: Reading the events available.\n");
|
|
#endif
|
|
|
|
nxagentDispatchEvents(NULL);
|
|
}
|
|
|
|
/*
|
|
* Check if there is any data remaining, either in the display
|
|
* buffer or in the NX transport.
|
|
*/
|
|
|
|
flushable = NXDisplayFlushable(nxagentDisplay);
|
|
|
|
if (flushable > 0)
|
|
{
|
|
#ifdef FLUSH_AFTER_MULTIPLE_READS
|
|
|
|
/*
|
|
* Flush all the outstanding data if the wakeup handler didn't
|
|
* detect any activity.
|
|
*/
|
|
|
|
if (nxagentReady == 0 || now - nxagentFlush >=
|
|
nxagentOption(DisplayCoalescence))
|
|
{
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "nxagentBlockHandler: Flushing the display with [%d] bytes to flush.\n",
|
|
flushable);
|
|
#endif
|
|
|
|
NXFlushDisplay(nxagentDisplay, NXFlushLink);
|
|
|
|
/*
|
|
* New events may have become available after the flush.
|
|
*/
|
|
|
|
if (nxagentPendingEvents(nxagentDisplay) > 0)
|
|
{
|
|
#ifdef TEST
|
|
fprintf(stderr, "nxagentBlockHandler: Reading the events available.\n");
|
|
#endif
|
|
|
|
nxagentDispatchEvents(NULL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "nxagentBlockHandler: Delaying flush with [%d][%d] and [%d] bytes.\n",
|
|
synchronize, nxagentReady, flushable);
|
|
#endif
|
|
|
|
zero.tv_sec = 0;
|
|
zero.tv_usec = 0;
|
|
|
|
*timeout = &zero;
|
|
}
|
|
|
|
#else /* #ifdef FLUSH_AFTER_MULTIPLE_READS */
|
|
|
|
/*
|
|
* We are entering the select. Tell the NX transport to write any
|
|
* produced data to the remote end.
|
|
*/
|
|
|
|
NXFlushDisplay(nxagentDisplay, NXFlushLink);
|
|
|
|
if (nxagentPendingEvents(nxagentDisplay) > 0)
|
|
{
|
|
#ifdef TEST
|
|
fprintf(stderr, "nxagentBlockHandler: Reading the events available.\n");
|
|
#endif
|
|
|
|
nxagentDispatchEvents(NULL);
|
|
}
|
|
|
|
#endif /* #ifdef FLUSH_AFTER_MULTIPLE_READS */
|
|
}
|
|
else
|
|
{
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "nxagentBlockHandler: Nothing to flush with [%d][%d].\n",
|
|
synchronize, nxagentReady);
|
|
#endif
|
|
|
|
if (NXDisplayError(nxagentDisplay) == 0 &&
|
|
nxagentQueuedEvents(nxagentDisplay) > 0)
|
|
{
|
|
#ifdef WARNING
|
|
fprintf(stderr, "nxagentBlockHandler: WARNING! Forcing a null timeout with events queued.\n");
|
|
#endif
|
|
|
|
zero.tv_sec = 0;
|
|
zero.tv_usec = 0;
|
|
|
|
*timeout = &zero;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* WaitForSomething() sets a zero timeout if there are clients with
|
|
* input, but doesn't stop the timer. The select is then interrupted
|
|
* to update the schedule time even if, what the dispatcher cares,
|
|
* is only the number of ticks at the time the client is scheduled
|
|
* in.
|
|
*/
|
|
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "nxagentBlockHandler: Stopping the smart schedule timer.\n");
|
|
#endif
|
|
|
|
nxagentStopTimer();
|
|
|
|
nxagentPrintGeometry();
|
|
|
|
#ifdef BLOCKS
|
|
fprintf(stderr, "[End block]\n");
|
|
#endif
|
|
}
|
|
|
|
void nxagentWakeupHandler(void * data, int count, void * mask)
|
|
{
|
|
#ifdef BLOCKS
|
|
fprintf(stderr, "[Begin wakeup]\n");
|
|
#endif
|
|
|
|
if (nxagentException.sigHup || nxagentException.ioError)
|
|
{
|
|
#ifdef TEST
|
|
fprintf(stderr,"nxagentWakeupHandler: Got SIGHUP or I/O error.\n");
|
|
#endif
|
|
|
|
#ifdef TEST
|
|
fprintf(stderr, "nxagentWakeupHandler: Calling nxagentHandleConnectionStates "
|
|
"with ioError [%d] sigHup [%d].\n", nxagentException.ioError, nxagentException.sigHup);
|
|
#endif
|
|
|
|
nxagentHandleConnectionStates();
|
|
}
|
|
|
|
if (!SmartScheduleSignalEnable)
|
|
{
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "nxagentWakeupHandler: Resetting the dispatch state after wakeup.\n");
|
|
#endif
|
|
|
|
nxagentDispatch.start = GetTimeInMillis();
|
|
|
|
nxagentDispatch.in = nxagentBytesIn;
|
|
nxagentDispatch.out = nxagentBytesOut;
|
|
}
|
|
|
|
/*
|
|
* Can become true during the dispatch loop.
|
|
*/
|
|
|
|
nxagentBlocking = False;
|
|
|
|
/*
|
|
* Check if we got new events.
|
|
*/
|
|
|
|
if (count > 0 && FD_ISSET(nxagentXConnectionNumber, (fd_set *) mask))
|
|
{
|
|
#ifdef TEST
|
|
fprintf(stderr, "nxagentWakeupHandler: Reading the X events with count [%d].\n",
|
|
count);
|
|
#endif
|
|
|
|
nxagentDispatchEvents(NULL);
|
|
|
|
#ifdef TEST
|
|
fprintf(stderr, "nxagentWakeupHandler: Removing the X descriptor from the count.\n");
|
|
#endif
|
|
|
|
FD_CLR(nxagentXConnectionNumber, (fd_set *) mask);
|
|
|
|
count--;
|
|
}
|
|
else if (nxagentQueuedEvents(nxagentDisplay) == 1)
|
|
{
|
|
/*
|
|
* We may have left some events in the queue.
|
|
*/
|
|
|
|
#ifdef TEST
|
|
fprintf(stderr, "nxagentWakeupHandler: Reading the queued X events with count [%d].\n",
|
|
count);
|
|
#endif
|
|
|
|
nxagentDispatchEvents(NULL);
|
|
}
|
|
|
|
/*
|
|
* Save the number of descriptors ready.
|
|
*/
|
|
|
|
if (count <= 0)
|
|
{
|
|
count = (XFD_ANYSET(&ClientsWithInput) ? 1 : 0);
|
|
}
|
|
|
|
nxagentReady = count;
|
|
|
|
#ifdef TEST
|
|
if (nxagentReady == 0)
|
|
{
|
|
fprintf(stderr, "nxagentWakeupHandler: No X clients found to be processed.\n");
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* If the XDM connection can't be established we'll need to create a
|
|
* dialog to notify the user and give him/her a chance to terminate
|
|
* the session.
|
|
*/
|
|
|
|
if (nxagentOption(Xdmcp) && !nxagentXdmcpUp)
|
|
{
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "nxagentWakeupHandler: XdmcpState [%d].\n", XdmcpState);
|
|
#endif
|
|
|
|
if (XdmcpState == XDM_RUN_SESSION)
|
|
{
|
|
nxagentXdmcpUp = True;
|
|
}
|
|
|
|
if (!nxagentXdmcpUp)
|
|
{
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "nxagentWakeupHandler: XdmcpTime [%lu].\n",
|
|
GetTimeInMillis() - XdmcpStartTime);
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "nxagentWakeupHandler: XdmcpTimeOutRtx [%d].\n",
|
|
XdmcpTimeOutRtx);
|
|
#endif
|
|
|
|
if (!nxagentXdmcpAlertUp &&
|
|
GetTimeInMillis() - XdmcpStartTime >= XDM_TIMEOUT)
|
|
{
|
|
#ifdef WARNING
|
|
fprintf(stderr, "nxagentWakeupHandler: WARNING! The XDM session seems to be "
|
|
"unable to start after [%ld] ms.\n",(long int)(GetTimeInMillis() - XdmcpStartTime));
|
|
#endif
|
|
|
|
NXTransAlert(FAILED_XDMCP_CONNECTION_ALERT, NX_ALERT_REMOTE);
|
|
|
|
nxagentXdmcpAlertUp = True;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef BLOCKS
|
|
fprintf(stderr, "[End wakeup]\n");
|
|
#endif
|
|
}
|
|
|
|
void nxagentShadowBlockHandler(void * data, struct timeval **timeout, void * mask)
|
|
{
|
|
static struct timeval zero;
|
|
|
|
int changed;
|
|
int suspended = 0;
|
|
int width_, height_;
|
|
|
|
#ifdef BLOCKS
|
|
fprintf(stderr, "[Begin block]\n");
|
|
#endif
|
|
|
|
if (nxagentNeedConnectionChange())
|
|
{
|
|
nxagentHandleConnectionChanges();
|
|
}
|
|
|
|
if (nxagentSessionState == SESSION_DOWN && nxagentOption(SleepTimeMillis) > 0)
|
|
{
|
|
#ifdef TEST
|
|
fprintf(stderr, "nxagentShadowBlockHandler: sleeping for %d milliseconds for slowdown.\n",
|
|
nxagentOption(SleepTimeMillis));
|
|
#endif
|
|
usleep(nxagentOption(SleepTimeMillis) * 1000);
|
|
}
|
|
#ifdef TEST
|
|
else if (0 == nxagentOption(SleepTimeMillis)) {
|
|
fprintf(stderr, "nxagentShadowBlockHandler: not sleeping for slowdown.\n");
|
|
}
|
|
#endif
|
|
|
|
if (nxagentReadEvents(nxagentDisplay) > 0 ||
|
|
nxagentReadEvents(nxagentShadowDisplay) > 0)
|
|
{
|
|
#ifdef TEST
|
|
fprintf(stderr, "nxagentShadowBlockHandler: Reading X events queued.\n");
|
|
#endif
|
|
|
|
nxagentDispatchEvents(NULL);
|
|
}
|
|
|
|
if (nxagentShadowResize)
|
|
{
|
|
nxagentShadowResize = False;
|
|
|
|
nxagentShadowAdaptToRatio();
|
|
}
|
|
|
|
changed = 0;
|
|
|
|
NXShadowGetScreenSize(&width_, &height_);
|
|
|
|
if (width_ != nxagentShadowWidth || height_ != nxagentShadowHeight)
|
|
{
|
|
/*
|
|
* The master session has been resized.
|
|
*/
|
|
|
|
NXShadowSetScreenSize(&nxagentShadowWidth, &nxagentShadowHeight);
|
|
|
|
nxagentShadowAdaptToRatio();
|
|
}
|
|
|
|
nxagentShadowPoll(nxagentShadowPixmapPtr, nxagentShadowGCPtr, nxagentShadowDepth, nxagentShadowWidth,
|
|
nxagentShadowHeight, nxagentShadowBuffer, &changed, &suspended);
|
|
|
|
nxagentShadowSendUpdates(&suspended);
|
|
|
|
if (!nxagentBlocking)
|
|
{
|
|
nxagentSynchronizeDrawable((DrawablePtr) nxagentShadowPixmapPtr, DONT_WAIT,
|
|
ALWAYS_BREAK, nxagentShadowWindowPtr);
|
|
}
|
|
|
|
/*
|
|
* We are entering the select. Tell the NX transport to write any
|
|
* produced data to the remote end.
|
|
*/
|
|
/*
|
|
FIXME: Must queue multiple writes and handle the events by resembling
|
|
the ordinary block handler.
|
|
*/
|
|
|
|
NXFlushDisplay(nxagentDisplay, NXFlushLink);
|
|
|
|
if (*timeout == NULL)
|
|
{
|
|
*timeout = &zero;
|
|
}
|
|
|
|
if (changed == 0)
|
|
{
|
|
(*timeout) -> tv_sec = 0;
|
|
(*timeout) -> tv_usec = 50 * 1000;
|
|
}
|
|
else
|
|
{
|
|
(*timeout) -> tv_sec = 0;
|
|
(*timeout) -> tv_usec = 0;
|
|
}
|
|
|
|
nxagentPrintGeometry();
|
|
|
|
#ifdef BLOCKS
|
|
fprintf(stderr, "[End block]\n");
|
|
#endif
|
|
}
|
|
|
|
void nxagentShadowWakeupHandler(void * data, int count, void * mask)
|
|
{
|
|
#ifdef BLOCKS
|
|
fprintf(stderr, "[Begin wakeup]\n");
|
|
#endif
|
|
|
|
if (nxagentException.sigHup || nxagentException.ioError)
|
|
{
|
|
#ifdef TEST
|
|
fprintf(stderr,"nxagentShadowWakeupHandler: Got SIGHUP or I/O error.\n");
|
|
#endif
|
|
|
|
nxagentHandleConnectionStates();
|
|
}
|
|
|
|
if (!SmartScheduleSignalEnable)
|
|
{
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "nxagentShadowWakeupHandler: Resetting the dispatch state after wakeup.\n");
|
|
#endif
|
|
|
|
nxagentDispatch.start = GetTimeInMillis();
|
|
|
|
nxagentDispatch.in = nxagentBytesIn;
|
|
nxagentDispatch.out = nxagentBytesOut;
|
|
}
|
|
|
|
/*
|
|
* Can become true during the dispatch loop.
|
|
*/
|
|
|
|
nxagentBlocking = False;
|
|
|
|
/*
|
|
* Check if we got new events.
|
|
*/
|
|
|
|
if (count > 0 && FD_ISSET(nxagentXConnectionNumber, (fd_set *) mask))
|
|
{
|
|
#ifdef TEST
|
|
fprintf(stderr, "nxagentShadowWakeupHandler: Reading the X events with count [%d].\n",
|
|
count);
|
|
#endif
|
|
|
|
nxagentDispatchEvents(NULL);
|
|
|
|
#ifdef TEST
|
|
fprintf(stderr, "nxagentShadowWakeupHandler: Removing the X descriptor from the count.\n");
|
|
#endif
|
|
|
|
FD_CLR(nxagentXConnectionNumber, (fd_set *) mask);
|
|
|
|
count--;
|
|
}
|
|
else if (nxagentQueuedEvents(nxagentDisplay) == 1)
|
|
{
|
|
/*
|
|
* We may have left some events in the queue.
|
|
*/
|
|
|
|
#ifdef TEST
|
|
fprintf(stderr, "nxagentShadowWakeupHandler: Reading the queued X events with count [%d].\n",
|
|
count);
|
|
#endif
|
|
|
|
nxagentDispatchEvents(NULL);
|
|
}
|
|
|
|
/*
|
|
* Save the number of descriptors ready.
|
|
*/
|
|
|
|
if (count <= 0)
|
|
{
|
|
count = (XFD_ANYSET(&ClientsWithInput) ? 1 : 0);
|
|
}
|
|
|
|
nxagentReady = count;
|
|
|
|
#ifdef TEST
|
|
|
|
if (nxagentReady == 0)
|
|
{
|
|
fprintf(stderr, "nxagentShadowWakeupHandler: No X clients found to be processed.\n");
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef BLOCKS
|
|
fprintf(stderr, "[End wakeup]\n");
|
|
#endif
|
|
}
|
|
|
|
void nxagentHandleCollectInputFocusEvent(int resource)
|
|
{
|
|
/*
|
|
* While we don't even need window or revert_to later on, a discrepancy in
|
|
* data type sizes between the X server (Window being a 32bit ID) and
|
|
* the Xlib (Window being a 64bit ID) will lead to stack corruption here.
|
|
* Calling functions from CompExt from nxagent sounds like a very bad idea
|
|
* to begin with, but let's assume that's necessary for now and work around
|
|
* the corruption issue.
|
|
*
|
|
* Even though the CompExt header shows that the function expects a Window-sized
|
|
* parameter, it's not the Window type as defined and used within the X.Org
|
|
* Server, but an Xlib type. Hence, we'll be using the "XlibWindow" type here
|
|
* and to avoid compiler warnings, "rewrite" the CompExt.h header file via
|
|
* overriding the original "Window" type with the XlibWindow type, including
|
|
* the header file and undefining the macro again, essentially unshadowing
|
|
* the original type.
|
|
*/
|
|
XlibWindow window;
|
|
|
|
int revert_to;
|
|
|
|
if (NXGetCollectedInputFocus(nxagentDisplay, resource, &window, &revert_to) == 0)
|
|
{
|
|
#ifdef PANIC
|
|
fprintf(stderr, "nxagentHandleCollectInputFocusEvent: PANIC! Failed to get the input focus "
|
|
"reply for resource [%d].\n", resource);
|
|
#endif
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "nxagentHandleCollectInputFocusEvent: Received a sync reply with [%d] pending.\n",
|
|
nxagentTokens.pending);
|
|
#endif
|
|
|
|
nxagentTokens.pending--;
|
|
|
|
nxagentCongestion = (nxagentTokens.pending >= TOKENS_PENDING_LIMIT / 2);
|
|
|
|
#ifdef TEST
|
|
fprintf(stderr, "nxagentHandleCollectInputFocusEvent: Current congestion level is [%d].\n",
|
|
nxagentCongestion);
|
|
#endif
|
|
}
|
|
|
|
Bool nxagentCollectInputFocusPredicate(Display *disp, XEvent *X, XPointer ptr)
|
|
{
|
|
return (X -> xclient.window == 0 &&
|
|
X -> xclient.message_type == 0 &&
|
|
X -> xclient.format == 32 &&
|
|
X -> xclient.data.l[0] == NXCollectInputFocusNotify);
|
|
}
|
|
|
|
void nxagentDispatchHandler(ClientPtr client, int in, int out)
|
|
{
|
|
/*
|
|
* This function is called by the dispatcher (with 0 bytes out)
|
|
* after a new request has been processed. It is also called by the
|
|
* write handler (with 0 bytes in) after more data has been written
|
|
* to the display. It may be optionally called in the block and
|
|
* wakeup handlers. In this case both in and out must be 0.
|
|
*/
|
|
|
|
if (out > 0)
|
|
{
|
|
/*
|
|
* Called by the display write callback.
|
|
*/
|
|
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "nxagentDispatchHandler: Called with [%d] bytes written.\n",
|
|
out);
|
|
#endif
|
|
|
|
nxagentBytesOut += out;
|
|
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "nxagentDispatchHandler: Total bytes are [%.0f] in [%.0f] out.\n",
|
|
nxagentBytesIn, nxagentBytesOut);
|
|
#endif
|
|
|
|
/*
|
|
* Don't take care of the synchronization if the NX transport is
|
|
* running. The NX transport has its own token-based control flow.
|
|
*
|
|
* We can't produce more output here because we are in the middle
|
|
* of the flush. We will take care of the sync requests when
|
|
* called by the dispatcher.
|
|
*/
|
|
|
|
if (nxagentOption(LinkType) == LINK_TYPE_NONE)
|
|
{
|
|
nxagentTokens.soft += out;
|
|
|
|
if (out > BYTES_BEFORE_HARD_TOKEN)
|
|
{
|
|
nxagentTokens.hard += out;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "nxagentDispatchHandler: Sync bytes accumulated are [%d] and [%d].\n",
|
|
nxagentTokens.soft, nxagentTokens.hard);
|
|
#endif
|
|
}
|
|
|
|
if (!SmartScheduleSignalEnable)
|
|
{
|
|
/*
|
|
* Pay attention to the next client if this client produced
|
|
* enough output.
|
|
*/
|
|
|
|
if (nxagentBytesOut - nxagentDispatch.out > BYTES_BEFORE_YIELD)
|
|
{
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "nxagentDispatchHandler: Yielding with [%ld][%.0f][%.0f] for client [%d].\n",
|
|
GetTimeInMillis() - nxagentDispatch.start, nxagentBytesIn - nxagentDispatch.in,
|
|
nxagentBytesOut - nxagentDispatch.out, nxagentDispatch.client);
|
|
#endif
|
|
|
|
nxagentDispatch.start = GetTimeInMillis();
|
|
|
|
nxagentDispatch.in = nxagentBytesIn;
|
|
nxagentDispatch.out = nxagentBytesOut;
|
|
|
|
isItTimeToYield = TRUE;
|
|
}
|
|
#ifdef DEBUG
|
|
else
|
|
{
|
|
fprintf(stderr, "nxagentDispatchHandler: Dispatching with [%ld][%.0f][%.0f] for client [%d].\n",
|
|
GetTimeInMillis() - nxagentDispatch.start, nxagentBytesIn - nxagentDispatch.in,
|
|
nxagentBytesOut - nxagentDispatch.out, nxagentDispatch.client);
|
|
}
|
|
#endif
|
|
}
|
|
return;
|
|
}
|
|
else if (in > 0)
|
|
{
|
|
/*
|
|
* Called by the dispatcher.
|
|
*/
|
|
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "nxagentDispatchHandler: Called with [%d] bytes processed for client [%d].\n",
|
|
in, client -> index);
|
|
#endif
|
|
|
|
#ifdef COUNT_CLIENT_BYTES
|
|
/*
|
|
* This is presently unused.
|
|
*
|
|
* nxagentClientAddBytes(client, in);
|
|
*
|
|
* #ifdef DEBUG
|
|
* fprintf(stderr, "nxagentDispatchHandler: Bytes processed for client [%d] are [%ld].\n",
|
|
* client -> index, nxagentClientBytes(client));
|
|
* #endif
|
|
*
|
|
*/
|
|
#endif
|
|
|
|
nxagentBytesIn += in;
|
|
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "nxagentDispatchHandler: Total bytes are [%.0f] in [%.0f] out.\n",
|
|
nxagentBytesIn, nxagentBytesOut);
|
|
#endif
|
|
|
|
/*
|
|
* When using the dumb scheduler, before reading from another
|
|
* client, the dispatcher tries to drain all the input from the
|
|
* client being processed. This means that, if isItTimeToYield is
|
|
* never set and the client never produces any output, we'll stick
|
|
* into the inner dispatch loop forever.
|
|
*/
|
|
|
|
/*
|
|
* The behaviour described in the comment above also happens with the
|
|
* smart scheduler if all of the following conditions are met:
|
|
* - the agent is suspended
|
|
* - SleepTimeMillis is set (the default is sufficient to trigger this)
|
|
* - a client is doing a lot of image operations
|
|
* - nxagentShadowCounter is 0
|
|
* In that case the agent will slow down the image operations by calling
|
|
* an intermediate sleep resulting in the client's request queue never
|
|
* being empty. Which in turn leads to the drain loop described above.
|
|
* isItTimeToYield will then never be set. The (dramatic) result of this
|
|
* is that nxagent will not process any signals and therefore cannot be
|
|
* resumed anymore!
|
|
* For this reason we do not limit below code to the dumb scheduler but also
|
|
* run it if above conditions are met - ensuring regular yields.
|
|
* See issue #903
|
|
*/
|
|
|
|
if (!SmartScheduleSignalEnable ||
|
|
(nxagentShadowCounter == 0 &&
|
|
NXDisplayError(nxagentDisplay) == 1 &&
|
|
nxagentOption(SleepTimeMillis) > 0))
|
|
{
|
|
if (client -> index != nxagentDispatch.client)
|
|
{
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "nxagentDispatchHandler: Resetting the dispatch state with [%d][%d].\n",
|
|
nxagentDispatch.client, client -> index);
|
|
#endif
|
|
|
|
nxagentDispatch.client = client -> index;
|
|
nxagentDispatch.start = GetTimeInMillis();
|
|
|
|
nxagentDispatch.in = nxagentBytesIn;
|
|
nxagentDispatch.out = nxagentBytesOut;
|
|
}
|
|
else
|
|
{
|
|
static unsigned long int now;
|
|
|
|
now = GetTimeInMillis();
|
|
|
|
if (now - nxagentDispatch.start > TIME_BEFORE_YIELD ||
|
|
nxagentBytesIn - nxagentDispatch.in > BYTES_BEFORE_YIELD)
|
|
{
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "nxagentDispatchHandler: Yielding with [%ld][%.0f][%.0f] for client [%d].\n",
|
|
now - nxagentDispatch.start, nxagentBytesIn - nxagentDispatch.in, nxagentBytesOut -
|
|
nxagentDispatch.out, nxagentDispatch.client);
|
|
#endif
|
|
|
|
nxagentDispatch.start = now;
|
|
|
|
nxagentDispatch.in = nxagentBytesIn;
|
|
nxagentDispatch.out = nxagentBytesOut;
|
|
|
|
isItTimeToYield = TRUE;
|
|
}
|
|
#ifdef DEBUG
|
|
else
|
|
{
|
|
fprintf(stderr, "nxagentDispatchHandler: Dispatching with [%ld][%.0f][%.0f] for client [%d].\n",
|
|
now - nxagentDispatch.start, nxagentBytesIn - nxagentDispatch.in, nxagentBytesOut -
|
|
nxagentDispatch.out, nxagentDispatch.client);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Let's see if it's time to sync.
|
|
*/
|
|
|
|
if (nxagentOption(LinkType) == LINK_TYPE_NONE)
|
|
{
|
|
if (nxagentTokens.hard > BYTES_BEFORE_HARD_TOKEN)
|
|
{
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "nxagentDispatchHandler: Requesting a hard sync reply with [%d] bytes.\n",
|
|
nxagentTokens.hard);
|
|
#endif
|
|
|
|
XSync(nxagentDisplay, 0);
|
|
|
|
if (nxagentPendingEvents(nxagentDisplay) > 0)
|
|
{
|
|
nxagentDispatchEvents(NULL);
|
|
}
|
|
|
|
nxagentTokens.soft = 0;
|
|
nxagentTokens.hard = 0;
|
|
}
|
|
else if (nxagentTokens.soft > BYTES_BEFORE_SOFT_TOKEN)
|
|
{
|
|
/*
|
|
* Alternatively, the amounts of bytes accounted for each sync
|
|
* request may be decreased according to the number of pending
|
|
* replies already awaited.
|
|
*
|
|
* else if (nxagentTokens.soft > (BYTES_BEFORE_SOFT_TOKEN / (nxagentTokens.pending + 1)))
|
|
*/
|
|
|
|
int resource;
|
|
|
|
/*
|
|
* Wait eventually for the number of synchronization requests to
|
|
* return below the limit.
|
|
*/
|
|
|
|
#ifdef TEST
|
|
if (nxagentTokens.pending == TOKENS_PENDING_LIMIT)
|
|
{
|
|
fprintf(stderr, "nxagentDispatchHandler: WARNING! Waiting for the synchronization reply.\n");
|
|
}
|
|
#endif
|
|
|
|
while (nxagentTokens.pending == TOKENS_PENDING_LIMIT)
|
|
{
|
|
if (nxagentWaitEvents(nxagentDisplay, 0) == -1)
|
|
{
|
|
nxagentTokens.pending = 0;
|
|
|
|
nxagentTokens.soft = 0;
|
|
|
|
return;
|
|
}
|
|
|
|
nxagentDispatchEvents(NULL);
|
|
|
|
nxagentBlocking = True;
|
|
}
|
|
|
|
/*
|
|
* Send a new synchronization request.
|
|
*/
|
|
|
|
resource = nxagentWaitForResource(NXGetCollectInputFocusResource,
|
|
nxagentCollectInputFocusPredicate);
|
|
|
|
if (resource == -1)
|
|
{
|
|
#ifdef PANIC
|
|
fprintf(stderr, "nxagentDispatchHandler: PANIC! Cannot allocate any valid resource.\n");
|
|
#endif
|
|
|
|
nxagentTokens.soft = 0;
|
|
|
|
return;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "nxagentDispatchHandler: Requesting a sync reply with [%d] bytes "
|
|
"and [%d] pending.\n", nxagentTokens.soft, nxagentTokens.pending);
|
|
#endif
|
|
|
|
NXCollectInputFocus(nxagentDisplay, resource);
|
|
|
|
NXFlushDisplay(nxagentDisplay, NXFlushBuffer);
|
|
|
|
if (nxagentPendingEvents(nxagentDisplay) > 0)
|
|
{
|
|
nxagentDispatchEvents(NULL);
|
|
}
|
|
|
|
nxagentTokens.pending++;
|
|
|
|
nxagentCongestion = (nxagentTokens.pending >= TOKENS_PENDING_LIMIT / 2);
|
|
|
|
#ifdef TEST
|
|
fprintf(stderr, "nxagentDispatchHandler: Current congestion level is [%d].\n",
|
|
nxagentCongestion);
|
|
#endif
|
|
|
|
nxagentTokens.soft = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check if there are events to read.
|
|
*/
|
|
|
|
if (nxagentPendingEvents(nxagentDisplay) > 0)
|
|
{
|
|
nxagentDispatchEvents(NULL);
|
|
}
|
|
}
|