/**************************************************************************/
/*                                                                        */
/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com)          */
/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de>  */
/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de>                */
/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de>                 */
/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com)           */
/*                                                                        */
/* NXAGENT, NX protocol compression and NX extensions to this software    */
/* are copyright of the aforementioned persons and companies.             */
/*                                                                        */
/* Redistribution and use of the present software is allowed according    */
/* to terms specified in the file LICENSE which comes in the source       */
/* distribution.                                                          */
/*                                                                        */
/* All rights reserved.                                                   */
/*                                                                        */
/* NOTE: This software has received contributions from various other      */
/* contributors, only the core maintainers and supporters are listed as   */
/* copyright holders. Please contact us, if you feel you should be listed */
/* as copyright holder, as well.                                          */
/*                                                                        */
/**************************************************************************/

#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);
  }
}
