/*
 * 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.
 *
 */

#include "../configure.h"

#define LOCAL_DEBUG
#include "asapp.h"
#include "afterstep.h"
#include "asdatabase.h"
#include "screen.h"
#include "functions.h"
#include "clientprops.h"
#include "hints.h"
#include "desktop_category.h"
#include "freestor.h"
#include "../libAfterImage/afterimage.h"


/*********************************************************************************
 * Raw Hints must be aggregated into usable forma :
 *********************************************************************************/
static void init_ashints (ASHints * hints)
{
	if (hints) {									/* some defaults to start with : */
		hints->flags =
				AS_HitPager | AS_Gravity | AS_AcceptsFocus | AS_Titlebar |
				AS_IconTitle | AS_Handles | AS_Border;
		/* can't gracefully close the window if it does not support WM_DELETE_WINDOW */
		hints->function_mask = ~(AS_FuncClose | AS_FuncPinMenu);
		hints->gravity = NorthWestGravity;
		hints->border_width = 1;
	}
}

ASFlagType get_asdb_hint_mask (ASDatabaseRecord * db_rec)
{
	ASFlagType hint_mask = ASFLAGS_EVERYTHING;

	if (db_rec) {
		if (get_flags (db_rec->set_flags, STYLE_GROUP_HINTS)
				&& !get_flags (db_rec->flags, STYLE_GROUP_HINTS))
			clear_flags (hint_mask, (0x01 << HINTS_GroupLead));
		if (get_flags (db_rec->set_flags, STYLE_TRANSIENT_HINTS)
				&& !get_flags (db_rec->flags, STYLE_TRANSIENT_HINTS))
			clear_flags (hint_mask, (0x01 << HINTS_Transient));
		if (get_flags (db_rec->set_flags, STYLE_MOTIF_HINTS)
				&& !get_flags (db_rec->flags, STYLE_MOTIF_HINTS))
			clear_flags (hint_mask, (0x01 << HINTS_Motif));
		if (get_flags (db_rec->set_flags, STYLE_GNOME_HINTS)
				&& !get_flags (db_rec->flags, STYLE_GNOME_HINTS))
			clear_flags (hint_mask, (0x01 << HINTS_Gnome));
		if (get_flags (db_rec->set_flags, STYLE_EXTWM_HINTS)
				&& !get_flags (db_rec->flags, STYLE_EXTWM_HINTS))
			clear_flags (hint_mask, (0x01 << HINTS_ExtendedWM));
		if (get_flags (db_rec->set_flags, STYLE_KDE_HINTS)
				&& !get_flags (db_rec->flags, STYLE_KDE_HINTS))
			clear_flags (hint_mask, (0x01 << HINTS_KDE));
		if (get_flags (db_rec->set_flags, STYLE_XRESOURCES_HINTS)
				&& !get_flags (db_rec->flags, STYLE_XRESOURCES_HINTS))
			clear_flags (hint_mask, (0x01 << HINTS_XResources));
	}
	return hint_mask;
}

static ASFlagType
get_afterstep_resources (XrmDatabase db, ASStatusHints * status)
{
	ASFlagType found = 0;

	if (db != NULL && status) {
		if (read_int_resource
				(db, "afterstep*desk", "AS*Desk", &(status->desktop)))
			set_flags (found, AS_StartDesktop);
		if (read_int_resource
				(db, "afterstep*layer", "AS*Layer", &(status->layer)))
			set_flags (found, AS_StartLayer);
		if (read_int_resource
				(db, "afterstep*viewportx", "AS*ViewportX", &(status->viewport_x)))
			set_flags (found, AS_StartViewportX);
		if (read_int_resource
				(db, "afterstep*viewporty", "AS*ViewportY", &(status->viewport_y)))
			set_flags (found, AS_StartViewportY);
		set_flags (status->flags, found);
	}
	return found;
}

void
merge_command_line (ASHints * clean, ASStatusHints * status,
										ASRawHints * raw)
{
	static XrmOptionDescRec xrm_cmd_opts[] = {
		/* Want to accept -xrm "afterstep*desk:N" as options
		 * to specify the desktop. Have to include dummy options that
		 * are meaningless since Xrm seems to allow -x to match -xrm
		 * if there would be no ambiguity. */
		{"-xrnblahblah", NULL, XrmoptionResArg, (caddr_t) NULL},
		{"-xrm", NULL, XrmoptionResArg, (caddr_t) NULL},
	};

	if (raw == NULL)
		return;
	if (raw->wm_cmd_argc > 0 && raw->wm_cmd_argv != NULL) {
		XrmDatabase cmd_db = NULL;	/* otherwise it will try to use value as if it was database */
		ASFlagType found;

		init_xrm ();

		XrmParseCommand (&cmd_db, xrm_cmd_opts, 2, "afterstep",
										 &(raw->wm_cmd_argc), raw->wm_cmd_argv);
		if (status != NULL) {
			found = get_afterstep_resources (cmd_db, status);

			if (!get_flags (found, AS_StartDesktop)) {	/* checking for CDE workspace specification */
				if (read_int_resource
						(cmd_db, "*workspaceList", "*WorkspaceList",
						 &(status->desktop)))
					set_flags (status->flags, AS_StartDesktop);
			}
			XrmDestroyDatabase (cmd_db);
		}
		if (raw->wm_client_machine && clean)
			clean->client_host = text_property2string (raw->wm_client_machine);

		if (raw->wm_cmd_argc > 0 && clean) {
			int i;
			int len = 0;

			/* there still are some args left ! lets save it for future use : */
			/* first check to remove any geometry settings : */
			for (i = 0; i < raw->wm_cmd_argc; i++)
				if (raw->wm_cmd_argv[i]) {
					if (i + 1 < raw->wm_cmd_argc)
						if (raw->wm_cmd_argv[i + 1] != NULL) {
							register char *g = raw->wm_cmd_argv[i + 1];

							if (isdigit ((int)*g)
									|| ((*g == '-' || *g == '+') && isdigit ((int)*(g + 1))))
								if (mystrcasecmp (raw->wm_cmd_argv[i], "-g") == 0
										|| mystrcasecmp (raw->wm_cmd_argv[i],
																		 "-geometry") == 0) {
									raw->wm_cmd_argv[i] = NULL;
									raw->wm_cmd_argv[++i] = NULL;
									continue;
								}
						}
					len += strlen (raw->wm_cmd_argv[i]) + 1;
				}
			if (len > 0) {
				register char *trg, *src;

				if (clean->client_cmd)
					free (clean->client_cmd);
				trg = clean->client_cmd =
						safecalloc (1, len + raw->wm_cmd_argc * 2 + 1);
				for (i = 0; i < raw->wm_cmd_argc; i++)
					if ((src = raw->wm_cmd_argv[i]) != NULL) {
						register int k;
						Bool add_quotes = False;

						for (k = 0; src[k]; k++)
							if (isspace (src[k]) ||
									src[k] == '#' ||
									src[k] == '*' ||
									src[k] == '$' ||
									src[k] == ';' ||
									src[k] == '&' || src[k] == '<' || src[k] == '>'
									|| src[k] == '|' || iscntrl (src[k])) {
								add_quotes = True;
								break;
							}

						if (add_quotes) {
							trg[0] = '"';
							++trg;
						}
						for (k = 0; src[k]; k++)
							trg[k] = src[k];
						if (add_quotes) {
							trg[k] = '"';
							++k;
						}
						trg[k] = ' ';
						trg += k + 1;
					}
				if (trg > clean->client_cmd)
					trg--;
				*trg = '\0';
			}
		}
	}
}

void
check_hints_sanity (ScreenInfo * scr, ASHints * clean,
										ASStatusHints * status, Window client)
{
	if (clean) {
		if (clean->border_width == 0
				|| clean->border_width > scr->MyDisplayHeight / 2)
			clear_flags (clean->flags, AS_Border);

		if (clean->handle_width > scr->MyDisplayHeight / 2)
			clean->handle_width = 0;

		if (get_flags (clean->flags, AS_SizeInc)) {
			if (clean->width_inc == 0)
				clean->width_inc = 1;
			if (clean->height_inc == 0)
				clean->height_inc = 1;
		}

		if (get_flags (clean->flags, AS_Aspect))
			if ((clean->min_aspect.x == 0 || clean->min_aspect.y == 0) &&
					(clean->max_aspect.x == 0 || clean->max_aspect.y == 0))
				clear_flags (clean->flags, AS_Aspect);

		if (get_flags (clean->flags, AS_Icon))
			if (clean->icon_file == NULL && clean->icon.pixmap == None)
				clear_flags (clean->flags, AS_Icon);

		if (status && status->width <= 2 && status->height <= 2) {
			if (clean->res_class
					&& strcasecmp (clean->res_class, "DockApp") == 0)
				set_flags (clean->flags, AS_WMDockApp);
		}

		if (clean->icon.window == client) {
			clean->icon.window = None;
			clear_flags (clean->function_mask, AS_FuncMinimize);
			clear_flags (clean->client_icon_flags, AS_ClientIcon);
		}


		if (clean->frame_name == NULL)
			clear_flags (clean->flags, AS_Frame);
		if (clean->windowbox_name == NULL)
			clear_flags (clean->flags, AS_Windowbox);
	}
}

void check_status_sanity (ScreenInfo * scr, ASStatusHints * status)
{
	if (status) {
		/* we want to limit user specifyed layers to AS_LayerDesktop < layer < AS_LayerMenu
		 * which is reasonable, since you cannot be lower then Desktop and higher then Menu
		 */
		if (get_flags (status->flags, AS_StartPositionUser))
			set_flags (status->flags, AS_StartPosition);

		if (status->layer <= AS_LayerDesktop)
			status->layer = AS_LayerDesktop + 1;
		else if (status->layer >= AS_LayerMenu)
			status->layer = AS_LayerMenu - 1;

		if (status->desktop == INVALID_DESK)
			clear_flags (status->flags, AS_StartDesktop);
		LOCAL_DEBUG_OUT ("viewport_x = %d", status->viewport_x);
		status->viewport_x = FIT_IN_RANGE (0, status->viewport_x, scr->VxMax);
		LOCAL_DEBUG_OUT ("viewport_x = %d", status->viewport_x);
		status->viewport_y = FIT_IN_RANGE (0, status->viewport_y, scr->VyMax);
		if (status->width < 2)
			status->width = 2;
		if (status->height < 2)
			status->height = 2;
		if (status->border_width == 0
				|| status->border_width > scr->MyDisplayHeight / 2)
			clear_flags (status->flags, AS_StartBorderWidth);
	}
}

unsigned char get_hint_name_encoding (ASHints * hints, int name_idx)
{
	if (hints && name_idx >= 0 && name_idx <= MAX_WINDOW_NAMES)
		return hints->names_encoding[name_idx];
	return AS_Text_ASCII;
}

/* Hints merging functions : */
ASHints *merge_hints (ASRawHints * raw, ASDatabase * db,
											ASStatusHints * status, ASSupportedHints * list,
											ASFlagType what, ASHints * reusable_memory,
											Window client)
{
	ASHints *clean = NULL;
	ASDatabaseRecord db_rec, *pdb_rec;
	int i;
	ASFlagType hints_types = ASFLAGS_EVERYTHING;

	if (raw == NULL || list == NULL || what == 0)
		return NULL;
	if ((clean = reusable_memory) == NULL)
		clean = (ASHints *) safecalloc (1, sizeof (ASHints));
	else
		memset (clean, 0x00, sizeof (ASHints));
	if (status)
		memset (status, 0x00, sizeof (ASStatusHints));

	init_ashints (clean);

	/* collect the names first */
	if (get_flags (what, HINT_NAME)) {
		for (i = 0; i < list->hints_num; i++)
			(list->merge_funcs[i]) (clean, raw, NULL, status, HINT_NAME);

		what &= ~HINT_NAME;
	}
	if (clean->names[0] == NULL) {
		clean->names[0] = mystrdup ("");	/* must have at least one valid name string - even if empty */
		clean->names_encoding[0] = AS_Text_ASCII;
	}
	/* we want to make sure that we have Icon Name at all times, if at all possible */
	if (clean->icon_name == NULL) {
		clean->icon_name = clean->names[0];
		clean->icon_name_idx = 0;
	}
	/* we don't want to do anything else if all that was requested are names */
	if (what == 0)
		return clean;

	pdb_rec = fill_asdb_record (db, clean->names, &db_rec, False);

	if (clean->matched_name0)
		free (clean->matched_name0);
	clean->matched_name0 = mystrdup (clean->names[0]);
	clean->matched_name0_encoding = clean->names_encoding[0];


	LOCAL_DEBUG_OUT ("printing db record %p for names %p and db %p", pdb_rec,
									 clean->names, db);
	if (is_output_level_under_threshold (OUTPUT_LEVEL_DATABASE))
		print_asdb_matched_rec (NULL, NULL, db, pdb_rec);

	hints_types = get_asdb_hint_mask (pdb_rec);
	hints_types &=
			raw->hints_types | (0x01 << HINTS_ASDatabase) | (0x01 <<
																											 HINTS_XResources);

	/* now do the rest : */
	if (what != 0)
		for (i = 0; i < list->hints_num; i++) {	/* only do work if needed */
			if (get_flags (hints_types, (0x01 << list->hints_types[i]))) {
				(list->merge_funcs[i]) (clean, raw, pdb_rec, status, what);
				LOCAL_DEBUG_OUT ("merging hints %d (of %d ) - flags == 0x%lX", i,
												 list->hints_num, clean->flags);
			}
		}
	if (get_flags (what, HINT_STARTUP))
		merge_command_line (clean, status, raw);

	check_hints_sanity (raw->scr, clean, status, client);
	check_status_sanity (raw->scr, status);

	/* this is needed so if user changes the list of supported hints -
	 * we could track what was used, and if we need to remerge them
	 */
	clean->hints_types_raw = raw->hints_types;
	clean->hints_types_clean =
			get_flags (raw->hints_types, list->hints_flags);

	return clean;
}

/*
 * few function - shortcuts to implement update of selected hints :
 */
/* returns True if protocol/function hints actually changed :*/
Bool
update_protocols (ScreenInfo * scr, Window w, ASSupportedHints * list,
									ASFlagType * pprots, ASFlagType * pfuncs)
{
	ASRawHints raw;
	ASHints clean;
	Bool changed = False;

	if (w == None)
		return False;

	if (collect_hints (scr, w, HINT_PROTOCOL, &raw) != NULL) {
		if (merge_hints (&raw, NULL, NULL, list, HINT_PROTOCOL, &clean, w) !=
				NULL) {
			if (pprots)
				if ((changed = (*pprots != clean.protocols)))
					*pprots = clean.protocols;
			if (pfuncs)
				if ((changed = (*pfuncs != clean.function_mask)))
					*pfuncs = clean.function_mask;
			destroy_hints (&clean, True);
		}
		destroy_raw_hints (&raw, True);
	}
	return changed;
}

/* returns True if protocol/function hints actually changed :*/
Bool
update_colormaps (ScreenInfo * scr, Window w, ASSupportedHints * list,
									CARD32 ** pcmap_windows)
{
	ASRawHints raw;
	ASHints clean;
	Bool changed = False;
	register CARD32 *old_win, *new_win;


	if (w == None || pcmap_windows == NULL)
		return False;

	if (collect_hints (scr, w, HINT_COLORMAP, &raw) != NULL) {
		if (merge_hints (&raw, NULL, NULL, list, HINT_COLORMAP, &clean, w) !=
				NULL) {
			old_win = *pcmap_windows;
			new_win = clean.cmap_windows;
			changed = (old_win != new_win);
			if (new_win != NULL && old_win != NULL) {
				while (*old_win == *new_win && *old_win != None) {
					old_win++;
					new_win++;
				}
				changed = (*old_win != *new_win);
			}
			if (changed) {
				if (*pcmap_windows != NULL)
					free (*pcmap_windows);
				*pcmap_windows = clean.cmap_windows;
				clean.cmap_windows = NULL;
			}
			destroy_hints (&clean, True);
		}
		destroy_raw_hints (&raw, True);
	}
	return changed;
}

/****************************************************************************
 * The following functions actually implement hints merging :
 ****************************************************************************/
static int
add_name_to_list (ASHints * hints, char *name, unsigned char encoding,
									Bool to_front)
{
	register int i;
	char **list = hints->names;
	unsigned char *encoding_list = hints->names_encoding;

	if (name == NULL)
		return -1;

	for (i = 0; i < MAX_WINDOW_NAMES; i++) {
		if (list[i] == NULL)
			break;
		if (encoding_list[i] == encoding && strcmp (name, list[i]) == 0) {
			free (name);
			return i;
		}
	}
	if (i >= MAX_WINDOW_NAMES) {	/* tough luck - no more space */
		free (name);
		return -1;
	}

	if (to_front) {
		for (; i > 0; i--) {
			list[i] = list[i - 1];
			encoding_list[i] = encoding_list[i - 1];
		}
		if (hints->res_name_idx >= 0)
			++(hints->res_name_idx);
		if (hints->res_class_idx >= 0)
			++(hints->res_class_idx);
		if (hints->icon_name_idx >= 0)
			++(hints->icon_name_idx);
	}
	list[i] = name;
	encoding_list[i] = encoding;
	return i;
}

static int pointer_name_to_index_in_list (char **list, char *name)
{
	register int i;

	if (name)
		for (i = 0; i < MAX_WINDOW_NAMES; i++) {
			if (list[i] == NULL)
				break;
			if (name == list[i])
				return i;
		}
	return MAX_WINDOW_NAMES;
}


static void
merge_icccm_hints (ASHints * clean, ASRawHints * raw,
									 ASDatabaseRecord * db_rec, ASStatusHints * status,
									 ASFlagType what)
{
	if (raw == NULL)
		return;
	if (get_flags (what, HINT_NAME)) {	/* adding all the possible window names in order of increasing importance */
		if (raw->wm_name)
			add_name_to_list (clean, text_property2string (raw->wm_name),
												AS_Text_ASCII, True);
		if (raw->wm_icon_name) {
			clean->icon_name_idx =
					add_name_to_list (clean,
														text_property2string (raw->wm_icon_name),
														AS_Text_ASCII, False);
			clean->icon_name =
					(clean->icon_name_idx <
					 0) ? NULL : clean->names[clean->icon_name_idx];
		}
		if (raw->wm_class) {
			if (raw->wm_class->res_class) {
				clean->res_class_idx =
						add_name_to_list (clean, stripcpy (raw->wm_class->res_class),
															AS_Text_ASCII, False);
				clean->res_class =
						(clean->res_class_idx <
						 0) ? NULL : clean->names[clean->res_class_idx];
			}
			if (raw->wm_class->res_name) {
				clean->res_name_idx =
						add_name_to_list (clean, stripcpy (raw->wm_class->res_name),
															AS_Text_ASCII, False);
				clean->res_name =
						(clean->res_name_idx <
						 0) ? NULL : clean->names[clean->res_name_idx];
			}
		}
	}

	if (get_flags (what, HINT_STARTUP) && status != NULL) {
		Bool position_user = False;

		if (raw->wm_hints) {
			if (get_flags (raw->wm_hints->flags, StateHint)) {
				if (raw->wm_hints->input == IconicState)
					set_flags (status->flags, AS_StartsIconic);
				else
					clear_flags (status->flags, AS_StartsIconic);
			}
			if (get_flags (raw->wm_hints->flags, UrgencyHint)) {
				status->layer = AS_LayerUrgent;
				set_flags (status->flags, AS_StartLayer);
				set_flags (status->flags, AS_Urgent);
			}
		}

		if (raw->wm_normal_hints)
			position_user =
					(get_flags (raw->wm_normal_hints->flags, USPosition));

		if (!get_flags (status->flags, AS_StartPositionUser) || position_user) {
			status->x = raw->placement.x;
			status->y = raw->placement.y;
			set_flags (status->flags, AS_StartPosition);
			if (position_user)
				set_flags (status->flags, AS_StartPositionUser);
		}
		status->width = raw->placement.width;
		status->height = raw->placement.height;
		set_flags (status->flags, AS_StartSize);
		status->border_width = raw->border_width;
		set_flags (status->flags, AS_StartBorderWidth);
		if (status->border_width > 0) {
			clean->border_width = status->border_width;
			set_flags (clean->flags, AS_Border);
		}

		if (raw->wm_state == IconicState)
			set_flags (status->flags, AS_StartsIconic);
		if (raw->wm_state_icon_win != None)
			status->icon_window = raw->wm_state_icon_win;
	}

	if (get_flags (what, HINT_GENERAL)) {
		if (raw->wm_hints) {
			XWMHints *wmh = raw->wm_hints;

			if (get_flags (wmh->flags, InputHint)) {
				if (wmh->input)
					set_flags (clean->flags, AS_AcceptsFocus);
				else
					clear_flags (clean->flags, AS_AcceptsFocus);
			}

			if (get_flags (wmh->flags, IconWindowHint)
					&& wmh->icon_window != None) {
				clean->icon.window = wmh->icon_window;
				set_flags (clean->client_icon_flags, AS_ClientIcon);
				clear_flags (clean->client_icon_flags, AS_ClientIconPixmap);
				if (get_flags (wmh->flags, IconWindowIsChildHint))
					set_flags (clean->flags, AS_WMDockApp);
			} else if (get_flags (wmh->flags, IconPixmapHint)
								 && wmh->icon_pixmap != None) {
				clean->icon.pixmap = wmh->icon_pixmap;
				if (get_flags (wmh->flags, IconMaskHint) && wmh->icon_mask != None)
					clean->icon_mask = wmh->icon_mask;
				set_flags (clean->client_icon_flags, AS_ClientIcon);
				set_flags (clean->client_icon_flags, AS_ClientIconPixmap);
			}

			if (get_flags (wmh->flags, IconPositionHint)) {
				clean->icon_x = wmh->icon_x;
				clean->icon_y = wmh->icon_y;
				set_flags (clean->client_icon_flags, AS_ClientIconPosition);
			}
		}

		if (raw->wm_normal_hints) {
			XSizeHints *nh = raw->wm_normal_hints;

			if (get_flags (nh->flags, PMinSize)) {
				clean->min_width = nh->min_width;
				clean->min_height = nh->min_height;
				set_flags (clean->flags, AS_MinSize);
			}
			if (get_flags (nh->flags, PMaxSize)) {
				clean->max_width = nh->max_width;
				clean->max_height = nh->max_height;
				set_flags (clean->flags, AS_MaxSize);
			}
			if (get_flags (nh->flags, PResizeInc)) {
				clean->width_inc = nh->width_inc;
				clean->height_inc = nh->height_inc;
				set_flags (clean->flags, AS_SizeInc);
			}
			if (get_flags (nh->flags, PAspect)) {
				clean->min_aspect.x = nh->min_aspect.x;
				clean->min_aspect.y = nh->min_aspect.y;
				clean->max_aspect.x = nh->max_aspect.x;
				clean->max_aspect.y = nh->max_aspect.y;
				set_flags (clean->flags, AS_Aspect);
			}
			if (get_flags (nh->flags, PBaseSize)) {
				clean->base_width = nh->base_width;
				clean->base_height = nh->base_height;
				set_flags (clean->flags, AS_BaseSize);
			}
			if (get_flags (nh->flags, PWinGravity)) {
				clean->gravity = nh->win_gravity;
				set_flags (clean->flags, AS_Gravity);
			}
		}

	}

	if (get_flags (what, HINT_COLORMAP)) {
		if (raw->wm_cmap_windows && raw->wm_cmap_win_count > 0) {
			register int i;

			if (clean->cmap_windows)
				free (clean->cmap_windows);
			clean->cmap_windows =
					safecalloc (raw->wm_cmap_win_count + 1, sizeof (CARD32));
			for (i = 0; i < raw->wm_cmap_win_count; i++)
				clean->cmap_windows[i] = raw->wm_cmap_windows[i];
			clean->cmap_windows[i] = None;
		}
	}

	if (get_flags (what, HINT_PROTOCOL)) {
		clean->protocols |= raw->wm_protocols;
		if (get_flags (clean->protocols, AS_DoesWmDeleteWindow))
			set_flags (clean->function_mask, AS_FuncClose);
	}
}

static void
merge_parent_hints (ASParentHints * hints, ASStatusHints * status)
{
	if (get_flags (hints->flags, AS_StartDesktop))
		status->desktop = hints->desktop;
	if (get_flags (hints->flags, AS_StartViewportX))
		status->viewport_x = hints->viewport_x;
	if (get_flags (hints->flags, AS_StartViewportY))
		status->viewport_y = hints->viewport_y;
	set_flags (status->flags, hints->flags);
}

static void
merge_group_hints (ASHints * clean, ASRawHints * raw,
									 ASDatabaseRecord * db_rec, ASStatusHints * status,
									 ASFlagType what)
{
	if (raw == NULL)
		return;
	if (raw->group_leader) {
		if (get_flags (what, HINT_STARTUP) && status != NULL)
			merge_parent_hints (raw->group_leader, status);

		if (get_flags (what, HINT_GENERAL))
			clean->group_lead = raw->group_leader->parent;
	}
}

static void
merge_transient_hints (ASHints * clean, ASRawHints * raw,
											 ASDatabaseRecord * db_rec, ASStatusHints * status,
											 ASFlagType what)
{
	if (raw == NULL)
		return;
	if (raw->transient_for) {
		if (get_flags (what, HINT_STARTUP) && status != NULL)
			merge_parent_hints (raw->transient_for, status);

		if (get_flags (what, HINT_GENERAL)) {
			clean->transient_for = raw->transient_for->parent;
			set_flags (clean->flags, AS_Transient);
			set_flags (clean->flags, AS_ShortLived);
		}
	}
}

static void
decode_flags (ASFlagType * dst_flags, ASFlagsXref * xref,
							ASFlagType set_flags, ASFlagType flags)
{
	if (dst_flags == NULL || set_flags == 0)
		return;
	LOCAL_DEBUG_CALLER_OUT
			("dst_flags = %lX, set_flags = %lX, flags = 0x%lX", *dst_flags,
			 set_flags, flags);
	while ((*xref)[0] != 0) {
		ASFlagType to_set;
		ASFlagType to_clear;
		int point;

		if (get_flags (set_flags, (*xref)[0])) {
			point = (get_flags (flags, (*xref)[0])) ? 1 : 3;
			to_set = (*xref)[point];
			to_clear = (*xref)[point + 1];
			if (to_set != 0)
				set_flags (*dst_flags, to_set);
			if (to_clear != 0)
				clear_flags (*dst_flags, to_clear);
		}
		xref++;
	}
}

static void
encode_flags (ASFlagType * dst_flags, ASFlagsXref * xref,
							ASFlagType set_flags, ASFlagType flags)
{
	if (dst_flags == NULL || set_flags == 0)
		return;

	while ((*xref)[0] != 0) {
		if (get_flags (set_flags, (*xref)[0])) {
			if (((flags & (*xref)[1]) == (*xref)[1])
					&& (flags & (*xref)[2]) == 0)
				set_flags (*dst_flags, (*xref)[0]);
			else if (((flags & (*xref)[3]) == (*xref)[3])
							 && (flags & (*xref)[4]) == 0)
				clear_flags (*dst_flags, (*xref)[0]);
		}
		xref++;
	}
}

#define decode_simple_flags(dst,xref,flags) decode_flags((dst),(xref),ASFLAGS_EVERYTHING,(flags))
#define encode_simple_flags(dst,xref,flags) encode_flags((dst),(xref),ASFLAGS_EVERYTHING,(flags))

void check_motif_hints_sanity (MwmHints * motif_hints)
{

	if (get_flags (motif_hints->decorations, MWM_DECOR_ALL))
		motif_hints->decorations =
				MWM_DECOR_EVERYTHING & ~(motif_hints->decorations);

	/* Now I have the un-altered decor and functions, but with the
	 * ALL attribute cleared and interpreted. I need to modify the
	 * decorations that are affected by the functions */
	if (get_flags (motif_hints->flags, MWM_HINTS_FUNCTIONS)) {
		if (get_flags (motif_hints->functions, MWM_FUNC_ALL))
			motif_hints->functions =
					MWM_FUNC_EVERYTHING & ~(motif_hints->functions);
		if (!get_flags (motif_hints->functions, MWM_FUNC_RESIZE))
			motif_hints->decorations &= ~MWM_DECOR_RESIZEH;
		/* MWM_FUNC_MOVE has no impact on decorations. */
		if (!get_flags (motif_hints->functions, MWM_FUNC_MINIMIZE))
			motif_hints->decorations &= ~MWM_DECOR_MINIMIZE;
		if (!get_flags (motif_hints->functions, MWM_FUNC_MAXIMIZE))
			motif_hints->decorations &= ~MWM_DECOR_MAXIMIZE;
		/* MWM_FUNC_CLOSE has no impact on decorations. */
	}
	/* This rule is implicit, but its easier to deal with if
	 * I take care of it now */
	if (motif_hints->decorations & (MWM_DECOR_MENU | MWM_DECOR_MINIMIZE |
																	MWM_DECOR_MAXIMIZE))
		motif_hints->decorations |= MWM_DECOR_TITLE;
}

static ASFlagsXref mwm_decor_xref[] = {	/*Flag              Set if Set    ,Clear if Set,Set if Clear, Clear if Clear  */
	{MWM_DECOR_BORDER, AS_Border, 0, 0, AS_Border | AS_Frame},
	{MWM_DECOR_RESIZEH, AS_Handles, 0, 0, AS_Handles},
	{MWM_DECOR_TITLE, AS_Titlebar, 0, 0, AS_Titlebar},
	{0, 0, 0, 0, 0}
};

static ASFlagsXref mwm_decor_func_xref[] = {	/*Flag              Set if Set,  Clear if Set, Set if Clear, Clear if Clear  */
	{MWM_DECOR_RESIZEH, AS_FuncResize, 0, 0, AS_Handles},
	{MWM_DECOR_MENU, AS_FuncPopup, 0, 0, AS_FuncPopup},
	{MWM_DECOR_MINIMIZE, AS_FuncMinimize, 0, 0, AS_FuncMinimize},
	{MWM_DECOR_MAXIMIZE, AS_FuncMaximize, 0, 0, AS_FuncMaximize},
	{0, 0, 0, 0, 0}
};

static ASFlagsXref mwm_func_xref[] = {	/*Flag              Set if Set,  Clear if Set, Set if Clear, Clear if Clear  */
	{MWM_FUNC_RESIZE, AS_FuncResize, 0, 0, AS_FuncResize},
	{MWM_FUNC_MOVE, AS_FuncMove, 0, 0, AS_FuncMove},
	{MWM_FUNC_MINIMIZE, AS_FuncMinimize, 0, 0, AS_FuncMinimize},
	{MWM_FUNC_MAXIMIZE, AS_FuncMaximize, 0, 0, AS_FuncMaximize},
	{MWM_FUNC_CLOSE, AS_FuncClose | AS_FuncKill, 0, 0,
	 AS_FuncClose | AS_FuncKill},
	{0, 0, 0, 0, 0}
};

static void
merge_motif_hints (ASHints * clean, ASRawHints * raw,
									 ASDatabaseRecord * db_rec, ASStatusHints * status,
									 ASFlagType what)
{
	CARD32 decor = MWM_DECOR_EVERYTHING;
	CARD32 funcs = MWM_FUNC_EVERYTHING;

	if (raw == NULL)
		return;
	if (raw->motif_hints) {
		INT32 input_mode = 0;

		if (get_flags (raw->motif_hints->flags, MWM_HINTS_INPUT_MODE))
			input_mode = raw->motif_hints->inputMode;

		if (get_flags (what, HINT_STARTUP) && status && input_mode != 0) {
			if (input_mode == MWM_INPUT_SYSTEM_MODAL) {
				status->layer = AS_LayerUrgent;
				set_flags (status->flags, AS_StartLayer);
				set_flags (status->flags, AS_StartsSticky);
			}
			if (input_mode == MWM_INPUT_FULL_APPLICATION_MODAL) {
				status->layer = AS_LayerTop;
				set_flags (status->flags, AS_StartLayer);
			}
		}

		check_motif_hints_sanity (raw->motif_hints);

		if (get_flags (raw->motif_hints->flags, MWM_HINTS_FUNCTIONS))
			funcs = raw->motif_hints->functions;
		if (get_flags (raw->motif_hints->flags, MWM_HINTS_DECORATIONS))
			decor = raw->motif_hints->decorations;

		/* finally we can apply conglomerated hints to our flags : */
		if (get_flags (what, HINT_GENERAL)) {
			decode_simple_flags (&(clean->flags), mwm_decor_xref, decor);
			LOCAL_DEBUG_OUT ("motif decor = 0x%lX, clean_flags = 0x%lX", decor,
											 clean->flags);
		}
		if (get_flags (what, HINT_PROTOCOL)) {
			decode_simple_flags (&(clean->function_mask), mwm_decor_func_xref,
													 decor);
			decode_simple_flags (&(clean->function_mask), mwm_func_xref, funcs);
		}
	}
}

static ASFlagsXref gnome_state_xref[] = {	/*Flag                    Set if Set,  Clear if Set, Set if Clear, Clear if Clear  */
	{WIN_STATE_STICKY, AS_StartsSticky, 0, 0, 0},
	{WIN_STATE_MINIMIZED, AS_StartsIconic, 0, 0, 0},
	{WIN_STATE_MAXIMIZED_VERT, AS_StartsMaximizedY, 0, 0, 0},
	{WIN_STATE_MAXIMIZED_HORIZ, AS_StartsMaximizedX, 0, 0, 0},
	{WIN_STATE_HIDDEN, 0, 0, 0, 0},
	{WIN_STATE_SHADED, AS_StartsShaded, 0, 0, 0},
	{WIN_STATE_HID_WORKSPACE, 0, 0, 0, 0},
	{WIN_STATE_HID_TRANSIENT, 0, 0, 0, 0},
	{WIN_STATE_FIXED_POSITION, 0, 0, 0, 0},
	{WIN_STATE_ARRANGE_IGNORE, 0, 0, 0, 0},
	{0, 0, 0, 0, 0}
};

static ASFlagsXref gnome_hints_xref[] = {	/*Flag                    Set if Set,  Clear if Set, Set if Clear, Clear if Clear  */
	{WIN_HINTS_SKIP_FOCUS, 0, AS_AcceptsFocus, 0, 0},
	{WIN_HINTS_SKIP_WINLIST, AS_SkipWinList, 0, 0, 0},
	{WIN_HINTS_SKIP_TASKBAR, AS_SkipWinList, 0, 0, 0},
	{WIN_HINTS_GROUP_TRANSIENT, 0, 0, 0, 0},
	{WIN_HINTS_FOCUS_ON_CLICK, AS_ClickToFocus, 0, 0, 0},
	{0, 0, 0, 0, 0}
};

#define GNOME_AFFECTED_STATE  (AS_StartsSticky|AS_StartsIconic| \
							   AS_StartsMaximizedY|AS_StartsMaximizedX| \
							   AS_StartsShaded)

Bool
decode_gnome_state (ASFlagType state, ASHints * clean,
										ASStatusHints * status)
{
	Bool changed = False;
	ASFlagType new_state = 0, disable_func = 0;

	decode_simple_flags (&new_state, gnome_state_xref, state);
	if ((new_state ^ (status->flags & GNOME_AFFECTED_STATE)) != 0) {
		changed = True;
		status->flags = new_state | (status->flags & ~GNOME_AFFECTED_STATE);
	}
	if (get_flags (new_state, WIN_STATE_FIXED_POSITION))
		disable_func = AS_FuncMove;

	if ((disable_func ^ (clean->function_mask & AS_FuncMove)) != 0) {
		changed = True;
		clean->function_mask =
				disable_func | (clean->function_mask & ~AS_FuncMove);
	}
	return changed;
}

static ASFlagType
encode_gnome_state (ASHints * clean, ASStatusHints * status)
{
	ASFlagType new_state = 0;

	if (status)
		encode_simple_flags (&new_state, gnome_state_xref, status->flags);

	if (get_flags (clean->function_mask, AS_FuncMove))
		set_flags (new_state, WIN_STATE_FIXED_POSITION);

	return new_state;
}

static void
merge_gnome_hints (ASHints * clean, ASRawHints * raw,
									 ASDatabaseRecord * db_rec, ASStatusHints * status,
									 ASFlagType what)
{
	register GnomeHints *gh;

	if (raw == NULL)
		return;
	gh = &(raw->gnome_hints);
	if (gh->flags == 0)
		return;

	if (get_flags (what, HINT_STARTUP) && status != NULL) {
		if (get_flags (gh->flags, GNOME_LAYER)) {
			status->layer = gh->layer;	/*No clue why we used to do that  :  (gh->layer - WIN_LAYER_NORMAL) >> 1; */
			set_flags (status->flags, AS_StartLayer);
		}
		if (get_flags (gh->flags, GNOME_WORKSPACE)) {
			status->desktop = gh->workspace;
			set_flags (status->flags, AS_StartDesktop);
		}
		if (get_flags (gh->flags, GNOME_STATE) && gh->state != 0) {
			decode_gnome_state (gh->state, clean, status);
		}
	}

	if (get_flags (what, HINT_GENERAL)) {
		if (get_flags (gh->flags, GNOME_HINTS) && gh->hints != 0) {
			decode_simple_flags (&(clean->flags), gnome_hints_xref, gh->hints);
			if (get_flags (gh->hints, WIN_HINTS_SKIP_FOCUS))
				clear_flags (clean->protocols, AS_DoesWmTakeFocus);
		}
	}
}

static void
merge_kde_hints (ASHints * clean, ASRawHints * raw,
								 ASDatabaseRecord * db_rec, ASStatusHints * status,
								 ASFlagType what)
{
	register KDEHints *kh;

	if (raw == NULL)
		return;
	kh = &(raw->kde_hints);
	if (kh->flags == 0)
		return;

	if (get_flags (what, HINT_STARTUP) && status != NULL) {
		if (get_flags (kh->flags, KDE_SysTrayWindowFor))
			set_flags (status->flags, AS_StartsSticky);
	}

	if (get_flags (what, HINT_GENERAL)) {
		if (get_flags (kh->flags, KDE_DesktopWindow))
			set_flags (clean->protocols, AS_DoesKIPC);
		if (get_flags (kh->flags, KDE_SysTrayWindowFor)) {
			set_flags (clean->flags, AS_SkipWinList | AS_DontCirculate);
			clear_flags (clean->flags, AS_Handles | AS_Frame);
		}
	}
}

static ASFlagsXref extwm_state_xref[] = {	/*Flag                    Set if Set,  Clear if Set, Set if Clear, Clear if Clear  */
	{EXTWM_StateSticky, AS_StartsSticky, 0, 0, 0},
	{EXTWM_StateMaximizedV, AS_StartsMaximizedY, 0, 0, 0},
	{EXTWM_StateMaximizedH, AS_StartsMaximizedX, 0, 0, 0},
	{EXTWM_StateShaded, AS_StartsShaded, 0, 0, 0},
	{EXTWM_StateFullscreen, AS_Fullscreen, 0, 0, 0},
	{EXTWM_StateDemandsAttention, AS_Urgent, 0, 0, 0},
	{0, 0, 0, 0, 0}
};

#define EXTWM_AFFECTED_STATE  (AS_StartsSticky|AS_StartsMaximizedY| \
							   AS_StartsMaximizedX|AS_StartsShaded|AS_Fullscreen)

static ASFlagType extwm_types_start_properties[][3] = {
	{EXTWM_TypeDesktop, AS_LayerOtherDesktop, AS_StartsSticky},
	{EXTWM_TypeDock, AS_LayerService, AS_StartsSticky},
	{EXTWM_TypeToolbar, AS_LayerNormal, 0},
	{EXTWM_TypeMenu, AS_LayerUrgent, AS_StartsSticky},
	{EXTWM_TypeDialog, AS_LayerTop, 0},
	{EXTWM_TypeNormal, AS_LayerNormal, 0},
	{EXTWM_TypeUtility, AS_LayerTop, 0},
	{EXTWM_TypeSplash, AS_LayerTop, AS_ShortLived},
	{EXTWM_TypeASModule, AS_LayerNormal, AS_StartsSticky},
	{0, 0, 0}
};

static ASFlagType extwm_states_start_properties[][3] = {
	{EXTWM_StateFullscreen, AS_LayerUrgent, AS_Fullscreen},
	{EXTWM_StateAbove, AS_LayerTop, 0},
	{EXTWM_StateBelow, AS_LayerBack, 0},
	{EXTWM_StateDemandsAttention, AS_LayerUrgent, 0},
	{0, 0, 0}
};

static ASFlagsXref extwm_type_xref[] = {	/*Flag              Set if Set,      Clear if Set,     Set if Clear,    Clear if Clear  */
	{EXTWM_TypeDesktop, AS_SkipWinList | AS_DontCirculate,
	 AS_Titlebar | AS_Handles | AS_Frame, 0, 0},
	{EXTWM_TypeDock, AS_AvoidCover, AS_Handles | AS_Frame | AS_Titlebar, 0,
	 0},
	{EXTWM_TypeToolbar, 0, AS_Handles | AS_Frame, 0, 0},
	{EXTWM_TypeMenu, AS_SkipWinList | AS_DontCirculate,
	 AS_Handles | AS_Frame, 0, 0},
	{EXTWM_TypeDialog, AS_ShortLived, 0 /*may need to remove handles */ , 0,
	 0},
	{EXTWM_TypeASModule, AS_Module, 0, 0, AS_Module},
	{0, 0, 0, 0, 0}
};

static ASFlagsXref extwm_type_func_mask[] = {	/*Flag             Set if Set,  Clear if Set,     Set if Clear  , Clear if Clear  */
	{EXTWM_TypeDesktop, 0,
	 AS_FuncResize | AS_FuncMinimize | AS_FuncMaximize | AS_FuncMove, 0, 0},
	{EXTWM_TypeDock, 0, AS_FuncResize | AS_FuncMinimize | AS_FuncMaximize, 0,
	 0},
	{EXTWM_TypeToolbar, 0, AS_FuncResize, 0, 0},
	{EXTWM_TypeMenu, 0, AS_FuncResize | AS_FuncMinimize | AS_FuncMaximize, 0,
	 0},
	{0, 0, 0, 0, 0}
};

ASFlagType extwm_state2as_state_flags (ASFlagType extwm_flags)
{
	ASFlagType as_flags = 0;

	decode_simple_flags (&as_flags, extwm_state_xref, extwm_flags);
	return as_flags;
}

static CARD32 *select_client_icon_argb (CARD32 * icon, int icon_length)
{
	int offset = 0;
	CARD32 *res = NULL;

	if (icon == NULL || icon_length <= 2)
		return NULL;
	while (res == NULL && offset + 2 < icon_length) {
		int width = icon[offset];
		int height = icon[offset + 1];
		int len = width * height;

		if (len < 0)
			break;
		if (len <= (icon_length - offset - 2))
			if (width == 48 && height == 48) {
				res = safemalloc ((2 + len) * sizeof (CARD32));
				memcpy (res, &(icon[offset]), (2 + len) * sizeof (CARD32));
			}
		offset += 2 + len;
	}
	offset = 0;
	while (res == NULL && offset + 2 < icon_length) {
		int width = icon[offset];
		int height = icon[offset + 1];
		int len = width * height;

		if (len < 0)
			break;
		if (len <= (icon_length - offset - 2))
			if (width >= 32 && height >= 32) {
				res = safemalloc ((2 + len) * sizeof (CARD32));
				memcpy (res, &(icon[offset]), (2 + len) * sizeof (CARD32));
			}
		offset += 2 + len;
	}
	if (res == NULL) {
		int width = icon[0];
		int height = icon[1];
		int size = width * height;

		icon_length -= 2;
		if (size > 0) {
			if (size + 2 > icon_length) {
				for (width = 128; width > 0; width -= 8)
					if (icon_length > width * width) {
						height = width;
						size = width * width;
						break;
					}
			}
			res = safecalloc (size + 2, sizeof (CARD32));
			memcpy (res + 2, &(icon[2]), size * sizeof (CARD32));
			res[0] = width;
			res[1] = height;
		}
	}
	return res;
}

static void
merge_extwm_hints (ASHints * clean, ASRawHints * raw,
									 ASDatabaseRecord * db_rec, ASStatusHints * status,
									 ASFlagType what)
{

	register ExtendedWMHints *eh;

	if (raw == NULL)
		return;
	eh = &(raw->extwm_hints);

	if (get_flags (what, HINT_NAME)) {
		if (eh->name)
			add_name_to_list (clean, stripcpy ((const char *)(eh->name->value)),
												AS_Text_UTF8, True);
		if (eh->visible_name)
			add_name_to_list (clean,
												stripcpy ((const char *)(eh->visible_name->value)),
												AS_Text_UTF8, True);
		if (eh->icon_name)
			clean->icon_name_idx =
					add_name_to_list (clean,
														stripcpy ((const char *)(eh->
																										 icon_name->value)),
														AS_Text_UTF8, False);
		if (eh->visible_icon_name)
			clean->icon_name_idx =
					add_name_to_list (clean,
														stripcpy ((const char
																			 *)(eh->visible_icon_name->value)),
														AS_Text_UTF8, False);
		clean->icon_name =
				(clean->icon_name_idx <
				 0) ? NULL : clean->names[clean->icon_name_idx];
	}

	if (get_flags (what, HINT_STARTUP) && status != NULL) {
		if (get_flags (eh->flags, EXTWM_DESKTOP)) {
			LOCAL_DEBUG_OUT ("EXTWM Hints Desktop = 0x%X", eh->desktop);
			if (eh->desktop == 0xFFFFFFFF)
				set_flags (status->flags, AS_StartsSticky);
			else {
				status->desktop = eh->desktop;
				set_flags (status->flags, AS_StartDesktop);
			}
		}
		/* window state hints : */
		if (get_flags (eh->flags, EXTWM_StateSet)) {
			if (get_flags (eh->state_flags, EXTWM_StateModal)) {
				status->layer = AS_LayerTop;
				set_flags (status->flags, AS_StartLayer);
			}
			decode_simple_flags (&(status->flags), extwm_state_xref,
													 eh->state_flags);
		}
		/* window type hints : */
		if (get_flags
				(eh->type_flags, (EXTWM_TypeEverything & (~EXTWM_TypeNormal)))) {
			register int i;

			for (i = 0; extwm_types_start_properties[i][0] != 0; i++)
				if (get_flags (eh->type_flags, extwm_types_start_properties[i][0])) {
					if (!get_flags (status->flags, AS_StartLayer)
							|| status->layer < extwm_types_start_properties[i][1])
						status->layer = extwm_types_start_properties[i][1];
					set_flags (status->flags, AS_StartLayer);
					set_flags (status->flags, extwm_types_start_properties[i][2]);
				}
		}
		if (get_flags
				(eh->state_flags,
				 EXTWM_StateFullscreen | EXTWM_StateAbove | EXTWM_StateBelow)) {
			register int i;

			for (i = 0; extwm_states_start_properties[i][0] != 0; i++)
				if (get_flags
						(eh->state_flags, extwm_states_start_properties[i][0])) {
					if (!get_flags (status->flags, AS_StartLayer)
							|| status->layer < extwm_states_start_properties[i][1])
						status->layer = extwm_states_start_properties[i][1];
					set_flags (status->flags, AS_StartLayer);
					set_flags (status->flags, extwm_states_start_properties[i][2]);
				}
		}
	}
	if (get_flags (what, HINT_GENERAL)) {
		if (get_flags (eh->flags, EXTWM_PID)) {
			clean->pid = eh->pid;
			set_flags (clean->flags, AS_PID);
		}
		if (get_flags (eh->flags, EXTWM_WINDOW_OPACITY)) {
			clean->window_opacity = eh->window_opacity;
			set_flags (clean->flags, AS_WindowOpacity);
		}
		if (get_flags (eh->flags, EXTWM_ICON)) {
			if (clean->icon_argb)
				free (clean->icon_argb);
			clean->icon_argb =
					select_client_icon_argb (eh->icon, eh->icon_length);
			set_flags (clean->flags, AS_Icon);
			set_flags (clean->client_icon_flags,
								 AS_ClientIcon | AS_ClientIconARGB);
		}
		if (get_flags (eh->state_flags, EXTWM_StateSkipTaskbar))
			set_flags (clean->flags, AS_SkipWinList);

		if (get_flags (eh->flags, EXTWM_TypeSet)) {
			clean->extwm_window_type = eh->type_flags;
			decode_simple_flags (&(clean->flags), extwm_type_xref,
													 eh->type_flags);
		}
	}
	if (get_flags (what, HINT_PROTOCOL)) {
		if (get_flags (eh->flags, EXTWM_DoesWMPing))
			set_flags (clean->protocols, AS_DoesWmPing);
		if (get_flags (eh->flags, EXTWM_NAME) && eh->name != NULL)
			set_flags (clean->protocols, AS_NeedsVisibleName);
		if (get_flags (eh->flags, EXTWM_VISIBLE_NAME)
				&& eh->visible_name != NULL)
			set_flags (clean->protocols, AS_NeedsVisibleName);
		if (get_flags (eh->flags, EXTWM_TypeSet))
			decode_simple_flags (&(clean->function_mask), extwm_type_func_mask,
													 eh->type_flags);
	}
}

static void
merge_xresources_hints (ASHints * clean, ASRawHints * raw,
												ASDatabaseRecord * db_rec, ASStatusHints * status,
												ASFlagType what)
{
	XrmDatabase db = NULL;
	extern XrmDatabase as_xrm_user_db;
	char *rm_string;


	if (raw == NULL || status == NULL || !get_flags (what, HINT_STARTUP))
		return;

	init_xrm ();

	if ((rm_string = XResourceManagerString (dpy)) != NULL)
		db = XrmGetStringDatabase (rm_string);
	else
		db = as_xrm_user_db;

	if (db) {
		get_afterstep_resources (db, status);
		if (db != as_xrm_user_db)
			XrmDestroyDatabase (db);
	}
}

CARD32
set_hints_window_opacity_percent (ASHints * clean, int opaque_percent)
{
	CARD32 res = NET_WM_WINDOW_OPACITY_OPAQUE;

	if (opaque_percent <= 0)
		res = 0;
	else if (opaque_percent < 100)
		res = (NET_WM_WINDOW_OPACITY_OPAQUE / 100) * opaque_percent;
	if (clean) {
		clean->window_opacity = res;
		set_flags (clean->flags, AS_WindowOpacity);
	}
	return res;
}


void
merge_asdb_hints (ASHints * clean, ASRawHints * raw,
									ASDatabaseRecord * db_rec, ASStatusHints * status,
									ASFlagType what)
{
	static ASFlagsXref asdb_startup_xref[] = {	/*Flag                    Set if Set    ,Clear if Set    , Set if Clear, Clear if Clear  */
		{STYLE_STICKY, AS_StartsSticky, 0, 0, AS_StartsSticky},
		{STYLE_START_ICONIC, AS_StartsIconic, 0, 0, AS_StartsIconic},
		{STYLE_PPOSITION, AS_StartPositionUser, 0, 0, AS_StartPosition},
		{STYLE_FULLSCREEN, AS_Fullscreen, 0, 0, AS_Fullscreen},
		{0, 0, 0, 0, 0}
	};
	static ASFlagsXref asdb_hints_xref[] = {	/*Flag                  Set if Set      ,Clear if Set    ,Set if Clear       ,Clear if Clear  */
		{STYLE_TITLE, AS_Titlebar, 0, 0, AS_Titlebar},
		{STYLE_CIRCULATE, 0, AS_DontCirculate, AS_DontCirculate, 0},
		{STYLE_WINLIST, 0, AS_SkipWinList, AS_SkipWinList, 0},
		{STYLE_ICON_TITLE, AS_IconTitle, 0, 0, AS_IconTitle},
		{STYLE_FOCUS, AS_AcceptsFocus, 0, 0, AS_AcceptsFocus},
		{STYLE_AVOID_COVER, AS_AvoidCover, 0, 0, AS_AvoidCover},
		{STYLE_VERTICAL_TITLE, AS_VerticalTitle, 0, 0, AS_VerticalTitle},
		{STYLE_HANDLES, AS_Handles, 0, 0, AS_Handles},
		{STYLE_FOCUS_ON_MAP, AS_FocusOnMap, 0, 0, AS_FocusOnMap},
		{STYLE_LONG_LIVING, 0, AS_ShortLived, AS_ShortLived, 0},
		{STYLE_IGNORE_CONFIG, AS_IgnoreConfigRequest, 0, 0,
		 AS_IgnoreConfigRequest},
		{STYLE_IGNORE_RESTACK, AS_IgnoreRestackRequest, 0, 0,
		 AS_IgnoreRestackRequest},
		{STYLE_CURRENT_VIEWPORT, AS_UseCurrentViewport, 0, 0,
		 AS_UseCurrentViewport},
		{STYLE_PAGER, AS_HitPager, 0, 0, AS_HitPager},
		{0, 0, 0, 0, 0}
	};

	LOCAL_DEBUG_CALLER_OUT ("0x%lX", what);

	if (db_rec == NULL)
		return;
	if (get_flags (what, HINT_STARTUP) && status != NULL) {
		if (get_flags (db_rec->set_data_flags, STYLE_STARTUP_DESK)) {
			status->desktop = db_rec->desk;
			set_flags (status->flags, AS_StartDesktop);
		}
		if (get_flags (db_rec->set_data_flags, STYLE_VIEWPORTX)) {
			status->viewport_x = db_rec->viewport_x;
			LOCAL_DEBUG_OUT ("viewport_x = %d", status->viewport_x);
			set_flags (status->flags, AS_StartViewportX);
		}
		if (get_flags (db_rec->set_data_flags, STYLE_VIEWPORTY)) {
			status->viewport_y = db_rec->viewport_y;
			set_flags (status->flags, AS_StartViewportY);
		}
		if (get_flags (db_rec->set_data_flags, STYLE_LAYER)) {
			status->layer = db_rec->layer;
			set_flags (status->flags, AS_StartLayer);
		}

		/*not exactly clean solution for the default geometry, but I don't see any other way : */
		if (raw != NULL
				&& get_flags (db_rec->set_data_flags, STYLE_DEFAULT_GEOMETRY)
				&& !get_flags (status->flags, AS_StartPositionUser)) {
			register ASGeometry *g = &(db_rec->default_geometry);

			if (get_flags (g->flags, WidthValue))
				status->width = g->width;
			else if (!get_flags (status->flags, AS_StartSize))	/* defaulting to the startup width */
				status->width = raw->placement.width;

			if (get_flags (g->flags, HeightValue))
				status->height = g->height;
			else if (!get_flags (status->flags, AS_StartSize))	/* defaulting to the startup height */
				status->height = raw->placement.height;

			set_flags (status->flags, AS_StartSize);

			if (get_flags (g->flags, XValue))
				status->x = get_flags (g->flags, XNegative) ?
						raw->scr->MyDisplayWidth - raw->border_width * 2 -
						status->width - g->x : g->x;
			else if (!get_flags (status->flags, AS_StartPosition))
				status->x = raw->placement.x;

			if (get_flags (g->flags, YValue))
				status->y = get_flags (g->flags, YNegative) ?
						raw->scr->MyDisplayHeight - raw->border_width * 2 -
						status->height - g->y : g->y;
			else if (!get_flags (status->flags, AS_StartPosition))
				status->y = raw->placement.y;

			if (get_flags (g->flags, YValue | XValue))
				set_flags (status->flags, AS_StartPosition | AS_StartPositionUser);
		}
		/* taking care of startup status flags : */
		decode_flags (&(status->flags), asdb_startup_xref, db_rec->set_flags,
									db_rec->flags);
	}
	if (get_flags (what, HINT_GENERAL)) {
		if (get_flags (db_rec->set_data_flags, STYLE_ICON))
			set_string_value (&(clean->icon_file), mystrdup (db_rec->icon_file),
												&(clean->flags), AS_Icon);
		/* the following should fix NoIcon handling in the database : */
		if (get_flags (db_rec->set_flags, STYLE_ICON)) {
			if (get_flags (db_rec->flags, STYLE_ICON))
				set_flags (clean->flags, AS_Icon);
			else
				clear_flags (clean->flags, AS_Icon);
		}
		if (get_flags (db_rec->set_data_flags, STYLE_BORDER_WIDTH)) {
			clean->border_width = db_rec->border_width;
			set_flags (clean->flags, AS_Border);
		}
		if (get_flags (db_rec->set_data_flags, STYLE_HANDLE_WIDTH)) {
			clean->handle_width = db_rec->resize_width;
			set_flags (clean->flags, AS_Handles);
		}
		if (get_flags (db_rec->set_data_flags, STYLE_GRAVITY)) {
			clean->gravity = db_rec->gravity;
			set_flags (clean->flags, AS_Gravity);
		}
		if (get_flags (db_rec->set_data_flags, STYLE_WINDOW_OPACITY))
			set_hints_window_opacity_percent (clean, db_rec->window_opacity);

		if (get_flags (db_rec->set_data_flags, STYLE_FRAME))
			set_string_value (&(clean->frame_name),
												mystrdup (db_rec->frame_name), &(clean->flags),
												AS_Frame);
		if (get_flags (db_rec->set_data_flags, STYLE_WINDOWBOX))
			set_string_value (&(clean->windowbox_name),
												mystrdup (db_rec->windowbox_name), &(clean->flags),
												AS_Windowbox);
		if (get_flags (db_rec->set_data_flags, STYLE_MYSTYLES)) {
			register int i;

			for (i = 0; i < BACK_STYLES; i++)
				if (db_rec->window_styles[i])
					set_string (&(clean->mystyle_names[i]),
											mystrdup (db_rec->window_styles[i]));
		}
		/* taking care of flags : */
		decode_flags (&(clean->flags), asdb_hints_xref, db_rec->set_flags,
									db_rec->flags);
		if (get_flags (db_rec->set_flags, STYLE_FOCUS)
				&& !get_flags (db_rec->flags, STYLE_FOCUS))
			clear_flags (clean->protocols, AS_DoesWmTakeFocus);

		clean->disabled_buttons = (~(db_rec->buttons)) & (db_rec->set_buttons);
	}
}

/* Don't forget to Cleanup after yourself : */
void destroy_hints (ASHints * clean, Bool reusable)
{
	if (clean) {
		register int i;

		for (i = 0; i < MAX_WINDOW_NAMES; i++)
			if (clean->names[i] == NULL)
				break;
			else
				free (clean->names[i]);

		if (clean->matched_name0)
			free (clean->matched_name0);

		if (clean->cmap_windows)
			free (clean->cmap_windows);
		if (clean->icon_file)
			free (clean->icon_file);
		if (clean->icon_argb)
			free (clean->icon_argb);
		if (clean->frame_name)
			free (clean->frame_name);
		if (clean->windowbox_name)
			free (clean->windowbox_name);
		for (i = 0; i < BACK_STYLES; i++)
			if (clean->mystyle_names[i])
				free (clean->mystyle_names[i]);

		if (clean->client_host)
			free (clean->client_host);
		if (clean->client_cmd)
			free (clean->client_cmd);

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

Bool
update_property_hints (Window w, Atom property, ASHints * hints,
											 ASStatusHints * status)
{
	ASRawHints raw;
	Bool changed = False;

	if (status == NULL || hints == NULL)
		return False;
	memset (&raw, 0x00, sizeof (ASRawHints));
	raw.wm_state_icon_win = status->icon_window;
	raw.scr = ASDefaultScr;
	if (handle_client_property_update (w, property, &raw)) {
		/* Here we are only interested in properties updtaed by the Window Manager : */
		if (property == _XA_WM_STATE) {
			unsigned long new_state =
					(raw.wm_state == IconicState) ? AS_Iconic : 0;

			if ((changed = ((new_state ^ (status->flags & AS_Iconic)) != 0 ||
											raw.wm_state_icon_win != status->icon_window))) {
				status->icon_window = raw.wm_state_icon_win;
				status->flags = (status->flags & (~AS_Iconic)) | new_state;
			}
		} else if (property == _XA_WIN_LAYER) {
			if ((changed = (raw.gnome_hints.layer != status->layer)))
				status->layer = raw.gnome_hints.layer;
		} else if (property == _XA_WIN_STATE) {
			changed = decode_gnome_state (raw.gnome_hints.state, hints, status);
		} else if (property == _XA_WIN_WORKSPACE) {
			if ((changed = (raw.gnome_hints.workspace != status->desktop)))
				status->desktop = raw.gnome_hints.workspace;
		} else if (property == _XA_NET_WM_DESKTOP) {	/* Extended WM-Hints : */
			if ((changed = (raw.extwm_hints.desktop != status->desktop)))
				status->desktop = raw.extwm_hints.desktop;
		} else if (property == _XA_NET_WM_STATE) {
			ASFlagType new_state = 0;

			decode_simple_flags (&new_state, extwm_state_xref,
													 raw.extwm_hints.state_flags);
			if ((changed =
					 (((status->flags & EXTWM_AFFECTED_STATE) ^ new_state) != 0)))
				status->flags = new_state | (status->flags & EXTWM_AFFECTED_STATE);
		}
		destroy_raw_hints (&raw, True);

	}
	return changed;
}

/* same as above only for window manager : */
void
update_cmd_line_hints (Window w, Atom property, ASHints * hints,
											 ASStatusHints * status)
{
	ASRawHints raw;

	memset (&raw, 0x00, sizeof (ASRawHints));
	raw.scr = ASDefaultScr;
	show_debug (__FILE__, __FUNCTION__, __LINE__,
							"trying to handle property change for WM_COMMAND");
	if (handle_manager_property_update (w, property, &raw)) {
		merge_command_line (hints, status, &raw);
		destroy_raw_hints (&raw, True);
	}
}

/* same as above only for window manager : */
Bool
update_property_hints_manager (Window w, Atom property,
															 ASSupportedHints * list, ASDatabase * db,
															 ASHints * hints, ASStatusHints * status)
{
	ASRawHints raw;
	Bool changed = False;

	memset (&raw, 0x00, sizeof (ASRawHints));
	raw.scr = ASDefaultScr;
	if (status)
		raw.wm_state_icon_win = status->icon_window;
	show_debug (__FILE__, __FUNCTION__, __LINE__,
							"trying to handle property change");
	if (handle_manager_property_update (w, property, &raw)) {
		ASHints clean;

		memset (&clean, 0x00, sizeof (ASHints));
		clean.res_name_idx = clean.res_class_idx = clean.icon_name_idx = -1;

		show_debug (__FILE__, __FUNCTION__, __LINE__,
								"property update handled");
		if (property == _XA_WM_STATE) {
			if (status) {
				unsigned long new_state =
						(raw.wm_state == IconicState) ? AS_Iconic : 0;

				if ((changed = ((new_state ^ (status->flags & AS_Iconic)) != 0 ||
												raw.wm_state_icon_win != status->icon_window))) {
					status->icon_window = raw.wm_state_icon_win;
					status->flags = (status->flags & (~AS_Iconic)) | new_state;
				}
			}
			return changed;
		}
		if (hints
				&& merge_hints (&raw, db, NULL, list, HINT_ANY, &clean,
												w) != NULL) {
			show_debug (__FILE__, __FUNCTION__, __LINE__, "hints merged");
			if (IsNameProp (property)) {
				int i;

				if (hints->names_encoding[0] == clean.names_encoding[0] &&
						mystrcmp (hints->names[0], clean.names[0]) != 0)
					changed = True;
				else if (mystrcmp (hints->res_name, clean.res_name) != 0)
					changed = True;
				else if (mystrcmp (hints->res_class, clean.res_class) != 0)
					changed = True;
				else if (mystrcmp (hints->icon_name, clean.icon_name) != 0)
					changed = True;

				for (i = 0; i <= MAX_WINDOW_NAMES; ++i)
					if (hints->names[i] != NULL) {
						free (hints->names[i]);
						hints->names[i] = NULL;
					} else
						break;

				for (i = 0; i <= MAX_WINDOW_NAMES; ++i) {
					hints->names_encoding[i] = clean.names_encoding[i];
					hints->names[i] = clean.names[i];
					clean.names[i] = NULL;
				}
				hints->res_name = clean.res_name;
				hints->res_name_idx = clean.res_name_idx;
				hints->res_class = clean.res_class;
				hints->res_class_idx = clean.res_class_idx;
				hints->icon_name = clean.icon_name;
				hints->icon_name_idx = clean.icon_name_idx;
				show_debug (__FILE__, __FUNCTION__, __LINE__, "names set");

				/* Must not do that as it requires sugnificant changes in decorations and stuff :
				 * and should be done only when FollowTitleChanges is requested */
				/* hints->flags = clean.flags ;
				   hints->function_mask = clean.function_mask ;
				 */
			} else if (property == XA_WM_HINTS) {

			} else if (property == XA_WM_NORMAL_HINTS) {

			} else if (property == _XA_WM_PROTOCOLS) {

			} else if (property == _XA_WM_COLORMAP_WINDOWS) {
			}
		}
		destroy_hints (&clean, True);
		destroy_raw_hints (&raw, True);
	} else
		show_debug (__FILE__, __FUNCTION__, __LINE__,
								"failed to handle property update");

	return changed;
}

static Bool compare_strings (char *str1, char *str2)
{
	if (str1 != NULL && str2 != NULL)
		return (strcmp (str1, str2) != 0);
	return (str1 != str2);
}

Bool compare_names (ASHints * old, ASHints * hints)
{
	register int i;

	if (old != NULL && hints != NULL) {
		for (i = 0; i < MAX_WINDOW_NAMES; i++)
			if (compare_strings (old->names[i], hints->names[i]))
				return True;
			else if (old->names[i] == NULL)
				break;
	} else if (old != hints)
		return True;

	return False;
}

ASFlagType compare_hints (ASHints * old, ASHints * hints)
{
	ASFlagType changed = 0;

	if (old != NULL && hints != NULL) {
		if (compare_strings (old->names[0], hints->names[0]))
			set_flags (changed, AS_HintChangeName);
		if (compare_strings (old->res_class, hints->res_class))
			set_flags (changed, AS_HintChangeClass);
		if (compare_strings (old->res_name, hints->res_name))
			set_flags (changed, AS_HintChangeResName);
		if (compare_strings (old->icon_name, hints->icon_name))
			set_flags (changed, AS_HintChangeIconName);
	} else if (old != hints)
		changed = AS_HintChangeEverything;
	return changed;
}

ASFlagType function2mask (int function)
{
	static ASFlagType as_function_masks[F_PIN_MENU + 1 -
																			F_WINDOW_FUNC_START] = {
		AS_FuncMove,								/* F_MOVE,               30 */
		AS_FuncResize,							/* F_RESIZE,             */
		0,													/* F_RAISE,              */
		0,													/* F_LOWER,              */
		0,													/* F_RAISELOWER,         */
		0,													/* F_PUTONTOP,           */
		0,													/* F_PUTONBACK,          */
		0,													/* F_SETLAYER,           */
		0,													/* F_TOGGLELAYER,        */
		0,													/* F_SHADE,              */
		AS_FuncKill,								/* F_DELETE,             */
		0,													/* F_DESTROY,            */
		AS_FuncClose,								/* F_CLOSE,              */
		AS_FuncMinimize,						/* F_ICONIFY,            */
		AS_FuncMaximize,						/* F_MAXIMIZE,           */
		AS_FuncMaximize,						/* F_FULLSCREEN,         */
		0,													/* F_STICK,              */
		0,													/* F_FOCUS,              */
		0,													/* F_CHANGEWINDOW_UP     */
		0,													/* F_CHANGEWINDOW_DOWN   */
		0,													/* F_GOTO_BOOKMARK     */
		0,													/* F_GETHELP,            */
		0,													/* F_PASTE_SELECTION,    */
		0,													/* F_CHANGE_WINDOWS_DESK, */
		0,													/* F_BOOKMARK_WINDOW,  */
		AS_FuncPinMenu							/* F_PIN_MENU           */
	};

	if (function == F_POPUP)
		return AS_FuncPopup;
	if (function <= F_WINDOW_FUNC_START || function > F_PIN_MENU)
		return 0;
	return as_function_masks[function - (F_WINDOW_FUNC_START + 1)];
}

/****************************************************************************
 * Initial placement/anchor management code
 ****************************************************************************/

void
constrain_size (ASHints * hints, ASStatusHints * status, int max_width,
								int max_height)
{
	int minWidth = 1, minHeight = 1;
	int xinc = 1, yinc = 1, delta;
	int baseWidth = 0, baseHeight = 0;
	int clean_width =
			status->width - (status->frame_size[FR_W] +
											 status->frame_size[FR_E]);
	int clean_height =
			status->height - (status->frame_size[FR_N] +
												status->frame_size[FR_S]);

	if (get_flags (hints->flags, AS_MinSize)) {
		if (minWidth < hints->min_width)
			minWidth = hints->min_width;
		if (minHeight < hints->min_height)
			minHeight = hints->min_height;
	} else if (get_flags (hints->flags, AS_BaseSize)) {
		if (minWidth < hints->base_width)
			minWidth = hints->base_width;
		if (minHeight < hints->base_height)
			minHeight = hints->base_height;
	}

	if (get_flags (hints->flags, AS_BaseSize)) {
		baseWidth = hints->base_width;
		baseHeight = hints->base_height;
	} else if (get_flags (hints->flags, AS_MinSize)) {
		baseWidth = minWidth;
		baseHeight = minHeight;
	}

	if (get_flags (hints->flags, AS_MaxSize)
			&& !get_flags (status->flags, AS_Fullscreen)) {
		if (max_width == 0 || max_width > hints->max_width)
			max_width = hints->max_width;
		if (max_height == 0 || max_height > hints->max_height)
			max_height = hints->max_height;
	} else {
		if (max_width == 0)
			max_width = MAX ((unsigned int)minWidth, clean_width);
		if (max_height == 0)
			max_height = MAX ((unsigned int)minHeight, clean_height);
	}
	LOCAL_DEBUG_OUT
			("base_size = %dx%d, min_size = %dx%d, curr_size = %dx%d, max_size = %dx%d",
			 baseWidth, baseHeight, minWidth, minHeight, clean_width,
			 clean_height, max_width, max_height);
	/* First, clamp to min and max values  */
	clean_width = FIT_IN_RANGE (minWidth, clean_width, max_width);
	clean_height = FIT_IN_RANGE (minHeight, clean_height, max_height);
	LOCAL_DEBUG_OUT ("clumped_size = %dx%d", clean_width, clean_height);

	/* Second, fit to base + N * inc */
	if (get_flags (hints->flags, AS_SizeInc)) {
		xinc = hints->width_inc;
		yinc = hints->height_inc;
		clean_width = (((clean_width - baseWidth) / xinc) * xinc) + baseWidth;
		clean_height =
				(((clean_height - baseHeight) / yinc) * yinc) + baseHeight;
		LOCAL_DEBUG_OUT ("inced_size = %dx%d", clean_width, clean_height);
	}

	/* Third, adjust for aspect ratio */
#define maxAspectX hints->max_aspect.x
#define maxAspectY hints->max_aspect.y
#define minAspectX hints->min_aspect.x
#define minAspectY hints->min_aspect.y
#define makemult(a,b) ((b==1) ? (a) : (((int)((a)/(b))) * (b)) )

	/* The math looks like this:

	 * minAspectX    dwidth     maxAspectX
	 * ---------- <= ------- <= ----------
	 * minAspectY    dheight    maxAspectY
	 *
	 * If that is multiplied out, then the width and height are
	 * invalid in the following situations:
	 *
	 * minAspectX * dheight > minAspectY * dwidth
	 * maxAspectX * dheight < maxAspectY * dwidth
	 *
	 */

	if (get_flags (hints->flags, AS_Aspect)) {
		if ((minAspectX * clean_height > minAspectY * clean_width)) {
			delta =
					makemult (minAspectX * clean_height / minAspectY - clean_width,
										xinc);
			if (clean_width + delta <= max_width)
				clean_width += delta;
			else {
				delta =
						makemult (clean_height - clean_width * minAspectY / minAspectX,
											yinc);
				if (clean_height - delta >= minHeight)
					clean_height -= delta;
			}
		}
		if (maxAspectX * clean_height < maxAspectY * clean_width) {
			delta =
					makemult (clean_width * maxAspectY / maxAspectX - clean_height,
										yinc);
			if (clean_height + delta <= max_height)
				clean_height += delta;
			else {
				delta =
						makemult (clean_width - maxAspectX * clean_height / maxAspectY,
											xinc);
				if (clean_width - delta >= minWidth)
					clean_width -= delta;
			}
		}
		LOCAL_DEBUG_OUT ("aspected_size = %dx%d", clean_width, clean_height);
	}
	status->width =
			clean_width + status->frame_size[FR_W] + status->frame_size[FR_E];
	status->height =
			clean_height + status->frame_size[FR_N] + status->frame_size[FR_S];
}

static int _as_gravity_offsets[11][2] = {
	{0, 0},												/* ForgetGravity */
	{-1, -1},											/* NorthWestGravity */
	{2, -1},											/* NorthGravity */
	{1, -1},											/* NorthEastGravity */
	{-1, 2},											/* WestGravity */
	{0, 0},												/* CenterGravity */
	{1, 2},												/* EastGravity */
	{-1, 1},											/* SouthWestGravity */
	{2, 1},												/* SouthGravity */
	{1, 1},												/* SouthEastGravity */
	{2, 2}												/* StaticGravity */
};

void get_gravity_offsets (ASHints * hints, int *xp, int *yp)
{
	register int g = NorthWestGravity;

	if (get_flags (hints->flags, AS_Gravity))
		g = hints->gravity;

	if (g < ForgetGravity || g > StaticGravity)
		*xp = *yp = 0;
	else {
		*xp = (int)_as_gravity_offsets[g][0];
		*yp = (int)_as_gravity_offsets[g][1];
	}
}

int
translate_asgeometry (ScreenInfo * scr, ASGeometry * asg, int *px, int *py,
											unsigned int *pwidth, unsigned int *pheight)
{
	int grav = NorthWestGravity;
	unsigned int width = 1, height = 1;

	if (scr == NULL)
		scr = ASDefaultScr;

	if (asg) {
		if (get_flags (asg->flags, XNegative)) {
			if (get_flags (asg->flags, YNegative))
				grav = SouthEastGravity;
			else
				grav = NorthEastGravity;
		} else if (get_flags (asg->flags, YNegative))
			grav = SouthWestGravity;
	}
	if (asg && get_flags (asg->flags, WidthValue)) {
		width = asg->width;
		if (pwidth)
			*pwidth = width;
	} else if (pwidth)
		width = *pwidth;

	if (asg && get_flags (asg->flags, HeightValue)) {
		height = asg->height;
		if (pheight)
			*pheight = height;
	} else if (pheight)
		height = *pheight;
	if (asg) {
		if (get_flags (asg->flags, XValue) && px) {
			if (get_flags (asg->flags, XNegative)) {
				if (asg->x <= 0)
					*px = scr->MyDisplayWidth + asg->x;
				else
					*px = scr->MyDisplayWidth - asg->x;
				*px -= width;
			} else
				*px = asg->x;
		}
		if (get_flags (asg->flags, YValue) && py) {
			if (get_flags (asg->flags, YNegative)) {
				if (asg->x <= 0)
					*py = scr->MyDisplayHeight + asg->y;
				else
					*py = scr->MyDisplayHeight - asg->y;
				*py -= height;
			} else
				*py = asg->y;
		}
	} else {
		if (px)
			*px = 0;
		if (py)
			*py = 0;
	}

	return grav;
}

int
make_anchor_pos (ASStatusHints * status, int pos, int size, int vpos,
								 int grav, int max_pos)
{																/* anchor position is always in virtual coordinates */
	int bw = 0;

	if (get_flags (status->flags, AS_StartBorderWidth))
		bw = status->border_width;

	/* position of the sticky window is stored in real coordinates */
	if (!get_flags (status->flags, AS_Sticky))
		pos += vpos;

	/* trying to place window partly on screen, unless user really wants it */
	if (!get_flags (status->flags, AS_StartPositionUser)) {
		if (pos > max_pos)
			pos = max_pos;
		else if (pos + size < 16)
			pos = 16 - size;
	}

	switch (grav) {
	case 0:											/* Center */
		pos += bw + (size >> 1);
		break;
	case 1:											/* East */
		pos += bw + size + bw;
		break;
	case 2:											/* Static */
		pos += bw;
		break;
	default:											/* West */
		break;
	}
	return pos;
}

/* reverse transformation */
int
make_status_pos (ASStatusHints * status, int pos, unsigned int size,
								 int vpos, int grav)
{																/* status position is always in real coordinates */
	unsigned int bw = 0;

	if (get_flags (status->flags, AS_StartBorderWidth))
		bw = status->border_width;

	/* position of the sticky window is stored in real coordinates */
	if (!get_flags (status->flags, AS_Sticky))
		pos -= vpos;

	switch (grav) {
	case 0:											/* Center */
		pos -= bw + (size >> 1);
		break;
	case 1:											/* East */
		pos -= bw + size + bw;
		break;
	case 2:											/* Static */
		pos -= bw;
		break;
	default:											/* West */
		break;
	}
	return pos;
}

void
make_detach_pos (ASHints * hints, ASStatusHints * status,
								 XRectangle * anchor, int *detach_x, int *detach_y)
{
	unsigned int bw = 0;
	int x = 0, y = 0;
	int grav_x, grav_y;

	if (hints == NULL || status == NULL || anchor == NULL)
		return;

	if (get_flags (status->flags, AS_StartBorderWidth))
		bw = status->border_width;

	get_gravity_offsets (hints, &grav_x, &grav_y);

	/* position of the sticky window is stored in real coordinates */
	x = anchor->x;
	y = anchor->y;
	if (!get_flags (status->flags, AS_Sticky)) {
		x -= status->viewport_x;
		y -= status->viewport_y;
	}

	if (detach_x) {
		APPLY_GRAVITY (grav_x, x, anchor->width, bw, bw);
		*detach_x = x;
	}
	if (detach_y) {
		APPLY_GRAVITY (grav_y, y, anchor->height, bw, bw);
		*detach_y = y;
	}
}

/***** New Code : *****************************************************/

/*
 * frame_size[] must be set to the real frame decoration size if window
 * is being moved by us or to border_width if moved by client.
 *
 * width and height must be that of a client
 *
 * x, y are of top left corner of the client.
 * 		North  		Central 		South 			Static
 * y	-FR_N       +height/2       +height+FR_S    +0
 *
 * 		West   		Central 		East  			Static
 * x	-FR_W       +width/2       +width+FR_E      +0
 */

void
status2anchor (XRectangle * anchor, ASHints * hints,
							 ASStatusHints * status, int vwidth, int vheight)
{
	if (get_flags (status->flags, AS_Size)) {
		int w, h;

		constrain_size (hints, status, vwidth, vheight);
		w = (int)status->width - ((int)status->frame_size[FR_W] +
															(int)status->frame_size[FR_E]);
		if (w > 0)
			anchor->width = w;
		h = (int)status->height - ((int)status->frame_size[FR_N] +
															 (int)status->frame_size[FR_S]);
		if (h > 0)
			anchor->height = h;
	}

	if (get_flags (status->flags, AS_Position)) {
		int grav_x = -1, grav_y = -1;
		int offset;

		get_gravity_offsets (hints, &grav_x, &grav_y);

		LOCAL_DEBUG_OUT
				("grav_x = %d, width = %d, bw1 = %d, bw2 = %d, status_x = %d",
				 grav_x, anchor->width, status->frame_size[FR_W],
				 status->frame_size[FR_E], status->x);
		offset = 0;
		APPLY_GRAVITY (grav_x, offset, anchor->width,
									 status->frame_size[FR_W] + status->frame_border_width,
									 status->frame_size[FR_E] + status->frame_border_width);
		anchor->x = status->x - offset;

		LOCAL_DEBUG_OUT
				("grav_y = %d, height = %d, bw1 = %d, bw2 = %d, status_y = %d",
				 grav_y, anchor->height, status->frame_size[FR_N],
				 status->frame_size[FR_S], status->y);

		offset = 0;
		APPLY_GRAVITY (grav_y, offset, anchor->height,
									 status->frame_size[FR_N] + status->frame_border_width,
									 status->frame_size[FR_S] + status->frame_border_width);
		anchor->y = status->y - offset;

		LOCAL_DEBUG_OUT ("anchor = %+d%+d", anchor->x, anchor->y);
		if (!get_flags (status->flags, AS_Sticky)) {
			anchor->x += (int)status->viewport_x;
			anchor->y += (int)status->viewport_y;
		}
		LOCAL_DEBUG_OUT ("anchor = %+d%+d", anchor->x, anchor->y);
	}
}

void
anchor2status (ASStatusHints * status, ASHints * hints,
							 XRectangle * anchor)
{
	int grav_x = -1, grav_y = -1;
	int offset;

	status->width =
			anchor->width + status->frame_size[FR_W] + status->frame_size[FR_E];
	status->height =
			anchor->height + status->frame_size[FR_N] + status->frame_size[FR_S];
	set_flags (status->flags, AS_Size);

	get_gravity_offsets (hints, &grav_x, &grav_y);

	LOCAL_DEBUG_OUT
			("grav_x = %d, width = %d, bw1 = %d, bw2 = %d, anchor_x = %d",
			 grav_x, anchor->width, status->frame_size[FR_W],
			 status->frame_size[FR_E], anchor->x);
	offset = 0;
	APPLY_GRAVITY (grav_x, offset, anchor->width,
								 status->frame_size[FR_W] + status->frame_border_width,
								 status->frame_size[FR_E] + status->frame_border_width);
	status->x = anchor->x + offset;

	LOCAL_DEBUG_OUT
			("grav_y = %d, height = %d, bw1 = %d, bw2 = %d, anchor_y = %d",
			 grav_y, anchor->height, status->frame_size[FR_N],
			 status->frame_size[FR_S], anchor->y);

	offset = 0;
	APPLY_GRAVITY (grav_y, offset, anchor->height,
								 status->frame_size[FR_N] + status->frame_border_width,
								 status->frame_size[FR_S] + status->frame_border_width);
	status->y = anchor->y + offset;

	LOCAL_DEBUG_OUT ("status = %+d%+d", status->x, status->y);
	if (!get_flags (status->flags, AS_Sticky)) {
		status->x -= (int)status->viewport_x;
		status->y -= (int)status->viewport_y;
	}
	LOCAL_DEBUG_OUT ("status = %+d%+d", status->x, status->y);
	set_flags (status->flags, AS_Position);
}


/***** Old Code : *****************************************************/

ASFlagType
change_placement (ScreenInfo * scr, ASHints * hints,
									ASStatusHints * status, XPoint * anchor,
									ASStatusHints * new_status, int vx, int vy,
									ASFlagType what)
{
	ASFlagType todo = 0;
	int grav_x, grav_y;
	register int max_x = scr->MyDisplayWidth - 16;
	register int max_y = scr->MyDisplayHeight - 16;
	int new_x, new_y;

	if (!get_flags (what, AS_Size | AS_Position) || status == NULL
			|| new_status == NULL)
		return 0;

	new_x = status->x - (vx - status->viewport_x);
	new_y = status->y - (vy - status->viewport_y);
	get_gravity_offsets (hints, &grav_x, &grav_y);
	if (!get_flags (status->flags, AS_Sticky)) {
		max_x += scr->VxMax;
		max_y += scr->VyMax;
	}

	if (get_flags (what, AS_Size)) {
		constrain_size (hints, new_status, scr->VxMax + scr->MyDisplayWidth,
										scr->VyMax + scr->MyDisplayHeight);
		if (new_status->width != status->width) {
			status->width = new_status->width;
			set_flags (todo, TODO_RESIZE_X);
			if (grav_x == 0 || grav_x == 1)
				set_flags (todo, TODO_MOVE_X);
		}
		if (new_status->height != status->height) {
			status->height = new_status->height;
			set_flags (todo, TODO_RESIZE_Y);
			if (grav_y == 0 || grav_y == 1)
				set_flags (todo, TODO_MOVE_Y);
		}
	}

	if (!get_flags (what, AS_BorderWidth)
			&& new_status->border_width != status->border_width) {
		status->border_width = new_status->border_width;
		set_flags (todo, TODO_MOVE);
	}

	if (!get_flags (status->flags, AS_Sticky)
			&& (status->viewport_x != vx || status->viewport_y != vy))
		set_flags (todo, TODO_MOVE);

	if (get_flags (what, AS_Position)) {
		if (status->x != new_status->x) {
			new_x = new_status->x;
			set_flags (todo, TODO_MOVE_X);
		} else
				if (make_anchor_pos
						(status, new_x, status->width, vx, max_x, grav_x) != anchor->x)
			set_flags (todo, TODO_MOVE_X);

		if (status->y != new_status->y) {
			new_y = new_status->y;
			set_flags (todo, TODO_MOVE_Y);
		} else
				if (make_anchor_pos
						(status, new_y, status->height, vy, max_y,
						 grav_y) != anchor->y)
			set_flags (todo, TODO_MOVE_Y);
	}
	if (get_flags (todo, TODO_MOVE_X)) {
		anchor->x =
				make_anchor_pos (status, new_x, status->width, vx, max_x, grav_x);
		status->x =
				make_status_pos (status, anchor->x, status->width, vx, grav_x);
	}
	if (get_flags (todo, TODO_MOVE_Y)) {
		anchor->y =
				make_anchor_pos (status, new_y, status->height, vy, max_y, grav_y);
		status->y =
				make_status_pos (status, anchor->y, status->height, vy, grav_y);
	}
	if (!get_flags (status->flags, AS_Sticky)) {
		status->viewport_x = vx;
		status->viewport_y = vy;
	}

	return what;
}

int
calculate_viewport (int *ppos, int size, int scr_vpos, int scr_size,
										int max_viewport)
{
	int viewport = -1;
	int pos = ppos ? *ppos : 0;

	if (pos >= scr_size)
		viewport = pos / scr_size;
	else if (pos + size < 0) {
		if (pos + scr_vpos > 0)
			viewport = (scr_vpos + pos) / scr_size;
		else
			viewport = 0;
	} else
		return scr_vpos;

	viewport *= scr_size;
	viewport = MIN (viewport, max_viewport);
	if (ppos)
		*ppos = pos + (scr_vpos - viewport);
	return viewport;
}

int
gravitate_position (int pos, unsigned int size, unsigned int scr_size,
										int grav, unsigned int bw)
{
	if (pos < 0 || pos + size > scr_size)
		return pos;
	if (grav == 1)								/* East or South gravity */
		pos = (int)scr_size - (int)(pos + bw + size + bw);

	return pos;
}

/***********************************************************************************
 * we build a command line here, so we can restart an app with exactly the same
 * parameters:
 ***********************************************************************************/
static char *format_geometry_string (int x, int y, int width, int height,
																		 int unit_width, int unit_height,
																		 int screen_size_x, int screen_size_y,
																		 int gravity)
{
	char *g =
			safemalloc (15 + 1 + 15 + 1 + 15 + 1 + 15 + 1 /* large enough */ );
	char x_sign = '+';
	char y_sign = '+';
	int x2 = x + width, y2 = y + height;

#define FGS_CHECK_SIGN(d)  do{if(d<0){d = 0;/* d##_sign = '-';*/} }while(0)
#define FGS_APPLY_NEGATIVE_GRAV(d)  do{ d = screen_size_##d - d##2; if(d<0) d=0; d##_sign = '-' ;}while(0)
	if (gravity == SouthWestGravity) {
		FGS_APPLY_NEGATIVE_GRAV (y);
		FGS_CHECK_SIGN (x);
	} else if (gravity == SouthEastGravity) {
		FGS_APPLY_NEGATIVE_GRAV (x);
		FGS_APPLY_NEGATIVE_GRAV (y);
	} else if (gravity == NorthEastGravity) {
		FGS_APPLY_NEGATIVE_GRAV (x);
		FGS_CHECK_SIGN (y);
	} else {
		FGS_CHECK_SIGN (x);
		FGS_CHECK_SIGN (y);
	}
#undef FGS_CHECK_SIGN
#undef FGS_APPLY_NEGATIVE_GRAV

	sprintf (g, "%dx%d%c%d%c%d ", unit_width, unit_height, x_sign, x, y_sign,
					 y);
	return g;
}

char *make_client_geometry_string (ScreenInfo * scr, ASHints * hints,
																	 ASStatusHints * status,
																	 XRectangle * anchor, int vx, int vy,
																	 char **pure_geometry)
{
	char *geom = NULL;
	int detach_x, detach_y;
	int grav_x, grav_y;
	int bw = 0;
	int width, height, unit_width, unit_height;

	if (hints == NULL || status == NULL || anchor == NULL)
		return NULL;

	if (get_flags (status->flags, AS_StartBorderWidth))
		bw = status->border_width;

	make_detach_pos (hints, status, anchor, &detach_x, &detach_y);

	vx = calculate_viewport (&detach_x, anchor->width, vx,
													 scr->MyDisplayWidth, scr->VxMax);
	vy = calculate_viewport (&detach_y, anchor->height, vy,
													 scr->MyDisplayHeight, scr->VyMax);

	get_gravity_offsets (hints, &grav_x, &grav_y);

	detach_x =
			gravitate_position (detach_x, anchor->width, bw, scr->MyDisplayWidth,
													grav_x);
	detach_y =
			gravitate_position (detach_y, anchor->height, bw,
													scr->MyDisplayHeight, grav_y);

	width = anchor->width;
	height = anchor->height;

	unit_width =
			(hints->width_inc >
			 0) ? (width - hints->base_width) / hints->width_inc : width;
	unit_height =
			(hints->height_inc >
			 0) ? (height - hints->base_height) / hints->height_inc : height;

	if (pure_geometry) {
		*pure_geometry =
				format_geometry_string (detach_x, detach_y, width, height, width,
																height, scr->MyDisplayWidth,
																scr->MyDisplayHeight, hints->gravity);
	}
	geom =
			format_geometry_string (detach_x, detach_y, width, height,
															unit_width, unit_height, scr->MyDisplayWidth,
															scr->MyDisplayHeight, hints->gravity);;
	return geom;
}

char *make_client_command (ScreenInfo * scr, ASHints * hints,
													 ASStatusHints * status, XRectangle * anchor,
													 int vx, int vy)
{
	char *client_cmd = NULL;
	char *geom =
			make_client_geometry_string (scr, hints, status, anchor, vx, vy,
																	 NULL);

	if (hints->client_cmd == NULL || geom == NULL)
		return NULL;

	/* supplying everything as : -xrm "afterstep*desk:N" */
	client_cmd =
			safemalloc (strlen (hints->client_cmd) + 11 + strlen (geom) + 1 + 1);
	sprintf (client_cmd, "%s -geometry %s ", hints->client_cmd, geom);
	/*, status->desktop, status->layer, status->viewport_x, status->viewport_y */
	return client_cmd;
}

/***********************************************************************
 * Setrting Hints on the window :
 ***********************************************************************/
static Bool
client_hints2wm_hints (XWMHints * wm_hints, ASHints * hints,
											 ASStatusHints * status)
{
	memset (wm_hints, 0x00, sizeof (XWMHints));

	if (status) {
		wm_hints->flags = StateHint;
		if (get_flags (status->flags, AS_StartsIconic))
			wm_hints->initial_state = IconicState;
		else
			wm_hints->initial_state = WithdrawnState;
	}
	/* does this application rely on the window manager to get keyboard input? */
	if (get_flags (hints->flags, AS_AcceptsFocus)) {
		set_flags (wm_hints->flags, InputHint);
		wm_hints->input = True;
	}

	/* window to be used as icon */
	if (get_flags (hints->client_icon_flags, AS_ClientIcon)) {
		if (!get_flags (hints->client_icon_flags, AS_ClientIconPixmap)) {
			wm_hints->icon_window = hints->icon.window;
			set_flags (wm_hints->flags, IconWindowHint);
		} else {										/* pixmap to be used as icon */
			set_flags (wm_hints->flags, IconPixmapHint);
			wm_hints->icon_pixmap = hints->icon.pixmap;

			if (hints->icon_mask) {		/* pixmap to be used as mask for icon_pixmap */
				set_flags (wm_hints->flags, IconMaskHint);
				wm_hints->icon_mask = hints->icon_mask;
			}
		}
	}

	/* initial position of icon */
	if (get_flags (hints->client_icon_flags, AS_ClientIconPosition)) {
		set_flags (wm_hints->flags, IconPositionHint);
		wm_hints->icon_x = hints->icon_x;
		wm_hints->icon_y = hints->icon_y;
	}

	if (hints->group_lead) {
		set_flags (wm_hints->flags, WindowGroupHint);
		wm_hints->window_group = hints->group_lead;
	}

/*	if( get_flags( hints->flags, AS_AvoidCover ) )
		set_flags( wm_hints->flags, UrgencyHint );
 */
	return (wm_hints->flags != StateHint
					|| wm_hints->initial_state == IconicState);
}

static Bool
client_hints2size_hints (XSizeHints * size_hints, ASHints * hints,
												 ASStatusHints * status)
{
	memset (size_hints, 0x00, sizeof (XSizeHints));
	if (status) {
		if (get_flags (status->flags, AS_StartPosition | AS_StartPositionUser)) {
			if (get_flags (status->flags, AS_StartPositionUser))
				set_flags (size_hints->flags, USPosition);
			else
				set_flags (size_hints->flags, PPosition);
		}

		if (get_flags (status->flags, AS_StartSize | AS_StartSizeUser)) {
			if (get_flags (status->flags, AS_StartSizeUser))
				set_flags (size_hints->flags, USSize);
			else
				set_flags (size_hints->flags, PSize);
		}
	}
	if (get_flags (hints->flags, AS_MinSize)) {
		size_hints->min_width = hints->min_width;
		size_hints->min_height = hints->min_height;
		set_flags (size_hints->flags, PMinSize);
	}
	if (get_flags (hints->flags, AS_MaxSize)) {
		size_hints->max_width = hints->max_width;
		size_hints->max_height = hints->max_height;
		set_flags (size_hints->flags, PMaxSize);
	}
	if (get_flags (hints->flags, AS_SizeInc)) {
		size_hints->width_inc = hints->width_inc;
		size_hints->height_inc = hints->height_inc;
		set_flags (size_hints->flags, PResizeInc);
	}
	if (get_flags (hints->flags, AS_Aspect)) {
		size_hints->min_aspect.x = hints->min_aspect.x;
		size_hints->min_aspect.y = hints->min_aspect.y;
		size_hints->max_aspect.x = hints->max_aspect.x;
		size_hints->max_aspect.y = hints->max_aspect.y;
		set_flags (size_hints->flags, PAspect);
	}
	if (get_flags (hints->flags, AS_BaseSize)) {
		size_hints->base_width = hints->base_width;
		size_hints->base_height = hints->base_height;
		set_flags (size_hints->flags, PBaseSize);
	}
	if (get_flags (hints->flags, AS_Gravity)) {
		size_hints->win_gravity = hints->gravity;
		set_flags (size_hints->flags, PWinGravity);
	}
	return (size_hints->flags != 0);
}

static Bool
client_hints2wm_protocols (ASFlagType * protocols, ASHints * hints)
{
	if (protocols == NULL || hints == NULL)
		return False;

	*protocols =
			hints->protocols & (AS_DoesWmDeleteWindow | AS_DoesWmTakeFocus);
	return (*protocols != 0);
}

static Bool
client_hints2motif_hints (MwmHints * motif_hints, ASHints * hints,
													ASStatusHints * status)
{
	ASFlagType tmp;

	memset (motif_hints, 0x00, sizeof (MwmHints));

	if (status) {
		if (get_flags (status->flags, AS_StartLayer)) {
			if (status->layer == AS_LayerUrgent
					&& get_flags (status->flags, AS_StartsSticky)) {
				motif_hints->inputMode = MWM_INPUT_SYSTEM_MODAL;
				set_flags (motif_hints->flags, MWM_HINTS_INPUT_MODE);
			} else if (status->layer == AS_LayerTop) {
				motif_hints->inputMode = MWM_INPUT_SYSTEM_MODAL;
				set_flags (motif_hints->flags, MWM_INPUT_FULL_APPLICATION_MODAL);
			}
		}
	}
	/* finally we can apply conglomerated hints to our flags : */
	tmp = motif_hints->decorations;
	encode_simple_flags (&tmp, mwm_decor_xref, hints->flags);
	encode_simple_flags (&tmp, mwm_decor_func_xref, hints->function_mask);
	motif_hints->decorations = tmp;
	tmp = motif_hints->functions;
	encode_simple_flags (&tmp, mwm_func_xref, hints->function_mask);
	motif_hints->functions = tmp;

	check_motif_hints_sanity (motif_hints);

	if (motif_hints->functions != 0)
		set_flags (motif_hints->flags, MWM_HINTS_FUNCTIONS);
	if (motif_hints->decorations != 0)
		set_flags (motif_hints->flags, MWM_HINTS_DECORATIONS);

	return (motif_hints->flags != 0);
}

static Bool
client_hints2gnome_hints (GnomeHints * gnome_hints, ASHints * hints,
													ASStatusHints * status)
{
	ASFlagType tmp = 0;

	memset (gnome_hints, 0x00, sizeof (GnomeHints));

	if (status) {
		if (get_flags (status->flags, AS_StartLayer)) {
			gnome_hints->layer = (status->layer << 1) + WIN_LAYER_NORMAL;
			set_flags (gnome_hints->flags, GNOME_LAYER);
		}

		if (get_flags (status->flags, AS_StartDesktop)) {
			gnome_hints->workspace = status->desktop;
			set_flags (gnome_hints->flags, GNOME_WORKSPACE);
		}
	}
	gnome_hints->state = encode_gnome_state (hints, status);
	if (gnome_hints->state != 0)
		set_flags (gnome_hints->flags, GNOME_STATE);

	tmp = gnome_hints->hints;
	encode_simple_flags (&tmp, gnome_hints_xref, hints->flags);
	gnome_hints->hints = tmp;
	if (gnome_hints->hints != 0)
		set_flags (gnome_hints->flags, GNOME_HINTS);

	return (gnome_hints->flags != 0);
}

static Bool
client_hints2extwm_hints (ExtendedWMHints * extwm_hints, ASHints * hints,
													ASStatusHints * status)
{
	memset (extwm_hints, 0x00, sizeof (ExtendedWMHints));

	if (status) {
		if (get_flags (status->flags, AS_StartsSticky)) {
			extwm_hints->desktop = 0xFFFFFFFF;
			set_flags (extwm_hints->flags, EXTWM_DESKTOP);
		} else if (get_flags (status->flags, AS_StartDesktop)) {
			extwm_hints->desktop = status->desktop;
			set_flags (extwm_hints->flags, EXTWM_DESKTOP);
		}
		/* window state hints : */
		if (get_flags (status->flags, AS_StartLayer)
				&& status->layer >= AS_LayerTop) {
			set_flags (extwm_hints->state_flags, EXTWM_StateModal);
			set_flags (extwm_hints->flags, EXTWM_StateSet);
			encode_simple_flags (&(extwm_hints->state_flags), extwm_state_xref,
													 status->flags);
		}
		if (get_flags (status->flags, AS_SkipWinList)) {
			set_flags (extwm_hints->state_flags, EXTWM_StateSkipTaskbar);
			set_flags (extwm_hints->flags, EXTWM_StateSet);
		}

		/* window type hints : */
		if (get_flags (status->flags, AS_StartLayer)
				&& status->layer != AS_LayerNormal) {
			register int i;

			for (i = 0; extwm_types_start_properties[i][0] != 0; i++)
				if (status->layer == extwm_types_start_properties[i][1] &&
						(get_flags (status->flags, extwm_types_start_properties[i][2])
						 || extwm_types_start_properties[i][2] == 0)) {
					set_flags (extwm_hints->type_flags,
										 extwm_types_start_properties[i][0]);
				}
		}
	}

	encode_simple_flags (&(extwm_hints->type_flags), extwm_type_xref,
											 hints->flags);
	encode_simple_flags (&(extwm_hints->type_flags), extwm_type_func_mask,
											 hints->function_mask);
	if (extwm_hints->type_flags != 0)
		set_flags (extwm_hints->flags, EXTWM_TypeSet);

	if (hints->pid >= 0 && get_flags (hints->flags, AS_PID)) {
		set_flags (extwm_hints->flags, EXTWM_PID);
		extwm_hints->pid = hints->pid;
	}
	if (hints->icon_argb != NULL
			&& get_flags (hints->client_icon_flags, AS_ClientIcon)) {
		set_flags (extwm_hints->flags, EXTWM_ICON);
		extwm_hints->icon_length =
				hints->icon_argb[0] * hints->icon_argb[1] + 2;
		extwm_hints->icon =
				safemalloc (extwm_hints->icon_length * sizeof (CARD32));
		memcpy (extwm_hints->icon, hints->icon_argb,
						extwm_hints->icon_length * sizeof (CARD32));
	}
	if (get_flags (hints->protocols, AS_DoesWmPing))
		set_flags (extwm_hints->flags, EXTWM_DoesWMPing);

	return (extwm_hints->flags != 0);
}


Bool
set_all_client_hints (Window w, ASHints * hints, ASStatusHints * status,
											Bool set_command)
{
	XWMHints wm_hints;
	XSizeHints size_hints;
	ASFlagType protocols = 0;
	MwmHints mwm_hints;
	GnomeHints gnome_hints;
	ExtendedWMHints extwm_hints;

	if (w == None || hints == NULL)
		return False;

	set_client_names (w, hints->names[0], hints->icon_name, hints->res_class,
										hints->res_name);

	if (client_hints2wm_hints (&wm_hints, hints, status))
		XSetWMHints (dpy, w, &wm_hints);

	if (get_flags (hints->flags, AS_Transient))
		XSetTransientForHint (dpy, w, hints->transient_for);

	if (client_hints2size_hints (&size_hints, hints, status))
		XSetWMNormalHints (dpy, w, &size_hints);

	if (client_hints2extwm_hints (&extwm_hints, hints, status))
		set_extwm_hints (w, &extwm_hints);

	if (client_hints2wm_protocols (&protocols, hints)
			|| get_flags (extwm_hints.flags, EXTWM_DoesWMPing))
		set_client_protocols (w, protocols, extwm_hints.flags);

	if (client_hints2motif_hints (&mwm_hints, hints, status))
		set_multi32bit_property (w, _XA_MwmAtom, XA_CARDINAL, 4,
														 mwm_hints.flags, mwm_hints.functions,
														 mwm_hints.decorations, mwm_hints.inputMode);

	if (client_hints2gnome_hints (&gnome_hints, hints, status))
		set_gnome_hints (w, &gnome_hints);

	return True;
}


ASImage *get_client_icon_image (ScreenInfo * scr, ASHints * hints,
																int desired_size)
{
	ASImage *im = NULL;

	if (hints) {
		char *icon_file = hints->icon_file;
		ASImage *icon_file_im = NULL;
		Bool icon_file_isDefault = False;

		if (icon_file && Database) {
			if (Database->style_default.icon_file != NULL
					&& strcmp (icon_file, Database->style_default.icon_file) == 0)
				icon_file_isDefault = True;
		}
		if (get_flags (hints->client_icon_flags, AS_ClientIcon)) {
			Bool use_client_icon = (hints->icon_file == NULL
															|| Database == NULL);

			if (!use_client_icon) {
				if (icon_file_isDefault)
					use_client_icon = True;
				else {
					icon_file_im =
							get_asimage (scr->image_manager, icon_file, 0xFFFFFFFF, 100);
					if (icon_file_im == NULL)
						use_client_icon = True;
					LOCAL_DEBUG_OUT ("loaded icon from file \"%s\" into %p",
													 icon_file ? icon_file : "(null)", im);
				}

			}
			if (use_client_icon) {
				/* first try ARGB icon If provided by the application : */
				if (get_flags (hints->client_icon_flags, AS_ClientIconARGB)
						&& hints->icon_argb != NULL) {
					/* TODO: we also need to check for newfashioned ARGB icon from
					 * extended WM specs here
					 */
					int width = hints->icon_argb[0];
					int height = hints->icon_argb[1];

					im = convert_argb2ASImage (scr->asv, width, height,
																		 hints->icon_argb + 2, NULL);
					LOCAL_DEBUG_OUT ("converted client's ARGB into an icon %dx%d %p",
													 width, height, im);

				}
				if (im == NULL && get_flags (hints->client_icon_flags, AS_ClientIconPixmap) && hints->icon.pixmap != None) {	/* convert client's icon into ASImage */
					unsigned int width, height;

					get_drawable_size (hints->icon.pixmap, &width, &height);
					im = picture2asimage (scr->asv, hints->icon.pixmap,
																hints->icon_mask, 0, 0, width, height,
																0xFFFFFFFF, False, 100);

					LOCAL_DEBUG_OUT
							("converted client's pixmap into an icon %dx%d %p", width,
							 height, im);
				}
			}
		}
		LOCAL_DEBUG_OUT ("im =  %p", im);
		if (im == NULL) {
			if (CombinedCategories != NULL) {
				ASDesktopEntry *de = NULL;

				if (hints->names[0]) {
					char *name = hints->names[0];
					int i = 0;
					char old;

					while (name[i] && !isspace (name[i]))
						++i;
					if (i > 0) {
						old = name[i];
						name[i] = '\0';
						de = fetch_desktop_entry (CombinedCategories, name);
						LOCAL_DEBUG_OUT
								("found desktop entry %p, for name[0] = \"%s\"", de, name);
						name[i] = old;
					}
				}
				LOCAL_DEBUG_OUT ("icon file = %p, default = %d", icon_file,
												 icon_file_isDefault);
				if (de == NULL && (icon_file == NULL || icon_file_isDefault)) {
					LOCAL_DEBUG_OUT ("CombinedCategories = %p", CombinedCategories);

					de = fetch_desktop_entry (CombinedCategories, hints->res_name);
					LOCAL_DEBUG_OUT ("found desktop entry %p, for res_name = \"%s\"",
													 de, hints->res_name);
					if (de == NULL)
						de = fetch_desktop_entry (CombinedCategories,
																			hints->res_class);
					LOCAL_DEBUG_OUT
							("found desktop entry %p, for res_class = \"%s\"", de,
							 hints->res_class);
				}
				if (de) {
					if (de && de->ref_count > 0 && de->Icon) {
						if (icon_file_im) {
							safe_asimage_destroy (icon_file_im);
							icon_file_im = NULL;
						}
						icon_file = de->Icon;
					}
				}

			}
			if (icon_file) {
				if (icon_file_im)
					im = icon_file_im;
				else
					im = load_environment_icon_any (icon_file, desired_size);
				/*get_asimage (scr->image_manager, icon_file, 0xFFFFFFFF, 100); */
				LOCAL_DEBUG_OUT ("loaded icon from \"%s\" into %dx%d %p",
												 icon_file, im ? im->width : 0,
												 im ? im->height : 0, im);
			} else {
				LOCAL_DEBUG_OUT ("no icon to use %s", "");
			}
		}
	}
	return im;
}


/***********************************************************************************
 * Hints printing functions :
 ***********************************************************************************/

void print_clean_hints (stream_func func, void *stream, ASHints * clean)
{
	register int i;

	if (!pre_print_check
			(&func, &stream, clean, "No hints available(NULL)."))
		return;
	for (i = 0; i < MAX_WINDOW_NAMES && clean->names[i]; i++) {
		func (stream, "CLEAN.NAMES[%d] = \"%s\";\n", i, clean->names[i]);
		func (stream, "CLEAN.NAMES_ENCODING[%d] = %d;\n", i,
					clean->names_encoding[i]);
	}
	if (clean->icon_name) {
		func (stream, "CLEAN.icon_name = \"%s\";\n", clean->icon_name);
		func (stream, "CLEAN.icon_name_encoding = %d;\n",
					clean->names_encoding[clean->icon_name_idx]);
	}
	if (clean->res_name) {
		func (stream, "CLEAN.res_name = \"%s\";\n", clean->res_name);
		func (stream, "CLEAN.res_name_encoding = %d;\n",
					clean->names_encoding[clean->res_name_idx]);
	}
	if (clean->res_class) {
		func (stream, "CLEAN.res_class = \"%s\";\n", clean->res_class);
		func (stream, "CLEAN.res_class_encoding = %d;\n",
					clean->names_encoding[clean->res_class_idx]);
	}
	func (stream, "CLEAN.flags = 0x%lX;\n", clean->flags);
	func (stream, "CLEAN.protocols = 0x%lX;\n", clean->protocols);
	func (stream, "CLEAN.function_mask = 0x%lX;\n", clean->function_mask);

	if (get_flags (clean->flags, AS_Icon)) {
		if (get_flags (clean->client_icon_flags, AS_ClientIcon)) {
			if (get_flags (clean->client_icon_flags, AS_ClientIconARGB)
					&& clean->icon_argb) {
				func (stream, "CLEAN.icon.argb.width = 0x%lX;\n",
							clean->icon_argb[0]);
				func (stream, "CLEAN.icon.argb.height = 0x%lX;\n",
							clean->icon_argb[1]);
			} else if (get_flags (clean->client_icon_flags, AS_ClientIconPixmap))
				func (stream, "CLEAN.icon.pixmap = 0x%lX;\n", clean->icon.pixmap);
			else
				func (stream, "CLEAN.icon.window = 0x%lX;\n", clean->icon.window);
			func (stream, "CLEAN.icon_mask = 0x%lX;\n", clean->icon_mask);
			if (get_flags (clean->client_icon_flags, AS_ClientIconPosition))
				func (stream, "CLEAN.icon_x = %d;\nCLEAN.icon_y = %d;\n",
							clean->icon_x, clean->icon_y);
		} else if (clean->icon_file)
			func (stream, "CLEAN.icon_file = \"%s\";\n", clean->icon_file);
	}
	if (get_flags (clean->flags, AS_MinSize))
		func (stream, "CLEAN.min_width = %u;\nCLEAN.min_height = %u;\n",
					clean->min_width, clean->min_height);
	if (get_flags (clean->flags, AS_MaxSize))
		func (stream, "CLEAN.max_width = %u;\nCLEAN.max_height = %u;\n",
					clean->max_width, clean->max_height);
	if (get_flags (clean->flags, AS_SizeInc))
		func (stream, "CLEAN.width_inc = %u;\nCLEAN.height_inc = %u;\n",
					clean->width_inc, clean->height_inc);
	if (get_flags (clean->flags, AS_Aspect)) {
		func (stream, "CLEAN.min_aspect.x = %d;\nCLEAN.min_aspect.y = %d;\n",
					clean->min_aspect.x, clean->min_aspect.y);
		func (stream, "CLEAN.max_aspect.x = %d;\nCLEAN.max_aspect.y = %d;\n",
					clean->max_aspect.x, clean->max_aspect.y);
	}
	if (get_flags (clean->flags, AS_BaseSize))
		func (stream, "CLEAN.base_width = %u;\nCLEAN.base_height = %u;\n",
					clean->base_width, clean->base_height);
	if (get_flags (clean->flags, AS_Gravity))
		func (stream, "CLEAN.gravity = %d;\n", clean->gravity);
	if (get_flags (clean->flags, AS_Border))
		func (stream, "CLEAN.border_width = %u;\n", clean->border_width);
	if (get_flags (clean->flags, AS_Handles))
		func (stream, "CLEAN.handle_width = %u;\n", clean->handle_width);

	if (clean->group_lead)
		func (stream, "CLEAN.group_lead = 0x%lX;\n", clean->group_lead);
	if (get_flags (clean->flags, AS_Transient))
		func (stream, "CLEAN.transient_for = 0x%lX;\n", clean->transient_for);

	if (clean->cmap_windows)
		for (i = 0; clean->cmap_windows[i] != None; i++)
			func (stream, "CLEAN.cmap_windows[%d] = 0x%lX;\n", i,
						clean->cmap_windows[i]);

	if (get_flags (clean->flags, AS_PID))
		func (stream, "CLEAN.pid = %d;\n", clean->pid);
	if (clean->frame_name && get_flags (clean->flags, AS_Frame))
		func (stream, "CLEAN.frame_name = \"%s\";\n", clean->frame_name);
	if (clean->windowbox_name && get_flags (clean->flags, AS_Windowbox))
		func (stream, "CLEAN.windowbox_name = \"%s\";\n",
					clean->windowbox_name);

	for (i = 0; i < BACK_STYLES; i++)
		if (clean->mystyle_names[i])
			func (stream, "CLEAN.mystyle_names[%d] = \"%s\";\n", i,
						clean->mystyle_names[i]);

	func (stream, "CLEAN.disabled_buttons = 0x%lX;\n",
				clean->disabled_buttons);

	func (stream, "CLEAN.hints_types_raw = 0x%lX;\n",
				clean->hints_types_raw);
	func (stream, "CLEAN.hints_types_clean = 0x%lX;\n",
				clean->hints_types_clean);

	if (clean->client_host)
		func (stream, "CLEAN.client_host = \"%s\";\n", clean->client_host);
	if (clean->client_cmd)
		func (stream, "CLEAN.client_cmd = \"%s\";\n", clean->client_cmd);	

    func (stream, "CLEAN.extwm_window_type = \"0x%lX\";\n", clean->extwm_window_type);
}

void
print_status_hints (stream_func func, void *stream, ASStatusHints * status)
{
	if (!pre_print_check
			(&func, &stream, status, "No status available(NULL)."))
		return;

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

	if (get_flags (status->flags, AS_StartPositionUser)) {
		func (stream, "STATUS.user_x = %d;\n", status->x);
		func (stream, "STATUS.user_y = %d;\n", status->y);
	} else {
		func (stream, "STATUS.x = %d;\n", status->x);
		func (stream, "STATUS.y = %d;\n", status->y);
	}
	if (get_flags (status->flags, AS_Size)) {
		func (stream, "STATUS.width = %d;\n", status->width);
		func (stream, "STATUS.height = %d;\n", status->height);
	}
	if (get_flags (status->flags, AS_Desktop))
		func (stream, "STATUS.desktop = %d;\n", status->desktop);
	if (get_flags (status->flags, AS_Layer))
		func (stream, "STATUS.layer = %d;\n", status->layer);
	if (get_flags (status->flags, AS_StartViewportX | AS_StartViewportX)) {
		func (stream, "STATUS.viewport_x = %d;\n", status->viewport_x);
		func (stream, "STATUS.viewport_y = %d;\n", status->viewport_y);
	}
}

/*********************************************************************************
 *  serialization for purpose of inter-module communications                     *
 *********************************************************************************/
void serialize_string (char *string, ASVector * buf)
{
	if (buf) {
		register CARD32 *ptr;
		register char *src = string;
		register int i = string ? strlen (string) >> 2 : 0;	/* assume CARD32 == 4*CARD8 :)) */

		append_vector (buf, NULL, 1 + i + 1);
		ptr = VECTOR_TAIL (CARD32, *buf);
		VECTOR_USED (*buf) += i + 1;
		ptr[0] = i + 1;
		++ptr;
		if (string == NULL) {
			ptr[0] = 0;
			return;
		}
		src = &(string[i << 2]);
		/* unrolling loop here : */
		ptr[i] = src[0] & 0x0FF;
		if (src[0]) {
			if (src[1]) {							/* we don't really want to use bitwise operations */
				/* so we get "true" number and later can do ENDIANNES transformations */
				ptr[i] |= (((CARD32) src[1]) << 8) & 0x0FF00;
				if (src[2])
					ptr[i] |= (((CARD32) src[2]) << 16) & 0x0FF0000;
			}
		}
		while (--i >= 0) {
			src -= 4;
			ptr[i] =
					(((CARD32) src[0]) & 0x0FF) | (((CARD32) src[1] << 8) & 0x0FF00)
					| (((CARD32) src[2] << 16) & 0x0FF0000)
					| (((CARD32) src[3] << 24) & 0xFF000000);
		}
	}
}

void serialize_CARD32_zarray (CARD32 * array, ASVector * buf)
{
	register int i = 0;
	register CARD32 *ptr;

	if (array)
		while (array[i])
			i++;
	i++;
	append_vector (buf, NULL, 1 + i);
	ptr = VECTOR_TAIL (CARD32, *buf);
	VECTOR_USED (*buf) += i;
	ptr[0] = i;
	ptr++;
	if (array == NULL)
		ptr[0] = 0;
	else
		while (--i >= 0)
			ptr[i] = array[i];
}

Bool serialize_clean_hints (ASHints * clean, ASVector * buf)
{
	register CARD32 *ptr;
	register int i = 0;

	if (clean == NULL || buf == NULL)
		return False;

	/* we expect CARD32 vector here : */
	if (VECTOR_UNIT (*buf) != sizeof (CARD32))
		return False;

	append_vector (buf, NULL, ASHINTS_STATIC_DATA);
	ptr = VECTOR_TAIL (CARD32, *buf);
	ptr[i++] = clean->flags;
	ptr[i++] = clean->protocols;
	ptr[i++] = clean->function_mask;
	ptr[i++] = clean->icon.window;
	ptr[i++] = clean->icon_mask;
	ptr[i++] = clean->icon_x;
	ptr[i++] = clean->icon_y;

	ptr[i++] = clean->min_width;
	ptr[i++] = clean->min_height;
	ptr[i++] = clean->max_width;
	ptr[i++] = clean->max_height;
	ptr[i++] = clean->width_inc;
	ptr[i++] = clean->height_inc;
	ptr[i++] = clean->min_aspect.x;
	ptr[i++] = clean->min_aspect.y;
	ptr[i++] = clean->max_aspect.x;
	ptr[i++] = clean->max_aspect.y;
	ptr[i++] = clean->base_width;
	ptr[i++] = clean->base_height;

	ptr[i++] = clean->gravity;
	ptr[i++] = clean->border_width;
	ptr[i++] = clean->handle_width;

	ptr[i++] = clean->group_lead;
	ptr[i++] = clean->transient_for;

	ptr[i++] = clean->pid;

	ptr[i++] = clean->disabled_buttons;

	ptr[i++] = clean->hints_types_raw;
	ptr[i++] = clean->hints_types_clean;
	VECTOR_USED (*buf) += i;

	serialize_CARD32_zarray (clean->cmap_windows, buf);

	serialize_string (clean->icon_file, buf);
	serialize_string (clean->frame_name, buf);
	serialize_string (clean->windowbox_name, buf);

	for (i = 0; i < BACK_STYLES; i++)
		serialize_string (clean->mystyle_names[i], buf);

	serialize_string (clean->client_host, buf);
	serialize_string (clean->client_cmd, buf);

	return True;
}

Bool serialize_names (ASHints * clean, ASVector * buf)
{
	CARD32 header[4];
	register int i;

	if (clean == NULL || buf == NULL)
		return False;
	header[0] =
			pointer_name_to_index_in_list (clean->names, clean->res_name);
	header[1] =
			pointer_name_to_index_in_list (clean->names, clean->res_class);
	header[2] =
			pointer_name_to_index_in_list (clean->names, clean->icon_name);
	for (i = 0; clean->names[i] != NULL && i < MAX_WINDOW_NAMES; i++) ;
	header[3] = i;
	append_vector (buf, &(header[0]), 4);

	for (i = 0; clean->names[i] != NULL && i < MAX_WINDOW_NAMES; i++)
		serialize_string (clean->names[i], buf);

	return True;
}

Bool serialize_status_hints (ASStatusHints * status, ASVector * buf)
{
	register CARD32 *ptr;
	register int i = 0;

	if (status == NULL || buf == NULL)
		return False;

	/* we expect CARD32 vector here : */
	if (VECTOR_UNIT (*buf) != sizeof (CARD32))
		return False;

	append_vector (buf, NULL, ASSTATUSHINTS_STATIC_DATA);
	ptr = VECTOR_TAIL (CARD32, *buf);
	ptr[i++] = status->flags;

	ptr[i++] = status->x;
	ptr[i++] = status->y;
	ptr[i++] = status->width;
	ptr[i++] = status->height;
	ptr[i++] = status->border_width;
	ptr[i++] = status->viewport_x;
	ptr[i++] = status->viewport_y;
	ptr[i++] = status->desktop;
	ptr[i++] = status->layer;
	ptr[i++] = status->icon_window;
	VECTOR_USED (*buf) += i;

	return True;
}

/*********************************************************************************
 *  deserialization so that module can read out communications 	                 *
 *********************************************************************************/
char *deserialize_string (CARD32 ** pbuf, size_t * buf_size)
{
	char *string;
	CARD32 *buf = *pbuf;
	size_t len;
	register int i;
	register char *str;

	if (*pbuf == NULL)
		return NULL;
	if (buf_size && *buf_size < 2)
		return NULL;
	len = buf[0];
	if (buf_size && len > *buf_size + 1)
		return NULL;
	buf++;
	str = string = safemalloc (len << 2);
	for (i = 0; i < len; i++) {
		str[0] = (buf[i] & 0x0FF);
		str[1] = (buf[i] >> 8) & 0x0FF;
		str[2] = (buf[i] >> 16) & 0x0FF;
		str[3] = (buf[i] >> 24) & 0x0FF;
		str += 4;
	}

	if (buf_size)
		*buf_size -= len;
	*pbuf += len;

	return string;
}

CARD32 *deserialize_CARD32_zarray (CARD32 ** pbuf, size_t * buf_size)
{
	CARD32 *array;
	CARD32 *buf = *pbuf;
	size_t len;
	register int i;

	if (*pbuf == NULL || *buf_size < 2)
		return NULL;
	len = buf[0];
	if (len > *buf_size + 1)
		return NULL;
	buf++;
	array = safemalloc (len * sizeof (CARD32));
	for (i = 0; i < len; i++)
		array[i] = buf[i];

	*buf_size -= len;
	*pbuf += len;

	return array;
}

ASHints *deserialize_clean_hints (CARD32 ** pbuf, size_t * buf_size,
																	ASHints * reusable_memory)
{
	ASHints *clean = reusable_memory;
	register int i = 0;
	register CARD32 *buf = *pbuf;

	if (buf == NULL || *buf_size < ASHINTS_STATIC_DATA)
		return False;

	if (clean == NULL)
		clean = safecalloc (1, sizeof (ASHints));

	clean->flags = buf[i++];
	clean->protocols = buf[i++];
	clean->function_mask = buf[i++];

	clean->icon.window = buf[i++];
	clean->icon_mask = buf[i++];
	clean->icon_x = buf[i++];
	clean->icon_y = buf[i++];

	clean->min_width = buf[i++];
	clean->min_height = buf[i++];
	clean->max_width = buf[i++];
	clean->max_height = buf[i++];
	clean->width_inc = buf[i++];
	clean->height_inc = buf[i++];
	clean->min_aspect.x = buf[i++];
	clean->min_aspect.y = buf[i++];
	clean->max_aspect.x = buf[i++];
	clean->max_aspect.y = buf[i++];
	clean->base_width = buf[i++];
	clean->base_height = buf[i++];
	clean->gravity = buf[i++];
	clean->border_width = buf[i++];
	clean->handle_width = buf[i++];
	clean->group_lead = buf[i++];
	clean->transient_for = buf[i++];
	clean->pid = buf[i++];
	clean->disabled_buttons = buf[i++];

	clean->hints_types_raw = buf[i++];
	clean->hints_types_clean = buf[i++];

	*buf_size -= i;
	*pbuf += i;
	if (clean->cmap_windows)
		free (clean->cmap_windows);
	clean->cmap_windows = deserialize_CARD32_zarray (pbuf, buf_size);

	if (clean->icon_file)
		free (clean->icon_file);
	clean->icon_file = deserialize_string (pbuf, buf_size);
	if (clean->frame_name)
		free (clean->frame_name);
	clean->frame_name = deserialize_string (pbuf, buf_size);
	if (clean->windowbox_name)
		free (clean->windowbox_name);
	clean->windowbox_name = deserialize_string (pbuf, buf_size);

	for (i = 0; i < BACK_STYLES; i++) {
		if (clean->mystyle_names[i])
			free (clean->mystyle_names[i]);
		clean->mystyle_names[i] = deserialize_string (pbuf, buf_size);
	}
	if (clean->client_host)
		free (clean->client_host);
	clean->client_host = deserialize_string (pbuf, buf_size);
	if (clean->client_cmd)
		free (clean->client_cmd);
	clean->client_cmd = deserialize_string (pbuf, buf_size);

	return clean;
}

Bool deserialize_names (ASHints * clean, CARD32 ** pbuf, size_t * buf_size)
{
	CARD32 header[4];
	CARD32 *buf = *pbuf;
	register int i;

	if (clean == NULL || buf == NULL || *buf_size < 4)
		return False;

	header[0] = buf[0];
	header[1] = buf[1];
	header[2] = buf[2];
	header[3] = buf[3];

	if (header[3] <= 0 || header[3] >= MAX_WINDOW_NAMES)
		return False;

	*buf_size -= 4;
	*pbuf += 4;

	for (i = 0; i < header[3]; i++) {
		if (clean->names[i])
			free (clean->names[i]);
		clean->names[i] = deserialize_string (pbuf, buf_size);
	}
	clean->res_name =
			(header[0] < MAX_WINDOW_NAMES) ? clean->names[header[0]] : NULL;
	clean->res_name_idx = 0;
	clean->res_class =
			(header[1] < MAX_WINDOW_NAMES) ? clean->names[header[1]] : NULL;
	clean->res_class_idx = 0;
	clean->icon_name =
			(header[2] < MAX_WINDOW_NAMES) ? clean->names[header[2]] : NULL;
	clean->icon_name_idx = 0;

	return True;
}


ASStatusHints *deserialize_status_hints (CARD32 ** pbuf, size_t * buf_size,
																				 ASStatusHints * reusable_memory)
{
	ASStatusHints *status = reusable_memory;
	register int i = 0;
	register CARD32 *buf = *pbuf;

	if (buf == NULL || *buf_size < ASSTATUSHINTS_STATIC_DATA)
		return False;

	if (status == NULL)
		status = safecalloc (1, sizeof (ASStatusHints));

	status->flags = buf[i++];
	status->x = buf[i++];
	status->y = buf[i++];
	status->width = buf[i++];
	status->height = buf[i++];
	status->border_width = buf[i++];
	status->viewport_x = buf[i++];
	status->viewport_y = buf[i++];
	status->desktop = buf[i++];
	status->layer = buf[i++];
	status->icon_window = buf[i++];
	*buf_size -= i;
	*pbuf += i;

	return status;
}

/*********************************************************************************
 *                  List of Supported Hints management                           *
 *********************************************************************************/
ASSupportedHints *create_hints_list ()
{
	ASSupportedHints *list;

	list = (ASSupportedHints *) safecalloc (1, sizeof (ASSupportedHints));
	return list;
}

void destroy_hints_list (ASSupportedHints ** plist)
{
	if (*plist) {
		free (*plist);
		*plist = NULL;
	}
}

static hints_merge_func HintsTypes2Func (HintsTypes type)
{
	switch (type) {
	case HINTS_ICCCM:
		return merge_icccm_hints;
	case HINTS_GroupLead:
		return merge_group_hints;
	case HINTS_Transient:
		return merge_transient_hints;
	case HINTS_Motif:
		return merge_motif_hints;
	case HINTS_Gnome:
		return merge_gnome_hints;
	case HINTS_KDE:
		return merge_kde_hints;
	case HINTS_ExtendedWM:
		return merge_extwm_hints;
	case HINTS_XResources:
		return merge_xresources_hints;
	case HINTS_ASDatabase:
		return merge_asdb_hints;
	case HINTS_Supported:
		break;
	}
	return NULL;
}

HintsTypes Func2HintsTypes (hints_merge_func func)
{
	if (func == merge_group_hints)
		return HINTS_GroupLead;
	else if (func == merge_transient_hints)
		return HINTS_Transient;
	else if (func == merge_motif_hints)
		return HINTS_Motif;
	else if (func == merge_gnome_hints)
		return HINTS_Gnome;
	else if (func == merge_extwm_hints)
		return HINTS_ExtendedWM;
	else if (func == merge_kde_hints)
		return HINTS_KDE;
	else if (func == merge_xresources_hints)
		return HINTS_XResources;
	else if (func == merge_asdb_hints)
		return HINTS_ASDatabase;
	return HINTS_ICCCM;
}

Bool enable_hints_support (ASSupportedHints * list, HintsTypes type)
{
	if (list) {
		if (list->hints_num >= HINTS_Supported)
			list->hints_num = HINTS_Supported - 1;	/* we are being paranoid */

		if (get_flags (list->hints_flags, (0x01 << type)))	/* checking for duplicates */
			return False;

		if ((list->merge_funcs[list->hints_num] =
				 HintsTypes2Func (type)) == NULL)
			return False;

		list->hints_types[list->hints_num] = type;
		set_flags (list->hints_flags, (0x01 << type));
		list->hints_num++;
		return True;
	}
	return False;
}

Bool disable_hints_support (ASSupportedHints * list, HintsTypes type)
{
	if (list) {
		register int i;

		if (list->hints_num > HINTS_Supported)
			list->hints_num = HINTS_Supported;	/* we are being paranoid */
		for (i = 0; i < list->hints_num; i++)
			if (list->hints_types[i] == type) {
				list->hints_num--;
				for (; i < list->hints_num; i++) {
					list->merge_funcs[i] = list->merge_funcs[i + 1];
					list->hints_types[i] = list->hints_types[i + 1];
				}
				list->merge_funcs[i] = NULL;
				list->hints_types[i] = HINTS_Supported;
				clear_flags (list->hints_flags, (0x01 << type));
				return True;
			}
	}
	return False;
}

HintsTypes *supported_hints_types (ASSupportedHints * list,
																	 int *num_return)
{
	HintsTypes *types = NULL;
	int curr = 0;

	if (list)
		if ((curr = list->hints_num) > 0)
			types = list->hints_types;

	if (num_return)
		*num_return = curr;
	return types;
}
