/**************************************************************************/
/*                                                                        */
/* 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 "X.h"
#include "signal.h"
#include "unistd.h"
#include "Xproto.h"
#include "screenint.h"
#include "input.h"
#include "dix.h"
#include "misc.h"
#include "scrnintstr.h"
#include "windowstr.h"
#include "servermd.h"
#include "mi.h"
#include "selection.h"
#include "keysym.h"
#include "fb.h"
#include "mibstorest.h"
#include "osdep.h"

#include "Agent.h"
#include "Args.h"
#include "Atoms.h"
#include "Colormap.h"
#include "Display.h"
#include "Screen.h"
#include "Windows.h"
#include "Pixmaps.h"
#include "Keyboard.h"
#include "Keystroke.h"
#include "Events.h"
#include "Pointer.h"
#include "Rootless.h"
#include "Splash.h"
#include "Trap.h"
#include "Dialog.h"
#include "Client.h"
#include "Clipboard.h"
#include "Split.h"
#include "Drawable.h"
#include "Handlers.h"
#include "Utils.h"
#include "Error.h"

#include <nx/NX.h>
#include <nx/NXvars.h>
#include <nx/NXproto.h>

#include "xfixesproto.h"
#define Window     XlibWindow
#define Atom   XlibAtom
#define Time XlibXID
#include "X11/include/Xfixes_nxagent.h"
#undef Window
#undef Atom
#undef Time

#ifdef NXAGENT_FIXKEYS
#include "inputstr.h"
#include "input.h"
#endif

#define Time XlibXID
#include "XKBlib.h"
#undef Time

#define GC     XlibGC
#define Font   XlibFont
#define KeySym XlibKeySym
#define XID    XlibXID
#include "Xlibint.h"
#undef  GC
#undef  Font
#undef  KeySym
#undef  XID

#include <nx-X11/cursorfont.h>

#include <nx/Shadow.h>
#include "X11/include/Xrandr_nxagent.h"

#include "compext/Compext.h"

/*
 * Set here the required log level. Please note
 * that if you want to enable DEBUG here, then
 * you need to enable DEBUG even in Rootless.c
 */

#define PANIC
#define WARNING
#undef  TEST
#undef  DEBUG

/* debug individual subsystems */
#undef DEBUG_AUTOGRAB

/* aktivate subsystems if generic DEBUG is activated */
#ifdef DEBUG
#ifndef DEBUG_AUTOGRAB
#define DEBUG_AUTOGRAB
#endif
#endif
/*
 * Log begin and end of the important handlers.
 */

#undef  BLOCKS

extern Bool nxagentOnce;

extern WindowPtr nxagentRootTileWindow;

extern int nxagentLastClipboardClient;

#ifdef NX_DEBUG_INPUT
int nxagentDebugInput = 0;
#endif

#ifdef DEBUG
extern Bool nxagentRootlessTreesMatch(void);
#endif

extern Selection *CurrentSelections;
extern int NumCurrentSelections;

typedef union _XFixesSelectionEvent {
        int                          type;
        XFixesSelectionNotifyEvent   xfixesselection;
        XEvent                       core;
} XFixesSelectionEvent;

Bool   xkbdRunning = False;
pid_t  pidkbd;

WindowPtr nxagentLastEnteredWindow = NULL;

PropertyRequestRec nxagentPropertyRequests[NXNumberOfResources];

void nxagentHandleCollectPropertyEvent(XEvent*);

/*
 * Finalize the asynchronous handling
 * of the X_GrabPointer requests.
 */

void nxagentHandleCollectGrabPointerEvent(int resource);

Bool nxagentCollectGrabPointerPredicate(Display *display, XEvent *X, XPointer ptr);

/*
 * Used in Handlers.c to synchronize
 * the agent with the remote X server.
 */

void nxagentHandleCollectInputFocusEvent(int resource);

/*
 * Viewport navigation.
 */

static int viewportInc = 1;
static enum HandleEventResult viewportLastKeyPressResult;
static int viewportLastX;
static int viewportLastY;
static Cursor viewportCursor;

#define MAX_INC 200
#define INC_STEP 5
#define nextinc(x)  ((x) < MAX_INC ? (x) += INC_STEP : (x))

/*
 * Keyboard and pointer are handled as they were real devices by
 * Xnest and we inherit this behaviour. The following mask will
 * contain the event mask selected for the root window of the
 * agent. All the keyboard and pointer events will be translated
 * by the agent and sent to the internal clients according to
 * events selected by the inferior windows.
 */

static Mask defaultEventMask;

static int lastEventSerial = 0;

/*
 * Used to mask the appropriate bits in
 * the state reported by XkbStateNotify
 * and XkbGetIndicatorState.
 */

#define CAPSFLAG_IN_REPLY     1
#define CAPSFLAG_IN_EVENT     2
#define NUMFLAG_IN_EVENT      16
#define NUMFLAG_IN_REPLY      2

CARD32 nxagentLastEventTime     = 0;
CARD32 nxagentLastKeyPressTime  = 0;
Time   nxagentLastServerTime    = 0;

/*
 * Used for storing windows that need to
 * receive expose events from the agent.
 */

#define nxagentExposeQueueHead nxagentExposeQueue.exposures[nxagentExposeQueue.start]

ExposeQueue nxagentExposeQueue;

RegionPtr nxagentRemoteExposeRegion = NULL;

static void nxagentForwardRemoteExpose(void);

static int nxagentClipAndSendExpose(WindowPtr pWin, void * ptr);

/*
 * This is from NXproperty.c.
 */

int GetWindowProperty(WindowPtr pWin, Atom property, long longOffset,
                          long longLength, Bool delete, Atom type,
                              Atom *actualType, int *format, unsigned
                                  long *nItems, unsigned long *bytesAfter,
                                      unsigned char **propData);

/*
 * Associate a resource to a drawable and
 * store the region affected by the split
 * operation.
 */

SplitResourceRec nxagentSplitResources[NXNumberOfResources];

/*
 * Associate a resource to an unpack
 * operation.
 */

UnpackResourceRec nxagentUnpackResources[NXNumberOfResources];

/*
 * We have to check these before launching
 * the terminate dialog in rootless mode.
 */

Bool nxagentLastWindowDestroyed = False;
Time nxagentLastWindowDestroyedTime = 0;

/*
 * Set this flag when an user input event
 * is received.
 */

int nxagentInputEvent = 0;

int nxagentKeyDown = 0;

void nxagentSwitchResizeMode(ScreenPtr pScreen);

int nxagentCheckWindowConfiguration(XConfigureEvent* X);

#define nxagentMonitoredDuplicate(keysym) \
    ((keysym) == XK_Left || (keysym) == XK_Up || \
        (keysym) == XK_Right || (keysym) == XK_Down || \
            (keysym) == XK_Page_Up || (keysym) == XK_Page_Down || \
                (keysym) == XK_Delete || (keysym) == XK_BackSpace)

void nxagentRemoveDuplicatedKeys(XEvent *X);

void ProcessInputEvents(void)
{
  #ifdef NX_DEBUG_INPUT
  if (nxagentDebugInput == 1)
  {
    fprintf(stderr, "ProcessInputEvents: Processing input.\n");
  }
  #endif

  mieqProcessInputEvents();
}

#ifdef DEBUG
char * nxagentGetNotifyMode(int mode)
{
  switch (mode)
  {
    case NotifyNormal:
    {
      return "NotifyNormal";
      break;
    }
    case NotifyGrab:
    {
      return "NotifyGrab";
      break;
    }
    case NotifyUngrab:
    {
      return "NotifyUngrab";
      break;
    }
    case NotifyWhileGrabbed:
    {
      return "NotifyWhileGrabbed";
      break;
    }
  }
  return "Unknown";
}
#endif

#ifdef DEBUG_TREE

/*
 * Print ID and name of window.
 */

void nxagentRemoteWindowID(Window window, Bool newline)
{
#ifdef NO_I18N
    char *winName;
#else
    XTextProperty tp;
#endif

  fprintf(stderr, "0x%x", window);

  if (!window)
  {
    fprintf(stderr, " (none) ");
  }
  else
  {
    if (window == DefaultRootWindow(nxagentDisplay))
    {
      fprintf(stderr, " (the root window) ");
    }

#ifdef NO_I18N

    if (!XFetchName(nxagentDisplay, window, &winName))
    {
      fprintf(stderr, " (has no name) ");
    }
    else if (winName)
    {
      fprintf(stderr, " \"%s\" ", winName);
      SAFE_XFree(winName);
    }

#else

    if (XGetWMName(nxagentDisplay, window, &tp) == 0)
    {
      fprintf(stderr, " (has no name) ");
    }
    else if (tp.nitems > 0)
    {
      fprintf(stderr, " \"");

      int count = 0;
      char **list = NULL;
      int ret = XmbTextPropertyToTextList(nxagentDisplay, &tp, &list, &count);

      if ((ret == Success || ret > 0) && list != NULL)
      {
        for (int i = 0; i < count; i++)
        {
          fprintf(stderr, "%s", list[i]);
        }

        XFreeStringList(list);
      }
      else
      {
        fprintf(stderr, "%s", tp.value);
      }

      fprintf(stderr, "\" ");
    }

#endif

    else
    {
      fprintf(stderr, " (has no name) ");
    }
  }

  if (newline)
  {
    fprintf(stderr, "\n");
  }

  return;
}

/*
 * Print info about remote window.
 */

void nxagentRemoteWindowInfo(Window win, int indent, Bool newLine)
{
  XWindowAttributes attributes;

  if (XGetWindowAttributes(nxagentDisplay, win, &attributes) == 0)
  {
    return;
  }

  fprintf(stderr, "%*sx=%d y=%d width=%d height=%d class=%s map_state=%s "
          "override_redirect=%s\n", indent, "", attributes.x, attributes.y,
                 attributes.width, attributes.height,
                     (attributes.class == 0) ? "CopyFromParent" :
                     ((attributes.class == 1) ? "InputOutput" : "InputOnly"),
                     (attributes.map_state == 0) ?
                         "IsUnmapped" : (attributes.map_state == 1 ?
                             "IsUnviewable" : "IsViewable"),
                                 (attributes.override_redirect == 0) ?
                                     "No" : "Yes" );

  if (newLine)
  {
    fprintf(stderr, "\n");
  }
}


/*
 * Walk remote windows tree.
 *
 * FIXME:
 * ========== nxagentRemoteWindowsTree ============
 *
 *   Root Window ID: 0x169 (the root window)  (has no name)
 *   Parent window ID: 0x2a00063 "NX Agent"
 *      0 children.
 *
 * ========== nxagentInternalWindowsTree ==========
 * Window ID=[0x9d] Remote ID=[0x2a0007e] Name: ( has no name )
 * x=0 y=0 width=1440 height=810 class=InputOutput map_state=IsViewable override_redirect=No
 *
 * -> Internal root window's remote id is not listed in RemoteWindowsTree.
 */

void nxagentRemoteWindowsTree(Window window, int level)
{
  unsigned long rootWin, parentWin;
  unsigned int numChildren;
  unsigned long *childList = NULL;

  if (!XQueryTree(nxagentDisplay, window, &rootWin, &parentWin, &childList,
                      &numChildren))
  {
    fprintf(stderr, "nxagentRemoteWindowsTree - XQueryTree failed.\n");
    return;
  }

  if (level == 0)
  {
    fprintf(stderr, "\n");

    fprintf(stderr, "  Root Window ID: ");
    nxagentRemoteWindowID(rootWin, TRUE);

    fprintf(stderr, "  Parent window ID: ");
    nxagentRemoteWindowID(parentWin, TRUE);
  }

  if (level == 0 || numChildren > 0)
  {
    fprintf(stderr, "%*s", (level * 4) + 5, ""); /* 4 spaces per level */

    fprintf(stderr, "%d child%s%s\n", numChildren, (numChildren == 1) ? "" :
               "ren", (numChildren == 1) ? ":" : ".");
  }

  for (int i = (int) numChildren - 1; i >= 0; i--)
  {
    fprintf(stderr, "%*s", (level * 5) + 6, ""); /* 5 spaces per level */

    nxagentRemoteWindowID(childList[i], TRUE);

    nxagentRemoteWindowInfo(childList[i], (level * 5) + 6, TRUE);

    nxagentRemoteWindowsTree(childList[i], level + 1);
  }

  SAFE_XFree(childList);
}

/*
 * Print info about internal window.
 */

void nxagentInternalWindowInfo(WindowPtr pWin, int indent, Bool newLine)
{
  int result;
  unsigned long ulReturnItems;
  unsigned long ulReturnBytesLeft;
  Atom          atomReturnType;
  int           iReturnFormat;
  unsigned char *pszReturnData = NULL;

  fprintf(stderr, "Window ID=[0x%x] %s Remote ID=[0x%x] ", pWin -> drawable.id,
          pWin->parent ? "" : "(the root window)", nxagentWindow(pWin));

  result = GetWindowProperty(pWin, MakeAtom("WM_NAME", 7, False) , 0,
                                sizeof(CARD32), False, AnyPropertyType,
                                    &atomReturnType, &iReturnFormat,
                                        &ulReturnItems, &ulReturnBytesLeft,
                                            &pszReturnData);

  fprintf(stderr, "Name: ");

  if (result == Success && pszReturnData != NULL)
  {
    fprintf(stderr, "\"%*.*s\"\n", (int)ulReturnItems, (int)ulReturnItems, (char *) pszReturnData);
  }
  else
  {
    fprintf(stderr, "%s\n", "( has no name )");
  }

  fprintf(stderr, "%*sx=%d y=%d width=%d height=%d class=%s map_state=%s "
          "override_redirect=%s", indent, "", pWin -> drawable.x, pWin -> drawable.y,
                 pWin -> drawable.width, pWin -> drawable.height,
                     (pWin -> drawable.class == 0) ? "CopyFromParent" :
                     ((pWin -> drawable.class == 1) ? "InputOutput" :
                      "InputOnly"),
                      (pWin -> mapped == 0) ?
                             "IsUnmapped" : (pWin -> realized == 0 ?
                                 "IsUnviewable" : "IsViewable"),
                                     (pWin -> overrideRedirect == 0) ?
                                         "No" : "Yes");

  if (newLine)
  {
    fprintf(stderr, "\n");
  }
}

/*
 * Walk internal windows tree.
 */

void nxagentInternalWindowsTree(WindowPtr pWin, int indent)
{
  while (pWin)
  {
    WindowPtr pChild = pWin -> firstChild;

    fprintf(stderr, "%*s", indent, "");

    nxagentInternalWindowInfo(pWin, indent, TRUE);

    fprintf(stderr, "\n");

    nxagentInternalWindowsTree(pChild, indent + 4);

    pWin = pWin -> nextSib;
  }
}

#endif /* DEBUG_TREE */

void nxagentSwitchResizeMode(ScreenPtr pScreen)
{
  #ifdef DEBUG
  fprintf(stderr, "nxagentSwitchResizeMode called.\n");
  #endif

  int desktopResize = nxagentOption(DesktopResize);

  nxagentChangeOption(DesktopResize, !desktopResize);

  if (nxagentOption(DesktopResize) == 0)
  {
    fprintf(stderr,"Info: Disabled desktop resize mode in agent.\n");

    nxagentLaunchDialog(DIALOG_DISABLE_DESKTOP_RESIZE_MODE);

    if (nxagentOption(Fullscreen) == 0)
    {
      nxagentSetWMNormalHintsMaxsize(pScreen,
                                     nxagentOption(RootWidth),
                                     nxagentOption(RootHeight));
    }
  }
  else
  {
    fprintf(stderr,"Info: Enabled desktop resize mode in agent.\n");

    nxagentLaunchDialog(DIALOG_ENABLE_DESKTOP_RESIZE_MODE);

    nxagentChangeScreenConfig(0, nxagentOption(Width), nxagentOption(Height));

    if (nxagentOption(ClientOs) == ClientOsWinnt)
    {
      NXSetExposeParameters(nxagentDisplay, 0, 0, 0);
    }

    nxagentSetWMNormalHintsMaxsize(pScreen,
                                   WidthOfScreen(DefaultScreenOfDisplay(nxagentDisplay)),
                                   HeightOfScreen(DefaultScreenOfDisplay(nxagentDisplay)));
  }
}

void nxagentShadowSwitchResizeMode(ScreenPtr pScreen)
{
  int desktopResize = nxagentOption(DesktopResize);

  nxagentChangeOption(DesktopResize, !desktopResize);

  if (nxagentOption(DesktopResize) == 0)
  {
    nxagentShadowSetRatio(1.0, 1.0);

    nxagentShadowCreateMainWindow(screenInfo.screens[DefaultScreen(nxagentDisplay)], screenInfo.screens[0]->root,
                                      screenInfo.screens[0]->root -> drawable.width, screenInfo.screens[0]->root -> drawable.height);

    nxagentSetWMNormalHintsMaxsize(pScreen,
                                   nxagentOption(RootWidth),
                                   nxagentOption(RootHeight));

    fprintf(stderr,"Info: Disabled resize mode in shadow agent.\n");
  }
  else
  {
    nxagentShadowSetRatio(nxagentOption(Width) * 1.0 /
                              screenInfo.screens[0]->root -> drawable.width,
                                  nxagentOption(Height) * 1.0 /
                                      screenInfo.screens[0]->root -> drawable.height);

    nxagentShadowCreateMainWindow(screenInfo.screens[DefaultScreen(nxagentDisplay)],
                                      screenInfo.screens[0]->root, screenInfo.screens[0]->root -> drawable.width,
                                          screenInfo.screens[0]->root -> drawable.height);

    nxagentSetWMNormalHintsMaxsize(pScreen,
                                   WidthOfScreen(DefaultScreenOfDisplay(nxagentDisplay)),
                                   HeightOfScreen(DefaultScreenOfDisplay(nxagentDisplay)));

    fprintf(stderr,"Info: Enabled resize mode in shadow agent.\n");
  }
}

static void nxagentSwitchDeferMode(void)
{
  if (nxagentOption(DeferLevel) == 0)
  {
    nxagentChangeOption(DeferLevel, UNDEFINED);

    nxagentSetDeferLevel();
  }
  else
  {
    nxagentChangeOption(DeferLevel, 0);
  }

  if (nxagentOption(DeferLevel) != 0)
  {
    nxagentLaunchDialog(DIALOG_ENABLE_DEFER_MODE);
  }
  else
  {
    nxagentLaunchDialog(DIALOG_DISABLE_DEFER_MODE);

    nxagentForceSynchronization = 1;
  }
}

static void nxagentEnableAutoGrab(void)
{
  #ifdef DEBUG_AUTOGRAB
  fprintf(stderr, "enabling autograb\n");
  #endif

  nxagentGrabPointerAndKeyboard(NULL);
  nxagentChangeOption(AutoGrab, True);
  nxagentLaunchDialog(DIALOG_ENABLE_AUTOGRAB_MODE);
}

static void nxagentDisableAutoGrab(void)
{
  #ifdef DEBUG_AUTOGRAB
  fprintf(stderr, "disabling autograb\n");
  #endif

  nxagentUngrabPointerAndKeyboard(NULL);
  nxagentChangeOption(AutoGrab, False);
  nxagentLaunchDialog(DIALOG_DISABLE_AUTOGRAB_MODE);
}

static void nxagentToggleAutoGrab(void)
{
  /* autograb only works in windowed mode */
  if (nxagentOption(Rootless) || nxagentOption(Fullscreen))
    return;

  if (!nxagentOption(AutoGrab))
    nxagentEnableAutoGrab();
  else
    nxagentDisableAutoGrab();
}

static Bool nxagentExposurePredicate(Display *display, XEvent *event, XPointer window)
{
  /*
   *  Handle both Expose and ProcessedExpose events.
   *  The latters are those not filtered by function
   *  nxagentWindowExposures().
   */

  if (window)
  {
    return ((event -> type == Expose || event -> type == ProcessedExpose) &&
                event -> xexpose.window == *((Window *) window));
  }
  else
  {
    return (event -> type == Expose || event -> type == ProcessedExpose);
  }
}

static int nxagentAnyEventPredicate(Display *display, XEvent *event, XPointer parameter)
{
  return 1;
}

int nxagentInputEventPredicate(Display *display, XEvent *event, XPointer parameter)
{
  switch (event -> type)
  {
    case KeyPress:
    case KeyRelease:
    case ButtonPress:
    case ButtonRelease:
    {
      return 1;
    }
    default:
    {
      return 0;
    }
  }
}

void nxagentInitDefaultEventMask(void)
{
  Mask mask = NoEventMask;

  mask |= (StructureNotifyMask | VisibilityChangeMask);

  mask |= ExposureMask;

  mask |= NXAGENT_KEYBOARD_EVENT_MASK;
  mask |= NXAGENT_POINTER_EVENT_MASK;

  defaultEventMask = mask;
}

Mask nxagentGetDefaultEventMask(void)
{
  return defaultEventMask;
}

void nxagentSetDefaultEventMask(Mask mask)
{
  defaultEventMask = mask;
}

Mask nxagentGetEventMask(WindowPtr pWin)
{
  Mask mask = NoEventMask;

  if (nxagentOption(Rootless))
  {
    /*
     * mask = pWin -> eventMask &
     *           ~(NXAGENT_KEYBOARD_EVENT_MASK | NXAGENT_POINTER_EVENT_MASK);
     */

    if (pWin -> drawable.class == InputOutput)
    {
      if (nxagentWindowTopLevel(pWin))
      {
        mask = defaultEventMask;
      }
      else
      {
        mask = ExposureMask | VisibilityChangeMask | PointerMotionMask;
      }
    }

    mask |= PropertyChangeMask;
  }
  else if (pWin -> drawable.class != InputOnly)
  {
    mask = ExposureMask | VisibilityChangeMask;
  }

  return mask;
}

static int nxagentChangeMapPrivate(WindowPtr pWin, void * ptr)
{
  if (pWin && nxagentWindowPriv(pWin))
  {
    nxagentWindowPriv(pWin) -> isMapped = *((Bool *) ptr);
  }

  return WT_WALKCHILDREN;
}

static int nxagentChangeVisibilityPrivate(WindowPtr pWin, void * ptr)
{
  if (pWin && nxagentWindowPriv(pWin))
  {
    nxagentWindowPriv(pWin) -> visibilityState = *((int *) ptr);
  }

  return WT_WALKCHILDREN;
}

void nxagentDispatchEvents(PredicateFuncPtr predicate)
{
  XEvent X;
  xEvent x;
  ScreenPtr pScreen = NULL;

  Bool minimize = False;
  Bool closeSession = False;
  Bool switchFullscreen = False;
  Bool switchAllScreens = False;

  /*
   * Last entered top level window.
   */

  static WindowPtr nxagentLastEnteredTopLevelWindow = NULL;

  #ifdef BLOCKS
  fprintf(stderr, "[Begin read]\n");
  #endif

  #ifdef TEST
  fprintf(stderr, "nxagentDispatchEvents: Going to handle new events with "
              "predicate [%p].\n", *(void **)&predicate);
  #endif

  if (nxagentRemoteExposeRegion == NULL)
  {
    nxagentInitRemoteExposeRegion();
  }

  /*
   * We must read here, even if apparently there is
   * nothing to read. The ioctl() based readable
   * function, in fact, is often unable to detect a
   * failure of the socket, in particular if the
   * agent was connected to the proxy and the proxy
   * is gone. Thus we must trust the wakeup handler
   * that called us after the select().
   */

  #ifdef TEST

  if (nxagentPendingEvents(nxagentDisplay) == 0)
  {
    fprintf(stderr, "nxagentDispatchEvents: PANIC! No event needs to be dispatched.\n");
  }

  #endif

  /*
   * We want to process all the events already in
   * the queue, plus any additional event that may
   * be read from the network. If no event can be
   * read, we want to continue handling our clients
   * without flushing the output buffer.
   */

  while (nxagentCheckEvents(nxagentDisplay, &X, predicate != NULL ? predicate :
                                nxagentAnyEventPredicate, NULL) == 1)
  {
    #ifdef DEBUG
    fprintf(stderr, "nxagentDispatchEvents: Going to handle new event type [%d].\n",
                X.type);
    #endif

    /*
     * Handle the incoming event.
     */

    switch (X.type)
    {
      #ifdef NXAGENT_CLIPBOARD

      case SelectionClear:
      {
        #ifdef TEST
        fprintf(stderr, "nxagentDispatchEvents: Going to handle new SelectionClear event.\n");
        #endif

        nxagentClearSelection(&X);

        break;
      }
      case SelectionRequest:
      {
        #ifdef TEST
        fprintf(stderr, "nxagentDispatchEvents: Going to handle new SelectionRequest event.\n");
        #endif

        nxagentRequestSelection(&X);

        break;
      }
      case SelectionNotify:
      {
        #ifdef TEST
        fprintf(stderr, "nxagentDispatchEvents: Going to handle new SelectionNotify event.\n");
        #endif

        nxagentHandleSelectionNotifyFromXServer(&X);

        break;
      }

      #endif /* NXAGENT_CLIPBOARD */

      case PropertyNotify:
      {
        #ifdef TEST
        fprintf(stderr, "nxagentDispatchEvents: PropertyNotify on "
                    "prop %d[%s] window %lx state %d\n",
                        (int)X.xproperty.atom, validateString(XGetAtomName(nxagentDisplay, X.xproperty.atom)),
                            X.xproperty.window, X.xproperty.state);
        #endif

        nxagentHandlePropertyNotify(&X);

        break;
      }
      case KeyPress:
      {
        enum HandleEventResult result;

        KeySym keysym;

        #ifdef TEST
        fprintf(stderr, "nxagentDispatchEvents: Going to handle new KeyPress event.\n");
        #endif

        nxagentInputEvent = 1;

        nxagentKeyDown++;

        nxagentHandleKeyPress(&X, &result);

        if (viewportLastKeyPressResult != result)
        {
          viewportInc = 1;

          viewportLastKeyPressResult = result;
        }

        if (result != doNothing)
        {
          pScreen = nxagentScreen(X.xkey.window);
        }

        switch (result)
        {
          case doNothing:
          {
            break;
          }

          #ifdef DEBUG_TREE

          case doDebugTree:
          {
            fprintf(stderr, "\n========== nxagentRemoteWindowsTree ============\n");
            nxagentRemoteWindowsTree(nxagentWindow(screenInfo.screens[0]->root), 0);

            fprintf(stderr, "\n========== nxagentInternalWindowsTree ==========\n");
            nxagentInternalWindowsTree(screenInfo.screens[0]->root, 0);

            break;
          }

          #endif /* DEBUG_TREE */

          case doCloseSession:
          {
            closeSession = TRUE;

            break;
          }
          case doMinimize:
          {
            minimize = TRUE;

            break;
          }
          case doSwitchFullscreen:
          {
            switchFullscreen = TRUE;

            break;
          }
          case doSwitchAllScreens:
          {
            switchAllScreens = TRUE;

            break;
          }
          case doViewportMoveUp:
          {
            nxagentMoveViewport(pScreen, 0, -nxagentOption(Height));

            break;
          }
          case doViewportMoveDown:
          {
            nxagentMoveViewport(pScreen, 0, nxagentOption(Height));

            break;
          }
          case doViewportMoveLeft:
          {
            nxagentMoveViewport(pScreen, -nxagentOption(Width), 0);

            break;
          }
          case doViewportMoveRight:
          {
            nxagentMoveViewport(pScreen, nxagentOption(Width), 0);

            break;
          }
          case doViewportUp:
          {
            nxagentMoveViewport(pScreen, 0, -nextinc(viewportInc));

            break;
          }
          case doViewportDown:
          {
            nxagentMoveViewport(pScreen, 0, +nextinc(viewportInc));

            break;
          }
          case doViewportLeft:
          {
            nxagentMoveViewport(pScreen, -nextinc(viewportInc), 0);

            break;
          }
          case doViewportRight:
          {
            nxagentMoveViewport(pScreen, +nextinc(viewportInc), 0);

            break;
          }
          case doSwitchResizeMode:
          {
            if (nxagentOption(Shadow) == 0)
            {
              if (nxagentNoDialogIsRunning)
              {
                nxagentSwitchResizeMode(pScreen);
              }
            }
            else
            {
              nxagentShadowSwitchResizeMode(pScreen);
            }

            break;
          }
          case doSwitchDeferMode:
          {
            if (nxagentNoDialogIsRunning)
            {
              nxagentSwitchDeferMode();
            }

            break;
          }
          case doAutoGrab:
          {
            nxagentToggleAutoGrab();

            break;
          }
          default:
          {
            FatalError("nxagentDispatchEvent: handleKeyPress returned unknown value\n");

            break;
          }
        }

        /*
         * Elide multiple KeyPress/KeyRelease events of
         * the same key and generate a single pair. This
         * is intended to reduce the impact of the laten-
         * cy on the key auto-repeat, handled by the re-
         * mote X server. We may optionally do that only
         * if the timestamps in the events show an exces-
         * sive delay.
         */

        keysym = XKeycodeToKeysym(nxagentDisplay, X.xkey.keycode, 0);

        if (nxagentMonitoredDuplicate(keysym) == 1)
        {
          nxagentRemoveDuplicatedKeys(&X);
        }

        if (nxagentOption(ViewOnly) == 0 && nxagentOption(Shadow) == 1 && result == doNothing)
        {
          X.xkey.keycode = nxagentConvertKeycode(X.xkey.keycode);

          NXShadowEvent(nxagentDisplay, X);
        }

        break;
      }
      case KeyRelease:
      {
        enum HandleEventResult result;
        int sendKey = 0;

/*
FIXME: If we don't flush the queue here, it could happen
       that the inputInfo structure will not be up to date
       when we perform the following check on down keys.
*/
        ProcessInputEvents();

/*
FIXME: Don't enqueue the KeyRelease event if the key was
       not already pressed. This workaround avoids a fake
       KeyPress being enqueued by the XKEYBOARD extension.
       Another solution would be to let the events
       enqueued and to remove the KeyPress afterwards.
*/
        if (BitIsOn(inputInfo.keyboard -> key -> down,
                       nxagentConvertKeycode(X.xkey.keycode)))
        {
          sendKey = 1;
        }

        #ifdef TEST
        fprintf(stderr, "nxagentDispatchEvents: Going to handle new KeyRelease event.\n");
        #endif

        nxagentInputEvent = 1;

        nxagentKeyDown--;

        if (nxagentKeyDown <= 0)
        {
          nxagentKeyDown = 0;
        }

        if (nxagentXkbState.Initialized == 0)
        {
          if (X.xkey.keycode == nxagentCapsLockKeycode)
          {
            nxagentXkbCapsTrap = 1;
          }
          else if (X.xkey.keycode == nxagentNumLockKeycode)
          {
            nxagentXkbNumTrap = 1;
          }

          nxagentInitXkbKeyboardState();

          nxagentXkbCapsTrap = 0;
          nxagentXkbNumTrap = 0;
        }

        /* Calculate the time elapsed between this and the last event we
           received. Add this delta to time we recorded for the last
           KeyPress event we passed on to our clients.  */
        memset(&x, 0, sizeof(xEvent));
        x.u.u.type = KeyRelease;
        x.u.u.detail = nxagentConvertKeycode(X.xkey.keycode);
        x.u.keyButtonPointer.time = nxagentLastKeyPressTime +
            (X.xkey.time - nxagentLastServerTime);

        nxagentLastServerTime = X.xkey.time;

        nxagentLastEventTime = GetTimeInMillis();

        if (x.u.keyButtonPointer.time > nxagentLastEventTime)
        {
          x.u.keyButtonPointer.time = nxagentLastEventTime;
        }

        /* do not send a KeyRelease for a special keystroke since we
           also did not send a KeyPress event in that case */
        if (!(nxagentCheckSpecialKeystroke(&X.xkey, &result)) && (sendKey == 1))
        {
          #ifdef TEST
          fprintf(stderr, "%s: passing KeyRelease event to clients\n", __func__);
          #endif

          mieqEnqueue(&x);

          CriticalOutputPending = 1;

          if (nxagentOption(ViewOnly) == 0 && nxagentOption(Shadow))
          {
            X.xkey.keycode = nxagentConvertKeycode(X.xkey.keycode);

            NXShadowEvent(nxagentDisplay, X);
          }
        }
        else
        {
          #ifdef TEST
          fprintf(stderr, "%s: NOT passing KeyRelease event to clients\n", __func__);
          #endif
        }

        break;
      }
      case ButtonPress:
      {
        #ifdef NX_DEBUG_INPUT
        if (nxagentDebugInput == 1)
        {
          fprintf(stderr, "nxagentDispatchEvents: Going to handle new ButtonPress event.\n");
        }
        #endif

        nxagentInputEvent = 1;

        if (nxagentOption(Fullscreen))
        {
          if ( (nxagentOption(MagicPixel) == 1) && (nxagentMagicPixelZone(X.xbutton.x, X.xbutton.y)) )
          {
            pScreen = nxagentScreen(X.xbutton.window);

            minimize = True;

            break;
          }
        }

        if (nxagentOption(DesktopResize) == False &&
                (X.xbutton.state & (ControlMask | Mod1Mask)) == (ControlMask | Mod1Mask))
        {
          /*
           * Start viewport navigation mode.
           */

          int resource = nxagentWaitForResource(NXGetCollectGrabPointerResource,
                                                    nxagentCollectGrabPointerPredicate);

          ScreenPtr pScreen = nxagentScreen(X.xbutton.window);
          viewportCursor = XCreateFontCursor(nxagentDisplay, XC_fleur);

          NXCollectGrabPointer(nxagentDisplay, resource,
                                   nxagentDefaultWindows[pScreen -> myNum], True,
                                       NXAGENT_POINTER_EVENT_MASK, GrabModeAsync,
                                           GrabModeAsync, None, viewportCursor,
                                               CurrentTime);
          viewportLastX = X.xbutton.x;
          viewportLastY = X.xbutton.y;

          break;
        }

        if (!(nxagentOption(Fullscreen) &&
                X.xbutton.window == nxagentFullscreenWindow &&
                    X.xbutton.subwindow == None))
        {
          memset(&x, 0, sizeof(xEvent));
          x.u.u.type = ButtonPress;
          x.u.u.detail = inputInfo.pointer -> button -> map[nxagentReversePointerMap[X.xbutton.button]];
          x.u.keyButtonPointer.time = nxagentLastEventTime = GetTimeInMillis();

          if (nxagentOption(Rootless))
          {
            x.u.keyButtonPointer.rootX = X.xmotion.x_root;
            x.u.keyButtonPointer.rootY = X.xmotion.y_root;
          }
          else
          {
            x.u.keyButtonPointer.rootX = X.xmotion.x - nxagentOption(RootX);
            x.u.keyButtonPointer.rootY = X.xmotion.y - nxagentOption(RootY);
          }

          #ifdef NX_DEBUG_INPUT
          if (nxagentDebugInput == 1)
          {
            fprintf(stderr, "nxagentDispatchEvents: Adding ButtonPress event.\n");
          }
          #endif

          mieqEnqueue(&x);

          CriticalOutputPending = 1;
        }

        if (nxagentOption(ViewOnly) == 0 && nxagentOption(Shadow))
        {
          X.xbutton.x -= nxagentOption(RootX);
          X.xbutton.y -= nxagentOption(RootY);

          if (nxagentOption(YRatio) != DONT_SCALE)
          {
            X.xbutton.x = (X.xbutton.x << PRECISION) / nxagentOption(YRatio);
          }

          if (nxagentOption(XRatio) != DONT_SCALE)
          {
            X.xbutton.y = (X.xbutton.y << PRECISION) / nxagentOption(YRatio);
          }

          NXShadowEvent(nxagentDisplay, X);
        }

        break;
      }
      case ButtonRelease:
      {
        #ifdef NX_DEBUG_INPUT
        if (nxagentDebugInput == 1)
        {
          fprintf(stderr, "nxagentDispatchEvents: Going to handle new ButtonRelease event.\n");
        }
        #endif

        nxagentInputEvent = 1;

        if (viewportCursor)
        {
          /*
           * Leave viewport navigation mode.
           */

          XUngrabPointer(nxagentDisplay, CurrentTime);

          XFreeCursor(nxagentDisplay, viewportCursor);

          viewportCursor = None;
        }

        if (minimize != True)
        {
          memset(&x, 0, sizeof(xEvent));
          x.u.u.type = ButtonRelease;
          x.u.u.detail = inputInfo.pointer -> button -> map[nxagentReversePointerMap[X.xbutton.button]];
          x.u.keyButtonPointer.time = nxagentLastEventTime = GetTimeInMillis();

          if (nxagentOption(Rootless))
          {
            x.u.keyButtonPointer.rootX = X.xmotion.x_root;
            x.u.keyButtonPointer.rootY = X.xmotion.y_root;
          }
          else
          {
            x.u.keyButtonPointer.rootX = X.xmotion.x - nxagentOption(RootX);
            x.u.keyButtonPointer.rootY = X.xmotion.y - nxagentOption(RootY);
          }

          #ifdef NX_DEBUG_INPUT
          if (nxagentDebugInput == 1)
          {
            fprintf(stderr, "nxagentDispatchEvents: Adding ButtonRelease event.\n");
          }
          #endif

          mieqEnqueue(&x);

          CriticalOutputPending = 1;
        }

        if (nxagentOption(ViewOnly) == 0 && nxagentOption(Shadow))
        {
          X.xbutton.x -= nxagentOption(RootX);
          X.xbutton.y -= nxagentOption(RootY);

          if (nxagentOption(XRatio) != DONT_SCALE)
          {
            X.xbutton.x = (X.xbutton.x << PRECISION) / nxagentOption(XRatio);
          }

          if (nxagentOption(YRatio) != DONT_SCALE)
          {
            X.xbutton.y = (X.xbutton.y << PRECISION) / nxagentOption(YRatio);
          }

          NXShadowEvent(nxagentDisplay, X);
        }

        break;
      }
      case MotionNotify:
      {
        ScreenPtr pScreen = nxagentScreen(X.xmotion.window);

        #ifdef TEST
        fprintf(stderr, "nxagentDispatchEvents: Going to handle new MotionNotify event.\n");
        #endif

        #ifdef NX_DEBUG_INPUT
        if (nxagentDebugInput == 1)
        {
          fprintf(stderr, "nxagentDispatchEvents: Handling motion notify window [%ld] root [%ld] child [%ld].\n",
                    X.xmotion.window, X.xmotion.root, X.xmotion.subwindow);

          fprintf(stderr, "nxagentDispatchEvents: Pointer at [%d][%d] relative root [%d][%d].\n",
                    X.xmotion.x, X.xmotion.y, X.xmotion.x_root, X.xmotion.y_root);
        }
        #endif

        memset(&x, 0, sizeof(xEvent));
        x.u.u.type = MotionNotify;

        if (nxagentOption(Rootless))
        {
          WindowPtr pWin = nxagentWindowPtr(X.xmotion.window);

          if (pWin)
          {
            nxagentLastEnteredWindow = pWin;
          }

          if (nxagentPulldownDialogPid == 0 && nxagentLastEnteredTopLevelWindow &&
                  (X.xmotion.y_root < nxagentLastEnteredTopLevelWindow -> drawable.y + 4))
          {
            if (pWin && nxagentClientIsDialog(wClient(pWin)) == 0 &&
                    nxagentLastEnteredTopLevelWindow -> parent == screenInfo.screens[0]->root &&
                        nxagentLastEnteredTopLevelWindow -> overrideRedirect == False &&
                            X.xmotion.x_root > (nxagentLastEnteredTopLevelWindow -> drawable.x +
                                (nxagentLastEnteredTopLevelWindow -> drawable.width >> 1) - 50) &&
                                    X.xmotion.x_root < (nxagentLastEnteredTopLevelWindow -> drawable.x +
                                        (nxagentLastEnteredTopLevelWindow -> drawable.width >> 1) + 50) &&
                                            nxagentOption(Menu) == 1)
            {
              nxagentPulldownDialog(nxagentLastEnteredTopLevelWindow -> drawable.id);
            }
          }

          x.u.keyButtonPointer.rootX = X.xmotion.x_root;
          x.u.keyButtonPointer.rootY = X.xmotion.y_root;
        }
        else
        {
          x.u.keyButtonPointer.rootX = X.xmotion.x - nxagentOption(RootX);
          x.u.keyButtonPointer.rootY = X.xmotion.y - nxagentOption(RootY);
        }

        x.u.keyButtonPointer.time = nxagentLastEventTime = GetTimeInMillis();

        if (viewportCursor == None &&
                !(nxagentOption(Fullscreen) &&
                    X.xmotion.window == nxagentDefaultWindows[pScreen -> myNum]
                        && X.xmotion.subwindow == None))
        {
          #ifdef NX_DEBUG_INPUT
          if (nxagentDebugInput == 1)
          {
            fprintf(stderr, "nxagentDispatchEvents: Adding motion event [%d, %d] to the queue.\n",
                        x.u.keyButtonPointer.rootX, x.u.keyButtonPointer.rootY);
          }
          #endif

          mieqEnqueue(&x);
        }

        /*
         * This test is more complicated and probably not necessary, compared
         * to a simple check on viewportCursor.
         *
         * if (!nxagentOption(Fullscreen) &&
         *       (X.xmotion.state & (ControlMask | Mod1Mask | Button1Mask)) ==
         *            (ControlMask | Mod1Mask | Button1Mask))
         */

        if (viewportCursor)
        {
          /*
           * Pointer is in viewport navigation mode.
           */

          nxagentMoveViewport(pScreen, viewportLastX - X.xmotion.x, viewportLastY - X.xmotion.y);

          viewportLastX = X.xmotion.x;
          viewportLastY = X.xmotion.y;
        }

        if (nxagentOption(ViewOnly) == 0 && nxagentOption(Shadow) && !viewportCursor)
        {
          X.xmotion.x -= nxagentOption(RootX);
          X.xmotion.y -= nxagentOption(RootY);

          if (nxagentOption(XRatio) != DONT_SCALE)
          {
            X.xmotion.x = (X.xmotion.x << PRECISION) / nxagentOption(XRatio);
          }

          if (nxagentOption(YRatio) != DONT_SCALE)
          {
            X.xmotion.y = (X.xmotion.y << PRECISION) / nxagentOption(YRatio);
          }

          NXShadowEvent(nxagentDisplay, X);
        }

        if (nxagentOption(Shadow) == 0)
        {
          nxagentInputEvent = 1;
        }

        break;
      }
      case FocusIn:
      {
        WindowPtr pWin;

        #ifdef DEBUG
        fprintf(stderr, "%s: Going to handle new FocusIn event [0x%x] mode: [%s]\n", __func__, X.xfocus.window, nxagentGetNotifyMode(X.xfocus.mode));
        {
          XlibWindow w;
          int revert_to;
          XGetInputFocus(nxagentDisplay, &w, &revert_to);
          fprintf(stderr, "%s: (FocusIn): Event win [0x%x] Focus owner [0x%x] nxagentDefaultWindows[0] [0x%x]\n", __func__, X.xfocus.window, w, nxagentDefaultWindows[0]);
        }
        #else
          #ifdef TEST
        fprintf(stderr, "%s: Going to handle new FocusIn event\n", __func__);
          #endif
        #endif

        /*
         * Here we change the focus state in the agent.
         * It looks like this is needed only for root-
         * less mode at the present moment.
         */

        if (nxagentOption(Rootless) &&
                (pWin = nxagentWindowPtr(X.xfocus.window)))
        {
          SetInputFocus(serverClient, inputInfo.keyboard, pWin -> drawable.id,
                            RevertToPointerRoot, GetTimeInMillis(), False);
        }

        if (X.xfocus.detail != NotifyInferior)
        {
          pScreen = nxagentScreen(X.xfocus.window);

          if (pScreen)
          {
            nxagentDirectInstallColormaps(pScreen);
          }
        }

        if (nxagentOption(AutoGrab) && !(nxagentOption(AllScreens) || nxagentOption(Fullscreen) || nxagentOption(Rootless)))
        {
          if (X.xfocus.window == nxagentDefaultWindows[0] && X.xfocus.mode == NotifyNormal)
          {
            #if defined(DEBUG) || defined(DEBUG_AUTOGRAB)
            fprintf(stderr, "%s: (FocusIn): grabbing\n", __func__);
            #endif
            nxagentGrabPointerAndKeyboard(NULL);
          }
          /*      else
          {
            #if defined(DEBUG) || defined(DEBUG_AUTOGRAB)
            fprintf(stderr, "%s: (FocusIn): ungrabbing\n", __func__);
            #endif
            nxagentUngrabPointerAndKeyboard(NULL);
          }
          */
        }
        break;
      }
      case FocusOut:
      {
        #ifdef DEBUG
        fprintf(stderr, "%s: Going to handle new FocusOut event [0x%x] mode: [%s]\n", __func__, X.xfocus.window, nxagentGetNotifyMode(X.xfocus.mode));
        #else
          #ifdef TEST
          fprintf(stderr, "%s: Going to handle new FocusOut event.\n", __func__);
          #endif
        #endif

        if (X.xfocus.detail != NotifyInferior)
        {
          pScreen = nxagentScreen(X.xfocus.window);

          if (pScreen)
          {
            nxagentDirectUninstallColormaps(pScreen);
          }
        }

        #ifdef NXAGENT_FIXKEYS

        {
          /*
           * Force the keys all up when focus is lost.
           */

          for (int i = 0; i < DOWN_LENGTH; i++) /* input.h */
          {
            CARD8 val = inputInfo.keyboard->key->down[i];

            if (val != 0)
            {
              for (int k = 0; k < 8; k++)
              {
                {
                  #ifdef NXAGENT_FIXKEYS_DEBUG
                  fprintf(stderr, "sending KeyRelease event for keycode: %x\n",
                              i * 8 + k);
                  #endif

                  if (!nxagentOption(Rootless) ||
                          inputInfo.keyboard->key->modifierMap[i * 8 + k])
                  {
                    memset(&x, 0, sizeof(xEvent));
                    x.u.u.type = KeyRelease;
                    x.u.u.detail = i * 8 + k;
                    x.u.keyButtonPointer.time = nxagentLastEventTime = GetTimeInMillis();

                    if (nxagentOption(ViewOnly) == 0 && nxagentOption(Shadow))
                    {
                      XEvent xM;

                      memset(&xM, 0, sizeof(XEvent));
                      xM.type = KeyRelease;
                      xM.xkey.display = nxagentDisplay;
                      xM.xkey.type = KeyRelease;
                      xM.xkey.keycode = i * 8 + k;
                      xM.xkey.state = inputInfo.keyboard->key->state;
                      xM.xkey.time = GetTimeInMillis();
                      NXShadowEvent(nxagentDisplay, xM);
                    }

                    mieqEnqueue(&x);
                  }
                }
              }
            }
          }

          nxagentKeyDown = 0;
        }

        #endif /* NXAGENT_FIXKEYS */

        if (nxagentOption(AutoGrab) && !nxagentFullscreenWindow)
        {
          XlibWindow w;
          int revert_to;
          XGetInputFocus(nxagentDisplay, &w, &revert_to);
          if (w != nxagentDefaultWindows[0] && X.xfocus.mode == NotifyWhileGrabbed)
          {
            #if defined(DEBUG) || defined(DEBUG_AUTOGRAB)
            fprintf(stderr, "%s: (FocusOut): ungrabbing\n", __func__);
            #endif
            nxagentUngrabPointerAndKeyboard(NULL);
          }
        }
        break;
      }
      case KeymapNotify:
      {
        #ifdef TEST
        fprintf(stderr, "nxagentDispatchEvents: Going to handle new KeymapNotify event.\n");
        #endif

        break;
      }
      case EnterNotify:
      {
        WindowPtr pWin;

        WindowPtr pTLWin = NULL;

        #ifdef TEST
        fprintf(stderr, "nxagentDispatchEvents: Going to handle new EnterNotify event.\n");
        #endif

        if (nxagentOption(Rootless))
        {
          pWin = nxagentWindowPtr(X.xcrossing.window);

          if (pWin != NULL)
          {
            for (pTLWin = pWin;
                     pTLWin -> parent != pTLWin -> drawable.pScreen -> root;
                         pTLWin = pTLWin -> parent);
          }

          if (pTLWin)
          {
            nxagentLastEnteredTopLevelWindow = pTLWin;
          }

          #ifdef TEST
          fprintf(stderr, "nxagentDispatchEvents: nxagentLastEnteredTopLevelWindow [%p].\n",
                      (void *)nxagentLastEnteredTopLevelWindow);
          #endif
        }

        if (nxagentOption(Rootless) && nxagentWMIsRunning &&
                (pWin = nxagentWindowPtr(X.xcrossing.window)) &&
                    nxagentWindowTopLevel(pWin) && !pWin -> overrideRedirect &&
                        (pWin -> drawable.x != X.xcrossing.x_root - X.xcrossing.x - pWin -> borderWidth ||
                            pWin -> drawable.y != X.xcrossing.y_root - X.xcrossing.y - pWin -> borderWidth))
        {
          /*
           * This code is useful for finding the window
           * position. It should be re-implemented by
           * following the ICCCM 4.1.5 recommendations.
           */

          XID values[4];
          register XID *value = values;
          Mask mask = 0;
          ClientPtr pClient = wClient(pWin);

          #ifdef TEST
          fprintf(stderr, "nxagentDispatchEvents: pWin -> drawable.x [%d] pWin -> drawable.y [%d].\n",
                      pWin -> drawable.x, pWin -> drawable.y);
          #endif

          *value++ = (XID) (X.xcrossing.x_root - X.xcrossing.x - pWin -> borderWidth);
          *value++ = (XID) (X.xcrossing.y_root - X.xcrossing.y - pWin -> borderWidth);

          /*
           * nxagentWindowPriv(pWin)->x = (X.xcrossing.x_root - X.xcrossing.x);
           * nxagentWindowPriv(pWin)->y = (X.xcrossing.y_root - X.xcrossing.y);
           */

          mask = CWX | CWY;

          nxagentScreenTrap = 1;

          ConfigureWindow(pWin, mask, (XID *) values, pClient);

          nxagentScreenTrap = 0;
        }

        if (nxagentOption(Fullscreen) == 1 &&
                X.xcrossing.window == nxagentFullscreenWindow &&
                    X.xcrossing.detail != NotifyInferior)
        {
          nxagentGrabPointerAndKeyboard(&X);
        }

        if (X.xcrossing.detail != NotifyInferior)
        {
          pScreen = nxagentScreen(X.xcrossing.window);

          if (pScreen)
          {
            NewCurrentScreen(pScreen, X.xcrossing.x, X.xcrossing.y);

            memset(&x, 0, sizeof(xEvent));
            x.u.u.type = MotionNotify;

            if (nxagentOption(Rootless))
            {
              nxagentLastEnteredWindow = nxagentWindowPtr(X.xcrossing.window);
              x.u.keyButtonPointer.rootX = X.xcrossing.x_root;
              x.u.keyButtonPointer.rootY = X.xcrossing.y_root;
            }
            else
            {
              x.u.keyButtonPointer.rootX = X.xcrossing.x - nxagentOption(RootX);
              x.u.keyButtonPointer.rootY = X.xcrossing.y - nxagentOption(RootY);
            }

            x.u.keyButtonPointer.time = nxagentLastEventTime = GetTimeInMillis();

            mieqEnqueue(&x);

            nxagentDirectInstallColormaps(pScreen);
          }
        }

        nxagentInputEvent = 1;

        break;
      }
      case LeaveNotify:
      {
        #ifdef TEST
        fprintf(stderr, "nxagentDispatchEvents: Going to handle new LeaveNotify event.\n");
        #endif

        if (nxagentOption(Rootless) && X.xcrossing.mode == NotifyNormal &&
                X.xcrossing.detail != NotifyInferior)
        {
          nxagentLastEnteredWindow = NULL;
        }

        if (!nxagentOption(AutoGrab))
        {
          if (X.xcrossing.window == nxagentDefaultWindows[0] &&
              X.xcrossing.detail != NotifyInferior &&
              X.xcrossing.mode == NotifyNormal)
          {
             nxagentUngrabPointerAndKeyboard(&X);
          }
        }

        if (X.xcrossing.detail != NotifyInferior)
        {
          pScreen = nxagentScreen(X.xcrossing.window);

          if (pScreen)
          {
            nxagentDirectUninstallColormaps(pScreen);
          }
        }

        nxagentInputEvent = 1;

        break;
      }
      case DestroyNotify:
      {
        #ifdef TEST
        fprintf(stderr, "nxagentDispatchEvents: Going to handle new DestroyNotify event.\n");
        #endif

        if (nxagentParentWindow != (Window) 0 &&
                X.xdestroywindow.window == nxagentParentWindow)
        {
          fprintf(stderr, "Warning: Unhandled destroy notify event received in agent.\n");
        }

        break;
      }
      case ClientMessage:
      {
        enum HandleEventResult result;

        #ifdef TEST
        fprintf(stderr, "nxagentDispatchEvents: Going to handle new ClientMessage event.\n");
        #endif

        nxagentHandleClientMessageEvent(&X, &result);

        if (result == doCloseSession)
        {
          closeSession = TRUE;
        }

        break;
      }
      case VisibilityNotify:
      {
        #ifdef TEST
        fprintf(stderr, "nxagentDispatchEvents: Going to handle new VisibilityNotify event.\n");
        #endif

        if (X.xvisibility.window != nxagentDefaultWindows[0])
        {
          Window window = X.xvisibility.window;

          WindowPtr pWin = nxagentWindowPtr(window);

          if (pWin && nxagentWindowPriv(pWin))
          {
            if (nxagentWindowPriv(pWin) -> visibilityState != X.xvisibility.state)
            {
              int value = X.xvisibility.state;

              if (nxagentOption(Rootless) == 1)
              {
                TraverseTree(pWin, nxagentChangeVisibilityPrivate, &value);
              }
              else
              {
                nxagentChangeVisibilityPrivate(pWin, &value);
              }
            }
          }

          #ifdef TEST
          fprintf(stderr, "nxagentDispatchEvents: Suppressing visibility notify on window [%lx].\n",
                      X.xvisibility.window);
          #endif

          break;
        }

        #ifdef TEST
        fprintf(stderr, "nxagentDispatchEvents: Visibility notify state is [%d] with previous [%d].\n",
                    X.xvisibility.state, nxagentVisibility);
        #endif

        nxagentVisibility = X.xvisibility.state;

        break;
      }
      case Expose:
      {
        #ifdef DEBUG
        fprintf(stderr, "nxagentDispatchEvents: Going to handle new Expose event.\n");

        fprintf(stderr, "nxagentDispatchEvents: WARNING! Received Expose event "
                    "for drawable [%lx] geometry [%d, %d, %d, %d] count [%d].\n",
                        X.xexpose.window, X.xexpose.x, X.xexpose.y, X.xexpose.width,
                            X.xexpose.height, X.xexpose.count);
        #endif

        nxagentHandleExposeEvent(&X);

        break;
      }
      case GraphicsExpose:
      {
        #ifdef DEBUG
        fprintf(stderr, "nxagentDispatchEvents: Going to handle new GraphicsExpose event.\n");

        fprintf(stderr, "nxagentDispatchEvents: WARNING! Received GraphicsExpose event "
                    "for drawable [%lx] geometry [%d, %d, %d, %d] count [%d].\n",
                        X.xgraphicsexpose.drawable, X.xgraphicsexpose.x, X.xgraphicsexpose.y,
                            X.xgraphicsexpose.width, X.xgraphicsexpose.height,
                                X.xgraphicsexpose.count);
        #endif

        nxagentHandleGraphicsExposeEvent(&X);

        break;
      }
      case NoExpose:
      {
        #ifdef DEBUG
        fprintf(stderr, "nxagentDispatchEvents: Going to handle new NoExpose event.\n");

        fprintf(stderr, "nxagentDispatchEvents: WARNING! Received NoExpose event for "
                    "drawable [%lx].\n", X.xnoexpose.drawable);
        #endif

        break;
      }
      case CirculateNotify:
      {
        /*
         * WindowPtr pWin;
         * WindowPtr pSib;
         * ClientPtr pClient;

         * XID values[2];
         * register XID *value = values;
         * Mask mask = 0;
         */

        #ifdef WARNING
        fprintf(stderr, "nxagentDispatchEvents: Going to handle new CirculateNotify event.\n");
        #endif

        /*
         * FIXME: Do we need this?
         *
         * pWin = nxagentWindowPtr(X.xcirculate.window);
         *
         * if (!pWin)
         * {
         *   pWin = nxagentRootlessTopLevelWindow(X.xcirculate.window);
         * }
         *
         * if (!pWin)
         * {
         *   break;
         * }
         *
         * XQueryTree(nxagentDisplay, DefaultRootWindow(nxagentDisplay),
         *                &root_return, &parent_return, &children_return, &nchildren_return);
         *
         * nxagentRootlessRestack(children_return, nchildren_return);
         */

        break;
      }
      case ConfigureNotify:
      {
        #ifdef TEST
        fprintf(stderr, "nxagentDispatchEvents: Going to handle new ConfigureNotify event.\n");
        #endif

        if (nxagentConfiguredSynchroWindow == X.xconfigure.window)
        {
          if (nxagentExposeQueue.exposures[nxagentExposeQueue.start].serial != X.xconfigure.x)
          {
            #ifdef WARNING
            if (nxagentVerbose == 1)
            {
              fprintf(stderr, "nxagentDispatchEvents: Requested ConfigureNotify changes didn't take place.\n");
            }
            #endif
          }

          #ifdef TEST
          fprintf(stderr, "nxagentDispatchEvents: Received ConfigureNotify and going to call "
                      "nxagentSynchronizeExpose.\n");
          #endif

          nxagentSynchronizeExpose();

          break;
        }

        nxagentHandleConfigureNotify(&X);

        break;
      }
      case GravityNotify:
      {
        #ifdef TEST
        fprintf(stderr, "nxagentDispatchEvents: Going to handle new GravityNotify event.\n");
        #endif

        break;
      }
      case ReparentNotify:
      {
        #ifdef TEST
        fprintf(stderr, "nxagentDispatchEvents: Going to handle new ReparentNotify event.\n");
        #endif

        nxagentHandleReparentNotify(&X);

        break;
      }
      case UnmapNotify:
      {
        #ifdef TEST
        fprintf(stderr, "nxagentDispatchEvents: Going to handle new UnmapNotify event.\n");
        #endif

        if (nxagentOption(Rootless) == 1)
        {
          WindowPtr pWin;

          if ((pWin = nxagentRootlessTopLevelWindow(X.xunmap.window)) != NULL ||
                  ((pWin = nxagentWindowPtr(X.xunmap.window)) != NULL &&
                      nxagentWindowTopLevel(pWin) == 1))
          {
            nxagentScreenTrap = 1;

            UnmapWindow(pWin, False);

            nxagentScreenTrap = 0;
          }
        }

        if (nxagentUseNXTrans == 1 && nxagentOption(Rootless) == 0 &&
                nxagentOption(Nested) == 0 &&
                    X.xmap.window != nxagentIconWindow)
        {
          nxagentVisibility = VisibilityFullyObscured;
        }

        break;
      }
      case MapNotify:
      {
        #ifdef TEST
        fprintf(stderr, "nxagentDispatchEvents: Going to handle new MapNotify event.\n");
        #endif

        if (nxagentOption(Rootless) == 1)
        {
          WindowPtr pWin;

          if ((pWin = nxagentRootlessTopLevelWindow(X.xmap.window)) != NULL ||
                  ((pWin = nxagentWindowPtr(X.xmap.window)) != NULL &&
                      nxagentWindowTopLevel(pWin) == 1))
          {
            ClientPtr pClient = wClient(pWin);

            nxagentScreenTrap = 1;

            MapWindow(pWin, pClient);

            nxagentScreenTrap = 0;
          }

          if (pWin != NULL)
          {
            Bool value = 1;

            TraverseTree(pWin, nxagentChangeMapPrivate, &value);
          }
        }

        if (nxagentOption(AllScreens) == 1)
        {
          if (X.xmap.window == nxagentIconWindow)
          {
            pScreen = nxagentScreen(X.xmap.window);
            nxagentMaximizeToFullScreen(pScreen);
          }
        }

        if (nxagentOption(Fullscreen) == 1)
        {
          nxagentVisibility = VisibilityUnobscured;
          nxagentVisibilityStop = False;
          nxagentVisibilityTimeout = GetTimeInMillis() + 2000;
        }

        /*
         * without window manager there will be no ConfigureNotify
         * event that would trigger xinerama updates. So we do that once
         * the nxagent window gets mapped.
         */
        if (!nxagentWMIsRunning &&
            X.xmap.window == nxagentDefaultWindows[nxagentScreen(X.xmap.window)->myNum])
        {
          nxagentChangeScreenConfig(nxagentScreen(X.xmap.window)->myNum, nxagentOption(Width),
                                    nxagentOption(Height));
        }

        break;
      }
      case MappingNotify:
      {
        XMappingEvent *mappingEvent = (XMappingEvent *) &X;

        #ifdef DEBUG
        fprintf(stderr, "nxagentDispatchEvents: WARNING! Going to handle new MappingNotify event.\n");
        #endif

        if (mappingEvent -> request == MappingPointer)
        {
            nxagentInitPointerMap();
        }

        break;
      }
      default:
      {
        /*
         * Let's check if this is a XKB
         * state modification event.
         */

        if (nxagentHandleXkbKeyboardStateEvent(&X) == 0 && nxagentHandleXFixesSelectionNotify(&X) == 0)
        {
          #ifdef TEST
          fprintf(stderr, "nxagentDispatchEvents: WARNING! Unhandled event code [%d].\n",
                      X.type);
          #endif
        }

        break;
      }

    } /* End of switch (X.type) */

    if (X.xany.serial < lastEventSerial)
    {
      /*
       * Start over.
       */

      nxagentDeleteStaticResizedWindow(0);
    }
    else
    {
      nxagentDeleteStaticResizedWindow(X.xany.serial - 1);
    }

    lastEventSerial = X.xany.serial;

  } /* End of while (...) */

  /*
   * Send the exposed regions to the clients.
   */

  nxagentForwardRemoteExpose();

  /*
   * Handle the agent window's changes.
   */

  if (closeSession)
  {
    if (nxagentOption(Persistent))
    {
      if (nxagentNoDialogIsRunning)
      {
        nxagentLaunchDialog(DIALOG_SUSPEND_SESSION);
      }
    }
    else
    {
      if (nxagentNoDialogIsRunning)
      {
        nxagentLaunchDialog(DIALOG_KILL_SESSION);
      }
    }
  }

  if (minimize)
  {
    nxagentWMDetect();

    if (nxagentWMIsRunning)
    {
      if (nxagentOption(AllScreens))
      {
        nxagentMinimizeFromFullScreen(pScreen);
      }
      else
      {
        XIconifyWindow(nxagentDisplay, nxagentDefaultWindows[0],
                           DefaultScreen(nxagentDisplay));
      }
    }
  }

  if (switchFullscreen)
  {
    if (nxagentOption(AllScreens) == 1 && nxagentOption(Fullscreen) == 1)
    {
      nxagentSwitchAllScreens(pScreen, 0);
    }
    else
    {
      nxagentSwitchFullscreen(pScreen, !nxagentOption(Fullscreen));
    }
  }

  if (switchAllScreens)
  {
    if (nxagentOption(AllScreens) == 0 && nxagentOption(Fullscreen) == 1)
    {
      nxagentSwitchFullscreen(pScreen, 0);
    }
    else
    {
      nxagentSwitchAllScreens(pScreen, !nxagentOption(AllScreens));
    }
  }

  #ifdef BLOCKS
  fprintf(stderr, "[End read]\n");
  #endif

  /*
   * Let the underlying X server code
   * process the input events.
   */

  #ifdef BLOCKS
  fprintf(stderr, "[Begin events]\n");
  #endif

  ProcessInputEvents();

  #ifdef TEST
  fprintf(stderr, "nxagentDispatchEvents: Output pending flag is [%d] critical [%d].\n",
              NewOutputPending, CriticalOutputPending);
  #endif

  /*
   * Write the events to our clients. We may
   * flush only in the case of critical output
   * but this doesn't seem beneficial.
   *
   * if (CriticalOutputPending == 1)
   * {
   *   FlushAllOutput();
   * }
   */

  if (NewOutputPending == 1)
  {
    #ifdef TEST
    fprintf(stderr, "nxagentDispatchEvents: Flushed the processed events to clients.\n");
    #endif

    FlushAllOutput();
  }

  #ifdef TEST

  if (nxagentPendingEvents(nxagentDisplay) > 0)
  {
    fprintf(stderr, "nxagentDispatchEvents: WARNING! More events need to be dispatched.\n");
  }

  #endif

  #ifdef BLOCKS
  fprintf(stderr, "[End events]\n");
  #endif
}

/*
 * Functions providing the ad-hoc handling
 * of the remote X events.
 */

int nxagentHandleKeyPress(XEvent *X, enum HandleEventResult *result)
{
  if (nxagentXkbState.Initialized == 0)
  {
    if (X -> xkey.keycode == nxagentCapsLockKeycode)
    {
      nxagentXkbCapsTrap = 1;
    }
    else if (X -> xkey.keycode == nxagentNumLockKeycode)
    {
      nxagentXkbNumTrap = 1;
    }

    nxagentInitXkbKeyboardState();

    nxagentXkbCapsTrap = 0;
    nxagentXkbNumTrap = 0;
  }

  if (nxagentCheckSpecialKeystroke(&X -> xkey, result))
  {
    #ifdef TEST
    fprintf(stderr, "%s: NOT passing KeyPress event to clients\n", __func__);
    #endif
    return 1;
  }

  if (X -> xkey.keycode == nxagentCapsLockKeycode)
  {
    nxagentXkbState.Caps = (~nxagentXkbState.Caps & 1);
  }
  else if (X -> xkey.keycode == nxagentNumLockKeycode)
  {
    nxagentXkbState.Num = (~nxagentXkbState.Num & 1);
  }

  nxagentLastServerTime = X -> xkey.time;

  nxagentLastEventTime = nxagentLastKeyPressTime = GetTimeInMillis();

  xEvent x = {0};
  x.u.u.type = KeyPress;
  x.u.u.detail = nxagentConvertKeycode(X -> xkey.keycode);
  x.u.keyButtonPointer.time = nxagentLastKeyPressTime;

  #ifdef TEST
  fprintf(stderr, "%s: passing KeyPress event to clients\n", __func__);
  #endif

  mieqEnqueue(&x);

  CriticalOutputPending = 1;

  return 1;
}

int nxagentHandlePropertyNotify(XEvent *X)
{
  if (nxagentOption(Rootless) && !nxagentNotifyMatchChangeProperty((XPropertyEvent *) X))
  {
    #ifdef TEST
    fprintf(stderr, "nxagentHandlePropertyNotify: Property %ld on window %lx.\n",
                X -> xproperty.atom, X -> xproperty.window);
    #endif

    if (nxagentWindowPtr(X -> xproperty.window) != NULL)
    {
      int resource = NXGetCollectPropertyResource(nxagentDisplay);

      if (resource == -1)
      {
        #ifdef WARNING
        fprintf(stderr, "nxagentHandlePropertyNotify: WARNING! Asynchronous get property queue is full.\n");
        #endif

        return 0;
      }

      NXCollectProperty(nxagentDisplay, resource,
                            X -> xproperty.window, X -> xproperty.atom, 0,
                                MAX_RETRIEVED_PROPERTY_SIZE, False, AnyPropertyType);

      nxagentPropertyRequests[resource].window = X -> xproperty.window;
      nxagentPropertyRequests[resource].property = X -> xproperty.atom;
    }
    #ifdef TEST
    else
    {
      fprintf(stderr, "nxagentHandlePropertyNotify: Failed to look up remote window property.\n");
    }
    #endif
  }

  return 1;
}

int nxagentHandleExposeEvent(XEvent *X)
{
  WindowPtr pWin = NULL;
  Window window = None;

  RegionRec sum;
  RegionRec add;
  BoxRec box;
  int index = 0;
  int overlap = 0;

  StaticResizedWindowStruct *resizedWinPtr = NULL;

  #ifdef DEBUG
  fprintf(stderr, "nxagentHandleExposeEvent: Checking remote expose events.\n");

  fprintf(stderr, "nxagentHandleExposeEvent: Looking for window id [%ld].\n",
              X -> xexpose.window);
  #endif

  window = X -> xexpose.window;

  pWin = nxagentWindowPtr(window);

  if (pWin != NULL)
  {
    RegionInit(&sum, (BoxRec *) NULL, 1);
/*
FIXME: This can be maybe optimized by consuming the
       events that do not match the predicate.
*/
    do
    {
      #ifdef DEBUG
      fprintf(stderr, "nxagentHandleExposeEvent: Adding event for window id [%ld].\n",
                  X -> xexpose.window);
      #endif

      box.x1 = pWin -> drawable.x + wBorderWidth(pWin) + X -> xexpose.x;
      box.y1 = pWin -> drawable.y + wBorderWidth(pWin) + X -> xexpose.y;

      resizedWinPtr = nxagentFindStaticResizedWindow(X -> xany.serial);

      while (resizedWinPtr)
      {
        if (resizedWinPtr -> pWin == pWin)
        {
          box.x1 += resizedWinPtr -> offX;
          box.y1 += resizedWinPtr -> offY;
        }

        resizedWinPtr = resizedWinPtr -> prev;
      }

      box.x2 = box.x1 + X -> xexpose.width;
      box.y2 = box.y1 + X -> xexpose.height;

      RegionInit(&add, &box, 1);

      RegionAppend(&sum, &add);

      RegionUninit(&add);

      if (X -> xexpose.count == 0)
      {
        break;
      }
    }
    while (nxagentCheckEvents(nxagentDisplay, X, nxagentExposurePredicate,
                                  (XPointer) &window) == 1);

    RegionValidate(&sum, &overlap);

    RegionIntersect(&sum, &sum,
                         &pWin->drawable.pScreen->root->winSize);

    #ifdef DEBUG
    fprintf(stderr, "nxagentHandleExposeEvent: Sending events for window id [%ld].\n",
                X -> xexpose.window);
    #endif

    /*
     * If the agent has already sent auto-generated expose,
     * save received exposes for later processing.
     */

    index = nxagentLookupByWindow(pWin);

    if (index == -1)
    {
      miWindowExposures(pWin, &sum, NullRegion);
    }
    else
    {
      RegionTranslate(&sum, -pWin -> drawable.x, -pWin -> drawable.y);

      if (nxagentExposeQueue.exposures[index].remoteRegion == NullRegion)
      {
        nxagentExposeQueue.exposures[index].remoteRegion = RegionCreate(NULL, 1);
      }

      RegionUnion(nxagentExposeQueue.exposures[index].remoteRegion,
                       nxagentExposeQueue.exposures[index].remoteRegion, &sum);

      #ifdef TEST
      fprintf(stderr, "nxagentHandleExposeEvent: Added region for window [%u] to position [%d].\n",
                  nxagentWindow(pWin), index);
      #endif

      if (X -> xexpose.count == 0)
      {
        nxagentExposeQueue.exposures[index].remoteRegionIsCompleted = True;
      }
      else
      {
        nxagentExposeQueue.exposures[index].remoteRegionIsCompleted = False;
      }
    }

    RegionUninit(&sum);
  }

  return 1;
}

int nxagentHandleGraphicsExposeEvent(XEvent *X)
{
  /*
   * Send an expose event to client, instead of graphics
   * expose. If target drawable is a backing pixmap, send
   * expose event for the saved window, else do nothing.
   */

  RegionPtr exposeRegion;
  BoxRec rect;
  WindowPtr pWin;
  StoringPixmapPtr pStoringPixmapRec = NULL;
  miBSWindowPtr pBSwindow = NULL;
  int drawableType;

  pWin = nxagentWindowPtr(X -> xgraphicsexpose.drawable);

  if (pWin != NULL)
  {
    drawableType = DRAWABLE_WINDOW;
  }
  else
  {
    drawableType = DRAWABLE_PIXMAP;
  }

  if (drawableType == DRAWABLE_PIXMAP)
  {
    pStoringPixmapRec = nxagentFindItemBSPixmapList(X -> xgraphicsexpose.drawable);

    if (pStoringPixmapRec == NULL)
    {
      #ifdef TEST
      fprintf(stderr, "nxagentHandleGraphicsExposeEvent: WARNING! Storing pixmap not found.\n");
      #endif

      return 1;
    }

    pBSwindow = (miBSWindowPtr) pStoringPixmapRec -> pSavedWindow -> backStorage;

    if (pBSwindow == NULL)
    {
      #ifdef TEST
      fprintf(stderr, "nxagentHandleGraphicsExposeEvent: WARNING! Back storage not found.\n");
      #endif

      return 1;
    }

    pWin = pStoringPixmapRec -> pSavedWindow;
  }

  /*
   * Rectangle affected by GraphicsExpose
   * event.
   */

  rect.x1 = X -> xgraphicsexpose.x;
  rect.y1 = X -> xgraphicsexpose.y;
  rect.x2 = rect.x1 + X -> xgraphicsexpose.width;
  rect.y2 = rect.y1 + X -> xgraphicsexpose.height;

  exposeRegion = RegionCreate(&rect, 0);

  if (drawableType == DRAWABLE_PIXMAP)
  {
    #ifdef TEST
    fprintf(stderr, "nxagentHandleGraphicsExposeEvent: Handling GraphicsExpose event on pixmap with id"
                " [%lu].\n", X -> xgraphicsexpose.drawable);
    #endif

    /*
     * The exposeRegion coordinates are relative
     * to the pixmap to which GraphicsExpose
     * event refers. But the BS coordinates of
     * the savedRegion  are relative to the
     * window.
     */

    RegionTranslate(exposeRegion, pStoringPixmapRec -> backingStoreX,
                         pStoringPixmapRec -> backingStoreY);

    /*
     * We remove from SavedRegion the part
     * affected by the GraphicsExpose event.
     */

    RegionSubtract(&(pBSwindow -> SavedRegion), &(pBSwindow -> SavedRegion),
                        exposeRegion);
  }

  /*
   * Store the exposeRegion in order to send
   * the expose event later. The coordinates
   * must be relative to the screen.
   */

  RegionTranslate(exposeRegion, pWin -> drawable.x, pWin -> drawable.y);

  RegionUnion(nxagentRemoteExposeRegion, nxagentRemoteExposeRegion, exposeRegion);

  RegionDestroy(exposeRegion);

  return 1;
}

int nxagentHandleClientMessageEvent(XEvent *X, enum HandleEventResult *result)
{
  *result = doNothing;

  #ifdef TEST
  fprintf(stderr, "nxagentHandleClientMessageEvent: ClientMessage event window [%ld] with "
              "type [%ld] format [%d].\n", X -> xclient.window, X -> xclient.message_type,
                  X -> xclient.format);
  #endif

  /*
   * If window is 0, message_type is 0 and format is
   * 32 then we assume event is coming from proxy.
   */

  if (X -> xclient.window == 0 &&
          X -> xclient.message_type == 0 &&
              X -> xclient.format == 32)
  {
    nxagentHandleProxyEvent(X);

    return 1;
  }

  if (nxagentOption(Rootless))
  {
    Atom message_type = nxagentRemoteToLocalAtom(X -> xclient.message_type);

    if (!ValidAtom(message_type))
    {
      #ifdef WARNING
      fprintf(stderr, "nxagentHandleClientMessageEvent: WARNING Invalid type in client message.\n");
      #endif

      return 0;
    }

    WindowPtr pWin = nxagentWindowPtr(X -> xclient.window);

    if (pWin == NULL)
    {
      #ifdef WARNING
      fprintf(stderr, "WARNING: Invalid window in ClientMessage.\n");
      #endif

      return 0;
    }

    if (message_type == MakeAtom("WM_PROTOCOLS", strlen("WM_PROTOCOLS"), False))
    {
      xEvent x;

      memset(&x, 0, sizeof(xEvent));
      x.u.u.type = ClientMessage;
      x.u.u.detail = X -> xclient.format;

      x.u.clientMessage.window = pWin -> drawable.id;
      x.u.clientMessage.u.l.type = message_type;
      x.u.clientMessage.u.l.longs0 = nxagentRemoteToLocalAtom(X -> xclient.data.l[0]);
      x.u.clientMessage.u.l.longs1 = GetTimeInMillis();

      if (!ValidAtom(x.u.clientMessage.u.l.longs0))
      {
        #ifdef WARNING
        fprintf(stderr, "nxagentHandleClientMessageEvent: WARNING Invalid value in client message "
                    "of type WM_PROTOCOLS.\n");
        #endif

        return 0;
      }
      #ifdef TEST
      else
      {
        fprintf(stderr, "nxagentHandleClientMessageEvent: Sent client message of type WM_PROTOCOLS "
                "and value [%s].\n", validateString(NameForAtom(x.u.clientMessage.u.l.longs0)));
      }
      #endif

      TryClientEvents(wClient(pWin), &x, 1, 1, 1, 0);
    }
    else
    {
      #ifdef WARNING
      fprintf(stderr, "nxagentHandleClientMessageEvent: Ignored message type %ld [%s].\n",
                  (long int) message_type, validateString(NameForAtom(message_type)));
      #endif

      return 0;
    }

    return 1;
  }

  if (X -> xclient.message_type == nxagentAtoms[1]) /* WM_PROTOCOLS */
  {
    Atom deleteWMatom, wmAtom;

    wmAtom = (Atom) X -> xclient.data.l[0];

    deleteWMatom = nxagentAtoms[2]; /* WM_DELETE_WINDOW */

    if (wmAtom == deleteWMatom)
    {
      if (nxagentOnce && (nxagentClients == 0))
      {
        GiveUp(0);
      }
      else
      {
        #ifdef TEST
        fprintf(stderr, "Events: WM_DELETE_WINDOW arrived Atom = %u.\n", wmAtom);
        #endif

        if (X -> xclient.window == nxagentIconWindow)
        {
          XMapRaised(nxagentDisplay, nxagentFullscreenWindow);

          XIconifyWindow(nxagentDisplay, nxagentIconWindow,
                             DefaultScreen(nxagentDisplay));

        }

        if (X -> xclient.window == (nxagentOption(Fullscreen) ?
                nxagentIconWindow : nxagentDefaultWindows[0]) ||
                    !nxagentWMIsRunning)
        {
          *result = doCloseSession;
        }
      }
    }
  }

  return 1;
}

int nxagentHandleXkbKeyboardStateEvent(XEvent *X)
{
  XkbEvent *xkbev = (XkbEvent *) X;

  if (nxagentXkbInfo.EventBase != -1 &&
      xkbev -> type == nxagentXkbInfo.EventBase + XkbEventCode &&
          xkbev -> any.xkb_type == XkbStateNotify)
  {
    #ifdef TEST
    fprintf(stderr, "%s: Handling event with caps [%d] num [%d] locked [%d].\n", __func__,
	    nxagentXkbState.Caps, nxagentXkbState.Num, nxagentXkbState.Locked);
    #endif

    nxagentXkbState.Locked = xkbev -> state.locked_mods;

    #ifdef TEST
    fprintf(stderr, "%s: Updated XKB locked modifier bits to [%x].\n", __func__,
                nxagentXkbState.Locked);
    #endif

    nxagentXkbState.Initialized = 1;

    if (nxagentXkbState.Caps == 0 &&
            (nxagentXkbState.Locked & CAPSFLAG_IN_EVENT))
    {
      nxagentXkbState.Caps = 1;

      #ifdef TEST
      fprintf(stderr, "%s: Sending fake key [66] to engage capslock.\n", __func__);
      #endif

      if (!nxagentXkbCapsTrap)
      {
        nxagentSendFakeKey(66);
      }
    }

    if (nxagentXkbState.Caps == 1 &&
          !(nxagentXkbState.Locked & CAPSFLAG_IN_EVENT))
    {
      nxagentXkbState.Caps = 0;

      #ifdef TEST
      fprintf(stderr, "%s: Sending fake key [66] to release capslock.\n", __func__);
      #endif

      nxagentSendFakeKey(66);
    }

    if (nxagentXkbState.Caps == 0 &&
          !(nxagentXkbState.Locked & CAPSFLAG_IN_EVENT) &&
              nxagentXkbCapsTrap)
    {

      #ifdef TEST
      fprintf(stderr, "%s: Sending fake key [66] to release capslock.\n", __func__);
      #endif

      nxagentSendFakeKey(66);
    }

    if (nxagentXkbState.Num == 0 &&
            (nxagentXkbState.Locked & NUMFLAG_IN_EVENT))
    {
      nxagentXkbState.Num = 1;

      #ifdef TEST
      fprintf(stderr, "%s: Sending fake key [77] to engage numlock.\n", __func__);
      #endif

      if (!nxagentXkbNumTrap)
      {
        nxagentSendFakeKey(77);
      }
    }

    if (nxagentXkbState.Num == 1 &&
            !(nxagentXkbState.Locked & NUMFLAG_IN_EVENT))
    {
      nxagentXkbState.Num = 0;

      #ifdef TEST
      fprintf(stderr, "%s: Sending fake key [77] to release numlock.\n", __func__);
      #endif

      nxagentSendFakeKey(77);
    }

    if (nxagentXkbState.Num == 0 &&
          !(nxagentXkbState.Locked & NUMFLAG_IN_EVENT) &&
              nxagentXkbNumTrap)
    {

      #ifdef TEST
      fprintf(stderr, "%s: Sending fake key [77] to release numlock.\n", __func__);
      #endif

      nxagentSendFakeKey(77);
    }

    return 1;
  }

  return 0;
}

int nxagentHandleXFixesSelectionNotify(XEvent *X)
{
  Atom local;

  XFixesSelectionEvent *xfixesEvent = (XFixesSelectionEvent *) X;

  if (nxagentXFixesInfo.Initialized == 0)
  {
      #ifdef DEBUG
      fprintf(stderr, "nxagentHandleXFixesSelectionNotify: XFixes not initialized - doing nothing.\n");
      #endif
      return 0;
  }

  if (xfixesEvent -> type != (nxagentXFixesInfo.EventBase + XFixesSelectionNotify))
  {
      #ifdef DEBUG
      fprintf(stderr, "nxagentHandleXFixesSelectionNotify: event type is [%d] - doing nothing.\n", xfixesEvent->type);
      #endif
      return 0;
  }

  #ifdef TEST
  fprintf(stderr, "nxagentHandleXFixesSelectionNotify: Handling event.\n");
  #endif

  local = nxagentRemoteToLocalAtom(xfixesEvent -> xfixesselection.selection);

  if (SelectionCallback)
  {
    int i = nxagentFindCurrentSelectionIndex(local);
    if (i < NumCurrentSelections)
    {
      SelectionInfoRec    info;

      if (CurrentSelections[i].client != 0)
      {
        #ifdef TEST
        fprintf(stderr, "nxagentHandleXFixesSelectionNotify: Do nothing.\n");
        #endif

        return 1;
      }

      #ifdef TEST
      fprintf(stderr, "nxagentHandleXFixesSelectionNotify: Calling callbacks for %d [%s] selection.\n",
                       CurrentSelections[i].selection, NameForAtom(CurrentSelections[i].selection));
      #endif

      #ifdef DEBUG
      fprintf(stderr, "%s: CurrentSelections[i].lastTimeChanged [%d]\n", __func__, CurrentSelections[i].lastTimeChanged.milliseconds);
      fprintf(stderr, "%s: Event timestamp [%ld]\n", __func__, xfixesEvent->xfixesselection.timestamp);
      fprintf(stderr, "%s: Event selection timestamp [%ld]\n", __func__, xfixesEvent->xfixesselection.selection_timestamp);
      fprintf(stderr, "%s: Event selection window [0x%x]\n", __func__, xfixesEvent->xfixesselection.window);
      fprintf(stderr, "%s: Event selection owner [0x%x]\n", __func__, xfixesEvent->xfixesselection.owner);
      fprintf(stderr, "%s: Event selection [%s]\n", __func__, NameForAtom(nxagentRemoteToLocalAtom(xfixesEvent->xfixesselection.selection)));

      fprintf(stderr, "nxagentHandleXFixesSelectionNotify: Subtype ");

      switch (xfixesEvent -> xfixesselection.subtype)
      {
        case SelectionSetOwner:
          fprintf(stderr, "SelectionSetOwner.\n");
          break;
        case SelectionWindowDestroy:
          fprintf(stderr, "SelectionWindowDestroy.\n");
          break;
        case SelectionClientClose:
          fprintf(stderr, "SelectionClientClose.\n");
          break;
        default:
          fprintf(stderr, ".\n");
          break;
      }
      #endif

      info.selection = &CurrentSelections[i];
      info.kind = xfixesEvent->xfixesselection.subtype;

      /*
       * The trap indicates that we are triggered by a clipboard event
       * originating from the real X server. In that case we do not
       * want to propagate back changes to the real X server, because
       * it already knows about them and we would end up in an
       * infinite loop of events. If there was a better way to
       * identify that situation during Callback processing we could
       * get rid of the Trap...
       */
      nxagentExternalClipboardEventTrap = 1;
      CallCallbacks(&SelectionCallback, &info);
      nxagentExternalClipboardEventTrap = 0;
    }
  }
  return 1;
}

int nxagentHandleProxyEvent(XEvent *X)
{
  switch (X -> xclient.data.l[0])
  {
    case NXNoSplitNotify:
    case NXStartSplitNotify:
    {
      /*
       * We should never receive such events
       * in the event loop, as they should
       * be caught at the time the split is
       * initiated.
       */

      #ifdef PANIC

      int client = (int) X -> xclient.data.l[1];

      if (X -> xclient.data.l[0] == NXNoSplitNotify)
      {
        fprintf(stderr, "nxagentHandleProxyEvent: PANIC! NXNoSplitNotify received "
                    "with client [%d].\n", client);
      }
      else
      {
        fprintf(stderr, "nxagentHandleProxyEvent: PANIC! NXStartSplitNotify received "
                    "with client [%d].\n", client);
      }

      #endif

      return 1;
    }
    case NXCommitSplitNotify:
    {
      /*
       * We need to commit an image. Image can be the
       * result of a PutSubImage() generated by Xlib,
       * so there can be more than a single image to
       * commit, even if only one PutImage was perfor-
       * med by the agent.
       */

      int client   = (int) X -> xclient.data.l[1];
      int request  = (int) X -> xclient.data.l[2];
      int position = (int) X -> xclient.data.l[3];

      #ifdef TEST
      fprintf(stderr, "nxagentHandleProxyEvent: NXCommitSplitNotify received with "
                  "client [%d] request [%d] and position [%d].\n",
                      client, request, position);
      #endif

      nxagentHandleCommitSplitEvent(client, request, position);

      return 1;
    }
    case NXEndSplitNotify:
    {
      /*
       * All images for the split were transferred and
       * we need to restart the client.
       */

      int client = (int) X -> xclient.data.l[1];

      #ifdef TEST
      fprintf(stderr, "nxagentHandleProxyEvent: NXEndSplitNotify received with "
                  "client [%d].\n", client);
      #endif

      nxagentHandleEndSplitEvent(client);

      return 1;
    }
    case NXEmptySplitNotify:
    {
      /*
       * All splits have been completed and none remain.
       */

      #ifdef TEST
      fprintf(stderr, "nxagentHandleProxyEvent: NXEmptySplitNotify received.\n");
      #endif

      nxagentHandleEmptySplitEvent();

      return 1;
    }
    case NXCollectPropertyNotify:
    {
      #ifdef TEST
      int resource = (int) X -> xclient.data.l[1];

      fprintf(stderr, "nxagentHandleProxyEvent: NXCollectPropertyNotify received with resource [%d].\n",
                  resource);
      #endif

      nxagentHandleCollectPropertyEvent(X);

      return 1;
    }
    case NXCollectGrabPointerNotify:
    {
      int resource = (int) X -> xclient.data.l[1];

      #ifdef TEST
      fprintf(stderr, "nxagentHandleProxyEvent: NXCollectGrabPointerNotify received with resource [%d].\n",
                  resource);
      #endif

      nxagentHandleCollectGrabPointerEvent(resource);

      return 1;
    }
    case NXCollectInputFocusNotify:
    {
      int resource = (int) X -> xclient.data.l[1];

      /*
       * This is not used at the present moment.
       */

      #ifdef TEST
      fprintf(stderr, "nxagentHandleProxyEvent: NXCollectInputFocusNotify received with resource [%d].\n",
                  resource);
      #endif

      nxagentHandleCollectInputFocusEvent(resource);

      return 1;
    }
    default:
    {
      /*
       *  Not a recognized ClientMessage event.
       */

      #ifdef WARNING
      fprintf(stderr, "nxagentHandleProxyEvent: WARNING! Not a recognized ClientMessage proxy event [%d].\n",
                  (int) X -> xclient.data.l[0]);
      #endif

      return 0;
    }
  }
}

/*
 * In this function it is assumed that we never
 * get a configure with both stacking order and
 * geometry changed, this way we can ignore
 * stacking changes if the geometry has changed.
 */

int nxagentCheckWindowConfiguration(XConfigureEvent* X)
{
  static int x = 0;
  static int y = 0;
  static int width = 0;
  static int height = 0;
  static Window win = None;
  Bool geometryChanged = False;

  XlibWindow root_return = 0;
  XlibWindow parent_return = 0;
  XlibWindow *children_return = NULL;
  unsigned int nchildren_return = 0;
  Status result;

  if (win == X -> window)
  {
    if (x != X -> x ||
            y != X -> y ||
                width != X -> width ||
                    height != X -> height)
    {
      geometryChanged = True;
    }
  }

  win = X -> window;

  x = X -> x;
  y = X -> y;
  width = X -> width;
  height = X -> height;

  if (geometryChanged)
  {
    #ifdef TEST
    fprintf(stderr, "nxagentCheckWindowConfiguration: Configure frame. No restack.\n");
    #endif

    return 1;
  }

  #ifdef TEST
  {
    fprintf(stderr, "nxagentCheckWindowConfiguration: Before restacking top level window [%p]\n",
                (void *) nxagentWindowPtr(X -> window));

    for (WindowPtr pSib = screenInfo.screens[0]->root -> firstChild; pSib; pSib = pSib -> nextSib)
    {
      fprintf(stderr, "nxagentCheckWindowConfiguration: Top level window: [%p].\n",
                  (void *) pSib);
    }
  }
  #endif

  result = XQueryTree(nxagentDisplay, DefaultRootWindow(nxagentDisplay),
                          &root_return, &parent_return, &children_return, &nchildren_return);

  if (result)
  {
    nxagentRootlessRestack(children_return, nchildren_return);
  }
  else
  {
    #ifdef WARNING
    fprintf(stderr, "nxagentCheckWindowConfiguration: WARNING! Failed QueryTree request.\n");
    #endif
  }

  SAFE_XFree(children_return);

  #if 0
  fprintf(stderr, "nxagentCheckWindowConfiguration: Trees match: %s\n",
              nxagentRootlessTreesMatch() ? "Yes" : "No");
  #endif

  return 1;
}

int nxagentHandleConfigureNotify(XEvent* X)
{
  if (nxagentOption(Rootless) == True)
  {
    ClientPtr pClient;
    WindowPtr pWinWindow;
    WindowPtr pWin;
    int sendEventAnyway = 0;

    pWinWindow = nxagentWindowPtr(X -> xconfigure.window);

    #ifdef TEST
    {
      WindowPtr pWinEvent  = nxagentWindowPtr(X -> xconfigure.event);

      fprintf(stderr, "nxagentHandleConfigureNotify: Generating window is [%p][%ld] target [%p][%ld].\n",
                  (void *) pWinEvent, X -> xconfigure.event, (void *) pWinWindow, X -> xconfigure.window);
    }
    #endif

    #ifdef TEST
    fprintf(stderr, "nxagentHandleConfigureNotify: New configuration for window [%p][%ld] is [%d][%d][%d][%d] "
                "send_event [%i].\n", (void *) pWinWindow, X -> xconfigure.window,
                    X -> xconfigure.x, X -> xconfigure.y, X -> xconfigure.width,
                        X -> xconfigure.height, X -> xconfigure.send_event);
    #endif

    if ((pWin = nxagentRootlessTopLevelWindow(X -> xconfigure.window)) != NULL)
    {
      /*
       * Checking for new geometry or stacking order changes.
       */

      nxagentCheckWindowConfiguration((XConfigureEvent*)X);

      return 1;
    }

    if (nxagentWindowTopLevel(pWinWindow) && !X -> xconfigure.override_redirect)
    {
      XID values[5];
      Mask mask = 0;

      register XID *value = values;

      pClient = wClient(pWinWindow);

      /* FIXME: override_redirect is always FALSE here */
      if (X -> xconfigure.send_event || !nxagentWMIsRunning ||
                X -> xconfigure.override_redirect)
      {
        *value++ = (XID)X -> xconfigure.x;
        *value++ = (XID)X -> xconfigure.y;

        /*
         * nxagentWindowPriv(pWinWindow)->x = X -> xconfigure.x;
         * nxagentWindowPriv(pWinWindow)->y = X -> xconfigure.y;
         */

        mask |= CWX | CWY;
      }

      *value++ = (XID)X -> xconfigure.width;
      *value++ = (XID)X -> xconfigure.height;
      *value++ = (XID)X -> xconfigure.border_width;

      /*
       * We don't need width and height here.
       *
       * nxagentWindowPriv(pWinWindow)->width = X -> xconfigure.width;
       * nxagentWindowPriv(pWinWindow)->height = X -> xconfigure.height;
       */

      mask |= CWHeight | CWWidth | CWBorderWidth;

      nxagentScreenTrap = 1;

      ConfigureWindow(pWinWindow, mask, (XID *) values, pClient);

      nxagentScreenTrap = 0;

      nxagentCheckWindowConfiguration((XConfigureEvent*)X);

      /*
       * This workaround should help with
       * Java 1.6.0 that seems to ignore
       * non-synthetic events.
       */

      if (nxagentOption(ClientOs) == ClientOsWinnt)
      {
        #ifdef TEST
        fprintf(stderr, "nxagentHandleConfigureNotify: Apply workaround for NXWin.\n");
        #endif

        sendEventAnyway = 1;
      }

      if (sendEventAnyway || X -> xconfigure.send_event)
      {
        xEvent x;

        memset(&x, 0, sizeof(xEvent));
        x.u.u.type = X -> xconfigure.type;
        x.u.u.type |= 0x80;

        x.u.configureNotify.event = pWinWindow -> drawable.id;
        x.u.configureNotify.window = pWinWindow -> drawable.id;

        if (pWinWindow -> nextSib)
        {
          x.u.configureNotify.aboveSibling = pWinWindow -> nextSib -> drawable.id;
        }
        else
        {
          x.u.configureNotify.aboveSibling = None;
        }

        x.u.configureNotify.x = X -> xconfigure.x;
        x.u.configureNotify.y = X -> xconfigure.y;
        x.u.configureNotify.width = X -> xconfigure.width;
        x.u.configureNotify.height = X -> xconfigure.height;
        x.u.configureNotify.borderWidth = X -> xconfigure.border_width;
        x.u.configureNotify.override = X -> xconfigure.override_redirect;

        TryClientEvents(pClient, &x, 1, 1, 1, 0);
      }

      return 1;
    }
  }
  else
  {
    /*
     * Save the position of the agent default window. Don't
     * save the values if the agent is in fullscreen mode.
     *
     * If we use these values to restore the position of a
     * window after that we have dynamically changed the
     * fullscreen attribute, depending on the behaviour of
     * window manager, we could be not able to place the
     * window exactly in the requested position, so let the
     * window manager do the job for us.
     */

    ScreenPtr pScreen = nxagentScreen(X -> xconfigure.window);

    Bool doRandR = False;

    if (X -> xconfigure.window == nxagentDefaultWindows[pScreen -> myNum])
    {
      if (nxagentOption(AllScreens) == 0)
      {
        /*
         * - WITHOUT window manager any position change is relevant
         * - WITH window manager only synthetic position changes send
         *   by the window manager are relevant, see ICCCM Chapter 4,
         *   "Configuring the Window"
         */
        Bool updatePos = (!nxagentWMIsRunning || X -> xconfigure.send_event != 0);
        int newX = X -> xconfigure.x;
        int newY = X -> xconfigure.y;

        if (nxagentOption(DesktopResize) == 1)
        {
          if (nxagentOption(Width) != X -> xconfigure.width ||
                nxagentOption(Height) != X -> xconfigure.height ||
                  (updatePos && (nxagentOption(X) != newX ||
                                   nxagentOption(Y) != newY)))
          {
            #ifdef DEBUG
            int count = 0;
            #endif
            Bool newEvents = False;

            doRandR = True;

            NXFlushDisplay(nxagentDisplay, NXFlushLink);

            do
            {
              newEvents = False;

              nxagentWaitEvents(nxagentDisplay, 500);

              /*
               * This should also flush the NX link for us.
               */

              XSync(nxagentDisplay, 0);

              while (XCheckTypedWindowEvent(nxagentDisplay, nxagentDefaultWindows[pScreen -> myNum],
                                              ConfigureNotify, X))
              {
                #ifdef DEBUG
                count++;
                #endif

                if (!nxagentWMIsRunning || X -> xconfigure.send_event)
                {
                  updatePos = True;
                  newX = X -> xconfigure.x;
                  newY = X -> xconfigure.y;
                }
                newEvents = True;
              }

            } while (newEvents);

            #ifdef DEBUG
            fprintf(stderr, "%s: accumulated %d events\n", __func__, count);
            #endif
          }
        }

        if (updatePos)
        {
          #ifdef DEBUG
          fprintf(stderr, "%s: Updating nxagent window position [%d,%d]\n", __func__, newX, newY);
          #endif
          nxagentChangeOption(X, newX);
          nxagentChangeOption(Y, newY);
        }

        if (nxagentOption(Shadow) == 1 && nxagentOption(DesktopResize) == 1 &&
                (nxagentOption(Width) != X -> xconfigure.width ||
                    nxagentOption(Height) != X -> xconfigure.height))
        {
          nxagentShadowResize = 1;
        }

        nxagentChangeOption(Width, X -> xconfigure.width);
        nxagentChangeOption(Height, X -> xconfigure.height);

        nxagentChangeOption(ViewportXSpan, (int) X -> xconfigure.width -
                                (int) nxagentOption(RootWidth));
        nxagentChangeOption(ViewportYSpan, (int) X -> xconfigure.height -
                                (int) nxagentOption(RootHeight));

        nxagentMoveViewport(pScreen, 0, 0);

        /* if in shadowing mode or if neither size nor position have
           changed we do not need to adjust RandR */
        /* FIXME: Comment makes no sense */
        if (nxagentOption(Shadow) == 1 ||
                (nxagentOption(Width) == nxagentOption(RootWidth) &&
		 nxagentOption(Height) == nxagentOption(RootHeight) &&
		 nxagentOption(X) == nxagentOption(RootX) &&
		 nxagentOption(Y) == nxagentOption(RootY)))
        {
          doRandR = False;
        }

        nxagentChangeOption(Width, X -> xconfigure.width);
        nxagentChangeOption(Height, X -> xconfigure.height);

        XMoveResizeWindow(nxagentDisplay, nxagentInputWindows[0], 0, 0,
                              X -> xconfigure.width, X -> xconfigure.height);

        if (nxagentOption(Fullscreen) == 0)
        {
          nxagentMoveViewport(pScreen, 0, 0);
        }
        else
        {
          nxagentChangeOption(RootX, (nxagentOption(Width) -
                                  nxagentOption(RootWidth)) / 2);
          nxagentChangeOption(RootY, (nxagentOption(Height) -
                                  nxagentOption(RootHeight)) / 2);
          nxagentChangeOption(ViewportXSpan, nxagentOption(Width) -
                                  nxagentOption(RootWidth));
          nxagentChangeOption(ViewportYSpan, nxagentOption(Height) -
                                  nxagentOption(RootHeight));

          nxagentUpdateViewportFrame(0, 0, nxagentOption(RootWidth),
                                         nxagentOption(RootHeight));

          XMoveWindow(nxagentDisplay, nxagentWindow(pScreen->root),
                          nxagentOption(RootX), nxagentOption(RootY));
        }

        if (doRandR)
        {
          #ifdef TEST
          fprintf(stderr,"nxagentHandleConfigureNotify: Width %d Height %d.\n",
                      nxagentOption(Width), nxagentOption(Height));
          #endif

          nxagentChangeScreenConfig(0, nxagentOption(Width),
                                       nxagentOption(Height));
        }
      }

      return 1;
    }
    else
    {
      if ( (X -> xconfigure.window == DefaultRootWindow(nxagentDisplay)) || nxagentFullscreenWindow )
      {
        #ifdef TEST
        fprintf(stderr, "nxagentHandleConfigureNotify: remote root window has changed: %d,%d %dx%d\n", X -> xconfigure.x, X -> xconfigure.y, X -> xconfigure.width, X -> xconfigure.height);
        #endif

        nxagentChangeOption(RootX, X -> xconfigure.x);
        nxagentChangeOption(RootY, X -> xconfigure.y);
        nxagentChangeOption(RootWidth, X -> xconfigure.width);
        nxagentChangeOption(RootHeight, X -> xconfigure.height);

        nxagentChangeScreenConfig(0, nxagentOption(Width),
                                     nxagentOption(Height));

        return 1;
      }
    }
  }

  return 0;
}

int nxagentHandleReparentNotify(XEvent* X)
{
  #ifdef TEST
  fprintf(stderr, "nxagentHandleReparentNotify: Going to handle a new reparent event.\n");
  #endif

  if (nxagentOption(Rootless))
  {
    XlibWindow w;
    XlibWindow root_return = 0;
    XlibWindow parent_return = 0;
    XlibWindow *children_return = NULL;
    unsigned int nchildren_return = 0;
    Status result;
    WindowPtr pWin = nxagentWindowPtr(X -> xreparent.window);

    #ifdef TEST

    {
      WindowPtr pParent = nxagentWindowPtr(X -> xreparent.parent);
      WindowPtr pEvent = nxagentWindowPtr(X -> xreparent.event);

      fprintf(stderr, "nxagentHandleReparentNotify: event %p[%lx] window %p[%lx] parent %p[%lx] at (%d, %d)\n",
                  (void*)pEvent, X -> xreparent.event, (void*)pWin, X -> xreparent.window,
                          (void*)pParent, X -> xreparent.parent, X -> xreparent.x, X -> xreparent.y);
    }

    #endif

    if (nxagentWindowTopLevel(pWin))
    {
      /*
       * If the window manager reparents our top level
       * window, we need to know the new top level
       * ancestor.
       */

      w = None;
      parent_return = X -> xreparent.parent;

      while (parent_return != RootWindow(nxagentDisplay, 0))
      {
        w = parent_return;
        result = XQueryTree(nxagentDisplay, w, &root_return,
                                &parent_return, &children_return, &nchildren_return);

        SAFE_XFree(children_return);

        if (!result)
        {
          #ifdef WARNING
          fprintf(stderr, "nxagentHandleReparentNotify: WARNING! Failed QueryTree request.\n");
          #endif

          break;
        }
      }

      if (w && !nxagentWindowPtr(w))
      {
        XSelectInput(nxagentDisplay, w, StructureNotifyMask);

        nxagentRootlessAddTopLevelWindow(pWin, w);

        #ifdef TEST
        fprintf(stderr, "nxagentHandleReparentNotify: new top level window [%ld].\n", w);
        fprintf(stderr, "nxagentHandleReparentNotify: reparented window [%ld].\n",
                    X -> xreparent.window);
        #endif

        result = XQueryTree(nxagentDisplay, DefaultRootWindow(nxagentDisplay),
                                &root_return, &parent_return, &children_return, &nchildren_return);

        if (result)
        {
          nxagentRootlessRestack(children_return, nchildren_return);
        }
        else
        {
          #ifdef WARNING
          fprintf(stderr, "nxagentHandleReparentNotify: WARNING! Failed QueryTree request.\n");
          #endif
        }

        SAFE_XFree(children_return);
      }
      else
      {
        #ifdef TEST
        fprintf(stderr, "nxagentHandleReparentNotify: Window at [%p] has been reparented to [%ld]"
                    " top level parent [%ld].\n", (void *) pWin, X -> xreparent.parent, w);
        #endif

        nxagentRootlessDelTopLevelWindow(pWin);
      }
    }

    return 1;
  }
  else if (nxagentWMIsRunning && nxagentOption(Fullscreen) == 0 &&
               nxagentOption(WMBorderWidth) == -1)
  {
    XlibWindow w;
    XlibWindow rootReturn = 0;
    XlibWindow parentReturn = 0;
    XlibWindow junk;
    XlibWindow *childrenReturn = NULL;
    unsigned int nchildrenReturn = 0;
    XWindowAttributes attributes;
    int x, y;

    /*
     * Calculate the absolute upper-left X e Y
     */

    if ((XGetWindowAttributes(nxagentDisplay, X -> xreparent.window,
                                  &attributes) == 0))
    {
      #ifdef WARNING
      fprintf(stderr, "nxagentHandleReparentNotify: WARNING! "
                  "XGetWindowAttributes failed.\n");
      #endif

      return 1;
    }

    x = attributes.x;
    y = attributes.y;

    XTranslateCoordinates(nxagentDisplay, X -> xreparent.window,
                              attributes.root, -attributes.border_width,
                                  -attributes.border_width, &x, &y, &junk);

   /*
    * Calculate the parent X and parent Y.
    */

    w = X -> xreparent.parent;

    if (w != DefaultRootWindow(nxagentDisplay))
    {
      int xParent, yParent;

      do
      {
        Status result = XQueryTree(nxagentDisplay, w, &rootReturn, &parentReturn,
                                       &childrenReturn, &nchildrenReturn);

        SAFE_XFree(childrenReturn);

        if (parentReturn == rootReturn || parentReturn == 0 || result == 0)
        {
          break;
        }

        w = parentReturn;
      }
      while (True);

      /*
       * WM reparented. Find edge of the frame.
       */

      if (XGetWindowAttributes(nxagentDisplay, w, &attributes) == 0)
      {
        #ifdef WARNING
        fprintf(stderr, "nxagentHandleReparentNotify: WARNING! "
                    "XGetWindowAttributes failed for parent window.\n");
        #endif

        return 1;
      }

      xParent = attributes.x;
      yParent = attributes.y;

      /*
       * Difference between Absolute X and Parent X gives thickness of side frame.
       * Difference between Absolute Y and Parent Y gives thickness of title bar.
       */

      nxagentChangeOption(WMBorderWidth, (x - xParent));
      nxagentChangeOption(WMTitleHeight, (y - yParent));
    }
  }

  return 1;
}

/*
 * Helper for nxagent(Enable|Disable)(Keyboard|Pointer)Events
 */
static void nxagentSwitchEventsAllScreens(Mask mask, Bool enable)
{
  Mask newmask = nxagentGetDefaultEventMask();

  if (enable)
    newmask |= mask;
  else
    newmask &= ~mask;

  nxagentSetDefaultEventMask(newmask);

  for (int i = 0; i < nxagentNumScreens; i++)
  {
    XSelectInput(nxagentDisplay, nxagentDefaultWindows[i], newmask);
  }
}

void nxagentEnableKeyboardEvents(void)
{
  nxagentSwitchEventsAllScreens(NXAGENT_KEYBOARD_EVENT_MASK, True);

  XkbSelectEvents(nxagentDisplay, XkbUseCoreKbd,
                      NXAGENT_KEYBOARD_EXTENSION_EVENT_MASK,
                          NXAGENT_KEYBOARD_EXTENSION_EVENT_MASK);
}

void nxagentDisableKeyboardEvents(void)
{
  nxagentSwitchEventsAllScreens(NXAGENT_KEYBOARD_EVENT_MASK, False);

  XkbSelectEvents(nxagentDisplay, XkbUseCoreKbd, 0x0, 0x0);
}

void nxagentEnablePointerEvents(void)
{
  nxagentSwitchEventsAllScreens(NXAGENT_POINTER_EVENT_MASK, True);
}

void nxagentDisablePointerEvents(void)
{
  nxagentSwitchEventsAllScreens(NXAGENT_POINTER_EVENT_MASK, False);
}

void nxagentSendFakeKey(int key)
{
  xEvent fake;
  Time   now;

  now = GetTimeInMillis();

  memset(&fake, 0, sizeof(xEvent));
  fake.u.u.type = KeyPress;
  fake.u.u.detail = key;
  fake.u.keyButtonPointer.time = now;

  mieqEnqueue(&fake);

  fake.u.u.type = KeyRelease;
  fake.u.u.detail = key;
  fake.u.keyButtonPointer.time = now;

  mieqEnqueue(&fake);
}

int nxagentInitXkbKeyboardState(void)
{
  XEvent X;

  unsigned int modifiers;

  XkbEvent *xkbev = (XkbEvent *) &X;

  if (nxagentXkbInfo.EventBase == -1)
  {
      return 1;
  }

  #ifdef TEST
  fprintf(stderr, "%s: Initializing XKB state.\n", __func__);
  #endif

  memset(&X, 0, sizeof(XEvent));

  XkbGetIndicatorState(nxagentDisplay, XkbUseCoreKbd, &modifiers);

  xkbev -> state.locked_mods = 0x0;

  if (modifiers & CAPSFLAG_IN_REPLY)
  {
    xkbev -> state.locked_mods |= CAPSFLAG_IN_EVENT;
  }

  if (modifiers & NUMFLAG_IN_REPLY)
  {
    xkbev -> state.locked_mods |= NUMFLAG_IN_EVENT;
  }

  #ifdef TEST
  fprintf(stderr, "%s: Assuming XKB locked modifier bits [%x].\n", __func__,
              xkbev -> state.locked_mods);
  #endif

  xkbev -> type         = nxagentXkbInfo.EventBase + XkbEventCode;
  xkbev -> any.xkb_type = XkbStateNotify;

  nxagentHandleXkbKeyboardStateEvent(&X);

  return 1;
}

int nxagentWaitForResource(GetResourceFuncPtr pGetResource, PredicateFuncPtr pPredicate)
{
  int resource;

  while ((resource = (*pGetResource)(nxagentDisplay)) == -1)
  {
    if (nxagentWaitEvents(nxagentDisplay, 0) == -1)
    {
      return -1;
    }

    nxagentDispatchEvents(pPredicate);
  }

  return resource;
}

void nxagentGrabPointerAndKeyboard(XEvent *X)
{
  unsigned long now;

  int resource;

  int result;

  #ifdef TEST
  fprintf(stderr, "nxagentGrabPointerAndKeyboard: Grabbing pointer and keyboard with event at [%p].\n",
              (void *) X);
  #endif

  if (X != NULL)
  {
    now = X -> xcrossing.time;
  }
  else
  {
    now = CurrentTime;
  }

  #ifdef TEST
  fprintf(stderr, "nxagentGrabPointerAndKeyboard: Going to grab the keyboard in context [B1].\n");
  #endif

  if (nxagentFullscreenWindow)
    result = XGrabKeyboard(nxagentDisplay, nxagentFullscreenWindow,
      True, GrabModeAsync, GrabModeAsync, now);
  else
    result = XGrabKeyboard(nxagentDisplay, RootWindow(nxagentDisplay, DefaultScreen(nxagentDisplay)),
      True, GrabModeAsync, GrabModeAsync, now);

  if (result != GrabSuccess)
  {
    #ifdef DEBUG
    fprintf(stderr, "%s: keyboard grab failed.\n", __func__);
    #endif
    return;
  }
  #ifdef DEBUG
  else
  {
    fprintf(stderr, "%s: keyboard grab successful.\n", __func__);
  }
  #endif

  /*
   * The smart scheduler could be stopped while
   * waiting for the reply. In this case we need
   * to yield explicitly to avoid to be stuck in
   * the dispatch loop forever.
   */

  isItTimeToYield = 1;

  #ifdef TEST
  fprintf(stderr, "nxagentGrabPointerAndKeyboard: Going to grab the pointer in context [B2].\n");
  #endif

  resource = nxagentWaitForResource(NXGetCollectGrabPointerResource,
                                        nxagentCollectGrabPointerPredicate);

  if (nxagentFullscreenWindow)
     NXCollectGrabPointer(nxagentDisplay, resource,
                           nxagentFullscreenWindow, True, NXAGENT_POINTER_EVENT_MASK,
                               GrabModeAsync, GrabModeAsync, None, None, now);

  /*
   * This should not be needed.
   *
   * XGrabKey(nxagentDisplay, AnyKey, AnyModifier, nxagentFullscreenWindow,
   *              True, GrabModeAsync, GrabModeAsync);
   */

  if (X != NULL)
  {
    #ifdef TEST
    fprintf(stderr, "nxagentGrabPointerAndKeyboard: Going to force focus in context [B4].\n");
    #endif

    XSetInputFocus(nxagentDisplay, nxagentFullscreenWindow,
                       RevertToParent, now);
  }
}

void nxagentUngrabPointerAndKeyboard(XEvent *X)
{
  unsigned long now;

  #ifdef TEST
  fprintf(stderr, "nxagentUngrabPointerAndKeyboard: Ungrabbing pointer and keyboard with event at [%p].\n",
              (void *) X);
  #endif

  if (X != NULL)
  {
    now = X -> xcrossing.time;
  }
  else
  {
    now = CurrentTime;
  }

  #ifdef TEST
  fprintf(stderr, "nxagentUngrabPointerAndKeyboard: Going to ungrab the keyboard in context [B5].\n");
  #endif

  XUngrabKeyboard(nxagentDisplay, now);

  #ifdef TEST
  fprintf(stderr, "nxagentUngrabPointerAndKeyboard: Going to ungrab the pointer in context [B6].\n");
  #endif

  XUngrabPointer(nxagentDisplay, now);
}

void nxagentDeactivatePointerGrab(void)
{
  GrabPtr grab = inputInfo.pointer -> grab;

  if (grab)
  {
    XButtonEvent X = {
      .type = ButtonRelease,
      .serial = 0,
      .send_event = FALSE,
      .time = currentTime.milliseconds,
      .display = nxagentDisplay,
      .window = nxagentWindow(grab -> window),
      .root = RootWindow(nxagentDisplay, 0),
      .subwindow = 0,
      .x = 0,
      .y = 0,
      .x_root = 0,
      .y_root = 0,
      .state = 0x100,
      .button = 1,
      .same_screen = TRUE,
    };

    XPutBackEvent(nxagentDisplay, (XEvent*)&X);
  }
}

Bool nxagentCollectGrabPointerPredicate(Display *display, XEvent *X, XPointer ptr)
{
  return (X -> xclient.window == 0 &&
             X -> xclient.message_type == 0 &&
                 X -> xclient.format == 32 &&
                     X -> xclient.data.l[0] == NXCollectGrabPointerNotify);
}

void nxagentHandleCollectGrabPointerEvent(int resource)
{
  int status;

  if (NXGetCollectedGrabPointer(nxagentDisplay, resource, &status) == 0)
  {
    #ifdef PANIC
    fprintf(stderr, "nxagentHandleCollectGrabPointerEvent: PANIC! Failed to get GrabPointer "
                "reply for resource [%d].\n", resource);
    #endif
  }
}

void nxagentHandleCollectPropertyEvent(XEvent *X)
{
  Window window;
  Atom property;
  Atom atomReturnType;
  int resultFormat;
  unsigned long ulReturnItems;
  unsigned long ulReturnBytesLeft;
  unsigned char *pszReturnData = NULL;
  int result;
  int resource = X -> xclient.data.l[1];

  if (X -> xclient.data.l[2] == False)
  {
    #ifdef DEBUG
    fprintf (stderr, "nxagentHandleCollectPropertyEvent: Failed to get reply data for client [%d].\n",
                 resource);
    #endif

    return;
  }

  if (resource == nxagentLastClipboardClient)
  {
    nxagentCollectPropertyEvent(resource);
  }
  else
  {
    result = NXGetCollectedProperty(nxagentDisplay,
                                    resource,
                                    &atomReturnType,
                                    &resultFormat,
                                    &ulReturnItems,
                                    &ulReturnBytesLeft,
                                    &pszReturnData);

    if (result == True)
    {
      window = nxagentPropertyRequests[resource].window;
      property = nxagentPropertyRequests[resource].property;

      nxagentImportProperty(window, property, atomReturnType, resultFormat,
                                ulReturnItems, ulReturnBytesLeft, pszReturnData);
    }
    else
    {
      #ifdef DEBUG
      fprintf (stderr, "nxagentHandleCollectPropertyEvent: Failed to get reply data for client [%d].\n",
                   resource);
      #endif
    }

    SAFE_XFree(pszReturnData);

    return;
  }
}

void nxagentSynchronizeExpose(void)
{
  WindowPtr pWin;

  if (nxagentExposeQueue.length <= 0)
  {
    #ifdef TEST
    fprintf(stderr, "nxagentSynchronizeExpose: PANIC! Called with nxagentExposeQueue.length [%d].\n",
                nxagentExposeQueue.length);
    #endif

    return;
  }

  pWin = nxagentExposeQueueHead.pWindow;

  if (pWin)
  {
    if ((nxagentExposeQueueHead.localRegion) != NullRegion)
    {
      RegionTranslate((nxagentExposeQueueHead.localRegion),
                           pWin -> drawable.x, pWin -> drawable.y);
    }

    if ((nxagentExposeQueueHead.remoteRegion) != NullRegion)
    {
      RegionTranslate((nxagentExposeQueueHead.remoteRegion),
                           pWin -> drawable.x, pWin -> drawable.y);
    }

    if ((nxagentExposeQueueHead.localRegion) != NullRegion &&
             (nxagentExposeQueueHead.remoteRegion) != NullRegion)
    {
      RegionSubtract((nxagentExposeQueueHead.remoteRegion),
                          (nxagentExposeQueueHead.remoteRegion),
                              (nxagentExposeQueueHead.localRegion));

      if (RegionNil(nxagentExposeQueueHead.remoteRegion) == 0 &&
             ((pWin -> eventMask|wOtherEventMasks(pWin)) & ExposureMask))
      {
        #ifdef TEST
        fprintf(stderr, "nxagentSynchronizeExpose: Going to call miWindowExposures"
                    " for window [%d] - rects [%d].\n", nxagentWindow(pWin),
                        RegionNumRects(nxagentExposeQueueHead.remoteRegion));
        #endif

        miWindowExposures(pWin, nxagentExposeQueueHead.remoteRegion, NullRegion);
      }
    }
  }

  nxagentExposeQueueHead.pWindow = NULL;

  if (nxagentExposeQueueHead.localRegion != NullRegion)
  {
    RegionDestroy(nxagentExposeQueueHead.localRegion);
  }

  nxagentExposeQueueHead.localRegion = NullRegion;

  if (nxagentExposeQueueHead.remoteRegion != NullRegion)
  {
    RegionDestroy(nxagentExposeQueueHead.remoteRegion);
  }

  nxagentExposeQueueHead.remoteRegion = NullRegion;

  nxagentExposeQueueHead.remoteRegionIsCompleted = False;

  nxagentExposeQueue.start = (nxagentExposeQueue.start + 1) % EXPOSED_SIZE;

  nxagentExposeQueue.length--;

  return;
}

int nxagentLookupByWindow(WindowPtr pWin)
{
  for (int j = 0; j < nxagentExposeQueue.length; j++)
  {
    int i = (nxagentExposeQueue.start + j) % EXPOSED_SIZE;

    if (nxagentExposeQueue.exposures[i].pWindow == pWin &&
            !nxagentExposeQueue.exposures[i].remoteRegionIsCompleted)
    {
      return i;
    }
  }

  return -1;
}

void nxagentRemoveDuplicatedKeys(XEvent *X)
{
  _XQEvent *qelt = nxagentDisplay -> head;

  KeyCode lastKeycode = X -> xkey.keycode;

  if (qelt == NULL)
  {
    #ifdef TEST

    fprintf(stderr, "nxagentRemoveDuplicatedKeys: Trying to read more events "
                "from the X server.\n");

    if (nxagentReadEvents(nxagentDisplay) > 0)
    {
      fprintf(stderr, "nxagentRemoveDuplicatedKeys: Successfully read more events "
                  "from the X server.\n");
    }

    #else

    nxagentReadEvents(nxagentDisplay);

    #endif

    qelt = nxagentDisplay -> head;
  }

  if (qelt != NULL)
  {
    _XQEvent *prev;
    _XQEvent *qeltKeyRelease;
    _XQEvent *prevKeyRelease;

    prev = qeltKeyRelease = prevKeyRelease = NULL;

    LockDisplay(nxagentDisplay);

    while (qelt != NULL)
    {
      if (qelt -> event.type == KeyRelease ||
              qelt -> event.type == KeyPress)
      {
        if (qelt -> event.xkey.keycode != lastKeycode ||
               (qelt -> event.type == KeyPress && qeltKeyRelease == NULL) ||
                   (qelt -> event.type == KeyRelease && qeltKeyRelease != NULL))
        {
          break;
        }

        if (qelt -> event.type == KeyRelease)
        {
          prevKeyRelease = prev;

          qeltKeyRelease = qelt;
        }
        else if (qelt -> event.type == KeyPress)
        {
          _XDeq(nxagentDisplay, prev, qelt);

          qelt = prev -> next;

          if (prev == qeltKeyRelease)
          {
            prev = prevKeyRelease;
          }

          _XDeq(nxagentDisplay, prevKeyRelease, qeltKeyRelease);

          qeltKeyRelease = prevKeyRelease = NULL;

          continue;
        }
      }

      prev = qelt;

      qelt = qelt -> next;
    }

    UnlockDisplay(nxagentDisplay);
  }
}

void nxagentInitRemoteExposeRegion(void)
{
  if (nxagentRemoteExposeRegion == NULL)
  {
    nxagentRemoteExposeRegion = RegionCreate(NULL, 1);

    if (nxagentRemoteExposeRegion == NULL)
    {
      #ifdef PANIC
      fprintf(stderr, "nxagentInitRemoteExposeRegion: PANIC! Failed to create expose region.\n");
      #endif
    }
  }
}

void nxagentForwardRemoteExpose(void)
{
  if (RegionNotEmpty(nxagentRemoteExposeRegion))
  {
    #ifdef DEBUG
    fprintf(stderr, "nxagentForwardRemoteExpose: Going to forward events.\n");
    #endif

    TraverseTree(screenInfo.screens[0]->root, nxagentClipAndSendExpose, (void *)nxagentRemoteExposeRegion);

    /*
     * Now this region should be empty.
     */

    RegionEmpty(nxagentRemoteExposeRegion);
  }
}

void nxagentAddRectToRemoteExposeRegion(BoxPtr rect)
{
  RegionRec exposeRegion;

  if (nxagentRemoteExposeRegion == NULL)
  {
    return;
  }

  RegionInit(&exposeRegion, rect, 1);

  RegionUnion(nxagentRemoteExposeRegion,
                   nxagentRemoteExposeRegion, &exposeRegion);

  RegionUninit(&exposeRegion);
}

int nxagentClipAndSendExpose(WindowPtr pWin, void * ptr)
{
  RegionPtr remoteExposeRgn = (RegionRec *) ptr;

  #ifdef DEBUG
  fprintf(stderr, "nxagentClipAndSendExpose: Called.\n");
  #endif

  if (pWin -> drawable.class != InputOnly)
  {
    RegionPtr exposeRgn = RegionCreate(NULL, 1);

    #ifdef DEBUG
    BoxRec box = *RegionExtents(remoteExposeRgn);

    fprintf(stderr, "nxagentClipAndSendExpose: Root expose extents: [%d] [%d] [%d] [%d].\n",
                box.x1, box.y1, box.x2, box.y2);

    box = *RegionExtents(&pWin -> clipList);

    fprintf(stderr, "nxagentClipAndSendExpose: Clip list extents for window at [%p]: [%d] [%d] [%d] [%d].\n",
                (void *)pWin, box.x1, box.y1, box.x2, box.y2);
    #endif

    RegionIntersect(exposeRgn, remoteExposeRgn, &pWin -> clipList);

    if (RegionNotEmpty(exposeRgn))
    {
      #ifdef DEBUG
      fprintf(stderr, "nxagentClipAndSendExpose: Forwarding expose to window at [%p] pWin.\n",
                  (void *)pWin);
      #endif

      /*
       * The miWindowExposures() clears out the
       * region parameters, so the subtract ope-
       * ration must be done before calling it.
       */

      RegionSubtract(remoteExposeRgn, remoteExposeRgn, exposeRgn);

      miWindowExposures(pWin, exposeRgn, NullRegion);
    }

    RegionDestroy(exposeRgn);
  }

  if (RegionNotEmpty(remoteExposeRgn))
  {
    #ifdef DEBUG
    fprintf(stderr, "nxagentClipAndSendExpose: Region not empty. Walk children.\n");
    #endif

    return WT_WALKCHILDREN;
  }
  else
  {
    #ifdef DEBUG
    fprintf(stderr, "nxagentClipAndSendExpose: Region empty. Stop walking.\n");
    #endif

    return WT_STOPWALKING;
  }
}

int nxagentUserInput(void *p)
{
  int result = 0;

  /*
   * This function is used as callback in
   * the polling handler of agent in shadow
   * mode. When inside the polling loop the
   * handlers are never called, so we have
   * to dispatch enqueued events to eventu-
   * ally change the nxagentInputEvent sta-
   * tus.
   */

  if (nxagentOption(Shadow) == 1 &&
          nxagentPendingEvents(nxagentDisplay) > 0)
  {
    nxagentDispatchEvents(NULL);
  }

  if (nxagentInputEvent == 1)
  {
    nxagentInputEvent = 0;

    result = 1;
  }

  /*
   * The agent working in shadow mode synch-
   * ronizes the remote X server even if a
   * button/key is not released (i.e. when
   * scrolling a long browser's page), in
   * order to update the screen smoothly.
   */

  if (nxagentOption(Shadow) == 1)
  {
    return result;
  }

  if (result == 0)
  {
    /*
     * If there is at least one button/key down,
     * we are receiving an input. This is not a
     * condition to break a synchronization loop
     * if there is enough bandwidth.
     */

    if (nxagentCongestion > 0 &&
            (inputInfo.pointer -> button -> buttonsDown > 0 ||
                nxagentKeyDown > 0))
    {
      #ifdef TEST
      fprintf(stderr, "nxagentUserInput: Buttons [%d] Keys [%d].\n",
                  inputInfo.pointer -> button -> buttonsDown, nxagentKeyDown);
      #endif

      result = 1;
    }
  }

  return result;
}

int nxagentHandleRRScreenChangeNotify(XEvent *X)
{
  XRRScreenChangeNotifyEvent *Xr = (XRRScreenChangeNotifyEvent *) X;

  #ifdef DEBUG
  fprintf(stderr, "nxagentHandleRRScreenChangeNotify called.\n");
  #endif

  nxagentResizeScreen(screenInfo.screens[DefaultScreen(nxagentDisplay)], Xr -> width, Xr -> height,
                          Xr -> mwidth, Xr -> mheight);

  nxagentShadowCreateMainWindow(screenInfo.screens[DefaultScreen(nxagentDisplay)], screenInfo.screens[0]->root,
                                Xr -> width, Xr -> height);

  nxagentShadowSetWindowsSize();

  return 1;
}

/*
 * Returns true if there is any event waiting to
 * be dispatched. This function is critical for
 * the performance because it is called very,
 * very often. It must also handle the case when
 * the display is down. The display descriptor,
 * in fact, may have been reused by some other
 * client.
 */

int nxagentPendingEvents(Display *dpy)
{
  if (_XGetIOError(dpy) != 0)
  {
    #ifdef DEBUG
    fprintf(stderr, "nxagentPendingEvents: Returning error with display down.\n");
    #endif

    return -1;
  }
  else if (XQLength(dpy) > 0)
  {
    #ifdef DEBUG
    fprintf(stderr, "nxagentPendingEvents: Returning true with [%d] events queued.\n",
                XQLength(dpy));
    #endif

    return 1;
  }
  else
  {
    int readable;

    if (NXTransReadable(dpy -> fd, &readable) == 0)
    {
      if (readable > 0)
      {
        #ifdef DEBUG
        fprintf(stderr, "nxagentPendingEvents: Returning true with [%d] bytes readable.\n",
                    readable);
        #endif

        return 1;
      }

      #ifdef DEBUG
      fprintf(stderr, "nxagentPendingEvents: Returning false with [%d] bytes readable.\n",
                  readable);
      #endif

      return 0;
    }

    #ifdef TEST
    fprintf(stderr, "nxagentPendingEvents: WARNING! Error detected on the X display.\n");
    #endif

    NXForceDisplayError(dpy);

    return -1;
  }
}

/*
 * Blocks until an event becomes available.
 */

int nxagentWaitEvents(Display *dpy, useconds_t msec)
{
  #ifdef DEBUG
  fprintf(stderr, "nxagentWaitEvents called.\n");
  #endif

  NXFlushDisplay(dpy, NXFlushLink);

  /*
   * If the transport is not running we have to rely on Xlib to wait
   * for an event. In this case the timeout is ignored.
   */

  if (NXTransRunning(NX_FD_ANY) == 1)
  {
    if (msec > 0)
    {
      struct timeval tm = {
	  .tv_sec  = 0,
	  .tv_usec = msec * 1000
      };
      NXTransContinue(&tm);
    }
    else
    {
      NXTransContinue(NULL);
    }
  }
  else
  {
    XEvent ev;
    XPeekEvent(dpy, &ev);
  }

  /*
   * Check if we encountered a display error. If we did, wait for the
   * time requested by the caller.
   */

  if (NXDisplayError(dpy) == 1)
  {
    if (msec > 0)
    {
      usleep(msec * 1000);
    }

    return -1;
  }

  return 1;
}

#ifdef NX_DEBUG_INPUT

void nxagentGuessDumpInputInfo(ClientPtr client, Atom property, char *data)
{
  if (strcmp(validateString(NameForAtom(property)), "NX_DEBUG_INPUT") == 0)
  {
    if (*data != 0)
    {
      nxagentDebugInput = 1;
    }
    else
    {
      nxagentDebugInput = 0;
    }
  }
}

void nxagentDeactivateInputDevicesGrabs(void)
{
  fprintf(stderr, "Info: Deactivating input devices grabs.\n");

  if (inputInfo.pointer -> grab)
  {
    (*inputInfo.pointer -> DeactivateGrab)(inputInfo.pointer);
  }

  if (inputInfo.keyboard -> grab)
  {
    (*inputInfo.keyboard -> DeactivateGrab)(inputInfo.keyboard);
  }
}

static const char *nxagentGrabStateToString(int state)
{
  switch (state)
  {
    case 0:
      return "NOT_GRABBED";
    case 1:
      return "THAWED";
    case 2:
      return "THAWED_BOTH";
    case 3:
      return "FREEZE_NEXT_EVENT";
    case 4:
      return "FREEZE_BOTH_NEXT_EVENT";
    case 5:
      return "FROZEN_NO_EVENT";
    case 6:
      return "FROZEN_WITH_EVENT";
    case 7:
      return "THAW_OTHERS";
    default:
      return "unknown state";
  }
}

void nxagentDumpInputDevicesState(void)
{
  DeviceIntPtr dev;
  GrabPtr grab;
  WindowPtr pWin = NULL;

  fprintf(stderr, "\n*** Dump input devices state: BEGIN ***"
              "\nKeys down:");

  dev = inputInfo.keyboard;

  for (int i = 0; i < DOWN_LENGTH; i++)
  {
    CARD8 val = dev -> key -> down[i];

    if (val != 0)
    {
      for (int k = 0; k < 8; k++)
      {
        if (val & (1 << k))
        {
          fprintf(stderr, "\n\t[%d] [%s]", i * 8 + k,
                      XKeysymToString(XKeycodeToKeysym(nxagentDisplay, i * 8 + k, 0)));
        }
      }
    }
  }

  fprintf(stderr, "\nKeyboard device state: \n\tdevice [%p]\n\tlast grab time [%u]"
              "\n\tfrozen [%s]\n\tstate [%s]\n\tother [%p]\n\tevent count [%d]"
                  "\n\tfrom passive grab [%s]\n\tactivating key [%d]", (void *)dev,
                      dev -> grabTime.milliseconds, dev -> sync.frozen ? "Yes": "No",
                          nxagentGrabStateToString(dev -> sync.state),
                              (void *)dev -> sync.other, dev -> sync.evcount,
                                  dev -> fromPassiveGrab ? "Yes" : "No",
                                      dev -> activatingKey);

  grab = dev -> grab;

  if (grab)
  {
    fprintf(stderr, "\nKeyboard grab state: \n\twindow pointer [%p]"
                "\n\towner events flag [%s]\n\tgrab mode [%s]",
                    (void *)grab -> window, grab -> ownerEvents ? "True" : "False",
                        grab -> keyboardMode ? "asynchronous" : "synchronous");

   /*
    * Passive grabs.
    */

    pWin = grab -> window;
    grab = wPassiveGrabs(pWin);

    while (grab)
    {
      fprintf(stderr, "\nPassive grab state: \n\tdevice [%p]\n\towner events flag [%s]"
                  "\n\tpointer grab mode [%s]\n\tkeyboard grab mode [%s]\n\tevent type [%d]"
                      "\n\tmodifiers [%x]\n\tbutton/key [%u]\n\tevent mask [%x]",
                          (void *)grab -> device, grab -> ownerEvents ? "True" : "False",
                              grab -> pointerMode ? "asynchronous" : "synchronous",
                                  grab -> keyboardMode ? "asynchronous" : "synchronous",
                                      grab -> type, grab -> modifiersDetail.exact,
                                          grab -> detail.exact, grab -> eventMask);

      grab = grab -> next;
    }
  }

  fprintf(stderr, "\nButtons down:");

  dev = inputInfo.pointer;

  for (int i = 0; i < DOWN_LENGTH; i++)
  {
    CARD8 val = dev -> button -> down[i];

    if (val != 0)
    {
      for (int k = 0; k < 8; k++)
      {
        if (val & (1 << k))
        {
          fprintf(stderr, "\n\t[%d]", i * 8 + k);
        }
      }
    }
  }

  fprintf(stderr, "\nPointer device state: \n\tdevice [%p]\n\tlast grab time [%u]"
              "\n\tfrozen [%s]\n\tstate [%s]\n\tother [%p]\n\tevent count [%d]"
                  "\n\tfrom passive grab [%s]\n\tactivating button [%d]", (void *)dev,
                      dev -> grabTime.milliseconds, dev -> sync.frozen ? "Yes" : "No",
                          nxagentGrabStateToString(dev -> sync.state),
                              (void *)dev -> sync.other, dev -> sync.evcount,
                                  dev -> fromPassiveGrab ? "Yes" : "No",
                                      dev -> activatingKey);

  grab = dev -> grab;

  if (grab)
  {
    fprintf(stderr, "\nPointer grab state: \n\twindow pointer [%p]"
                "\n\towner events flag [%s]\n\tgrab mode [%s]",
                    (void *)grab -> window, grab -> ownerEvents ? "True" : "False",
                        grab -> pointerMode ? "asynchronous" : "synchronous");

    if (grab -> window != pWin)
    {
      /*
       * Passive grabs.
       */

      grab = wPassiveGrabs(grab -> window);

      while (grab)
      {
        fprintf(stderr, "\nPassive grab state: \n\tdevice [%p]\n\towner events flag [%s]"
                    "\n\tpointer grab mode [%s]\n\tkeyboard grab mode [%s]\n\tevent type [%d]"
                        "\n\tmodifiers [%x]\n\tbutton/key [%u]\n\tevent mask [%x]",
                            (void *)grab -> device, grab -> ownerEvents ? "True" : "False",
                                grab -> pointerMode ? "asynchronous" : "synchronous",
                                    grab -> keyboardMode ? "asynchronous" : "synchronous",
                                        grab -> type, grab -> modifiersDetail.exact,
                                            grab -> detail.exact, grab -> eventMask);

        grab = grab -> next;
      }
    }
  }

  fprintf(stderr, "\n*** Dump input devices state: FINISH ***\n");
}

#endif
