diff lib/IMProto.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
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/IMProto.c	Mon Mar 08 04:44:30 2010 +0900
@@ -0,0 +1,1022 @@
+#ifndef lint
+static char *rcsid = "$Id: IMProto.c,v 1.20 1999/04/12 08:52:23 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
+ */
+
+/*
+ * X Input Method Protocol handler is considered to be still in
+ * beta testing phase.  Current version has the following
+ * restrictions.
+ *	- it does not support on-demand-synchronous method.
+ *	- it does not support front-end model.
+ *	- it does not SetIMValues operation.
+ *	- it supports only X, local and TCP transports.
+ * Also, there might be various bugs.
+ */
+
+#define DEBUG_VAR debug_IMProtocol
+
+#include <ctype.h>
+#include <X11/Xos.h>
+#include <sys/stat.h>
+#include <X11/IntrinsicP.h>
+#include <X11/StringDefs.h>
+#include <X11/Xatom.h>
+#include <X11/Xmu/SysUtil.h>
+#include "IMProtoP.h"
+#include "ParseKey.h"
+#include "InputConv.h"
+#include "im.h"
+
+
+#define SERVER_NAME		"kinput2"
+#define UNIX_SOCKET_DIR		"/tmp/.ki2-unix"
+
+/*- resource table -*/
+static XtResource resources[] = {
+#define offset(field) XtOffset(IMProtocolWidget, imp.field)
+    { XtNserverName, XtCServerName, XtRString, sizeof(String),
+	offset(server_name), XtRString, (XtPointer)SERVER_NAME },
+    { XtNlanguage, XtCLanguage, XtRString, sizeof(String),
+	offset(language), XtRImmediate, (XtPointer)NULL },
+    { XtNlocales, XtCLocales, XtRString, sizeof(String),
+	offset(locales), XtRImmediate, (XtPointer)NULL },
+    { XtNinputObjectClass, XtCClass, XtRPointer, sizeof(WidgetClass),
+	offset(input_object_class), XtRImmediate, (XtPointer)NULL },
+    { XtNdisplayObjectClass, XtCClass, XtRPointer, sizeof(WidgetClass),
+	offset(display_object_class), XtRImmediate, (XtPointer)NULL },
+    { XtNdefaultFontList, XtCFontList, XtRString, sizeof(String),
+	offset(default_fontlist), XtRImmediate, (XtPointer)NULL },
+    { XtNconversionStartKeys, XtCConversionStartKeys, XtRString, sizeof(String),
+	offset(conversion_start_keys), XtRImmediate, (XtPointer)NULL },
+    { XtNforeground, XtCForeground, XtRPixel, sizeof (Pixel),
+	offset(foreground), XtRString, XtDefaultForeground },
+    { XtNstatusWidth, XtCStatusWidth, XtRDimension, sizeof(Dimension),
+	offset(status_width), XtRImmediate, (XtPointer)0 },
+    { XtNtransports, XtCTransports, XtRString, sizeof(String),
+	offset(transport_list), XtRString, (XtPointer)"tcp,unix,x" },
+#undef offset
+};
+
+static void Initialize _Pt_((Widget req, Widget new,
+			     ArgList args, Cardinal *num_args));
+static void Destroy _Pt_((Widget w));
+static void Realize _Pt_((Widget w, XtValueMask *mask,
+			  XSetWindowAttributes *value));
+
+/*- IMProtocolClassRec -*/
+IMProtocolClassRec imProtocolClassRec = {
+  { /* core fields */
+    /* superclass		*/	(WidgetClass) &widgetClassRec,
+    /* class_name		*/	"IMProtocol",
+    /* widget_size		*/	sizeof(IMProtocolRec),
+    /* class_initialize		*/	NULL,
+    /* class_part_initialize	*/	NULL,
+    /* class_inited		*/	FALSE,
+    /* initialize		*/	Initialize,
+    /* initialize_hook		*/	NULL,
+    /* realize			*/	Realize,
+    /* actions			*/	NULL,
+    /* num_actions		*/	0,
+    /* 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		*/	NULL,
+    /* set_values_hook		*/	NULL,
+    /* set_values_almost	*/	XtInheritSetValuesAlmost,
+    /* get_values_hook		*/	NULL,
+    /* accept_focus		*/	NULL,
+    /* version			*/	XtVersion,
+    /* callback_private		*/	NULL,
+    /* tm_table			*/	NULL,
+    /* query_geometry		*/	XtInheritQueryGeometry,
+    /* display_accelerator	*/	XtInheritDisplayAccelerator,
+    /* extension		*/	NULL
+  },
+  { /* imProtocol fields */
+    /* dummy			*/	0
+  }
+};
+
+WidgetClass imProtocolWidgetClass = (WidgetClass)&imProtocolClassRec;
+
+static void getAtoms _Pt_((IMProtocolWidget ipw));
+static void setProperty _Pt_((IMProtocolWidget ipw));
+static int ownSelection _Pt_((IMProtocolWidget ipw));
+static Boolean convertSelection _Pt_((Widget w, Atom *selectionp,
+				      Atom *targetp, Atom *typep,
+				      XtPointer *valuep,
+				      unsigned long *lengthp, int *formatp));
+static void loseSelection _Pt_((Widget w, Atom *selectionp));
+#ifdef IM_TCP_TRANSPORT
+static void acceptTCPService _Pt_((XtPointer client_data,
+				  int *sourcep, XtInputId *idp));
+#endif
+#ifdef IM_UNIX_TRANSPORT
+static void acceptUnixService _Pt_((XtPointer client_data,
+				    int *sourcep, XtInputId *idp));
+#endif
+#ifdef IM_X_TRANSPORT
+static void acceptXService _Pt_((Widget w, XtPointer client_data,
+				 XEvent *event, Boolean *continuep));
+#endif
+static void initializeError _Pt_((Widget w, String resname));
+static char *compactList _Pt_((char *s));
+static void setTransport _Pt_((Widget w));
+static int makeConverter _Pt_((Widget w));
+static void getTriggerKeys _Pt_((Widget w));
+static void ioeCallback _Pt_((XPointer cldata));
+
+
+/*
+ *+ Core class methods
+ */
+
+/*- Initialize: intern Atoms, get default fonts, etc. -*/
+/* ARGSUSED */
+static void
+Initialize(req, new, args, num_args)
+Widget req;
+Widget new;
+ArgList args;
+Cardinal *num_args;
+{
+    IMProtocolWidget ipw = (IMProtocolWidget)new;
+
+    TRACE(("IMProtocolWidget:Initialize()\n"));
+
+    /*
+     * Check resources which must be specified at the initialization.
+     */
+#define NULLorEMPTY(p)	((p) == NULL || (p)[0] == '\0')
+    if (NULLorEMPTY(ipw->imp.server_name)) {
+	initializeError(new, XtNserverName);
+    }
+
+    ipw->imp.server_name = XtNewString(ipw->imp.server_name);
+
+    if (NULLorEMPTY(ipw->imp.language)) {
+	initializeError(new, XtNlanguage);
+    } else if (NULLorEMPTY(ipw->imp.locales)) {
+	initializeError(new, XtNlocales);
+    } else if (ipw->imp.input_object_class == NULL) {
+	initializeError(new, XtNinputObjectClass);
+    } else if (ipw->imp.display_object_class == NULL) {
+	initializeError(new, XtNdisplayObjectClass);
+    }
+    ipw->imp.locales = compactList(XtNewString(ipw->imp.locales));
+#undef NULLorEMPTY
+
+    /*
+     * Initialize converter info.
+     */
+    if (makeConverter(new) < 0) {
+	/*
+	 * locales is empty.
+	 */
+	String params[1];
+	Cardinal num_params;
+
+	params[0] = XtClass(new)->core_class.class_name;
+	num_params = 1;
+	XtAppErrorMsg(XtWidgetToApplicationContext(new),
+		      "initializeError", "invalidValue", "WidgetError",
+		      "%s: locale list is empty",
+		      params, &num_params);
+    }
+
+    /*
+     * Create font bank (a bank of cached fonts) and enter
+     * default fonts.
+     */
+    ipw->imp.font_bank = FontBankCreate(XtDisplay(new), ipw->imp.language);
+    if (ipw->imp.font_bank == NULL) {
+	/*
+	 * The specified language is not supported.
+	 */
+	String params[2];
+	Cardinal num_params;
+
+	params[0] = XtClass(new)->core_class.class_name;
+	params[1] = ipw->imp.language;
+	num_params = 2;
+	XtAppErrorMsg(XtWidgetToApplicationContext(new),
+		      "initializeError", "invalidValue", "WidgetError",
+		      "%s: language %s not supported",
+		      params, &num_params);
+    }
+
+    if (ipw->imp.default_fontlist != NULL) {
+	ipw->imp.default_fontlist = XtNewString(ipw->imp.default_fontlist);
+
+	DDPRINT(2, ("cache default fonts: %s\n", ipw->imp.default_fontlist));
+	ipw->imp.default_fonts = FontBankGet(ipw->imp.font_bank,
+					     ipw->imp.default_fontlist,
+					     &ipw->imp.num_default_fonts);
+    } else {
+	ipw->imp.default_fonts = NULL;
+	ipw->imp.num_default_fonts = 0;
+    }
+
+    /*
+     * Initialize private data.
+     */
+    ipw->imp.connection_list = NULL;
+    ipw->imp.no_more_connections = False;
+    ipw->imp.scheduler_queue = NULL;
+    setTransport(new);
+    getTriggerKeys(new);
+    IMInitHash(new);
+    getAtoms(ipw);
+
+    /*
+     * Initialilze transport layer.
+     */
+    /* 1. TCP/IP */
+    ipw->imp.tcp_sock = -1;
+#ifdef IM_TCP_TRANSPORT
+    if (ipw->imp.use_tcp_transport) {
+	ipw->imp.tcp_port = 0;	/* let the system choose the port number */
+	ipw->imp.tcp_sock = IMCreateTCPService(&ipw->imp.tcp_port);
+    }
+    if (ipw->imp.tcp_sock >= 0) {
+	TRACE(("call XtAppAddInput for tcp socket(%d)\n", ipw->imp.tcp_sock));
+	ipw->imp.tcp_id = XtAppAddInput(XtWidgetToApplicationContext(new),
+					ipw->imp.tcp_sock,
+					(XtPointer)XtInputReadMask,
+					acceptTCPService, (XtPointer)ipw);
+    }
+#endif /* IM_TCP_TRANSPORT */
+
+    /* 2. UNIX domain */
+    ipw->imp.unix_sock = -1;
+#ifdef IM_UNIX_TRANSPORT
+    if (ipw->imp.use_unix_transport) {
+	char path[1024];
+	char *p;
+
+	/*
+	 * The unix domain socket pathname has the following form:
+	 *   <UNIX_SOCKET_DIR>/<Display Name>-<Language>
+	 */
+	(void)mkdir(UNIX_SOCKET_DIR, 0777);
+#ifdef S_IFLNK
+	{
+	    /*
+	     * This system has symbolic links.  Make sure UNIX_SOCKET_DIR
+	     * is not a symbolic link but a directory before calling
+	     * chmod().
+	     */
+	    struct stat st;
+	    if (lstat(UNIX_SOCKET_DIR, &st) == 0 &&
+		(st.st_mode & S_IFMT) == S_IFDIR) {
+		(void)chmod(UNIX_SOCKET_DIR, 0777);
+	    }
+	}
+#else
+	(void)chmod(UNIX_SOCKET_DIR, 0777);
+#endif
+	(void)sprintf(path, "%s/%s", UNIX_SOCKET_DIR,
+		      DisplayString(XtDisplay(new)));
+	/*
+	 * Omit screen number and the preceding period.
+	 */
+	for (p = path + strlen(path) - 1; p > path && *p != ':'; p--) {
+	    if (*p == '.') {
+		*p = '\0';
+		break;
+	    }
+	}
+	/*
+	 * Append language part.
+	 */
+	(void)strcat(path, "-");
+	(void)strcat(path, ipw->imp.language);
+	/*
+	 * Change every ':' in the path name to '_', since ':' is not
+	 * included in POSIX Portable Filename Character Set.
+	 */
+	for (p = path; *p != '\0'; p++) {
+	    if (*p == ':') *p = '_';
+	}
+	ipw->imp.unix_path = XtNewString(path);
+	ipw->imp.unix_sock = IMCreateUnixService(ipw->imp.unix_path);
+    }
+    if (ipw->imp.unix_sock >= 0) {
+	TRACE(("call XtAppAddInput for unix socket(%d)\n", ipw->imp.unix_sock));
+	ipw->imp.unix_id = XtAppAddInput(XtWidgetToApplicationContext(new),
+					 ipw->imp.unix_sock,
+					 (XtPointer)XtInputReadMask,
+					 acceptUnixService, (XtPointer)ipw);
+	ipw->imp.ioe_handle = XIOESet(ioeCallback, (XPointer)ipw);
+    }
+#endif /* IM_UNIX_TRANSPORT */
+
+#ifdef IM_X_TRANSPORT
+    if (ipw->imp.use_x_transport) {
+	TRACE(("call XtAddEventHandler for X transport\n"));
+	XtAddEventHandler(new, NoEventMask, True, acceptXService,
+			  (XtPointer)NULL);
+    }
+#endif /* IM_X_TRANSPORT */
+
+    /*
+     * Compile request dispatching table.
+     */
+    IMCompileReq();
+}
+
+/*- Destroy: free allocated memory -*/
+static void
+Destroy(w)
+Widget w;
+{
+    IMProtocolWidget ipw = (IMProtocolWidget)w;
+    IMConnection *conn;
+    int i;
+
+    TRACE(("IMProtocolWidget:Destroy()\n"));
+
+    XtFree(ipw->imp.server_name);
+    XtFree(ipw->imp.locales);
+    if (ipw->imp.default_fontlist != NULL) XtFree(ipw->imp.default_fontlist);
+    if (ipw->imp.trigger_keys != NULL) XtFree((char *)ipw->imp.trigger_keys);
+
+    for (i = 0; i < ipw->imp.converter.num_locales; i++) {
+	XtFree(ipw->imp.converter.supported_locales[i]);
+    }
+    XtFree((char *)ipw->imp.converter.supported_locales);
+
+    /*
+     * Close down all connections.
+     */
+    conn = ipw->imp.connection_list;
+    while (conn != NULL) {
+	IMConnection *next = conn->next;
+
+	IMCloseConnection(conn);
+	conn = next;
+    }
+
+    /*
+     * Close down TCP/Unix service sockets.
+     */
+    if (ipw->imp.tcp_sock >= 0) {
+	XtRemoveInput(ipw->imp.tcp_id);
+	(void)close(ipw->imp.tcp_sock);
+    }
+    if (ipw->imp.unix_sock >= 0) {
+	XIOEUnset(ipw->imp.ioe_handle);
+	(void)unlink(ipw->imp.unix_path);
+	XtRemoveInput(ipw->imp.unix_id);
+	(void)close(ipw->imp.unix_sock);
+	XtFree(ipw->imp.unix_path);
+    }
+
+    /*
+     * Unload default fonts.
+     */
+    if (ipw->imp.num_default_fonts > 0) {
+	FontBankFreeFonts(ipw->imp.font_bank,
+			  ipw->imp.default_fonts,
+			  ipw->imp.num_default_fonts);
+    }
+
+    /*
+     * Free font bank.
+     */
+    FontBankDestroy(ipw->imp.font_bank);
+}
+
+/*- Realize: own selection -*/
+static void
+Realize(w, mask, value)
+Widget w;
+XtValueMask *mask;
+XSetWindowAttributes *value;
+{
+    IMProtocolWidget ipw = (IMProtocolWidget)w;
+    CoreWidgetClass super = (CoreWidgetClass)XtClass(w)->core_class.superclass;
+
+    TRACE(("IMProtocolWidget:Realize()\n"));
+
+    (*super->core_class.realize)(w, mask, value);
+
+    if (ownSelection(ipw) < 0) {
+	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);
+    }
+
+    setProperty(ipw);
+}
+
+/*
+ *+ Atom, property and selection handling
+ */
+
+/*- getAtoms: intern atoms -*/
+static void
+getAtoms(ipw)
+IMProtocolWidget ipw;
+{
+    Display *dpy = XtDisplay((Widget)ipw);
+    char buf[256];
+
+    TRACE(("IMProtocolWidget:getAtoms()\n"));
+
+    (void)strcpy(buf, "@server=");
+    (void)strcat(buf, ipw->imp.server_name);
+#define MAKEATOM(s)	XInternAtom(dpy, s, False)
+    ipw->imp.server_atom = MAKEATOM(buf);
+    ipw->imp.ctext_atom = MAKEATOM("COMPOUND_TEXT");
+    ipw->imp.locales_atom = MAKEATOM("LOCALES");
+    ipw->imp.transport_atom = MAKEATOM("TRANSPORT");
+    ipw->imp.ki2comm_atom = MAKEATOM("_KINPUT2_COMM");
+    ipw->imp.xim_xconnect = MAKEATOM("_XIM_XCONNECT");
+    ipw->imp.xim_protocol = MAKEATOM("_XIM_PROTOCOL");
+    ipw->imp.xim_moredata = MAKEATOM("_XIM_MOREDATA");
+#undef MAKEATOM
+}
+
+/*- setProperty: set XIM_SERVERS property -*/
+static void
+setProperty(ipw)
+IMProtocolWidget ipw;
+{
+    Display *dpy = XtDisplay((Widget)ipw);
+    Atom xim_servers = XInternAtom(dpy, "XIM_SERVERS", False);
+    Atom server_atom = ipw->imp.server_atom;
+    Window root0 = RootWindow(dpy, 0);
+    int op_mode = PropModePrepend;
+    int no_registration = 0;
+    Atom type;
+    int format;
+    unsigned long nitems;
+    unsigned long bytes_after;
+    unsigned char *value;
+    unsigned long data;
+
+    TRACE(("IMProtocolWidget:setProperty()\n"));
+
+    /*
+     * For atomic operation, grab the server.
+     */
+#ifndef DEBUG
+    XGrabServer(dpy);
+#endif
+
+    /*
+     * First, check the XIM_SERVERS property's existance.
+     * If it exists, examine the contents.
+     */
+    if (XGetWindowProperty(dpy, root0, xim_servers, 0L, 1024L, False,
+			   AnyPropertyType, &type, &format, &nitems,
+			   &bytes_after, &value) == Success) {
+	if (type != XA_ATOM || format != 32) {
+	    /*
+	     * The contents of the property is invalid.
+	     */
+	    DDPRINT(2, ("XIM_SERVERS is corrupted (type=%ld, format=%d)\n",
+		       type, format));
+	    op_mode = PropModeReplace;
+	} else {
+	    int i;
+	    unsigned long *atoms = (unsigned long *)value;
+
+	    for (i = 0; i < nitems; i++) {
+		if (atoms[i] == server_atom) {
+		    /*
+		     * Already registered.
+		     */
+		    TRACE(("server is already registered in XIM_SERVERS\n"));
+		    no_registration = 1;
+		    break;
+		}
+	    }
+	}
+	if (value != NULL) XFree((char *)value);
+    }
+
+    if (!no_registration) {
+	TRACE(("changing XIM_SERVERS property\n"));
+	data = ipw->imp.server_atom;
+	XChangeProperty(dpy, root0, xim_servers, XA_ATOM, 32, op_mode,
+			(unsigned char *)&data, 1);
+    } else {
+	TRACE(("touching XIM_SERVERS property to generate PropertyNotify\n"));
+	XChangeProperty(dpy, root0, xim_servers, XA_ATOM, 32, PropModeAppend,
+			(unsigned char *)&data, 0);
+    }
+
+#ifndef DEBUG
+    XUngrabServer(dpy);
+#endif
+}
+
+/*- ownSelection: own conversion selection -*/
+static int
+ownSelection(ipw)
+IMProtocolWidget ipw;
+{
+    Display *dpy = XtDisplay((Widget)ipw);
+    Time time = XtLastTimestampProcessed(dpy);
+
+
+    TRACE(("IMProtocolWidget:ownSelection()\n"));
+
+    if (!XtOwnSelection((Widget)ipw, ipw->imp.server_atom, time,
+			convertSelection, loseSelection,
+			(XtSelectionDoneProc)NULL)) {
+	DPRINT(("cannot own selection"));
+	return -1;
+    }
+    DPRINT(("selection atom:%ld owner: %08lx (%ld)\n", ipw->imp.server_atom,
+	    XtWindow((Widget)ipw), XtWindow((Widget)ipw)));
+    return 0;
+}
+
+/*- convertSelection: convert selections -*/
+/* ARGSUSED */
+static Boolean
+convertSelection(w, selectionp, targetp, typep, valuep, lengthp, formatp)
+Widget w;
+Atom *selectionp;
+Atom *targetp;
+Atom *typep;
+XtPointer *valuep;
+unsigned long *lengthp;
+int *formatp;
+{
+    Display *dpy = XtDisplay(w);
+    IMProtocolWidget ipw = (IMProtocolWidget)w;
+
+    TRACE(("IMProtocolWidget:convertSelection()\n"));
+
+    if (*targetp == XInternAtom(dpy, "TARGETS", False)) {
+	Atom *targets;
+
+	TRACE(("target is \"TARGETS\"\n"));
+	targets = (Atom *)XtMalloc(sizeof(Atom) * 2);
+	targets[0] = ATOM_LOCALES(w);
+	targets[1] = ATOM_TRANSPORT(w);
+
+	*typep = XA_ATOM;
+	*valuep = (XtPointer)targets;
+	*lengthp = 2;
+	*formatp = 32;
+	return True;
+    } else if (*targetp == ATOM_LOCALES(w)) {
+	char buf[1024];
+
+	TRACE(("target is \"LOCALES\"\n"));
+	(void)strcpy(buf, "@locale=");
+	(void)strcat(buf, ipw->imp.locales);
+	TRACE(("\ttype: STRING, value: %s\n", buf));
+	/*
+	 * The protocol spec is unclear on the type of the
+	 * selection value.  Since R6 sample implementation
+	 * uses LOCALES, use it.
+	 */
+	*typep = *targetp;
+	/* *typep = XA_STRING; */
+	*valuep = (XtPointer)XtNewString(buf);
+	*lengthp = strlen(buf);
+	*formatp = 8;
+	return True;
+    } else if (*targetp == ATOM_TRANSPORT(w)) {
+	char buf[1024];
+	char hostname[256];
+
+	TRACE(("target is \"TRANSPORT\"\n"));
+
+	XmuGetHostname(hostname, 256);
+
+	(void)strcpy(buf, "@transport=");
+
+#ifdef IM_X_TRANSPORT
+	if (ipw->imp.use_x_transport) {
+	    (void)strcat(buf, "X/,");
+	}
+#endif /* IM_X_TRANSPORT */
+
+#ifdef IM_TCP_TRANSPORT
+	if (ipw->imp.use_tcp_transport) {
+	    char t_buf[1024];
+	    (void)sprintf(t_buf, "tcp/%s:%d,", hostname, ipw->imp.tcp_port);
+	    (void)strcat(buf, t_buf);
+	}
+#endif /* IM_TCP_TRANSPORT */
+
+#ifdef IM_UNIX_TRANSPORT
+	if (ipw->imp.use_unix_transport) {
+	    char u_buf[1024];
+	    (void)sprintf(u_buf, "local/%s:%s,", hostname, ipw->imp.unix_path);
+	    (void)strcat(buf, u_buf);
+	}
+#endif /* IM_UNIX_TRANSPORT */
+
+	/* delete trailing comma */
+	if (buf[strlen(buf) - 1] == ',') buf[strlen(buf) - 1] = '\0';
+	TRACE(("\ttype: STRING, value: %s\n", buf));
+
+	*typep = *targetp;	/* -- see the comment on LOCALES above */
+	*valuep = (XtPointer)XtNewString(buf);
+	*lengthp = strlen(buf);
+	*formatp = 8;
+	return True;
+    } else {
+	DDPRINT(2, ("unknown target atom (%ld)\n", *targetp));
+	return False;
+    }
+}
+
+/*- loseSelection: disable IM protocol handling -*/
+/* ARGSUSED */
+static void
+loseSelection(w, selectionp)
+Widget w;
+Atom *selectionp;
+{
+    IMProtocolWidget ipw = (IMProtocolWidget)w;
+
+    TRACE(("IMProtocolWidget:loseSelection()\n"));
+
+    /*
+     * Someone takes over the selection.  That means
+     * another kinput2 process has been started.
+     * Let the newly process handle new clients, but
+     * as long as existing clients are remained, we have
+     * to maintain them.
+     */
+
+    if (ipw->imp.connection_list == NULL) {
+	/*
+	 * There are no clients.  It is OK to destroy protocol handler.
+	 */
+	XtDestroyWidget(w);
+	return;
+    }
+
+    ipw->imp.no_more_connections = True;
+
+    /*
+     * Close down TCP/Unix service sockets.
+     */
+    if (ipw->imp.tcp_sock >= 0) {
+	TRACE(("\tclose tcp socket\n"));
+	XtRemoveInput(ipw->imp.tcp_id);
+	(void)close(ipw->imp.tcp_sock);
+	ipw->imp.tcp_sock = -1;
+    }
+    if (ipw->imp.unix_sock >= 0) {
+	TRACE(("\tclose unix socket\n"));
+	XtRemoveInput(ipw->imp.unix_id);
+	(void)close(ipw->imp.unix_sock);
+	ipw->imp.unix_sock = -1;
+    }
+}
+
+
+/*
+ *+ Connection acceptance
+ */
+
+#ifdef IM_TCP_TRANSPORT
+/*- acceptTCPService: establish connection via TCP transport -*/
+/* ARGSUSED */
+static void
+acceptTCPService(client_data, sourcep, idp)
+XtPointer client_data;
+int *sourcep;
+XtInputId *idp;
+{
+    IMProtocolWidget ipw = (IMProtocolWidget)client_data;
+    IMConnection *conn;
+
+    TRACE(("IMProtocolWidget:acceptTCPService()\n"));
+
+    /*
+     * Accept connection request.
+     */
+    conn = IMTCPConnection((Widget)ipw, *sourcep);
+
+    /*
+     * Set dispatcher.
+     */
+    if (conn != NULL) IMSetInitialDispatcher(conn);
+
+    /*
+     * Enter to the connections list.
+     */
+    if (conn != NULL) IMRegisterConnection(conn);
+}
+#endif /* IM_TCP_TRANSPORT */
+
+#ifdef IM_UNIX_TRANSPORT
+/*- acceptUnixService: establish connection via UNIX domain transport -*/
+/* ARGSUSED */
+static void
+acceptUnixService(client_data, sourcep, idp)
+XtPointer client_data;
+int *sourcep;
+XtInputId *idp;
+{
+    IMProtocolWidget ipw = (IMProtocolWidget)client_data;
+    IMConnection *conn;
+
+    TRACE(("IMProtocolWidget:acceptUnixService()\n"));
+
+    /*
+     * Accept connection request.
+     */
+    conn = IMUnixConnection((Widget)ipw, *sourcep);
+
+    /*
+     * Set dispatcher.
+     */
+    if (conn != NULL) IMSetInitialDispatcher(conn);
+
+    /*
+     * Enter to the connections list.
+     */
+    if (conn != NULL) IMRegisterConnection(conn);
+}
+#endif /* IM_UNIX_TRANSPORT */
+
+#ifdef IM_X_TRANSPORT
+/*- acceptXService: establish connection via X transport -*/
+/* ARGSUSED */
+static void
+acceptXService(w, client_data, event, continuep)
+Widget w;
+XtPointer client_data;
+XEvent *event;
+Boolean *continuep;
+{
+    IMConnection *conn;
+
+    TRACE(("IMProtocolWidget:acceptXService()\n"));
+
+    /*
+     * Check if the event is really a connection request.
+     */
+    if (event->type != ClientMessage) return;
+    conn = IMXConnection(w, event);
+
+    /*
+     * Set dispatcher.
+     */
+    if (conn != NULL) IMSetInitialDispatcher(conn);
+
+    /*
+     * Enter to the connections list.
+     */
+    if (conn != NULL) IMRegisterConnection(conn);
+}
+#endif /* IM_X_TRANSPORT */
+
+
+/*
+ *+ utility functions
+ */
+
+/*- 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);
+}
+
+/*- compactList: remove unnecessary spaces in a comma-separated list -*/
+static char *
+compactList(s)
+char *s;
+{
+    char *src, *dst;
+    int c;
+
+    src = dst = s;
+    for (;;) {
+	/* skip leading space */
+	while (isspace(*src)) src++;
+
+	if (*src == '\0') {
+	    *dst = '\0';
+	    return s;
+	}
+
+	/* copy string until comma or NUL appears */
+	while ((c = *dst++ = *src++) != ',') {
+	    if (c == '\0') return s;
+	}
+    }
+}
+
+/*- setTransport: determine which transport to be used -*/
+static void
+setTransport(w)
+Widget w;
+{
+    IMProtocolWidget ipw = (IMProtocolWidget)w;
+    char *p;
+
+    TRACE(("IMProtocolWidget:setTransport(%s)\n", ipw->imp.transport_list));
+
+    ipw->imp.use_tcp_transport = False;
+    ipw->imp.use_unix_transport = False;
+    ipw->imp.use_x_transport = False;
+
+    p = ipw->imp.transport_list;
+    while (*p != '\0') {
+	char lower[256];
+	char *q;
+
+	while (isspace(*p)) p++;
+	if (*p == '\0') break;
+
+	q = lower;
+	while (*p != '\0' && *p != ',' && !isspace(*p)) {
+	    if (isupper(*p)) {
+		*q++ = tolower(*p);
+	    } else {
+		*q++ = *p;
+	    }
+	    p++;
+	}
+	*q = '\0';
+	while (isspace(*p)) p++;
+	if (*p == ',') p++;
+
+	if (!strcmp(lower, "tcp")) {
+	    TRACE(("\tTCP transport\n"));
+	    ipw->imp.use_tcp_transport = True;
+	} else if (!strcmp(lower, "unix")) {
+	    TRACE(("\tUNIX domain transport\n"));
+	    ipw->imp.use_unix_transport = True;
+	} else if (!strcmp(lower, "x")) {
+	    TRACE(("\tX transport\n"));
+	    ipw->imp.use_x_transport = True;
+	}
+    }
+}
+
+/*- makeConverter: create converter record -*/
+static int
+makeConverter(w)
+Widget w;
+{
+    IMProtocolWidget ipw = (IMProtocolWidget)w;
+    char *locales[100];
+    int num_locales;
+    int size;
+    char *p;
+
+    TRACE(("IMProtocolWidget:makeConverter()\n"));
+
+    ipw->imp.converter.input_object_class = ipw->imp.input_object_class;
+    ipw->imp.converter.display_object_class = ipw->imp.display_object_class;
+
+    p = ipw->imp.locales;
+    num_locales = 0;
+    do {
+	char buf[256];
+	char *q = buf;
+
+	while (isspace(*p)) p++;
+	if (*p == '\0') break;
+
+	while (*p != '\0' && *p != ',' && !isspace(*p)) *q++ = *p++;
+	*q = '\0';
+	TRACE(("\tsupported locale: %s\n", buf));
+	locales[num_locales++] = XtNewString(buf);
+	while (isspace(*p)) p++;
+	if (*p == ',') p++;
+    } while (*p != '\0' && num_locales < 100);
+    TRACE(("\tnumber of supported locales: %d\n", num_locales));
+
+    if (num_locales == 0) return -1;
+    ipw->imp.converter.num_locales = num_locales;
+
+    size = sizeof(char *) * num_locales;
+    ipw->imp.converter.supported_locales = (char **)XtMalloc(size);
+    bcopy((char *)locales, (char *)ipw->imp.converter.supported_locales, size);
+    return 0;
+}
+
+/*- getTriggerKeys: parse conversion trigger key specification -*/
+static void
+getTriggerKeys(w)
+Widget w;
+{
+    IMProtocolWidget ipw = (IMProtocolWidget)w;
+    char *key_str;
+    IMTriggerKey keys[100];
+    int num_keys;
+    int c, n;
+    ICTriggerKey *ckeys, *ekeys;
+
+    TRACE(("IMProtocolWidget:getTriggerKeys()\n"));
+
+    key_str = ipw->imp.conversion_start_keys;
+    num_keys = 0;
+    TRACE(("\tstart keys: %s\n", key_str));
+
+    if (key_str != NULL) {
+	do {
+	    char buf[256];
+	    char *p = buf;
+	    KeySym keysym;
+	    long mods, chk_mods;
+
+	    while ((c = *key_str++) != '\0' && c != '\n') {
+		*p++ = c;
+	    }
+	    *p = '\0';
+	    if (ParseKeyEvent(buf, &keysym, &mods, &chk_mods)) {
+		TRACE(("\tkeysym: %08lx, modifiers: %04lx, check: %04lx\n",
+		       keysym, mods, chk_mods));
+		keys[num_keys].keysym = keysym;
+		keys[num_keys].modifiers = mods;
+		keys[num_keys].check_modifiers = chk_mods;
+		num_keys++;
+	    }
+	} while  (c != '\0' && num_keys < 100);
+    }
+
+    n = ICGetTriggerKeysOfInputObjectClass(ipw->imp.input_object_class,
+					   &ckeys);
+    for (ekeys = ckeys + n ;
+	 ckeys < ekeys && num_keys < (sizeof(keys) / sizeof(IMTriggerKey)) ;
+	 ckeys++) {
+      keys[num_keys].keysym = ckeys->keysym;
+      keys[num_keys].modifiers = ckeys->modifiers;
+      keys[num_keys].check_modifiers = ckeys->modifiermask;
+      num_keys++;
+    }
+
+    TRACE(("\tnumber of trigger keys: %d\n", num_keys));
+    ipw->imp.num_trigger_keys = num_keys;
+
+    if (num_keys > 0) {
+	int size;
+
+	size = sizeof(IMTriggerKey) * num_keys;
+	ipw->imp.trigger_keys = (IMTriggerKey *)XtMalloc(size);
+	bcopy((char *)keys, (char *)ipw->imp.trigger_keys, size);
+    } else {
+	ipw->imp.trigger_keys = NULL;
+    }
+}
+
+/*- ioeCallback: callback procedure for X I/O error -*/
+static void
+ioeCallback(cldata)
+XPointer cldata;
+{
+    IMProtocolWidget ipw = (IMProtocolWidget)cldata;
+
+    if (ipw->imp.unix_sock >= 0 && ipw->imp.unix_path != NULL) {
+	(void)unlink(ipw->imp.unix_path);
+    }
+}