/*
 * Copyright 2003 Sun Microsystems Inc.
 *
 * This is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 * Authors: Karl Park <karl.park@sun.com>
 */

#pragma ident	"@(#)xaux_so_common.c 1.37 99/12/20"

#include <stdio.h>
#include <limits.h>
#include <unistd.h>
#include <locale.h>

#include <X11/Xlib.h>
#include <X11/Xatom.h>

#include "iiimpAux.h"

#include "xaux_common.h"
#include "xaux_locale.h"
#include "kolelog.h"
#include "logf.h"

extern xaux_class_t xaux_classes[];

#ifdef AUX_ICID

typedef struct _aux_icid {
	aux_t *			aux;
	int			icid;
	struct _aux_icid *	prev;
	struct _aux_icid *	next;
} aux_icid_t;

static void		aux_icid_init(void);
static void		aux_icid_finish(void);
static aux_icid_t *	aux_icid_get(int, Bool);
static void		aux_icid_delete(int);

#define HASH_SIZE 137
static aux_icid_t	aux_icid[HASH_SIZE];

#else

/* workaround for "stored reference to aux_t is corrupred" problem */
static void (*aux_setvalue)(aux_t *, const unsigned char *, int);
static unsigned char * (* compose)(const aux_data_t *, int *);
static aux_t aux_tmp;
#endif

static int		aux_initialized = 0;

static Bool xaux_so_Create(aux_t *);
static Bool xaux_so_Start(aux_t *, const unsigned char *, int size);
static Bool xaux_so_Draw(aux_t *, const unsigned char *, int size);
static Bool xaux_so_Done(aux_t *, const unsigned char *, int size);
static Bool xaux_so_Switched(aux_t *, int im_id, int on_off);
static Bool xaux_so_Destroy(aux_t *);

/* workaround_slowdtlogin */
static Atom	atom_xbe = None;
static Bool	is_server = False;
/* workaround_slowdtlogin_end */

aux_method_t xaux_so_methods = {
	xaux_so_Create,
	xaux_so_Start,
	xaux_so_Draw,
	xaux_so_Done,
	xaux_so_Switched,
	xaux_so_Destroy
};

static xaux_class_t *
xaux_getclass_byatomxs(Atom atom)
{
	xaux_class_t	*p = xaux_classes;

	while (p->classname != NULL) {
		if (p->atom_xs[0] == atom)
			return (p);
		p++;
	}

	return (NULL);
}

static xaux_class_t *
xaux_getclass_byutfname(
	const CARD16 *	utfname,
	size_t		len)
{
	xaux_class_t	*p = xaux_classes;

	while (p->classname != NULL) {
		if (memcmp(p->utfname, utfname, len) == 0) {
			return p;
		}
		p++;
	}

	return NULL;
}

static Bool
xaux_so_get_extwin(
	xaux_class_t	*xc,
	Display		*display)
{
	size_t		i;

	if (xc->atom_extwin == (Atom)None) {
		logf("xaux_so_get_extwin: atom_extwin is None\n");
		return False;
	}

	xc->extwin = XGetSelectionOwner(display, xc->atom_extwin);
	logf("xaux_so_get_extwin: xc->extwin:%d\n", xc->extwin);
	if (xc->extwin == None) {

		if (XAUX_MAXRETRY_EXTWIN <= 0) {
			logf("xaux_so_get_extwin: XAUX_MAXRETRY_EXTWIN <= 0\n");
			return False;
		}

		/* workaround_slowdtlogin */
		if (is_server == False) {
			Window	w;

			if (atom_xbe == None) {
				xc->atom_extwin = (Atom)None;
				return False;
			}
			w = XGetSelectionOwner(display, atom_xbe);
			if (w == None) {
				xc->atom_extwin = (Atom)None;
				return False;
			}
		}
		/* workaround_slowdtlogin_end */

		sleep(XAUX_RETRYINT_EXTWIN);

		xc->extwin = XGetSelectionOwner(display, xc->atom_extwin);
		if (xc->extwin != None)
			return True;

		return False;
	}
	return True;
}

static Bool
xaux_so_launch_ext(
	xaux_class_t	*xc,
	aux_t		*aux
)
{
	pid_t		pid;
	const char	*ext = xc->extexec;
	
	if (xc->atom_extwin == (Atom)0) {
		return False;
	}

	if (xaux_so_get_extwin(xc, aux->service->display(aux)) == True) {
		/*
		 * succeeded in communicating
		 * with already running external program
		 * no need to launch external program
		 */
		return True;
	}

	if (aux->service->server(aux) == False) {
		/*
		 * do not launch external program
		 * if not loaded by server (htt_xbe) program
		 */
		return False;
	}

	if (access(ext, X_OK) != 0) {
		logf("executable \"%s\" not found\n", ext);
		/* no use to try to get extwin */
		xc->atom_extwin = (Atom)0;
		return False;
	}
			
#ifdef	sun
	pid = fork1();
#else
	pid = fork();
#endif

	if (pid == (pid_t)(-1)) { /* fork failed */
		return False;
	} else if (0 == pid) { /* child */
		char *locale_name, env_LC_ALL[128], env_LANG[128];
		
		/* Get locale name */
/*
		locale_name = (char *)getenv("LANG");
		if (locale_name == NULL) {
			locale_name = (char *)getenv("LC_ALL");
			if (locale_name == NULL) {
				locale_name = DEFAULT_LOCALE_START_IN_UTF8;
			}
		}
*/
		locale_name = setlocale(LC_CTYPE, NULL);

		if (locale_name != NULL) {
			logf("setlocale(LC_CTYPE) return: %s\n", locale_name);
		} else {
			logf("setlocale(LC_CTYPE) return: NULL\n");
		}

		/* if in non_UTF-8 locale, start with the locale that htt_xbe is. */
		/* if in UTF-8 locale, need set proper locale.  */
		if (locale_name == NULL) {
			locale_name = DEFAULT_LOCALE_START_IN_UTF8;
		} else if (strstr(locale_name, "UTF-8") != NULL) {
			logf(" AuxObj started is UTF-8 locale, default:%s\n", 
				DEFAULT_LOCALE_START_IN_UTF8);

			/*  if is Simplified Chinese languange, */
			/*  and not in zh_CN.UTF-8 locale,      */
			/*  then set locale to "zh_CN.EUC"      */
			if (!strcmp(XAUX_LOCALE_NAME, "zh_CN")) {
				/* if in zh_CN language, need check whether is zh.UTF-8 locale */
				if ( (!strcmp(locale_name, "zh.UTF-8")) || 
				     (!strcmp(locale_name, "zh_CN.UTF-8")) ) {
					logf("is zh.UTF-8 locale\n");
					locale_name = DEFAULT_LOCALE_START_IN_UTF8;
				} else {
					logf("not zh.UTF-8 locale, set to zh_CN.EUC\n");
					locale_name = "zh_CN.EUC";
				}
			} else {
				locale_name = DEFAULT_LOCALE_START_IN_UTF8;
			}
		}

		logf("start aux with locale_name: %s\n", locale_name);

		sprintf(env_LC_ALL, "LC_ALL=%s", locale_name);
		putenv(env_LC_ALL);

		sprintf(env_LANG, "LANG=%s", locale_name);
		putenv(env_LANG);

		chdir("/tmp");
		execl(ext, xc->classname, NULL);
		_exit(1);
	}
	
	/* parent */
	return True;
}

static Bool
xaux_so_send_message_detour_to_prop(
	aux_t *			aux,
	xaux_class_t *		xc,
	int			im_id,
	int			ic_id,
	aux_data_type_t		type)
{
	Display *		display;
	char			string_buf[1024];

	logf("xaux_so_send_message_detour_to_prop: sowin\n");
	display = aux->service->display(aux);

	SX_PROP_ATOM_AUX_NAME(string_buf) = xc->atom_classname;
	SX_PROP_TYPE(string_buf) = type;
	SX_PROP_INDEX(string_buf) = xc->index;
	SX_PROP_IMID(string_buf) = im_id;
	SX_PROP_ICID(string_buf) = ic_id;

	XChangeProperty(display, xc->sowin,
		xc->atom_sx[xc->atom_sx_idx], XA_STRING,
		8, PropModeReplace, (unsigned char *)(&string_buf),
		SX_SIZE_PROP_STARTDONE);

	/* needed in en_US.UTF-8 */
        XFlush(display);

	if (++xc->atom_sx_idx == xc->atom_sx_num)
		xc->atom_sx_idx = 1;
	
	return True;
}

static Bool
xaux_so_send_message(
	aux_t *		aux,
	xaux_class_t *	xc,
	int		im_id,
	int		ic_id,
	aux_data_type_t	type,
	Atom		atom)
{
	Display *		display;
	XClientMessageEvent	event;

	display = aux->service->display(aux);

	logf("xaux_so_send_message:  check extwin \n");
	if ((xc->extwin == None) &&
		(xaux_so_get_extwin(xc, display) == False)) {
		/* cannot get extwin */
		if (xc->atom_extwin == (Atom)None) {
			logf("atom_extwin is None\n");
			return False;
		} else {
			logf("xaux_so_get_extwin(xc, display) is False\n");
			if (type == AUX_DATA_DRAW) {
				return True;
			} else {
				return xaux_so_send_message_detour_to_prop(
					aux, xc, im_id, ic_id, type);
			}
		}
	}
		
	KOLE_LOG (LOGDEST_STDOUT,
		  "xaux_so_send_message:  begin XSendEvent");
	KOLE_LOG (LOGDEST_STDOUT,
		  "im_id : %d, ic_id : %d", im_id, ic_id);
	
	event.type = ClientMessage;
	event.serial = 0;
	event.send_event = True;
	event.display = display;
	event.window = xc->extwin;
	event.message_type = xc->atom_sx[0];
	event.format = 32;

	event.data.l[0] = xc->atom_classname;
	event.data.l[1] = ((CARD32)im_id << 16) | ((CARD32)ic_id & 0xffff);
	event.data.l[2] = xc->index;
	event.data.l[3] = type; /* CREATE, DONE, ... */
	if (type == AUX_DATA_DRAW) {
		event.data.l[4] = atom;
	} else {
		event.data.l[4] = 0; /* unused */
	}

	XSendEvent(display, xc->extwin, True, 0, (XEvent *)(&event));

	/* needed in en_US.UTF-8 */
        XFlush(display);

	return True;
}

static Bool
xaux_so_send_property(
	aux_t *			aux,
	xaux_class_t *		xc,
	const unsigned char *	p,
	int			len)
{
	Display *	display;
	int		i = 1;
	Bool		rv;
	Window		win;

	display = aux->service->display(aux);

	if ((xc->extwin == None) &&
		(xaux_so_get_extwin(xc, display) == False) &&
		(xc->atom_extwin == (Atom)None)) {
		return False;
	}

	win = (xc->extwin != (Window)None) ? xc->extwin : xc->sowin;

	logf("xaux_so_send_property:  XChangeProperty change extwin\n");
	XChangeProperty(display, win,
		xc->atom_sx[xc->atom_sx_idx], XA_STRING,
		8, PropModeReplace, (unsigned char *)p, len);

	if (xaux_so_send_message(aux, xc,
		aux->service->im_id(aux), aux->service->ic_id(aux),
		AUX_DATA_DRAW, xc->atom_sx[xc->atom_sx_idx]) == False) {
			return False;
	}

	/* XFlush() has been called in xaux_so_send_message() */
	
	if (++xc->atom_sx_idx == xc->atom_sx_num)
		xc->atom_sx_idx = 1;
	
	return True;
}

static Bool
xaux_so_process_property_update(
	Display	*		display,
	Window			window,
	Atom			atom,
	xaux_class_t *		xc)
{
	Atom		actual_type_return;
	int		actual_format_return;
	unsigned long	nitem_return;
	unsigned long	bytes_after_return;
	unsigned char *	prop_return;
	int		r;
	int		size;
	aux_data_t	aux_data_;
	aux_data_t *	aux_data = &(aux_data_);
	unsigned char *	p;
	int		i;
	int		n=0;

#ifdef AUX_ICID
	aux_icid_t * 	ic;
#endif

	r = XGetWindowProperty(display, window,
			       atom, 0, INT_MAX, False,
			       AnyPropertyType, &actual_type_return,
			       &actual_format_return, &nitem_return,
			       &bytes_after_return, &prop_return);

	if (r != Success) {
		return False;
	}


	/* header */

	aux_data->type = AUX_DATA_SETVALUE;
	aux_data->im = XS_PROP_IMID(prop_return);
	aux_data->ic = XS_PROP_ICID(prop_return);
	aux_data->aux_index = xc->index;
	aux_data->aux_name = (unsigned char *)xc->utfname;
	aux_data->aux_name_length = strlen(xc->classname)*sizeof(CARD16);

	/* int values */

	aux_data->integer_count = XS_PROP_INT_COUNT(prop_return);

	if (aux_data->integer_count > 0) {
		aux_data->integer_list = (int *)XS_PROP_INT_LIST(prop_return);
	} else {
		aux_data->integer_list = NULL;
	}

	/* string values */

	aux_data->string_count = XS_PROP_STR_COUNT(prop_return);

	if (aux_data->string_count > 0) {
		unsigned char * prop_str = XS_PROP_STR_LIST(prop_return);
		char *		outbuf;
		size_t		outbufsiz;

		if ((aux_data->string_list =
			(aux_string_t *)malloc(sizeof (aux_string_t) *
				aux_data->string_count)) == NULL) {
				XFree(prop_return);
				return False;
		}

		for (i = 0; i < aux_data->string_count; i++) {
			int	j;
			int	pn;
			size_t	len;

			len = *((CARD16 *)(prop_str));
			prop_str += sizeof (CARD16);
			aux_data->string_list[i].ptr = prop_str;
			aux_data->string_list[i].length = len;
			prop_str += len;
			pn = padding[(sizeof(CARD16) + len) % 4];
			for (j = 0; j < pn; j++) {
				*prop_str++ = 0U;
			}
		}
	} else {
		aux_data->string_list = NULL;
	}

	aux_data->string_ptr = NULL;

	/* compose and send message to engine */
#ifdef AUX_ICID
	if ((ic = aux_icid_get(aux_data->ic, False)) == NULL) {
		free(aux_data->string_list);
		XFree(prop_return);
		return False;
	}
	if ((p = ic->aux->service->compose(aux_data, &size)) == NULL) {
		free(aux_data->string_list);
		XFree(prop_return);
		return False;
	}
	ic->aux->service->aux_setvalue(ic->aux, p, size);
#else
	/* workaround for "stored reference to aux_t is corrupred" problem */
	if ((p = /*ic->aux->service->*/compose(aux_data, &size)) == NULL) {
		free(aux_data->string_list);
		XFree(prop_return);
		return False;
	}

	/* workaround for "stored reference to aux_t is corrupred" problem */
	/*ic->aux->service->*/aux_setvalue(/*ic->aux*/&aux_tmp, p, size);
#endif

	logf("so_SetValue[%s] im:0x%x ic:0x%x in=%d sn=%d\n",
		xc->classname, aux_data->im, aux_data->ic,
		aux_data->integer_count, aux_data->string_count);

	free(p);
	free(aux_data->string_list);
	XFree(prop_return);

	return True;
}

static Bool
xaux_so_process_client_message(
	Display	*		display,
	Window			window,
	XClientMessageEvent *	event)
{
	aux_data_t	aux_data_;
	aux_data_t	*aux_data = &(aux_data_);
	aux_data_type_t	type;
	xaux_class_t	*xc;

	if ((xc = xaux_getclass_byatomxs(event->message_type)) == NULL) {
		return False;
	}

	aux_data->im = ((CARD32)(event->data.l[1])) >> 16;
	aux_data->ic = ((CARD32)(event->data.l[1])) & 0xffff;
	aux_data->aux_index = (CARD32)(event->data.l[2]);

	type = (CARD32)(event->data.l[3]);

	switch (type) {
	case AUX_DATA_SETVALUE:
		return xaux_so_process_property_update(display, window,
				(Atom)(event->data.l[4]), xc);
	default:
		return False;
	}
}

static Bool
xaux_so_event_filter(
	Display *	display,
	Window		window,
	XEvent *	event,
	XPointer	pointer)
{
	switch (event->type) {
	case ClientMessage:
		return (xaux_so_process_client_message(display, window,
			(XClientMessageEvent *)event));
	}
	return False;
}

#ifdef AUX_ICID
static void
aux_icid_init(void)
{
	int	i;

	for (i = 0; i < HASH_SIZE; i++) {
		aux_icid[i].icid = (-1);
	}
}

static void
aux_icid_finish(void)
{
	int		i;
	aux_icid_t *	p0;
	aux_icid_t *	p1;

	for (i = 0; i < HASH_SIZE; i++) {
		p0 = aux_icid[i].next;

		for (; p0 != NULL; p0 = p1) {
			p1 = p0->next;
			free(p0);
		}
	}
}

static aux_icid_t *
aux_icid_get(
	int	icID,
	Bool	createit)
{
	int		hash_value;
	aux_icid_t *	p;

	p = &(aux_icid[icID % HASH_SIZE]);

	if (p->icid == -1) {
		if (!createit)
			return NULL;
		p->icid = icID;
		return (p);
	}

	for (; ; p = p->next) {
		if (icID == p->icid) {
			return p;
		}
		if (p->next == NULL) {
			if (!createit)
				return NULL;
			p->next = malloc(sizeof (aux_icid_t));
			if (p->next == NULL) {
				return NULL;
			}
			memset(p->next, 0, (sizeof (aux_icid_t)));
			p->next->prev = p;
			p->next->next = NULL;
			p->next->icid = icID;
			return (p->next);
		}
	}
}

static void
aux_icid_delete(int icID)
{
	aux_icid_t *	p;

	p = aux_icid_get(icID, False);

	if (NULL == p) {
		return;
	}

	p->icid = -1;

	if (p->next != NULL) {
		p->next->prev = p->prev;
	}
	if (p->prev != NULL) {
		p->prev->next = p->next;
		free(p);
	}
}
#endif

static Bool
xaux_so_init_classes(aux_t * aux)
{
	Display *	display;
	char		buf[XAUX_MAXCLASSNAMELEN + XAUX_MAXSUFFIXLEN + 1];
	int		i;

	xaux_class_t *p = xaux_classes;

	display = aux->service->display(aux);

	/* workaround_slowdtlogin */
	atom_xbe = XInternAtom(display, "xaux_xbe", False);

	is_server = aux->service->server(aux);

	logf("so_init_classes: is_server:%d, atom_xbe:%d\n", is_server, atom_xbe);

	if (is_server == True && atom_xbe != None) {
		XSetSelectionOwner(display, atom_xbe,
				DefaultRootWindow(display), CurrentTime);
	}
	/* workaround_slowdtlogin_end */

	while (p->classname != NULL) {
		p->atom_classname = XInternAtom(display, p->classname, False);

		sprintf(buf, "%s%s", p->classname, XAUX_SOWIN_SUFFIX);
		p->atom_sowin = XInternAtom(display, buf, False);

		sprintf(buf, "%s%s", p->classname, XAUX_EXTWIN_SUFFIX);
		p->atom_extwin = XInternAtom(display, buf, False);

		for (i = 0; i < p->atom_sx_num; i++) {
			sprintf(buf, "%s%s_%d",
				p->classname, XAUX_SX_SUFFIX, i);
			p->atom_sx[i] = XInternAtom(display, buf, False);
		}
		p->atom_sx_idx = 1;

		for (i = 0; i < p->atom_xs_num; i++) {
			sprintf(buf, "%s%s_%d",
				p->classname, XAUX_XS_SUFFIX, i);
			p->atom_xs[i] = XInternAtom(display, buf, False);
		}
		p->atom_xs_idx = 1;

		if (XGetSelectionOwner(display, p->atom_sowin) != None) {
			logf("%s: %s already exists. [%s](1)\n",
				ME_SO, ME_SO, p->classname);
			goto done_sowin;
		}

		p->sowin = XCreateSimpleWindow(display, RootWindow(display, 0),
			     0, 0, 1, 1, 0, 0, 0);

		if (p->sowin == None) {
			logf("%s: creating window for \"%s\" failed.\n",
				ME_SO, p->classname);
			goto done_sowin;
		}

	        XSelectInput(display, p->sowin, PropertyChangeMask);

	        aux->service->register_X_filter(display, p->sowin,
			ClientMessage, ClientMessage,
			xaux_so_event_filter, NULL);

		XSetSelectionOwner(display,
			p->atom_sowin, p->sowin, CurrentTime);

		if (XGetSelectionOwner(display, p->atom_sowin) != p->sowin) {
			logf("%s: %s already exists.[%s](2)\n",
				ME_SO, ME_SO, p->classname);
			XDestroyWindow(display, p->sowin);
			p->sowin = (Window)0;
			goto done_sowin;
		}
		xaux_so_launch_ext(p, aux);

done_sowin:
		p->extwin = (Window)0;

		p++;
	}

	return True;
}

static Bool
xaux_so_Create(aux_t * aux)
{
	int		i;
	pid_t		pid;

#ifdef AUX_ICID
	aux_icid_t *	aux_icid;

	if (0 == aux_initialized) {
		aux_icid_init();
	}

	if (NULL == (aux_icid = aux_icid_get(aux->service->ic_id(aux), True))) {
		return False;
	}

	aux_icid->aux = aux;
#else
	/* workaround for "stored reference to aux_t is corrupred" problem */
	aux_tmp.ic = aux->ic;
	aux_setvalue = aux->service->aux_setvalue;
	compose = aux->service->compose;
#endif
	logf("xaux_so_Create aux: 0x%x======================\n", aux);

	if (aux_initialized == 0) {
		xaux_so_init_classes(aux);
		aux_initialized = 1;
	}

	return True;
}

static Bool
xaux_so_Start(aux_t * aux, const unsigned char * p, int size)
{
	aux_data_t *	aux_data;
	xaux_class_t *	xc;
	Bool		rv;

	logf("xaux_so_Start aux: 0x%x======================\n", aux);

	aux_data = aux->service->decompose(AUX_DATA_START, p);

	if ((xc = xaux_getclass_byutfname(
		(const CARD16 *)aux_data->aux_name,
		aux_data->aux_name_length)) == NULL) {
		aux->service->decompose_free(aux_data);
		return False;
	}

	if (xc->extwin == (Window)None
		&& xaux_so_launch_ext(xc, aux) == False) {
		logf("cannot communicatie with external program [%s]",
			xc->classname);
		aux->service->decompose_free(aux_data);
		return False;
	}

	logf("so_Start[%s] im:0x%x ic:0x%x\n",
		xc->classname, aux_data->im, aux_data->ic);

	rv = xaux_so_send_message(aux, xc, aux_data->im, aux_data->ic,
		AUX_DATA_START, (Atom)0);

	aux->service->decompose_free(aux_data);

	return (rv);
}

static Bool
xaux_so_Draw(aux_t * aux, const unsigned char * p, int size)
{
	aux_data_t *	aux_data;
	XPoint		point;
	char *		string_buf;
	xaux_class_t *	xc;
	size_t		i;
	int		*ip;
	char		*sp;
	Bool		rv = True;
	size_t		total;

	logf("xaux_so_Draw aux: 0x%x======================\n", aux);

	aux_data = aux->service->decompose(AUX_DATA_DRAW, p);

	if ((xc = xaux_getclass_byutfname(
		(const CARD16 *)aux_data->aux_name,
		aux_data->aux_name_length)) == NULL) {
		aux->service->decompose_free(aux_data);
		return False;
	}


	logf("xaux_so_Draw: integer_count:%d, string_count:%d\n",
			aux_data->integer_count,aux_data->string_count);

	/* estimate enough size for string_buf */

	/* size for header */
	total = SX_SIZE_PROP_HEADER_DRAW;

	/* add size for integer_values */
	total += (sizeof (CARD32) * aux_data->integer_count);

	/* add size for string_values */
	if (aux_data->string_count > 0) {
		for (i = 0; i < aux_data->string_count; i++) {
			size_t	len;

			/* number of bytes */
			len = aux_data->string_list[i].length;

			/* consider padding */
			total +=
			((sizeof (CARD16) + len + 3) / 4) * 4;
		}
	}

	if ((string_buf = (char *)malloc(total)) == NULL) {
		aux->service->decompose_free(aux_data);

		return False;
	}

	SX_PROP_ATOM_AUX_NAME(string_buf) = xc->atom_classname;
	SX_PROP_TYPE(string_buf) = AUX_DATA_DRAW;
	SX_PROP_INDEX(string_buf) = xc->index;
	SX_PROP_IMID(string_buf) = aux_data->im;
	SX_PROP_ICID(string_buf) = aux_data->ic;
	SX_PROP_SOWIN(string_buf) = xc->sowin;

	SX_PROP_CLIENTWIN(string_buf) = aux->service->client_window(aux);

	aux->service->point(aux, &point);
	SX_PROP_POSX(string_buf) = point.x;
	SX_PROP_POSY(string_buf) = point.y;

	SX_PROP_FOCUSWIN(string_buf) = aux->service->window(aux);

	SX_PROP_INT_COUNT(string_buf) = aux_data->integer_count;
	SX_PROP_STR_COUNT(string_buf) = aux_data->string_count;

	ip = (int *)SX_PROP_INT_LIST(string_buf);

	if (aux_data->integer_count > 0) {

		for (i = 0; i < aux_data->integer_count; i++) {
			*ip++ = aux_data->integer_list[i];
		}
	}

	sp = (char *)SX_PROP_STR_LIST(string_buf);

	if (aux_data->string_count > 0) {
		char *		ob;
		size_t		obl;

		ob = sp;

		for (i = 0; i < aux_data->string_count; i++) {
			size_t		len;
			int		pn;
			unsigned char	*p;
			size_t		j;

			len = aux_data->string_list[i].length;
			p = aux_data->string_list[i].ptr;

			*(CARD16 *)ob = len;
			ob += sizeof (CARD16);

			for (j = 0; j < len; j++) {
				*ob++ = *p++;
			}
				
			pn = padding[(sizeof (CARD16) + len) % 4];

			/* padding */
			for (j = 0; j < pn; j++) {
				*ob++ = 0U;
			}
			sp = ob;
		}
	}

	logf("so_Draw[%s] im:0x%x ic:0x%x in=%d sn=%d\n",
		xc->classname, aux_data->im, aux_data->ic,
		aux_data->integer_count, aux_data->string_count);
	logf("total = %d\n", total);

	if (aux_data->integer_count != 0 || aux_data->string_count != 0) {
		rv = xaux_so_send_property(aux, xc,
			(unsigned char *)string_buf, (sp - &(string_buf[0])));
	}

	aux->service->decompose_free(aux_data);
	free(string_buf);

	return (rv);
}

static Bool
xaux_so_Done(aux_t * aux, const unsigned char * p, int size)
{
	aux_data_t	* aux_data;
	xaux_class_t *	xc;
	Bool		rv;

	logf("xaux_so_Done aux: 0x%x======================\n", aux);

	aux_data = aux->service->decompose(AUX_DATA_DONE, p);

	if ((xc = xaux_getclass_byutfname(
		(const CARD16 *)aux_data->aux_name,
		aux_data->aux_name_length)) == NULL) {
		aux->service->decompose_free(aux_data);
		return False;
	}

	logf("so_Done[%s] im:0x%x ic:0x%x\n",
		xc->classname, aux_data->im, aux_data->ic);

	rv = xaux_so_send_message(aux, xc, aux_data->im, aux_data->ic,
		AUX_DATA_DONE, (Atom)0);

	aux->service->decompose_free(aux_data);

	return (rv);

}

static Bool
xaux_so_Switched(aux_t * aux, int im_id, int on_off)
{
	return True;
}

static Bool
xaux_so_Destroy(aux_t * aux)
{
	return True;
}
