/*
 * _NET_WM... aka Extended Window Manager Hint (EWMH) functions.
 */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "Ecore.h"
#include "ecore_x_private.h"
#include "Ecore_X.h"

typedef struct _Ecore_X_Startup_Info Ecore_X_Startup_Info;

struct _Ecore_X_Startup_Info
{
   Ecore_X_Window win;

   int            init;

   int            buffer_size;
   char          *buffer;

   int            length;

   /* These are the sequence info fields */
   char          *id;
   char          *name;
   int            screen;
   char          *bin;
   char          *icon;
   int            desktop;
   int            timestamp;
   char          *description;
   char          *wmclass;
   int            silent;
};

static void  _ecore_x_window_prop_string_utf8_set(Ecore_X_Window win,
                                                  Ecore_X_Atom atom,
                                                  const char *str);
static char *_ecore_x_window_prop_string_utf8_get(Ecore_X_Window win,
                                                  Ecore_X_Atom atom);
#if 0 /* Unused */
static int   _ecore_x_netwm_startup_info_process(Ecore_X_Startup_Info *info);
static int   _ecore_x_netwm_startup_info_parse(Ecore_X_Startup_Info *info,
                                               char *data);
#endif /* if 0 */
static void  _ecore_x_netwm_startup_info_free(void *data);

/*
 * Convenience macros
 */
#define _ATOM_SET_UTF8_STRING_LIST(win, atom, string, cnt) \
  XChangeProperty(_ecore_x_disp,                           \
                  win,                                     \
                  atom,                                    \
                  ECORE_X_ATOM_UTF8_STRING,                \
                  8,                                       \
                  PropModeReplace,                         \
                  (unsigned char *)string,                 \
                  cnt)

/*
 * Local variables
 */

static Eina_Hash *startup_info = NULL;

EAPI void
ecore_x_netwm_init(void)
{
   LOGFN;
   startup_info = eina_hash_string_superfast_new(
       _ecore_x_netwm_startup_info_free);
}

EAPI void
ecore_x_netwm_shutdown(void)
{
   LOGFN;
   if (startup_info)
     eina_hash_free(startup_info);

   startup_info = NULL;
}

/*
 * WM identification
 */
EAPI void
ecore_x_netwm_wm_identify(Ecore_X_Window root,
                          Ecore_X_Window check,
                          const char *wm_name)
{
   LOGFN;
   ecore_x_window_prop_window_set(check,
                                  ECORE_X_ATOM_NET_SUPPORTING_WM_CHECK,
                                  &check,
                                  1);
   _ecore_x_window_prop_string_utf8_set(check,
                                        ECORE_X_ATOM_NET_WM_NAME,
                                        wm_name);
   /* This one isn't mandatory */
   _ecore_x_window_prop_string_utf8_set(root,
                                        ECORE_X_ATOM_NET_WM_NAME,
                                        wm_name);
   ecore_x_window_prop_window_set(root,
                                  ECORE_X_ATOM_NET_SUPPORTING_WM_CHECK,
                                  &check,
                                  1);
}

/*
 * Set supported atoms
 */
EAPI void
ecore_x_netwm_supported_set(Ecore_X_Window root,
                            Ecore_X_Atom *supported,
                            int num)
{
   LOGFN;
   ecore_x_window_prop_atom_set(root,
                                ECORE_X_ATOM_NET_SUPPORTED,
                                supported,
                                num);
}

EAPI Eina_Bool
ecore_x_netwm_supported_get(Ecore_X_Window root,
                            Ecore_X_Atom **supported,
                            int *num)
{
   int num_ret;

   EINA_SAFETY_ON_NULL_RETURN_VAL(supported, EINA_FALSE);

   if (num)
     *num = 0;

   LOGFN;
   num_ret = ecore_x_window_prop_atom_list_get(root, ECORE_X_ATOM_NET_SUPPORTED,
                                               supported);
   if (num_ret <= 0)
     return EINA_FALSE;

   if (num)
     *num = num_ret;

   return EINA_TRUE;
}

/*
 * Desktop configuration and status
 */
EAPI void
ecore_x_netwm_desk_count_set(Ecore_X_Window root,
                             unsigned int n_desks)
{
   LOGFN;
   ecore_x_window_prop_card32_set(root, ECORE_X_ATOM_NET_NUMBER_OF_DESKTOPS,
                                  &n_desks, 1);
}

EAPI void
ecore_x_netwm_desk_roots_set(Ecore_X_Window root,
                             Ecore_X_Window *vroots,
                             unsigned int n_desks)
{
   LOGFN;
   ecore_x_window_prop_window_set(root,
                                  ECORE_X_ATOM_NET_VIRTUAL_ROOTS,
                                  vroots,
                                  n_desks);
}

EAPI void
ecore_x_netwm_desk_names_set(Ecore_X_Window root,
                             const char **names,
                             unsigned int n_desks)
{
   char ss[32], *buf, *t;
   const char *s;
   unsigned int i;
   int l, len;

   LOGFN;
   buf = NULL;
   len = 0;

   for (i = 0; i < n_desks; i++)
     {
        s = (names) ? names[i] : NULL;
        if (!s)
          {
             /* Default to "Desk-<number>" */
             sprintf(ss, "Desk-%u", i);
             s = ss;
          }

        l = strlen(s) + 1;
        t = realloc(buf, len + l);
        if (t)
          {
             buf = t;
             memcpy(buf + len, s, l);
          }
        len += l;
     }

   _ATOM_SET_UTF8_STRING_LIST(root, ECORE_X_ATOM_NET_DESKTOP_NAMES, buf, len);
   if (_ecore_xlib_sync) ecore_x_sync();

   free(buf);
}

EAPI void
ecore_x_netwm_desk_size_set(Ecore_X_Window root,
                            unsigned int width,
                            unsigned int height)
{
   unsigned int size[2];

   LOGFN;
   size[0] = width;
   size[1] = height;
   ecore_x_window_prop_card32_set(root, ECORE_X_ATOM_NET_DESKTOP_GEOMETRY, size,
                                  2);
}

EAPI void
ecore_x_netwm_desk_viewports_set(Ecore_X_Window root,
                                 unsigned int *origins,
                                 unsigned int n_desks)
{
   LOGFN;
   ecore_x_window_prop_card32_set(root, ECORE_X_ATOM_NET_DESKTOP_VIEWPORT,
                                  origins, 2 * n_desks);
}

EAPI void
ecore_x_netwm_desk_layout_set(Ecore_X_Window root,
                              int orientation,
                              int columns,
                              int rows,
                              int starting_corner)
{
   unsigned int layout[4];

   LOGFN;
   layout[0] = orientation;
   layout[1] = columns;
   layout[2] = rows;
   layout[3] = starting_corner;
   ecore_x_window_prop_card32_set(root, ECORE_X_ATOM_NET_DESKTOP_LAYOUT,
                                  layout, 4);
}

EAPI void
ecore_x_netwm_desk_workareas_set(Ecore_X_Window root,
                                 unsigned int *areas,
                                 unsigned int n_desks)
{
   LOGFN;
   ecore_x_window_prop_card32_set(root, ECORE_X_ATOM_NET_WORKAREA, areas,
                                  4 * n_desks);
}

EAPI unsigned int *
ecore_x_netwm_desk_workareas_get(Ecore_X_Window root, unsigned int *n_desks)
{
   int ret;
   unsigned int *areas = NULL;

   if (!root) root = DefaultRootWindow(_ecore_x_disp);

   ret = ecore_x_window_prop_card32_list_get(root, ECORE_X_ATOM_NET_WORKAREA,
                                             &areas);
   if (!areas)
     {
        if (n_desks) *n_desks = 0;
        return 0;
     }
   if (n_desks) *n_desks = ret / 4;
   return areas;
}

EAPI void
ecore_x_netwm_desk_current_set(Ecore_X_Window root,
                               unsigned int desk)
{
   LOGFN;
   ecore_x_window_prop_card32_set(root, ECORE_X_ATOM_NET_CURRENT_DESKTOP, &desk,
                                  1);
}

EAPI void
ecore_x_netwm_showing_desktop_set(Ecore_X_Window root,
                                  Eina_Bool on)
{
   unsigned int val;

   LOGFN;
   val = (on) ? 1 : 0;
   ecore_x_window_prop_card32_set(root, ECORE_X_ATOM_NET_SHOWING_DESKTOP, &val,
                                  1);
}

/*
 * Client status
 */

/* Mapping order */
EAPI void
ecore_x_netwm_client_list_set(Ecore_X_Window root,
                              Ecore_X_Window *p_clients,
                              unsigned int n_clients)
{
   LOGFN;
   ecore_x_window_prop_window_set(root, ECORE_X_ATOM_NET_CLIENT_LIST,
                                  p_clients, n_clients);
}

/* Stacking order */
EAPI void
ecore_x_netwm_client_list_stacking_set(Ecore_X_Window root,
                                       Ecore_X_Window *p_clients,
                                       unsigned int n_clients)
{
   LOGFN;
   ecore_x_window_prop_window_set(root, ECORE_X_ATOM_NET_CLIENT_LIST_STACKING,
                                  p_clients, n_clients);
}

EAPI void
ecore_x_netwm_client_active_set(Ecore_X_Window root,
                                Ecore_X_Window win)
{
   LOGFN;
   ecore_x_window_prop_window_set(root, ECORE_X_ATOM_NET_ACTIVE_WINDOW,
                                  &win, 1);
}

EAPI void
ecore_x_netwm_client_active_request(Ecore_X_Window root,
                                    Ecore_X_Window win,
                                    int type,
                                    Ecore_X_Window current_win)
{
   XEvent xev = { 0 };

   LOGFN;
   if (!root)
     root = DefaultRootWindow(_ecore_x_disp);

   xev.xclient.type = ClientMessage;
   xev.xclient.display = _ecore_x_disp;
   xev.xclient.window = win;
   xev.xclient.message_type = ECORE_X_ATOM_NET_ACTIVE_WINDOW;
   xev.xclient.format = 32;
   xev.xclient.data.l[0] = type;
   xev.xclient.data.l[1] = CurrentTime;
   xev.xclient.data.l[2] = current_win;
   xev.xclient.data.l[3] = 0;
   xev.xclient.data.l[4] = 0;

   XSendEvent(_ecore_x_disp, root, False,
              SubstructureRedirectMask | SubstructureNotifyMask, &xev);
   if (_ecore_xlib_sync) ecore_x_sync();
}

EAPI void
ecore_x_netwm_name_set(Ecore_X_Window win,
                       const char *name)
{
   LOGFN;
   _ecore_x_window_prop_string_utf8_set(win, ECORE_X_ATOM_NET_WM_NAME, name);
}

EAPI int
ecore_x_netwm_name_get(Ecore_X_Window win,
                       char **name)
{
   LOGFN;
   if (name)
     *name = _ecore_x_window_prop_string_utf8_get(win,
                                                  ECORE_X_ATOM_NET_WM_NAME);

   return 1;
}

EAPI void
ecore_x_netwm_startup_id_set(Ecore_X_Window win,
                             const char *id)
{
   LOGFN;
   _ecore_x_window_prop_string_utf8_set(win, ECORE_X_ATOM_NET_STARTUP_ID, id);
}

EAPI int
ecore_x_netwm_startup_id_get(Ecore_X_Window win,
                             char **id)
{
   LOGFN;
   if (id)
     *id = _ecore_x_window_prop_string_utf8_get(win,
                                                ECORE_X_ATOM_NET_STARTUP_ID);

   return 1;
}

EAPI void
ecore_x_netwm_visible_name_set(Ecore_X_Window win,
                               const char *name)
{
   LOGFN;
   _ecore_x_window_prop_string_utf8_set(win, ECORE_X_ATOM_NET_WM_VISIBLE_NAME,
                                        name);
}

EAPI int
ecore_x_netwm_visible_name_get(Ecore_X_Window win,
                               char **name)
{
   LOGFN;
   if (name)
     *name = _ecore_x_window_prop_string_utf8_get(
         win,
         ECORE_X_ATOM_NET_WM_VISIBLE_NAME);

   return 1;
}

EAPI void
ecore_x_netwm_icon_name_set(Ecore_X_Window win,
                            const char *name)
{
   LOGFN;
   _ecore_x_window_prop_string_utf8_set(win, ECORE_X_ATOM_NET_WM_ICON_NAME,
                                        name);
}

EAPI int
ecore_x_netwm_icon_name_get(Ecore_X_Window win,
                            char **name)
{
   LOGFN;
   if (name)
     *name = _ecore_x_window_prop_string_utf8_get(
         win,
         ECORE_X_ATOM_NET_WM_ICON_NAME);

   return 1;
}

EAPI void
ecore_x_netwm_visible_icon_name_set(Ecore_X_Window win,
                                    const char *name)
{
   LOGFN;
   _ecore_x_window_prop_string_utf8_set(win,
                                        ECORE_X_ATOM_NET_WM_VISIBLE_ICON_NAME,
                                        name);
}

EAPI int
ecore_x_netwm_visible_icon_name_get(Ecore_X_Window win,
                                    char **name)
{
   LOGFN;
   if (name)
     *name = _ecore_x_window_prop_string_utf8_get(
         win,
         ECORE_X_ATOM_NET_WM_VISIBLE_ICON_NAME);

   return 1;
}

EAPI void
ecore_x_netwm_desktop_set(Ecore_X_Window win,
                          unsigned int desk)
{
   LOGFN;
   ecore_x_window_prop_card32_set(win, ECORE_X_ATOM_NET_WM_DESKTOP, &desk, 1);
}

EAPI Eina_Bool
ecore_x_netwm_desktop_get(Ecore_X_Window win,
                          unsigned int *desk)
{
   int ret;
   unsigned int tmp;

   LOGFN;
   ret = ecore_x_window_prop_card32_get(win, ECORE_X_ATOM_NET_WM_DESKTOP,
                                        &tmp, 1);

   if (desk)
     *desk = tmp;

   return ret == 1 ? EINA_TRUE : EINA_FALSE;
}

/*
 * _NET_WM_STRUT is deprecated
 */
EAPI void
ecore_x_netwm_strut_set(Ecore_X_Window win,
                        int left,
                        int right,
                        int top,
                        int bottom)
{
   unsigned int strut[4];

   LOGFN;
   strut[0] = left;
   strut[1] = right;
   strut[2] = top;
   strut[3] = bottom;
   ecore_x_window_prop_card32_set(win, ECORE_X_ATOM_NET_WM_STRUT, strut, 4);
}

/*
 * _NET_WM_STRUT is deprecated
 */
EAPI Eina_Bool
ecore_x_netwm_strut_get(Ecore_X_Window win,
                        int *left,
                        int *right,
                        int *top,
                        int *bottom)
{
   int ret = 0;
   unsigned int strut[4];

   LOGFN;
   ret = ecore_x_window_prop_card32_get(win,
                                        ECORE_X_ATOM_NET_WM_STRUT,
                                        strut,
                                        4);
   if (ret != 4)
     return EINA_FALSE;

   if (left)
     *left = strut[0];

   if (right)
     *right = strut[1];

   if (top)
     *top = strut[2];

   if (bottom)
     *bottom = strut[3];

   return EINA_TRUE;
}

EAPI void
ecore_x_netwm_strut_partial_set(Ecore_X_Window win,
                                int left,
                                int right,
                                int top,
                                int bottom,
                                int left_start_y,
                                int left_end_y,
                                int right_start_y,
                                int right_end_y,
                                int top_start_x,
                                int top_end_x,
                                int bottom_start_x,
                                int bottom_end_x)
{
   unsigned int strut[12];

   LOGFN;
   strut[0] = left;
   strut[1] = right;
   strut[2] = top;
   strut[3] = bottom;
   strut[4] = left_start_y;
   strut[5] = left_end_y;
   strut[6] = right_start_y;
   strut[7] = right_end_y;
   strut[8] = top_start_x;
   strut[9] = top_end_x;
   strut[10] = bottom_start_x;
   strut[11] = bottom_end_x;
   ecore_x_window_prop_card32_set(win,
                                  ECORE_X_ATOM_NET_WM_STRUT_PARTIAL,
                                  strut,
                                  12);
}

EAPI Eina_Bool
ecore_x_netwm_strut_partial_get(Ecore_X_Window win,
                                int *left,
                                int *right,
                                int *top,
                                int *bottom,
                                int *left_start_y,
                                int *left_end_y,
                                int *right_start_y,
                                int *right_end_y,
                                int *top_start_x,
                                int *top_end_x,
                                int *bottom_start_x,
                                int *bottom_end_x)
{
   int ret = 0;
   unsigned int strut[12];

   LOGFN;
   ret = ecore_x_window_prop_card32_get(win,
                                        ECORE_X_ATOM_NET_WM_STRUT_PARTIAL,
                                        strut,
                                        12);
   if (ret != 12)
     return EINA_FALSE;

   if (left)
     *left = strut[0];

   if (right)
     *right = strut[1];

   if (top)
     *top = strut[2];

   if (bottom)
     *bottom = strut[3];

   if (left_start_y)
     *left_start_y = strut[4];

   if (left_end_y)
     *left_end_y = strut[5];

   if (right_start_y)
     *right_start_y = strut[6];

   if (right_end_y)
     *right_end_y = strut[7];

   if (top_start_x)
     *top_start_x = strut[8];

   if (top_end_x)
     *top_end_x = strut[9];

   if (bottom_start_x)
     *bottom_start_x = strut[10];

   if (bottom_end_x)
     *bottom_end_x = strut[11];

   return EINA_TRUE;
}

EAPI void
ecore_x_netwm_icons_set(Ecore_X_Window win,
                        Ecore_X_Icon *icon,
                        int num)
{
   unsigned int *data, *p, *p2;
   unsigned int i, size, x, y;

   LOGFN;
   size = 0;
   for (i = 0; i < (unsigned int)num; i++)
     {
        size += 2 + (icon[i].width * icon[i].height);
     }
   data = malloc(size * sizeof(unsigned int));
   if (!data) return;
   p = data;
   for (i = 0; i < (unsigned int)num; i++)
     {
        p[0] = icon[i].width;
        p[1] = icon[i].height;
        p += 2;
        p2 = icon[i].data;
        for (y = 0; y < icon[i].height; y++)
          {
             for (x = 0; x < icon[i].width; x++)
               {
                  unsigned int r, g, b, a;

                  a = (*p2 >> 24) & 0xff;
                  r = (*p2 >> 16) & 0xff;
                  g = (*p2 >> 8 ) & 0xff;
                  b = (*p2      ) & 0xff;
                  if ((a > 0) && (a < 255))
                    {
                       // unpremul
                       r = (r * 255) / a;
                       g = (g * 255) / a;
                       b = (b * 255) / a;
                    }
                  *p = (a << 24) | (r << 16) | (g << 8) | b;
                  p++;
                  p2++;
               }
          }
     }
   ecore_x_window_prop_card32_set(win, ECORE_X_ATOM_NET_WM_ICON,
                                  data, size);
   free(data);
}

EAPI Eina_Bool
ecore_x_netwm_icons_get(Ecore_X_Window win,
                        Ecore_X_Icon **icon,
                        int *num)
{
   unsigned int *data, *p;
   unsigned int *src;
   unsigned int len, icons, i;
   int num_ret;

   LOGFN;
   if (num)
     *num = 0;

   if (icon)
     *icon = NULL;

   num_ret = ecore_x_window_prop_card32_list_get(win, ECORE_X_ATOM_NET_WM_ICON,
                                                 &data);
   if (num_ret <= 0)
     return EINA_FALSE;

   if (!data)
     return EINA_FALSE;

   if (num_ret < 2)
     {
        free(data);
        return EINA_FALSE;
     }

   /* Check how many icons there are */
   icons = 0;
   p = data;
   while (p)
     {
        len = p[0] * p[1];
        p += (len + 2);
        if ((p - data) > num_ret)
          {
             free(data);
             return EINA_FALSE;
          }

        icons++;

        if ((p - data) == num_ret)
          p = NULL;
     }
   if (num)
     *num = icons;

   /* If the user doesn't want the icons, return */
   if (!icon)
     {
        free(data);
        return EINA_TRUE;
     }

   /* Allocate memory */
   *icon = malloc(icons * sizeof(Ecore_X_Icon));
   if (!(*icon))
     {
        free(data);
        return EINA_FALSE;
     }

   /* Fetch the icons */
   p = data;
   for (i = 0; i < icons; i++)
     {
        unsigned int *ps, *pd, *pe;

        len = p[0] * p[1];
        ((*icon)[i]).width = p[0];
        ((*icon)[i]).height = p[1];
        src = &(p[2]);
        ((*icon)[i]).data = malloc(len * sizeof(unsigned int));
        if (!((*icon)[i]).data)
          {
             while (i)
               free(((*icon)[--i]).data);
             free(*icon);
             free(data);
             return EINA_FALSE;
          }

        pd = ((*icon)[i]).data;
        ps = src;
        pe = ps + len;
        for (; ps < pe; ps++)
          {
             unsigned int r, g, b, a;

             a = (*ps >> 24) & 0xff;
             r = (((*ps >> 16) & 0xff) * a) / 255;
             g = (((*ps >> 8) & 0xff) * a) / 255;
             b = (((*ps) & 0xff) * a) / 255;
             *pd = (a << 24) | (r << 16) | (g << 8) | (b);
             pd++;
          }
        p += (len + 2);
     }

   free(data);

   return EINA_TRUE;
}

EAPI void
ecore_x_netwm_icon_geometry_set(Ecore_X_Window win,
                                int x,
                                int y,
                                int width,
                                int height)
{
   unsigned int geometry[4];

   LOGFN;
   geometry[0] = x;
   geometry[1] = y;
   geometry[2] = width;
   geometry[3] = height;
   ecore_x_window_prop_card32_set(win,
                                  ECORE_X_ATOM_NET_WM_ICON_GEOMETRY,
                                  geometry,
                                  4);
}

EAPI Eina_Bool
ecore_x_netwm_icon_geometry_get(Ecore_X_Window win,
                                int *x,
                                int *y,
                                int *width,
                                int *height)
{
   int ret;
   unsigned int geometry[4];

   LOGFN;
   ret = ecore_x_window_prop_card32_get(win,
                                        ECORE_X_ATOM_NET_WM_ICON_GEOMETRY,
                                        geometry,
                                        4);
   if (ret != 4)
     return EINA_FALSE;

   if (x)
     *x = geometry[0];

   if (y)
     *y = geometry[1];

   if (width)
     *width = geometry[2];

   if (height)
     *height = geometry[3];

   return EINA_TRUE;
}

EAPI void
ecore_x_netwm_pid_set(Ecore_X_Window win,
                      int pid)
{
   unsigned int tmp;

   LOGFN;
   tmp = pid;
   ecore_x_window_prop_card32_set(win, ECORE_X_ATOM_NET_WM_PID,
                                  &tmp, 1);
}

EAPI Eina_Bool
ecore_x_netwm_pid_get(Ecore_X_Window win,
                      int *pid)
{
   int ret;
   unsigned int tmp;

   LOGFN;
   ret = ecore_x_window_prop_card32_get(win, ECORE_X_ATOM_NET_WM_PID,
                                        &tmp, 1);
   if (pid)
     *pid = tmp;

   return ret == 1 ? EINA_TRUE : EINA_FALSE;
}

EAPI void
ecore_x_netwm_handled_icons_set(Ecore_X_Window win)
{
   LOGFN;
   ecore_x_window_prop_card32_set(win, ECORE_X_ATOM_NET_WM_HANDLED_ICONS,
                                  NULL, 0);
}

EAPI Eina_Bool
ecore_x_netwm_handled_icons_get(Ecore_X_Window win)
{
   int ret = 0;
   LOGFN;
   ret = ecore_x_window_prop_card32_get(win, ECORE_X_ATOM_NET_WM_HANDLED_ICONS,
                                        NULL, 0);
   return ret == 0 ? EINA_TRUE : EINA_FALSE;
}

EAPI void
ecore_x_netwm_user_time_set(Ecore_X_Window win,
                            unsigned int tim)
{
   LOGFN;
   ecore_x_window_prop_card32_set(win, ECORE_X_ATOM_NET_WM_USER_TIME,
                                  &tim, 1);
}

EAPI Eina_Bool
ecore_x_netwm_user_time_get(Ecore_X_Window win,
                            unsigned int *tim)
{
   int ret;
   unsigned int tmp;

   LOGFN;
   ret = ecore_x_window_prop_card32_get(win, ECORE_X_ATOM_NET_WM_USER_TIME,
                                        &tmp, 1);
   if (tim)
     *tim = tmp;

   return ret == 1 ? EINA_TRUE : EINA_FALSE;
}

Ecore_X_Window_State
_ecore_x_netwm_state_get(Ecore_X_Atom a)
{
   if (a == ECORE_X_ATOM_NET_WM_STATE_MODAL)
     return ECORE_X_WINDOW_STATE_MODAL;
   else if (a == ECORE_X_ATOM_NET_WM_STATE_STICKY)
     return ECORE_X_WINDOW_STATE_STICKY;
   else if (a == ECORE_X_ATOM_NET_WM_STATE_MAXIMIZED_VERT)
     return ECORE_X_WINDOW_STATE_MAXIMIZED_VERT;
   else if (a == ECORE_X_ATOM_NET_WM_STATE_MAXIMIZED_HORZ)
     return ECORE_X_WINDOW_STATE_MAXIMIZED_HORZ;
   else if (a == ECORE_X_ATOM_NET_WM_STATE_SHADED)
     return ECORE_X_WINDOW_STATE_SHADED;
   else if (a == ECORE_X_ATOM_NET_WM_STATE_SKIP_TASKBAR)
     return ECORE_X_WINDOW_STATE_SKIP_TASKBAR;
   else if (a == ECORE_X_ATOM_NET_WM_STATE_SKIP_PAGER)
     return ECORE_X_WINDOW_STATE_SKIP_PAGER;
   else if (a == ECORE_X_ATOM_NET_WM_STATE_HIDDEN)
     return ECORE_X_WINDOW_STATE_HIDDEN;
   else if (a == ECORE_X_ATOM_NET_WM_STATE_FULLSCREEN)
     return ECORE_X_WINDOW_STATE_FULLSCREEN;
   else if (a == ECORE_X_ATOM_NET_WM_STATE_ABOVE)
     return ECORE_X_WINDOW_STATE_ABOVE;
   else if (a == ECORE_X_ATOM_NET_WM_STATE_BELOW)
     return ECORE_X_WINDOW_STATE_BELOW;
   else if (a == ECORE_X_ATOM_NET_WM_STATE_DEMANDS_ATTENTION)
     return ECORE_X_WINDOW_STATE_DEMANDS_ATTENTION;
   else
     return ECORE_X_WINDOW_STATE_UNKNOWN;
}

static Ecore_X_Atom
_ecore_x_netwm_state_atom_get(Ecore_X_Window_State s)
{
   switch (s)
     {
      case ECORE_X_WINDOW_STATE_MODAL:
        return ECORE_X_ATOM_NET_WM_STATE_MODAL;

      case ECORE_X_WINDOW_STATE_STICKY:
        return ECORE_X_ATOM_NET_WM_STATE_STICKY;

      case ECORE_X_WINDOW_STATE_MAXIMIZED_VERT:
        return ECORE_X_ATOM_NET_WM_STATE_MAXIMIZED_VERT;

      case ECORE_X_WINDOW_STATE_MAXIMIZED_HORZ:
        return ECORE_X_ATOM_NET_WM_STATE_MAXIMIZED_HORZ;

      case ECORE_X_WINDOW_STATE_SHADED:
        return ECORE_X_ATOM_NET_WM_STATE_SHADED;

      case ECORE_X_WINDOW_STATE_SKIP_TASKBAR:
        return ECORE_X_ATOM_NET_WM_STATE_SKIP_TASKBAR;

      case ECORE_X_WINDOW_STATE_SKIP_PAGER:
        return ECORE_X_ATOM_NET_WM_STATE_SKIP_PAGER;

      case ECORE_X_WINDOW_STATE_HIDDEN:
        return ECORE_X_ATOM_NET_WM_STATE_HIDDEN;

      case ECORE_X_WINDOW_STATE_FULLSCREEN:
        return ECORE_X_ATOM_NET_WM_STATE_FULLSCREEN;

      case ECORE_X_WINDOW_STATE_ABOVE:
        return ECORE_X_ATOM_NET_WM_STATE_ABOVE;

      case ECORE_X_WINDOW_STATE_BELOW:
        return ECORE_X_ATOM_NET_WM_STATE_BELOW;

      case ECORE_X_WINDOW_STATE_DEMANDS_ATTENTION:
        return ECORE_X_ATOM_NET_WM_STATE_DEMANDS_ATTENTION;

      default:
        return 0;
     }
}

EAPI void
ecore_x_netwm_window_state_set(Ecore_X_Window win,
                               Ecore_X_Window_State *state,
                               unsigned int num)
{
   Ecore_X_Atom *set;
   unsigned int i;

   LOGFN;
   if (!num)
     {
        ecore_x_window_prop_property_del(win, ECORE_X_ATOM_NET_WM_STATE);
        return;
     }

   set = malloc(num * sizeof(Ecore_X_Atom));
   if (!set)
     return;

   for (i = 0; i < num; i++)
     set[i] = _ecore_x_netwm_state_atom_get(state[i]);

   ecore_x_window_prop_atom_set(win, ECORE_X_ATOM_NET_WM_STATE, set, num);

   free(set);
}

EAPI Eina_Bool
ecore_x_netwm_window_state_get(Ecore_X_Window win,
                               Ecore_X_Window_State **state,
                               unsigned int *num)
{
   int num_ret, i;
   Ecore_X_Atom *atoms;

   LOGFN;
   if (num)
     *num = 0;

   if (state)
     *state = NULL;

   num_ret = ecore_x_window_prop_atom_list_get(win, ECORE_X_ATOM_NET_WM_STATE,
                                               &atoms);
   if (num_ret <= 0)
     return EINA_FALSE;

   if (state)
     {
        *state = malloc(num_ret * sizeof(Ecore_X_Window_State));
        if (*state)
          for (i = 0; i < num_ret; ++i)
            (*state)[i] = _ecore_x_netwm_state_get(atoms[i]);

        if (num)
          *num = num_ret;
     }

   free(atoms);
   return EINA_TRUE;
}

static Ecore_X_Window_Type
_ecore_x_netwm_window_type_type_get(Ecore_X_Atom atom)
{
   if (atom == ECORE_X_ATOM_NET_WM_WINDOW_TYPE_DESKTOP)
     return ECORE_X_WINDOW_TYPE_DESKTOP;
   else if (atom == ECORE_X_ATOM_NET_WM_WINDOW_TYPE_DOCK)
     return ECORE_X_WINDOW_TYPE_DOCK;
   else if (atom == ECORE_X_ATOM_NET_WM_WINDOW_TYPE_TOOLBAR)
     return ECORE_X_WINDOW_TYPE_TOOLBAR;
   else if (atom == ECORE_X_ATOM_NET_WM_WINDOW_TYPE_MENU)
     return ECORE_X_WINDOW_TYPE_MENU;
   else if (atom == ECORE_X_ATOM_NET_WM_WINDOW_TYPE_UTILITY)
     return ECORE_X_WINDOW_TYPE_UTILITY;
   else if (atom == ECORE_X_ATOM_NET_WM_WINDOW_TYPE_SPLASH)
     return ECORE_X_WINDOW_TYPE_SPLASH;
   else if (atom == ECORE_X_ATOM_NET_WM_WINDOW_TYPE_DIALOG)
     return ECORE_X_WINDOW_TYPE_DIALOG;
   else if (atom == ECORE_X_ATOM_NET_WM_WINDOW_TYPE_NORMAL)
     return ECORE_X_WINDOW_TYPE_NORMAL;
   else if (atom == ECORE_X_ATOM_NET_WM_WINDOW_TYPE_DROPDOWN_MENU)
     return ECORE_X_WINDOW_TYPE_DROPDOWN_MENU;
   else if (atom == ECORE_X_ATOM_NET_WM_WINDOW_TYPE_POPUP_MENU)
     return ECORE_X_WINDOW_TYPE_POPUP_MENU;
   else if (atom == ECORE_X_ATOM_NET_WM_WINDOW_TYPE_TOOLTIP)
     return ECORE_X_WINDOW_TYPE_TOOLTIP;
   else if (atom == ECORE_X_ATOM_NET_WM_WINDOW_TYPE_NOTIFICATION)
     return ECORE_X_WINDOW_TYPE_NOTIFICATION;
   else if (atom == ECORE_X_ATOM_NET_WM_WINDOW_TYPE_COMBO)
     return ECORE_X_WINDOW_TYPE_COMBO;
   else if (atom == ECORE_X_ATOM_NET_WM_WINDOW_TYPE_DND)
     return ECORE_X_WINDOW_TYPE_DND;
   else
     return ECORE_X_WINDOW_TYPE_UNKNOWN;
}

static Ecore_X_Atom
_ecore_x_netwm_window_type_atom_get(Ecore_X_Window_Type type)
{
   switch (type)
     {
      case ECORE_X_WINDOW_TYPE_DESKTOP:
        return ECORE_X_ATOM_NET_WM_WINDOW_TYPE_DESKTOP;

      case ECORE_X_WINDOW_TYPE_DOCK:
        return ECORE_X_ATOM_NET_WM_WINDOW_TYPE_DOCK;

      case ECORE_X_WINDOW_TYPE_TOOLBAR:
        return ECORE_X_ATOM_NET_WM_WINDOW_TYPE_TOOLBAR;

      case ECORE_X_WINDOW_TYPE_MENU:
        return ECORE_X_ATOM_NET_WM_WINDOW_TYPE_MENU;

      case ECORE_X_WINDOW_TYPE_UTILITY:
        return ECORE_X_ATOM_NET_WM_WINDOW_TYPE_UTILITY;

      case ECORE_X_WINDOW_TYPE_SPLASH:
        return ECORE_X_ATOM_NET_WM_WINDOW_TYPE_SPLASH;

      case ECORE_X_WINDOW_TYPE_DIALOG:
        return ECORE_X_ATOM_NET_WM_WINDOW_TYPE_DIALOG;

      case ECORE_X_WINDOW_TYPE_NORMAL:
        return ECORE_X_ATOM_NET_WM_WINDOW_TYPE_NORMAL;

      case ECORE_X_WINDOW_TYPE_DROPDOWN_MENU:
        return ECORE_X_ATOM_NET_WM_WINDOW_TYPE_DROPDOWN_MENU;

      case ECORE_X_WINDOW_TYPE_POPUP_MENU:
        return ECORE_X_ATOM_NET_WM_WINDOW_TYPE_POPUP_MENU;

      case ECORE_X_WINDOW_TYPE_TOOLTIP:
        return ECORE_X_ATOM_NET_WM_WINDOW_TYPE_TOOLTIP;

      case ECORE_X_WINDOW_TYPE_NOTIFICATION:
        return ECORE_X_ATOM_NET_WM_WINDOW_TYPE_NOTIFICATION;

      case ECORE_X_WINDOW_TYPE_COMBO:
        return ECORE_X_ATOM_NET_WM_WINDOW_TYPE_COMBO;

      case ECORE_X_WINDOW_TYPE_DND:
        return ECORE_X_ATOM_NET_WM_WINDOW_TYPE_DND;

      default:
        return 0;
     }
}

/*
 * FIXME: We should set WM_TRANSIENT_FOR if type is ECORE_X_WINDOW_TYPE_TOOLBAR
 * , ECORE_X_WINDOW_TYPE_MENU or ECORE_X_WINDOW_TYPE_DIALOG
 */
EAPI void
ecore_x_netwm_window_type_set(Ecore_X_Window win,
                              Ecore_X_Window_Type type)
{
   Ecore_X_Atom atom;

   LOGFN;
   atom = _ecore_x_netwm_window_type_atom_get(type);
   ecore_x_window_prop_atom_set(win, ECORE_X_ATOM_NET_WM_WINDOW_TYPE,
                                &atom, 1);
}

/* FIXME: Maybe return 0 on some conditions? */
EAPI Eina_Bool
ecore_x_netwm_window_type_get(Ecore_X_Window win,
                              Ecore_X_Window_Type *type)
{
   int num;
   Ecore_X_Atom *atoms = NULL;

   LOGFN;
   if (type)
     *type = ECORE_X_WINDOW_TYPE_NORMAL;

   num = ecore_x_window_prop_atom_list_get(win,
                                           ECORE_X_ATOM_NET_WM_WINDOW_TYPE,
                                           &atoms);
   if ((type) && (num >= 1) && (atoms))
     *type = _ecore_x_netwm_window_type_type_get(atoms[0]);

   free(atoms);
   if (num >= 1)
     return EINA_TRUE;

   return EINA_FALSE;
}

EAPI int
ecore_x_netwm_window_types_get(Ecore_X_Window win,
                               Ecore_X_Window_Type **types)
{
   int num, i;
   Ecore_X_Atom *atoms = NULL;
   Ecore_X_Window_Type *atoms2 = NULL;

   LOGFN;
   if (types)
     *types = NULL;

   num = ecore_x_window_prop_atom_list_get(win,
                                           ECORE_X_ATOM_NET_WM_WINDOW_TYPE,
                                           &atoms);
   if ((num <= 0) || (!atoms))
     {
        if (atoms)
          free(atoms);

        return 0;
     }

   atoms2 = malloc(num * sizeof(Ecore_X_Window_Type));
   if (!atoms2)
     {
        if (atoms) free(atoms);
        return 0;
     }

   for (i = 0; i < num; i++)
     atoms2[i] = _ecore_x_netwm_window_type_type_get(atoms[i]);
   free(atoms);
   if (types)
     *types = atoms2;
   else
     free(atoms2);

   return num;
}

static Ecore_X_Atom
_ecore_x_netwm_action_atom_get(Ecore_X_Action action)
{
   switch (action)
     {
      case ECORE_X_ACTION_MOVE:
        return ECORE_X_ATOM_NET_WM_ACTION_MOVE;

      case ECORE_X_ACTION_RESIZE:
        return ECORE_X_ATOM_NET_WM_ACTION_RESIZE;

      case ECORE_X_ACTION_MINIMIZE:
        return ECORE_X_ATOM_NET_WM_ACTION_MINIMIZE;

      case ECORE_X_ACTION_SHADE:
        return ECORE_X_ATOM_NET_WM_ACTION_SHADE;

      case ECORE_X_ACTION_STICK:
        return ECORE_X_ATOM_NET_WM_ACTION_STICK;

      case ECORE_X_ACTION_MAXIMIZE_HORZ:
        return ECORE_X_ATOM_NET_WM_ACTION_MAXIMIZE_HORZ;

      case ECORE_X_ACTION_MAXIMIZE_VERT:
        return ECORE_X_ATOM_NET_WM_ACTION_MAXIMIZE_VERT;

      case ECORE_X_ACTION_FULLSCREEN:
        return ECORE_X_ATOM_NET_WM_ACTION_FULLSCREEN;

      case ECORE_X_ACTION_CHANGE_DESKTOP:
        return ECORE_X_ATOM_NET_WM_ACTION_CHANGE_DESKTOP;

      case ECORE_X_ACTION_CLOSE:
        return ECORE_X_ATOM_NET_WM_ACTION_CLOSE;

      case ECORE_X_ACTION_ABOVE:
        return ECORE_X_ATOM_NET_WM_ACTION_ABOVE;

      case ECORE_X_ACTION_BELOW:
        return ECORE_X_ATOM_NET_WM_ACTION_BELOW;

      default:
        return 0;
     }
}

/* FIXME: Get complete list */
EAPI Eina_Bool
ecore_x_netwm_allowed_action_isset(Ecore_X_Window win,
                                   Ecore_X_Action action)
{
   int num, i;
   Ecore_X_Atom *atoms, atom;
   Eina_Bool ret = EINA_FALSE;

   LOGFN;
   num = ecore_x_window_prop_atom_list_get(win, ECORE_X_ATOM_NET_WM_WINDOW_TYPE,
                                           &atoms);
   if (num <= 0)
     return ret;

   atom = _ecore_x_netwm_action_atom_get(action);

   for (i = 0; i < num; ++i)
     {
        if (atom == atoms[i])
          {
             ret = 1;
             break;
          }
     }

   free(atoms);
   return ret;
}

/* FIXME: Set complete list */
EAPI void
ecore_x_netwm_allowed_action_set(Ecore_X_Window win,
                                 Ecore_X_Action *action,
                                 unsigned int num)
{
   Ecore_X_Atom *set;
   unsigned int i;

   LOGFN;
   if (!num)
     {
        ecore_x_window_prop_property_del(win,
                                         ECORE_X_ATOM_NET_WM_ALLOWED_ACTIONS);
        return;
     }

   set = malloc(num * sizeof(Ecore_X_Atom));
   if (!set)
     return;

   for (i = 0; i < num; i++)
     set[i] = _ecore_x_netwm_action_atom_get(action[i]);

   ecore_x_window_prop_atom_set(win,
                                ECORE_X_ATOM_NET_WM_ALLOWED_ACTIONS,
                                set,
                                num);

   free(set);
}

EAPI Eina_Bool
ecore_x_netwm_allowed_action_get(Ecore_X_Window win,
                                 Ecore_X_Action **action,
                                 unsigned int *num)
{
   int num_ret, i;
   Ecore_X_Atom *atoms;

   LOGFN;
   if (num)
     *num = 0;

   if (action)
     *action = NULL;

   num_ret = ecore_x_window_prop_atom_list_get(
       win,
       ECORE_X_ATOM_NET_WM_ALLOWED_ACTIONS,
       &atoms);
   if (num_ret <= 0)
     return EINA_FALSE;

   if (action)
     {
        *action = malloc(num_ret * sizeof(Ecore_X_Action));
        if (*action)
          for (i = 0; i < num_ret; ++i)
            (*action)[i] = _ecore_x_netwm_action_atom_get(atoms[i]);

        if (num)
          *num = num_ret;
     }

   free(atoms);
   return EINA_TRUE;
}

EAPI void
ecore_x_netwm_opacity_set(Ecore_X_Window win,
                          unsigned int opacity)
{
   LOGFN;
   ecore_x_window_prop_card32_set(win, ECORE_X_ATOM_NET_WM_WINDOW_OPACITY,
                                  &opacity, 1);
}

EAPI Eina_Bool
ecore_x_netwm_opacity_get(Ecore_X_Window win,
                          unsigned int *opacity)
{
   int ret;
   unsigned int tmp;

   LOGFN;
   ret = ecore_x_window_prop_card32_get(win, ECORE_X_ATOM_NET_WM_WINDOW_OPACITY,
                                        &tmp, 1);
   if (opacity)
     *opacity = tmp;

   return ret == 1 ? EINA_TRUE : EINA_FALSE;
}

EAPI void
ecore_x_netwm_frame_size_set(Ecore_X_Window win,
                             int fl,
                             int fr,
                             int ft,
                             int fb)
{
   unsigned int frames[4];

   LOGFN;
   frames[0] = fl;
   frames[1] = fr;
   frames[2] = ft;
   frames[3] = fb;
   ecore_x_window_prop_card32_set(win,
                                  ECORE_X_ATOM_NET_FRAME_EXTENTS,
                                  frames,
                                  4);
}

EAPI Eina_Bool
ecore_x_netwm_frame_size_get(Ecore_X_Window win,
                             int *fl,
                             int *fr,
                             int *ft,
                             int *fb)
{
   int ret = 0;
   unsigned int frames[4];

   LOGFN;
   ret = ecore_x_window_prop_card32_get(win,
                                        ECORE_X_ATOM_NET_FRAME_EXTENTS,
                                        frames,
                                        4);
   if (ret != 4)
     return EINA_FALSE;

   if (fl)
     *fl = frames[0];

   if (fr)
     *fr = frames[1];

   if (ft)
     *ft = frames[2];

   if (fb)
     *fb = frames[3];

   return EINA_TRUE;
}

EAPI Eina_Bool
ecore_x_netwm_sync_counter_get(Ecore_X_Window win,
                               Ecore_X_Sync_Counter *counter)
{
   int ret;
   unsigned int tmp;

   LOGFN;
   ret = ecore_x_window_prop_card32_get(
       win,
       ECORE_X_ATOM_NET_WM_SYNC_REQUEST_COUNTER,
       &tmp,
       1);

   if (counter)
     *counter = tmp;

   return ret == 1 ? EINA_TRUE : EINA_FALSE;
}

EAPI void
ecore_x_netwm_ping_send(Ecore_X_Window win)
{
   XEvent xev = { 0 };

   if (!win)
     return;

   LOGFN;
   xev.xclient.type = ClientMessage;
   xev.xclient.display = _ecore_x_disp;
   xev.xclient.window = win;
   xev.xclient.message_type = ECORE_X_ATOM_WM_PROTOCOLS;
   xev.xclient.format = 32;
   xev.xclient.data.l[0] = ECORE_X_ATOM_NET_WM_PING;
   xev.xclient.data.l[1] = _ecore_x_event_last_time;
   xev.xclient.data.l[2] = win;
   xev.xclient.data.l[3] = 0;
   xev.xclient.data.l[4] = 0;

   XSendEvent(_ecore_x_disp, win, False, NoEventMask, &xev);
   if (_ecore_xlib_sync) ecore_x_sync();
}

EAPI void
ecore_x_netwm_sync_request_send(Ecore_X_Window win,
                                unsigned int serial)
{
   XSyncValue value;
   XEvent xev = { 0 };

   if (!win)
     return;

   LOGFN;
   XSyncIntToValue(&value, (int)serial);

   xev.xclient.type = ClientMessage;
   xev.xclient.display = _ecore_x_disp;
   xev.xclient.window = win;
   xev.xclient.message_type = ECORE_X_ATOM_WM_PROTOCOLS;
   xev.xclient.format = 32;
   xev.xclient.data.l[0] = ECORE_X_ATOM_NET_WM_SYNC_REQUEST;
   xev.xclient.data.l[1] = _ecore_x_event_last_time;
   xev.xclient.data.l[2] = XSyncValueLow32(value);
   xev.xclient.data.l[3] = XSyncValueHigh32(value);
   xev.xclient.data.l[4] = 0;

   XSendEvent(_ecore_x_disp, win, False, NoEventMask, &xev);
   if (_ecore_xlib_sync) ecore_x_sync();
}

EAPI void
ecore_x_netwm_state_request_send(Ecore_X_Window win,
                                 Ecore_X_Window root,
                                 Ecore_X_Window_State s1,
                                 Ecore_X_Window_State s2,
                                 Eina_Bool set)
{
   XEvent xev = { 0 };

   if (!win)
     return;

   LOGFN;
   if (!root)
     root = DefaultRootWindow(_ecore_x_disp);

   xev.xclient.type = ClientMessage;
   xev.xclient.serial = 0;
   xev.xclient.send_event = True;
   xev.xclient.display = _ecore_x_disp;
   xev.xclient.window = win;
   xev.xclient.format = 32;
   xev.xclient.message_type = ECORE_X_ATOM_NET_WM_STATE;
   xev.xclient.data.l[0] = !!set;
   xev.xclient.data.l[1] = _ecore_x_netwm_state_atom_get(s1);
   xev.xclient.data.l[2] = _ecore_x_netwm_state_atom_get(s2);
   /* 1 == normal client, if someone wants to use this
    * function in a pager, this should be 2 */
   xev.xclient.data.l[3] = 1;
   xev.xclient.data.l[4] = 0;

   XSendEvent(_ecore_x_disp, root, False,
              SubstructureNotifyMask | SubstructureRedirectMask, &xev);
   if (_ecore_xlib_sync) ecore_x_sync();
}

EAPI void
ecore_x_netwm_desktop_request_send(Ecore_X_Window win,
                                   Ecore_X_Window root,
                                   unsigned int desktop)
{
   XEvent xev = { 0 };

   if (!win)
     return;

   LOGFN;
   if (!root)
     root = DefaultRootWindow(_ecore_x_disp);

   xev.xclient.type = ClientMessage;
   xev.xclient.serial = 0;
   xev.xclient.send_event = True;
   xev.xclient.display = _ecore_x_disp;
   xev.xclient.window = win;
   xev.xclient.format = 32;
   xev.xclient.message_type = ECORE_X_ATOM_NET_WM_DESKTOP;
   xev.xclient.data.l[0] = desktop;

   XSendEvent(_ecore_x_disp, root, False,
              SubstructureNotifyMask | SubstructureRedirectMask, &xev);
   if (_ecore_xlib_sync) ecore_x_sync();
}

EAPI void
ecore_x_netwm_moveresize_request_send(Ecore_X_Window win,
                                      int x,
                                      int y,
                                      Ecore_X_Netwm_Direction direction,
                                      unsigned int button)
{
   XEvent xev = { 0 };

   if (!win)
     return;

   LOGFN;
   xev.xclient.window = win;
   xev.xclient.type = ClientMessage;
   xev.xclient.message_type = ECORE_X_ATOM_NET_WM_MOVERESIZE;
   xev.xclient.format = 32;
   xev.xclient.data.l[0] = x;
   xev.xclient.data.l[1] = y;
   xev.xclient.data.l[2] = direction;
   xev.xclient.data.l[3] = button;
   xev.xclient.data.l[4] = 1;

   XSendEvent(_ecore_x_disp, win, False,
              SubstructureNotifyMask | SubstructureRedirectMask, &xev);
   if (_ecore_xlib_sync) ecore_x_sync();
}

int
_ecore_x_netwm_startup_info_begin(Ecore_X_Window win EINA_UNUSED,
                                  char *data EINA_UNUSED)
{
#if 0
   Ecore_X_Startup_Info *info;
   unsigned char *exists = 0;

   if (!startup_info)
     return 0;

   info = eina_hash_find(startup_info, (void *)win);
   if (info)
     {
        exists = 1;
        WRN("Already got info for win: 0x%x", win);
        _ecore_x_netwm_startup_info_free(info);
     }

   info = calloc(1, sizeof(Ecore_X_Startup_Info));
   if (!info)
     return 0;

   info->win = win;
   info->length = 0;
   info->buffer_size = 161;
   info->buffer = calloc(info->buffer_size, sizeof(char));
   if (!info->buffer)
     {
        _ecore_x_netwm_startup_info_free(info);
        return 0;
     }

   memcpy(info->buffer, data, 20);
   info->length += 20;
   info->buffer[info->length] = 0;
   if (exists)
     eina_hash_modify(startup_info, (void *)info->win, info);
   else
     eina_hash_add(startup_info, (void *)info->win, info);

   if (strlen(info->buffer) != 20)
     /* We have a '\0' in there, the message is done */
     _ecore_x_netwm_startup_info_process(info);

#endif /* if 0 */
   return 1;
}

int
_ecore_x_netwm_startup_info(Ecore_X_Window win EINA_UNUSED,
                            char *data EINA_UNUSED)
{
#if 0
   Ecore_X_Startup_Info *info;
   char *p;

   if (!startup_info)
     return 0;

   info = eina_hash_find(startup_info, (void *)win);
   if (!info)
     return 0;

   if ((info->length + 20) > info->buffer_size)
     {
        info->buffer_size += 160;
        info->buffer = realloc(info->buffer, info->buffer_size * sizeof(char));
        if (!info->buffer)
          {
             eina_hash_del(startup_info, (void *)info->win);
             _ecore_x_netwm_startup_info_free(info);
             return 0;
          }
     }

   memcpy(info->buffer + info->length, data, 20);
   p = info->buffer + info->length;
   info->length += 20;
   info->buffer[info->length] = 0;
   if (strlen(p) != 20)
     /* We have a '\0' in there, the message is done */
     _ecore_x_netwm_startup_info_process(info);

#endif /* if 0 */
   return 1;
}

/*
 * Set UTF-8 string property
 */
static void
_ecore_x_window_prop_string_utf8_set(Ecore_X_Window win,
                                     Ecore_X_Atom atom,
                                     const char *str)
{
   XChangeProperty(_ecore_x_disp, win, atom, ECORE_X_ATOM_UTF8_STRING, 8,
                   PropModeReplace, (unsigned char *)str, strlen(str));
   if (_ecore_xlib_sync) ecore_x_sync();
}

/*
 * Get UTF-8 string property
 */
static char *
_ecore_x_window_prop_string_utf8_get(Ecore_X_Window win,
                                     Ecore_X_Atom atom)
{
   char *str;
   unsigned char *prop_ret;
   Atom type_ret;
   unsigned long bytes_after, num_ret;
   int format_ret;

   str = NULL;
   prop_ret = NULL;
   XGetWindowProperty(_ecore_x_disp, win, atom, 0, 0x7fffffff, False,
                      ECORE_X_ATOM_UTF8_STRING, &type_ret,
                      &format_ret, &num_ret, &bytes_after, &prop_ret);
   if (_ecore_xlib_sync) ecore_x_sync();
   if (prop_ret && num_ret > 0 && format_ret == 8)
     {
        str = malloc(num_ret + 1);
        if (str)
          {
             memcpy(str, prop_ret, num_ret);
             str[num_ret] = '\0';
          }
     }

   if (prop_ret)
     XFree(prop_ret);

   return str;
}

#if 0 /* Unused */
/*
 * Process startup info
 */
static int
_ecore_x_netwm_startup_info_process(Ecore_X_Startup_Info *info)
{
   Ecore_X_Event_Startup_Sequence *e;
   int event;
   char *p;

   p = strchr(info->buffer, ':');
   if (!p)
     {
        eina_hash_del(startup_info, (void *)info->win);
        _ecore_x_netwm_startup_info_free(info);
        return 0;
     }

   *p = 0;
   if (!strcmp(info->buffer, "new"))
     {
        if (info->init)
          event = ECORE_X_EVENT_STARTUP_SEQUENCE_CHANGE;
        else
          event = ECORE_X_EVENT_STARTUP_SEQUENCE_NEW;

        info->init = 1;
     }
   else if (!strcmp(info->buffer, "change"))
     event = ECORE_X_EVENT_STARTUP_SEQUENCE_CHANGE;
   else if (!strcmp(info->buffer, "remove"))
     event = ECORE_X_EVENT_STARTUP_SEQUENCE_REMOVE;
   else
     {
        eina_hash_del(startup_info, (void *)info->win);
        _ecore_x_netwm_startup_info_free(info);
        return 0;
     }

   p++;

   if (!_ecore_x_netwm_startup_info_parse(info, p))
     {
        eina_hash_del(startup_info, (void *)info->win);
        _ecore_x_netwm_startup_info_free(info);
        return 0;
     }

   if (info->init)
     {
        e = calloc(1, sizeof(Ecore_X_Event_Startup_Sequence));
        if (!e)
          {
             eina_hash_del(startup_info, (void *)info->win);
             _ecore_x_netwm_startup_info_free(info);
             return 0;
          }

        e->win = info->win;
        ecore_event_add(event, e, NULL, NULL);
     }

   if (event == ECORE_X_EVENT_STARTUP_SEQUENCE_REMOVE)
     {
        eina_hash_del(startup_info, (void *)info->win);
        _ecore_x_netwm_startup_info_free(info);
     }
   else
     {
        /* Discard buffer */
        info->length = 0;
        info->buffer[0] = 0;
     }

   return 1;
}

/*
 * Parse startup info
 */
static int
_ecore_x_netwm_startup_info_parse(Ecore_X_Startup_Info *info,
                                  char *data)
{
   while (*data)
     {
        int in_quot_sing, in_quot_dbl, escaped;
        char *p, *pp;
        char *key;
        char value[1024];

        /* Skip space */
        while (*data == ' ')
          data++;
        /* Get key */
        key = data;
        data = strchr(key, '=');
        if (!data)
          return 0;

        *data = 0;
        data++;

        /* Get value */
        p = data;
        pp = value;
        in_quot_dbl = 0;
        in_quot_sing = 0;
        escaped = 0;
        while (*p)
          {
             if ((pp - value) >= 1024)
               return 0;

             if (escaped)
               {
                  *pp = *p;
                  pp++;
                  escaped = 0;
               }
             else if (in_quot_sing)
               {
                  if (*p == '\\')
                    escaped = 1;
                  else if (*p == '\'')
                    in_quot_sing = 0;
                  else
                    {
                       *pp = *p;
                       pp++;
                    }
               }
             else if (in_quot_dbl)
               {
                  if (*p == '\\')
                    escaped = 1;
                  else if (*p == '\"')
                    in_quot_dbl = 0;
                  else
                    {
                       *pp = *p;
                       pp++;
                    }
               }
             else
               {
                  if (*p == '\\')
                    escaped = 1;
                  else if (*p == '\'')
                    in_quot_sing = 1;
                  else if (*p == '\"')
                    in_quot_dbl = 1;
                  else if (*p == ' ')
                    break;
                  else
                    {
                       *pp = *p;
                       pp++;
                    }
               }

             p++;
          }
        if ((in_quot_dbl) || (in_quot_sing))
          return 0;

        data = p;
        *pp = 0;

        /* Parse info */
        if (!strcmp(key, "ID"))
          {
             if ((info->id) && (strcmp(info->id, value)))
               return 0;

             info->id = strdup(value);
             p = strstr(value, "_TIME");
             if (p)
               info->timestamp = atoi(p + 5);
          }
        else if (!strcmp(key, "NAME"))
          {
             if (info->name)
               free(info->name);

             info->name = strdup(value);
          }
        else if (!strcmp(key, "SCREEN"))
          info->screen = atoi(value);
        else if (!strcmp(key, "BIN"))
          {
             if (info->bin)
               free(info->bin);

             info->bin = strdup(value);
          }
        else if (!strcmp(key, "ICON"))
          {
             if (info->icon)
               free(info->icon);

             info->icon = strdup(value);
          }
        else if (!strcmp(key, "DESKTOP"))
          info->desktop = atoi(value);
        else if (!strcmp(key, "TIMESTAMP"))
          {
             if (!info->timestamp)
               info->timestamp = atoi(value);
          }
        else if (!strcmp(key, "DESCRIPTION"))
          {
             if (info->description)
               free(info->description);

             info->description = strdup(value);
          }
        else if (!strcmp(key, "WMCLASS"))
          {
             if (info->wmclass)
               free(info->wmclass);

             info->wmclass = strdup(value);
          }
        else if (!strcmp(key, "SILENT"))
          info->silent = atoi(value);
        else
          ERR("Ecore X Sequence, Unknown: %s=%s", key, value);
     }
   if (!info->id)
     return 0;

   return 1;
}

#endif /* if 0 */

/*
 * Free startup info struct
 */
static void
_ecore_x_netwm_startup_info_free(void *data)
{
   Ecore_X_Startup_Info *info;

   info = data;
   if (!info)
     return;

   if (info->buffer)
     free(info->buffer);

   if (info->id)
     free(info->id);

   if (info->name)
     free(info->name);

   if (info->bin)
     free(info->bin);

   if (info->icon)
     free(info->icon);

   if (info->description)
     free(info->description);

   if (info->wmclass)
     free(info->wmclass);

   free(info);
}

/*
 * Is screen composited?
 */
EAPI Eina_Bool
ecore_x_screen_is_composited(int screen)
{
   Ecore_X_Window win;
   Ecore_X_Atom atom;
   char buf[32];

   LOGFN;
   snprintf(buf, sizeof(buf), "_NET_WM_CM_S%i", screen);
   atom = XInternAtom(_ecore_x_disp, buf, True);
   if (atom == None) return EINA_FALSE;
   win = XGetSelectionOwner(_ecore_x_disp, atom);
   if (_ecore_xlib_sync) ecore_x_sync();
   return (win != None) ? EINA_TRUE : EINA_FALSE;
}

EAPI void
ecore_x_screen_is_composited_set(int screen,
                                 Ecore_X_Window win)
{
   Ecore_X_Atom atom;
   char buf[32];

   LOGFN;
   snprintf(buf, sizeof(buf), "_NET_WM_CM_S%i", screen);
   atom = XInternAtom(_ecore_x_disp, buf, False);
   if (atom == None) return;
   XSetSelectionOwner(_ecore_x_disp, atom, win, _ecore_x_event_last_time);
   if (_ecore_xlib_sync) ecore_x_sync();
}

