/**************************************************************************/
/*                                                                        */
/* 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 "../../include/window.h"
#include "windowstr.h"
#include "colormapst.h"
#include "scrnintstr.h"
#include "propertyst.h"

#include "Agent.h"
#include "Display.h"
#include "Drawable.h"
#include "Windows.h"
#include "Pixmaps.h"
#include "Atoms.h"
#include "Trap.h"
#include "Utils.h"
#include "Atoms.h"

#include "compext/Compext.h"

/*
 * Set here the required log level.
 */

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

/*
 * Assigned at the time the root window is initialized.
 */

typedef struct
{
  CARD32 flags;
  CARD32 input;
  CARD32 initial_state;
  CARD32 icon_pixmap;
  CARD32 icon_window;
  INT32  icon_x;
  INT32  icon_y;
  CARD32 icon_mask;
  CARD32 window_group;
}
nxagentWMHints;

/*
 * This structure is compatible with 32 and 64 bit library
 * interface. It has been copied from Xatomtype.h and it's a parameter
 * of XChangeProperty().
 */

typedef struct
{
  unsigned long flags;
  long          input;
  long          initialState;
  unsigned long iconPixmap;
  unsigned long iconWindow;
  long          iconX;
  long          iconY;
  unsigned long iconMask;
  unsigned long windowGroup;
}
nxagentPropWMHints;

WindowPtr nxagentRootlessWindow = NULL;

#define TOP_LEVEL_TABLE_UNIT 100

typedef struct {
  Window xid;
  WindowPtr pWin;
} TopLevelParentRec;

typedef struct {
  TopLevelParentRec *elt;
  int next;
  int size;
} TopLevelParentMap;

static TopLevelParentMap topLevelParentMap = { NULL, 0, 0 };

static void nxagentRemovePropertyFromList(void);

#if 0
/*
 * This is currently unused.
 */

#ifdef TEST

static void nxagentPrintRootlessTopLevelWindowMap(void);

void nxagentPrintRootlessTopLevelWindowMap(void)
{
  fprintf(stderr, "%s: Map size is [%d] num of entry [%d].\n", __func__,
              topLevelParentMap.size, topLevelParentMap.next);

  for (int i = 0; i < topLevelParentMap.next; i++)
  {
    fprintf(stderr, "%s:: [%d] pWin at [%p] XID at [%ld].\n", __func__,
                i, (void *) topLevelParentMap.elt[i].pWin, (long int) topLevelParentMap.elt[i].xid);
  }
}

#endif
#endif

void nxagentRootlessAddTopLevelWindow(WindowPtr pWin, Window w)
{
  for (int i = 0; i < topLevelParentMap.next; i++)
  {
    if (topLevelParentMap.elt[i].pWin == pWin)
    {
      #ifdef TEST
      fprintf(stderr, "%s: WARNING! Trying to add duplicated entry window at [%p] xid [%d].\n",
                  __func__, (void *) pWin, w);
      #endif

      topLevelParentMap.elt[i].xid = w;

      return;
    }
  }

  if (topLevelParentMap.next == topLevelParentMap.size)
  {
    size_t size = (topLevelParentMap.size += TOP_LEVEL_TABLE_UNIT);

    TopLevelParentRec *ptr = realloc(topLevelParentMap.elt, size * sizeof(TopLevelParentRec));

    if (ptr == NULL)
    {
      #ifdef WARNING
      fprintf(stderr, "%s: Warning failed to allocate memory.\n", __func__);
      #endif

      return;
    }

    topLevelParentMap.elt = ptr;
    topLevelParentMap.size = size;
  }

  topLevelParentMap.elt[topLevelParentMap.next].xid = w;
  topLevelParentMap.elt[topLevelParentMap.next].pWin = pWin;
  topLevelParentMap.next++;
}

WindowPtr nxagentRootlessTopLevelWindow(Window w)
{
  for (int i = 0; i < topLevelParentMap.next; i++)
  {
    if (w == topLevelParentMap.elt[i].xid)
    {
      return topLevelParentMap.elt[i].pWin;
    }
  }
  return NULL;
}

void nxagentRootlessDelTopLevelWindow(WindowPtr pWin)
{
  for (int i = 0; i < topLevelParentMap.next; i++)
  {
    if (pWin == topLevelParentMap.elt[i].pWin)
    {
      topLevelParentMap.elt[i] = topLevelParentMap.elt[topLevelParentMap.next - 1];
      topLevelParentMap.next--;

      return;
    }
  }
}

void nxagentConfigureRootlessWindow(WindowPtr pWin, int x, int y, int w, int h, int bw,
                                        WindowPtr pSib, int stack_mode, Mask mask)
{
  XWindowChanges changes = {
    .x = x,
    .y = y,
    .width = w,
    .height = h,
    .border_width = bw,
    .stack_mode = stack_mode
  };

  if (pSib)
  {
    changes.sibling = nxagentWindow(pSib);
  }

  XConfigureWindow(nxagentDisplay, nxagentWindow(pWin), mask, &changes);
}

void nxagentCirculateRootlessWindows(int direction)
{
  XCirculateSubwindows(nxagentDisplay, DefaultRootWindow(nxagentDisplay), direction);
}

#ifdef DEBUG

Bool nxagentRootlessTreesMatch(void)
{
  XlibWindow root_return;
  XlibWindow parent_return;
  XlibWindow *children_return = NULL;
  unsigned int nChildrenReturn;
  WindowPtr pTestWin = screenInfo.screens[0]->root -> firstChild;
  Bool treesMatch = True;

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

  if (!result)
  {
    #ifdef WARNING
    fprintf(stderr, "%s: WARNING! Failed QueryTree request.\n", __func__);
    #endif
    return False;
  }

  while (nChildrenReturn > 0)
  {
    WindowPtr pW = nxagentWindowPtr(children_return[--nChildrenReturn]);
    if (!pW)
    {
      pW = nxagentRootlessTopLevelWindow(children_return[nChildrenReturn]);
    }

    if (pW && pW != screenInfo.screens[0]->root)
    {
      if (treesMatch && pTestWin && pTestWin == pW)
      {
        pTestWin = pTestWin -> nextSib;
      }
      else
      {
        treesMatch = False;
      }
    }
  }

  SAFE_XFree(children_return);

  return treesMatch;
}

#endif

#ifndef _XSERVER64
void nxagentRootlessRestack(Window children[], unsigned int nchildren)
#else
void nxagentRootlessRestack(unsigned long children[], unsigned int nchildren)
#endif
{
  WindowPtr *toplevel = malloc(sizeof(WindowPtr) * nchildren);

  if (!toplevel)
  {
    /* FIXME: Is this too much and we should simply return here? */
    FatalError("nxagentRootlessRestack: malloc() failed.");
  }

  unsigned int ntoplevel = 0;

  for(int i = 0; i < nchildren; i++)
  {
    WindowPtr pWin = nxagentWindowPtr(children[i]);

    if (!pWin)
    {
      pWin = nxagentRootlessTopLevelWindow(children[i]);
    }

    if (pWin && pWin != screenInfo.screens[0]->root)
    {
      toplevel[ntoplevel++] = pWin;
    }
  }

  if (!ntoplevel)
  {
    SAFE_free(toplevel);
    return;
  }

  #ifdef DEBUG

  fprintf(stderr, "%s: External top level windows before restack:\n", __func__);

  for (int i = 0; i < ntoplevel; i++)
  {
    fprintf(stderr, "%s: [%p]\n", __func__, (void *)toplevel[i]);
  }

  fprintf(stderr, "%s: Internal top level windows before restack:\n", __func__);

  for (WindowPtr pWin = screenInfo.screens[0]->root -> firstChild; pWin != NULL; pWin = pWin -> nextSib)
  {
    fprintf(stderr, "%s: [%p]\n", __func__, (void *)pWin);
  }

  #endif

  WindowPtr pWin = screenInfo.screens[0]->root -> firstChild;

  XID values[2] = {0, (XID) Above};

  for (int i = ntoplevel; i-- && pWin; pWin = toplevel[i] -> nextSib)
  {
    if (toplevel[i] != pWin)
    {
      Mask mask = CWSibling | CWStackMode;
      values[0] = pWin -> drawable.id;
      ClientPtr pClient = wClient(toplevel[i]);
      nxagentScreenTrap = True;
      ConfigureWindow(toplevel[i], mask, values, pClient);
      nxagentScreenTrap = False;

      #ifdef TEST
      fprintf(stderr, "%s: Restacked window [%p].\n", __func__, (void*) toplevel[i]);
      #endif
    }
  }

  #ifdef DEBUG

  fprintf(stderr, "%s: External top level windows after restack:\n", __func__);

  for (int i = 0; i < ntoplevel; i++)
  {
    fprintf(stderr, "%s: [%p]\n", __func__, (void *)toplevel[i]);
  }

  fprintf(stderr, "%s: Internal top level windows after restack:\n", __func__);

  for (pWin = screenInfo.screens[0]->root -> firstChild; pWin != NULL; pWin = pWin -> nextSib)
  {
    fprintf(stderr, "%s: [%p]\n", __func__, (void *)pWin);
  }

  #endif

  SAFE_free(toplevel);

  return;
}

/*
 * Determine if window is a top-level window.
 */

Window nxagentRootlessWindowParent(WindowPtr pWin)
{
  #ifdef TEST
  fprintf(stderr, "%s: Called for window at [%p][%d] with parent [%p][%d].\n", __func__,
              (void *) pWin, nxagentWindowPriv(pWin)->window, (void *) pWin->parent,
                  (pWin->parent ? nxagentWindowPriv(pWin->parent)->window : 0));
  #endif

  if (pWin -> parent == NULL)
  {
    return DefaultRootWindow(nxagentDisplay);
  }
  else if (pWin -> parent == nxagentRootlessWindow)
  {
    return DefaultRootWindow(nxagentDisplay);
  }
  else
  {
    return nxagentWindow(pWin -> parent);
  }
}

int nxagentExportAllProperty(WindowPtr pWin)
{
  int total = 0;

  for (PropertyPtr pProp = wUserProps(pWin); pProp; pProp = pProp->next)
  {
    total += nxagentExportProperty(pWin,
                                       pProp->propertyName,
                                           pProp->type,
                                               pProp->format,
                                                   PropModeReplace,
                                                       pProp->size,
                                                           pProp->data);
  }

  return total;
}

/*
 * Export a property from an agent window to the corresponding window
 * on the real X server This is e.g. called if a client changes a
 * property.
 */
int nxagentExportProperty(WindowPtr pWin,
                          Atom property,
                          Atom type,
                          int format,
                          int mode,
                          unsigned long nUnits,
                          void *value)
{
  char *output = NULL;
  Bool export = False;
  Bool freeMem = False;

  if (NXDisplayError(nxagentDisplay) == 1)
  {
    return 0;
  }

  const char *propertyS = NameForAtom(property);
  const char *typeS = NameForAtom(type);

  if (strncmp(propertyS, "WM_", 3) != 0 &&
          strncmp(propertyS, "_NET_", 5) != 0 &&
              strcmp(propertyS, "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR") != 0)
  {
    #ifdef TEST
    fprintf(stderr, "%s: WARNING! Ignored ChangeProperty on %swindow [0x%x] property [%s] "
                "type [%s] nUnits [%ld] format [%d]\n", __func__,
                    nxagentWindowTopLevel(pWin) ? "toplevel " : "", nxagentWindow(pWin),
                        validateString(propertyS), validateString(typeS), nUnits, format);
    #endif
  }
  else if (strcmp(typeS, "STRING") == 0 ||
               #ifndef _XSERVER64
               strcmp(typeS, "CARDINAL") == 0 ||
                   strcmp(typeS, "WM_SIZE_HINTS") == 0 ||
               #endif
                       strcmp(typeS, "UTF8_STRING") == 0)
  {
    output = value;
    export = True;
  }
  #ifdef _XSERVER64
  else if (strcmp(typeS, "CARDINAL") == 0 || strcmp(typeS, "WM_SIZE_HINTS") == 0)
  {
    unsigned long *buffer = malloc(nUnits * sizeof(*buffer));
    if (buffer == NULL)
    {
      FatalError("%s: malloc() failed.", __func__);
    }

    int *input = value;

    if (buffer)
    {
      freeMem = True;
      export = True;
      output = (char*) buffer;

      for (int i = 0; i < nUnits; i++)
      {
        buffer[i] = input[i];
      }
    }
  }
  #endif
  else if (strcmp(typeS, "WM_HINTS") == 0)
  {
    ClientPtr pClient = wClient(pWin);
    nxagentWMHints wmHints = *(nxagentWMHints*)value;

    wmHints.flags |= InputHint;
    wmHints.input = True;

    /*
     * Initialize the structure used in XChangeProperty().
     */

    nxagentPropWMHints propHints = {
      .flags = wmHints.flags,
      /*.input = (wmHints.input == True ? 1 : 0), is always True*/
      .input = 1,
      .initialState = wmHints.initial_state,
      .iconPixmap = wmHints.icon_pixmap,
      .iconWindow = wmHints.icon_window,
      .iconX = wmHints.icon_x,
      .iconY = wmHints.icon_y,
      .iconMask = wmHints.icon_mask,
      .windowGroup = wmHints.window_group
    };

    output = (char*) &propHints;
    export = True;

    if ((wmHints.flags & IconPixmapHint) && (wmHints.icon_pixmap != None))
    {
      PixmapPtr icon = (PixmapPtr)SecurityLookupIDByType(pClient, wmHints.icon_pixmap,
                                                             RT_PIXMAP, DixDestroyAccess);

      if (icon)
      {
        if (nxagentDrawableStatus((DrawablePtr) icon) == NotSynchronized)
        {
          nxagentSynchronizeRegion((DrawablePtr) icon, NullRegion, NEVER_BREAK, NULL);
        }

        propHints.iconPixmap = nxagentPixmap(icon);
      }
      else
      {
        propHints.flags &= ~IconPixmapHint;

        #ifdef WARNING
        fprintf(stderr, "%s: WARNING! Failed to look up icon pixmap [0x%x] from hint "
                    "exporting property [%s] type [%s] on window [%p].\n", __func__,
                        (unsigned int) wmHints.icon_pixmap, propertyS, typeS,
                            (void *) pWin);
        #endif
      }
    }

    if ((wmHints.flags & IconWindowHint) && (wmHints.icon_window != None))
    {
      WindowPtr icon = (WindowPtr)SecurityLookupWindow(wmHints.icon_window, pClient,
                                                  DixDestroyAccess);

      if (icon)
      {
        propHints.iconWindow = nxagentWindow(icon);
      }
      else
      {
        propHints.flags &= ~IconWindowHint;

        #ifdef WARNING
        fprintf(stderr, "%s: WARNING! Failed to look up icon window [0x%x] from hint "
                    "exporting property [%s] type [%s] on window [%p].\n", __func__,
                        (unsigned int) wmHints.icon_window, propertyS, typeS,
                            (void *) pWin);
        #endif
      }
    }

    if ((wmHints.flags & IconMaskHint) && (wmHints.icon_mask != None))
    {
      PixmapPtr icon = (PixmapPtr)SecurityLookupIDByType(pClient, wmHints.icon_mask,
                                                             RT_PIXMAP, DixDestroyAccess);

      if (icon)
      {
        propHints.iconMask = nxagentPixmap(icon);
      }
      else
      {
        propHints.flags &= ~IconMaskHint;

        #ifdef WARNING
        fprintf(stderr, "%s: WARNING! Failed to look up icon mask [0x%x] from hint "
                    "exporting property [%s] type [%s] on window [%p].\n", __func__,
                        (unsigned int) wmHints.icon_mask, propertyS, typeS,
                            (void *) pWin);
        #endif
      }
    }

    if ((wmHints.flags & WindowGroupHint) && (wmHints.window_group != None))
    {
      WindowPtr window = (WindowPtr)SecurityLookupWindow(wmHints.window_group, pClient,
                                                  DixDestroyAccess);

      if (window)
      {
        propHints.windowGroup = nxagentWindow(window);
      }
      else
      {
        propHints.flags &= ~WindowGroupHint;

        #ifdef WARNING
        fprintf(stderr, "%s: WARNING! Failed to look up window group [0x%x] from hint "
                    "exporting property [%s] type [%s] on window [%p].\n", __func__,
                        (unsigned int) wmHints.window_group, propertyS, typeS,
                            (void *) pWin);
        #endif
      }
    }
  }
  else if (strcmp(typeS, "ATOM") == 0)
  {
    XlibAtom *atoms = malloc(nUnits * sizeof(*atoms));
    Atom *input = value;
    const char *atomName = NULL;
    int j = 0;

    if (!atoms)
    {
      #ifdef WARNING
      fprintf(stderr, "%s: WARNING! malloc() failed for '[%s]'- bailing out.\n", __func__, typeS);
      #endif
      return False;
    }

    freeMem = True;
    export = True;
    output = (char *) atoms;

    for (int i = 0; i < nUnits; i++)
    {
      /*
       * Exporting the _NET_WM_PING property could result in rootless
       * windows being grayed out when the compiz window manager is
       * running.
       *
       * Better solution would probably be to handle the communication
       * with the window manager instead of just getting rid of the
       * property.
       */

      if ((atomName = NameForAtom(input[i])) != NULL &&
              strcmp(atomName, "_NET_WM_PING") != 0)
      {
        atoms[j] = nxagentLocalToRemoteAtom(input[i]);

        if (atoms[j] == None)
        {
          #ifdef WARNING
          fprintf(stderr, "%s: WARNING! Failed to convert local atom [%ld] [%s].\n", __func__,
                      (long int) input[i], validateString(atomName));
          #endif
        }

        j++;
      }
      #ifdef TEST
      else
      {
        fprintf(stderr, "%s: WARNING! "
		"Not exporting the _NET_WM_PING property.\n", __func__);
      }
      #endif
    }

    nUnits = j;
  }
  else if (strcmp(typeS, "WINDOW") == 0)
  {
    Window *input = value;
    XlibWindow *wind = malloc(nUnits * sizeof(*wind));
    ClientPtr pClient = wClient(pWin);

    if (!wind)
    {
      #ifdef WARNING
      fprintf(stderr, "%s: WARNING! malloc() failed for '[%s]' - bailing out.\n", __func__, typeS);
      #endif
      return False;
    }

    freeMem = True;
    export = True;
    output = (char*) wind;

    for (int i = 0; i < nUnits; i++)
    {
      WindowPtr pWindow = (WindowPtr)SecurityLookupWindow(input[i], pClient,
                                                              DixDestroyAccess);
      if ((input[i] != None) && pWindow)
      {
        wind[i] = nxagentWindow(pWindow);
      }
      else
      {
        #ifdef WARNING
        fprintf(stderr, "%s: WARNING! Failed to look up window [%ld] "
                    "exporting property [%s] type [%s] on window [%p].\n", __func__,
                        (long int) input[i], propertyS, typeS, (void *) pWin);
        #endif

        /*
         * It seems that clients specify strange windows, perhaps are
         * not real windows so we can try to let them pass anyway.
         *
         * wind[i] = None;
         */
      }
    }
  }

  if (export)
  {
    XlibAtom propertyX = nxagentLocalToRemoteAtom(property);
    XlibAtom typeX = nxagentLocalToRemoteAtom(type);

    if (propertyX == None || typeX == None)
    {
      #ifdef WARNING
      fprintf(stderr, "%s: WARNING! Failed to convert local atom.\n", __func__);
      #endif

      export = 0;
    }
    else
    {
      #ifdef TEST
      fprintf(stderr, "%s: Property [%lu] format [%i] units [%lu].\n", __func__,
                  propertyX, format, nUnits);
      #endif

      if ((format >> 3) * nUnits + sizeof(xChangePropertyReq) <
              (MAX_REQUEST_SIZE << 2))
      {
        XChangeProperty(nxagentDisplay, nxagentWindow(pWin), propertyX, typeX,
                            format, mode, (void*)output, nUnits);
      }
      else if (mode == PropModeReplace)
      {
        char * data = (char *) output;

        XDeleteProperty(nxagentDisplay, nxagentWindow(pWin), propertyX);

        while (nUnits > 0)
        {
          int n;

          if ((format >> 3) * nUnits + sizeof(xChangePropertyReq) <
                  (MAX_REQUEST_SIZE << 2))
          {
            n = nUnits;
          }
          else
          {
            n = ((MAX_REQUEST_SIZE << 2) - sizeof(xChangePropertyReq)) /
                    (format >> 3);
          }

          XChangeProperty(nxagentDisplay, nxagentWindow(pWin), propertyX,
                              typeX, format, PropModeAppend, (void*) data, n);

          nUnits -= n;

          data = (char *) data + n * (format >> 3);
        }
      }
      else
      {
        #ifdef WARNING
        fprintf(stderr, "%s: WARNING! Property [%lu] too long.\n", __func__,
                    (long unsigned int)propertyX);
        #endif

        goto nxagentExportPropertyError;
      }

      nxagentAddPropertyToList(propertyX, pWin);
    }
  }
  else
  {
    #ifdef TEST
    fprintf(stderr, "%s: WARNING! Ignored ChangeProperty on %swindow [0x%x] property [%s] "
                "type [%s] nUnits [%ld] format [%d]\n", __func__,
                    nxagentWindowTopLevel(pWin) ? "toplevel " : "",
                        nxagentWindow(pWin), validateString(propertyS), validateString(typeS),
                            nUnits, format);
    #endif
  }

  nxagentExportPropertyError:

  if (freeMem)
  {
    SAFE_free(output);
  }

  return export;
}

/*
 * Import a property from the proxy window on the real X server into
 * the agent's corresponding window. This is e.g. called on reception
 * of a property change event on the real X server.
 */
void nxagentImportProperty(Window window,
                           XlibAtom property,
                           XlibAtom type,
                           int format,
                           unsigned long nitems,
                           unsigned long bytes_after,
                           unsigned char *buffer)
{
  Bool import = False;
  Bool freeMem = False;

  typedef struct {
      CARD32 state;
      Window icon;
    } WMState;
  WMState wmState;

  char *output = NULL;

  WindowPtr pWin = nxagentWindowPtr(window);

  if (pWin == NULL)
  {
    #ifdef TEST
    fprintf(stderr, "%s: Failed to look up remote window [0x%x]  property [%ld] exiting.\n",
                __func__, window, property);
    #endif

    return;
  }

  Atom propertyL = nxagentRemoteToLocalAtom(property);

  if (!ValidAtom(propertyL))
  {
    #ifdef TEST
    fprintf(stderr, "%s: Failed to convert remote property atom [%ld].\n", __func__, property);
    #endif

    return;
  }

  #ifdef TEST
  fprintf(stderr, "%s: Window [0x%x] property: remote [%ld][%s] local [%d]\n", __func__,
              window, property, validateString(NameForAtom(propertyL)), propertyL);
  #endif

  /*
   * We settle a property size limit of 256K beyond which we simply
   * ignore them.
   * FIXME: where's this checked/set/enforced/whatever?
   */

  Atom typeL = nxagentRemoteToLocalAtom(type);
  const char *typeS = NameForAtom(typeL);

  #ifdef TEST
  fprintf(stderr, "%s: type: remote [%ld] local [%d][%s].\n", __func__, type, typeL, validateString(typeS));
  #endif

  if (buffer == NULL && (nitems > 0))
  {
    #ifdef WARNING
    fprintf(stderr, "%s: Failed to retrieve remote property [%ld] [%s] on window [%ld]\n", __func__,
                (long int) property, validateString(NameForAtom(propertyL)), (long int) window);
    #endif
  }
  else if (bytes_after != 0)
  {
    #ifdef WARNING
    fprintf(stderr, "%s: Remote property bigger than maximum limits.\n", __func__);
    #endif
  }
  else if (!ValidAtom(typeL))
  {
    #ifdef WARNING
    fprintf(stderr, "%s: Failed to convert remote atom [%ld].\n", __func__,
                (long int) type);
    #endif
  }
  else if (nitems == 0)
  {
    #ifdef TEST
    fprintf(stderr, "%s: Importing void property.\n", __func__);
    #endif

    import = True;
  }
  else if (property == nxagentAtoms[0])  /* NX_IDENTITY */
  {
    #ifdef WARNING
    fprintf(stderr, "%s: not importing private [%ld][NXDARWIN].\n", __func__,
                (long int) property);
    #endif
  }
  else if (property == nxagentAtoms[5])  /* NX_CUT_BUFFER_SERVER */
  {
    #ifdef WARNING
    fprintf(stderr, "%s: not importing private [%ld][NX_CUT_BUFFER_SERVER].\n", __func__,
                (long int) property);
    #endif
  }
  else if (property == nxagentAtoms[8])  /* NX_AGENT_SIGNATURE */
  {
    #ifdef WARNING
    fprintf(stderr, "%s: not importing private [%ld][NX_AGENT_SIGNATURE].\n", __func__,
                (long int) property);
    #endif
  }
  else if (property == nxagentAtoms[9])  /* NXDARWIN */
  {
    #ifdef TEST
    fprintf(stderr, "%s: not importing private [%ld][NXDARWIN].\n", __func__,
                (long int) property);
    #endif
  }
  else if (property == nxagentAtoms[15])  /* NX_SELTRANS_FROM_AGENT */
  {
    #ifdef TEST
    fprintf(stderr, "%s: not importing private [%ld][NX_SELTRANS_FROM_AGENT].\n", __func__,
                (long int) property);
    #endif
  }
  else if (strcmp(typeS, "STRING") == 0 ||
               strcmp(typeS, "UTF8_STRING") == 0 ||
                   strcmp(typeS, "CARDINAL") == 0 ||
                       strcmp(typeS, "WM_SIZE_HINTS") == 0)
  {
    output = (char*)buffer;
    import = True;
  }
  else if (strcmp(typeS, "WM_STATE") == 0)
  {
    /*
     * Contents of property of type WM_STATE are {CARD32 state, WINDOW
     * icon}. Only the icon field has to be modified before importing
     * the property.
     */

    wmState = *(WMState*)buffer;
    WindowPtr pIcon = nxagentWindowPtr(wmState.icon);

    if (pIcon || wmState.icon == None)
    {
      import = True;
      output = (char*) &wmState;
      wmState.icon = pIcon ? nxagentWindow(pIcon) : None;
    }
    else if (wmState.icon)
    {
      #ifdef WARNING
      fprintf(stderr, "%s: WARNING! Failed to convert remote window [%ld] importing property [%ld]"
                  " of type WM_STATE", __func__, (long int) wmState.icon, (long int) property);
      #endif
    }
  }
  else if (strcmp(typeS, "WM_HINTS") == 0)
  {
    nxagentWMHints wmHints = *(nxagentWMHints*)buffer;
    output = (char*) &wmHints;
    import = True;

    if ((wmHints.flags & IconPixmapHint) && (wmHints.icon_pixmap != None))
    {
      PixmapPtr icon = nxagentPixmapPtr(wmHints.icon_pixmap);

      if (icon)
      {
        wmHints.icon_pixmap = icon -> drawable.id;
      }
      else
      {
        wmHints.flags &= ~IconPixmapHint;

        #ifdef WARNING
        fprintf(stderr, "%s: WARNING! Failed to look up remote icon pixmap [%d] from hint importing"
                    " property [%ld] type [%s] on window [%p].\n", __func__,
                        (unsigned int) wmHints.icon_pixmap, (long int) property,
                            typeS, (void *) pWin);
        #endif
      }
    }

    if ((wmHints.flags & IconWindowHint) && (wmHints.icon_window != None))
    {
      WindowPtr icon = nxagentWindowPtr(wmHints.icon_window);

      if (icon)
      {
        wmHints.icon_window = icon -> drawable.id;
      }
      else
      {
        wmHints.flags &= ~IconWindowHint;

        #ifdef WARNING
        fprintf(stderr, "%s: WARNING! Failed to look up remote icon window [0x%x] from hint importing"
                " property [%ld] type [%s] on window [%p].\n", __func__,
                         (unsigned int) wmHints.icon_window,
                             (long int) property, typeS, (void *) pWin);
        #endif
      }
    }

    if ((wmHints.flags & IconMaskHint) && (wmHints.icon_mask != None))
    {
      PixmapPtr icon = nxagentPixmapPtr(wmHints.icon_mask);

      if (icon)
      {
        wmHints.icon_mask = icon -> drawable.id;
      }
      else
      {
        wmHints.flags &= ~IconMaskHint;

        #ifdef WARNING
        fprintf(stderr, "%s: WARNING! Failed to look up remote icon mask [0x%x] from hint importing"
                    " property [%ld] type [%s] on window [%p].\n", __func__,
                          (unsigned int) wmHints.icon_mask, (long int) property, typeS, (void *) pWin);
        #endif
      }
    }

    if ((wmHints.flags & WindowGroupHint) && (wmHints.window_group != None))
    {
      WindowPtr group = nxagentWindowPtr(wmHints.window_group);

      if (group)
      {
        wmHints.window_group = group -> drawable.id;
      }
      else
      {
        wmHints.flags &= ~WindowGroupHint;

        #ifdef WARNING
        fprintf(stderr, "%s: WARNING! Failed to look up remote window group [0x%x] from hint importing"
                    " property [%ld] type [%s] on window [%p].\n", __func__,
                          (unsigned int) wmHints.window_group,
                              (long int) property, typeS, (void *) pWin);
        #endif
      }
    }
  }
  else if (strcmp(typeS, "ATOM") == 0)
  {
    Atom *atoms = malloc(nitems * sizeof(Atom));
    CARD32 *input = (CARD32*) buffer;

    if (atoms == NULL)
    {
      #ifdef WARNING
      fprintf(stderr, "%s: WARNING! malloc() failed for '[%s]' - bailing out.\n", __func__, typeS);
      #endif
      return;
    }

    freeMem = True;
    import = True;
    output = (char *) atoms;

    for (int i = 0; i < nitems; i++)
    {
      atoms[i] = nxagentRemoteToLocalAtom((XlibAtom)input[i]);

      if (atoms[i] == None)
      {
        #ifdef WARNING
        fprintf(stderr, "%s: WARNING! Failed to convert remote atom [%ld].\n", __func__,
                    (long int) input[i]);
        #endif
      }
    }
  }
  else if (strcmp(typeS, "WINDOW") == 0)
  {
    Window *input = (Window*) buffer;
    Window *wind = malloc(nitems * sizeof(Window));
    WindowPtr pWindow;

    if (!wind)
    {
      #ifdef WARNING
      fprintf(stderr, "%s: WARNING! malloc() failed for '[%s]' - bailing out.\n", __func__, typeS);
      #endif
      return;
    }
    freeMem = True;
    import = True;
    output = (char*) wind;

    for (int i = 0; i < nitems; i++)
    {
      pWindow = nxagentWindowPtr(input[i]);

      if (pWindow)
      {
        wind[i] = pWindow -> drawable.id;
      }
      else
      {
        #ifdef WARNING
        fprintf(stderr, "%s: WARNING! Failed to look up remote window [0x%lx] importing property [%ld]"
                    " type [%s] on window [%p].\n", __func__,
                        (long int) input[i], (long int) property, typeS, (void*)pWin);
        #endif

        wind[i] = None;
      }
    }
  }

  if (import)
  {
    #ifdef TEST
    fprintf(stderr, "%s: ChangeProperty on window [0x%x] property [%ld] type [%s]"
                " nitems [%ld] format [%d]\n", __func__,
                    window, property, typeS, nitems, format);
    #endif

    ChangeWindowProperty(pWin, propertyL, typeL, format,
                             PropModeReplace, nitems, output, 1);
  }
  else
  {
    #ifdef TEST
    fprintf(stderr, "%s: WARNING! Ignored ChangeProperty on window [0x%x] property [%ld] type [%s]"
                " ntems [%ld] format [%d]\n", __func__,
                       window, property, validateString(typeS), nitems, format);
    #endif
  }

  if (freeMem)
  {
    SAFE_free(output);
  }

  return;
}

/*
 * We want to import all properties changed by external clients to
 * reflect properties of our internal windows but we must ignore all
 * the property notify events generated by our own requests.  For this
 * purpose we implement a FIFO to record every change property request
 * that we dispatch. In this way, when processing a property notify,
 * we can distinguish between the notifications generated by our
 * requests from those generated by other clients connected to the
 * real X server.
 */

struct nxagentPropertyRec{
  Window window;
  XlibAtom property;
  struct nxagentPropertyRec *next;
};

static struct{
  struct nxagentPropertyRec *first;
  struct nxagentPropertyRec *last;
  int size;
} nxagentPropertyList = {NULL, NULL, 0};

/*
 * Removing first element from list.
 */

void nxagentRemovePropertyFromList(void)
{
  struct nxagentPropertyRec *tmp = nxagentPropertyList.first;

  #ifdef TEST
  fprintf(stderr, "%s: Property [%ld] on Window [0x%x] to list, list size is [%d].\n", __func__,
              nxagentPropertyList.first -> property, nxagentPropertyList.first -> window,
                 nxagentPropertyList.size);
  #endif

  if (nxagentPropertyList.first)
  {
    nxagentPropertyList.first = nxagentPropertyList.first -> next;

    if (--nxagentPropertyList.size == 0)
    {
      nxagentPropertyList.last = NULL;
    }

    SAFE_free(tmp);
  }
}

/*
 * Add the record to the list.
 */

void nxagentAddPropertyToList(XlibAtom property, WindowPtr pWin)
{
  if (NXDisplayError(nxagentDisplay) == 1)
  {
    return;
  }

  struct nxagentPropertyRec *tmp = malloc(sizeof(struct nxagentPropertyRec));
  if (tmp == NULL)
  {
    FatalError("%s: malloc() failed.", __func__);
  }

  #ifdef TEST
  fprintf(stderr, "%s: Adding record Property [%ld] - Window [0x%x][%p] to list, list"
              " size is [%d].\n", __func__, property, nxagentWindow(pWin), (void*) pWin,
                 nxagentPropertyList.size);
  #endif

  tmp -> property = property;
  tmp -> window = nxagentWindow(pWin);
  tmp -> next = NULL;

  if (nxagentPropertyList.size == 0)
  {
    nxagentPropertyList.first = tmp;
  }
  else
  {
    nxagentPropertyList.last -> next = tmp;
  }

  nxagentPropertyList.last = tmp;
  nxagentPropertyList.size++;
}

void nxagentFreePropertyList(void)
{
  while (nxagentPropertyList.size != 0)
  {
    nxagentRemovePropertyFromList();
  }
}

/*
 * We are trying to distinguish notifications generated by an external
 * client from those generated by our own requests.
 */

Bool nxagentNotifyMatchChangeProperty(void *p)
{
  struct nxagentPropertyRec *first = nxagentPropertyList.first;
  XPropertyEvent *X = p;

  #ifdef TEST
  fprintf(stderr, "%s: Property notify on window [0x%lx] property [%ld].\n", __func__,
              X -> window, X -> atom);

  if (first)
  {
    fprintf(stderr, "%s: First element on list is window [0x%x] property [%ld] list size is [%d].\n", __func__,
                first -> window, first -> property, nxagentPropertyList.size);
  }
  else
  {
    fprintf(stderr, "%s: List is empty.\n", __func__);
  }
  #endif

  if (first == NULL ||
          X -> window != first -> window ||
              X -> atom != first -> property)
  {
    return False;
  }

  nxagentRemovePropertyFromList();

  return True;
}
