/*
 * Copyright (C) 2000 Sasha Vasko <sasha at aftercode.net>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 */

#define LOCAL_DEBUG
#include "../configure.h"
#include "asapp.h"
#include "screen.h"
#include "clientprops.h"
#include "hints.h"

/************************************************************************/
/*		New hints implementation :				*/
/************************************************************************/

ASHashTable  *hint_handlers = NULL;
Bool          default_parent_hints_func (Window parent, ASParentHints * dst);
static get_parent_hints_func parent_hints_func = default_parent_hints_func;

Bool          as_xrm_initialized = False;
XrmDatabase   as_xrm_user_db = None;

/*
 * these atoms are constants in X11, but we still need pointers to them -
 * simply defining our own variables to hold those constants :
 */
Atom          _XA_WM_NAME = XA_WM_NAME;
Atom          _XA_WM_ICON_NAME = XA_WM_ICON_NAME;
Atom          _XA_WM_CLASS = XA_WM_CLASS;
Atom          _XA_WM_HINTS = XA_WM_HINTS;
Atom          _XA_WM_NORMAL_HINTS = XA_WM_NORMAL_HINTS;
Atom          _XA_WM_TRANSIENT_FOR = XA_WM_TRANSIENT_FOR;
Atom          _XA_WM_COMMAND = XA_WM_COMMAND;
Atom          _XA_WM_CLIENT_MACHINE = XA_WM_CLIENT_MACHINE;

/*
 * rest of the atoms has to be interned by us :
 */
Atom          _XA_WM_PROTOCOLS;
Atom          _XA_WM_TAKE_FOCUS;
Atom          _XA_WM_DELETE_WINDOW;
Atom          _XA_WM_COLORMAP_WINDOWS;
Atom          _XA_WM_STATE;

/* Motif hints */
Atom          _XA_MwmAtom;

/* Gnome hints */
Atom          _XA_WIN_LAYER;
Atom          _XA_WIN_STATE;
Atom          _XA_WIN_WORKSPACE;
Atom          _XA_WIN_HINTS;

/* wm-spec _NET hints : */
Atom          _XA_NET_WM_NAME;
Atom          _XA_NET_WM_ICON_NAME;

Atom          _XA_NET_WM_VISIBLE_NAME;
Atom          _XA_NET_WM_VISIBLE_ICON_NAME;

Atom          _XA_NET_WM_DESKTOP;
Atom          _XA_NET_WM_WINDOW_TYPE;
Atom          _XA_NET_WM_WINDOW_TYPE_DESKTOP;
Atom          _XA_NET_WM_WINDOW_TYPE_DOCK;
Atom          _XA_NET_WM_WINDOW_TYPE_TOOLBAR;
Atom          _XA_NET_WM_WINDOW_TYPE_MENU;
Atom          _XA_NET_WM_WINDOW_TYPE_DIALOG;
Atom          _XA_NET_WM_WINDOW_TYPE_NORMAL;
Atom          _XA_NET_WM_WINDOW_TYPE_UTILITY;
Atom          _XA_NET_WM_WINDOW_TYPE_SPLASH;
Atom          _XA_AS_WM_WINDOW_TYPE_MODULE;

Atom          _XA_NET_WM_STATE;
Atom          _XA_NET_WM_STATE_MODAL;
Atom          _XA_NET_WM_STATE_STICKY;
Atom          _XA_NET_WM_STATE_MAXIMIZED_VERT;
Atom          _XA_NET_WM_STATE_MAXIMIZED_HORZ;
Atom          _XA_NET_WM_STATE_SHADED;
Atom          _XA_NET_WM_STATE_SKIP_TASKBAR;
Atom          _XA_NET_WM_STATE_SKIP_PAGER;
Atom          _XA_NET_WM_STATE_HIDDEN;
Atom          _XA_NET_WM_STATE_FULLSCREEN;
Atom          _XA_NET_WM_STATE_ABOVE;
Atom          _XA_NET_WM_STATE_BELOW;

Atom          _XA_NET_WM_PID;
Atom          _XA_NET_WM_PING;


/* Crossreferences of atoms into flag value for
   different atom list type of properties :*/

AtomXref      MainHints[] = {
	{"WM_PROTOCOLS", &_XA_WM_PROTOCOLS},
	{"WM_COLORMAP_WINDOWS", &_XA_WM_COLORMAP_WINDOWS},
	{"WM_STATE", &_XA_WM_STATE},
	{"_MOTIF_WM_HINTS", &_XA_MwmAtom},
	{"_WIN_LAYER", &_XA_WIN_LAYER},
	{"_WIN_STATE", &_XA_WIN_STATE},
	{"_WIN_WORKSPACE", &_XA_WIN_WORKSPACE},
	{"_WIN_HINTS", &_XA_WIN_HINTS},
	{"_NET_WM_NAME", &_XA_NET_WM_NAME},
	{"_NET_WM_ICON_NAME", &_XA_NET_WM_ICON_NAME},
	{"_NET_WM_VISIBLE_NAME", &_XA_NET_WM_VISIBLE_NAME},
	{"_NET_WM_VISIBLE_ICON_NAME", &_XA_NET_WM_VISIBLE_ICON_NAME},
	{"_NET_WM_DESKTOP", &_XA_NET_WM_DESKTOP},
	{"_NET_WM_WINDOW_TYPE", &_XA_NET_WM_WINDOW_TYPE},
	{"_NET_WM_STATE", &_XA_NET_WM_STATE},
	{"_NET_WM_PID", &_XA_NET_WM_PID},
	{NULL, NULL, 0, None}
};

AtomXref      WM_Protocols[] = {
	{"WM_TAKE_FOCUS", &_XA_WM_TAKE_FOCUS, AS_DoesWmTakeFocus},
	{"WM_DELETE_WINDOW", &_XA_WM_DELETE_WINDOW, AS_DoesWmDeleteWindow},
	{NULL, NULL, 0, None}
};

AtomXref      EXTWM_WindowType[] = {
	{"_NET_WM_WINDOW_TYPE_DESKTOP", &_XA_NET_WM_WINDOW_TYPE_DESKTOP, EXTWM_TypeDesktop},
	{"_NET_WM_WINDOW_TYPE_DOCK", &_XA_NET_WM_WINDOW_TYPE_DOCK, EXTWM_TypeDock},
	{"_NET_WM_WINDOW_TYPE_TOOLBAR", &_XA_NET_WM_WINDOW_TYPE_TOOLBAR, EXTWM_TypeToolbar},
	{"_NET_WM_WINDOW_TYPE_MENU", &_XA_NET_WM_WINDOW_TYPE_MENU, EXTWM_TypeMenu},
	{"_NET_WM_WINDOW_TYPE_DIALOG", &_XA_NET_WM_WINDOW_TYPE_DIALOG, EXTWM_TypeDialog},
	{"_NET_WM_WINDOW_TYPE_NORMAL", &_XA_NET_WM_WINDOW_TYPE_NORMAL, EXTWM_TypeNormal},
	{"_NET_WM_WINDOW_TYPE_UTILITY", &_XA_NET_WM_WINDOW_TYPE_UTILITY, EXTWM_TypeUtility},
	{"_NET_WM_WINDOW_TYPE_SPLASH", &_XA_NET_WM_WINDOW_TYPE_SPLASH, EXTWM_TypeSplash},
	{"_AS_WM_WINDOW_TYPE_MODULE", &_XA_AS_WM_WINDOW_TYPE_MODULE, EXTWM_TypeASModule},
	
	{NULL, NULL, 0, None}
};

AtomXref      _EXTWM_State[] = {
	{"_NET_WM_STATE_MODAL", &_XA_NET_WM_STATE_MODAL, EXTWM_StateModal},
	{"_NET_WM_STATE_STICKY", &_XA_NET_WM_STATE_STICKY, EXTWM_StateSticky},
	{"_NET_WM_STATE_MAXIMIZED_VERT", &_XA_NET_WM_STATE_MAXIMIZED_VERT, EXTWM_StateMaximizedV},
	{"_NET_WM_STATE_MAXIMIZED_HORZ", &_XA_NET_WM_STATE_MAXIMIZED_HORZ, EXTWM_StateMaximizedH},
	{"_NET_WM_STATE_SHADED", &_XA_NET_WM_STATE_SHADED, EXTWM_StateShaded},
    {"_NET_WM_STATE_SKIP_TASKBAR", &_XA_NET_WM_STATE_SKIP_TASKBAR, EXTWM_StateSkipTaskbar},
	{"_NET_WM_STATE_SKIP_PAGER", &_XA_NET_WM_STATE_SKIP_TASKBAR, EXTWM_StateSkipPager},
	{"_NET_WM_STATE_HIDDEN", &_XA_NET_WM_STATE_HIDDEN,			EXTWM_StateHidden    },
	{"_NET_WM_STATE_FULLSCREEN", &_XA_NET_WM_STATE_FULLSCREEN, 	EXTWM_StateFullscreen},
	{"_NET_WM_STATE_ABOVE", &_XA_NET_WM_STATE_ABOVE, 			EXTWM_StateAbove	 },
	{"_NET_WM_STATE_BELOW", &_XA_NET_WM_STATE_BELOW, 			EXTWM_StateBelow	 },
	{NULL, NULL, 0, None}
};

AtomXref      *EXTWM_State = &(_EXTWM_State[0]);

AtomXref      EXTWM_Protocols[] = {
	{"_NET_WM_PING", &_XA_NET_WM_PING, EXTWM_DoesWMPing},
	{NULL, NULL, 0, None}
};

/*********************** Utility functions ******************************/
/* X resources access :*/
void
init_xrm ()
{
	if (!as_xrm_initialized)
	{
		XrmInitialize ();
		as_xrm_initialized = True;
	}
}

Bool
read_int_resource (XrmDatabase db, const char *res_name, const char *res_class, int *value)
{
	char         *str_type;
	XrmValue      rm_value;

	if (XrmGetResource (db, res_name, res_class, &str_type, &rm_value) != 0)
		if (rm_value.size > 0)
		{
			register char *ptr = rm_value.addr;
			int           val;

			if (*ptr == 'w')
				ptr++;
			val = atoi (ptr);
			while (*ptr)
				if (!isdigit ((int)*ptr++))
					break;
			if (*ptr == '\0')
			{
				*value = val;
				return True;
			}
		}
	return False;
}

void
load_user_database ()
{
	if (!as_xrm_initialized)
		init_xrm ();

	destroy_user_database ();				   /* just in case */

	if (XResourceManagerString (dpy) == NULL)
	{
		char         *xdefaults_file = put_file_home ("~/.Xdefaults");

		if (!CheckFile (xdefaults_file))
		{
			char         *xenv = getenv ("XENVIRONMENT");

			show_warning ("Can't locate X resources database in \"%s\".", xdefaults_file);
			free (xdefaults_file);
			if (xenv == NULL)
				return;
			xdefaults_file = put_file_home (xenv);
			if (!CheckFile (xdefaults_file))
			{
				show_warning ("Can't locate X resources database in \"%s\".", xdefaults_file);
				free (xdefaults_file);
				return;
			}
		}
		as_xrm_user_db = XrmGetFileDatabase (xdefaults_file);
		free (xdefaults_file);
	}
}

void
destroy_user_database ()
{
	if (as_xrm_initialized && as_xrm_user_db != NULL)
	{
		XrmDestroyDatabase (as_xrm_user_db);
		as_xrm_user_db = NULL;
	}
}

/******************** Hints reading  functions **************************/

void
read_wm_name (ASRawHints * hints, Window w)
{
	if (hints)
	{
		read_text_property (w, XA_WM_NAME, &(hints->wm_name));
	}
}

void
read_wm_icon_name (ASRawHints * hints, Window w)
{
	if (hints)
	{
		read_text_property (w, XA_WM_ICON_NAME, &(hints->wm_icon_name));
	}
}

void
read_wm_class (ASRawHints * hints, Window w)
{
	if (hints && w != None)
	{
		if (hints->wm_class)
		{
			if (hints->wm_class->res_name)
				XFree (hints->wm_class->res_name);
			if (hints->wm_class->res_class)
				XFree (hints->wm_class->res_class);
		} else
			hints->wm_class = XAllocClassHint ();

		if (XGetClassHint (dpy, w, hints->wm_class) == 0)
		{
			XFree (hints->wm_class);
			hints->wm_class = NULL;
		}
	}
}

void
read_wm_hints (ASRawHints * hints, Window w)
{
	if (hints && w != None)
	{
		if (hints->wm_hints)
			XFree (hints->wm_hints);

		if ((hints->wm_hints = XGetWMHints (dpy, w)) != NULL)
			if (get_flags (hints->wm_hints->flags, WindowGroupHint) && hints->wm_hints->window_group != w)
			{
				ASParentHints parent_hints;

				if (parent_hints_func (hints->wm_hints->window_group, &parent_hints))
				{
					if (hints->group_leader == NULL)
						hints->group_leader = (ASParentHints *) safecalloc (1, sizeof (ASParentHints));
					*(hints->group_leader) = parent_hints;
				}
			}
	}
}

void
read_wm_normal_hints (ASRawHints * hints, Window w)
{
	if (hints && w != None)
	{
		long          supplied;

		if (hints->wm_normal_hints == NULL)
			hints->wm_normal_hints = XAllocSizeHints ();

		if (XGetWMNormalHints (dpy, w, hints->wm_normal_hints, &supplied) == 0)
		{
			XFree (hints->wm_normal_hints);
			hints->wm_normal_hints = NULL;
		}
	}
}

void
read_wm_transient_for (ASRawHints * hints, Window w)
{
	if (hints && w != None)
	{
		Window        transient_for;
		ASParentHints parent_hints;

		if (XGetTransientForHint (dpy, w, &transient_for) != 0)
			if (transient_for != w && parent_hints_func (transient_for, &parent_hints))
			{
				if (hints->transient_for == NULL)
					hints->transient_for = (ASParentHints *) safecalloc (1, sizeof (ASParentHints));
				*(hints->transient_for) = parent_hints;
			}
	}
}

void
read_wm_protocols (ASRawHints * hints, Window w)
{
    LOCAL_DEBUG_CALLER_OUT( "window(%lX)", w );
	if (hints && w != None)
	{
		CARD32        *protocols = NULL;
		long          nprotos = 0;

		hints->wm_protocols = 0;
		if (read_32bit_proplist (w, _XA_WM_PROTOCOLS, 3, &protocols, &nprotos))
		{
            LOCAL_DEBUG_OUT( "nprotos=%ld, protocols[0] = 0x%lX(\"%s\")", nprotos, protocols[0], XGetAtomName(dpy, protocols[0]));
			translate_atom_list (&(hints->wm_protocols), WM_Protocols, protocols, nprotos);
            LOCAL_DEBUG_OUT( "translated protocols =0x%lX", hints->wm_protocols );
			translate_atom_list (&(hints->extwm_hints.flags), EXTWM_Protocols, protocols, nprotos);
            LOCAL_DEBUG_OUT( "translated NET_ protocols =0x%lX", hints->extwm_hints.flags );
			free (protocols);
		}
	}
}

void
read_wm_cmap_windows (ASRawHints * hints, Window w)
{
	if (hints && w != None)
	{
		if (hints->wm_cmap_windows)
			free (hints->wm_cmap_windows);
		if (!read_32bit_proplist
			(w, _XA_WM_COLORMAP_WINDOWS, 10, &(hints->wm_cmap_windows), &(hints->wm_cmap_win_count)))
			hints->wm_cmap_win_count = 0;
	}
}

void
read_wm_client_machine (ASRawHints * hints, Window w)
{
	if (hints)
	{
		read_text_property (w, _XA_WM_CLIENT_MACHINE, &(hints->wm_client_machine));
	}
}

void
read_wm_command (ASRawHints * hints, Window w)
{
	if (hints)
	{										   /* there is some wierd bullshit about this propertie being depreciated and stuff
											    * so we just use standard X function to read it up : */
		if (hints->wm_cmd_argv)
			XFreeStringList (hints->wm_cmd_argv);
		LOCAL_DEBUG_OUT( "Reading WM_COMMAND property from window %lX", w );
		if (XGetCommand (dpy, w, &(hints->wm_cmd_argv), &(hints->wm_cmd_argc)) == 0)
		{
			LOCAL_DEBUG_OUT( "XGetCommand returned 0%s", "" );
			hints->wm_cmd_argv = NULL;
			hints->wm_cmd_argc = 0;
		}
	}
}

void
read_wm_state (ASRawHints * hints, Window w)
{
	if (hints)
	{
        CARD32       *data = NULL;
		long          nitems = 0;

		if (read_32bit_proplist (w, _XA_WM_STATE, 2, &data, &nitems))
		{
			if (nitems > 0)
			{
				hints->wm_state = data[0];
				if (nitems > 1)
					hints->wm_state_icon_win = data[1];
			}
		}
        if( data )
            free( data );
	}
}

void
read_motif_hints (ASRawHints * hints, Window w)
{
	if (hints && w != None)
	{
        CARD32 *raw_data = NULL ;
        long          nitems = 0 ;

		if (!read_32bit_proplist (w, _XA_MwmAtom, 4, &raw_data, &nitems))
			nitems = 0;

        if (hints->motif_hints)
		{
			free (hints->motif_hints);
			hints->motif_hints = NULL;
        }
        if (nitems >= 4)
		{
			hints->motif_hints = (MwmHints *) raw_data;
            raw_data = NULL ;
        }
        if( raw_data )
            free( raw_data );
	}
}

void
read_gnome_layer (ASRawHints * hints, Window w)
{
	if (hints && w != None)
	{
		if (read_32bit_property (w, _XA_WIN_LAYER, &(hints->gnome_hints.layer)))
			set_flags (hints->gnome_hints.flags, GNOME_LAYER);
	}
}

void
read_gnome_state (ASRawHints * hints, Window w)
{
	if (hints && w != None)
	{
		if (read_32bit_property (w, _XA_WIN_STATE, &(hints->gnome_hints.state)))
			set_flags (hints->gnome_hints.flags, GNOME_STATE);
	}
}

void
read_gnome_workspace (ASRawHints * hints, Window w)
{
	if (hints && w != None)
	{
		if (read_32bit_property (w, _XA_WIN_WORKSPACE, &(hints->gnome_hints.workspace)))
			set_flags (hints->gnome_hints.flags, GNOME_WORKSPACE);
	}
}

void
read_gnome_hints (ASRawHints * hints, Window w)
{
	if (hints && w != None)
	{
		if (read_32bit_property (w, _XA_WIN_HINTS, &(hints->gnome_hints.hints)))
			set_flags (hints->gnome_hints.flags, GNOME_HINTS);
	}
}

void
read_extwm_name (ASRawHints * hints, Window w)
{
	if (hints && w != None)
	{
		if (read_text_property (w, _XA_NET_WM_NAME, &(hints->extwm_hints.name)))
			set_flags (hints->extwm_hints.flags, EXTWM_NAME);
	}
}

void
read_extwm_icon_name (ASRawHints * hints, Window w)
{
	if (hints && w != None)
	{
		if (read_text_property (w, _XA_NET_WM_ICON_NAME, &(hints->extwm_hints.icon_name)))
			set_flags (hints->extwm_hints.flags, EXTWM_ICON_NAME);
	}
}

void
read_extwm_visible_name (ASRawHints * hints, Window w)
{
	if (hints && w != None)
	{
		if (read_text_property (w, _XA_NET_WM_VISIBLE_NAME, &(hints->extwm_hints.visible_name)))
			set_flags (hints->extwm_hints.flags, EXTWM_VISIBLE_NAME);
	}
}

void
read_extwm_visible_icon_name (ASRawHints * hints, Window w)
{
	if (hints && w != None)
	{
		if (read_text_property (w, _XA_NET_WM_VISIBLE_ICON_NAME, &(hints->extwm_hints.visible_icon_name)))
			set_flags (hints->extwm_hints.flags, EXTWM_VISIBLE_ICON_NAME);
	}
}

void
read_extwm_desktop (ASRawHints * hints, Window w)
{
	if (hints && w != None)
	{
		if (read_32bit_property (w, _XA_NET_WM_DESKTOP, &(hints->extwm_hints.desktop)))
			set_flags (hints->extwm_hints.flags, EXTWM_DESKTOP);
	}
}

void
read_extwm_window_type (ASRawHints * hints, Window w)
{
	if (hints && w != None)
	{
		CARD32         *protocols;
		long          nprotos = 0;

		if (read_32bit_proplist (w, _XA_NET_WM_WINDOW_TYPE, 6, &protocols, &nprotos))
		{
			translate_atom_list (&(hints->extwm_hints.flags), EXTWM_WindowType, protocols, nprotos);
			free (protocols);
		}
	}
}

void
read_extwm_state (ASRawHints * hints, Window w)
{
	if (hints && w != None)
	{
		CARD32         *protocols;
		long          nprotos = 0;

        if (read_32bit_proplist (w, _XA_NET_WM_STATE, 6, &protocols, &nprotos))
		{
			translate_atom_list (&(hints->extwm_hints.flags), EXTWM_State, protocols, nprotos);
			free (protocols);
		}
	}
}


void
read_extwm_pid (ASRawHints * hints, Window w)
{
	if (hints && w != None)
	{
		if (read_32bit_property (w, _XA_NET_WM_PID, &(hints->extwm_hints.pid)))
			set_flags (hints->extwm_hints.flags, EXTWM_PID);
	}
}

Bool
default_parent_hints_func (Window parent, ASParentHints * dst)
{
	Window        root;
	int           dummy;
	unsigned int  udummy;

	if (parent == None && dst == NULL)
		return False;
	if (!XGetGeometry (dpy, parent, &root, &dummy, &dummy, &udummy, &udummy, &udummy, &udummy))
		return False;
	if (parent != ASDefaultRoot)
	{
		CARD32        desktop;

		dst->parent = parent;
		dst->flags = 0;
		if (read_32bit_property (parent, _XA_NET_WM_DESKTOP, &desktop))
		{
			set_flags (dst->flags, AS_StartDesktop);
			dst->desktop = desktop;
		}
		/* can't really find out about viewport  here - write custom
		 * function for it, and override this deafult
		 */
	}
	return True;
}

get_parent_hints_func
set_parent_hints_func (get_parent_hints_func new_func)
{
	get_parent_hints_func old_func = parent_hints_func;

	if (new_func != NULL)
		parent_hints_func = new_func;
	return old_func;
}

/******************** Hints management functions **************************/

void
intern_hint_atoms ()
{
	intern_atom_list (MainHints);
	intern_atom_list (WM_Protocols);
	intern_atom_list (EXTWM_WindowType);
	intern_atom_list (EXTWM_State);
	intern_atom_list (EXTWM_Protocols);
}

struct hint_description_struct
{
	Atom         *id_variable;
	void          (*read_func) (ASRawHints *, Window);
	ASFlagType    hint_class;				   /* see HINT_ flags in hints.h */
	HintsTypes    hint_type;
}
HintsDescriptions[] =
{
	{&_XA_WM_NAME, read_wm_name, HINT_NAME, HINTS_ICCCM},
	{&_XA_WM_ICON_NAME, read_wm_icon_name, HINT_NAME, HINTS_ICCCM},
	{&_XA_WM_CLASS, read_wm_class, HINT_NAME, HINTS_ICCCM},
	{&_XA_WM_HINTS, read_wm_hints, HINT_STARTUP | HINT_GENERAL, HINTS_ICCCM},
	{&_XA_WM_NORMAL_HINTS, read_wm_normal_hints, HINT_STARTUP | HINT_GENERAL, HINTS_ICCCM},
	{&_XA_WM_TRANSIENT_FOR, read_wm_transient_for, HINT_STARTUP | HINT_GENERAL, HINTS_Transient},
	{&_XA_WM_PROTOCOLS, read_wm_protocols, HINT_PROTOCOL, HINTS_ICCCM|HINTS_ExtendedWM},
	{&_XA_WM_COLORMAP_WINDOWS, read_wm_cmap_windows, HINT_COLORMAP, HINTS_ICCCM},
	{&_XA_WM_CLIENT_MACHINE, read_wm_client_machine, HINT_STARTUP, HINTS_ICCCM},
	{&_XA_WM_COMMAND, read_wm_command, HINT_STARTUP, HINTS_ICCCM},
	{&_XA_WM_STATE, read_wm_state, HINT_STARTUP, HINTS_ICCCM},
		/* Motif hints */
	{&_XA_MwmAtom, read_motif_hints, HINT_GENERAL | HINT_STARTUP | HINT_PROTOCOL, HINTS_Motif},
		/* Gnome hints */
	{&_XA_WIN_LAYER, read_gnome_layer, HINT_STARTUP, HINTS_Gnome},
	{&_XA_WIN_STATE, read_gnome_state, HINT_STARTUP, HINTS_Gnome},
	{&_XA_WIN_WORKSPACE, read_gnome_workspace, HINT_STARTUP, HINTS_Gnome},
	{&_XA_WIN_HINTS, read_gnome_hints, HINT_GENERAL, HINTS_Gnome},
		/* wm-spec _NET hints : */
	{&_XA_NET_WM_NAME, read_extwm_name, HINT_NAME, HINTS_ExtendedWM},
	{&_XA_NET_WM_ICON_NAME, read_extwm_icon_name, HINT_NAME, HINTS_ExtendedWM},
	{&_XA_NET_WM_VISIBLE_NAME, read_extwm_visible_name, HINT_NAME, HINTS_ExtendedWM},
	{&_XA_NET_WM_VISIBLE_ICON_NAME, read_extwm_visible_icon_name, HINT_NAME, HINTS_ExtendedWM},
	{&_XA_NET_WM_DESKTOP, read_extwm_desktop, HINT_STARTUP | HINT_PROTOCOL, HINTS_ExtendedWM},
	{&_XA_NET_WM_WINDOW_TYPE, read_extwm_window_type, HINT_STARTUP | HINT_GENERAL, HINTS_ExtendedWM},
	{&_XA_NET_WM_STATE, read_extwm_state, HINT_STARTUP | HINT_GENERAL, HINTS_ExtendedWM},
	{&_XA_NET_WM_PID, read_extwm_pid, HINT_STARTUP, HINTS_ExtendedWM},
	{
	NULL, NULL, 0, HINTS_Supported}
};

void
init_hint_handlers ()
{
	register int  i;

	if (hint_handlers != NULL)
		destroy_ashash (&hint_handlers);

	hint_handlers = create_ashash (7, NULL, NULL, NULL);

	for (i = 0; HintsDescriptions[i].id_variable != NULL; i++)
	{
		add_hash_item (hint_handlers, (ASHashableValue) * (HintsDescriptions[i].id_variable), &(HintsDescriptions[i]));
	}
}

void
clientprops_cleanup ()
{
	if (hint_handlers != NULL)
		destroy_ashash (&hint_handlers);
}

ASRawHints   *
collect_hints (ScreenInfo * scr, Window w, ASFlagType what, ASRawHints * reusable_memory)
{
	ASRawHints   *hints = NULL;

	if (w != None && what != 0)
	{
		Atom         *all_props = NULL;
		int           props_num = 0;
		struct hint_description_struct *descr;
		Window        root;
		int           x = 0, y = 0;
		unsigned int  width = 2, height = 2, bw = 0, depth = 1;

		if (get_flags (what, HINT_STARTUP))
		{
			Status        res = XGetGeometry (dpy, w, &root, &x, &y, &width, &height, &bw, &depth);

			if (res == 0)
				return NULL;
		}

		if( (all_props = XListProperties (dpy, w, &props_num))== NULL )
			return NULL ;

		if (reusable_memory == NULL)
			hints = (ASRawHints *) safecalloc (1, sizeof (ASRawHints));
		else
		{
			hints = reusable_memory;
			memset (hints, 0x00, sizeof (ASRawHints));
		}

		hints->scr = scr ? scr : ASDefaultScr;

		if (get_flags (what, HINT_STARTUP))
		{									   /* we also need to get window position, size and border width */
			hints->placement.x = x;
			hints->placement.y = y;
			hints->placement.width = width;
			hints->placement.height = height;
			hints->border_width = bw;
			hints->wm_state = WithdrawnState;
		}

		if (hint_handlers == NULL)
			init_hint_handlers ();

		if (all_props)
		{
			while (props_num-- > 0)
			{
				ASHashData hdata = {0} ;
				if (get_hash_item (hint_handlers, AS_HASHABLE(all_props[props_num]), &hdata.vptr) == ASH_Success)
				{
					if ((descr = hdata.vptr) != NULL)
						if (get_flags (descr->hint_class, what) && descr->read_func != NULL)
						{
							descr->read_func (hints, w);
							set_flags (hints->hints_types, (0x01 << descr->hint_type));
						}
				}
			}
			XFree (all_props);
		}
		if( hints->group_leader != NULL ) 
			set_flags (hints->hints_types, (0x01 << HINTS_GroupLead));
	}
	return hints;
}

void
destroy_raw_hints (ASRawHints * hints, Bool reusable)
{
	if (hints)
	{
		if (hints->wm_name)
			free_text_property (&(hints->wm_name));
		if (hints->wm_icon_name)
			free_text_property (&(hints->wm_icon_name));
		if (hints->wm_class)
		{
			if (hints->wm_class->res_name)
				XFree (hints->wm_class->res_name);
			if (hints->wm_class->res_class)
				XFree (hints->wm_class->res_class);
			XFree (hints->wm_class);
		}
		if (hints->wm_hints)
			XFree (hints->wm_hints);
		if (hints->group_leader)
			free (hints->group_leader);
		if (hints->wm_normal_hints)
			XFree (hints->wm_normal_hints);

		if (hints->transient_for)
			free (hints->transient_for);

		if (hints->wm_cmap_windows)
			free (hints->wm_cmap_windows);

		if (hints->wm_client_machine)
			free_text_property (&(hints->wm_client_machine));
		if (hints->wm_cmd_argv)
			XFreeStringList (hints->wm_cmd_argv);

		/* Motif Hints : */
		if (hints->motif_hints)
			free (hints->motif_hints);

		/* Gnome Hints : */
		/* nothing to free here */

		/* WM-specs (new Gnome) Hints : */
		if (hints->extwm_hints.name)
			free_text_property (&(hints->extwm_hints.name));
		if (hints->extwm_hints.icon_name)
			free_text_property (&(hints->extwm_hints.icon_name));

		if (reusable)						   /* we are being paranoid */
			memset (hints, 0x00, sizeof (ASRawHints));
		else
			free (hints);
	}
}

/***********************************************************************************
 * Hints printing functions :
 ***********************************************************************************/
void
print_wm_hints (stream_func func, void *stream, XWMHints * hints)
{
	if (!pre_print_check (&func, &stream, hints, "No ICCCM.WM_HINTS available(NULL)."))
		return;

	func (stream, "ICCCM.WM_HINTS.flags=0x%lX;\n", hints->flags);
	if (get_flags (hints->flags, InputHint))
		func (stream, "ICCCM.WM_HINTS.input = %s;\n", hints->input ? "True" : "False");
	if (get_flags (hints->flags, StateHint))
		func (stream, "ICCCM.WM_HINTS.initial_state = %s;\n",
			  (hints->initial_state ==
			   NormalState) ? "Normal" : ((hints->initial_state == IconicState) ? "Iconic" : "Withdrawn"));
	if (get_flags (hints->flags, IconPixmapHint))
		func (stream, "ICCCM.WM_HINTS.icon_pixmap = 0x%lX;\n", hints->icon_pixmap);
	if (get_flags (hints->flags, IconWindowHint))
		func (stream, "ICCCM.WM_HINTS.icon_window = 0x%lX;\n", hints->icon_window);
	if (get_flags (hints->flags, IconMaskHint))
		func (stream, "ICCCM.WM_HINTS.icon_mask = 0x%lX;\n", hints->icon_mask);
	if (get_flags (hints->flags, IconPositionHint))
		func (stream, "ICCCM.WM_HINTS.icon_x = %d;\nICCCM.WM_HINTS.icon_y = %d;\n", hints->icon_x, hints->icon_y);
	if (get_flags (hints->flags, WindowGroupHint))
		func (stream, "ICCCM.WM_HINTS.window_group = 0x%lX;\n", hints->window_group);
}

void
print_parent_hints (stream_func func, void *stream, ASParentHints * hints, const char *prompt)
{
	if (!pre_print_check (&func, &stream, hints, prompt))
		return;

	if (hints->parent != ASDefaultRoot)
		func (stream, "%s.PARENT.parent = 0x%lX;\n", prompt, hints->parent);
	else
		func (stream, "%s.PARENT.parent = root;\n", prompt);

	func (stream, "%s.PARENT.flags=0x%lX;\n", prompt, hints->flags);
	if (get_flags (hints->flags, AS_StartDesktop))
		func (stream, "%s.PARENT.desktop = %d;\n", prompt, hints->desktop);
	if (get_flags (hints->flags, AS_StartViewportX))
		func (stream, "%s.PARENT.viewport_x = %d;\n", prompt, hints->viewport_x);
	if (get_flags (hints->flags, AS_StartViewportY))
		func (stream, "%s.PARENT.viewport_y = %d;\n", prompt, hints->viewport_y);
}

void
print_wm_normal_hints (stream_func func, void *stream, XSizeHints * hints)
{
	if (!pre_print_check (&func, &stream, hints, "No ICCCM.WM_NORMAL_HINTS available(NULL)."))
		return;

	func (stream, "ICCCM.WM_NORMAL_HINTS.flags = 0x%lX;\n", hints->flags);

	if (get_flags (hints->flags, PMinSize))
		func (stream, "ICCCM.WM_NORMAL_HINTS.min_width = %d;\nICCCM.WM_NORMAL_HINTS.min_height = %d;\n",
			  hints->min_width, hints->min_height);
	if (get_flags (hints->flags, PMaxSize))
		func (stream, "ICCCM.WM_NORMAL_HINTS.max_width = %d;\nICCCM.WM_NORMAL_HINTS.max_height = %d;\n",
			  hints->max_width, hints->max_height);
	if (get_flags (hints->flags, PResizeInc))
		func (stream, "ICCCM.WM_NORMAL_HINTS.width_inc = %d;\nICCCM.WM_NORMAL_HINTS.height_inc = %d;\n",
			  hints->width_inc, hints->height_inc);
	if (get_flags (hints->flags, PAspect))
	{
		func (stream, "ICCCM.WM_NORMAL_HINTS.min_aspect.x = %d;\nICCCM.WM_NORMAL_HINTS.min_aspect.y = %d;\n",
			  hints->min_aspect.x, hints->min_aspect.y);
		func (stream, "ICCCM.WM_NORMAL_HINTS.max_aspect.x = %d;\nICCCM.WM_NORMAL_HINTS.max_aspect.y = %d;\n",
			  hints->max_aspect.x, hints->max_aspect.y);
	}
	if (get_flags (hints->flags, PBaseSize))
		func (stream, "ICCCM.WM_NORMAL_HINTS.base_width = %d;\nICCCM.WM_NORMAL_HINTS.base_height = %d;\n",
			  hints->base_width, hints->base_height);
	if (get_flags (hints->flags, PWinGravity))
		func (stream, "ICCCM.WM_NORMAL_HINTS.win_gravity = %d;\n", hints->win_gravity);
}

void
print_motif_hints (stream_func func, void *stream, MwmHints * hints)
{
	if (!pre_print_check (&func, &stream, hints, "No MOTIF_WM_HINTS available(NULL)."))
		return;

	func (stream, "Motif.MOTIF_WM_HINTS.flags = 0x%lX;\n", hints->flags);

	if (get_flags (hints->flags, MWM_HINTS_FUNCTIONS))
		func (stream, "Motif.MOTIF_WM_HINTS.functions = 0x%lX;\n", hints->functions);
	if (get_flags (hints->flags, MWM_HINTS_DECORATIONS))
		func (stream, "Motif.MOTIF_WM_HINTS.decorations = 0x%lX;\n", hints->decorations);
	if (get_flags (hints->flags, MWM_HINTS_INPUT_MODE))
		func (stream, "Motif.MOTIF_WM_HINTS.inputMode = 0x%lX;\n", hints->inputMode);
}

void
print_gnome_hints (stream_func func, void *stream, GnomeHints * hints)
{
	if (!pre_print_check (&func, &stream, hints, "No GNOME hints (_WIN_*) available(NULL)."))
		return;

	func (stream, "GNOME.flags = 0x%lX;\n", hints->flags);

	if (get_flags (hints->flags, GNOME_LAYER))
		func (stream, "GNOME._WIN_LAYER = %ld;\n", hints->layer);
	if (get_flags (hints->flags, GNOME_STATE))
		func (stream, "GNOME._WIN_STATE = 0x%lX;\n", hints->state);
	if (get_flags (hints->flags, GNOME_WORKSPACE))
		func (stream, "GNOME._WIN_WORKSPACE = %ld;\n", hints->workspace);
	if (get_flags (hints->flags, GNOME_HINTS))
		func (stream, "GNOME._WIN_HINTS = 0x%lX;\n", hints->hints);
}

void
print_extwm_hints (stream_func func, void *stream, ExtendedWMHints * hints)
{
	if (!pre_print_check (&func, &stream, hints, "No Extended WM hints (_NET_*) available(NULL)."))
		return;

	func (stream, "EXTWM.flags = 0x%lX;\n", hints->flags);

	if (hints->name && get_flags (hints->flags, EXTWM_NAME))
		print_text_property (func, stream, hints->name, "EXTWM._NET_WM_NAME");
	if (hints->name && get_flags (hints->flags, EXTWM_ICON_NAME))
		print_text_property (func, stream, hints->icon_name, "EXTWM._NET_WM_ICON_NAME");
	if (get_flags (hints->flags, EXTWM_DESKTOP))
		func (stream, "EXTWM._NET_WM_DESKTOP = %ld;\n", hints->desktop);
	if (get_flags (hints->flags, EXTWM_PID))
		func (stream, "EXTWM._NET_WM_PID = %ld;\n", hints->pid);

	print_list_hints (func, stream, hints->flags, EXTWM_WindowType, "EXTWM._NET_WM_WINDOW_TYPE");
	print_list_hints (func, stream, hints->flags, EXTWM_State, "EXTWM._NET_WM_STATE");
	print_list_hints (func, stream, hints->flags, EXTWM_Protocols, "EXTWM._NET_WM_PROTOCOLS");
}

void
print_hints (stream_func func, void *stream, ASRawHints * hints)
{
	register int  i;

	if (!pre_print_check (&func, &stream, hints, "No hints available(NULL)."))
		return;

	/* ICCCM hints : */
	if (hints->wm_name)
		print_text_property (func, stream, hints->wm_name, "ICCCM.WM_NAME");
	if (hints->wm_icon_name)
		print_text_property (func, stream, hints->wm_icon_name, "ICCCM.WM_ICON_NAME");
	if (hints->wm_class)
		func (stream, "ICCCM.WM_CLASS.res_name = \"%s\";\nICCCM.WM_CLASS.res_class = \"%s\";\n",
			  hints->wm_class->res_name, hints->wm_class->res_class);
	if (hints->wm_hints)
		print_wm_hints (func, stream, hints->wm_hints);
	if (hints->group_leader)
		print_parent_hints (func, stream, hints->group_leader, "ICCCM.WM_HINTS.WINDOW_GROUP");
	if (hints->wm_normal_hints)
		print_wm_normal_hints (func, stream, hints->wm_normal_hints);

	func (stream,
		  "ICCCM.placement.x = %d;\nICCCM.placement.y = %d;\nICCCM.placement.width = %d;\nICCCM.placement.height = %d;\n",
		  hints->placement.x, hints->placement.y, hints->placement.width, hints->placement.height);

	if (hints->transient_for)
		print_parent_hints (func, stream, hints->transient_for, "ICCCM.WM_HINTS.TRANSIENT_FOR");

	print_list_hints (func, stream, hints->wm_protocols, WM_Protocols, "ICCCM.WM_PROTOCOLS");

	if (hints->wm_client_machine)
		print_text_property (func, stream, hints->wm_client_machine, "ICCCM.WM_CLIENT_MACHINE");

	func (stream, "ICCCM.WM_COMMAND.argc = %d;\n", hints->wm_cmd_argc);
	if (hints->wm_cmd_argv)
		for (i = 0; i < hints->wm_cmd_argc; i++)
			func (stream, "ICCCM.WM_COMMAND.argv[%d] = \"%s\";\n", i,
				  hints->wm_cmd_argv[i] ? hints->wm_cmd_argv[i] : "NULL");

	if (hints->wm_cmap_windows)
		for (i = 0; i < hints->wm_cmap_win_count; i++)
			func (stream, "ICCCM.WM_COLORMAP_WINDOWS[i] = 0x%lX;\n", i, hints->wm_cmap_windows[i]);

	func (stream, "ICCCM.WM_STATE = %d;\n", hints->wm_state);
	func (stream, "ICCCM.WM_STATE.icon = 0x%lX;\n", hints->wm_state_icon_win);

	/* Motif Hints : */
	if (hints->motif_hints)
		print_motif_hints (func, stream, hints->motif_hints);

	/* Gnome Hints : */
	if (hints->gnome_hints.flags != 0)
		print_gnome_hints (func, stream, &(hints->gnome_hints));

	/* WM-specs (new Gnome) Hints : */
	if (hints->extwm_hints.flags != 0)
		print_extwm_hints (func, stream, &(hints->extwm_hints));
}

/**********************************************************************************/
/* This is so that client app could re-read property updated by WM : */

Bool
handle_client_property_update (Window w, Atom property, ASRawHints * raw)
{
	void          (*read_func) (ASRawHints * hints, Window w) = NULL;

	if (w && property && raw)
	{
		/* Here we are only interested in properties updtaed by the Window Manager : */
		if (property == _XA_WM_STATE)		   /* ICCCM : */
			read_func = read_wm_state;
		else if (property == _XA_WIN_LAYER)	   /* GNOME : */
			read_func = read_gnome_layer;
		else if (property == _XA_WIN_STATE)
			read_func = read_gnome_state;
		else if (property == _XA_WIN_WORKSPACE)
			read_func = read_gnome_workspace;
		else if (property == _XA_NET_WM_DESKTOP)	/* Extended WM-Hints : */
			read_func = read_extwm_desktop;
		else if (property == _XA_NET_WM_STATE)
			read_func = read_extwm_state;
		else
			return False;
		
		read_func (raw, w);
		
		return (validate_drawable( w, NULL, NULL )==w);
	}
	return False;
}

/* This is so that client app could re-read property updated by client : */
Bool
handle_manager_property_update (Window w, Atom property, ASRawHints * raw)
{
	void          (*read_func) (ASRawHints * hints, Window w) = NULL;

	if (w && property && raw)
	{
		/* Here we are only interested in properties updtaed by the Window Manager : */
		if (property == _XA_WM_NAME || property == _XA_WM_ICON_NAME ||
			property == _XA_NET_WM_NAME || property == _XA_NET_WM_ICON_NAME ||
			property == _XA_NET_WM_VISIBLE_NAME || property == _XA_NET_WM_VISIBLE_ICON_NAME)
		{
			read_wm_state (raw, w);
			read_wm_icon_name (raw, w);
			read_wm_class (raw, w);
			read_extwm_name (raw, w);
			read_extwm_icon_name (raw, w);
			read_extwm_visible_name (raw, w);
			read_extwm_visible_icon_name (raw, w);
			return (validate_drawable( w, NULL, NULL )==w);
		} else if (property == _XA_WM_HINTS)
			read_func = read_wm_hints;
		else if (property == _XA_WM_NORMAL_HINTS)
			read_func = read_wm_normal_hints;
		else if (property == _XA_WM_PROTOCOLS)
			read_func = read_wm_protocols;
		else if (property == _XA_WM_COLORMAP_WINDOWS)
			read_func = read_wm_cmap_windows;
		else if (property == _XA_WM_STATE)
			read_func = read_wm_state;
		else if (property == _XA_WM_COMMAND)
			read_func = read_wm_command;
		else if (property == _XA_WM_CLIENT_MACHINE)
			read_func = read_wm_client_machine;
		else
		{
			show_debug (__FILE__, __FUNCTION__, __LINE__, "unknown property %X", property);
			return False;
		}
		read_func (raw, w);
		return (validate_drawable( w, NULL, NULL )==w);
	} else
		show_debug (__FILE__, __FUNCTION__, __LINE__, "incomplete parameters (%X, %X, %p)", w, property, raw);

	return False;
}



/**********************************************************************************/
/**********************************************************************************/
/***************** Setting property values here  : ********************************/
/**********************************************************************************/

/****************************************************************************
 * This is used to tell applications which windows on the screen are
 * top level appication windows, and which windows are the icon windows
 * that go with them.
 ****************************************************************************/
void
set_client_state (Window w, struct ASStatusHints *status)
{
	if (w != None)
	{
        if (status != NULL)
		{
            CARD32        extwm_states[MAX_NET_WM_STATES];
			long          used = 0;
			CARD32        gnome_state = 0;
			CARD32       *old_extwm_state = NULL;
			long          old_extwm_used = 0;
			ASFlagType    old_state = 0;

			if (get_flags (status->flags, AS_Sticky))
			{
				extwm_states[used++] = _XA_NET_WM_STATE_STICKY;
				gnome_state |= WIN_STATE_STICKY;
			}
			if (get_flags (status->flags, AS_Shaded))
			{
				extwm_states[used++] = _XA_NET_WM_STATE_SHADED;
				gnome_state |= WIN_STATE_SHADED;
			}
			if (get_flags (status->flags, AS_MaximizedX))
			{
				extwm_states[used++] = _XA_NET_WM_STATE_MAXIMIZED_HORZ;
				gnome_state |= WIN_STATE_MAXIMIZED_HORIZ;
			}
			if (get_flags (status->flags, AS_MaximizedY))
			{
				extwm_states[used++] = _XA_NET_WM_STATE_MAXIMIZED_VERT;
				gnome_state |= WIN_STATE_MAXIMIZED_VERT;
			}
			if (read_32bit_proplist (w, _XA_NET_WM_WINDOW_TYPE, MAX_NET_WM_STATES, &old_extwm_state, &old_extwm_used))
			{
				translate_atom_list (&old_state, EXTWM_State, old_extwm_state, old_extwm_used);
				free (old_extwm_state);
			}
			if (get_flags (old_state, EXTWM_StateModal))
				extwm_states[used++] = _XA_NET_WM_STATE_MODAL;
			if (get_flags (old_state, EXTWM_StateSkipTaskbar))
				extwm_states[used++] = _XA_NET_WM_STATE_SKIP_TASKBAR;
			if (get_flags (old_state, EXTWM_StateSkipPager))
				extwm_states[used++] = _XA_NET_WM_STATE_SKIP_PAGER;

			set_32bit_proplist (w, _XA_NET_WM_STATE, XA_ATOM, &(extwm_states[0]), used);
			set_32bit_property (w, _XA_WIN_STATE, XA_CARDINAL, gnome_state);

			if (get_flags (status->flags, AS_Layer))
				set_32bit_property (w, _XA_WIN_LAYER, XA_CARDINAL, status->layer);
		}
    }
}

void
set_client_desktop (Window w, int ext_desk)
{
	if (w)
	{
		if (ext_desk >= 0)
		{
			set_32bit_property (w, _XA_WIN_WORKSPACE, XA_CARDINAL, ext_desk);
			set_32bit_property (w, _XA_NET_WM_DESKTOP, XA_CARDINAL, ext_desk);
		}
	}
}

void
set_client_names (Window w, char *name, char *icon_name, char *res_class, char *res_name)
{
	if (w)
	{
		if (name)
		{
			set_text_property (w, _XA_WM_NAME, &name, 1, TPE_String);
			/* need to convert string to UTF8 */
			set_text_property (w, _XA_NET_WM_NAME, &name, 1, TPE_UTF8);
		}
		if (icon_name)
		{
			set_text_property (w, _XA_WM_ICON_NAME, &icon_name, 1, TPE_String);
			/* need to convert string to UTF8 */
			set_text_property (w, _XA_NET_WM_ICON_NAME, &icon_name, 1, TPE_UTF8);
		}
		if (res_class || res_name)
		{
			XClassHint   *class_hint = XAllocClassHint ();

			if (class_hint)
			{
				class_hint->res_class = res_class;
				class_hint->res_name = res_name;
				XSetClassHint (dpy, w, class_hint);
				XFree (class_hint);
			}
		}
	}
}

void
set_client_protocols (Window w, ASFlagType protocols, ASFlagType extwm_protocols)
{
LOCAL_DEBUG_OUT( "protocols=0x%lX", protocols );
    if (w && protocols)
	{
		CARD32         *list, *extwm_list;
		long          nitems, extwm_nitems;

		encode_atom_list (&(WM_Protocols[0]), &list, &nitems, protocols);
        encode_atom_list (&(EXTWM_Protocols[0]), &extwm_list, &extwm_nitems, extwm_protocols);

        LOCAL_DEBUG_OUT( "nitems=%ld, extwm_nitems = %ld", nitems, extwm_nitems );
		if( extwm_nitems > 0 )
		{
			int i ;
			list = realloc( list, sizeof(CARD32)*extwm_nitems );
			for( i = 0  ; i < extwm_nitems ; ++i )
				list[nitems+i] = extwm_list[i] ;
			free( extwm_list );
			nitems += extwm_nitems ;
		}	 

        if (nitems > 0)
		{
			set_32bit_proplist (w, _XA_WM_PROTOCOLS, XA_ATOM, list, nitems);
			free (list);
		}
	}
}

void
set_extwm_hints (Window w, ExtendedWMHints * extwm_hints)
{
	if (w && extwm_hints)
	{
		CARD32         *list;
		long          nitems;

		encode_atom_list (&(EXTWM_WindowType[0]), &list, &nitems, extwm_hints->flags);
		if (nitems > 0)
		{
			set_32bit_proplist (w, _XA_NET_WM_WINDOW_TYPE, XA_ATOM, list, nitems);
			free (list);
		}
		encode_atom_list (&(EXTWM_State[0]), &list, &nitems, extwm_hints->flags);
		if (nitems > 0)
		{
			set_32bit_proplist (w, _XA_NET_WM_STATE, XA_CARDINAL, list, nitems);
			free (list);
            list = NULL ;
		}
		if (get_flags (extwm_hints->flags, EXTWM_DESKTOP))
			set_32bit_property (w, _XA_NET_WM_DESKTOP, XA_CARDINAL, extwm_hints->desktop);
		if (get_flags (extwm_hints->flags, EXTWM_PID))
			set_32bit_property (w, _XA_NET_WM_PID, XA_CARDINAL, extwm_hints->pid);
	}
}

void
set_gnome_hints (Window w, GnomeHints * gnome_hints)
{

}

void
set_client_hints (Window w, XWMHints * hints, XSizeHints * size_hints, ASFlagType protocols,
				  ExtendedWMHints * extwm_hints)
{
	if (w)
	{
		if (hints)
			XSetWMHints (dpy, w, hints);
		if (size_hints)
			XSetWMNormalHints (dpy, w, size_hints);
		if (protocols)
			set_client_protocols (w, protocols, extwm_hints->flags);

		if (extwm_hints)
			set_extwm_hints (w, extwm_hints);
	}
}

/***************************************************************************
 * ICCCM Client Messages - Section 4.2.8 of the ICCCM dictates that all
 * client messages will have the following form:
 *
 *     event type	ClientMessage
 *     message type	_XA_WM_PROTOCOLS
 *     window       client window
 *     format		32
 *     data[0]		message atom
 *     data[1]		time stamp
 ****************************************************************************/
void
send_wm_protocol_request (Window w, Atom request, Time timestamp)
{
	XClientMessageEvent ev;

	ev.type = ClientMessage;
	ev.window = w;
	ev.message_type = _XA_WM_PROTOCOLS;
	ev.format = 32;
	ev.data.l[0] = request;
	ev.data.l[1] = timestamp;
    XSendEvent (dpy, w, False, 0, (XEvent *) & ev);
}
