#include <string.h>

#include <Ecore.h>

#include "ecore_xcb_private.h"
#include "Ecore_X_Atoms.h"

static int ignore_num = 0;
static Ecore_X_Window *ignore_list = NULL;

static Ecore_X_Window      _ecore_x_window_at_xy_get(Ecore_X_Window  base,
                                                     int16_t         base_x,
                                                     int16_t         base_y,
                                                     int16_t         x,
                                                     int16_t         y,
                                                     Ecore_X_Window *skip,
                                                     int             skip_num);

#ifdef ECORE_XCB_RENDER
static Ecore_X_Window      _ecore_x_window_argb_internal_new(Ecore_X_Window parent,
                                                             int16_t        x,
                                                             int16_t        y,
                                                             uint16_t       w,
                                                             uint16_t       h,
                                                             uint8_t        override_redirect,
                                                             uint8_t        save_under);
#endif /* ECORE_XCB_RENDER */

/**
 * @defgroup Ecore_X_Window_Create_Group X Window Creation Functions
 *
 * Functions that can be used to create an X window.
 */

/**
 * Creates a new window.
 * @param   parent The parent window to use.  If @p parent is @c 0, the root
 *                 window of the default display is used.
 * @param   x      X position.
 * @param   y      Y position.
 * @param   w      Width.
 * @param   h      Height.
 * @return  The new window handle.
 * @ingroup Ecore_X_Window_Create_Group
 */
EAPI Ecore_X_Window
ecore_x_window_new(Ecore_X_Window parent,
                   int            x,
                   int            y,
                   int            width,
                   int            height)
{
   uint32_t value_list[9];
   Ecore_X_Window window;
   xcb_visualid_t vis = { XCB_WINDOW_CLASS_COPY_FROM_PARENT };
   uint32_t value_mask;

   if (parent == 0)
      parent = ((xcb_screen_t *)_ecore_xcb_screen)->root;

   value_mask =
      XCB_CW_BACK_PIXMAP | XCB_CW_BORDER_PIXEL | XCB_CW_BIT_GRAVITY |
      XCB_CW_WIN_GRAVITY | XCB_CW_BACKING_STORE | XCB_CW_OVERRIDE_REDIRECT |
      XCB_CW_SAVE_UNDER | XCB_CW_EVENT_MASK | XCB_CW_DONT_PROPAGATE;

   value_list[0] = XCB_NONE;
   value_list[1] = 0;
   value_list[2] = XCB_GRAVITY_NORTH_WEST;
   value_list[3] = XCB_GRAVITY_NORTH_WEST;
   value_list[4] = XCB_BACKING_STORE_NOT_USEFUL;
   value_list[5] = 0;
   value_list[6] = 0;
   value_list[7] =
      XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE |
      XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE |
      XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW |
      XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_EXPOSURE |
      XCB_EVENT_MASK_VISIBILITY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY |
      XCB_EVENT_MASK_FOCUS_CHANGE | XCB_EVENT_MASK_PROPERTY_CHANGE |
      XCB_EVENT_MASK_COLOR_MAP_CHANGE;
   value_list[8] = XCB_EVENT_MASK_NO_EVENT;

   window = xcb_generate_id(_ecore_xcb_conn);
   xcb_create_window(_ecore_xcb_conn,
                     XCB_WINDOW_CLASS_COPY_FROM_PARENT,
                     window, parent, x, y, width, height, 0,
                     XCB_WINDOW_CLASS_INPUT_OUTPUT,
                     vis,
                     value_mask,
                     value_list);

   if (parent == ((xcb_screen_t *)_ecore_xcb_screen)->root)
      ecore_x_window_defaults_set(window);

   return window;
} /* ecore_x_window_new */

/**
 * Creates a window with the override redirect attribute set to @c True.
 * @param   parent The parent window to use.  If @p parent is @c 0, the root
 *                 window of the default display is used.
 * @param   x      X position.
 * @param   y      Y position.
 * @param   w      Width.
 * @param   h      Height.
 * @return  The new window handle.
 * @ingroup Ecore_X_Window_Create_Group
 */
EAPI Ecore_X_Window
ecore_x_window_override_new(Ecore_X_Window parent,
                            int            x,
                            int            y,
                            int            width,
                            int            height)
{
   uint32_t value_list[9];
   Ecore_X_Window window;
   xcb_visualid_t vis = { XCB_WINDOW_CLASS_COPY_FROM_PARENT };
   uint32_t value_mask;

   if (parent == 0)
      parent = ((xcb_screen_t *)_ecore_xcb_screen)->root;

   value_mask =
      XCB_CW_BACK_PIXMAP | XCB_CW_BORDER_PIXEL | XCB_CW_BIT_GRAVITY |
      XCB_CW_WIN_GRAVITY | XCB_CW_BACKING_STORE | XCB_CW_OVERRIDE_REDIRECT |
      XCB_CW_SAVE_UNDER | XCB_CW_EVENT_MASK | XCB_CW_DONT_PROPAGATE;

   value_list[0] = XCB_NONE;
   value_list[1] = 0;
   value_list[2] = XCB_GRAVITY_NORTH_WEST;
   value_list[3] = XCB_GRAVITY_NORTH_WEST;
   value_list[4] = XCB_BACKING_STORE_NOT_USEFUL;
   value_list[5] = 1;
   value_list[6] = 0;
   value_list[7] =
      XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE |
      XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE |
      XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW |
      XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_EXPOSURE |
      XCB_EVENT_MASK_VISIBILITY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY |
      XCB_EVENT_MASK_FOCUS_CHANGE | XCB_EVENT_MASK_PROPERTY_CHANGE |
      XCB_EVENT_MASK_COLOR_MAP_CHANGE;
   value_list[8] = XCB_EVENT_MASK_NO_EVENT;

   window = xcb_generate_id(_ecore_xcb_conn);
   xcb_create_window(_ecore_xcb_conn,
                     XCB_WINDOW_CLASS_COPY_FROM_PARENT,
                     window, parent, x, y, width, height, 0,
                     XCB_WINDOW_CLASS_INPUT_OUTPUT,
                     vis,
                     value_mask,
                     value_list);
   return window;
} /* ecore_x_window_override_new */

/**
 * Creates a new input window.
 * @param   parent The parent window to use.    If @p parent is @c 0, the root
 *                 window of the default display is used.
 * @param   x      X position.
 * @param   y      Y position.
 * @param   w      Width.
 * @param   h      Height.
 * @return  The new window.
 * @ingroup Ecore_X_Window_Create_Group
 */
EAPI Ecore_X_Window
ecore_x_window_input_new(Ecore_X_Window parent,
                         int            x,
                         int            y,
                         int            width,
                         int            height)
{
   uint32_t value_list[3];
   Ecore_X_Window window;
   xcb_visualid_t vis = { XCB_WINDOW_CLASS_COPY_FROM_PARENT };
   uint32_t value_mask;

   if (parent == 0)
      parent = ((xcb_screen_t *)_ecore_xcb_screen)->root;

   value_mask = XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK | XCB_CW_DONT_PROPAGATE;

   value_list[0] = 1;
   value_list[1] =
      XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE |
      XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE |
      XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW |
      XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_EXPOSURE |
      XCB_EVENT_MASK_VISIBILITY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY |
      XCB_EVENT_MASK_FOCUS_CHANGE | XCB_EVENT_MASK_PROPERTY_CHANGE |
      XCB_EVENT_MASK_COLOR_MAP_CHANGE;
   value_list[2] = XCB_EVENT_MASK_NO_EVENT;

   window = xcb_generate_id(_ecore_xcb_conn);
   xcb_create_window(_ecore_xcb_conn,
                     XCB_WINDOW_CLASS_COPY_FROM_PARENT,
                     window, parent, x, y, width, height, 0,
                     XCB_WINDOW_CLASS_INPUT_OUTPUT,
                     vis,
                     value_mask,
                     value_list);

   if (parent == ((xcb_screen_t *)_ecore_xcb_screen)->root)
     {
     }

   return window;
} /* ecore_x_window_input_new */

/**
 * Creates a new window.
 * @param   parent The parent window to use.  If @p parent is @c 0, the root
 *                 window of the default display is used.
 * @param   x      X position.
 * @param   y      Y position.
 * @param   width  Width.
 * @param   height Height.
 * @return         The new window handle.
 * @ingroup Ecore_X_Window_Create_Group
 */
EAPI Ecore_X_Window
ecore_x_window_manager_argb_new(Ecore_X_Window parent,
                                int            x,
                                int            y,
                                int            width,
                                int            height)
{
   Ecore_X_Window window = 0;

#ifdef ECORE_XCB_RENDER
   window = _ecore_x_window_argb_internal_new(parent,
                                              x, y, width, height,
                                              1, 0);
#endif /* ECORE_XCB_RENDER */

   return window;
} /* ecore_x_window_manager_argb_new */

/**
 * Creates a new window.
 * @param   parent The parent window to use.  If @p parent is @c 0, the root
 *                 window of the default display is used.
 * @param   x      X position.
 * @param   y      Y position.
 * @param   width  Width.
 * @param   height Height.
 * @return         The new window handle.
 * @ingroup Ecore_X_Window_Create_Group
 */
EAPI Ecore_X_Window
ecore_x_window_argb_new(Ecore_X_Window parent,
                        int            x,
                        int            y,
                        int            width,
                        int            height)
{
   Ecore_X_Window window = 0;

#ifdef ECORE_XCB_RENDER
   window = _ecore_x_window_argb_internal_new(parent,
                                              x, y, width, height,
                                              0, 0);
#endif /* ECORE_XCB_RENDER */

   return window;
} /* ecore_x_window_argb_new */

/**
 * Creates a window with the override redirect attribute set to @c True.
 * @param   parent The parent window to use.  If @p parent is @c 0, the root
 *                 window of the default display is used.
 * @param   x      X position.
 * @param   y      Y position.
 * @param   width  Width.
 * @param   height Height.
 * @return         The new window handle.
 * @ingroup Ecore_X_Window_Create_Group
 */
EAPI Ecore_X_Window
ecore_x_window_override_argb_new(Ecore_X_Window parent,
                                 int            x,
                                 int            y,
                                 int            width,
                                 int            height)
{
   Ecore_X_Window window = 0;

#ifdef ECORE_XCB_RENDER
   window = _ecore_x_window_argb_internal_new(parent,
                                              x, y, width, height,
                                              1, 0);
#endif /* ECORE_XCB_RENDER */

   return window;
} /* ecore_x_window_override_argb_new */

/**
 * @defgroup Ecore_X_Window_Destroy_Group X Window Destroy Functions
 *
 * Functions to destroy X windows.
 */

/**
 * Deletes the given window.
 * @param   window The given window.
 * @ingroup Ecore_X_Window_Destroy_Group
 */
EAPI void
ecore_x_window_free(Ecore_X_Window window)
{
   /* sorry sir, deleting the root window doesn't sound like
    * a smart idea.
    */
   if (window)
      xcb_destroy_window(_ecore_xcb_conn, window);
} /* ecore_x_window_free */

/**
 * Sends a delete request to the given window.
 * @param   window The given window.
 * @ingroup Ecore_X_Window_Destroy_Group
 */
EAPI void
ecore_x_window_delete_request_send(Ecore_X_Window window)
{
   xcb_client_message_event_t ev;

   /* sorry sir, deleting the root window doesn't sound like
    * a smart idea.
    */
   if (window == 0)
      return;

   ev.response_type = XCB_CLIENT_MESSAGE;
   ev.format = 32;
   ev.sequence = 0;
   ev.window = window;
   ev.type = ECORE_X_ATOM_WM_PROTOCOLS;
   ev.data.data32[0] = ECORE_X_ATOM_WM_DELETE_WINDOW;
   ev.data.data32[1] = XCB_CURRENT_TIME;

   xcb_send_event(_ecore_xcb_conn, 0, window,
                  XCB_EVENT_MASK_NO_EVENT, (const char *)&ev);
} /* ecore_x_window_delete_request_send */

/**
 * @defgroup Ecore_X_Window_Configure_Group X Window Configure Functions
 *
 * Functions to configure X windows.
 */

/**
 * Configures the given window with the given mask.
 * @param   window       The given window.
 * @param   mask         The given mask.
 * @param   x            The X coordinate of the window.
 * @param   y            The Y coordinate of the window.
 * @param   width        The width of the window.
 * @param   height       The height of the window.
 * @param   border_width The border width of the window.
 * @param   sibling      The sibling window of the window.
 * @param   stack_mode   The stack mode of the window.
 * @ingroup Ecore_X_Window_Configure_Group
 */
EAPI void
ecore_x_window_configure(Ecore_X_Window                window,
                         Ecore_X_Window_Configure_Mask mask,
                         int                           x,
                         int                           y,
                         int                           width,
                         int                           height,
                         int                           border_width,
                         Ecore_X_Window                sibling,
                         int                           stack_mode)
{
   uint32_t *value_list;
   uint32_t value_mask;
   int length = 0;

   if (!window)
      return;

   value_mask = mask;
   for (; value_mask; value_mask >>= 1)
      if (value_mask & 1)
         length++;

   value_list = (uint32_t *)malloc(sizeof(uint32_t) * length);
   if (!value_list)
      return;

   value_mask = mask;
   for (; value_mask; value_mask >>= 1, value_list++)
      if (value_mask & 1)
        {
           switch (value_mask) {
              case XCB_CONFIG_WINDOW_X:
                 *value_list = x;
                 break;

              case XCB_CONFIG_WINDOW_Y:
                 *value_list = y;
                 break;

              case XCB_CONFIG_WINDOW_WIDTH:
                 *value_list = width;
                 break;

              case XCB_CONFIG_WINDOW_HEIGHT:
                 *value_list = height;
                 break;

              case XCB_CONFIG_WINDOW_BORDER_WIDTH:
                 *value_list = border_width;
                 break;

              case XCB_CONFIG_WINDOW_SIBLING:
                 *value_list = sibling;
                 break;

              case XCB_CONFIG_WINDOW_STACK_MODE:
                 *value_list = stack_mode;
                 break;
             } /* switch */
        }

   xcb_configure_window(_ecore_xcb_conn, window, mask, value_list);
   free(value_list);
} /* ecore_x_window_configure */

/**
 * Moves a window to the position @p x, @p y.
 *
 * The position is relative to the upper left hand corner of the
 * parent window.
 *
 * @param   window The window to move.
 * @param   x      X position.
 * @param   y      Y position.
 * @ingroup Ecore_X_Window_Configure_Group
 */
EAPI void
ecore_x_window_move(Ecore_X_Window window,
                    int            x,
                    int            y)
{
   uint32_t value_list[2];
   uint32_t value_mask;

   if (!window)
      return;

   value_mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y;

   value_list[0] = x;
   value_list[1] = y;

   xcb_configure_window(_ecore_xcb_conn, window, value_mask, value_list);
} /* ecore_x_window_move */

/**
 * Resizes a window.
 * @param   window The window to resize.
 * @param   width  New width of the window.
 * @param   height New height of the window.
 * @ingroup Ecore_X_Window_Configure_Group
 */
EAPI void
ecore_x_window_resize(Ecore_X_Window window,
                      int            width,
                      int            height)
{
   uint32_t value_list[2];
   uint32_t value_mask;

   if (!window)
      return;

   if (width < 1)
      width = 1;

   if (height < 1)
      height = 1;

   value_mask = XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;

   value_list[0] = width;
   value_list[1] = height;

   xcb_configure_window(_ecore_xcb_conn, window, value_mask, value_list);
} /* ecore_x_window_resize */

/**
 * Moves and resizes a window.
 * @param   window The window to move and resize.
 * @param   x      New X position of the window.
 * @param   y      New Y position of the window.
 * @param   width  New width of the window.
 * @param   height New height of the window.
 * @ingroup Ecore_X_Window_Configure_Group
 */
EAPI void
ecore_x_window_move_resize(Ecore_X_Window window,
                           int            x,
                           int            y,
                           int            width,
                           int            height)
{
   uint32_t value_list[4];
   uint32_t value_mask;

   if (!window)
      return;

   if (width < 1)
      width = 1;

   if (height < 1)
      height = 1;

   value_mask =
      XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y |
      XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;

   value_list[0] = x;
   value_list[1] = y;
   value_list[2] = width;
   value_list[3] = height;

   xcb_configure_window(_ecore_xcb_conn, window, value_mask, value_list);
} /* ecore_x_window_move_resize */

/**
 * Sets the width of the border of the given window.
 * @param   window       The given window.
 * @param   border_width The new border width.
 * @ingroup Ecore_X_Window_Configure_Group
 */
EAPI void
ecore_x_window_border_width_set(Ecore_X_Window window,
                                int            border_width)
{
   uint32_t value_list;

   /* doesn't make sense to call this on a root window */
   if (!window)
      return;

   value_list = border_width;

   xcb_configure_window(_ecore_xcb_conn, window, XCB_CONFIG_WINDOW_BORDER_WIDTH, &value_list);
} /* ecore_x_window_border_width_set */

/**
 * Raises the given window.
 * @param   window The window to raise.
 * @ingroup Ecore_X_Window_Configure_Group
 */
EAPI void
ecore_x_window_raise(Ecore_X_Window window)
{
   uint32_t value_list;

   if (!window)
      return;

   value_list = XCB_STACK_MODE_ABOVE;

   xcb_configure_window(_ecore_xcb_conn, window, XCB_CONFIG_WINDOW_STACK_MODE, &value_list);
} /* ecore_x_window_raise */

/**
 * Lowers the given window.
 * @param   window The window to lower.
 * @ingroup Ecore_X_Window_Configure_Group
 */
EAPI void
ecore_x_window_lower(Ecore_X_Window window)
{
   uint32_t value_list;

   if (!window)
      return;

   value_list = XCB_STACK_MODE_BELOW;

   xcb_configure_window(_ecore_xcb_conn, window, XCB_CONFIG_WINDOW_STACK_MODE, &value_list);
} /* ecore_x_window_lower */

/**
 * @defgroup Ecore_X_Window_Change_Properties_Group X Window Change Property Functions
 *
 * Functions that change window properties.
 */

/**
 * Sets the default properties for the given window.
 *
 * The default properties set for the window are @c WM_CLIENT_MACHINE and
 * @c _NET_WM_PID.
 *
 * @param   window The given window.
 * @ingroup Ecore_X_Window_Change_Property_Group
 */
EAPI void
ecore_x_window_defaults_set(Ecore_X_Window window)
{
   char buf[MAXHOSTNAMELEN];
   pid_t pid;
   int argc;
   char **argv;

   /*
    * Set WM_CLIENT_MACHINE.
    */
   gethostname(buf, MAXHOSTNAMELEN);
   buf[MAXHOSTNAMELEN - 1] = '\0';
   /* The ecore function uses UTF8 which Xlib may not like (especially
    * with older clients) */
   /* ecore_xcb_window_prop_string_set(win, ECORE_X_ATOM_WM_CLIENT_MACHINE,
                                  (char *)buf); */
   xcb_change_property(_ecore_xcb_conn, XCB_PROP_MODE_REPLACE, window,
                       ECORE_X_ATOM_WM_CLIENT_MACHINE,
                       ECORE_X_ATOM_STRING,
                       8, strlen(buf), buf);

   /*
    * Set _NET_WM_PID
    */
   pid = getpid();
   ecore_x_netwm_pid_set(window, pid);

   ecore_x_netwm_window_type_set(window, ECORE_X_WINDOW_TYPE_NORMAL);

   ecore_app_args_get(&argc, &argv);
   ecore_x_icccm_command_set(window, argc, argv);
} /* ecore_x_window_defaults_set */

/**
 * @defgroup Ecore_X_Window_Visibility_Group X Window Visibility Functions
 *
 * Functions to change the visibility of X windows.
 */

/**
 * Shows a window.
 *
 * Synonymous to "mapping" a window in X Window System terminology.
 *
 * @param   window The window to show.
 * @ingroup Ecore_X_Window_Visibility_Group
 */
EAPI void
ecore_x_window_show(Ecore_X_Window window)
{
   xcb_map_window(_ecore_xcb_conn, window);
} /* ecore_x_window_show */

/**
 * Hides a window.
 *
 * Synonymous to "unmapping" a window in X Window System terminology.
 *
 * @param   window The window to hide.
 * @ingroup Ecore_X_Window_Visibility_Group
 */
EAPI void
ecore_x_window_hide(Ecore_X_Window window)
{
   xcb_unmap_notify_event_t ev;
   Ecore_X_Window root;

   /* ICCCM: SEND unmap event... */
   root = window;
   /* FIXME: is it correct ? */
   if (xcb_setup_roots_iterator(xcb_get_setup(_ecore_xcb_conn)).rem == 1)
      root = ((xcb_screen_t *)_ecore_xcb_screen)->root;
   else
     {
        xcb_get_geometry_cookie_t cookie;
        xcb_get_geometry_reply_t *rep;
        Ecore_X_Drawable draw;

        /* FIXME: can we avoid round trips, here ? */
        draw = window;
        cookie = xcb_get_geometry_unchecked(_ecore_xcb_conn, draw);
        rep = xcb_get_geometry_reply(_ecore_xcb_conn, cookie, NULL);
        if (!rep)
           return;

        root = rep->root;
        free(rep);
     }

   ev.response_type = XCB_UNMAP_NOTIFY;
   ev.pad0 = 0;
   ev.sequence = 0;
   ev.event = root;
   ev.window = window;
   ev.from_configure = 0;

   xcb_send_event(_ecore_xcb_conn, 0, root,
                  XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
                  (const char *)&ev);
   xcb_unmap_window(_ecore_xcb_conn, window);
} /* ecore_x_window_hide */

/**
 * @defgroup Ecore_X_Window_Input_Focus_Group X Window Input Focus Functions
 *
 * Functions that manage the focus of an X Window.
 */

/**
 * Sets the focus to the window @p window.
 * @param   window The window to focus.
 * @ingroup Ecore_X_Window_Input_Focus_Group
 */
EAPI void
ecore_x_window_focus(Ecore_X_Window window)
{
   Ecore_X_Time time = XCB_CURRENT_TIME;

   if (window == 0)
      window = ((xcb_screen_t *)_ecore_xcb_screen)->root;

   /* xcb_set_input_focus(_ecore_xcb_conn, XCB_INPUT_FOCUS_NONE, win, time); */
   xcb_set_input_focus(_ecore_xcb_conn,
                       XCB_INPUT_FOCUS_POINTER_ROOT, window, time);
} /* ecore_x_window_focus */

/**
 * Sets the focus to the given window at a specific time.
 * @param   window The window to focus.
 * @param   time   When to set the focus to the window.
 * @ingroup Ecore_X_Window_Input_Focus_Group
 */
EAPI void
ecore_x_window_focus_at_time(Ecore_X_Window window,
                             Ecore_X_Time   time)
{
   if (window == 0)
      window = ((xcb_screen_t *)_ecore_xcb_screen)->root;

   /* xcb_set_input_focus(_ecore_xcb_conn, XCB_INPUT_FOCUS_NONE, win, time); */
   xcb_set_input_focus(_ecore_xcb_conn,
                       XCB_INPUT_FOCUS_POINTER_ROOT, window, time);
} /* ecore_x_window_focus_at_time */

/**
 * @defgroup Ecore_X_Window_Reparent_Group X Window Reparent Functions
 *
 * Functions that retrieve or changes the parent window of a window.
 */

/**
 * Moves a window to within another window at a given position.
 * @param   window     The window to reparent.
 * @param   new_parent The new parent window.
 * @param   x          X position within new parent window.
 * @param   y          Y position within new parent window.
 * @ingroup Ecore_X_Window_Reparent_Group
 */
EAPI void
ecore_x_window_reparent(Ecore_X_Window window,
                        Ecore_X_Window new_parent,
                        int            x,
                        int            y)
{
   if (new_parent == 0)
      new_parent = ((xcb_screen_t *)_ecore_xcb_screen)->root;

   xcb_reparent_window(_ecore_xcb_conn, window, new_parent, x, y);
} /* ecore_x_window_reparent */

/**
 * @defgroup Ecore_X_Window_Change_Attributes_Group X Window Change Attributes Functions
 *
 * Functions that change the attributes of a window.
 */

/**
 * Sets the background pixmap of the given window.
 * @param   window The given window.
 * @param   pixmap The pixmap to set to.
 * @ingroup Ecore_X_Window_Change_Attributes_Group
 */
EAPI void
ecore_x_window_pixmap_set(Ecore_X_Window window,
                          Ecore_X_Pixmap pixmap)
{
   uint32_t value_list;

   value_list = pixmap;
   xcb_change_window_attributes(_ecore_xcb_conn, window,
                                XCB_CW_BACK_PIXMAP, &value_list);
} /* ecore_x_window_pixmap_set */

/**
 * Sets the background color of the given window.
 * @param   window The given window.
 * @param   red    The red component of the color to set to.
 * @param   green  The green component of the color to set to.
 * @param   blue   The blue component of the color to set to.
 * @ingroup Ecore_X_Window_Change_Attributes_Group
 */
EAPI void
ecore_x_window_background_color_set(Ecore_X_Window window,
                                    unsigned short red,
                                    unsigned short green,
                                    unsigned short blue)
{
   xcb_alloc_color_cookie_t cookie;
   xcb_alloc_color_reply_t *rep;
   uint32_t value_list;

   /* FIXME: should I provide a reply, and not the color components, here ? */
   /*        (because of roundtrips) */
   cookie = xcb_alloc_color_unchecked(_ecore_xcb_conn,
                                      ((xcb_screen_t *)_ecore_xcb_screen)->default_colormap,
                                      red, green, blue);
   rep = xcb_alloc_color_reply(_ecore_xcb_conn, cookie, NULL);
   if (!rep)
      return;

   value_list = rep->pixel;
   xcb_change_window_attributes(_ecore_xcb_conn, window,
                                XCB_CW_BACK_PIXEL, &value_list);
   free(rep);
} /* ecore_x_window_background_color_set */

/**
 * Sets the bit gravity of the given window.
 * @param   window  The given window.
 * @param   gravity The gravity.
 * @ingroup Ecore_X_Window_Change_Attributes_Group
 */
EAPI void
ecore_x_window_pixel_gravity_set(Ecore_X_Window  window,
                                 Ecore_X_Gravity gravity)
{
   uint32_t value_list;

   value_list = gravity;
   xcb_change_window_attributes(_ecore_xcb_conn, window,
                                XCB_CW_BIT_GRAVITY, &value_list);
} /* ecore_x_window_pixel_gravity_set */

/**
 * Sets the gravity of the given window.
 * @param   window  The given window.
 * @param   gravity The gravity.
 * @ingroup Ecore_X_Window_Change_Attributes_Group
 */
EAPI void
ecore_x_window_gravity_set(Ecore_X_Window  window,
                           Ecore_X_Gravity gravity)
{
   uint32_t value_list;

   value_list = gravity;
   xcb_change_window_attributes(_ecore_xcb_conn, window,
                                XCB_CW_WIN_GRAVITY, &value_list);
} /* ecore_x_window_gravity_set */

/**
 * Sets the override attribute of the given window.
 * @param   window            The given window.
 * @param   override_redirect The override_redirect boolean.
 * @ingroup Ecore_X_Window_Change_Attributes_Group
 */
EAPI void
ecore_x_window_override_set(Ecore_X_Window window,
                            Eina_Bool      override_redirect)
{
   uint32_t value_list;

   value_list = override_redirect;
   xcb_change_window_attributes(_ecore_xcb_conn, window,
                                XCB_CW_OVERRIDE_REDIRECT, &value_list);
} /* ecore_x_window_override_set */

/**
 * Shows the cursor of the given window.
 * @param   window The given window.
 * @param   show   If set to @c 0, hide the cursor. Show it otherwise.
 * @ingroup Ecore_X_Window_Change_Attributes_Group
 */
EAPI void
ecore_x_window_cursor_show(Ecore_X_Window window,
                           Eina_Bool      show)
{
   if (window == 0)
      window = ((xcb_screen_t *)_ecore_xcb_screen)->root;

   if (!show)
     {
        Ecore_X_Cursor cursor;
        Ecore_X_Drawable draw;
        Ecore_X_Pixmap pixmap;
        Ecore_X_Pixmap mask;
        Ecore_X_GC gc;
        xcb_point_t point;
        uint32_t value_list;

        draw = window;
        pixmap = xcb_generate_id(_ecore_xcb_conn);
        xcb_create_pixmap(_ecore_xcb_conn,
                          1, pixmap, draw,
                          1, 1);
        mask = xcb_generate_id(_ecore_xcb_conn);
        xcb_create_pixmap(_ecore_xcb_conn,
                          1, mask, draw,
                          1, 1);

        gc = xcb_generate_id(_ecore_xcb_conn);
        xcb_create_gc (_ecore_xcb_conn, gc, draw, 0, NULL);
        value_list = 0;
        xcb_change_gc(_ecore_xcb_conn, gc, XCB_GC_FOREGROUND, &value_list);

        draw = mask;
        point.x = 0;
        point.y = 0;
        xcb_poly_point(_ecore_xcb_conn, XCB_COORD_MODE_ORIGIN, draw,
                       gc, 1, &point);

        xcb_free_gc(_ecore_xcb_conn, gc);

        cursor = xcb_generate_id(_ecore_xcb_conn);
        xcb_create_cursor(_ecore_xcb_conn, cursor,
                          pixmap, mask,
                          0, 0, 0,
                          0, 0, 0,
                          0, 0);
        value_list = cursor;
        xcb_change_window_attributes(_ecore_xcb_conn, window,
                                     XCB_CW_CURSOR, &value_list);

        xcb_free_cursor(_ecore_xcb_conn, cursor);
        xcb_free_pixmap(_ecore_xcb_conn, mask);
        xcb_free_pixmap(_ecore_xcb_conn, pixmap);
     }
   else
     {
        uint32_t value_list;

        value_list = 0;
        xcb_change_window_attributes(_ecore_xcb_conn, window,
                                     XCB_CW_CURSOR, &value_list);
     }
} /* ecore_x_window_cursor_show */

/**
 * Sets the cursor of the given window.
 * @param   window The given window.
 * @param   cursor The given cursor.
 * @ingroup Ecore_X_Window_Change_Attributes_Group
 */
EAPI void
ecore_x_window_cursor_set(Ecore_X_Window window,
                          Ecore_X_Cursor cursor)
{
   uint32_t value_list;

   value_list = cursor;
   xcb_change_window_attributes(_ecore_xcb_conn, window,
                                XCB_CW_CURSOR, &value_list);
} /* ecore_x_window_cursor_set */

/**
 * Todo
 * @param   window The given window.
 * @ingroup Ecore_X_Window_Change_Attributes_Group
 */
EAPI void
ecore_x_window_container_manage(Ecore_X_Window window)
{
   uint32_t value_list;

   value_list =
      XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
      XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT;
   xcb_change_window_attributes(_ecore_xcb_conn, window,
                                XCB_CW_EVENT_MASK, &value_list);
} /* ecore_x_window_container_manage */

/**
 * Todo
 * @param   window The given window.
 * @ingroup Ecore_X_Window_Change_Attributes_Group
 */
EAPI void
ecore_x_window_client_manage(Ecore_X_Window window)
{
   uint32_t value_list;

   value_list =
      XCB_EVENT_MASK_VISIBILITY_CHANGE |
/*      XCB_EVENT_MASK_RESIZE_REDIRECT | */
      XCB_EVENT_MASK_STRUCTURE_NOTIFY |
      XCB_EVENT_MASK_FOCUS_CHANGE |
      XCB_EVENT_MASK_PROPERTY_CHANGE |
      XCB_EVENT_MASK_COLOR_MAP_CHANGE;
   xcb_change_window_attributes(_ecore_xcb_conn, window,
                                XCB_CW_EVENT_MASK, &value_list);
#ifdef ECORE_XCB_SHAPE
   xcb_shape_select_input(_ecore_xcb_conn, window, 1);
#endif /* ECORE_XCB_SHAPE */
} /* ecore_x_window_client_manage */

/**
 * Todo
 * @param   window The given window.
 * @ingroup Ecore_X_Window_Change_Attributes_Group
 */
EAPI void
ecore_x_window_sniff(Ecore_X_Window window)
{
   uint32_t value_list;

   value_list =
      XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
      XCB_EVENT_MASK_PROPERTY_CHANGE;
   xcb_change_window_attributes(_ecore_xcb_conn, window,
                                XCB_CW_EVENT_MASK, &value_list);
} /* ecore_x_window_sniff */

/**
 * Todo
 * @param   window The given window.
 * @ingroup Ecore_X_Window_Change_Attributes_Group
 */
EAPI void
ecore_x_window_client_sniff(Ecore_X_Window window)
{
   uint32_t value_list;

   value_list =
      XCB_EVENT_MASK_VISIBILITY_CHANGE |
      XCB_EVENT_MASK_STRUCTURE_NOTIFY |
      XCB_EVENT_MASK_FOCUS_CHANGE |
      XCB_EVENT_MASK_PROPERTY_CHANGE |
      XCB_EVENT_MASK_COLOR_MAP_CHANGE;
   xcb_change_window_attributes(_ecore_xcb_conn, window,
                                XCB_CW_EVENT_MASK, &value_list);
#ifdef ECORE_XCB_SHAPE
   xcb_shape_select_input(_ecore_xcb_conn, window, 1);
#endif /* ECORE_XCB_SHAPE */
} /* ecore_x_window_client_sniff */

/**
 * Clears an area of the given window.
 * @param   window  The given window.
 * @param   x            The X coordinate of the area.
 * @param   y            The Y coordinate of the area.
 * @param   width        The width of the area.
 * @param   height       The height of the area.
 * @ingroup Ecore_X_Window_Clear_Area_Group
 */
EAPI void
ecore_x_window_area_clear(Ecore_X_Window window,
                          int            x,
                          int            y,
                          int            width,
                          int            height)
{
   xcb_clear_area(_ecore_xcb_conn, 0, window, x, y, width, height);
} /* ecore_x_window_area_clear */

/**
 * Exposes an area of the given window.
 * @param   window  The given window.
 * @param   x            The X coordinate of the area.
 * @param   y            The Y coordinate of the area.
 * @param   width        The width of the area.
 * @param   height       The height of the area.
 * @ingroup Ecore_X_Window_Clear_Area_Group
 */
EAPI void
ecore_x_window_area_expose(Ecore_X_Window window,
                           int            x,
                           int            y,
                           int            width,
                           int            height)
{
   xcb_clear_area(_ecore_xcb_conn, 1, window, x, y, width, height);
} /* ecore_x_window_area_expose */

/**
 * @defgroup Ecore_X_Window_Save_Set_Group X Window Change Save Set Functions
 *
 * Functions that either inserts or deletes the specified window from
 * the client's save-set.
 */

/**
 * Inserts the window in the client's save-set.
 * @param   window The window to insert in the client's save-set.
 * @ingroup Ecore_X_Window_Save_Set_Group
 */
EAPI void
ecore_x_window_save_set_add(Ecore_X_Window window)
{
   xcb_change_save_set(_ecore_xcb_conn, XCB_SET_MODE_INSERT, window);
} /* ecore_x_window_save_set_add */

/**
 * Deletes the window from the client's save-set.
 * @param   window The window to delete from the client's save-set.
 * @ingroup Ecore_X_Window_Save_Set_Group
 */
EAPI void
ecore_x_window_save_set_del(Ecore_X_Window window)
{
   xcb_change_save_set(_ecore_xcb_conn, XCB_SET_MODE_DELETE, window);
} /* ecore_x_window_save_set_del */

/******************************
*
* Request that have a reply
*
******************************/

/**
 * Sends the GetInputFocus request.
 * @ingroup Ecore_X_Window_Input_Focus_Group
 */
EAPI void
ecore_x_get_input_focus_prefetch(void)
{
   xcb_get_input_focus_cookie_t cookie;

   cookie = xcb_get_input_focus_unchecked(_ecore_xcb_conn);
   _ecore_xcb_cookie_cache(cookie.sequence);
} /* ecore_x_get_input_focus_prefetch */

/**
 * Gets the reply of the GetInputFocus request sent by ecore_x_get_input_focus_prefetch().
 * @ingroup Ecore_X_Window_Input_Focus_Group
 */
EAPI void
ecore_x_get_input_focus_fetch(void)
{
   xcb_get_input_focus_cookie_t cookie;
   xcb_get_input_focus_reply_t *reply;

   cookie.sequence = _ecore_xcb_cookie_get();
   reply = xcb_get_input_focus_reply(_ecore_xcb_conn, cookie, NULL);
   _ecore_xcb_reply_cache(reply);
} /* ecore_x_get_input_focus_fetch */

/**
 * Gets the window that has focus.
 * @return  The window that has focus.
 *
 * Returns the window that has the focus. If an error aoocured, @c 0
 * is returned, otherwise the function returns the window that has focus.
 *
 * To use this function, you must call before, and in order,
 * ecore_x_get_input_focus_prefetch(), which sends the GetInputFocus request,
 * then ecore_x_get_input_focus_fetch(), which gets the reply.
 * @ingroup Ecore_X_Window_Input_Focus_Group
 */
EAPI Ecore_X_Window
ecore_x_window_focus_get(void)
{
   xcb_get_input_focus_reply_t *reply;
   Ecore_X_Window window = 0;

   reply = _ecore_xcb_reply_get();
   if (!reply)
      return window;

   return reply->focus;
} /* ecore_x_window_focus_get */

/**
 * @defgroup Ecore_X_Window_Get_Attributes_Group X Window Get Attributes Functions
 *
 * Functions that get the attributes of a window.
 */

/**
 * Sends the GetWindowAttributes request.
 * @ingroup Ecore_X_Window_Get_Attributes_Group
 */
EAPI void
ecore_x_get_window_attributes_prefetch(Ecore_X_Window window)
{
   xcb_get_window_attributes_cookie_t cookie;

   cookie = xcb_get_window_attributes_unchecked(_ecore_xcb_conn, window);
   _ecore_xcb_cookie_cache(cookie.sequence);
} /* ecore_x_get_window_attributes_prefetch */

/**
 * Gets the reply of the GetWindowAttributes request sent by ecore_x_get_window_attributes_prefetch().
 * @ingroup Ecore_X_Window_Get_Attributes_Group
 */
EAPI void
ecore_x_get_window_attributes_fetch(void)
{
   xcb_get_window_attributes_cookie_t cookie;
   xcb_get_window_attributes_reply_t *reply;

   cookie.sequence = _ecore_xcb_cookie_get();
   reply = xcb_get_window_attributes_reply(_ecore_xcb_conn, cookie, NULL);
   _ecore_xcb_reply_cache(reply);
} /* ecore_x_get_window_attributes_fetch */

/**
 * Retrieves the attributes of a window.
 * @param windows Unused.
 * @param att_ret Pointer to an Ecore_X_Window_Attributes
 *                  structure in which the attributes of a window
 *                  are to be stored.
 *
 * Retrieves the attributes of a window. If
 * @p att_ret is @c NULL, the function does nothing. If an error
 * occurred, @p att_ret is set to 0. Otherwise, the @p att_ret structure
 * is filled with the attributes os the requested window.
 *
 * To use this function, you must call before, and in order,
 * ecore_x_get_window_attributes_prefetch(), which sends the GetWindowAttributes request,
 * then ecore_x_get_window_attributes_fetch(), which gets the reply.
 * @ingroup Ecore_X_Window_Get_Attributes_Group
 */
EAPI Eina_Bool
ecore_x_window_attributes_get(Ecore_X_Window window      __UNUSED__,
                              Ecore_X_Window_Attributes *att_ret)
{
   xcb_get_window_attributes_reply_t *reply;

   if (!att_ret)
      return 0;

   reply = _ecore_xcb_reply_get();
   if (!reply)
      return 0;

   memset(att_ret, 0, sizeof(Ecore_X_Window_Attributes));

   if (reply->map_state != XCB_MAP_STATE_UNMAPPED)
      att_ret->visible = 1;

   if (reply->map_state == XCB_MAP_STATE_VIEWABLE)
      att_ret->viewable = 1;

   if (reply->override_redirect)
      att_ret->override = 1;

   if (reply->_class == XCB_WINDOW_CLASS_INPUT_ONLY)
      att_ret->input_only = 1;

   if (reply->save_under)
      att_ret->save_under = 1;

   att_ret->event_mask.mine = reply->your_event_mask;
   att_ret->event_mask.all = reply->all_event_masks;
   att_ret->event_mask.no_propagate = reply->do_not_propagate_mask;
   att_ret->window_gravity = reply->win_gravity;
   att_ret->pixel_gravity = reply->bit_gravity;
   att_ret->colormap = reply->colormap;
   att_ret->visual = reply->visual;

   return 1;
} /* ecore_x_window_attributes_get */

/**
 * Finds out whether the given window is currently visible.
 * @param  window Unused.
 * @return        1 if the window is visible, otherwise 0.
 *
 * Finds out whether the given window is currently visible.
 * If an error occurred, or if the window is not visible, 0 is
 * returned. Otherwise 1 is returned.
 *
 * To use this function, you must call before, and in order,
 * ecore_x_get_window_attributes_prefetch(), which sends the GetWindowAttributes request,
 * then ecore_x_get_window_attributes_fetch(), which gets the reply.
 * @ingroup Ecore_X_Window_Get_Attributes_Group
 */
EAPI int
ecore_x_window_visible_get(Ecore_X_Window window __UNUSED__)
{
   xcb_get_window_attributes_reply_t *reply;

   reply = _ecore_xcb_reply_get();
   if (!reply)
      return 0;

   return (reply->map_state == XCB_MAP_STATE_VIEWABLE) ? 1 : 0;
} /* ecore_x_window_visible_get */

/**
 * Sends the QueryPointer request.
 * @ingroup Ecore_X_Window_Parent_Group
 */
EAPI void
ecore_x_pointer_xy_get_prefetch(Ecore_X_Window window)
{
   xcb_query_pointer_cookie_t cookie;

   cookie = xcb_query_pointer_unchecked(_ecore_xcb_conn, window);
   _ecore_xcb_cookie_cache(cookie.sequence);
} /* ecore_x_pointer_xy_get_prefetch */

/**
 * Gets the reply of the QueryPointer request sent by ecore_x_query_pointer_prefetch().
 * @ingroup Ecore_X_Window_Parent_Group
 */
EAPI void
ecore_x_pointer_xy_get_fetch(void)
{
   xcb_query_pointer_cookie_t cookie;
   xcb_query_pointer_reply_t *reply;

   cookie.sequence = _ecore_xcb_cookie_get();
   reply = xcb_query_pointer_reply(_ecore_xcb_conn, cookie, NULL);
   _ecore_xcb_reply_cache(reply);
} /* ecore_x_pointer_xy_get_fetch */

/**
 * Retrieves the coordinates of the pointer.
 * @param window Unused.
 * @param x      The X coordinate of the pointer.
 * @param y      The Y coordinate of the pointer.
 *
 * Retrieves the coordinates of the pointer.
 * If the window used in
 * ecore_x_query_pointer_prefetch() is not on the same screen than
 * the root window or if an error occurred, @p x and @p y  are set
 * to 0. Otherwise, they are respectively set to the X and Y
 * coordinates of the pointer.
 *
 * To use this function, you must call before, and in order,
 * ecore_x_query_pointer_prefetch(), which sends the QueryPointer request,
 * then ecore_x_query_pointer_fetch(), which gets the reply.
 * @ingroup Ecore_X_Window_Parent_Group
 */
EAPI void
ecore_x_pointer_xy_get(Ecore_X_Window window __UNUSED__,
                       int                  *x,
                       int                  *y)
{
   xcb_query_pointer_reply_t *reply;

   reply = _ecore_xcb_reply_get();
   if (!reply)
     {
        if (x)
           *x = 0;

        if (y)
           *y = 0;

        return;
     }

   if (x)
      *x = reply->win_x;

   if (y)
      *y = reply->win_y;
} /* ecore_x_pointer_xy_get */

/**
 * Sends the QueryTree request.
 * @ingroup Ecore_X_Window_Parent_Group
 */
EAPI void
ecore_x_query_tree_prefetch(Ecore_X_Window window)
{
   xcb_query_tree_cookie_t cookie;

   cookie = xcb_query_tree_unchecked(_ecore_xcb_conn, window);
   _ecore_xcb_cookie_cache(cookie.sequence);
} /* ecore_x_query_tree_prefetch */

/**
 * Gets the reply of the QueryTree request sent by ecore_x_query_tree_prefetch().
 * @ingroup Ecore_X_Window_Parent_Group
 */
EAPI void
ecore_x_query_tree_fetch(void)
{
   xcb_query_tree_cookie_t cookie;
   xcb_query_tree_reply_t *reply;

   cookie.sequence = _ecore_xcb_cookie_get();
   reply = xcb_query_tree_reply(_ecore_xcb_conn, cookie, NULL);
   _ecore_xcb_reply_cache(reply);
} /* ecore_x_query_tree_fetch */

/**
 * Retrieves the parent window of the given window.
 * @param   window Unused.
 * @return         The parent window of @p window.
 *
 * Retrieves the parent window of the given window. If
 * an error occurred, @c 0 is returned.
 *
 * To use this function, you must call before, and in order,
 * ecore_x_query_tree_prefetch(), which sends the QueryTree request,
 * then ecore_x_query_tree_fetch(), which gets the reply.
 * @ingroup Ecore_X_Window_Parent_Group
 */
EAPI Ecore_X_Window
ecore_x_window_parent_get(Ecore_X_Window window __UNUSED__)
{
   xcb_query_tree_reply_t *reply;

   reply = _ecore_xcb_reply_get();
   if (!reply)
      return 0;

   return reply->parent;
} /* ecore_x_window_parent_get */

/**
 * Retrieves the children windows of the given window.
 * @param   window Unused.
 * @param   num    children windows count.
 * @return         The children windows.
 *
 * Retrieves the children windows of the given window. If
 * an error occurred, @c 0 is returned.
 *
 * To use this function, you must call before, and in order,
 * ecore_x_query_tree_prefetch(), which sends the QueryTree request,
 * then ecore_x_query_tree_fetch(), which gets the reply.
 * @ingroup Ecore_X_Window_Parent_Group
 */
EAPI Ecore_X_Window *
ecore_x_window_children_get(Ecore_X_Window window __UNUSED__,
                            int                  *num)
{
   xcb_query_tree_reply_t *reply;
   Ecore_X_Window *windows = NULL;

   if (num)
      *num = 0;

   reply = _ecore_xcb_reply_get();
   if (!reply)
      return NULL;

   windows = malloc(sizeof(Ecore_X_Window) * reply->children_len);
   if (!windows)
      return NULL;

   if (num)
      *num = reply->children_len;

   memcpy(windows,
          xcb_query_tree_children(reply),
          sizeof(Ecore_X_Window) * reply->children_len);

   return windows;
} /* ecore_x_window_children_get */

/* FIXME: I've tried to remove the round trips. 3 cookies are */
/*        created at the beginning of the function. Because of */
/*        the recursivity of the algo, I can't find better trick */
static Ecore_X_Window
_ecore_x_window_at_xy_get(Ecore_X_Window  base,
                          int16_t         base_x,
                          int16_t         base_y,
                          int16_t         x,
                          int16_t         y,
                          Ecore_X_Window *skip,
                          int             skip_num)
{
   xcb_get_window_attributes_cookie_t cookie_get_window_attributes;
   xcb_get_geometry_cookie_t cookie_get_geometry;
   xcb_query_tree_cookie_t cookie_query_tree;
   xcb_get_window_attributes_reply_t *reply_get_window_attributes;
   xcb_get_geometry_reply_t *reply_get_geometry;
   xcb_query_tree_reply_t *reply_query_tree;
   Ecore_X_Window window = 0;
   Ecore_X_Window child = 0;
   int16_t win_x;
   int16_t win_y;
   uint16_t win_width;
   uint16_t win_height;
   xcb_window_t *wins = NULL;
   int tree_c_len;
   int i;

   cookie_get_window_attributes = xcb_get_window_attributes_unchecked(_ecore_xcb_conn, base);
   cookie_get_geometry = xcb_get_geometry_unchecked(_ecore_xcb_conn, base);
   cookie_query_tree = xcb_query_tree_unchecked(_ecore_xcb_conn, base);

   reply_get_window_attributes = xcb_get_window_attributes_reply(_ecore_xcb_conn, cookie_get_window_attributes, NULL);
   if (!reply_get_window_attributes)
     {
        reply_get_geometry = xcb_get_geometry_reply(_ecore_xcb_conn, cookie_get_geometry, NULL);
        if (reply_get_geometry)
           free(reply_get_geometry);

        reply_query_tree = xcb_query_tree_reply(_ecore_xcb_conn, cookie_query_tree, NULL);
        if (reply_query_tree)
           free(reply_query_tree);

        return window;
     }

   if (reply_get_window_attributes->map_state != XCB_MAP_STATE_VIEWABLE)
     {
        free(reply_get_window_attributes);
        reply_get_geometry = xcb_get_geometry_reply(_ecore_xcb_conn, cookie_get_geometry, NULL);
        if (reply_get_geometry)
           free(reply_get_geometry);

        reply_query_tree = xcb_query_tree_reply(_ecore_xcb_conn, cookie_query_tree, NULL);
        if (reply_query_tree)
           free(reply_query_tree);

        return window;
     }

   free(reply_get_window_attributes);

   reply_get_geometry = xcb_get_geometry_reply(_ecore_xcb_conn, cookie_get_geometry, NULL);
   if (!reply_get_geometry)
     {
        reply_query_tree = xcb_query_tree_reply(_ecore_xcb_conn, cookie_query_tree, NULL);
        if (reply_query_tree)
           free(reply_query_tree);

        return window;
     }

   win_x = reply_get_geometry->x;
   win_y = reply_get_geometry->y;
   win_width = reply_get_geometry->width;
   win_height = reply_get_geometry->height;

   free(reply_get_geometry);

   win_x += base_x;
   win_y += base_y;

   if (!((x >= win_x) &&
         (y >= win_y) &&
         (x < (int16_t)(win_x + win_width)) &&
         (y < (int16_t)(win_y + win_height))))
     {
        reply_query_tree = xcb_query_tree_reply(_ecore_xcb_conn, cookie_query_tree, NULL);
        if (reply_query_tree)
           free(reply_query_tree);

        return window;
     }

   reply_query_tree = xcb_query_tree_reply(_ecore_xcb_conn, cookie_query_tree, NULL);
   if (!reply_query_tree)
     {
        if (skip)
          {
             int i;

             for (i = 0; i < skip_num; i++)
                if (base == skip[i])
                   return window;

          }

        return base;
     }

   wins = xcb_query_tree_children(reply_query_tree);
   tree_c_len = xcb_query_tree_children_length(reply_query_tree);
   for(i = 0; i < tree_c_len; i++)
     {
        if (skip)
          {
             int j;

             for (j = 0; j < skip_num; j++)
                if (wins[i] == skip[j])
                   continue;

          }

        child = _ecore_x_window_at_xy_get(wins[i], win_x, win_y, x, y, skip, skip_num);
        if (child)
          {
             free(reply_query_tree);

             return child;
          }
     }

   if (skip)
     {
        int i;

        for (i = 0; i < skip_num; i++)
           if (base == skip[i])
             {
                /* We return 0. child has an xid equal to 0 */
                free(reply_query_tree);
                return child;
             }

     }

   free(reply_query_tree);

   return base;
} /* _ecore_x_window_at_xy_get */

/**
 * @defgroup Ecore_X_Window_Geometry_Group X Window Geometry Functions
 *
 * Functions that change or retrieve the geometry of X windows.
 */

/**
 * Retrieves the top, visible window at the given location.
 * @param   x The given X position.
 * @param   y The given Y position.
 * @return    The window at that position.
 * @ingroup Ecore_X_Window_Geometry_Group
 */
EAPI Ecore_X_Window
ecore_x_window_at_xy_get(int x,
                         int y)
{
   Ecore_X_Window window;
   Ecore_X_Window root;

   /* FIXME: Proper function to determine current root/virtual root
    * window missing here */
   root = ((xcb_screen_t *)_ecore_xcb_screen)->root;

   ecore_x_grab();
   window = _ecore_x_window_at_xy_get(root, 0, 0, x, y, NULL, 0);
   ecore_x_ungrab();

   return window ? window : root;
} /* ecore_x_window_at_xy_get */

/**
 * Retrieves the top, visible window at the given location,
 * but skips the windows in the list.
 * @param   x The given X position.
 * @param   y The given Y position.
 * @return    The window at that position.
 * @ingroup Ecore_X_Window_Geometry_Group
 */
EAPI Ecore_X_Window
ecore_x_window_at_xy_with_skip_get(int             x,
                                   int             y,
                                   Ecore_X_Window *skip,
                                   int             skip_num)
{
   Ecore_X_Window window;
   Ecore_X_Window root;

   /* FIXME: Proper function to determine current root/virtual root
    * window missing here */
   root = ((xcb_screen_t *)_ecore_xcb_screen)->root;

   ecore_x_grab();
   window = _ecore_x_window_at_xy_get(root, 0, 0, x, y, skip, skip_num);
   ecore_x_ungrab();

   return window ? window : root;
} /* ecore_x_window_at_xy_with_skip_get */

/**
 * Retrieves the top, visible window at the given location,
 * but begins at the @p begin window instead of the root one.
 * @param   begin The window from which we begin.
 * @param   x     The given X position.
 * @param   y     The given Y position.
 * @return        The window at that position.
 * @ingroup Ecore_X_Window_Geometry_Group
 */
EAPI Ecore_X_Window
ecore_x_window_at_xy_begin_get(Ecore_X_Window begin,
                               int            x,
                               int            y)
{
   Ecore_X_Window window;

   ecore_x_grab();
   window = _ecore_x_window_at_xy_get(begin, 0, 0, x, y, NULL, 0);
   ecore_x_ungrab();

   return window ? window : begin;
} /* ecore_x_window_at_xy_begin_get */

/* FIXME: Should I provide the replies (or the cookies), instead of
          creating them in the function ? */
#ifdef ECORE_XCB_RENDER
static Ecore_X_Window
_ecore_x_window_argb_internal_new(Ecore_X_Window parent,
                                  int16_t        x,
                                  int16_t        y,
                                  uint16_t       w,
                                  uint16_t       h,
                                  uint8_t        override_redirect,
                                  uint8_t        save_under)
{
   uint32_t value_list[10];
   xcb_depth_iterator_t iter_depth;
   xcb_visualtype_iterator_t iter_visualtype;
   xcb_render_query_pict_formats_cookie_t cookie_pict_format;
   xcb_render_query_pict_formats_reply_t *rep_pict_format;
   Ecore_X_Screen *screen = NULL;
   Ecore_X_Window win = { 0 };
   xcb_visualid_t vis = { 0 };
   Ecore_X_Colormap colormap;
   uint32_t value_mask;

   cookie_pict_format = xcb_render_query_pict_formats_unchecked(_ecore_xcb_conn);

   if (parent == 0)
     {
        parent = ((xcb_screen_t *)_ecore_xcb_screen)->root;
        screen = ((xcb_screen_t *)_ecore_xcb_screen);
     }
   else
     {
        xcb_screen_iterator_t iter_screen;
        xcb_get_geometry_reply_t *rep;
        Ecore_X_Drawable draw;
        Ecore_X_Window root;

        draw = parent;
        rep = xcb_get_geometry_reply(_ecore_xcb_conn,
                                     xcb_get_geometry_unchecked(_ecore_xcb_conn,
                                                                draw),
                                     NULL);
        if (!rep)
           return win;

        root = rep->root;

        free(rep);

        for (; iter_screen.rem; xcb_screen_next(&iter_screen))
          {
             if (iter_screen.data->root == root)
               {
                  screen = iter_screen.data;
               }
          }
     }

   if (!screen)
      return win;

   /* we get the X visual types */
   iter_depth = xcb_screen_allowed_depths_iterator(screen);
   for (; iter_depth.rem; xcb_depth_next(&iter_depth)) {
        if (iter_depth.data->depth == 32)
          {
             iter_visualtype = xcb_depth_visuals_iterator(iter_depth.data);
             break;
          }
     }

   /* we get the X render visual id */
   rep_pict_format = xcb_render_query_pict_formats_reply(_ecore_xcb_conn,
                                                         cookie_pict_format,
                                                         NULL);
   if (!rep_pict_format)
      return win;

   for (; iter_visualtype.rem; xcb_visualtype_next(&iter_visualtype)) {
        if (iter_visualtype.data->_class == XCB_VISUAL_CLASS_TRUE_COLOR)
          {
             xcb_render_pictforminfo_iterator_t iter_forminfo;
             xcb_render_pictscreen_iterator_t iter_pictscreen;
             xcb_render_pictformat_t pict_format = { 0 };

             iter_forminfo = xcb_render_query_pict_formats_formats_iterator(rep_pict_format);
             for (; iter_forminfo.rem; xcb_render_pictforminfo_next(&iter_forminfo)) {
                  if (iter_forminfo.data->type == XCB_RENDER_PICT_TYPE_DIRECT &&
                      iter_forminfo.data->direct.alpha_mask && iter_forminfo.data->depth == 32)
                    {
                       pict_format = iter_forminfo.data->id;
                       break;
                    }
               }
             if (pict_format == 0)
               {
                  free(rep_pict_format);
                  return win;
               }

             iter_pictscreen = xcb_render_query_pict_formats_screens_iterator(rep_pict_format);
             for (; iter_pictscreen.rem; xcb_render_pictscreen_next(&iter_pictscreen)) {
                  xcb_render_pictdepth_iterator_t iter_depth;

                  iter_depth = xcb_render_pictscreen_depths_iterator(iter_pictscreen.data);
                  for (; iter_depth.rem; xcb_render_pictdepth_next(&iter_depth)) {
                       xcb_render_pictvisual_iterator_t iter_visual;

                       iter_visual = xcb_render_pictdepth_visuals_iterator(iter_depth.data);
                       for (; iter_visual.rem; xcb_render_pictvisual_next(&iter_visual)) {
                            if ((iter_visual.data->visual == iter_visualtype.data->visual_id) &&
                                (pict_format == iter_visual.data->format))
                              {
                                 vis = iter_visual.data->visual;
                                 break;
                              }
                         }
                    }
               }
          }
     }

   free(rep_pict_format);

   if (vis == 0)
      return win;

   colormap = xcb_generate_id(_ecore_xcb_conn);
   xcb_create_colormap(_ecore_xcb_conn, XCB_COLORMAP_ALLOC_NONE, colormap, parent, vis);

   value_mask =
      XCB_CW_BACK_PIXMAP | XCB_CW_BORDER_PIXEL | XCB_CW_BIT_GRAVITY |
      XCB_CW_WIN_GRAVITY | XCB_CW_BACKING_STORE | XCB_CW_OVERRIDE_REDIRECT |
      XCB_CW_SAVE_UNDER | XCB_CW_EVENT_MASK | XCB_CW_DONT_PROPAGATE |
      XCB_CW_COLORMAP;

   value_list[0] = XCB_NONE;
   value_list[1] = 0;
   value_list[2] = XCB_GRAVITY_NORTH_WEST;
   value_list[3] = XCB_GRAVITY_NORTH_WEST;
   value_list[4] = XCB_BACKING_STORE_NOT_USEFUL;
   value_list[5] = override_redirect;
   value_list[6] = save_under;
   value_list[7] =
      XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE |
      XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE |
      XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW |
      XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_EXPOSURE |
      XCB_EVENT_MASK_VISIBILITY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY |
      XCB_EVENT_MASK_FOCUS_CHANGE | XCB_EVENT_MASK_PROPERTY_CHANGE |
      XCB_EVENT_MASK_COLOR_MAP_CHANGE;
   value_list[8] = XCB_EVENT_MASK_NO_EVENT;
   value_list[9] = colormap;

   win = xcb_generate_id(_ecore_xcb_conn);
   xcb_create_window(_ecore_xcb_conn,
                     32, /* depth */
                     win, parent,
                     x, y, w, h, 0,
                     XCB_WINDOW_CLASS_INPUT_OUTPUT,
                     vis,
                     value_mask,
                     value_list);

   xcb_free_colormap(_ecore_xcb_conn, colormap);

   if (parent == ((xcb_screen_t *)_ecore_xcb_screen)->root)
      ecore_x_window_defaults_set(win);

   return win;
} /* _ecore_x_window_argb_internal_new */

#endif /* ECORE_XCB_RENDER */

/* FIXME: round trip */
EAPI int
ecore_x_window_argb_get(Ecore_X_Window win)
{
   uint8_t ret = 0;
#ifdef ECORE_XCB_RENDER
   xcb_render_pictforminfo_iterator_t iter_forminfo;
   xcb_render_pictscreen_iterator_t iter_pictscreen;
   xcb_render_pictformat_t pict_format = { 0 };
   xcb_render_query_pict_formats_reply_t *rep_pictformat;
   xcb_get_window_attributes_reply_t *rep;
   xcb_visualid_t visual;

   rep = xcb_get_window_attributes_reply(_ecore_xcb_conn,
                                         xcb_get_window_attributes_unchecked(_ecore_xcb_conn,
                                                                             win),
                                         NULL);
   if (!rep)
      return ret;

   visual = rep->visual;

   free(rep);

   rep_pictformat = xcb_render_query_pict_formats_reply(_ecore_xcb_conn,
                                                        xcb_render_query_pict_formats_unchecked(_ecore_xcb_conn),
                                                        NULL);
   if (!rep_pictformat)
      return ret;

   iter_forminfo = xcb_render_query_pict_formats_formats_iterator(rep_pictformat);
   for (; iter_forminfo.rem; xcb_render_pictforminfo_next(&iter_forminfo))
     {
        if ((iter_forminfo.data->type == XCB_RENDER_PICT_TYPE_DIRECT) &&
            (iter_forminfo.data->direct.alpha_mask))
          {
             pict_format = iter_forminfo.data->id;
             break;
          }
     }
   if (pict_format == 0)
     {
        free(rep_pictformat);

        return ret;
     }

   iter_pictscreen = xcb_render_query_pict_formats_screens_iterator(rep_pictformat);
   for (; iter_pictscreen.rem; xcb_render_pictscreen_next(&iter_pictscreen))
     {
        xcb_render_pictdepth_iterator_t iter_depth;

        iter_depth = xcb_render_pictscreen_depths_iterator(iter_pictscreen.data);
        for (; iter_depth.rem; xcb_render_pictdepth_next(&iter_depth))
          {
             xcb_render_pictvisual_iterator_t iter_visual;

             iter_visual = xcb_render_pictdepth_visuals_iterator(iter_depth.data);
             for (; iter_visual.rem; xcb_render_pictvisual_next(&iter_visual))
               {
                  if ((iter_visual.data->visual == visual) &&
                      (pict_format == iter_visual.data->format))
                    {
                       ret = 1;
                       break;
                    }
               }
          }
     }

   free(rep_pictformat);
#endif /* ECORE_XCB_RENDER */

   return ret;
} /* ecore_x_window_argb_get */

/**
 * Set if a window should be ignored.
 * @param   window The given window.
 * @param   ignore if to ignore
 */
EAPI void
ecore_x_window_ignore_set(Ecore_X_Window window,
                          int            ignore)
{
   int i, j;

   if (ignore)
     {
        if (ignore_list)
          {
             for (i = 0; i < ignore_num; i++)
               {
                  if (window == ignore_list[i])
                     return;
               }
             ignore_list = realloc(ignore_list, (ignore_num + 1) * sizeof(Ecore_X_Window));
             if (!ignore_list)
                return;

             ignore_list[ignore_num++] = window;
          }
        else
          {
             ignore_num = 0;
             ignore_list = malloc(sizeof(Ecore_X_Window));
             ignore_list[ignore_num++] = window;
          }
     }
   else
     {
        if (!ignore_list)
           return;

        for (i = 0, j = 0; i < ignore_num; i++)
          {
             if (window != ignore_list[i])
                ignore_list[i] = ignore_list[j++];
             else
                ignore_num--;
          }
        ignore_list = realloc(ignore_list, ignore_num * sizeof(Ecore_X_Window));
     }
} /* ecore_x_window_ignore_set */

/**
 * Get the ignore list
 * @param   num number of windows in the list
 * @return  list of windows to ignore
 */
EAPI Ecore_X_Window *
ecore_x_window_ignore_list(int *num)
{
   if (num)
      *num = ignore_num;

   return ignore_list;
} /* ecore_x_window_ignore_list */

/**
 * Retrieves the size of the given window.
 * @param   win The given window.
 * @param   w   Pointer to an integer into which the width is to be stored.
 * @param   h   Pointer to an integer into which the height is to be stored.
 *
 * To use this function, you must call before, and in order,
 * ecore_x_drawable_geometry_get_prefetch(), which sends the GetGeometry request,
 * then ecore_x_drawable_geometry_get_fetch(), which gets the reply.
 * @ingroup Ecore_X_Window_Geometry_Group
 */
EAPI void
ecore_x_window_size_get(Ecore_X_Window window,
                        int           *width,
                        int           *height)
{
   if (window == 0)
      window = ((xcb_screen_t *)_ecore_xcb_screen)->root;

   ecore_x_drawable_geometry_get(window, NULL, NULL, width, height);
} /* ecore_x_window_size_get */

/**
 * Retrieves the geometry of the given window.
 * @param   win The given window.
 * @param   x   Pointer to an integer in which the X position is to be stored.
 * @param   y   Pointer to an integer in which the Y position is to be stored.
 * @param   w   Pointer to an integer in which the width is to be stored.
 * @param   h   Pointer to an integer in which the height is to be stored.
 *
 * To use this function, you must call before, and in order,
 * ecore_x_drawable_geometry_get_prefetch(), which sends the GetGeometry request,
 * then ecore_x_drawable_geometry_get_fetch(), which gets the reply.
 * @ingroup Ecore_X_Window_Geometry_Group
 */
EAPI void
ecore_x_window_geometry_get(Ecore_X_Window window,
                            int           *x,
                            int           *y,
                            int           *width,
                            int           *height)
{
   if (!window)
      window = ((xcb_screen_t *)_ecore_xcb_screen)->root;

   ecore_x_drawable_geometry_get(window, x, y, width, height);
} /* ecore_x_window_geometry_get */

/**
 * Retrieves the width of the border of the given window.
 * @param   win The given window.
 * @return  Width of the border of @p win.
 * @ingroup Ecore_X_Window_Geometry_Group
 */
EAPI int
ecore_x_window_border_width_get(Ecore_X_Window win)
{
   /* doesn't make sense to call this on a root window */
   if (!win)
      return 0;

   return ecore_x_drawable_border_width_get(win);
} /* ecore_x_window_border_width_get */

/**
 * Retrieves the depth of the given window.
 * @param  win The given window.
 * @return Depth of the window.
 */
EAPI int
ecore_x_window_depth_get(Ecore_X_Window win)
{
   return ecore_x_drawable_depth_get(win);
} /* ecore_x_window_depth_get */

