/**************************************************************************/
/*                                                                        */
/* Copyright (c) 2001, 2007 NoMachine, http://www.nomachine.com/.         */
/*                                                                        */
/* NXAGENT, NX protocol compression and NX extensions to this software    */
/* are copyright of NoMachine. Redistribution and use of the present      */
/* software is allowed according to terms specified in the file LICENSE   */
/* which comes in the source distribution.                                */
/*                                                                        */
/* Check http://www.nomachine.com/licensing.html for applicability.       */
/*                                                                        */
/* NX and NoMachine are trademarks of NoMachine S.r.l.                    */
/*                                                                        */
/* All rights reserved.                                                   */
/*                                                                        */
/**************************************************************************/

#include "X.h"

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

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

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

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

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

/*
 * This is currently unused.
 */

#ifdef TEST

static void nxagentPrintRootlessTopLevelWindowMap(void);

void nxagentPrintRootlessTopLevelWindowMap()
{
  int i;

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

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

#endif

void nxagentRootlessAddTopLevelWindow(WindowPtr pWin, Window w)
{
  int i;

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

      topLevelParentMap.elt[i].xid = w;

      return;
    }
  }

  if (topLevelParentMap.next == topLevelParentMap.size)
  {
    TopLevelParentRec *ptr = topLevelParentMap.elt;
    size_t size = (topLevelParentMap.size += TOP_LEVEL_TABLE_UNIT);

    ptr = realloc(ptr, size * sizeof(TopLevelParentRec));

    if (ptr == NULL)
    {
      #ifdef WARNING
      fprintf(stderr, "nxagentRootlessAddTopLevelWindow: Warning failed to allocate memory.\n");
      #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)
{
  int i;

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

  return NULL;
}

void nxagentRootlessDelTopLevelWindow(WindowPtr pWin)
{
  int i;

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

      return;
    }
  }
}

Window nxagentRootlessWMTopLevelWindow(WindowPtr pWin);

void nxagentConfigureRootlessWindow(WindowPtr pWin, int x, int y, int w, int h, int bw,
                                        WindowPtr pSib, int stack_mode, Mask mask)
{
  XWindowChanges changes;
  Window sibw = 0;

  changes.x = x;
  changes.y = y;
  changes.width = w;
  changes.height = h;
  changes.border_width = bw;
  changes.stack_mode = stack_mode;

  if (pSib)
  {
    sibw = nxagentWindow(pSib);
  }

  if (sibw)
  {
    changes.sibling = sibw;
  }

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

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

#ifdef DEBUG

Bool nxagentRootlessTreesMatch()
{
  Window root_return;
  Window parent_return;
  Window *children_return;
  unsigned int nChildrenReturn;
  WindowPtr pW;
  WindowPtr pTestWin = WindowTable[0] -> firstChild;
  Bool treesMatch = True;
  Status result;

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

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

    return False;
  }

  while (nChildrenReturn > 0)
  {
    pW = nxagentWindowPtr(children_return[--nChildrenReturn]);

    if (!pW)
    {
      pW = nxagentRootlessTopLevelWindow(children_return[nChildrenReturn]);
    }

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

  if (children_return)
  {
    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;
  unsigned int ntoplevel;
  int i;
  WindowPtr pWin;
  ClientPtr pClient;
  XID values[2];
  Mask mask;

  toplevel = xalloc(sizeof(WindowPtr) * nchildren);
  ntoplevel = 0;

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

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

    if (pWin && pWin != WindowTable[0])
    {
      toplevel[ntoplevel++] = pWin;
    }
  }

  if (!ntoplevel)
  {
    return;
  }

  #ifdef DEBUG

  fprintf(stderr, "nxagentRootlessRestack: External top level windows before restack:");

  for (i = 0; i < ntoplevel; i++)
  {
    fprintf(stderr, "[%p]\n", toplevel[i]);
  }

  fprintf(stderr, "nxagentRootlessRestack: Internal top level windows before restack:");

  for (pWin = WindowTable[0] -> firstChild; pWin != NULL; pWin = pWin -> nextSib)
  {
    fprintf(stderr, "[%p]\n", pWin);
  }

  #endif

  pWin = WindowTable[0] -> firstChild;

  values[1] = (XID) Above;

  while(ntoplevel-- > 0 && pWin != NULL)
  {
    if (toplevel[ntoplevel] != pWin)
    {
      mask = CWSibling | CWStackMode;
      values[0] = pWin -> drawable.id;
      pClient = wClient(toplevel[ntoplevel]);
      nxagentScreenTrap = 1;
      ConfigureWindow(toplevel[ntoplevel], mask, (XID *) values, pClient);
      nxagentScreenTrap = 0;

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

    pWin = toplevel[ntoplevel] -> nextSib;
  }

  #ifdef DEBUG

  fprintf(stderr, "nxagentRootlessRestack: External top level windows after restack:");

  ntoplevel = i;

  for (i = 0; i < ntoplevel; i++)
  {
    fprintf(stderr, "[%p]\n", toplevel[i]);
  }

  fprintf(stderr, "nxagentRootlessRestack: Internal top level windows after restack:");

  for (pWin = WindowTable[0] -> firstChild; pWin != NULL; pWin = pWin -> nextSib)
  {
    fprintf(stderr, "[%p]\n", pWin);
  }

  #endif

  xfree(toplevel);

  return;
}

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

Window nxagentRootlessWindowParent(WindowPtr pWin)
{
  #ifdef TEST
  fprintf(stderr, "nxagentRootlessWindowParent: Called for window at [%p][%ld] with parent [%p][%ld].\n",
              (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(pWin)
  WindowPtr pWin;
{
  PropertyPtr pProp;
  int total = 0;

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

  return total;
}

int nxagentExportProperty(pWin, property, type, format, mode, nUnits, value)
    WindowPtr   pWin;
    Atom        property, type;
    int         format, mode;
    unsigned long nUnits;
    pointer     value;
{
  char *propertyS, *typeS;
  Atom propertyX, typeX;
  char *output = NULL;
  XWMHints wmHints;
  Bool export = False;
  Bool freeMem = False;

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

  propertyS = NameForAtom(property);
  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, "nxagentExportProperty: WARNING! Ignored ChangeProperty "
                "on %swindow %lx property %s type %s nUnits %ld format %d\n",
                    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));
    int *input = value;
    int i;

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

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

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

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

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

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

        wmHints.icon_pixmap = nxagentPixmap(icon);
      }
      else
      {
        wmHints.flags &= ~IconPixmapHint;

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

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

      if (icon)
      {
        wmHints.icon_window = nxagentWindow(icon);
      }
      else
      {
        wmHints.flags &= ~IconWindowHint;

        #ifdef WARNING
        fprintf(stderr, "nxagentExportProperty: WARNING! Failed to look up icon window %lx from hint "
                    "exporting property %s type %s on window %p.\n",
                        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, SecurityDestroyAccess);

      if (icon)
      {
        wmHints.icon_mask = nxagentPixmap(icon);
      }
      else
      {
        wmHints.flags &= ~IconMaskHint;

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

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

      if (window)
      {
        wmHints.window_group = nxagentWindow(window);
      }
      else
      {
        wmHints.flags &= ~WindowGroupHint;

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

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

    for (i = 0; i < nUnits; i++)
    {
       atoms[i] = nxagentLocalToRemoteAtom(input[i]);

       if (atoms[i] == None)
       {
         #ifdef WARNING
         fprintf(stderr, "nxagentExportProperty: WARNING! Failed to convert local atom %ld [%s].\n",
                     (long int) input[i], validateString(NameForAtom(input[i])));
         #endif
       }
    }
  }
  else if (strcmp(typeS, "WINDOW") == 0)
  {
    Window *input = value;
    XlibWindow *wind = malloc(nUnits * sizeof(*wind));
    ClientPtr pClient = wClient(pWin);
    WindowPtr pWindow;
    int i;

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

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

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

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

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

      export = 0;
    }
    else
    {
      XChangeProperty(nxagentDisplay, nxagentWindow(pWin), propertyX, typeX, format, mode, (void*)output, nUnits);
      nxagentAddPropertyToList(propertyX, pWin);
    }
  }
  else
  {
    #ifdef TEST
    fprintf(stderr, "nxagentExportProperty: WARNING! Ignored ChangeProperty "
                "on %swindow %lx property %s type %s nUnits %ld format %d\n",
                    nxagentWindowTopLevel(pWin) ? "toplevel " : "",
                        nxagentWindow(pWin), validateString(propertyS), validateString(typeS),
                            nUnits, format);
    #endif
  }

  if (freeMem)
  {
    xfree(output);
  }

  return export;
}

void nxagentImportProperty(Window window,
                           Atom property,
                           Atom type,
                           int format,
                           unsigned long nitems,
                           unsigned long bytes_after,
                           unsigned char *buffer)
{
  Atom propertyL;
  Atom typeL;

  WindowPtr pWin;
  Bool import = False;
  Bool freeMem = False;
  XWMHints wmHints;

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

  char *output = NULL;
  char *typeS;

  pWin = nxagentWindowPtr(window);

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

    return;
  }

  propertyL = nxagentRemoteToLocalAtom(property);

  if (!ValidAtom(propertyL))
  {
    #ifdef TEST
    fprintf(stderr, "nxagentImportProperty: Failed to convert remote property atom.\n");
    #endif

    return;
  }

  #ifdef TEST
  fprintf(stderr, "nxagentImportProperty: Window %lx property [%ld]: %s\n",
              window, property, validateString(NameForAtom(propertyL)));
  #endif

  /*
   * We settle a property size limit of
   * 256K beyond which we simply ignore them.
   */

  typeL = nxagentRemoteToLocalAtom(type);
  typeS = NameForAtom(typeL);

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

    import = True;
  }
  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.
     */

    WindowPtr pIcon;

    wmState = *(WMState*)buffer;
    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, "nxagentImportProperty: WARNING! Failed to convert remote window %ld"
                  " importing property %ld of type WM_STATE", (long int) wmState.icon,
                      (long int) property);
      #endif
    }
  }
  else if (strcmp(typeS, "WM_HINTS") == 0)
  {
    wmHints = *(XWMHints*)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, "nxagentImportProperty: WARNING! Failed to look up remote icon "
                    "pixmap %ld from hint importing property [%ld] type %s on window %p.\n",
                        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, "nxagenImportProperty: WARNING! Failed to look up remote icon "
                    "window %lx from hint importing property [%ld] type %s on window %p.\n",
                         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, "nxagentImportProperty: WARNING! Failed to look up remote icon "
                    "mask %lx from hint importing property [%ld] type %s on window %p.\n",
                          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, "nxagentImportProperty: WARNING! Failed to look up remote window "
                    "group %lx from hint importing property [%ld] type %s on window %p.\n",
                          wmHints.window_group, (long int) property, typeS, (void *) pWin);
        #endif
      }
    }
  }
  else if (strcmp(typeS, "ATOM") == 0)
  {
    Atom *atoms = malloc(nitems * sizeof(Atom));
    Atom *input = (Atom*) buffer;
    int i;

    if (atoms == NULL)
    {
      #ifdef WARNING
      fprintf(stderr, "nxagentImportProperty: WARNING! Malloc failed bailing out.\n");
      #endif

      return;
    }

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

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

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

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

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

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

        wind[i] = None;
      }
    }
  }

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

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

  if (freeMem)
  {
    xfree(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 pro-
 * perty 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;
  Atom 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()
{
  struct nxagentPropertyRec *tmp = nxagentPropertyList.first;

  #ifdef TEST
  fprintf(stderr, "nxagentRemovePropertyFromList: Property %ld on Window %lx to list, list size is %d.\n\n",
              nxagentPropertyList.first -> property, nxagentPropertyList.first -> window,
                 nxagentPropertyList.size);
  #endif

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

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

    xfree(tmp);
  }
}

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

void nxagentAddPropertyToList(Atom property, WindowPtr pWin)
{
  struct nxagentPropertyRec *tmp;

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

  if ((tmp = malloc(sizeof(struct nxagentPropertyRec))) == NULL)
  {
    FatalError("nxagentAddPropertyToList: malloc failed.");
  }

  #ifdef TEST
  fprintf(stderr, "nxagentAddPropertyToList: Adding record Property %ld - Window %lx[%p]"
             "to list, list size is %d.\n", 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()
{
  while (nxagentPropertyList.size != 0)
  {
    nxagentRemovePropertyFromList();
  }
}

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

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

  #ifdef TEST
  fprintf(stderr, "nxagentNotifyMatchChangeProperty: Property notify on window %lx property %ld.\n",
              X -> window, X -> atom);

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

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

  nxagentRemovePropertyFromList();

  return True;
}

