diff lib/imlib/imconv.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 1f9e9cb00c6c eda83436b27e
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/imlib/imconv.c	Mon Mar 08 04:44:30 2010 +0900
@@ -0,0 +1,1011 @@
+#ifndef lint
+static char *rcsid = "$Id: imconv.c,v 1.25 2002/01/24 09:07:19 ishisone Exp $";
+#endif
+/*
+ * Copyright (c) 1991, 1994  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
+ */
+
+#define COMMIT_SYNC
+#define STATUS_SYNC
+#include "im.h"
+#include "ConvMgr.h"
+#include "OverConv.h"
+#include "OffConv.h"
+#include "OnConv.h"
+#include "InputConv.h"
+
+static void fillDefaultAttributesForStartup _Pt_((IMIC *icp));
+static unsigned long makeConvAttributesForStartup _Pt_((IMIC *icp,
+							ConversionAttributes *attrp));
+static void commitString _Pt_((IMIC *icp, char *str, int len, int sync));
+static void fixCallback _Pt_((Widget w, XtPointer client_data,
+			      XtPointer call_data));
+static void detachConverter _Pt_((IMIC *icp));
+static void endCallback _Pt_((Widget w, XtPointer client_data,
+			      XtPointer call_data));
+static void unusedEventCallback _Pt_((Widget w, XtPointer client_data,
+				      XtPointer call_data));
+static void preeditStartCallback _Pt_((Widget w, XtPointer client_data,
+				       XtPointer call_data));
+static void preeditDoneCallback _Pt_((Widget w, XtPointer client_data,
+				      XtPointer call_data));
+static void preeditDrawCallback _Pt_((Widget w, XtPointer client_data,
+				      XtPointer call_data));
+static void preeditCaretCallback _Pt_((Widget w, XtPointer client_data,
+				       XtPointer call_data));
+static void statusStartCallback _Pt_((Widget w, XtPointer client_data,
+				      XtPointer call_data));
+static void statusDoneCallback _Pt_((Widget w, XtPointer client_data,
+				     XtPointer call_data));
+static void statusDrawCallback _Pt_((Widget w, XtPointer client_data,
+				     XtPointer call_data));
+static void preeditStart _Pt_((IMIC *icp));
+static void preeditDone _Pt_((IMIC *icp));
+static void preeditDraw _Pt_((IMIC *icp, OCCPreeditDrawArg *data));
+static void preeditCaret _Pt_((IMIC *icp, int caret));
+static void statusStart _Pt_((IMIC *icp));
+static void statusDone _Pt_((IMIC *icp));
+static void statusDraw _Pt_((IMIC *icp, OCCPreeditDrawArg *data));
+static void setEventMask _Pt_((IMIC *icp, unsigned long forward_mask,
+			       unsigned long synchronous_mask));
+
+
+/*- fillDefaultAttributesForStartup: put default necessary for conv. start -*/
+static void
+fillDefaultAttributesForStartup(icp)
+IMIC *icp;
+{
+    unsigned long cmask, pmask, smask;
+
+    cmask = ATTR_MASK_FOCUS | ATTR_MASK_PREEDIT_STATE | ATTR_MASK_RESET_STATE;
+
+    switch (icp->style) {
+    case IMSTYLE_OVER_THE_SPOT:
+	pmask = ATTR_MASK_FOREGROUND | ATTR_MASK_BACKGROUND |
+	    ATTR_MASK_FONT_SET;
+	smask = 0;
+	break;
+    case IMSTYLE_OFF_THE_SPOT:
+	pmask = ATTR_MASK_FOREGROUND | ATTR_MASK_BACKGROUND |
+	    ATTR_MASK_FONT_SET | ATTR_MASK_AREA;
+	smask = ATTR_MASK_AREA;
+	break;
+    case IMSTYLE_ON_THE_SPOT:
+	pmask = 0;
+	smask = 0;
+	break;
+    default:
+	pmask = 0;
+	smask = 0;
+    }
+    IMFillDefault(icp, cmask, pmask, smask);
+}
+
+/*- makeConvAttributesForStartup: get conv. attrs needed for startup -*/
+static unsigned long
+makeConvAttributesForStartup(icp, attrp)
+IMIC *icp;
+ConversionAttributes *attrp;
+{
+    icp->common_attr.change_mask = icp->common_attr.set_mask;
+    icp->preedit_attr.change_mask = icp->preedit_attr.set_mask;
+    icp->status_attr.change_mask = icp->status_attr.set_mask;
+    return IMMakeConvAttributes(icp, attrp);
+}
+
+/*- commitString: commmit converted string to client -*/
+static void
+commitString(icp, str, len, sync)
+IMIC *icp;
+char *str;
+int len;
+int sync;
+{
+    int offset;
+    IMConnection *conn = icp->im->connection;
+    unsigned int flag;
+
+    TRACE(("imlib:commitString()\n"));
+
+    if (DDEBUG_CONDITION(5)) {
+	unsigned char *p = (unsigned char *)str;
+	int i;
+
+	/*
+	 * Dump commiting string.
+	 */
+	printf("* commit string:\n\t");
+	for (i = 0; i < len; i++, p++) {
+	    if (*p == '\033') {
+		printf("ESC ");
+	    } else if (*p < ' ') {
+		printf("^%c ", *p + '@');
+	    } else if (*p == ' ') {
+		printf("sp ");
+	    } else if (*p >= 0x7f) {
+		printf("%x ", *p);
+	    } else {
+		printf("%c ", *p);
+	    }
+	}
+	printf("\n");
+    }
+
+    flag = XIM_FLAG_X_LOOKUP_CHARS;
+    if (sync) flag |= XIM_FLAG_SYNCHRONOUS;
+
+    offset = IMPutHeader(conn, XIM_COMMIT, 0, 0);
+    IMPutC16(conn, icp->im->id);
+    IMPutC16(conn, icp->id);
+    IMPutC16(conn, flag);
+    IMPutC16(conn, (unsigned int)len);
+    IMPutString(conn, str, len);
+    IMFinishRequest(conn, offset);
+}
+
+/*- fixCallback: fix callback -*/
+/* ARGSUSED */
+static void
+fixCallback(w, client_data, call_data)
+Widget w;
+XtPointer client_data;
+XtPointer call_data;
+{
+    IMIC *icp = (IMIC *)client_data;
+    IMConnection *conn = icp->im->connection;
+    Widget proto = conn->proto_widget;
+    Atom ctext = IMCtextAtom(proto);
+    CCTextCallbackArg *arg = (CCTextCallbackArg *)call_data;
+
+    TRACE(("imlib:fixCallback()\n"));
+
+    /* check encoding and format */
+    if (arg->encoding != ctext || arg->format != 8) {
+	/*
+	 * since every conversion object must support COMPOUND_TEXT,
+	 * it is a serious error.
+	 */
+	String params[2];
+	Cardinal num_params;
+	WidgetClass ioc = icp->im->converter->input_object_class;
+
+	params[0] = XtClass(proto)->core_class.class_name;
+	params[1] = ioc->core_class.class_name;
+	num_params = 2;
+
+	XtAppErrorMsg(XtWidgetToApplicationContext(proto),
+		      "encodingError", "convertedString", "WidgetError",
+		      "%s: encoding of the converted string is not COMPOUND_STRING. check inputObject %s",
+		      params, &num_params);
+    }
+
+    /*
+     * Send fixed string via XIM_COMMIT message.
+     * Since kinput2 uses full-synchronous mode,
+     * synchronous flag must be turned off.
+     */
+    commitString(icp, arg->text, arg->length, 0);
+
+#ifdef COMMIT_SYNC
+    /*
+     * Send XIM_SYNC_REPLY so that synchronize with clients here.
+     */
+    if (icp->state & IC_FORWARDING) {
+	icp->state &= ~IC_FORWARDING;
+	IMSendRequestWithIC(conn, XIM_SYNC_REPLY, 0, icp);
+    }
+#endif /* COMMIT_SYNC */
+}
+
+/*- detachConverter: detach conversion widget from specified IC -*/
+static void
+detachConverter(icp)
+IMIC *icp;
+{
+    Widget conv;
+
+    TRACE(("imlib:detachConverter()\n"));
+
+    conv = icp->conversion;
+    XtRemoveCallback(conv, XtNtextCallback, fixCallback, (XtPointer)icp);
+    XtRemoveCallback(conv, XtNendCallback, endCallback, (XtPointer)icp);
+    XtRemoveCallback(conv, XtNunusedEventCallback, unusedEventCallback, (XtPointer)icp);
+    if (icp->style == IMSTYLE_ON_THE_SPOT) {
+	XtRemoveCallback(conv, XtNpreeditStartCallback, preeditStartCallback,
+			 (XtPointer)icp);
+	XtRemoveCallback(conv, XtNpreeditDoneCallback, preeditDoneCallback,
+			 (XtPointer)icp);
+	XtRemoveCallback(conv, XtNpreeditDrawCallback, preeditDrawCallback,
+			 (XtPointer)icp);
+	XtRemoveCallback(conv, XtNpreeditCaretCallback, preeditCaretCallback,
+			 (XtPointer)icp);
+	XtRemoveCallback(conv, XtNstatusStartCallback, statusStartCallback,
+			 (XtPointer)icp);
+	XtRemoveCallback(conv, XtNstatusDoneCallback, statusDoneCallback,
+			 (XtPointer)icp);
+	XtRemoveCallback(conv, XtNstatusDrawCallback, statusDrawCallback,
+			 (XtPointer)icp);
+    }
+
+    CMReleaseConverter(XtParent(icp->im->connection->proto_widget), conv);
+    icp->conversion = NULL;
+}
+
+/*- endCallback: conversion end callback -*/
+/* ARGSUSED */
+static void
+endCallback(w, client_data, call_data)
+Widget w;
+XtPointer client_data;
+XtPointer call_data;
+{
+    IMIC *icp = (IMIC *)client_data;
+
+    TRACE(("imlib:endCallback()\n"));
+
+    if (icp->state & IC_CONVERTING) {
+	detachConverter(icp);
+	icp->state &= ~IC_CONVERTING;
+    }
+}
+
+/*- unusedEventCallback: unused key event callback -*/
+/* ARGSUSED */
+static void
+unusedEventCallback(w, client_data, call_data)
+Widget w;
+XtPointer client_data;
+XtPointer call_data;
+{
+    IMIC *icp = (IMIC *)client_data;
+    IMConnection *conn = icp->im->connection;
+    XKeyEvent *ev = (XKeyEvent *)call_data;
+    int offset;
+
+    TRACE(("imlib:unusedEventCallback()\n"));
+
+    if (icp->im->mask & XIM_EXT_FORWARD_KEYEVENT_MASK) {
+	offset = IMPutHeader(conn, XIM_EXT_FORWARD_KEYEVENT, 0, 0);
+	IMPutC16(conn, icp->im->id);
+	IMPutC16(conn, icp->id);
+	IMPutC16(conn, 0);
+	IMPutC16(conn, (unsigned int)(ev->serial & 0xffff));
+	IMPutC8(conn, ev->type);
+	IMPutC8(conn, (int)ev->keycode);
+	IMPutC16(conn, (unsigned int)ev->state);
+	IMPutC32(conn, ev->time);
+	IMPutC32(conn, ev->window);
+	IMFinishRequest(conn, offset);
+    } else {
+	offset = IMPutHeader(conn, XIM_FORWARD_EVENT, 0, 0);
+	IMPutC16(conn, icp->im->id);
+	IMPutC16(conn, icp->id);
+	IMPutC16(conn, 0);	/* ?? */
+	IMPutC16(conn, (unsigned int)((ev->serial >> 16) & 0xffff));
+
+	IMPutC8(conn, ev->type);
+	IMPutC8(conn, (int)ev->keycode);
+	IMPutC16(conn, (unsigned int)(ev->serial & 0xffff));
+	IMPutC32(conn, ev->time);
+	IMPutC32(conn, ev->root);
+	IMPutC32(conn, ev->window);
+	IMPutC32(conn, ev->subwindow);
+	IMPutI16(conn, ev->x_root);
+	IMPutI16(conn, ev->y_root);
+	IMPutI16(conn, ev->x);
+	IMPutI16(conn, ev->y);
+	IMPutC16(conn, ev->state);
+	IMPutC8(conn, ev->same_screen);
+
+	IMFinishRequest(conn, offset);
+    }
+}
+
+/*- preeditStartCallback: preedit start -*/
+/* ARGSUSED */
+static void
+preeditStartCallback(w, client_data, call_data)
+Widget w;
+XtPointer client_data;
+XtPointer call_data;
+{
+    IMIC *icp = (IMIC *)client_data;
+
+    TRACE(("preeditStartCallback(icp=0x%lx)\n", icp));
+
+    if (!(icp->common_attr.input_style & XIMPreeditCallbacks))
+	return;
+
+    preeditStart(icp);
+}
+
+/*- preeditDoneCallback: preedit done -*/
+/* ARGSUSED */
+static void
+preeditDoneCallback(w, client_data, call_data)
+Widget w;
+XtPointer client_data;
+XtPointer call_data;
+{
+    IMIC *icp = (IMIC *)client_data;
+
+    TRACE(("preeditDoneCallback(icp=0x%lx)\n", icp));
+
+    if (!(icp->common_attr.input_style & XIMPreeditCallbacks))
+	return;
+
+    preeditDone(icp);
+}
+
+/*- preeditDrawCallback: preedit draw -*/
+/* ARGSUSED */
+static void
+preeditDrawCallback(w, client_data, call_data)
+Widget w;
+XtPointer client_data;
+XtPointer call_data;
+{
+    IMIC *icp = (IMIC *)client_data;
+    OCCPreeditDrawArg *arg = (OCCPreeditDrawArg *)call_data;
+    IMConnection *conn = icp->im->connection;
+    Widget proto = conn->proto_widget;
+    Atom ctext = IMCtextAtom(proto);
+
+    TRACE(("preeditDrawCallback(icp=0x%lx, length=%d)\n",icp,arg->text_length));
+
+    if (!(icp->common_attr.input_style & XIMPreeditCallbacks))
+	return;
+
+    /* check encoding and format */
+    if (arg->encoding != ctext || arg->format != 8) {
+	/*
+	 * since every conversion object must support COMPOUND_TEXT,
+	 * it is a serious error.
+	 */
+	String params[2];
+	Cardinal num_params;
+	WidgetClass ioc = icp->im->converter->input_object_class;
+
+	params[0] = XtClass(proto)->core_class.class_name;
+	params[1] = ioc->core_class.class_name;
+	num_params = 2;
+
+	XtAppErrorMsg(XtWidgetToApplicationContext(proto),
+		      "encodingError", "preeditString", "WidgetError",
+		      "%s: encoding of the preedit string is not COMPOUND_STRING. check inputObject %s",
+		      params, &num_params);
+    }
+
+    preeditDraw(icp, arg);
+}
+
+/*- preeditCaretCallback: preedit caret -*/
+/* ARGSUSED */
+static void
+preeditCaretCallback(w, client_data, call_data)
+Widget w;
+XtPointer client_data;
+XtPointer call_data;
+{
+    IMIC *icp = (IMIC *)client_data;
+    int caret = (int)call_data;
+
+    TRACE(("preeditCaretCallback(icp=0x%lx, caret=%d)\n", icp, caret));
+
+    if (!(icp->common_attr.input_style & XIMPreeditCallbacks))
+	return;
+
+    preeditCaret(icp, caret);
+}
+
+/*- statusStartCallback: status start -*/
+/* ARGSUSED */
+static void
+statusStartCallback(w, client_data, call_data)
+Widget w;
+XtPointer client_data;
+XtPointer call_data;
+{
+    IMIC *icp = (IMIC *)client_data;
+
+    TRACE(("statusStartCallback(icp=0x%lx)\n", icp));
+
+    if (!(icp->common_attr.input_style & XIMStatusCallbacks))
+	return;
+
+    statusStart(icp);
+}
+
+/*- statusDoneCallback: status done -*/
+/* ARGSUSED */
+static void
+statusDoneCallback(w, client_data, call_data)
+Widget w;
+XtPointer client_data;
+XtPointer call_data;
+{
+    IMIC *icp = (IMIC *)client_data;
+
+    TRACE(("statusDoneCallback(icp=0x%lx)\n", icp));
+
+    if (!(icp->common_attr.input_style & XIMStatusCallbacks))
+	return;
+
+    statusDone(icp);
+}
+
+/*- statusDrawCallback: status draw -*/
+/* ARGSUSED */
+static void
+statusDrawCallback(w, client_data, call_data)
+Widget w;
+XtPointer client_data;
+XtPointer call_data;
+{
+    IMIC *icp = (IMIC *)client_data;
+    OCCPreeditDrawArg *arg = (OCCPreeditDrawArg *)call_data;
+    IMConnection *conn = icp->im->connection;
+    Widget proto = conn->proto_widget;
+    Atom ctext = IMCtextAtom(proto);
+
+    TRACE(("statusDrawCallback(icp=0x%lx, length=%d)\n", icp,arg->text_length));
+
+    if (!(icp->common_attr.input_style & XIMStatusCallbacks))
+	return;
+
+    /* check encoding and format */
+    if (arg->encoding != ctext || arg->format != 8) {
+	/*
+	 * since every conversion object must support COMPOUND_TEXT,
+	 * it is a serious error.
+	 */
+	String params[2];
+	Cardinal num_params;
+	WidgetClass ioc = icp->im->converter->input_object_class;
+
+	params[0] = XtClass(proto)->core_class.class_name;
+	params[1] = ioc->core_class.class_name;
+	num_params = 2;
+
+	XtAppErrorMsg(XtWidgetToApplicationContext(proto),
+		      "encodingError", "statusString", "WidgetError",
+		      "%s: encoding of the status string is not COMPOUND_STRING. check inputObject %s",
+		      params, &num_params);
+    }
+
+    statusDraw(icp, arg);
+}
+
+/*- preeditStart: do preedit start -*/
+static void
+preeditStart(icp)
+IMIC *icp;
+{
+    if (!(icp->state & IC_IN_PREEDIT)) {
+	int offset;
+	IMConnection *conn = icp->im->connection;
+
+	TRACE(("imlib:preeditStart()\n"));
+
+	offset = IMPutHeader(conn, XIM_PREEDIT_START, 0, 0);
+	IMPutC16(conn, icp->im->id);
+	IMPutC16(conn, icp->id);
+	IMFinishRequest(conn, offset);
+	icp->state |= IC_IN_PREEDIT;
+    }
+}
+
+/*- preeditDone: do preedit done -*/
+static void
+preeditDone(icp)
+IMIC *icp;
+{
+    if (icp->state & IC_IN_PREEDIT) {
+	int offset;
+	IMConnection *conn = icp->im->connection;
+
+	TRACE(("imlib:preeditDone()\n"));
+
+	offset = IMPutHeader(conn, XIM_PREEDIT_DONE, 0, 0);
+	IMPutC16(conn, icp->im->id);
+	IMPutC16(conn, icp->id);
+	IMFinishRequest(conn, offset);
+	icp->state &= ~IC_IN_PREEDIT;
+    }
+}
+
+/*- preeditDraw: do actual preedit draw -*/
+static void
+preeditDraw(icp, data)
+IMIC *icp;
+OCCPreeditDrawArg *data;
+{
+    IMConnection *conn = icp->im->connection;
+    int offset;
+    unsigned int status;
+    XIMFeedback feedback;
+    int i;
+
+    if (icp->state & IC_RESETTING) return;
+
+    preeditStart(icp);
+
+    TRACE(("imlib:preeditDraw()\n"));
+
+    if (DDEBUG_CONDITION(5)) {
+	unsigned char *p = (unsigned char *)data->text;
+
+	/*
+	 * Dump preedit string.
+	 */
+	printf("* preedit string:\n\t");
+	for (i = 0; i < data->text_length; i++, p++) {
+	    if (*p == '\033') {
+		printf("ESC ");
+	    } else if (*p < ' ') {
+		printf("^%c ", *p + '@');
+	    } else if (*p == ' ') {
+		printf("sp ");
+	    } else if (*p >= 0x7f) {
+		printf("%x ", *p);
+	    } else {
+		printf("%c ", *p);
+	    }
+	}
+	printf("\n");
+    }
+
+    offset = IMPutHeader(conn, XIM_PREEDIT_DRAW, 0, 0);
+    IMPutC16(conn, icp->im->id);
+    IMPutC16(conn, icp->id);
+    IMPutC32(conn, data->caret);
+    IMPutC32(conn, data->chg_first);
+    IMPutC32(conn, data->chg_length);
+    status = 0;
+    if (data->text_length == 0)  status |= 0x1; /* no string */
+    if (data->attrs_length == 0) status |= 0x2; /* no feedback */
+    IMPutC32(conn, status);
+    IMPutC16(conn, (unsigned int)data->text_length);
+    if (data->text_length > 0) {
+	IMPutString(conn, data->text, data->text_length);
+    }
+    IMPutPad(conn);
+    IMPutC16(conn, (unsigned int)(data->attrs_length * 4));
+    IMPutC16(conn, 0L); /* unused */
+    if (data->attrs_length > 0) {
+	for (i = 0; i < data->attrs_length; i++) {
+	    IMPutC32(conn, data->attrs[i]);
+	}
+    }
+    IMFinishRequest(conn, offset);
+}
+
+/*- preeditCaret: do actual preedit caret -*/
+static void
+preeditCaret(icp, caret)
+IMIC *icp;
+int caret;
+{
+    IMConnection *conn = icp->im->connection;
+    int offset;
+
+    if (icp->state & IC_RESETTING) return;
+
+    preeditStart(icp);
+
+    TRACE(("imlib:preeditCaret()\n"));
+
+    offset = IMPutHeader(conn, XIM_PREEDIT_CARET, 0, 0);
+    IMPutC16(conn, icp->im->id);
+    IMPutC16(conn, icp->id);
+    IMPutC32(conn, caret);
+    IMPutC32(conn, (long)XIMAbsolutePosition);
+    IMPutC32(conn, (long)XIMPrimary);
+    IMFinishRequest(conn, offset);
+}
+
+/*- statusStart: do status start -*/
+static void
+statusStart(icp)
+IMIC *icp;
+{
+    if (!(icp->state & IC_IN_STATUS)) {
+	int offset;
+	IMConnection *conn = icp->im->connection;
+
+	TRACE(("imlib:statusStart()\n"));
+
+	offset = IMPutHeader(conn, XIM_STATUS_START, 0, 0);
+	IMPutC16(conn, icp->im->id);
+	IMPutC16(conn, icp->id);
+	IMFinishRequest(conn, offset);
+	icp->state |= IC_IN_STATUS;
+#ifdef STATUS_SYNC
+        IMFlush(conn);
+#endif /* STATUS_SYNC */
+    }
+}
+
+/*- statusDone: do status done -*/
+static void
+statusDone(icp)
+IMIC *icp;
+{
+    if (icp->state & IC_IN_STATUS) {
+	int offset;
+	IMConnection *conn = icp->im->connection;
+
+	TRACE(("imlib:statusDone()\n"));
+
+	offset = IMPutHeader(conn, XIM_STATUS_DONE, 0, 0);
+	IMPutC16(conn, icp->im->id);
+	IMPutC16(conn, icp->id);
+	IMFinishRequest(conn, offset);
+	icp->state &= ~IC_IN_STATUS;
+#ifdef STATUS_SYNC
+        IMFlush(conn);
+#endif /* STATUS_SYNC */
+    }
+}
+
+/*- statusDraw: do actual status draw -*/
+static void
+statusDraw(icp, data)
+IMIC *icp;
+OCCPreeditDrawArg *data;
+{
+    IMConnection *conn = icp->im->connection;
+    int offset;
+    unsigned int status;
+
+    if (icp->state & IC_RESETTING) return;
+
+    statusStart(icp);
+
+    TRACE(("imlib:statusDraw()\n"));
+
+    offset = IMPutHeader(conn, XIM_STATUS_DRAW, 0, 0);
+    IMPutC16(conn, icp->im->id);
+    IMPutC16(conn, icp->id);
+    IMPutC32(conn, 0L); /* text type */
+    status = 0;
+    if (data->text_length == 0)  status |= 0x1; /* no string */
+    if (data->attrs_length == 0) status |= 0x2; /* no feedback */
+    IMPutC32(conn, status);
+    IMPutC16(conn, (unsigned int)data->text_length);
+    if (data->text_length > 0) {
+	IMPutString(conn, data->text, data->text_length);
+    }
+    IMPutPad(conn);
+    IMPutC16(conn, (unsigned int)(data->attrs_length * 32));
+    IMPutC16(conn, 0L); /* unused */
+    if (data->attrs_length > 0) {
+	int i;
+	for (i = 0; i < data->attrs_length; i++) {
+	    IMPutC32(conn, 0L);
+	}
+    }
+    IMFinishRequest(conn, offset);
+#ifdef STATUS_SYNC
+    IMFlush(conn);
+#endif /* STATUS_SYNC */
+}
+
+/*- setEventMask: put XIM_SET_EVENT_MASK request on the output stream -*/
+static void
+setEventMask(icp, forward_mask, synchronous_mask)
+IMIC *icp;
+unsigned long forward_mask;
+unsigned long synchronous_mask;
+{
+    IMConnection *conn = icp->im->connection;
+
+    (void)IMPutHeader(conn, XIM_SET_EVENT_MASK, 0, 12);
+    IMPutC16(conn, icp->im->id);
+    IMPutC16(conn, icp->id);
+    IMPutC32(conn, forward_mask);
+    IMPutC32(conn, synchronous_mask);
+    IMSchedule(conn, SCHED_WRITE);
+}
+
+
+/*
+ * Public functions
+ */
+
+int
+IMStartConversion(icp)
+IMIC *icp;
+{
+    IMIM *imp = icp->im;
+    Widget proto = imp->connection->proto_widget;
+    Widget converter;
+    WidgetClass class;
+    unsigned long attrmask;
+    ConversionAttributes attrs;
+
+    TRACE(("IMStartConversion()\n"));
+
+    if (icp->state & IC_CONVERTING) return 0;
+
+    /*
+     * Check required attributes i.e. client window.
+     */
+    if (!(icp->common_attr.set_mask & ATTR_MASK_CLIENT)) {
+	IMSendError(icp->im->connection, IMBadSomething, icp->im->id, icp->id,
+		    "client window required");
+	return -1;
+    }
+
+    /*
+     * Fill in default values for unspecified attributes.
+     */
+    fillDefaultAttributesForStartup(icp);
+
+    /*
+     * Get appropriate converter class.
+     */
+    if (icp->style == IMSTYLE_OVER_THE_SPOT) {
+	class = overTheSpotConversionWidgetClass;
+    } else if (icp->style == IMSTYLE_OFF_THE_SPOT) {
+	class = offTheSpotConversionWidgetClass;
+    } else if (icp->style == IMSTYLE_ON_THE_SPOT) {
+	class = onTheSpotConversionWidgetClass;
+    } else {
+	class = separateConversionWidgetClass;
+    }
+
+    /*
+     * Compute conversion attributes to be passed to the converter.
+     */
+    attrmask = makeConvAttributesForStartup(icp, &attrs);
+
+    icp->state &= ~IC_RESETTING;
+
+    /*
+     * Attach converter to this IC.
+     */
+    converter = CMGetConverter(XtParent(proto),
+			       icp->common_attr.client, class,
+			       imp->converter->input_object_class,
+			       imp->converter->display_object_class);
+    if (converter == NULL) {
+	IMSendError(imp->connection, IMBadSomething, imp->id, icp->id,
+		    "can't attach converter to this IC");
+	return -1;
+    }
+    icp->conversion = converter;
+
+    /*
+     * Add callback functions.
+     */
+    XtAddCallback(converter, XtNtextCallback, fixCallback, (XtPointer)icp);
+    XtAddCallback(converter, XtNendCallback, endCallback, (XtPointer)icp);
+    XtAddCallback(converter, XtNunusedEventCallback, unusedEventCallback, (XtPointer)icp);
+    if (icp->style == IMSTYLE_ON_THE_SPOT) {
+	XtAddCallback(converter, XtNpreeditStartCallback, preeditStartCallback,
+		      (XtPointer)icp);
+	XtAddCallback(converter, XtNpreeditDoneCallback, preeditDoneCallback,
+		      (XtPointer)icp);
+	XtAddCallback(converter, XtNpreeditDrawCallback, preeditDrawCallback,
+		      (XtPointer)icp);
+	XtAddCallback(converter, XtNpreeditCaretCallback, preeditCaretCallback,
+		      (XtPointer)icp);
+	XtAddCallback(converter, XtNstatusStartCallback, statusStartCallback,
+		      (XtPointer)icp);
+	XtAddCallback(converter, XtNstatusDoneCallback, statusDoneCallback,
+		      (XtPointer)icp);
+	XtAddCallback(converter, XtNstatusDrawCallback, statusDrawCallback,
+		      (XtPointer)icp);
+    }
+
+    /*
+     * Start conversion
+     */
+    /* !!! if front-end method is used, ESMethodSelectFocus should be used */
+    XtVaSetValues(converter, XtNeventSelectMethod, ESMethodNone, NULL);
+    CControlStartConversion(converter, icp->common_attr.client,
+			    attrmask, &attrs);
+
+    icp->state |= IC_CONVERTING;
+
+    if (icp->common_attr.input_style & XIMPreeditCallbacks)
+	preeditStart(icp);
+
+    /*
+     * Send XIM_SET_EVENT_MASK to let the client forward the key events.
+     */
+    IMStartForwarding(icp);
+
+    return 0;
+}
+
+void
+IMStopConversion(icp)
+IMIC *icp;
+{
+    TRACE(("IMStopConversion()\n"));
+
+    if (!(icp->state & IC_CONVERTING)) return;
+
+    /*
+     * Terminate conversion.
+     */
+    CControlEndConversion(icp->conversion);
+
+    if (icp->common_attr.input_style & XIMPreeditCallbacks)
+	preeditDone(icp);
+    IMStatusDone(icp);
+
+    /*
+     * Detach converter.
+     */
+    detachConverter(icp);
+
+    /*
+     * Stop forwarding key events unless this IC is being destroyed.
+     */
+    if (!(icp->state & IC_DESTROYING)) {
+	IMStopForwarding(icp);
+    }
+
+    icp->state &= ~IC_CONVERTING;
+}
+
+int
+IMResetIC(icp, preedit_strp)
+IMIC *icp;
+char **preedit_strp;
+{
+    int num_bytes = 0;
+
+    TRACE(("IMResetIC()\n"));
+
+    *preedit_strp = NULL;
+
+    if (icp->state & IC_CONVERTING) {
+	/*
+	 * get input object by asking conversion widget of XtNinputObject
+	 * resource. however, it is not recommended since protocol widget
+	 * should interact with input object only through conversion
+	 * widget.
+	 */
+	CCTextCallbackArg arg;
+	Widget input_obj;
+	Widget w = icp->im->connection->proto_widget;
+
+	XtVaGetValues(icp->conversion, XtNinputObject, &input_obj, NULL);
+	arg.encoding = IMCtextAtom(w);
+#ifdef notdef
+	if (ICGetConvertedString(input_obj, &arg.encoding, &arg.format,
+				 &arg.length, &arg.text) >= 0) {
+	    num_bytes = arg.length;
+	    *preedit_strp = (char *)arg.text;
+	}
+#else
+	/*
+	 * Canna seems to have some problem with ICGetConvertedString().
+	 * Use ICGetPreeditString instead.
+	 */
+	if (ICGetPreeditString(input_obj, 0, 0, &arg.encoding, &arg.format,
+			       &arg.length, &arg.text) >= 0) {
+	    num_bytes = arg.length;
+	    *preedit_strp = (char *)arg.text;
+	}
+#endif
+	ICClearConversion(input_obj);
+	TRACE(("\twas converting. %d bytes left\n", num_bytes));
+
+	if (icp->common_attr.reset_state == XIMInitialState) {
+	    /* Force to end the conversion. */
+	    TRACE(("\tback to the initial state\n"));
+	    IMStopConversion(icp);
+	}
+    }
+    return num_bytes;
+}
+
+void
+IMForwardEvent(icp, ev)
+IMIC *icp;
+XEvent *ev;
+{
+    TRACE(("IMForwardEvent()\n"));
+
+    if (icp->conversion == NULL) return;
+    XtCallActionProc(icp->conversion, "to-inputobj", ev,
+		     (String *)NULL, (Cardinal)0);
+}
+
+/* ARGSUSED */
+void
+IMSetFocus(icp)
+IMIC *icp;
+{
+    TRACE(("IMSetFocus(ic%d)\n", icp->id));
+    if (icp->conversion != NULL) {
+	CControlChangeFocus(icp->conversion, 1);
+    }
+}
+
+/* ARGSUSED */
+void
+IMUnsetFocus(icp)
+IMIC *icp;
+{
+    TRACE(("IMUnsetFocus(ic%d)\n", icp->id));
+    if (icp->conversion != NULL) {
+	CControlChangeFocus(icp->conversion, 0);
+    }
+}
+
+/* ARGSUSED */
+void
+IMStatusStart(icp)
+IMIC *icp;
+{
+    TRACE(("IMStatusStart(ic%d)\n", icp->id));
+    if (!(icp->common_attr.input_style & XIMStatusCallbacks))
+	return;
+    statusStart(icp);
+}
+
+/* ARGSUSED */
+void
+IMStatusDone(icp)
+IMIC *icp;
+{
+    TRACE(("IMStatusDone(ic%d)\n", icp->id));
+    if (!(icp->common_attr.input_style & XIMStatusCallbacks))
+	return;
+    statusDone(icp);
+}
+
+void
+IMStartForwarding(icp)
+IMIC *icp;
+{
+    /*
+     * Make the client forward key events to us.
+     */
+    TRACE(("IMStartForwarding(ic%d)\n", icp->id));
+
+#define FORWARD_MASK (KeyPressMask|KeyReleaseMask)
+
+#ifdef notdef
+    if (synchronous) {
+	setEventMask(icp, FORWARD_MASK, FORWARD_MASK);
+    } else {
+	setEventMask(icp, FORWARD_MASK, NoEventMask);
+    }
+#else
+    /* using full-synchronous method */
+    setEventMask(icp, FORWARD_MASK, FORWARD_MASK);
+#endif
+
+#undef FORWARD_MASK
+}
+
+void
+IMStopForwarding(icp)
+IMIC *icp;
+{
+    /*
+     * Make the client stop sending key events.
+     */
+    TRACE(("IMStopForwarding(ic%d)\n", icp->id));
+    setEventMask(icp, NoEventMask, NoEventMask);
+}