/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/*
 * Copyright (c) 1999 by Sun Microsystem, Inc.
 * All rights reserved.
 *
 * xaux_so_common.c
 */

/*
   Copyright (c) 2004 Yu Shao <yshao@redhat.com>
   Copyright (c) 1997-2004 Red Hat, Inc. All rights reserved.
*/

#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.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"

#ifdef	DEBUG
#define	ENTER	fprintf(stderr, "%s enter.\n", __FUNCTION__);
#define	LEAVE	fprintf(stderr, "%s leave.\n", __FUNCTION__);
#else
#define	ENTER
#define	LEAVE
#endif

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

#if 0
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;
}
#endif

#if 0
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;
}
#endif

static Bool
xaux_so_launch_ext(
	xaux_class_t	*xc,
	aux_t		*aux
)
{
    pid_t pid;
    const char *ext = xc->extexec;

    ENTER;

    if (xc->atom_extwin == (Atom)0) {
	LEAVE;
	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
	 */
	LEAVE;
	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;
	LEAVE;
	return False;
    }
			
#ifdef	sun
    pid = fork1();
#else
    pid = fork();
#endif

    if (pid == (pid_t)(-1)) { /* fork failed */
	LEAVE;
	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 */
    LEAVE;

    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;

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

	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 (xaux_so_get_extwin(xc, display) == False) {
            return False;
        }
	if (xc->extwin == None) {
		/* cannot get extwin */
		if (xc->atom_extwin == (Atom)None) {
			return False;
		} else {
			if (type == AUX_DATA_DRAW) {
				return True;
			} else {
                                if (xc->sowin != None)
				    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;
	Window		win;

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

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

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

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

    ENTER;

    if ((xc = xaux_getclass_byatomxs(event->message_type)) == NULL) {
	LEAVE;
	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;
    }

    return False;
}

static Bool
xaux_so_event_filter(Display *display,
		     Window window,
		     XEvent *event,
		     XPointer pointer)
{
    ENTER;

    switch (event->type) {
	case ClientMessage:
	    LEAVE;
	    return (xaux_so_process_client_message(display, window,
						   (XClientMessageEvent *)event));
    }

    LEAVE;

    return False;
}

static void
aux_icid_init(void)
{
    int i;

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

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

    ENTER;

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

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

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

    ENTER;

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

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

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

	    LEAVE;
	    return (p->next);
	}
    }
    LEAVE;

    return NULL;
}

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

    ENTER;

    p = aux_icid_get(icID, False);

    if (NULL == p) {
	LEAVE;
	return;
    }

    p->icid = -1;

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

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;

    ENTER;

    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) {
	LEAVE;
	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 */
	    LEAVE;
	    return False;
	}
#ifdef	sun
	pid = fork1();
#else
	pid = fork();
#endif
	if (pid == (pid_t)(-1)) { /* fork failed */
	    LEAVE;
	    return False;
	} else if (0 == pid) { /* child */
#if defined(DEBUG_XAUX)
	    chdir("/tmp");
#endif /* defined(DEBUG_XAUX) */
	    execl(ext, classname, NULL);
	    LEAVE;
	    _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_t *)aux, conversion_mode);
    }

    if (prop_return)
	XFree(prop_return);
    /* parent */

    LEAVE;

    return True;
}

static Bool
xaux_xbe_client_event_filter(Display *display,
			     Window window,
			     XEvent *event,
			     XPointer aux)
{
    ENTER;

    switch (event->type) {
	case ClientMessage:
	    return xaux_xbe_process_client_message(display, window,
						   (XClientMessageEvent *)event,
						   aux);
	    break;
	default:
	    break;
    }

    LEAVE;

    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;

    ENTER;

    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,
					    (XPointer)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;

	/* create it anyway, even there is multiple say from htt_xbe, we still use
	   this window as the contact point
	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
	    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;
	}

	//shao xaux_so_launch_ext(p, aux);
done_sowin:
	p->extwin = (Window)0;

	p++;
    }

    LEAVE;

    return True;
}

static Bool
xaux_so_Create(aux_t * aux)
{
    aux_icid_t *aux_icid;

    ENTER;

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

    LEAVE;

    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;

    ENTER;

    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);
	LEAVE;
	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);
	LEAVE;
	return False;
    }
#if defined(DEBUG_XAUX)
    fprintf(stderr, "so_Start[%s] im:0x%x ic:0x%x extwin:%x\n",
	    xc->classname, aux_data->im, aux_data->ic, xc->extwin);
#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);

    LEAVE;

    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 = sp;

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

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

    XSetSelectionOwner(aux->service->display(aux), xc->atom_sowin, xc->sowin, CurrentTime);

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

    ENTER;

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

#if 0
    if (xc->clientwin) {
	aux->service->unregister_X_filter(display, xc->clientwin,
					  xaux_client_event_filter, NULL);
	XSetSelectionOwner(display, xc->atom_clientwin, None, CurrentTime);
	XDestroyWindow(display, xc->clientwin);
	xc->clientwin = (Window)0;
    }
    if (xc->sowin) {
	XDestroyWindow(display, xc->sowin);
	XSetSelectionOwner(display, xc->atom_sowin, None, CurrentTime);
	xc->sowin = (Window)0;
    }
    if (xc->extwin) {
	XSync(display, True);
	XDestroyWindow(display, xc->extwin);
	XSetSelectionOwner(display, xc->atom_extwin, None, CurrentTime);
	xc->extwin = (Window)0;
    }
#endif

    LEAVE;

    return rv;
}

static Bool
xaux_so_Switched(aux_t * aux, int im_id, int on_off)
{
    ENTER;

    LEAVE;

    return True;
}

static Bool
xaux_so_Destroy(aux_t * aux)
{
    ENTER;

    LEAVE;

    return True;
}
