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

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif /* ifdef HAVE_CONFIG_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(__FILE__, __LINE__, __FUNCTION__);
   startup_info = eina_hash_string_superfast_new(
         _ecore_x_netwm_startup_info_free);
} /* ecore_x_netwm_init */

EAPI void
ecore_x_netwm_shutdown(void)
{
   LOGFN(__FILE__, __LINE__, __FUNCTION__);
   if (startup_info)
      eina_hash_free(startup_info);

   startup_info = NULL;
} /* ecore_x_netwm_shutdown */

/*
 * WM identification
 */
EAPI void
ecore_x_netwm_wm_identify(Ecore_X_Window root, Ecore_X_Window check,
                          const char *wm_name)
{
   LOGFN(__FILE__, __LINE__, __FUNCTION__);
   ecore_x_window_prop_window_set(root,
                                  ECORE_X_ATOM_NET_SUPPORTING_WM_CHECK,
                                  &check,
                                  1);
   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_netwm_wm_identify */

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

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

   if (num)
      *num = 0;

   if (supported)
      *supported = NULL;

   LOGFN(__FILE__, __LINE__, __FUNCTION__);
   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;
} /* ecore_x_netwm_supported_get */

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

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

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(__FILE__, __LINE__, __FUNCTION__);
   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-%d", 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);

   free(buf);
} /* ecore_x_netwm_desk_names_set */

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

   LOGFN(__FILE__, __LINE__, __FUNCTION__);
   size[0] = width;
   size[1] = height;
   ecore_x_window_prop_card32_set(root, ECORE_X_ATOM_NET_DESKTOP_GEOMETRY, size,
                                  2);
} /* ecore_x_netwm_desk_size_set */

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

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(__FILE__, __LINE__, __FUNCTION__);
   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);
} /* ecore_x_netwm_desk_layout_set */

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

EAPI void
ecore_x_netwm_desk_current_set(Ecore_X_Window root, unsigned int desk)
{
   LOGFN(__FILE__, __LINE__, __FUNCTION__);
   ecore_x_window_prop_card32_set(root, ECORE_X_ATOM_NET_CURRENT_DESKTOP, &desk,
                                  1);
} /* ecore_x_netwm_desk_current_set */

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

   LOGFN(__FILE__, __LINE__, __FUNCTION__);
   val = (on) ? 1 : 0;
   ecore_x_window_prop_card32_set(root, ECORE_X_ATOM_NET_SHOWING_DESKTOP, &val,
                                  1);
} /* ecore_x_netwm_showing_desktop_set */

/*
 * 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(__FILE__, __LINE__, __FUNCTION__);
   ecore_x_window_prop_window_set(root, ECORE_X_ATOM_NET_CLIENT_LIST,
                                  p_clients, n_clients);
} /* ecore_x_netwm_client_list_set */

/* 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(__FILE__, __LINE__, __FUNCTION__);
   ecore_x_window_prop_window_set(root, ECORE_X_ATOM_NET_CLIENT_LIST_STACKING,
                                  p_clients, n_clients);
} /* ecore_x_netwm_client_list_stacking_set */

EAPI void
ecore_x_netwm_client_active_set(Ecore_X_Window root, Ecore_X_Window win)
{
   LOGFN(__FILE__, __LINE__, __FUNCTION__);
   ecore_x_window_prop_window_set(root, ECORE_X_ATOM_NET_ACTIVE_WINDOW,
                                  &win, 1);
} /* ecore_x_netwm_client_active_set */

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;

   LOGFN(__FILE__, __LINE__, __FUNCTION__);
   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);
} /* ecore_x_netwm_client_active_request */

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

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

   return 1;
} /* ecore_x_netwm_name_get */

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

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

   return 1;
} /* ecore_x_netwm_startup_id_get */

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

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

   return 1;
} /* ecore_x_netwm_visible_name_get */

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

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

   return 1;
} /* ecore_x_netwm_icon_name_get */

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

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

   return 1;
} /* ecore_x_netwm_visible_icon_name_get */

EAPI void
ecore_x_netwm_desktop_set(Ecore_X_Window win, unsigned int desk)
{
   LOGFN(__FILE__, __LINE__, __FUNCTION__);
   ecore_x_window_prop_card32_set(win, ECORE_X_ATOM_NET_WM_DESKTOP, &desk, 1);
} /* ecore_x_netwm_desktop_set */

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

   LOGFN(__FILE__, __LINE__, __FUNCTION__);
   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;
} /* ecore_x_netwm_desktop_get */

/*
 * _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(__FILE__, __LINE__, __FUNCTION__);
   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);
} /* ecore_x_netwm_strut_set */

/*
 * _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(__FILE__, __LINE__, __FUNCTION__);
   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;
} /* ecore_x_netwm_strut_get */

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(__FILE__, __LINE__, __FUNCTION__);
   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);
} /* ecore_x_netwm_strut_partial_set */

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(__FILE__, __LINE__, __FUNCTION__);
   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;
} /* ecore_x_netwm_strut_partial_get */

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(__FILE__, __LINE__, __FUNCTION__);
   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;
} /* ecore_x_netwm_icons_get */

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(__FILE__, __LINE__, __FUNCTION__);
   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);
} /* ecore_x_netwm_icon_geometry_set */

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(__FILE__, __LINE__, __FUNCTION__);
   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;
} /* ecore_x_netwm_icon_geometry_get */

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

   LOGFN(__FILE__, __LINE__, __FUNCTION__);
   tmp = pid;
   ecore_x_window_prop_card32_set(win, ECORE_X_ATOM_NET_WM_PID,
                                  &tmp, 1);
} /* ecore_x_netwm_pid_set */

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

   LOGFN(__FILE__, __LINE__, __FUNCTION__);
   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;
} /* ecore_x_netwm_pid_get */

EAPI void
ecore_x_netwm_handled_icons_set(Ecore_X_Window win)
{
   LOGFN(__FILE__, __LINE__, __FUNCTION__);
   ecore_x_window_prop_card32_set(win, ECORE_X_ATOM_NET_WM_HANDLED_ICONS,
                                  NULL, 0);
} /* ecore_x_netwm_handled_icons_set */

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

EAPI void
ecore_x_netwm_user_time_set(Ecore_X_Window win, unsigned int time)
{
   LOGFN(__FILE__, __LINE__, __FUNCTION__);
   ecore_x_window_prop_card32_set(win, ECORE_X_ATOM_NET_WM_USER_TIME,
                                  &time, 1);
} /* ecore_x_netwm_user_time_set */

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

   LOGFN(__FILE__, __LINE__, __FUNCTION__);
   ret = ecore_x_window_prop_card32_get(win, ECORE_X_ATOM_NET_WM_USER_TIME,
                                        &tmp, 1);
   if (time)
      *time = tmp;

   return ret == 1 ? EINA_TRUE : EINA_FALSE;
} /* ecore_x_netwm_user_time_get */

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;
} /* _ecore_x_netwm_state_get */

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;
     } /* switch */
} /* _ecore_x_netwm_state_atom_get */

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(__FILE__, __LINE__, __FUNCTION__);
   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);
} /* ecore_x_netwm_window_state_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(__FILE__, __LINE__, __FUNCTION__);
   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;
} /* ecore_x_netwm_window_state_get */

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;
} /* _ecore_x_netwm_window_type_type_get */

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;
     } /* switch */
} /* _ecore_x_netwm_window_type_atom_get */

/*
 * 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(__FILE__, __LINE__, __FUNCTION__);
   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);
} /* ecore_x_netwm_window_type_set */

/* 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(__FILE__, __LINE__, __FUNCTION__);
   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;
} /* ecore_x_netwm_window_type_get */

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(__FILE__, __LINE__, __FUNCTION__);
   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)
      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;
} /* ecore_x_netwm_window_types_get */

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;
     } /* switch */
} /* _ecore_x_netwm_action_atom_get */

/* 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(__FILE__, __LINE__, __FUNCTION__);
   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;
} /* ecore_x_netwm_allowed_action_isset */

/* 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(__FILE__, __LINE__, __FUNCTION__);
   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);
} /* ecore_x_netwm_allowed_action_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(__FILE__, __LINE__, __FUNCTION__);
   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;
} /* ecore_x_netwm_allowed_action_get */

EAPI void
ecore_x_netwm_opacity_set(Ecore_X_Window win, unsigned int opacity)
{
   LOGFN(__FILE__, __LINE__, __FUNCTION__);
   ecore_x_window_prop_card32_set(win, ECORE_X_ATOM_NET_WM_WINDOW_OPACITY,
                                  &opacity, 1);
} /* ecore_x_netwm_opacity_set */

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

   LOGFN(__FILE__, __LINE__, __FUNCTION__);
   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;
} /* ecore_x_netwm_opacity_get */

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(__FILE__, __LINE__, __FUNCTION__);
   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);
} /* ecore_x_netwm_frame_size_set */

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(__FILE__, __LINE__, __FUNCTION__);
   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;
} /* ecore_x_netwm_frame_size_get */

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

   LOGFN(__FILE__, __LINE__, __FUNCTION__);
   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;
} /* ecore_x_netwm_sync_counter_get */

EAPI void
ecore_x_netwm_ping_send(Ecore_X_Window win)
{
   XEvent xev;

   if (!win)
      return;

   LOGFN(__FILE__, __LINE__, __FUNCTION__);
   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);
} /* ecore_x_netwm_ping_send */

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

   if (!win)
      return;

   LOGFN(__FILE__, __LINE__, __FUNCTION__);
   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);
} /* ecore_x_netwm_sync_request_send */

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;

   if (!win)
      return;

   LOGFN(__FILE__, __LINE__, __FUNCTION__);
   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);
} /* ecore_x_netwm_state_request_send */

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

   if (!win)
      return;

   LOGFN(__FILE__, __LINE__, __FUNCTION__);
   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);
} /* ecore_x_netwm_desktop_request_send */

int
_ecore_x_netwm_startup_info_begin(Ecore_X_Window win __UNUSED__,
                                  char *data         __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;
} /* _ecore_x_netwm_startup_info_begin */

int
_ecore_x_netwm_startup_info(Ecore_X_Window win __UNUSED__,
                            char *data         __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;
} /* _ecore_x_netwm_startup_info */

/*
 * 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));
} /* _ecore_x_window_prop_string_utf8_set */

/*
 * 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 (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;
} /* _ecore_x_window_prop_string_utf8_get */

#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;
} /* _ecore_x_netwm_startup_info_process */

/*
 * 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;
} /* _ecore_x_netwm_startup_info_parse */

#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);
} /* _ecore_x_netwm_startup_info_free */

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

   LOGFN(__FILE__, __LINE__, __FUNCTION__);
   snprintf(buf, sizeof(buf), "_NET_WM_CM_S%i", screen);
   if (atom == None)
      atom = XInternAtom(_ecore_x_disp, buf, False);

   if (atom == None)
      return EINA_FALSE;

   win = XGetSelectionOwner(_ecore_x_disp, atom);

   return (win != None) ? EINA_TRUE : EINA_FALSE;
} /* ecore_x_screen_is_composited */

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

   LOGFN(__FILE__, __LINE__, __FUNCTION__);
   snprintf(buf, sizeof(buf), "_NET_WM_CM_S%i", screen);
   if (atom == None)
      atom = XInternAtom(_ecore_x_disp, buf, False);

   if (atom == None)
      return;

   XSetSelectionOwner(_ecore_x_disp, atom, win, _ecore_x_event_last_time);
} /* ecore_x_screen_is_composited_set */

