/*
Copyright 1990-2003 Sun Microsystems, Inc. All Rights Reserved.

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions: The above copyright notice and this
permission notice shall be included in all copies or substantial
portions of the Software.


THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE OPEN GROUP OR SUN MICROSYSTEMS, INC. BE LIABLE
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE EVEN IF
ADVISED IN ADVANCE OF THE POSSIBILITY OF SUCH DAMAGES.


Except as contained in this notice, the names of The Open Group and/or
Sun Microsystems, Inc. shall not be used in advertising or otherwise to
promote the sale, use or other dealings in this Software without prior
written authorization from The Open Group and/or Sun Microsystems,
Inc., as applicable.


X Window System is a trademark of The Open Group

OSF/1, OSF/Motif and Motif are registered trademarks, and OSF, the OSF
logo, LBX, X Window System, and Xinerama are trademarks of the Open
Group. All other trademarks and registered trademarks mentioned herein
are the property of their respective owners. No right, title or
interest in or to any trademark, service mark, logo or trade name of
Sun Microsystems, Inc. or its licensors is granted.

*/

#pragma ident	"$Id$"

#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
#include <unistd.h>
#include <stdarg.h>
#include <poll.h>

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

#include "trace_message.h"

#include "iiimpAux.h"
#include "xaux_common.h"
#include "xaux_ext_common.h"
#include "xaux_ext_common_priv.h"

static unsigned int	xs_seqno = 0;
static unsigned int	maxpropsz = XAUX_MAXPROPSZ;

static Bool
xaux_ext_get_sowin(
	xaux_ext_class_t	*xc,
	Display		*display)
{
	xc->sowin = XGetSelectionOwner(display, xc->atom_sowin);

	if (xc->sowin == None) {
#if defined(DEBUG_XAUX)
		fprintf(stderr, "%s: cannot find sowin.[%s]\n", xc->classname);
#endif /* defined(DEBUG_XAUX) */
		return (False);
	} else {
		XSelectInput(display, xc->sowin, PropertyChangeMask);
		return (True);
	}
}

static Bool
xaux_xs_flushq(
	Display *		dpy,
	xaux_ext_class_t *	xc)
{
	Bool		rv = True;
	unsigned char	*p;
	int		len;
	Atom		atom;

	while (xaux_propq_check(xc->propq) > 0) {
		atom = xaux_atommng_get_atom(xc->atommng_data, xc->sowin);
		if (atom == (Atom)None) {
			rv = False;
			break;
		}
		if (xaux_propq_get(xc->propq, &p, &len) >= 0) {
			XSetSelectionOwner(dpy, atom, xc->extwin, CurrentTime);
			XFlush(dpy);
			XChangeProperty(dpy, xc->sowin, atom,
				XA_STRING, 8, PropModeReplace,
				p, len);
			XFlush(dpy);
#if defined(DEBUG_XAUX)
fprintf(stderr, "(%s) send property atom=%d\n", xc->shortname, atom);
#endif /* defined(DEBUG_XAUX) */
			free(p);
		} else {
			rv = False;
			break;
		}
	}

	return (rv);
}

static Bool
xaux_xs_send_property(
	Display *		dpy,
	xaux_ext_class_t *	xc,
	const unsigned char *	p,	/* data to send */
	int			len)	/* length of data (bytes) */
{
	Atom	atom;
	int	i;
	int	qcount;

	if ((xc->sowin == None) &&
		(xaux_ext_get_sowin(xc, dpy) == False)) {
		return (False);
	}

	if ((qcount = xaux_propq_add(xc->propq, (unsigned char *)p, len))
			== -1) {
fprintf(stderr, "(%s) SETVALUE: cannot add to send buffer (mem)\n",
		xc->shortname);
		free((char *)p);
		return (False);
	} else if (qcount == -2) {
fprintf(stderr, "(%s) SETVALUE: cannot add to send buffer (size)\n",
		xc->shortname);
		free((char *)p);
		return (False);
	}

	xaux_xs_flushq(dpy, xc);

	return (True);
}

static Bool
xaux_xs_send_property_setv(
	Display			*dpy,
	xaux_ext_class_t	*xc,
	char			*prop)
{
	char		*setv = XS_DATA_TOP(prop);
	Bool		rv;

#if defined(DEBUG_XAUX)
	if (BO_SEGNO(prop) == 0) {
		if (BO_SETV_INT_COUNT(setv) > 0) {
			fprintf(stderr, "(%s) SETV: im=0x%x ic=0x%x "
				"i[0]=0x%x in=%d sn=%d "
				"segno=%d segsize=0x%x\n",
				xc->shortname,
				BO_IMID(prop), BO_ICID(prop),
				*(BO_SETV_INT_LIST(setv)),
				BO_SETV_INT_COUNT(setv),
				BO_SETV_STR_COUNT(setv),
				BO_SEGNO(prop), SX_SEGSIZE(prop));
		} else {
			fprintf(stderr, "(%s) SETV: im=0x%x ic=0x%x "
				"in=%d sn=%d "
				"segno=%d segsize=0x%x\n",
				xc->shortname,
				BO_IMID(prop), BO_ICID(prop),
				BO_SETV_INT_COUNT(setv),
				BO_SETV_STR_COUNT(setv),
				BO_SEGNO(prop), BO_SEGSIZE(prop));
		}
	} else {
		fprintf(stderr, "(%s) SETV: im=0x%x ic=0x%x "
				"segno=%d segsize=0x%x\n",
			xc->shortname,
			BO_IMID(prop), BO_ICID(prop),
			BO_SEGNO(prop), BO_SEGSIZE(prop));
	}
#endif /* defined(DEBUG_XAUX) */

	rv = xaux_xs_send_property(dpy, xc, (unsigned char *)prop,
		XS_SIZE_HEADER + XS_SEGSIZE(prop));

	return (rv);
}

typedef struct _xaux_ext_draw_state {
	Bool		stored;
	int		seqno;
	aux_ext_data_t	aux_ext_data_;
	aux_ext_data_t	*aux_ext_data;
	int		segno;
	int		int_next;
	int		str_next;
	int		str_yet;
	int		outbufsz;
	char		*outbuf_;
	char		*outbuf;
	int		maxnprops;
	int		nprops;
	unsigned char	**props;
} xaux_ext_draw_state_t;

static xaux_ext_draw_state_t	_pending_draw[XAUX_EXT_MAXDEPTH];
static xaux_ext_draw_state_t	*pending_draw_array = NULL;

static Bool
xaux_ext_clean_draw_state(xaux_ext_draw_state_t *state, Bool initial)
{
	int		maxnprops_sv;
	unsigned char	**props_sv;
	aux_ext_data_t *data;
	int		i;

	if (!initial) {
		if (state->aux_ext_data != NULL) {
			free(state->aux_ext_data->integer_list);
			data = state->aux_ext_data;
			if (NULL != data->string_list) {
				for (i = 0; i < data->string_count; i++) {
					free((data->string_list + i)->ptr);
				}
			}
			free(state->aux_ext_data->string_list);
		}
		free(state->outbuf_);

		for (i = 0; i < state->nprops; i++) {
			if (state->props[i] != NULL) {
				XFree(state->props[i]);
			}
		}

		maxnprops_sv = state->maxnprops;
		props_sv = state->props;
	}

	memset(state, 0, sizeof (xaux_ext_draw_state_t));

	if (initial) {
		state->maxnprops = XAUX_MAXNPROPS_INIT;
		if ((state->props = (unsigned char **)malloc(
				sizeof (unsigned char *)
				* state->maxnprops)) == NULL) {
			return (False);
		}
	} else {
		state->maxnprops = maxnprops_sv;
		state->props = props_sv;
	}

	state->nprops = 0;
	state->stored = False;
	state->aux_ext_data = &(state->aux_ext_data_);

	return (True);
}

static Bool
xaux_ext_add_prop_to_draw_state(
	xaux_ext_draw_state_t	*state,
	unsigned char		*prop)
{
	int		maxnprops = state->maxnprops;
	int		nprops = state->nprops;
	unsigned char	**props = state->props;

	if ((state->nprops) >= state->maxnprops) {
		maxnprops += XAUX_MAXNPROPS_INIT;
		props = (unsigned char **)realloc(props,
			sizeof (unsigned char *) * maxnprops);
		if (props == NULL) {
			return (False);
		}
		state->maxnprops = maxnprops;
		state->props = props;
	}

	state->props[state->nprops] = prop;
	state->nprops++;
	return (True);
}

static Bool
xaux_ext_init_draw_state(
	xaux_ext_draw_state_t	*state,
	xaux_ext_priv_t		*priv,
	xaux_ext_class_t	*xc,
	unsigned char		*prop)
{
	aux_ext_data_t	*aux_ext_data;
	unsigned char	*draw;

	if (xaux_ext_clean_draw_state(state, False) == False) {
		return (False);
	}

	draw = SX_DATA_TOP(prop);

	state->seqno = BO_SEQNO(prop);

	aux_ext_data = state->aux_ext_data;
	aux_ext_data->type = AUX_EXT_DATA_DRAW;
	aux_ext_data->im = BO_IMID(prop);
	aux_ext_data->ic = BO_ICID(prop);
	aux_ext_data->aux_index = BO_INDEX(prop);
	aux_ext_data->aux_name = (unsigned char *)xc->classname;
	aux_ext_data->aux_name_length =
		strlen((const char *)aux_ext_data->aux_name);
	aux_ext_data->integer_count = BO_DRAW_INT_COUNT(prop);
	aux_ext_data->string_count = BO_DRAW_STR_COUNT(prop);
	aux_ext_data->clientwin = BO_DRAW_CLIENTWIN(prop);
	aux_ext_data->focuswin = BO_DRAW_FOCUSWIN(prop);
	aux_ext_data->point.x = BO_DRAW_POSX(prop);
	aux_ext_data->point.y = BO_DRAW_POSY(prop);

	state->segno = BO_SEGNO(prop);

	state->str_yet = 0;

	if (aux_ext_data->integer_count > 0) {
		if ((aux_ext_data->integer_list = (int *)malloc(sizeof (int)
				* aux_ext_data->integer_count)) == NULL) {
			(void)xaux_ext_clean_draw_state(state, False);
			return (False);
		}
	} else {
		aux_ext_data->integer_list = NULL;
	}

	if (aux_ext_data->string_count > 0) {

		if ((aux_ext_data->string_list = (aux_ext_string_t *)malloc(
				sizeof (aux_ext_string_t) *
				aux_ext_data->string_count)) == NULL) {
			(void)xaux_ext_clean_draw_state(state, False);
			return (False);
		}

		/*
		 * when conversion to locale codeset is needed,
		 * allocate buffer for conversion output.
		 */
		if (priv->do_mbconv) {
			size_t	c;

			/* total length (in bytes) of strings */
			c  = BO_DRAW_TOTAL_STR_LEN(prop); 
			/* converting to number of chars */
			c /= sizeof (CARD16);
			/* multiply with MB_CUR_MAX */
			c *= MB_CUR_MAX;
			/* for null termination of each string */
			c += aux_ext_data->string_count;

			state->outbufsz = c;
			/* allocate outbuf_ */
			if ((state->outbuf_ = (char *)malloc(state->outbufsz))
					== NULL) {
				(void)xaux_ext_clean_draw_state(state, False);
				return (False);
			}
			state->outbuf = state->outbuf_;
		}
	}

	return (True);
}

static Bool
xaux_ext_process_property_update(
	xaux_ext_priv_t *	priv,
	Window			window,
	Atom			atom)
{
	xaux_ext_draw_state_t	*state;
	aux_ext_data_t		*aux_ext_data;
	Display	*	dpy = priv->dpy;
	Atom		actual_type_return;
	int		actual_format_return;
	unsigned long	nitem_return;
	unsigned long	bytes_after_return;
	unsigned char *	prop;
	unsigned char *	proptail;
	unsigned char *	draw;
	int		r;
	int		imid;
	unsigned char *	p;
	int		i;
	int		n=0;
	xaux_ext_class_t	*xc = &(priv->class);
	XPoint		point;
	Bool		rv = True;
	int		type;
	int		*ip;
	unsigned char	*sp;
	xaux_ext_draw_state_t	*pending_draw;

	r = XGetWindowProperty(dpy, window,
				atom, 0, INT_MAX, True,
				XA_STRING, &actual_type_return,
				&actual_format_return, &nitem_return,
				&bytes_after_return, &prop);

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

	type = BO_AUXTYPE(prop);

	proptail = prop + SX_SIZE_HEADER + BO_SEGSIZE(prop);

	if (type == AUX_DATA_START || type == AUX_DATA_DONE) {
		aux_ext_data_t	aux_ext_data_;

		aux_ext_data = &(aux_ext_data_);

		if (BO_ATOM_AUX_NAME(prop) != xc->atom_classname) {
			rv = False;
			goto cleanup_return;
		}

		aux_ext_data->im = BO_IMID(prop);
		aux_ext_data->ic = BO_ICID(prop);
		aux_ext_data->aux_index = BO_INDEX(prop);

		switch (type) {
		case AUX_EXT_DATA_START:
#if defined(DEBUG_XAUX)
			fprintf(stderr, "(%s) START%s: im=0x%x ic=0x%x\n",
				xc->shortname,
				((window == xc->sowin) ? "(sowin)" : ""),
				aux_ext_data->im, aux_ext_data->ic);
#endif /* defined(DEBUG_XAUX) */
			if (priv->cb_start != NULL) {
				rv = (*priv->cb_start)(xc,
					aux_ext_data, priv->cb_start_data);
			} else {
#if defined(DEBUG_XAUX)
	fprintf(stderr, "(%s) START callback not registered\n", xc->shortname);
#endif /* defined(DEBUG_XAUX) */
				rv = False;
			}
			break;
		case AUX_EXT_DATA_DONE:
#if defined(DEBUG_XAUX)
			fprintf(stderr, "(%s) DONE%s: im=0x%x ic=0x%x\n",
				xc->shortname,
				((window == xc->sowin) ? "(sowin)" : ""),
				aux_ext_data->im, aux_ext_data->ic);
#endif /* defined(DEBUG_XAUX) */
			if (priv->cb_done != NULL) {
				rv = (*priv->cb_done)(xc,
					aux_ext_data, priv->cb_done_data);
			} else {
#if defined(DEBUG_XAUX)
	fprintf(stderr, "(%s) DONE callback not registered\n", xc->shortname);
#endif /* defined(DEBUG_XAUX) */
				rv = False;
			}
			break;
		}

		rv = True;
		goto cleanup_return;
	}

	draw = SX_DATA_TOP(prop);

	if (pending_draw_array == NULL) {
		int	i;
		pending_draw_array = &(_pending_draw[0]);

		for (i = 0; i < XAUX_EXT_MAXDEPTH; i++) {
			xaux_ext_clean_draw_state(
				&(pending_draw_array[i]), True);
		}
	}

#if defined(DEBUG_XAUX)
fprintf(stderr, "(%s) DRAW: using depth=%d pending_draw\n",
		xc->shortname, xc->depth);
#endif /* defined(DEBUG_XAUX) */
	pending_draw = &(pending_draw_array[xc->depth]);

	if (BO_SEGNO(prop) > 0) {
		if (pending_draw->stored == True) {
			if (BO_SEGNO(prop) == (pending_draw->segno + 1)) {
#if defined(DEBUG_XAUX)
fprintf(stderr, "(%s) DRAW: segno=%d, continue with stored segno=%d\n",
		xc->shortname, BO_SEGNO(prop), pending_draw->segno);
#endif /* defined(DEBUG_XAUX) */
				state = pending_draw;
				ip = (int *)draw;
			} else {
#if defined(DEBUG_XAUX)
fprintf(stderr, "(%s) DRAW: stored segno mismatch (%d expected %d actual)\n",
		xc->shortname, pending_draw->segno + 1, BO_SEGNO(prop));
#endif /* defined(DEBUG_XAUX) */
				xaux_ext_clean_draw_state(pending_draw, False);
				rv = False;
				goto cleanup_return;
			}
		} else {
#if defined(DEBUG_XAUX)
fprintf(stderr, "(%s) DRAW: received segno=%d but no stored segs\n",
		xc->shortname, BO_SEGNO(prop));
#endif /* defined(DEBUG_XAUX) */
			rv = False;
			goto cleanup_return;
		}
	} else {
		if (pending_draw->stored == True) {
#if defined(DEBUG_XAUX)
fprintf(stderr, "(%s) DRAW: stored but received segno=0 (%d expected)\n",
				xc->shortname, pending_draw->segno + 1);
#endif /* defined(DEBUG_XAUX) */
			xaux_ext_init_draw_state(pending_draw, priv, xc, prop);
		} else {
			xaux_ext_init_draw_state(pending_draw, priv, xc, prop);
		}
		state = pending_draw;
		ip = (int *)BO_DRAW_INT_LIST(draw);
	}

	aux_ext_data = state->aux_ext_data;

	for (i = state->int_next; i < aux_ext_data->integer_count; i++) {
		if ((unsigned char *)ip >= proptail) {
			state->int_next = i;
			state->segno = BO_SEGNO(prop);
			xaux_ext_add_prop_to_draw_state(state, prop);
			state->stored = True;
#if defined(DEBUG_XAUX)
fprintf(stderr, "(%s) DRAW: received up to int[%d]\n", xc->shortname, i - 1);
#endif /* defined(DEBUG_XAUX) */
			return (True);
		}
		aux_ext_data->integer_list[i] = BO_CARD32(prop, ip);
		ip++;
	}

	state->int_next = i;

	sp = (unsigned char *)ip;

	for(i = state->str_next; i < aux_ext_data->string_count; i++) {
		char *		ib;
		size_t		ibl;
		size_t		obl = state->outbufsz;

		if (sp >= proptail) {
			state->str_next = i;
			state->segno = BO_SEGNO(prop);
			xaux_ext_add_prop_to_draw_state(state, prop);
			state->stored = True;
#if defined(DEBUG_XAUX)
fprintf(stderr, "(%s) DRAW: received up to str[%d]\n", xc->shortname, i - 1);
#endif /* defined(DEBUG_XAUX) */
			return (True);
		}

		/* assign length of a string to ibl */
		ibl = BO_CARD16(prop, sp);
		/* move sp to head of the string */
		sp += sizeof(CARD16);
		/* assign head of the string to ib */
		ib = (char *)sp;
		/* move sp to point length of next string */
		sp += (ibl + sizeof (unsigned char) * 2 +
			padding[(sizeof(CARD16)
			+ ibl + sizeof (unsigned char) * 2) % 4]);

		if (priv->do_mbconv) { /* convert to locale codeset */
			char *		ob;
			size_t		obl_save;

			ob = state->outbuf;
			obl_save = obl;
			aux_ext_data->string_list[i].ptr = (unsigned char *)ob;
			utf16_mb((const char **)&ib, &ibl, &ob, &obl);
			aux_ext_data->string_list[i].length = obl_save - obl;
			state->outbuf =
				ob + aux_ext_data->string_list[i].length;
			/* null termination */
			*(state->outbuf++) = '\0';
			*(state->outbuf++) = '\0';
		} else { /* pass as UTF-16 string */
			aux_ext_data->string_list[i].ptr =
				(unsigned char *)malloc((sizeof (CARD16)) * ibl);
			if (1 == (*((CARD16 *)(prop)))) {
				memcpy((char *)(aux_ext_data->string_list[i].ptr),
				       ib,
				       (sizeof (CARD16)) * ibl);
			} else {
				swab(ib,
				     (char *)(aux_ext_data->string_list[i].ptr),
				     (sizeof (CARD16)) * ibl);
			}
			aux_ext_data->string_list[i].length = ibl;
		}
	}

#if defined(DEBUG_XAUX)
	if (aux_ext_data->integer_count > 0) {
		fprintf(stderr, "(%s) DRAW%s: im=0x%x ic=0x%x "
				"i[0]=0x%x in=%d sn=%d\n",
			xc->shortname,
			((window == xc->sowin) ? "(sowin)" : ""),
			aux_ext_data->im, aux_ext_data->ic,
			aux_ext_data->integer_list[0],
			aux_ext_data->integer_count,
			aux_ext_data->string_count);
	} else {
		fprintf(stderr, "(%s) DRAW%s: im=0x%x ic=0x%x in=%d sn=%d\n",
			xc->shortname,
			((window == xc->sowin) ? "(sowin)" : ""),
			aux_ext_data->im, aux_ext_data->ic,
			aux_ext_data->integer_count,
			aux_ext_data->string_count);
	}
#endif /* defined(DEBUG_XAUX) */
	if (priv->cb_draw != NULL) {
		rv = (*priv->cb_draw)(xc, aux_ext_data, priv->cb_draw_data);
	} else {
#if defined(DEBUG_XAUX)
	fprintf(stderr, "(%s) DRAW callback not registered\n", xc->shortname);
#endif /* defined(DEBUG_XAUX) */
		rv = False;
	}

	xaux_ext_clean_draw_state(state, False);

cleanup_return:
	if (prop != NULL) {
		XFree(prop);
	}

	return (rv);
}

static Bool
xaux_ext_increment_depth(
	xaux_ext_class_t	*xc
)
{
	xc->depth++;

	if (xc->depth > XAUX_EXT_MAXDEPTH) {
		fprintf(stderr,
			"(%s) depth exceeded (%d)\n", xc->shortname, xc->depth);
		xc->depth--;
		return (False);
	} else {
#if defined(DEBUG_XAUX)
		fprintf(stderr, "(%s) depth=%d\n", xc->shortname, xc->depth);
#endif /* defined(DEBUG_XAUX) */
		return (True);
	}
}

static Bool
xaux_ext_decrement_depth(
	xaux_ext_class_t	*xc
)
{
	xc->depth--;
#if defined(DEBUG_XAUX)
	fprintf(stderr, "(%s) depth=%d\n", xc->shortname, xc->depth);
#endif /* defined(DEBUG_XAUX) */
	return (True);
}

static Bool
xaux_ext_process_property_notify(
	xaux_ext_priv_t *	priv,
	Window			win,
	Atom			atom)
{
	Display	*	dpy = priv->dpy;
	xaux_ext_class_t	*xc = &(priv->class);
	Atom		*atoms;
	int		r;		/* return of Xlib functions */
	Bool		rv;		/* return of xaux functions */
	int		i;

	if (win == (Window)None) {
		return (False);
	}

	if (xaux_ext_increment_depth(xc) == False) {
		return (False);
	}
	
#if defined(DEBUG_XAUX)
fprintf(stderr, "(%s) process new atom=%d at depth=%d\n",
			xc->shortname, atom, xc->depth);
#endif /* defined(DEBUG_XAUX) */
	rv = xaux_ext_process_property_update(priv, win, atom);

	xaux_ext_decrement_depth(xc);

	return (True);
}

static Bool
xaux_ext_dispatch_propnot_extwin(
	xaux_ext_priv_t		*priv,
	Window			win,
	XPropertyEvent		*pev)
{
	Bool			rv = False;
	xaux_ext_class_t *	xc = &(priv->class);

	if (xaux_atommng_check_atom(xc->atommng_data, False, pev->atom)
			== True) {
		/* sx */
		if (pev->state == PropertyNewValue) {
			rv = xaux_ext_process_property_notify(priv,
				(win == (Window)None) ?
				pev->window : win, pev->atom);
		}
		rv = True;
	}

	return (rv);
}

static Bool
xaux_ext_dispatch_propnot_sowin(
	xaux_ext_priv_t		*priv,
	Window			win,
	XPropertyEvent		*pev)
{
	Bool			rv = False;
	xaux_ext_class_t *	xc = &(priv->class);

	if (xaux_atommng_check_atom(xc->atommng_data, True, pev->atom)
			== True) {
		/* xs */
		if (pev->state == PropertyDelete) {
			if (xaux_atommng_process_delete(xc->atommng_data, pev)
					== True) {
				xaux_xs_flushq(priv->dpy, xc);
			}
		}
		rv = True;
	}

	return (rv);
}

static Bool
xaux_ext_event_handler(
	xaux_ext_handle_t	hdl,	/* handle */
	Window			win,	/* window */
	XEvent *		ev,	/* event */
	void *			sodata)	/* so's data */
{
	xaux_ext_priv_t *	priv = (xaux_ext_priv_t *)sodata;
	Bool			rv = False;
	xaux_ext_class_t *	xc = &(priv->class);

	TRACE_MESSAGE('E', ("aux_ext:AuxEventHandler: %d\n", event->type));

	switch (ev->type) {
	case PropertyNotify:
		if (ev->xproperty.window == xc->extwin) {
			rv = xaux_ext_dispatch_propnot_extwin(priv, win,
				(XPropertyEvent *)(&(ev->xproperty)));
		}
		break;
	case SelectionClear:
		if ((rv = xaux_atommng_process_selclr(xc->atommng_data,
				(XSelectionClearEvent *)ev)) == True) {
			xaux_xs_flushq(priv->dpy, xc);
		}
		break;
	default:
#if defined(DEBUG_XAUX)
	fprintf(stderr, "(%s) ignore event type=%d\n", xc->shortname, ev->type);
#endif /* defined(DEBUG_XAUX) */
		break;
	}

	return (rv);
}

static void
xaux_ext_notify_extwin(
	xaux_ext_class_t *	xc,
	Display *		dpy)
{
	if (xc->sowin == (Window)None) {
		return;
	}


	XChangeProperty(dpy, xc->sowin, xc->atom_extwin, XA_WINDOW,
		32, PropModeReplace, (unsigned char *)&(xc->extwin), 1);
#if defined(DEBUG_XAUX)
fprintf(stderr, "ext: notify_extwin: extwin 0x%x to sowin 0x%0x\n",
	xc->extwin, xc->sowin);
#endif /* defined(DEBUG_XAUX) */

	XFlush(dpy);
}

/* xaux_ext_open: start to use xaux_ext */

static xaux_ext_handle_t		/* handle */
xaux_ext_open(
	const char *	classname,	/* classname (ASCII) */
	Display *	dpy,		/* display */
	Window		extwin,		/* window */
	...)				/* optional values */
{
	va_list		ap;
	int		ptr_no = 0;

	xaux_ext_priv_t *	priv;
	xaux_ext_class_t *	xc;
	char			buf[PATH_MAX];

	if (dpy == NULL || classname == NULL) {
		return (NULL);
	}

	priv = (xaux_ext_priv_t *)malloc(sizeof (xaux_ext_priv_t));

	bzero((void *)priv, sizeof (xaux_ext_priv_t));

	priv->dpy = dpy;
	priv->do_mbconv = False;

	xc = &(priv->class);

	bzero((void *)xc, sizeof (xaux_ext_class_t));

	xc->classname = (const char *)strdup(classname);

	if (xc->classname == NULL) {
		free(priv);
		return (NULL);
	}

	xc->shortname = strrchr(xc->classname, '.');
	if (xc->shortname == NULL) {
		xc->shortname = (char *)xc->classname;
	} else {
		xc->shortname++;
	}
	xc->shortname = strdup(xc->shortname);
	if (xc->shortname == NULL) {
		xc->shortname = "";
	} else {
		if (strlen(xc->shortname) > 4) {
			xc->shortname[4] = '\0';
		}
	}

	xc->atom_classname = XInternAtom(dpy, xc->classname, False);

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

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

	sprintf(buf, "%s_sx", xc->classname);
	xc->atom_sx = XInternAtom(dpy, buf, False);

	sprintf(buf, "%s_xs", xc->classname);
	xc->atom_xs = XInternAtom(dpy, buf, False);

	xc->atommng_data = xaux_atommng_alloc_data(
		xc->classname, True /* means "xs" */, dpy);
	
	if (xc->atommng_data == NULL) {
		free((void *)xc->classname);
		free(priv);
		return (NULL);
	}

	xc->propq = xaux_propq_alloc(XAUX_EXT_MAXPROPQDATASZ);

	if (xc->propq == NULL) {
		xaux_atommng_free_data(xc->atommng_data);
		free((void *)xc->classname);
		free(priv);
		return (NULL);
	}

	xc->sowin = (Window)0;

	xc->extwin = extwin;

	if (xc->extwin == (Window)0) {
		xaux_atommng_free_data(xc->atommng_data);
		free((void *)xc->classname);
		free(priv);
		return (NULL);
	}
		
	va_start(ap, 128);

	/* optional values can be set here */

	va_end(ap);

	if (XGetSelectionOwner(dpy, xc->atom_extwin) != None) {
#if defined(DEBUG_XAUX)
		fprintf(stderr, "%s: %s already exists.[%s](1)\n",
			ME_EXT, ME_EXT, xc->classname);
#endif /* defined(DEBUG_XAUX) */
		xaux_atommng_free_data(xc->atommng_data);
		free((void *)xc->classname);
		free(priv);
		return (NULL);
	}

	XSetSelectionOwner(dpy, xc->atom_extwin, xc->extwin, CurrentTime);

	if (XGetSelectionOwner(dpy, xc->atom_extwin) != xc->extwin) {
#if defined(DEBUG_XAUX)
		fprintf(stderr, "%s: %s already exists.[%s](2)\n",
			ME_EXT, ME_EXT, xc->classname);
#endif /* defined(DEBUG_XAUX) */
		xaux_atommng_free_data(xc->atommng_data);
		free((void *)xc->classname);
		free(priv);
		return (NULL);
	}

#if defined(DEBUG_XAUX)
		fprintf(stderr, "(%s) extwin created: 0x%0x\n", 
			xc->shortname, xc->extwin);
#endif /* defined(DEBUG_XAUX) */

	if (xaux_ext_get_sowin(xc, dpy) == True) {
		/* notify extwin */
		xaux_ext_notify_extwin(xc, dpy);
	}

	xc->depth = 0;

	xc->maxnprops = XAUX_EXT_MAXNPROPS;

	if ((xc->props = (Atom *)malloc(sizeof (Atom) * xc->maxnprops))
			== NULL) {
		free((void *)xc->classname);
		free(priv);
		return (NULL);
	} else {
		int	i;
		for (i = 0; i < xc->maxnprops; i++) {
			xc->props[i] = (Atom)None;
		}
	}

	return ((xaux_ext_handle_t)priv);
}

/* xaux_ext_close: finish to use xaux_ext */

static void
xaux_ext_close(
	xaux_ext_handle_t	hdl)		/* handle */
{
	xaux_ext_priv_t		*priv = (xaux_ext_priv_t *)hdl;

	if (priv != NULL) {
		xaux_ext_class_t * xc = (xaux_ext_class_t *)&(priv->class);
		xaux_atommng_free_data(xc->atommng_data);
		xaux_propq_free(xc->propq);
		free((void *)xc->classname);
		free(xc->props);
		free(priv);
	}

	return;
}

/*
 * xaux_ext_registercb_addevhandler:
 * register callback to add event handler
 */

static Bool
xaux_ext_registercb_addevhandler(
	xaux_ext_handle_t		hdl,	/* handle */
	xaux_ext_cb_addevhandler_t	cb,	/* callback */
	void *				data)	/* ext's data */
{
	xaux_ext_priv_t		*priv = (xaux_ext_priv_t *)hdl;

	if (priv != NULL && cb != NULL) {
#if defined(DEBUG_XAUX)
	fprintf(stderr, "(%s) cb_addevhandler registered\n",
		priv->class.shortname);
#endif /* defined(DEBUG_XAUX) */
		priv->cb_addevhandler = cb;
		priv->cb_addevhandler_data = data;
		/* execution of callback maybe moved to somewhere else */
		(*priv->cb_addevhandler)(hdl,
			PropertyChangeMask, True,
			xaux_ext_event_handler, hdl,
			priv->cb_addevhandler_data);
		/* process data arrived before evhandler registered (if any) */
#if defined(DEBUG_XAUX)
	fprintf(stderr,
	"(%s) process data arrived before evhanlder registered (if any)\n",
		priv->class.shortname);
#endif /* defined(DEBUG_XAUX) */
		(void)xaux_ext_process_property_notify(priv,
			priv->class.extwin,
			xaux_atommng_know_atom(priv->class.atommng_data, False)
			);
		return (True);
	} else {
		return (False);
	}
}

static Bool
xaux_ext_process_detoured_properties(
	xaux_ext_handle_t	hdl)
{
	xaux_ext_priv_t		*priv = (xaux_ext_priv_t *)hdl;
	xaux_ext_class_t	*xc = &(priv->class);

	if (xc->sowin != None) {
		if ((priv->cb_start == NULL) ||
				(priv->cb_draw == NULL) ||
				(priv->cb_done == NULL)) {
			return (False);
		}
		/* process sx properties which has been put on sowin */
#if defined(DEBUG_XAUX)
	fprintf(stderr, "(%s) check detoured properties\n",
				priv->class.shortname);
#endif /* defined(DEBUG_XAUX) */
		xaux_ext_process_property_notify(priv, xc->sowin,
			xaux_atommng_know_atom(priv->class.atommng_data, False)
			);
	}
}

/*
 * xaux_ext_registercb_evhandler:
 * register callback to remove event handler
 */

static Bool
xaux_ext_registercb_rmevhandler(
	xaux_ext_handle_t		hdl,	/* handle */
	xaux_ext_cb_addevhandler_t	cb,	/* callback */
	void *				data)	/* ext's data */
{
	xaux_ext_priv_t		*priv = (xaux_ext_priv_t *)hdl;

	if (priv != NULL && cb != NULL) {
#if defined(DEBUG_XAUX)
	fprintf(stderr, "(%s) cb_rmevhandler registered\n",
		priv->class.shortname);
#endif /* defined(DEBUG_XAUX) */
		priv->cb_rmevhandler = cb;
		priv->cb_rmevhandler_data = data;
		return (True);
	} else {
		return (False);
	}
}

/* xaux_ext_registercb_start: register callback for AUX_START */

static Bool
xaux_ext_registercb_start(
	xaux_ext_handle_t	hdl,	/* handle */
	xaux_ext_cb_start_t	cb,	/* callback */
	void *			data)	/* client data */
{
	xaux_ext_priv_t		*priv = (xaux_ext_priv_t *)hdl;

	if (priv != NULL && cb!= NULL) {
		priv->cb_start = cb;
		xaux_ext_process_detoured_properties(hdl);
		return (True);
	} else {
		return (False);
	}
}

/* xaux_ext_registercb_draw: register callback for AUX_DRAW */

static Bool
xaux_ext_registercb_draw(
	xaux_ext_handle_t	hdl,	/* handle */
	xaux_ext_cb_draw_t	cb,	/* callback */
	void *			data)	/* client data */
{
	xaux_ext_priv_t		*priv = (xaux_ext_priv_t *)hdl;

	if (priv != NULL && cb != NULL) {
		priv->cb_draw = cb;
		xaux_ext_process_detoured_properties(hdl);
		return (True);
	} else {
		return (False);
	}
}

/* xaux_ext_registercb_done: register callback for AUX_DONE */

static Bool
xaux_ext_registercb_done(
	xaux_ext_handle_t	hdl,	/* handle */
	xaux_ext_cb_done_t	cb,	/* callback */
	void *			data)	/* client data */
{
	xaux_ext_priv_t		*priv = (xaux_ext_priv_t *)hdl;

	if (priv != NULL && cb != NULL) {
		priv->cb_done = cb;
		xaux_ext_process_detoured_properties(hdl);
		return (True);
	} else {
		return (False);
	}
}

/* xaux_ext_setvalue: send AUX_SETVALUE to server */

static Bool
xaux_ext_setvalue(
	xaux_ext_handle_t	hdl,		/* handle */
	aux_ext_data_t		*aux_ext_data)	/* callback */
{
	xaux_ext_priv_t		*priv = (xaux_ext_priv_t *)hdl;
	xaux_ext_class_t	*xc = (xaux_ext_class_t *)&(priv->class);
	Display		*dpy = priv->dpy;
	static char	*string_buf = NULL;
	char		*setv;
	static size_t	bufsize = 0;
	size_t		i;
	int		*ip;
	char		*sp;
	Bool		rv = True;
	int		int_next;
	int		str_next;
	int		total_str_len;
	int		segno = 0;
	char		*propbuf = NULL;
	char		*propbuftail = NULL;
	int		allocsz;

	if (aux_ext_data == NULL) {
		/* reset; free string_buf */
		if (string_buf != NULL) {
			free(string_buf);
			string_buf = NULL;
		}
		bufsize = 0;
		return True;
	}

	if (aux_ext_data->string_count > 0) {
		size_t	maxlen = 0;
		size_t	newbufsize;

		for (i = 0, total_str_len = 0;
				i < aux_ext_data->string_count; i++) {
			if (priv->do_mbconv == True) {
				total_str_len +=
					((aux_ext_data->string_list[i].length 
						+ 1) * sizeof (CARD16));
				if (aux_ext_data->string_list[i].length
						> maxlen) {
					maxlen =
					aux_ext_data->string_list[i].length;
				}
			} else {
				total_str_len +=
					aux_ext_data->string_list[i].length;
			}
		}

		if (priv->do_mbconv == True) {
			newbufsize = maxlen * sizeof (CARD16);
			/*
			 * "+1" is required by mb_utf16() method.
			 * The method uses the area for BOM.
			 */
			newbufsize += sizeof (CARD16);

			if (newbufsize > bufsize) {
				char	*new_string_buf;
				new_string_buf =
					realloc(string_buf, newbufsize);
				if (new_string_buf == NULL) {
					free(string_buf);
					bufsize = 0;
					return (False);
				} else {
					bufsize = newbufsize;
					string_buf = new_string_buf;
				}
			}
		}
	}

	allocsz = XS_SIZE_HEADER + XS_SIZE_SETV_HEADER /* header */
		+ (sizeof (CARD32) * aux_ext_data->integer_count) /* int */
		+ total_str_len /* str */
		+ (sizeof (CARD16) * aux_ext_data->string_count) /* strlen */
		+ (3 * aux_ext_data->string_count); /* pad */
	
	if (allocsz > maxpropsz) {
		allocsz = maxpropsz;
	}

	for (segno = 0; ; segno++) {
		if (segno > 0) {
			allocsz = maxpropsz;
		}
		if ((propbuf = (char *)malloc(allocsz)) == NULL) {
			goto discard;
		}
		propbuftail = propbuf + allocsz;

		XS_PROTOVERS_MAJOR(propbuf) = XAUX_PROTOVERS_MAJOR;
		XS_PROTOVERS_MINOR(propbuf) = XAUX_PROTOVERS_MINOR;
		XS_SEQNO(propbuf) = xs_seqno;
		XS_ATOM_AUX_NAME(propbuf) = xc->atom_classname;
		XS_INDEX(propbuf) = xc->index;
		XS_PRIVTYPE(propbuf) = (CARD8)XAUX_PRIVTYPE_PUBLIC;
		XS_AUXTYPE(propbuf) = (CARD8)AUX_DATA_SETVALUE;
		XS_IMID(propbuf) = aux_ext_data->im;
		XS_ICID(propbuf) = aux_ext_data->ic;
		XS_MORESEGS(propbuf) = 0;
		XS_SEGNO(propbuf) = segno;

		setv = (char *)XS_DATA_TOP(propbuf);

		if (segno == 0) {
			XS_SETV_INT_COUNT(setv) = aux_ext_data->integer_count;
			XS_SETV_STR_COUNT(setv) = aux_ext_data->string_count;
			int_next = 0;
			str_next = 0;
			ip = (int *)XS_SETV_INT_LIST(setv);
		} else {
			ip = (int *)setv;
		}

		for (i = int_next; i < aux_ext_data->integer_count; i++) {
			if ((char *)(ip + 1) > propbuftail) {
				/* avoid to exceed tail of buffer */
				XS_MORESEGS(propbuf) = 1;
				XS_SEGSIZE(propbuf) = (char *)ip - setv;
				rv = xaux_xs_send_property_setv(
					dpy, xc, propbuf);
				if (rv == False) {
					goto discard;
				}
#if defined(DEBUG_XAUX)
fprintf(stderr, "(%s) SETV: sent up to integer[%d]\n", xc->shortname, i - 1);
#endif /* defined(DEBUG_XAUX) */
				int_next = i;
				break;
			}
			*ip++ = aux_ext_data->integer_list[i];
		}

		if (i < aux_ext_data->integer_count) {
			continue;
		}

		int_next = i;		/* no more int */
		sp = (char *)ip;	/* int array tail is str array head */

		for (i = str_next; i < aux_ext_data->string_count; i++) {
			size_t		len;
			int		pn;
			unsigned char	*p;
			size_t		j;
			char *		ib;
			size_t		ibl;

			ib = (char *)(aux_ext_data->string_list[i].ptr);
			ibl = (size_t)aux_ext_data->string_list[i].length;

			if (priv->do_mbconv) {
				char *		ob;
				size_t		obl;

				ob = string_buf;
				obl = bufsize;
				mb_utf16((const char **)&ib, &ibl, &ob, &obl);
				len = bufsize - obl;
				p = (unsigned char *)string_buf;
			} else {
				len = ibl;
				p = (unsigned char *)ib;
			}

			pn = padding[(sizeof (CARD16) + len) % 4];

			if ((sp + sizeof (CARD16) + len + pn) > propbuftail) {
				/* avoid to exceed tail of buffer */
				if (i == str_next) {
					/* single string too long */
					goto discard;
				}
				XS_MORESEGS(propbuf) = 1;
				XS_SEGSIZE(propbuf) = (char *)sp - setv;
				rv = xaux_xs_send_property_setv(
					dpy, xc, propbuf);
				if (rv == False) {
					goto discard;
				}
#if defined(DEBUG_XAUX)
fprintf(stderr, "(%s) SETV: sent up to string[%d]\n", xc->shortname, i - 1);
#endif /* defined(DEBUG_XAUX) */
				str_next = i;
				break;
			} else {
				/* put length */
				*(CARD16 *)sp = len;
				sp += sizeof (CARD16);
				/* put string */
				for (j = 0; j < len; j++) {
					*sp++ = *p++;
				}
				/* put padding */
				for (j = 0; j < pn; j++) {
					*sp++ = 0U;
				}
			}
		}

		if (i < aux_ext_data->string_count) {
			continue;
		}

		XS_MORESEGS(propbuf) = 0;
		XS_SEGSIZE(propbuf) = (int)(sp - setv);
		rv = xaux_xs_send_property_setv(dpy, xc, propbuf);
		if (rv == False) {
			goto discard;
		}
		break;
		/* NOTREACHED */
	}

discard:
	xs_seqno++;

	return (rv);
}

xaux_ext_methods_t	xaux_ext_methods = {
	xaux_ext_open,
	xaux_ext_close,
	xaux_ext_registercb_start,
	xaux_ext_registercb_draw,
	xaux_ext_registercb_done,
	xaux_ext_setvalue,
	xaux_ext_registercb_addevhandler,
	xaux_ext_registercb_rmevhandler,
	NULL,
	NULL
};
