diff lib/KIProto.c @ 0:92745d501b9a

initial import from kinput2-v3.1
author Yoshiki Yazawa <yaz@honeyplanet.jp>
date Mon, 08 Mar 2010 04:44:30 +0900
parents
children 7a454839a6de
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/KIProto.c	Mon Mar 08 04:44:30 2010 +0900
@@ -0,0 +1,2351 @@
+#ifndef lint
+static char *rcsid = "$Id: KIProto.c,v 1.49 1999/01/07 03:12:57 ishisone Exp $";
+#endif
+/*-
+ * Copyright (c) 1991  Software Research Associates, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of Software Research Associates not be
+ * used in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.  Software Research
+ * Associates makes no representations about the suitability of this software
+ * for any purpose.  It is provided "as is" without express or implied
+ * warranty.
+ *
+ * Author:  Makoto Ishisone, Software Research Associates, Inc., Japan
+ */
+
+#include <X11/IntrinsicP.h>
+#include <X11/StringDefs.h>
+#include <X11/Xatom.h>
+#include <X11/Xmu/Atoms.h>
+#include <X11/Xmu/CharSet.h>
+#include "KIProtoP.h"
+#include "ConvMgr.h"
+#include "OverConv.h"
+#include "OffConv.h"
+#include "ConvDisp.h"
+#include "MyDispatch.h"
+#include "CachedFont.h"
+#include "ConvProto.h"
+#include "AsyncErr.h"
+
+#define DEBUG_VAR debug_KinputProtocol
+#include "DebugPrint.h"
+
+#define JINPUT_PROTOCOL_VERSION 0x20000L	/* version 2 */
+
+#define XLC_AUTO_REPLACE	1
+#define XLC_ALL_INFORMATION	2
+#define XLC_FRAME_INFORMATION	4
+#define XLC_OFFSET_INFORMATION	8
+
+/*- resource table -*/
+static XtResource resources[] = {
+#define offset(field) XtOffset(KinputProtocolWidget, kinput.field)
+    { XtNlanguage, XtCLanguage, XtRString, sizeof(String),
+	offset(language), XtRString, (XtPointer)NULL },
+    { XtNinputObjectClass, XtCClass, XtRPointer, sizeof(WidgetClass),
+	offset(inputObjClass), XtRImmediate, (XtPointer)NULL },
+    { XtNdisplayObjectClass, XtCClass, XtRPointer, sizeof(WidgetClass),
+	offset(displayObjClass), XtRImmediate, (XtPointer)NULL },
+    { XtNbackwardCompatible, XtCBackwardCompatible, XtRBoolean, sizeof(Boolean),
+	offset(backward_compatible), XtRString, (XtPointer)"False" },
+    { XtNxlcConversionStartKey, XtCXlcConversionStartKey, XtRString, sizeof(String),
+	offset(xlcstartkey), XtRString, (XtPointer)NULL },
+#undef offset
+};
+
+static void ConversionRequestProc();
+static void ConversionEndRequestProc();
+static void ConversionOpenRequestProc();
+static void ConversionCloseRequestProc();
+static void ConversionCloseProc();
+static void ConversionXYNotifyProc();
+static void ConversionColorNotifyProc();
+static void ConversionFontsNotifyProc();
+static void ConversionAttributeNotifyProc();
+static void SelectionRequestProc();
+static void SelectionClearProc();
+static void XlcOnTheSpotChangedProc();
+
+/*- action table -*/
+static XtActionsRec actions[] = {
+    { "conversion-request",		ConversionRequestProc },
+    { "conversion-end-request",		ConversionEndRequestProc },
+    { "conversion-open-request",	ConversionOpenRequestProc },
+    { "conversion-close-request",	ConversionCloseRequestProc },
+    { "conversion-close",		ConversionCloseProc },
+    { "conversion-xy-notify",		ConversionXYNotifyProc },
+    { "conversion-color-notify",	ConversionColorNotifyProc },
+    { "conversion-fonts-notify",	ConversionFontsNotifyProc },
+    { "conversion-attribute-notify",	ConversionAttributeNotifyProc },
+    { "xlc-on-the-spot-changed",	XlcOnTheSpotChangedProc },
+    { "selection-request",		SelectionRequestProc },
+    { "selection-clear",		SelectionClearProc },
+};
+
+/*- default translation -*/
+static char translations[] =
+    "<Message>CONVERSION_REQUEST:	conversion-request()\n\
+     <Message>CONVERSION_END_REQUEST:	conversion-end-request()\n\
+     <Message>CONVERSION_OPEN_REQUEST:	conversion-open-request()\n\
+     <Message>CONVERSION_CLOSE_REQUEST:	conversion-close-request()\n\
+     <Message>CONVERSION_CLOSE:		conversion-close()\n\
+     <Message>CONVERSION_XY_NOTIFY:	conversion-xy-notify()\n\
+     <Message>CONVERSION_COLOR_NOTIFY:	conversion-color-notify()\n\
+     <Message>CONVERSION_FONTS_NOTIFY:	conversion-fonts-notify()\n\
+     <Message>CONVERSION_ATTRIBUTE_NOTIFY: conversion-attribute-notify()\n\
+     <Prop>_XLC_ON_THE_SPOT:	xlc-on-the-spot-changed()\n\
+     <SelReq>:	selection-request()\n\
+     <SelClr>:	selection-clear()";
+
+/*- static function declarations -*/
+static void Initialize(), Destroy();
+static void Realize();
+static Boolean SetValues();
+
+static void getAtoms();
+static Boolean ownSelection();
+
+static ConvClient *findClient();
+static ConvClient *newClient();
+static Widget attachConverter();
+static void detachConverter();
+static void jinputDetach();
+static void deleteClient();
+
+static Boolean isCorrectClientEvent();
+static Boolean isCorrectWindowID();
+
+static void myStartConversion();
+static void myChangeAttributes();
+
+static void getJinputInitialProperty();
+static void jinputSendReq();
+static void jinputFreeResources();
+static void kinput2FreeResources();
+static void xlcFreeResources();
+static void setAttribute();
+static int getFontsByFontAtoms();
+static int safeGetAttributeProperty();
+static void getAttributeFromProperty1();
+static void getAttributeFromProperty();
+static void getAttributeFromEvent();
+static ConvClient *getXlcDataFromProperty();
+static void initializeError();
+static int parseKeyEvent();
+static char *mystrstr();
+static void getDefaultFontHeight();
+
+static void setJinputProperty();
+static void setKinput2Property();
+static void setXlcProperty();
+static void setXlcStatusProperty();
+static void setXlcBCKey();
+
+static void sendClientMessage();
+static void sendNegativeConversionNotify();
+static void sendConversionNotify();
+static void sendNegativeConversionOpenNotify();
+static void sendConversionOpenNotify();
+static void sendColorRequest();
+static void sendFontRequest();
+static void sendXYRequest();
+
+static void fixCallback();
+static void fixProc();
+static void jinputFix();
+static void endCallback();
+static void endProc();
+static void jinputEnd();
+static void jinputNewTextCallback();
+static void xlcEnd();
+
+static void ClientDead();
+
+/*- KinputProtocolClassRec -*/
+KinputProtocolClassRec kinputProtocolClassRec = {
+  { /* core fields */
+    /* superclass		*/	(WidgetClass) &widgetClassRec,
+    /* class_name		*/	"KinputProtocol",
+    /* widget_size		*/	sizeof(KinputProtocolRec),
+    /* class_initialize		*/	NULL,
+    /* class_part_initialize	*/	NULL,
+    /* class_inited		*/	FALSE,
+    /* initialize		*/	Initialize,
+    /* initialize_hook		*/	NULL,
+    /* realize			*/	Realize,
+    /* actions			*/	actions,
+    /* num_actions		*/	XtNumber(actions),
+    /* resources		*/	resources,
+    /* num_resources		*/	XtNumber(resources),
+    /* xrm_class		*/	NULLQUARK,
+    /* compress_motion		*/	TRUE,
+    /* compress_exposure	*/	TRUE,
+    /* compress_enterleave	*/	TRUE,
+    /* visible_interest		*/	FALSE,
+    /* destroy			*/	Destroy,
+    /* resize			*/	NULL,
+    /* expose			*/	NULL,
+    /* set_values		*/	SetValues,
+    /* set_values_hook		*/	NULL,
+    /* set_values_almost	*/	XtInheritSetValuesAlmost,
+    /* get_values_hook		*/	NULL,
+    /* accept_focus		*/	NULL,
+    /* version			*/	XtVersion,
+    /* callback_private		*/	NULL,
+    /* tm_table			*/	translations,
+    /* query_geometry		*/	XtInheritQueryGeometry,
+    /* display_accelerator	*/	XtInheritDisplayAccelerator,
+    /* extension		*/	NULL
+  },
+  { /* kinputprotocol fields */
+    /* empty			*/	0
+  }
+};
+
+WidgetClass kinputProtocolWidgetClass = (WidgetClass)&kinputProtocolClassRec;
+
+/*
+ *+ Core class methods
+ */
+
+/*- Initialize: intern Atoms -*/
+/* ARGSUSED */
+static void
+Initialize(req, new, args, num_args)
+Widget req;
+Widget new;
+ArgList args;
+Cardinal *num_args;
+{
+    KinputProtocolWidget kpw = (KinputProtocolWidget)new;
+
+    if (kpw->kinput.language == NULL) {
+	initializeError(new, XtNlanguage);
+    } else if (kpw->kinput.inputObjClass == NULL) {
+	initializeError(new, XtNinputObjectClass);
+    } else if (kpw->kinput.displayObjClass == NULL) {
+	initializeError(new, XtNdisplayObjectClass);
+    }
+    kpw->kinput.language = XtNewString(kpw->kinput.language);
+    kpw->kinput.clients = (ConvClient *)NULL;
+    getDefaultFontHeight(kpw);
+    getAtoms(kpw);
+}
+
+/*- Destroy: free allocated memory -*/
+static void
+Destroy(w)
+Widget w;
+{
+    KinputProtocolWidget kpw = (KinputProtocolWidget)w;
+
+    XtFree(kpw->kinput.language);
+}
+
+/*- Realize: own selection -*/
+static void
+Realize(w, mask, value)
+Widget w;
+XtValueMask *mask;
+XSetWindowAttributes *value;
+{
+    KinputProtocolWidget kpw = (KinputProtocolWidget)w;
+    CoreWidgetClass super = (CoreWidgetClass)XtClass(w)->core_class.superclass;
+
+    (*super->core_class.realize)(w, mask, value);
+
+    setJinputProperty(kpw);
+    setKinput2Property(kpw);
+    setXlcProperty(kpw);
+
+    if (!ownSelection(kpw)) {
+	String params[1];
+	Cardinal num_params;
+
+	params[0] = XtClass(w)->core_class.class_name;
+	num_params = 1;
+	XtAppWarningMsg(XtWidgetToApplicationContext(w),
+			"selectionError", "ownSelection", "WidgetError",
+			"%s: can't own selection", params, &num_params);
+
+	XtDestroyWidget(w);
+    } else {
+	CMPrepareConverter(XtParent(w), XtScreen(w),
+			   separateConversionWidgetClass,
+			   kpw->kinput.inputObjClass,
+			   kpw->kinput.displayObjClass);
+    }
+}
+
+/*- SetValues: not implemented yet -*/
+/* ARGSUSED */
+static Boolean
+SetValues(cur, req, wid, args, num_args)
+Widget cur;
+Widget req;
+Widget wid;
+ArgList args;
+Cardinal *num_args;
+{
+#ifdef notdef
+    KinputProtocolWidget old = (KinputProtocolWidget)cur;
+    KinputProtocolWidget new = (KinputProtocolWidget)wid;
+    Boolean redisplay = False;
+#endif
+
+    return False;
+}
+
+/*
+ *+ atom handling
+ */
+
+/*- getAtoms: intern atoms -*/
+static void
+getAtoms(kpw)
+KinputProtocolWidget kpw;
+{
+    Display *dpy = XtDisplay((Widget)kpw);
+    char buf[256];
+
+    (void)sprintf(buf, "_%s_CONVERSION", kpw->kinput.language);
+    kpw->kinput.convAtom = XInternAtom(dpy, buf, False);
+    if (kpw->kinput.backward_compatible) {
+	(void)sprintf(buf, "%s_CONVERSION", kpw->kinput.language);
+	kpw->kinput.oldConvAtom = XInternAtom(dpy, buf, False);
+    } else {
+	kpw->kinput.oldConvAtom = None;
+    }
+
+    kpw->kinput.ctextAtom = XA_COMPOUND_TEXT(dpy);
+
+#define MAKEATOM(s)	XInternAtom(dpy, s, False)
+
+    kpw->kinput.convStringAtom = MAKEATOM("CONVERSION_STRING");
+    kpw->kinput.convNotifyAtom = MAKEATOM("CONVERSION_NOTIFY");
+    kpw->kinput.convEndAtom = MAKEATOM("CONVERSION_END");
+
+    (void)sprintf(buf, "%s_CONVERSION_VERSION", kpw->kinput.language);
+    kpw->kinput.convVersionAtom = XInternAtom(dpy, buf, False);
+    kpw->kinput.convInitialTypeAtom = MAKEATOM("CONVERSION_INITIAL_TYPE");
+    kpw->kinput.convOpenNotifyAtom = MAKEATOM("CONVERSION_OPEN_NOTIFY");
+    kpw->kinput.convXYRequestAtom = MAKEATOM("CONVERSION_XY_REQUEST");
+    kpw->kinput.convFontsRequestAtom = MAKEATOM("CONVERSION_FONTS_REQUEST");
+    kpw->kinput.convColorRequestAtom = MAKEATOM("CONVERSION_COLOR_REQUEST");
+    kpw->kinput.convCloseNotifyAtom = MAKEATOM("CONVERSION_CLOSE_NOTIFY");
+    kpw->kinput.convAttributeAtom = MAKEATOM("CONVERSION_ATTRIBUTE");
+    kpw->kinput.xlcStatusAtom = MAKEATOM("_XLC_STATUS");
+    kpw->kinput.xlcOnTheSpotAtom = MAKEATOM("_XLC_ON_THE_SPOT");
+    kpw->kinput.xlcBcModifierAtom = MAKEATOM("_XLC_BC_MODIFIER");
+    kpw->kinput.xlcBcKeycodeAtom = MAKEATOM("_XLC_BC_KEYCODE");
+
+    /*
+     * some of the clients who speak Xlc protocol check the existence
+     * of atom "CONVERSION_REQUEST" before starting conversion.
+     * so make it at initialization time.
+     */
+    (void)MAKEATOM("CONVERSION_REQUEST");
+
+#undef MAKEATOM
+}
+
+/*- ownSelection: own conversion selection -*/
+static Boolean
+ownSelection(kpw)
+KinputProtocolWidget kpw;
+{
+    Display *dpy = XtDisplay((Widget)kpw);
+    Window w = XtWindow((Widget)kpw);
+    Boolean res;
+
+    TRACE(("kinputProtocol:ownSelection()\n"));
+    XSetSelectionOwner(dpy, kpw->kinput.convAtom, w, CurrentTime);
+    /* $B0l1~(B SetSelectionOwner $B$7$?8e(B GetSelectionOwner $B$7$F$_$F3N$+$a$k(B */
+    res = XGetSelectionOwner(XtDisplay((Widget)kpw), kpw->kinput.convAtom) == w;
+
+    if (kpw->kinput.backward_compatible) {
+	XSetSelectionOwner(dpy, kpw->kinput.oldConvAtom, w, CurrentTime);
+    }
+
+    return res;
+}
+
+/*
+ *+ client data handling
+ */
+
+/*- findClient: get clientdata of given client -*/
+static ConvClient *
+findClient(kpw, client)
+KinputProtocolWidget kpw;
+Window client;
+{
+    register ConvClient *ccp = kpw->kinput.clients;
+
+    while (ccp != NULL) {
+	if (ccp->reqwin == client) return ccp;
+	ccp = ccp->next;
+    }
+
+    return NULL;
+}
+
+/*- newClient: get a clientdata for new client -*/
+static ConvClient *
+newClient(kpw, client, selection)
+KinputProtocolWidget kpw;
+Window client;
+Atom selection;
+{
+    ConvClient *ccp;
+
+    ccp = XtNew(ConvClient);
+    ccp->protocol = unresolved_protocol;
+    ccp->style = separate_style;	/* default */
+    ccp->protocolwidget = (Widget)kpw;
+    ccp->conversion = NULL;
+    ccp->reqwin = client;
+    ccp->selection = selection;
+    ccp->target = None;
+    ccp->property = None;
+    ccp->esm = ESMethodNone;
+    ccp->data = NULL;
+    ccp->attrmask = 0L;
+    ccp->validattrmask = 0L;
+    ccp->start_proc = NULL;
+    ccp->detach_proc = NULL;
+    ccp->fix_proc = NULL;
+    ccp->end_proc = NULL;
+    ccp->free_resources = NULL;
+
+    ccp->next = kpw->kinput.clients;
+    kpw->kinput.clients = ccp;
+
+    return ccp;
+}
+
+/*- attachConverter: attach converter to the client -*/
+static Widget
+attachConverter(ccp)
+ConvClient *ccp;
+{
+    WidgetClass class;
+    KinputProtocolWidget kpw = (KinputProtocolWidget)ccp->protocolwidget;
+
+    if (ccp->conversion != NULL) return ccp->conversion;
+
+    if (ccp->protocol == unresolved_protocol) {
+	ccp->protocol = kinput1_protocol;
+	ccp->style = separate_style;
+    }
+
+    if (ccp->style == overthespot_style) {
+	class = overTheSpotConversionWidgetClass;
+    } else if (ccp->style == offthespot_style) {
+	class = offTheSpotConversionWidgetClass;
+    } else {
+	class = separateConversionWidgetClass;
+    }
+
+    ccp->conversion = CMGetConverter(XtParent(ccp->protocolwidget),
+				     ccp->reqwin, class,
+				     kpw->kinput.inputObjClass,
+				     kpw->kinput.displayObjClass);
+
+    return ccp->conversion;
+}
+
+/*- detachConverter: detach converter from client -*/
+static void
+detachConverter(ccp)
+ConvClient *ccp;
+{
+    TRACE(("detachConverter(window=0x%lx)\n", ccp->reqwin));
+
+    XtRemoveCallback(ccp->conversion, XtNtextCallback,
+		     fixCallback, (XtPointer)ccp);
+    XtRemoveCallback(ccp->conversion, XtNendCallback,
+		     endCallback, (XtPointer)ccp);
+
+    /* call additional detach proc */
+    if (ccp->detach_proc != NULL) (*ccp->detach_proc)(ccp);
+
+    CMReleaseConverter(XtParent(ccp->protocolwidget), ccp->conversion);
+    ccp->conversion = NULL;
+}
+
+/*- jinputDetach: jinput protocol specific detach processing -*/
+static void
+jinputDetach(client)
+ConvClient *client;
+{
+    XtRemoveCallback(client->conversion, XtNnewTextCallback,
+		     jinputNewTextCallback, (XtPointer)client);
+}
+
+/*- deleteClient: delete specified client -*/
+static void
+deleteClient(client)
+ConvClient *client;
+{
+    KinputProtocolWidget kpw = (KinputProtocolWidget)client->protocolwidget;
+    ConvClient *ccp, *ccp0;
+
+    TRACE(("deleteClient(window=0x%lx)\n", client->reqwin));
+    if (client->conversion != NULL) detachConverter(client);
+    if (client->free_resources != NULL) (*client->free_resources)(client);
+
+    for (ccp = kpw->kinput.clients, ccp0 = NULL;
+	 ccp != NULL;
+	 ccp0 = ccp, ccp = ccp->next) {
+	if (ccp == client) {
+	    if (ccp0 == NULL) {
+		kpw->kinput.clients = ccp->next;
+	    } else {
+		ccp0->next = ccp->next;
+	    }
+	    XtFree((char *)client);
+	    return;
+	}
+    }
+    DPRINT(("deleteClient: cannot find the client in the client list!\n"));
+}
+
+/*
+ *+ utility functions
+ */
+
+/*- isCorrectClientEvent: is the event in correct format? -*/
+static Boolean
+isCorrectClientEvent(kpw, event)
+KinputProtocolWidget kpw;
+XEvent *event;
+{
+    XClientMessageEvent *ev = &(event->xclient);
+    Atom atom = (Atom)ev->data.l[0];
+
+    return (ev->window == XtWindow((Widget)kpw) &&
+	    ev->format == 32 &&
+	    (atom == kpw->kinput.convAtom ||
+	     kpw->kinput.backward_compatible && atom == kpw->kinput.oldConvAtom));
+}
+
+/*- isCorrectWindowID: is the given window ID valid? -*/
+static Boolean
+isCorrectWindowID(w, window)
+Widget w;
+Window window;
+{
+    int status;
+    Window root;
+    int x, y;
+    unsigned int width, height, bwidth, depth;
+    XAEHandle h;
+
+    /*
+     * previous version uses XGetWindowAttributes(), which
+     * issues both GetWindowAttributes and GetGeometry request.
+     * since it is a bit inefficient to issue two requests just
+     * for checking if a window exists, I've changed to use
+     * XGetGeometry which issues only GetGeometry.
+     */
+    h = XAESetIgnoreErrors(XtDisplay(w));
+    status = XGetGeometry(XtDisplay(w), window, &root, &x, &y,
+			  &width, &height, &bwidth, &depth);
+    XAEUnset(h);
+    return status == 0 ? False : True;
+}
+
+/*- myStartConversion: custom version of CControlStartConversion -*/
+static void
+myStartConversion(w, clwin, mask, attrs)
+Widget w;
+Window clwin;
+unsigned long mask;
+ConversionAttributes *attrs;
+{
+    if (mask & CAFonts) {
+	mask |= CAStatusFonts;
+	attrs->status_fonts = attrs->fonts;
+	attrs->num_status_fonts = attrs->num_fonts;
+    } else {
+	mask &= ~CAStatusFonts;
+    }
+    CControlStartConversion(w, clwin, mask, attrs);
+}
+
+/*- myChangeAttributes: custom version of CControlChangeAttributes -*/
+static void
+myChangeAttributes(w, mask, attrs)
+Widget w;
+unsigned long mask;
+ConversionAttributes *attrs;
+{
+    if (mask & CAFonts) {
+	mask |= CAStatusFonts;
+	attrs->status_fonts = attrs->fonts;
+	attrs->num_status_fonts = attrs->num_fonts;
+    } else {
+	mask &= ~CAStatusFonts;
+    }
+    CControlChangeAttributes(w, mask, attrs);
+}
+
+/*- getJinputInitialProperty: get property for jinput protocol and set data -*/
+static void
+getJinputInitialProperty(kpw, ccp, prop, reqtype)
+KinputProtocolWidget kpw;
+ConvClient *ccp;
+Atom prop;
+Atom reqtype;
+{
+    Atom type;
+    int format;
+    unsigned long nitems;
+    long *data;
+    unsigned long bytesafter;
+    JinputData *jdp = (JinputData *)ccp->data;
+
+    XGetWindowProperty(XtDisplay((Widget)kpw), ccp->reqwin, prop, 0L, 11L,
+		       False, reqtype, &type, &format, &nitems,
+		       &bytesafter, (unsigned char **)&data);
+
+    if (type != reqtype) {
+	DPRINT(("getJinputInitialProperty(): unknown property type (%ld)\n", type));
+	return;
+    }
+
+    /* data[0]: version number */
+    if (data[0] != JINPUT_PROTOCOL_VERSION) {
+	DPRINT(("getJinputInitialProperty(): unknown version (0x%lx)\n", data[0]));
+	return;
+    }
+    /* data[1]: requestor window */
+    if (data[1] != ccp->reqwin) {
+	return;
+    }
+    /* data[2]: preedit type */
+    if (data[2] == 3) {
+	/* over-the-spot */
+	ccp->style = overthespot_style;
+    } else {
+	ccp->style = separate_style;
+    }
+    /* data[3]: fold mode (ignore) */
+    /* data[4]: clip mode (ignore) */
+    /* data[5]: multi color mode */
+    if (data[5] == 0) {
+	/* fixed color */
+	ccp->attrmask |= CAColor;
+	/* data[6]: background */
+	ccp->attrs.background = data[6];
+	/* data[7]: foreground */
+	ccp->attrs.foreground = data[7];
+    } else {
+	/* query */
+	DPRINT(("getJinputInitialProperty(): multi-color mode\n"));
+	jdp->state |= JINPUT_MULTI_COLOR;
+    }
+    /* data[8]: multi font mode */
+    if (data[8] == 0) {
+	ccp->attrmask |= CAFonts;
+	ccp->attrs.num_fonts = 2;
+	ccp->attrs.fonts = (XFontStruct **)XtMalloc(sizeof(XFontStruct *) * 2);
+	/* data[9]: font1 */
+	ccp->attrs.fonts[0] = XQueryFont(XtDisplay((Widget)kpw),
+					 (XID)data[9]);	/* should make it safe */
+	/* data[10]: font2 */
+	ccp->attrs.fonts[1] = XQueryFont(XtDisplay((Widget)kpw),
+					 (XID)data[10]);
+    } else {
+	/* query */
+	TRACE(("getJinputInitialProperty(): multi-font mode\n"));
+	jdp->state |= JINPUT_MULTI_FONT;
+    }
+}
+
+/*- jinputSendReq: jinput protocol specific startup processing -*/
+static void
+jinputSendReq(client)
+ConvClient *client;
+{
+    JinputData *jdp = (JinputData *)client->data;
+
+    XtAddCallback(client->conversion, XtNnewTextCallback,
+		  jinputNewTextCallback, (XtPointer)client);
+
+    if (jdp->state & JINPUT_MULTI_COLOR) sendColorRequest(client);
+    if (jdp->state & JINPUT_MULTI_FONT) sendFontRequest(client);
+    if (client->style != separate_style) sendXYRequest(client);
+}
+
+/*- jinputFreeResources: free resources for jinput protocol client -*/
+static void
+jinputFreeResources(client)
+ConvClient *client;
+{
+    XtFree(client->data);
+    if (client->attrmask & CAFonts) {
+	/* free fonts */
+	XFreeFontInfo((char **)NULL, client->attrs.fonts[0], 1);
+	XFreeFontInfo((char **)NULL, client->attrs.fonts[1], 1);
+	XtFree((char *)client->attrs.fonts);
+    }
+}
+
+/*- xlcFreeResources: free resources for xlc protocol client -*/
+static void
+xlcFreeResources(client)
+ConvClient *client;
+{
+    if (client->validattrmask & CAFonts) {
+	/* free fonts */
+	XFreeFontInfo((char **)NULL, client->attrs.fonts[0], 1);
+	XFreeFontInfo((char **)NULL, client->attrs.fonts[1], 1);
+	XtFree((char *)client->attrs.fonts);
+    }
+}
+
+/*- kinput2FreeResources: free resources for kinput2 protocol client -*/
+static void
+kinput2FreeResources(client)
+ConvClient *client;
+{
+    if (client->attrmask & CAFonts) {	/* free fonts */
+	Cardinal i;
+	for (i = 0; i < client->attrs.num_fonts; i++) {
+	    CachedFreeFont(XtDisplay(client->protocolwidget),
+			   client->attrs.fonts[i]);
+	}
+	XtFree((char *)client->attrs.fonts);
+    }
+}
+
+struct proprec {
+    Atom prop;
+    struct proprec *prev;
+};
+
+/*- setAttribute: set conversion attribute (kinput2 only) -*/
+static void
+setAttribute(client, data, precp, fromevent)
+ConvClient *client;
+unsigned long *data;
+struct proprec *precp;	/* to prevent possible inifinite loop */
+Boolean fromevent;
+{
+    int code = CODE_OF_ATTR(*data);
+    int len = LENGTH_OF_ATTR(*data);
+
+#define CHECK_LENGTH(num, name) \
+    if (len != num) { DPRINT(("\tlength of %s attribute is wrong\n", name)); break; }
+
+    TRACE(("setAttribute()\n"));
+    data++;
+    switch (code) {
+    case CONVATTR_NONE:
+	TRACE(("\tNone:\n"));
+	break;
+    case CONVATTR_INDIRECT:
+	CHECK_LENGTH(1, "IndirectAttribute");
+	TRACE(("\tIndirectAttribute:\n"));
+	getAttributeFromProperty1(client, data[0], precp, fromevent);
+	break;
+    case CONVATTR_FOCUS_WINDOW:
+	CHECK_LENGTH(1, "FocusWindow");
+	client->attrmask |= CAFocusWindow;
+	client->attrs.focuswindow = data[0];
+	TRACE(("\tFocusWindow: 0x%lx\n", data[0]));
+	break;
+    case CONVATTR_SPOT_LOCATION:
+	CHECK_LENGTH(1, "SpotLocation");
+	client->attrmask |= CASpotLocation;
+	client->attrs.spotx = UPPER16S(data[0]);
+	client->attrs.spoty = LOWER16S(data[0]);
+	TRACE(("\tSpotLocation: x=%d, y=%d\n", client->attrs.spotx, client->attrs.spoty));
+	break;
+    case CONVATTR_CLIENT_AREA:
+	CHECK_LENGTH(2, "ClientArea");
+	client->attrmask |= CAClientArea;
+	client->attrs.clientarea.x = UPPER16S(data[0]);
+	client->attrs.clientarea.y = LOWER16S(data[0]);
+	client->attrs.clientarea.width = UPPER16U(data[1]);
+	client->attrs.clientarea.height = LOWER16U(data[1]);
+	TRACE(("\tClientArea: %d,%d-%d,%d\n", client->attrs.clientarea.x,client->attrs.clientarea.y,client->attrs.clientarea.width,client->attrs.clientarea.height));
+	break;
+    case CONVATTR_STATUS_AREA:
+	CHECK_LENGTH(2, "StatusArea");
+	client->attrmask |= CAStatusArea;
+	client->attrs.statusarea.x = UPPER16S(data[0]);
+	client->attrs.statusarea.y = LOWER16S(data[0]);
+	client->attrs.statusarea.width = UPPER16U(data[1]);
+	client->attrs.statusarea.height = LOWER16U(data[1]);
+	TRACE(("\tStatusArea: %d,%d-%d,%d\n", client->attrs.statusarea.x,client->attrs.statusarea.y,client->attrs.statusarea.width,client->attrs.statusarea.height));
+	break;
+    case CONVATTR_COLORMAP:
+	CHECK_LENGTH(1, "Colormap");
+	client->attrmask |= CAColormap;
+	client->attrs.colormap = data[0];
+	TRACE(("\tColormap: 0x%lx\n", data[0]));
+	break;
+    case CONVATTR_COLOR:
+	CHECK_LENGTH(2, "Color");
+	client->attrmask |= CAColor;
+	client->attrs.foreground = data[0];
+	client->attrs.background = data[1];
+	TRACE(("\tColor: fg=%ld,bg=%ld\n", data[0], data[1]));
+	break;
+    case CONVATTR_BACKGROUND_PIXMAP:
+	CHECK_LENGTH(1, "BackgroundPixmap");
+	client->attrmask |= CABackgroundPixmap;
+	client->attrs.background_pixmap = data[0];
+	TRACE(("\tBackgroundPixmap: 0x%lx\n", data[0]));
+	break;
+    case CONVATTR_LINE_SPACING:
+	CHECK_LENGTH(1, "LineSpacing");
+	client->attrmask |= CALineSpacing;
+	client->attrs.linespacing = data[0];
+	TRACE(("\tLineSpacing: %ld\n", data[0]));
+	break;
+    case CONVATTR_FONT_ATOMS:
+	if (len < 1) {
+	    DPRINT(("length of FontAtoms attribute is less than 1\n"));
+	    break;
+	}
+	if (client->attrmask & CAFonts) {
+	    /* fonts set more than once -- free previously specified fonts */
+	    Cardinal i;
+	    for (i = 0; i < client->attrs.num_fonts; i++) {
+		CachedFreeFont(XtDisplay(client->protocolwidget),
+			       client->attrs.fonts[i]);
+	    }
+	    XtFree((char *)client->attrs.fonts);
+	}
+	client->attrmask |= CAFonts;
+	client->attrs.num_fonts =
+	  getFontsByFontAtoms(XtDisplay(client->protocolwidget),
+			      (Atom *)data, (Cardinal)len,
+			      &client->attrs.fonts);
+	break;
+    case CONVATTR_CURSOR:
+	CHECK_LENGTH(1, "Cursor");
+	client->attrmask |= CACursor;
+	client->attrs.cursor = data[0];
+	TRACE(("\tCursor: 0x%lx\n", data[0]));
+	break;
+    case CONVATTR_INPUT_STYLE:
+	if (fromevent) {
+	    DPRINT(("InputStyle can't change during conversion\n"));
+	    break;
+	}
+	CHECK_LENGTH(1, "InputStyle");
+	if (data[0] == CONVARG_OVERTHESPOT) {
+	    client->style = overthespot_style;
+	} else if (data[0] == CONVARG_OFFTHESPOT) {
+	    client->style = offthespot_style;
+	} else {
+	    client->style = separate_style;
+	}
+	TRACE(("\tInputStyle: %ld\n", data[0]));
+	break;
+    case CONVATTR_EVENT_CAPTURE_METHOD:
+	if (fromevent) {
+	    DPRINT(("EventCaptureMethod can't change during conversion\n"));
+	    break;
+	}
+	CHECK_LENGTH(1, "EventCaptureMethod");
+	if (data[0] == CONVARG_NONE) {
+	    client->esm = ESMethodNone;
+	} else if (data[0] == CONVARG_SELECT_FOCUS_WINDOW) {
+	    client->esm = ESMethodSelectFocus;
+	} else {
+	    client->esm = ESMethodInputOnly;
+	}
+	TRACE(("\tEventCaptureMethod: %ld\n", data[0]));
+	break;
+    case CONVATTR_USE_EXTENSION:
+	if (fromevent) {
+	    DPRINT(("UseExtension must be specified at conversion startup\n"));
+	    break;
+	}
+	TRACE(("\tUseExtension: not supported\n"));
+	break;
+    default:
+	if (code > 255) {
+	    /* private extension code */
+	    DPRINT(("\tPrivateExtension (code=%d): not supported\n", code));
+	}
+    }
+#undef CHECK_LENGTH
+}
+
+/*- getFontsByFontAtoms: get fonts from 'FONT' atom list (kinput2 only) -*/
+static int
+getFontsByFontAtoms(dpy, atoms, len, fontsp)
+Display *dpy;
+Atom *atoms;
+Cardinal len;
+XFontStruct *** fontsp;
+{
+    XFontStruct **fonts;
+    Cardinal nf, i;
+
+    fonts = (XFontStruct **)XtMalloc(sizeof(XFontStruct *) * len);
+    for (nf = 0, i = 0; i < len; i++) {
+	if ((fonts[nf] = CachedLoadQueryFontByProp(dpy, atoms[i])) == NULL) {
+	    TRACE(("\tcan't load font (atom=%ld)\n", atoms[i]));
+	} else {
+	    TRACE(("\tfont loaded (atom=%ld)\n", atoms[i]));
+	    nf++;
+	}
+    }
+
+    *fontsp = fonts;
+    return nf;
+}
+
+/*- safeGetAttributeProperty: get attribute property safely -*/
+static int
+safeGetAttributeProperty(client, prop, data)
+ConvClient *client;
+Atom prop;
+unsigned long **data;
+{
+    KinputProtocolWidget kpw = (KinputProtocolWidget)client->protocolwidget;
+    int err;
+    Atom type;
+    int format;
+    unsigned long nitems;
+    unsigned long bytesafter;
+    XAEHandle h;
+
+    *data = NULL;
+    
+    h = XAESetIgnoreErrors(XtDisplay((Widget)kpw));
+    err = XGetWindowProperty(XtDisplay((Widget)kpw), client->reqwin, prop,
+			     0L, 1000L, False,
+			     kpw->kinput.convAttributeAtom,
+			     &type, &format, &nitems,
+			     &bytesafter, (unsigned char **)data);
+    XAEUnset(h);
+
+    if (err) {
+	DPRINT(("\tcan't get property value. bad property (0x%lx)?\n", prop));
+	return 0;
+    }else if (format != 32 || type != kpw->kinput.convAttributeAtom) {
+	DPRINT(("\twrong format or type\n"));
+	if (*data != NULL) XtFree((char *)*data);
+	return 0;
+    }
+
+    TRACE(("safeGetAttributeProperty(): returns %ld\n", nitems));
+    return nitems;
+}
+
+/*- getAttributeFromProperty1: get and set attribute data (kinput2 only) -*/
+static void
+getAttributeFromProperty1(client, prop, precp, fromevent)
+ConvClient *client;
+Atom prop;
+struct proprec *precp;
+Boolean fromevent;
+{
+    unsigned long *data, *datap;
+    int nitems;
+    struct proprec prec;
+
+    prec.prop = prop;
+    prec.prev = precp;
+
+    /* try to detect loop */
+    while (precp != NULL) {
+	if (precp->prop == prop) {
+	    DPRINT(("loop detected. property=0x%lx\n", prop));
+	    return;
+	}
+	precp = precp->prev;
+    }
+
+    data = NULL;
+    if ((nitems = safeGetAttributeProperty(client, prop, &data)) <= 0) {
+	return;
+    }
+
+    datap = data;
+    while (nitems > 0) {
+	int alen = LENGTH_OF_ATTR(datap[0]) + 1; /* 1 is for the header */
+
+	if (alen > nitems) break;
+	setAttribute(client, datap, &prec, fromevent);
+	nitems -= alen;
+	datap += alen;
+    }
+    XtFree((char *)data);
+}
+
+/*- getAttributeFromProperty: get and set attribute data (kinput2 only) -*/
+static void
+getAttributeFromProperty(client, prop)
+ConvClient *client;
+Atom prop;
+{
+    TRACE(("getAttributeFromProperty(reqwin=0x%lx, prop=%ld)\n", client->reqwin, prop));
+    getAttributeFromProperty1(client, prop, (struct proprec *)NULL, False);
+}
+
+/*- getAttributeFromEvent: set attribute data specified in an event (kinput2 only) -*/
+static void
+getAttributeFromEvent(client, ev)
+ConvClient *client;
+XEvent *ev;
+{
+    XClientMessageEvent *event = &(ev->xclient);
+    unsigned long *data;
+    int alen;
+
+    TRACE(("getAttributeFromEvent(client=0x%lx)\n", client->reqwin));
+
+    data = (unsigned long *)&(event->data.l[2]);
+    alen = LENGTH_OF_ATTR(data[0]);
+    if (alen >= 3) return;
+
+    setAttribute(client, data, (struct proprec *)NULL, True);
+}
+
+/*- getXlcDataFromProperty: get and set attribute data (xlc only) -*/
+static ConvClient *
+getXlcDataFromProperty(kpw, client, initial)
+KinputProtocolWidget kpw;
+ConvClient *client;
+Boolean initial;
+{
+    unsigned long *data;
+    Atom type;
+    int format;
+    unsigned long nitems;
+    unsigned long bytesafter;
+    int flag;
+    int err;
+
+    TRACE(("getXlcDataFromProperty(initial=%d)\n", initial));
+    data = NULL;
+    err = XGetWindowProperty(XtDisplay((Widget)kpw), XtWindow((Widget)kpw),
+			     kpw->kinput.xlcOnTheSpotAtom,
+			     0L, 1000L, True,
+			     kpw->kinput.xlcOnTheSpotAtom,
+			     &type, &format, &nitems,
+			     &bytesafter, (unsigned char **)&data);
+
+    if (err) {
+	DPRINT(("\tcan't get property value\n"));
+	return NULL;
+    } else if (/* format != 32 || */ type != kpw->kinput.xlcOnTheSpotAtom) {
+	DPRINT(("\twrong format(%d) or type(%ld)\n", format, type));
+	if (data != NULL) XtFree((char *)data);
+	return NULL;
+    } else if (nitems < 17) {
+	/* it seems that the current implementation of Xlc library
+	 * (client-side library for XLC protocol) has a bug.
+	 * it seems to set number of bytes instead of number of items
+	 * when calling XChangeProperty().
+	 */
+	DPRINT(("\twrong length (%ld)\n", nitems));
+	return NULL;
+    }
+
+    if (client == NULL) {
+	if ((client = findClient(kpw, (Window)data[1])) == NULL) {
+	    TRACE(("\tcreate new client rec. for the window ID\n"));
+	    /* check validity of the client window ID */
+	    if (!isCorrectWindowID(kpw, (Window)data[1])) {
+		DPRINT(("getXlcDataFromProperty(): client window doesn't exist\n"));
+		return NULL;
+	    }
+	    client = newClient(kpw, (Window)data[1], None);
+	    client->protocol = xlc_protocol;
+	    client->style = overthespot_style;
+	    client->esm = ESMethodInputOnly;
+	    client->end_proc = xlcEnd;
+	    client->free_resources = xlcFreeResources;
+	    MyAddEventHandler(XtDisplay((Widget)kpw), client->reqwin,
+			      DestroyNotify, StructureNotifyMask,
+			      ClientDead, (XtPointer)client);
+	    initial = True;
+	}
+    } else {
+	if (data[1] != client->reqwin) {
+	    DPRINT(("\twindow ID isn't the requestor window ID\n"));
+	    return NULL;
+	}
+    }
+    TRACE(("\t%ld items read\n", nitems));
+
+    flag = data[0];
+    if (flag & XLC_AUTO_REPLACE) {
+	/* set auto spot forwarding */
+	/* XXX */
+	TRACE(("\tauto spot forwarding\n"));
+    }
+    if (flag & XLC_ALL_INFORMATION) {
+	/* foreground / background colors */
+	if (!initial &&
+	    (client->validattrmask & CAColor) &&
+	    client->attrs.foreground == data[5] &&
+	    client->attrs.background == data[6]) {
+	    client->attrmask &= ~CAColor;
+	} else {
+	    client->attrs.foreground = data[5];
+	    client->attrs.background = data[6];
+	    client->attrmask |= CAColor;
+	}
+	client->validattrmask |= CAColor;
+	TRACE(("\tColor: fg=%ld,bg=%ld\n", data[5], data[6]));
+
+	/* fonts */
+	if (data[7] == 0L || data[8] == 0L) {
+	    /* invalid */
+	    if (client->validattrmask & CAFonts) {
+		XFreeFontInfo((char **)NULL, client->attrs.fonts[0], 1);
+		XFreeFontInfo((char **)NULL, client->attrs.fonts[2], 1);
+		XFree((char *)client->attrs.fonts);
+		client->validattrmask &= ~CAFonts;
+	    }
+	    client->attrmask &= ~CAFonts;
+	} else {
+	    if (!initial &&
+		(client->validattrmask & CAFonts) &&
+		client->attrs.fonts[0]->fid == data[7] &&
+		client->attrs.fonts[1]->fid == data[8]) {
+		client->attrmask &= ~CAFonts;
+	    } else if (client->validattrmask & CAFonts) {
+		if (client->attrs.fonts[0]->fid != data[7]) {
+		    XFreeFontInfo((char **)NULL, client->attrs.fonts[0], 1);
+		    client->attrs.fonts[0] = XQueryFont(XtDisplay((Widget)kpw),
+							(XID)data[7]);
+		}
+		if (client->attrs.fonts[1]->fid != data[8]) {
+		    XFreeFontInfo((char **)NULL, client->attrs.fonts[1], 1);
+		    client->attrs.fonts[1] = XQueryFont(XtDisplay((Widget)kpw),
+							(XID)data[8]);
+		}
+		client->attrmask |= CAFonts;
+	    } else {
+		client->attrs.num_fonts = 2;
+		client->attrs.fonts =
+		  (XFontStruct **)XtMalloc(sizeof(XFontStruct *) * 2);
+		client->attrs.fonts[0] = XQueryFont(XtDisplay((Widget)kpw),
+						    (XID)data[7]);
+		client->attrs.fonts[1] = XQueryFont(XtDisplay((Widget)kpw),
+						    (XID)data[8]);
+		client->attrmask |= CAFonts;
+	    }
+	    client->validattrmask |= CAFonts;
+	    TRACE(("\tFonts: 0x%lx, 0x%lx\n", data[7], data[8]));
+	}
+    }
+    if (flag & (XLC_ALL_INFORMATION | XLC_FRAME_INFORMATION)) {
+	/* frame (client area) */
+	if (!initial &&
+	    (client->validattrmask & CAClientArea) &&
+	    client->attrs.clientarea.x == data[10] &&
+	    client->attrs.clientarea.y == data[11] &&
+	    client->attrs.clientarea.width == data[12] &&
+	    client->attrs.clientarea.height == data[13]) {
+	    client->attrmask &= ~CAClientArea;
+	} else {
+	    client->attrs.clientarea.x = data[10];
+	    client->attrs.clientarea.y = data[11];
+	    client->attrs.clientarea.width = data[12];
+	    client->attrs.clientarea.height = data[13];
+	    if (data[12] == 0 || data[13] == 0) {
+		client->attrmask &= ~CAClientArea;
+	    } else {
+		client->attrmask |= CAClientArea;
+	    }
+	}
+	client->validattrmask |= CAClientArea;
+	TRACE(("\tClientArea: %ld,%ld-%ld,%ld\n",
+	       data[10],data[11],data[12],data[13]));
+    }
+    if (flag & (XLC_ALL_INFORMATION | XLC_FRAME_INFORMATION | XLC_OFFSET_INFORMATION)) {
+	/* spot location */
+	client->attrmask |= CASpotLocation;	/* always set spot location */
+	client->attrs.spotx = data[14];
+	client->attrs.spoty = data[15] +
+	  ((client->validattrmask & CAFonts) ?
+		client->attrs.fonts[0]->ascent : kpw->kinput.defaultascent);
+	client->validattrmask |= CASpotLocation;
+	TRACE(("\tSpotLocation: x=%ld, y=%ld\n", data[14], data[15]));
+
+	/* line spacing */
+	if (!initial &&
+	    (client->validattrmask & CALineSpacing) &&
+	    client->attrs.linespacing == data[16]) {
+	    client->attrmask &= ~CALineSpacing;
+	} else {
+	    client->attrmask |= CALineSpacing;
+	    client->attrs.linespacing = data[16];
+	}
+	client->validattrmask |= CALineSpacing;
+	TRACE(("\tLineSpacing: %ld\n", data[16]));
+    }
+
+    XtFree((char *)data);
+    return client;
+}
+
+/*- initializeError: display error message when resource isn't specified -*/
+static void
+initializeError(w, resname)
+Widget w;
+String resname;
+{
+    String params[2];
+    Cardinal num_params;
+
+    params[0] = XtClass(w)->core_class.class_name;
+    params[1] = resname;
+    num_params = 2;
+    XtAppErrorMsg(XtWidgetToApplicationContext(w),
+		  "initializeError", "noResource", "WidgetError",
+		  "%s: resource %s must be specified at widget creation",
+		  params, &num_params);
+}
+
+/*- parseKeyEvent: parses key event description and get keycode and modmask -*/
+static int
+parseKeyEvent(dpy, s, codep, modmaskp)
+Display *dpy;
+char *s;
+long *codep;
+long *modmaskp;
+{
+    char *mod, *key;
+    char *p;
+    KeySym keysym;
+    char buf[128];
+
+    /*
+     * keyevent description (stored in  argument 's') must be of the
+     * following format:
+     *		modifier-list<Key>keysym
+     * modifier-list is a combination of:
+     *		Ctrl, Shift, Lock, Meta, Alt, Mod1, Mod2, Mod3, Mod4, Mod5
+     */
+
+    strcpy(buf, s);
+
+    /* find "<Key>" */
+    if ((p = mystrstr(buf, "<Key>")) != NULL) {
+	key = p + 5;	/* p + strlen("<Key>") */
+    } else if ((p = mystrstr(buf, "<KeyPress>")) != NULL) {
+	key = p + 10;	/* p + strlen("<KeyPress>") */
+    } else if ((p = mystrstr(buf, "<KeyDown>")) != NULL) {
+	key = p + 9;	/* p + strlen("<KeyDown>") */
+    } else {
+	return 0;
+    }
+    mod = buf;
+    *p = '\0';
+
+    /* get modifier mask */
+    *modmaskp = 0;
+    if (mystrstr(mod, "Shift")) *modmaskp |= ShiftMask;
+    if (mystrstr(mod, "Lock")) *modmaskp |= LockMask;
+    if (mystrstr(mod, "Ctrl")) *modmaskp |= ControlMask;
+    if (mystrstr(mod, "Mod1")) *modmaskp |= Mod1Mask;
+    if (mystrstr(mod, "Mod2")) *modmaskp |= Mod2Mask;
+    if (mystrstr(mod, "Mod3")) *modmaskp |= Mod3Mask;
+    if (mystrstr(mod, "Mod4")) *modmaskp |= Mod4Mask;
+    if (mystrstr(mod, "Mod5")) *modmaskp |= Mod5Mask;
+    if (mystrstr(mod, "Meta")) *modmaskp |= Mod1Mask;
+    if (mystrstr(mod, "Alt")) *modmaskp |= Mod1Mask;
+
+    /* get keycode */
+    if ((keysym = XStringToKeysym(key)) == NoSymbol) return 0;
+    if ((*codep = (long)XKeysymToKeycode(dpy, keysym)) == 0) return 0;
+    return 1;
+}
+
+/*- mystrstr: not-so-good implementaion of ANSI strstr() -*/
+static char *
+mystrstr(s1, s2)
+char *s1;
+char *s2;
+{
+    char *p, *q;
+
+    while (*(p = s1++) != '\0') {
+	q = s2;
+	do {
+	    if (*q == '\0') return s1 - 1;
+	} while (*p++ == *q++);
+    }
+    return NULL;
+}
+
+/*- getDefaultFontHeight: get default font ascent (for xlc protocol) -*/
+static void
+getDefaultFontHeight(kpw)
+KinputProtocolWidget kpw;
+{
+    Widget obj;
+
+    obj = XtCreateWidget("displayObj", kpw->kinput.displayObjClass,
+			 (Widget)kpw, NULL, 0);
+    (void)CDLineHeight(obj, &kpw->kinput.defaultascent);
+    XtDestroyWidget(obj);
+}
+
+/*
+ *+ property handling
+ */
+
+/*- setJinputProperty: set version property for jinput protocol -*/
+static void
+setJinputProperty(kpw)
+KinputProtocolWidget kpw;
+{
+    long version;
+
+    version = JINPUT_PROTOCOL_VERSION;
+    XChangeProperty(XtDisplay((Widget)kpw), XtWindow((Widget)kpw),
+		    kpw->kinput.convVersionAtom, XA_INTEGER, 32,
+		    PropModeReplace, (unsigned char *)&version, 1);
+}
+
+/*- setKinput2Property: set version property for kinput2 protocol -*/
+static void
+setKinput2Property(kpw)
+KinputProtocolWidget kpw;
+{
+    Display *dpy = XtDisplay((Widget)kpw);
+    Atom property;
+    Atom type;
+    unsigned long profile[10];
+
+    property = XInternAtom(dpy, CONVERSION_PROFILE, False);
+    type = XInternAtom(dpy, CONVERSION_ATTRIBUTE_TYPE, False);
+
+    profile[0] = CONV_ATTR(CONVPROF_PROTOCOL_VERSION, 1);
+    profile[1] = XInternAtom(dpy, PROTOCOL_VERSION, False);
+    profile[2] = CONV_ATTR(CONVPROF_SUPPORTED_STYLES, 1);
+    profile[3] = CONVARG_ROOTWINDOW|CONVARG_OFFTHESPOT|CONVARG_OVERTHESPOT;
+
+    XChangeProperty(dpy, XtWindow((Widget)kpw),
+		    property, type, 32, PropModeReplace,
+		    (unsigned char *)profile, 4);
+}
+
+/*- setXlcProperty: set initial status property for xlc protocol -*/
+static void
+setXlcProperty(kpw)
+KinputProtocolWidget kpw;
+{
+    setXlcStatusProperty(kpw, 0);
+    setXlcBCKey(kpw);
+}
+
+/*- setXlcStatusProperty: set status property for xlc protocol -*/
+static void
+setXlcStatusProperty(kpw, status)
+KinputProtocolWidget kpw;
+int status;
+{
+    XChangeProperty(XtDisplay((Widget)kpw), XtWindow((Widget)kpw),
+		    kpw->kinput.xlcStatusAtom, XA_INTEGER, 32,
+		    PropModeReplace, (unsigned char *)&status, 1);
+}
+
+/*- setXlcBCKey: set conversion start key code property for xlc protocol -*/
+static void
+setXlcBCKey(kpw)
+KinputProtocolWidget kpw;
+{
+    long code;
+    long mask;
+
+    if (kpw->kinput.xlcstartkey == NULL) return;
+    if (parseKeyEvent(XtDisplay((Widget)kpw), kpw->kinput.xlcstartkey,
+		    &code, &mask) == 0) {
+	String params[1];
+	Cardinal num_params;
+
+	params[0] = XtClass((Widget)kpw)->core_class.class_name;
+	num_params = 1;
+	XtAppWarningMsg(XtWidgetToApplicationContext((Widget)kpw),
+			"parseError", XtNxlcConversionStartKey, "WidgetError",
+			"%s: can't parse coversion start key string",
+			params, &num_params);
+	return;
+    }
+
+    TRACE(("setXlcBCKey(): keycode=%ld, mask=0x%lx\n", code, mask));
+
+    XChangeProperty(XtDisplay((Widget)kpw), XtWindow((Widget)kpw),
+		    kpw->kinput.xlcBcModifierAtom, XA_INTEGER, 32,
+		    PropModeReplace, (unsigned char *)&mask, 1);
+    XChangeProperty(XtDisplay((Widget)kpw), XtWindow((Widget)kpw),
+		    kpw->kinput.xlcBcKeycodeAtom, XA_INTEGER, 32,
+		    PropModeReplace, (unsigned char *)&code, 1);
+}
+
+/*
+ *+ event sending
+ */
+
+/*- sendClientMessage: send a clientmessage event -*/
+static void
+sendClientMessage(dpy, window, type, l0, l1, l2, l3, l4)
+Display *dpy;
+Window window;
+Atom type;
+long l0, l1, l2, l3, l4;
+{
+    XEvent event;
+
+    event.xclient.type = ClientMessage;
+    event.xclient.window = window;
+    event.xclient.message_type = type;
+    event.xclient.format = 32;
+    event.xclient.data.l[0] = l0;
+    event.xclient.data.l[1] = l1;
+    event.xclient.data.l[2] = l2;
+    event.xclient.data.l[3] = l3;
+    event.xclient.data.l[4] = l4;
+
+    XSendEvent(dpy, window, False, NoEventMask, &event);
+}
+
+/*- sendNegativeConversionNotify: send negative conversion-notify event -*/
+static void
+sendNegativeConversionNotify(kpw, window, selection)
+KinputProtocolWidget kpw;
+Window window;
+Atom selection;
+{
+    TRACE(("sendNegativeConversionNotify(window=0x%lx)\n", window));
+    sendClientMessage(XtDisplay((Widget)kpw), window,
+		      kpw->kinput.convNotifyAtom,
+		      (long)selection, (long)None, 0L, 0L, 0L);
+}
+
+/*- sendConversionNotify: send conversion-notify event -*/
+static void
+sendConversionNotify(ccp)
+ConvClient *ccp;
+{
+    KinputProtocolWidget kpw = (KinputProtocolWidget)ccp->protocolwidget;
+    long l4;
+
+    TRACE(("sendConversionNotify(reqwin=0x%lx)\n", ccp->reqwin));
+    if (ccp->protocol == xlc_protocol) {
+	l4 = (long)kpw->kinput.xlcOnTheSpotAtom;
+    } else {
+	l4 = 0L;
+    }
+    sendClientMessage(XtDisplay((Widget)kpw), ccp->reqwin,
+		      kpw->kinput.convNotifyAtom,
+		      (long)ccp->selection, (long)ccp->target,
+		      (long)ccp->property, (long)XtWindow(ccp->conversion),
+		      l4);
+}
+
+/*- sendNegativeConversionOpenNotify: send negative conversion-open-notify event -*/
+static void
+sendNegativeConversionOpenNotify(kpw, window, selection)
+KinputProtocolWidget kpw;
+Window window;
+Atom selection;
+{
+    TRACE(("sendNegativeConversionOpenNotify(window=0x%lx)\n", window));
+    sendClientMessage(XtDisplay((Widget)kpw), window,
+		      kpw->kinput.convOpenNotifyAtom,
+		      (long)selection, 0L, (long)XtWindow(kpw), 0L, 0L);
+}
+
+/*- sendConversionOpenNotify: send conversion-open-notify event -*/
+static void
+sendConversionOpenNotify(ccp)
+ConvClient *ccp;
+{
+    KinputProtocolWidget kpw = (KinputProtocolWidget)ccp->protocolwidget;
+
+    TRACE(("sendConversionOpenNotify(reqwin=0x%lx)\n", ccp->reqwin));
+    sendClientMessage(XtDisplay((Widget)kpw), ccp->reqwin,
+		      kpw->kinput.convOpenNotifyAtom,
+		      (long)ccp->selection, JINPUT_PROTOCOL_VERSION,
+		      (long)XtWindow((Widget)kpw), 1L, 0L);
+}
+
+/*- sendColorRequest: send color-request event (jinput only) -*/
+static void
+sendColorRequest(ccp)
+ConvClient *ccp;
+{
+    KinputProtocolWidget kpw = (KinputProtocolWidget)ccp->protocolwidget;
+
+    TRACE(("sendColorRequest(reqwin=0x%lx)\n", ccp->reqwin));
+    sendClientMessage(XtDisplay((Widget)kpw), ccp->reqwin,
+		      kpw->kinput.convColorRequestAtom,
+		      (long)ccp->selection, JINPUT_PROTOCOL_VERSION,
+		      (long)XtWindow((Widget)kpw), 0L, 0L);
+}
+
+/*- sendFontRequest: send font-request event (jinput only) -*/
+static void
+sendFontRequest(ccp)
+ConvClient *ccp;
+{
+    KinputProtocolWidget kpw = (KinputProtocolWidget)ccp->protocolwidget;
+
+    TRACE(("sendFontRequest(reqwin=0x%lx)\n", ccp->reqwin));
+    sendClientMessage(XtDisplay((Widget)kpw), ccp->reqwin,
+		      kpw->kinput.convFontsRequestAtom,
+		      (long)ccp->selection, JINPUT_PROTOCOL_VERSION,
+		      (long)XtWindow((Widget)kpw), 0L, 0L);
+}
+
+/*- sendXYRequest: send XY-request event (jinput only) -*/
+static void
+sendXYRequest(ccp)
+ConvClient *ccp;
+{
+    KinputProtocolWidget kpw = (KinputProtocolWidget)ccp->protocolwidget;
+
+    TRACE(("sendXYRequest(reqwin=0x%lx)\n", ccp->reqwin));
+    sendClientMessage(XtDisplay((Widget)kpw), ccp->reqwin,
+		      kpw->kinput.convXYRequestAtom,
+		      (long)ccp->selection, JINPUT_PROTOCOL_VERSION,
+		      (long)XtWindow((Widget)kpw), 0L, 0L);
+}
+
+/*
+ *+ callback procedures
+ */
+
+/*- fixCallback: fix callback -*/
+/* ARGSUSED */
+static void
+fixCallback(w, client_data, call_data)
+Widget w;
+XtPointer client_data;
+XtPointer call_data;
+{
+    CCTextCallbackArg *arg = (CCTextCallbackArg *)call_data;
+    ConvClient *ccp = (ConvClient *)client_data;
+
+    TRACE(("fixCallback(reqwin=0x%lx, length=%d)\n",ccp->reqwin, arg->length));
+    fixProc(ccp, arg);
+}
+
+/*- fixProc: do actual fix processing -*/
+static void
+fixProc(client, arg)
+ConvClient *client;
+CCTextCallbackArg *arg;
+{
+    /* Property $B$K7k2L$r%;%C%H$9$k(B */
+    XChangeProperty(XtDisplay(client->conversion), client->reqwin,
+		    client->property, arg->encoding, arg->format,
+		    PropModeAppend, (unsigned char *)arg->text, arg->length);
+
+    /* call protocol dependent proc */
+    if (client->fix_proc != NULL) (*client->fix_proc)(client, arg);
+}
+
+/*- jinputFix: jinput protocol specific fix processing -*/
+static void
+jinputFix(client)
+ConvClient *client;
+{
+    JinputData *jdp = (JinputData *)client->data;
+
+    /* spotlocation information is no longer valid */
+    client->attrmask &= ~CASpotLocation;
+
+    if (jdp->state & JINPUT_MULTI_COLOR) sendColorRequest(client);
+    if (jdp->state & JINPUT_MULTI_FONT) sendFontRequest(client);
+    if (client->style != separate_style) sendXYRequest(client);
+}
+
+/*- endCallback: conversion end callback -*/
+/* ARGSUSED */
+static void
+endCallback(w, client_data, call_data)
+Widget w;
+XtPointer client_data;
+XtPointer call_data;
+{
+    ConvClient *ccp = (ConvClient *)client_data;
+    int abort = (int)call_data;
+
+    TRACE(("endCallback(reqwin=0x%lx,abort=%s)\n", ccp->reqwin, abort?"True":"False"));
+    endProc(ccp, abort);
+}
+
+/*- endProc: conversion end processing -*/
+static void
+endProc(client, abort)
+ConvClient *client;
+int abort;
+{
+    KinputProtocolWidget kpw = (KinputProtocolWidget)client->protocolwidget;
+
+    if (!abort) {
+	/* $B%/%i%$%"%s%H$K(B ClientMessage $B$rAw$C$FCN$i$;$k(B
+	 * $B%$%Y%s%H$N%U%)!<%^%C%H$O!"(B
+	 *	window:		requestor window
+	 *	message_type:	"CONVERSION_END"
+	 *	format:		32
+	 *	data.l[0]:	selection
+	 *	data.l[1]:	selection-owner window
+	 */
+	sendClientMessage(XtDisplay((Widget)kpw), client->reqwin,
+			  kpw->kinput.convEndAtom,
+			  (long)client->selection, (long)XtWindow((Widget)kpw),
+			  0L, 0L, 0L);
+    }
+    if (client->conversion != NULL) {
+	XtRemoveCallback(client->conversion, XtNtextCallback,
+			 fixCallback, (XtPointer)client);
+	XtRemoveCallback(client->conversion, XtNendCallback,
+			 endCallback, (XtPointer)client);
+    }
+
+    /* call protocol dependent proc */
+    if (client->end_proc != NULL) {
+	(*client->end_proc)(client, abort);
+    } else {
+	deleteClient(client);
+    }
+}
+
+/*- jinputEnd: jinput specific conversion end processing -*/
+/* ARGSUSED */
+static void
+jinputEnd(client, abort)
+ConvClient *client;
+Boolean abort;		/* UNUSED */
+{
+    /* don't delete client until CONVERSION_CLOSE_REQUEST or client dead */
+    detachConverter(client);
+
+    client->attrmask &= ~CASpotLocation;
+}
+
+/*- jinputNewTextCallback: jinput protocol specific new text callback -*/
+/* ARGSUSED */
+static void
+jinputNewTextCallback(w, client_data, call_data)
+Widget w;
+XtPointer client_data;
+XtPointer call_data;
+{
+    ConvClient *ccp = (ConvClient *)client_data;
+
+    TRACE(("jinputNewTextCallback(reqwin=0x%lx)\n",ccp->reqwin));
+    if (ccp->style != separate_style) sendXYRequest(ccp);
+}
+
+/*- xlcEnd: xlc specific conversion end processing -*/
+/* ARGSUSED */
+static void
+xlcEnd(client, abort)
+ConvClient *client;
+Boolean abort;		/* UNUSED */
+{
+    /* don't delete client until CONVERSION_CLOSE or client dead */
+    detachConverter(client);
+
+    client->attrmask &= ~CASpotLocation;
+}
+
+/*
+ *+ ClientMessage event handler
+ */
+
+/*- ConversionOpenRequestProc: CONVERSION_OPEN_REQUEST event handler -*/
+/* ARGSUSED */
+static void
+ConversionOpenRequestProc(w, event, args, num_args)
+Widget w;
+XEvent *event;
+String *args;
+Cardinal *num_args;
+{
+    KinputProtocolWidget kpw = (KinputProtocolWidget)w;
+    XClientMessageEvent	*ev = &event->xclient;
+    int version;
+    Window reqwin;
+    Atom convatom;
+    Atom initproperty;
+    Atom initpropertytype;
+    ConvClient	*ccp;
+    JinputData *jdp;
+
+    TRACE(("ConversionOpenRequestProc(window=0x%lx)\n", ev->data.l[2]));
+    /* is it a correct event? */
+    if (!isCorrectClientEvent(kpw, event)) {
+	/*ignore */
+	DPRINT(("got invalid clientmessage event.\n"));
+	return;
+    }
+
+    convatom = (Atom)ev->data.l[0];
+    version = ev->data.l[1];
+    reqwin = (Window)ev->data.l[2];
+    initproperty = ev->data.l[3];
+    initpropertytype = ev->data.l[4];
+
+    /* check the protocol version & initial property type */
+    /* also check if the client already opened connection */
+    if (version != JINPUT_PROTOCOL_VERSION ||
+	initpropertytype != kpw->kinput.convInitialTypeAtom ||
+	findClient(kpw, reqwin) != NULL) {
+	DPRINT(("ConversionOpenRequestProc(): open denied\n"));
+	sendNegativeConversionOpenNotify(kpw, reqwin, convatom);
+	return;
+    }
+    /* check validity of the client window ID */
+    if (!isCorrectWindowID(w, reqwin)) {
+	DPRINT(("ConversionOpenRequestProc(): requestor window doesn't exist\n"));
+	/* nothing to do */
+	return;
+    }
+
+    ccp = newClient(kpw, reqwin, convatom);
+    ccp->protocol = jinput_protocol;
+    ccp->esm = ESMethodInputOnly;
+    ccp->start_proc = jinputSendReq;
+    ccp->detach_proc = jinputDetach;
+    ccp->fix_proc = jinputFix;
+    ccp->end_proc = jinputEnd;
+    ccp->free_resources = jinputFreeResources;
+    jdp = XtNew(JinputData);
+    jdp->state = 0;
+    ccp->data = (XtPointer)jdp;
+
+    getJinputInitialProperty(kpw, ccp, initproperty, initpropertytype);
+
+    /* watch for client destroy */
+    MyAddEventHandler(XtDisplay(w), reqwin, DestroyNotify, StructureNotifyMask,
+		      ClientDead, (XtPointer)ccp);
+
+    sendConversionOpenNotify(ccp);
+}
+
+/*- ConversionRequestProc: CONVERSION_REQUEST event handler -*/
+/* ARGSUSED */
+static void
+ConversionRequestProc(w, event, args, num_args)
+Widget w;
+XEvent *event;
+String *args;
+Cardinal *num_args;
+{
+    KinputProtocolWidget kpw = (KinputProtocolWidget)w;
+    XClientMessageEvent	*ev = &event->xclient;
+    Window reqwin;
+    Atom convatom;
+    Atom prop;
+    ConvClient	*ccp;
+
+    TRACE(("ConversionRequestProc(window=0x%lx)\n", ev->data.l[1]));
+    /* is it a correct event? */
+    if (!isCorrectClientEvent(kpw, event)) {
+	/*ignore */
+	DPRINT(("got invalid clientmessage event.\n"));
+	return;
+    }
+
+    convatom = (Atom)ev->data.l[0];
+    reqwin = (Window)ev->data.l[1];
+    prop = (Atom)ev->data.l[4];
+    TRACE(("\tatom=0x%lx, reqwin=0x%lx\n", convatom, reqwin));
+
+    if ((ccp = findClient(kpw, reqwin)) == NULL) {
+	/* check validity of the client window ID */
+	if (!isCorrectWindowID(w, reqwin)) {
+	    DPRINT(("ConversionRequestProc(): requestor window doesn't exist\n"));
+	    /* nothing to do */
+	    return;
+	}
+	ccp = newClient(kpw, reqwin, convatom);
+	if (prop == kpw->kinput.xlcOnTheSpotAtom) {
+	    /* xlc protocol */
+	    TRACE(("\txlc protocol\n"));
+	    if (getXlcDataFromProperty(kpw, ccp, True)) {
+		ccp->protocol = xlc_protocol;
+		ccp->style = overthespot_style;
+		ccp->esm = ESMethodInputOnly;
+		ccp->end_proc = xlcEnd;
+		ccp->free_resources = xlcFreeResources;
+		MyAddEventHandler(XtDisplay(w), reqwin,
+				  DestroyNotify, StructureNotifyMask,
+				  ClientDead, (XtPointer)ccp);
+	    } else {
+		TRACE(("\tchanged to kinput protocol\n"));
+		ccp->protocol = kinput1_protocol;
+		ccp->style = separate_style;
+		ccp->esm = ESMethodInputOnly;
+	    }
+	} else if (prop != None) {
+	    /* kinput2 protocol */
+	    TRACE(("\tkinput2 protocol\n"));
+	    ccp->protocol = kinput2_protocol;
+	    ccp->style = separate_style;
+	    ccp->esm = ESMethodInputOnly;
+	    ccp->free_resources = kinput2FreeResources;
+	    /* ccp->fix_proc = sendKeyCode0 */
+	    getAttributeFromProperty(ccp, prop);
+	} else {
+	    /* old protocol */
+	    ccp->protocol = kinput1_protocol;
+	    ccp->style = separate_style;
+	    ccp->esm = ESMethodInputOnly;
+	}
+    } else if (ccp->conversion != NULL) {
+	/* now converting */
+	if (ccp->protocol == xlc_protocol) {
+	    /*
+	     * xlc protocol uses CONVERSION_REQUEST event to notify
+	     * the frontend of changing conversion attribute
+	     */
+	    return;
+	}
+	sendNegativeConversionNotify(kpw, reqwin, convatom);
+	return;
+    } else if (ccp->protocol == xlc_protocol) {
+	if (prop == kpw->kinput.xlcOnTheSpotAtom) {
+	    /* reread property before starting conversion */
+	    (void)getXlcDataFromProperty(kpw, ccp, False);
+	}
+    }
+
+    /* set convatom */
+    if (ccp->selection == None) ccp->selection = convatom;
+
+    if (attachConverter(ccp) == NULL) {
+	sendNegativeConversionNotify(kpw, reqwin, convatom);
+	return;
+    }
+
+    /* set target type (ignore client's request) */
+    ccp->target = kpw->kinput.ctextAtom;
+
+    /* use default property if not specified */
+    if ((ccp->property = ev->data.l[3]) == None) {
+	ccp->property = kpw->kinput.convStringAtom;
+    }
+
+    XtAddCallback(ccp->conversion, XtNtextCallback,
+		  fixCallback, (XtPointer)ccp);
+    XtAddCallback(ccp->conversion, XtNendCallback,
+		  endCallback, (XtPointer)ccp);
+
+    /* startup the conversion window */
+    XtVaSetValues(ccp->conversion, XtNeventSelectMethod, ccp->esm, NULL);
+    myStartConversion(ccp->conversion, ccp->reqwin,
+		      ccp->attrmask, &ccp->attrs);
+
+    /* send ConversionNotify to the client */
+    sendConversionNotify(ccp);
+
+    if (ccp->start_proc != NULL) (*ccp->start_proc)(ccp);
+}
+
+/*- ConversionEndRequestProc: CONVERSION_END_REQUEST event handler -*/
+/* ARGSUSED */
+static void
+ConversionEndRequestProc(w, event, args, num_args)
+Widget w;
+XEvent *event;
+String *args;
+Cardinal *num_args;
+{
+    KinputProtocolWidget kpw = (KinputProtocolWidget)w;
+    XClientMessageEvent	*ev = &event->xclient;
+    Window reqwin;
+    ConvClient	*ccp;
+
+    TRACE(("ConversionEndRequestProc(window=0x%lx)\n", ev->data.l[1]));
+    /* is it a correct event? */
+    if (!isCorrectClientEvent(kpw, event)) {
+	/*ignore */
+	DPRINT(("got invalid clientmessage event.\n"));
+	return;
+    }
+
+    reqwin = (Window)ev->data.l[1];
+    if ((ccp = findClient(kpw, reqwin)) == NULL) {
+	/* request from unknown client. just ignore */
+	DPRINT(("got conversion end request from unknown window\n"));
+	return;
+    }
+
+    if (ccp->conversion != NULL) {
+	CControlEndConversion(ccp->conversion);
+	endProc(ccp, False);
+    }
+}
+
+/*- ConversionCloseRequestProc: CONVERSION_CLOSE_REQUEST event handler (jinput only) -*/
+/* ARGSUSED */
+static void
+ConversionCloseRequestProc(w, event, args, num_args)
+Widget w;
+XEvent *event;
+String *args;
+Cardinal *num_args;
+{
+    KinputProtocolWidget kpw = (KinputProtocolWidget)w;
+    XClientMessageEvent	*ev = &event->xclient;
+    Window reqwin;
+    ConvClient	*ccp;
+
+    TRACE(("ConversionCloseRequestProc(window=0x%lx)\n", ev->data.l[2]));
+    /* is it a correct event? */
+    if (!isCorrectClientEvent(kpw, event)) {
+	/*ignore */
+	DPRINT(("got invalid clientmessage event.\n"));
+	return;
+    }
+    if (ev->data.l[1] != JINPUT_PROTOCOL_VERSION) {
+	/* wrong version number */
+	DPRINT(("ConversionCloseRequestProc(): unknown version number (0x%lx)\n", ev->data.l[1]));
+	return;
+    }
+    reqwin = ev->data.l[2];
+    if ((ccp = findClient(kpw, reqwin)) == NULL) {
+	/* request from unknown client. just ignore */
+	DPRINT(("got conversion end request from unknown window\n"));
+	return;
+    }
+    if (ccp->protocol != jinput_protocol) {
+	DPRINT(("got jinput-specific event from a client that use other protocol\n"));
+	return;
+    }
+
+    MyRemoveEventHandler(XtDisplay(w), reqwin, DestroyNotify,
+			 ClientDead, (XtPointer)ccp);
+
+    deleteClient(ccp);
+}
+
+/*- ConversionXYNotifyProc: CONVERSION_XY_NOTIFY event handler (jinput only) -*/
+/* ARGSUSED */
+static void
+ConversionXYNotifyProc(w, event, args, num_args)
+Widget w;
+XEvent *event;
+String *args;
+Cardinal *num_args;
+{
+    KinputProtocolWidget kpw = (KinputProtocolWidget)w;
+    XClientMessageEvent	*ev = &event->xclient;
+    Window reqwin;
+    ConvClient	*ccp;
+    JinputData *jdp;
+
+    TRACE(("ConversionXYNotifyProc(window=0x%lx)\n", ev->data.l[2]));
+    /* is it a correct event? */
+    if (!isCorrectClientEvent(kpw, event)) {
+	/*ignore */
+	DPRINT(("got invalid clientmessage event.\n"));
+	return;
+    }
+    if (ev->data.l[1] != JINPUT_PROTOCOL_VERSION) {
+	/* wrong version number */
+	DPRINT(("ConversionXYNotifyProc(): unknown version number (0x%lx)\n", ev->data.l[1]));
+	return;
+    }
+    reqwin = ev->data.l[2];
+    if ((ccp = findClient(kpw, reqwin)) == NULL) {
+	/* request from unknown client. just ignore */
+	DPRINT(("got conversion end request from unknown window\n"));
+	return;
+    }
+    if (ccp->protocol != jinput_protocol) {
+	DPRINT(("got jinput-specific event from a client that use other protocol\n"));
+	return;
+    }
+    jdp = (JinputData *)ccp->data;
+    jdp->rawspotx = ev->data.l[3];
+    jdp->rawspoty = ev->data.l[4];
+
+    ccp->attrmask |= CASpotLocation;
+    ccp->attrs.spotx = jdp->rawspotx;
+    ccp->attrs.spoty = jdp->rawspoty +
+      ((ccp->attrmask & CAFonts) ? ccp->attrs.fonts[0]->ascent : 0);
+
+    if (ccp->conversion != NULL) {
+	myChangeAttributes(ccp->conversion, CASpotLocation, &ccp->attrs);
+    }
+}
+
+/*- ConversionColorNotifyProc: CONVERSION_COLOR_NOTIFY event handler (jinput only) -*/
+/* ARGSUSED */
+static void
+ConversionColorNotifyProc(w, event, args, num_args)
+Widget w;
+XEvent *event;
+String *args;
+Cardinal *num_args;
+{
+    KinputProtocolWidget kpw = (KinputProtocolWidget)w;
+    XClientMessageEvent	*ev = &event->xclient;
+    Window reqwin;
+    ConvClient	*ccp;
+
+    TRACE(("ConversionColorNotifyProc(window=0x%lx)\n", ev->data.l[2]));
+    /* is it a correct event? */
+    if (!isCorrectClientEvent(kpw, event)) {
+	/*ignore */
+	DPRINT(("got invalid clientmessage event.\n"));
+	return;
+    }
+    if (ev->data.l[1] != JINPUT_PROTOCOL_VERSION) {
+	/* wrong version number */
+	DPRINT(("ConversionColorNotifyProc(): unknown version number (0x%lx)\n", ev->data.l[1]));
+	return;
+    }
+    reqwin = ev->data.l[2];
+    if ((ccp = findClient(kpw, reqwin)) == NULL) {
+	/* request from unknown client. just ignore */
+	DPRINT(("got conversion end request from unknown window\n"));
+	return;
+    }
+    if (ccp->protocol != jinput_protocol) {
+	DPRINT(("got jinput-specific event from a client that use other protocol\n"));
+	return;
+    }
+    ccp->attrmask |= CAColor;
+    ccp->attrs.background = ev->data.l[3];
+    ccp->attrs.foreground = ev->data.l[4];
+
+    if (ccp->conversion != NULL) {
+	myChangeAttributes(ccp->conversion, CAColor, &ccp->attrs);
+    }
+}
+
+/*- ConversionFontsNotifyProc: CONVERSION_FONTS_NOTIFY event handler (jinput only) -*/
+/* ARGSUSED */
+static void
+ConversionFontsNotifyProc(w, event, args, num_args)
+Widget w;
+XEvent *event;
+String *args;
+Cardinal *num_args;
+{
+    KinputProtocolWidget kpw = (KinputProtocolWidget)w;
+    XClientMessageEvent	*ev = &event->xclient;
+    Window reqwin;
+    ConvClient	*ccp;
+    unsigned long attrmask;
+
+    TRACE(("ConversionFontsNotifyProc(window=0x%lx)\n", ev->data.l[2]));
+    /* is it a correct event? */
+    if (!isCorrectClientEvent(kpw, event)) {
+	/*ignore */
+	DPRINT(("got invalid clientmessage event.\n"));
+	return;
+    }
+    if (ev->data.l[1] != JINPUT_PROTOCOL_VERSION) {
+	/* wrong version number */
+	DPRINT(("ConversionFontsNotifyProc(): unknown version number (0x%lx)\n", ev->data.l[1]));
+	return;
+    }
+    reqwin = ev->data.l[2];
+    if ((ccp = findClient(kpw, reqwin)) == NULL) {
+	/* request from unknown client. just ignore */
+	DPRINT(("got conversion end request from unknown window\n"));
+	return;
+    }
+    if (ccp->protocol != jinput_protocol) {
+	DPRINT(("got jinput-specific event from a client that use other protocol\n"));
+	return;
+    }
+
+    attrmask = CAFonts;
+    if (ccp->attrmask & CAFonts) {
+	if (ccp->attrs.fonts[0]->fid != ev->data.l[3]) {
+	    XFreeFontInfo((char **)NULL, ccp->attrs.fonts[0], 1);
+	    ccp->attrs.fonts[0] = XQueryFont(XtDisplay((Widget)kpw),
+					     (XID)ev->data.l[3]);
+	}
+	if (ccp->attrs.fonts[1]->fid != ev->data.l[4]) {
+	    XFreeFontInfo((char **)NULL, ccp->attrs.fonts[1], 1);
+	    ccp->attrs.fonts[1] = XQueryFont(XtDisplay((Widget)kpw),
+					     (XID)ev->data.l[4]);
+	}
+    } else {
+	ccp->attrmask |= CAFonts;
+	ccp->attrs.num_fonts = 2;
+	ccp->attrs.fonts = (XFontStruct **)XtMalloc(sizeof(XFontStruct *) * 2);
+	ccp->attrs.fonts[0] = XQueryFont(XtDisplay((Widget)kpw),
+					 (XID)ev->data.l[3]);
+	ccp->attrs.fonts[1] = XQueryFont(XtDisplay((Widget)kpw),
+					 (XID)ev->data.l[4]);
+    }
+    if (ccp->attrmask & CASpotLocation) {
+	JinputData *jdp = (JinputData *)ccp->data;
+	ccp->attrs.spotx = jdp->rawspotx;
+	ccp->attrs.spoty = jdp->rawspoty + ccp->attrs.fonts[0]->ascent;
+	attrmask |= CASpotLocation;
+    }
+
+    if (ccp->conversion != NULL) {
+	myChangeAttributes(ccp->conversion, attrmask, &ccp->attrs);
+    }
+}
+
+/*- ConversionAttributeNotifyProc: CONVERSION_ATTRIBUTE_NOTIFY event handler (kinput2 only) -*/
+/* ARGSUSED */
+static void
+ConversionAttributeNotifyProc(w, event, args, num_args)
+Widget w;
+XEvent *event;
+String *args;
+Cardinal *num_args;
+{
+    KinputProtocolWidget kpw = (KinputProtocolWidget)w;
+    XClientMessageEvent	*ev = &event->xclient;
+    Window reqwin;
+    ConvClient	*ccp;
+    XFontStruct **fonts;
+    Cardinal nfonts;
+
+    TRACE(("ConversionAttributeNotifyProc(window=0x%lx)\n", ev->data.l[1]));
+    /* is it a correct event? */
+    if (!isCorrectClientEvent(kpw, event)) {
+	/*ignore */
+	DPRINT(("got invalid clientmessage event.\n"));
+	return;
+    }
+
+    reqwin = (Window)ev->data.l[1];
+    TRACE(("\treqwin=0x%lx\n", reqwin));
+
+    if ((ccp = findClient(kpw, reqwin)) == NULL) {
+	/* request from unknown client. just ignore */
+	DPRINT(("got conversion attribute request from unknown window\n"));
+	return;
+    } else if (ccp->protocol != kinput2_protocol) {
+	DPRINT(("get conversion attribute request from a client that doesn't use kinput2 protocol\n"));
+	return;
+    } else if (ccp->conversion == NULL) {
+	/* not converting (this should not happen) */
+	DPRINT(("got conversion attribute request before conversion start\n"));
+	return;
+    }
+
+    /*
+     * special treat for fonts -- because if they are changed,
+     * you should release old fonts.
+     */
+    if (ccp->attrmask & CAFonts) {
+	fonts = ccp->attrs.fonts;
+	nfonts = ccp->attrs.num_fonts;
+    } else {
+	fonts = NULL;
+	nfonts = 0;
+    }
+
+    ccp->attrmask = 0L;
+    getAttributeFromEvent(ccp, event);
+
+    /* change it */
+    myChangeAttributes(ccp->conversion, ccp->attrmask, &ccp->attrs);
+
+    if (ccp->attrmask & CAFonts) {
+	if (fonts != NULL) {
+	    /* fonts changed -- free old fonts */
+	    Cardinal i;
+	    for (i = 0; i < nfonts; i++) {
+		CachedFreeFont(XtDisplay(w), fonts[i]);
+	    }
+	    XtFree((char *)fonts);
+	}
+    } else {
+	/* restore fonts data */
+	ccp->attrmask = CAFonts;
+	ccp->attrs.fonts = fonts;
+	ccp->attrs.num_fonts = nfonts;
+    }
+}
+
+/*- ConversionCloseProc: CONVERSION_CLOSE event handler (xlc only) -*/
+/* ARGSUSED */
+static void
+ConversionCloseProc(w, event, args, num_args)
+Widget w;
+XEvent *event;
+String *args;
+Cardinal *num_args;
+{
+    KinputProtocolWidget kpw = (KinputProtocolWidget)w;
+    XClientMessageEvent	*ev = &event->xclient;
+    Window reqwin;
+    ConvClient	*ccp;
+
+    TRACE(("ConversionCloseProc(window=0x%lx)\n", ev->data.l[2]));
+    /* is it a correct event? */
+    if (!isCorrectClientEvent(kpw, event)) {
+	/*ignore */
+	DPRINT(("got invalid clientmessage event.\n"));
+	return;
+    }
+    reqwin = ev->data.l[1];
+    if ((ccp = findClient(kpw, reqwin)) == NULL) {
+	/* request from unknown client. just ignore */
+	DPRINT(("got conversion end from unknown window\n"));
+	return;
+    }
+
+    if (ccp->protocol != xlc_protocol && ccp->protocol != kinput1_protocol) {
+	/*
+	 * Only XLC protocol uses this event (CONVERSION_CLOSE ClientMessage),
+	 * so the checking for kinput1 protocol seems to be unnecessary.
+	 * ...Wrong. A client using kinput1 protocol and a client using
+	 * XLC protocol with off-the-spot mode can't be distinguishable
+	 * until you get this event.
+	 */
+	DPRINT(("got xlc-specific event from a client that use other protocol\n"));
+	return;
+    }
+
+    MyRemoveEventHandler(XtDisplay(w), reqwin, DestroyNotify,
+			 ClientDead, (XtPointer)ccp);
+    if (ccp->conversion != NULL) {
+	CControlEndConversion(ccp->conversion);
+	endProc(ccp, False);
+    }
+    if (ccp->protocol == xlc_protocol) deleteClient(ccp);
+}
+
+/*
+ *+ other event handler
+ */
+
+/*- XlcOnTheSpotChangedProc: ProptyNotify of "_XLC_ON_THE_SPOT" handler -*/ 
+/* ARGSUSED */
+static void
+XlcOnTheSpotChangedProc(w, event, args, num_args)
+Widget w;
+XEvent *event;
+String *args;			/* not used */
+Cardinal *num_args;		/* not used */
+{
+    KinputProtocolWidget kpw = (KinputProtocolWidget)w;
+    XPropertyEvent *ev = &(event->xproperty);
+    ConvClient *client;
+
+    TRACE(("XlcOnTheSpotChangedProc(window=0x%lx)\n", ev->window));
+
+    if (ev->window != XtWindow(w) ||
+	ev->atom != kpw->kinput.xlcOnTheSpotAtom) {
+	DPRINT(("\tgot invalid PropertyNotify event.\n"));
+	return;
+    } else if (ev->state != PropertyNewValue) {
+	return;
+    }
+    client = getXlcDataFromProperty(kpw, (ConvClient *)NULL, False);
+    if (client == NULL) return;
+
+    if (client->conversion != NULL) {
+	myChangeAttributes(client->conversion,
+			   client->attrmask, &client->attrs);
+    }
+}
+
+/*- SelectionRequestProc: SelectionRequest event handler -*/
+/*ARGSUSED*/
+static void
+SelectionRequestProc(w, event, args, num_args)
+Widget w;
+XEvent *event;
+String *args;			/* not used */
+Cardinal *num_args;		/* not used */
+{
+    XSelectionRequestEvent *ev = &(event->xselectionrequest);
+    XEvent repl;
+    String params[1];
+    Cardinal num_params;
+
+    repl.xselection.type = SelectionNotify;
+    repl.xselection.requestor = ev->requestor;
+    repl.xselection.selection = ev->selection;
+    repl.xselection.target = ev->target;
+    repl.xselection.property = None;
+    repl.xselection.time = ev->time;
+
+    params[0] = XtClass(w)->core_class.class_name;
+    num_params = 1;
+    XtAppWarningMsg(XtWidgetToApplicationContext(w),
+		    "selectionError", "SelectionRequest", "WidgetError",
+		    "%s: SelectionRequest event received",
+		    params, &num_params);
+
+    XSendEvent(ev->display, ev->requestor, False, NoEventMask, &repl);
+}
+
+/*- SelectionClearProc: SelectionClear event handler -*/
+/* ARGSUSED */
+static void
+SelectionClearProc(w, event, args, num_args)
+Widget w;
+XEvent *event;
+String *args;
+Cardinal *num_args;
+{
+    KinputProtocolWidget kpw = (KinputProtocolWidget)w;
+    ConvClient	*ccp;
+    String params[1];
+    Cardinal num_params;
+
+    /* Selection owner changed. kill myself */
+
+    /*
+     * send ConversionEnd event to the clients before exit
+     */
+    for (ccp = kpw->kinput.clients; ccp; ccp = ccp->next) {
+	if (ccp->reqwin != None) {
+	    if (ccp->conversion != NULL) {
+		CControlEndConversion(ccp->conversion);
+	    }
+	    endProc(ccp, False);
+	}
+    }
+
+    params[0] = XtClass(w)->core_class.class_name;
+    num_params = 1;
+    XtAppWarningMsg(XtWidgetToApplicationContext(w),
+		    "selectionError", "SelectionClear", "WidgetError",
+		    "%s: SelectionClear event received",
+		    params, &num_params);
+
+    XtDestroyWidget(w);
+}
+
+/*- ClientDead: DestroyNotify event handler (jinput and xlc) -*/
+static void
+ClientDead(ev, data)
+XEvent *ev;
+XtPointer data;
+{
+    ConvClient	*ccp = (ConvClient *)data;
+
+    TRACE(("ClientDead(window=0x%lx)\n", ev->xdestroywindow.window));
+    if (ev->type != DestroyNotify ||
+	ev->xdestroywindow.window != ccp->reqwin) return;
+
+    MyRemoveAllEventHandler(ev->xany.display, ccp->reqwin);
+    deleteClient(ccp);
+}