/*
 * Copyright (c) 1999 by Sun Microsystem, Inc.
 * All rights reserved.
 *
 * xaux_so_common.c
 */

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

#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <limits.h>
#include <libgen.h>
#include <sys/param.h>

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

#include "iiimpAux.h"

#include "xaux_common.h"
#include "xaux_so_common.h"

extern xaux_class_t xaux_classes[];

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);

static int		aux_initialized = 0;

#define HASH_SIZE 137
static aux_icid_t	aux_icid[HASH_SIZE];

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

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 *);

static Bool xaux_set_property (xaux_class_t *xc, unsigned char *prop_return);

/* workaround_slowdtlogin */
static Atom	atom_xbe = None;
static Bool	is_server = False;
static Atom	atom_launch_this = None;
static Atom	atom_draw_this = None;
/* 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) {
		return False;
	}

	xc->extwin = XGetSelectionOwner(display, xc->atom_extwin);
	if (xc->extwin == None) {

		if (XAUX_MAXRETRY_EXTWIN <= 0) {
			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_send_aux_launch_to_external (xaux_class_t *xc,
				     aux_t *aux)
{
  Display *display;
  XClientMessageEvent event;
  Window external;
  char string_buf[20];

  display = aux->service->display(aux);
  external = XGetSelectionOwner(display, atom_xbe);

  if (atom_launch_this == None)
    atom_launch_this = XInternAtom(display, "xaux_launch_this", False);

  SX_PROP_ATOM_AUX_NAME(string_buf) = xc->atom_classname;
  SX_PROP_TYPE(string_buf) = 0;
  SX_PROP_INDEX(string_buf) = xc->index;
  SX_PROP_IMID(string_buf) = 0;
  SX_PROP_ICID(string_buf) = 0;

  XChangeProperty(display, external,
		  atom_launch_this, XA_STRING,
		  8, PropModeReplace, (unsigned char *)(&string_buf),
		  SX_SIZE_PROP_STARTDONE);

  event.type = ClientMessage;
  event.serial = 0;
  event.send_event = True;
  event.display = display;
  event.window = external;
  event.message_type = atom_launch_this;
  event.format = 32;

  /* not used */
  event.data.l[0] = 0;
  event.data.l[1] = 0;
  event.data.l[2] = 0;
  event.data.l[3] = 0;
  event.data.l[4] = 0;
  XSendEvent(display, external, True, 0, (XEvent *)(&event));
  return True;
}

static Bool
xaux_so_send_aux_draw_to_external (xaux_class_t *xc,
				   aux_t *aux)
{
  Display *display;
  XClientMessageEvent event;
  Window external;
  char string_buf[20];
  Bool conversion_mode;

  display = aux->service->display(aux);
  external = XGetSelectionOwner(display, atom_xbe);

  if (atom_draw_this == None)
    atom_draw_this = XInternAtom(display, "xaux_draw_this", False);

  conversion_mode = aux->service->get_conversion_mode(aux);

  SX_PROP_ATOM_AUX_NAME(string_buf) = xc->atom_classname;
  SX_PROP_TYPE(string_buf) = 0;
  SX_PROP_INDEX(string_buf) = xc->index;
  SX_PROP_IMID(string_buf) = conversion_mode; /* hack */
  SX_PROP_ICID(string_buf) = 0;

  XChangeProperty(display, external,
		  atom_draw_this, XA_STRING,
		  8, PropModeReplace, (unsigned char *)(&string_buf),
		  SX_SIZE_PROP_STARTDONE);

  event.type = ClientMessage;
  event.serial = 0;
  event.send_event = True;
  event.display = display;
  event.window = external;
  event.message_type = atom_draw_this;
  event.format = 32;

  /* not used */
  event.data.l[0] = 0;
  event.data.l[1] = 0;
  event.data.l[2] = 0;
  event.data.l[3] = 0;
  event.data.l[4] = 0;
  XSendEvent(display, external, True, 0, (XEvent *)(&event));
  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) {
	  /*
	   * let external program be launched if not loaded by server 
	   * (htt_xbe) program yet,
	   */
	  return xaux_so_send_aux_launch_to_external (xc, aux);
	}
	if (access(ext, X_OK) != 0) {
#if defined(DEBUG_XAUX)
		fprintf(stderr, "executable \"%s\" not found\n", ext);
#endif /* defined(DEBUG_XAUX) */
		/* 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 */
#if defined(DEBUG_XAUX)
		chdir("/tmp");
#endif /* defined(DEBUG_XAUX) */
		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];

	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 == XAUX_SX_NATOMS)
		xc->atom_sx_idx = 1;
	
	return True;
}

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

  while (p->classname != NULL) {
    if (p->atom_clientwin == atom)
      return (p);
    p++;
  }

  return (NULL);
}

static Bool
xaux_client_process_client_message(Display *display, Window window,
				   XClientMessageEvent *event)
{
  Atom actual_type_return;
  int actual_format_return;
  unsigned long nitem_return;
  unsigned long bytes_after_return;
  unsigned char *prop_return = NULL;
  int r;
  xaux_class_t *xc;
  Bool ret;

  if ((xc = xaux_getclass_byclientwinatom(event->message_type)) == NULL)
    return False;

  r = XGetWindowProperty(display, window,
			 xc->atom_clientwin, 0, INT_MAX, False,
			 AnyPropertyType, &actual_type_return,
			 &actual_format_return, &nitem_return,
			 &bytes_after_return, &prop_return);
  if (r != Success || prop_return == NULL)
    return False;
  else
    ret = xaux_set_property (xc, prop_return);

  XFree (prop_return);
  return ret;
}

static Bool
xaux_set_property (xaux_class_t *xc, unsigned char *prop_return)
{
    int		size;
    aux_data_t	aux_data_;
    aux_data_t *	aux_data = &(aux_data_);
    unsigned char *	p;
    int		i;
    int		n=0;

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

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

#if defined(DEBUG_XAUX)
    fprintf(stderr, "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);
#endif /* defined(DEBUG_XAUX) */

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

    free(p);
    free(aux_data->string_list);
    return True;
}

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

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);

	if ((xc->extwin == None) &&
		(xaux_so_get_extwin(xc, display) == False)) {
		/* cannot get extwin */
		if (xc->atom_extwin == (Atom)None) {
			return False;
		} else {
			if (type == AUX_DATA_DRAW) {
				return True;
			} else {
				return xaux_so_send_message_detour_to_prop(
					aux, xc, im_id, ic_id, type);
			}
		}
	}
		
	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));
	if (xc->clientwin == None)
	  {
	    xc->clientwin = XCreateSimpleWindow(display, RootWindow(display, 0),
					    0, 0, 1, 1, 0, 0, 0);
	    if (xc->clientwin)
	      aux->service->register_X_filter(display, xc->clientwin,
					      ClientMessage, ClientMessage,
					      xaux_client_event_filter, NULL);
	  }
	if (xc->clientwin && type == AUX_DATA_DRAW)
	  XSetSelectionOwner(display, xc->atom_clientwin,
			     xc->clientwin, CurrentTime);

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

	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 == XAUX_SX_NATOMS)
		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;
	char *atom_name;
/*
	aux_icid_t * 	ic;
*/
	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;
	}

/*
	if ((ic = aux_icid_get(XS_PROP_ICID(prop_return), False)) == NULL) {
		XFree(prop_return);
		return False;
	}
*/

	atom_name = XGetAtomName (display, atom);
/*
	printf("xaux_so_process_property_update: atom_name: %s\n", atom_name);
*/
	if ((strstr (atom_name, "palette") != NULL ) &&
	    xc->atom_clientwin != None)
	{
	    Window owner = XGetSelectionOwner(display, xc->atom_clientwin);
	    if (owner != None)
	      {
		XClientMessageEvent event;

		XChangeProperty(display, owner,
				xc->atom_clientwin, XA_STRING,
				8, PropModeReplace,
				(unsigned char *)prop_return, nitem_return);

		event.type = ClientMessage;
		event.serial = 0;
		event.send_event = True;
		event.display = display;
		event.window = owner;
		event.message_type = xc->atom_clientwin;
		event.format = 32;

		event.data.l[0] = atom;
		event.data.l[1] = nitem_return;

		XSendEvent(display, owner, True, 0, (XEvent *)(&event));
	      }
	}
	else
	    xaux_set_property (xc, prop_return);

	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;
}

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);
	}
}

static Bool
xaux_xbe_process_client_message(Display *display, Window window,
				XClientMessageEvent *event,
				XPointer aux)
{
  Atom actual_type_return;
  int actual_format_return;
  unsigned long nitem_return;
  unsigned long bytes_after_return;
  unsigned char *prop_return = NULL;
  int r;

  if (atom_launch_this == None)
    atom_launch_this = XInternAtom(display, "xaux_launch_this", False);
  if (atom_draw_this == None)
    atom_draw_this = XInternAtom(display, "xaux_draw_this", False);

  r = XGetWindowProperty(display, window,
			 event->message_type, 0, INT_MAX, False,
			 AnyPropertyType, &actual_type_return,
			 &actual_format_return, &nitem_return,
			 &bytes_after_return, &prop_return);
  if (r != Success || prop_return == NULL)
    return False;

  if (atom_launch_this == event->message_type)
    {
      const char *ext;
      char *classname;
      pid_t pid;
      CARD16 class_index;

      classname = XGetAtomName (display, (Atom)(*((CARD32*)(prop_return + 0))));
      class_index = (*((CARD16 *)(prop_return + 6)));
      ext = xaux_classes[class_index - 1].extexec;

      if (access(ext, X_OK) != 0) {
#if defined(DEBUG_XAUX)
	fprintf(stderr, "executable \"%s\" not found\n", ext);
#endif /* defined(DEBUG_XAUX) */
	/* no use to try to get extwin */
	return False;
      }
#ifdef	sun
      pid = fork1();
#else
      pid = fork();
#endif
      if (pid == (pid_t)(-1)) { /* fork failed */
	return False;
      } else if (0 == pid) { /* child */
#if defined(DEBUG_XAUX)
	chdir("/tmp");
#endif /* defined(DEBUG_XAUX) */
	execl(ext, classname, NULL);
	_exit(1);
      }
    }
  else if (atom_draw_this == event->message_type)
    {
      Bool conversion_mode;

      conversion_mode = (*((CARD16 *)(prop_return + 8))); /* hack */

      ((aux_t*)aux)->service->set_conversion_mode(aux, conversion_mode);
    }
	
  if (prop_return)
    XFree(prop_return);
  /* parent */
  return True;
}

static Bool
xaux_xbe_client_event_filter(Display *display,
			     Window window,
			     XEvent *event,
			     XPointer aux)
{
  switch (event->type)
    {
    case ClientMessage:
      return xaux_xbe_process_client_message(display, window,
					     (XClientMessageEvent *)event,
					     aux);
      break;
    default:
      break;
    }
  return False;
}

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);

	if (is_server == True && atom_xbe != None) {
	  Window htt_xbe_win;
	  htt_xbe_win = XCreateSimpleWindow(display, RootWindow(display, 0),
					    0, 0, 1, 1, 0, 0, 0);
	  if (htt_xbe_win) {
	    XSetSelectionOwner(display, atom_xbe,
			       htt_xbe_win, CurrentTime);
	    aux->service->register_X_filter(display, htt_xbe_win,
					    ClientMessage, ClientMessage,
					    xaux_xbe_client_event_filter,
					    aux);
	  } else {
	    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);

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

		for (i = 0; i < XAUX_SX_NATOMS; 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 < XAUX_XS_NATOMS; 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) {
#if defined(DEBUG_XAUX)
			fprintf(stderr, "%s: %s already exists. [%s](1)\n",
				ME_SO, ME_SO, p->classname);
#endif /* defined(DEBUG_XAUX) */
			goto done_sowin;
		}

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

		if (p->sowin == None) {
			fprintf(stderr,
				"%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) {
			fprintf(stderr, "%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;
	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;

/* workaround for "stored reference to aux_t is corrupred" problem */
aux_tmp.ic = aux->ic;
aux_setvalue = aux_icid->aux->service->aux_setvalue;
mb_utf16 = aux_icid->aux->service->mb_utf16;
compose = aux_icid->aux->service->compose;

	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;

	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) {
		fprintf(stderr,
			"cannot communicate with external program [%s]\n",
			xc->classname);
		aux->service->decompose_free(aux_data);
		return False;
	}

#if defined(DEBUG_XAUX)
	fprintf(stderr, "so_Start[%s] im:0x%x ic:0x%x\n",
		xc->classname, aux_data->im, aux_data->ic);
#endif /* defined(DEBUG_XAUX) */

	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;


	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;
	}

#if 1
	/* workaround for "stored reference to aux_t is corrupred" problem */
	aux_tmp.ic = aux->ic;
#endif

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

#if defined(DEBUG_XAUX)
	fprintf(stderr, "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);
	fprintf(stderr, "total = %d\n", total);
#endif /* defined(DEBUG_XAUX) */

	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);

	/* for XIM clients
	   if (!aux->service->server (aux)) */
	xaux_so_send_aux_draw_to_external (xc, aux);

	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;

	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;
	}

#if defined(DEBUG_XAUX)
	fprintf(stderr, "so_Done[%s] im:0x%x ic:0x%x\n",
		xc->classname, aux_data->im, aux_data->ic);
#endif /* defined(DEBUG_XAUX) */

	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;
}
