diff lib/imlib/imattr.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
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/imlib/imattr.c	Mon Mar 08 04:44:30 2010 +0900
@@ -0,0 +1,2711 @@
+#ifndef lint
+static char *rcsid = "$Id: imattr.c,v 1.18 2002/01/10 15:04:05 ishisone Exp $";
+#endif
+/*
+ * Copyright (c) 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
+ */
+
+#include "im.h"
+
+#ifndef XNSeparatorofNestedList
+#define XNSeparatorofNestedList "separatorofNesttedList"
+#endif
+#ifndef XNPreeditState
+#define XNPreeditState		"preeditState"
+#endif
+#ifndef XIMPreeditEnable
+#define XIMPreeditEnable	1L
+#define XIMPreeditDisable	2L
+#endif
+#ifndef XNResetState
+#define XNResetState		"resetState"
+#endif
+#ifndef XIMInitialState
+#define XIMInitialState		1L
+#define XIMPreserveState	2L
+#endif
+
+#define PAD4(n)	((((n) + 3) / 4) * 4)
+
+/*
+ * List of supported input styles.
+ */
+
+typedef struct {
+    XIMStyle xim_style;		/* X11R5 spec. */
+    int conversion_style;	/* kinput2 spec. */
+} InputStyle;
+
+static InputStyle styles[] = {
+    { XIMPreeditPosition|XIMStatusArea, IMSTYLE_OVER_THE_SPOT },
+    { XIMPreeditPosition|XIMStatusNothing, IMSTYLE_OVER_THE_SPOT },
+    { XIMPreeditArea|XIMStatusArea, IMSTYLE_OFF_THE_SPOT },
+    { XIMPreeditCallbacks|XIMStatusCallbacks, IMSTYLE_ON_THE_SPOT },
+    { XIMPreeditCallbacks|XIMStatusNothing, IMSTYLE_ON_THE_SPOT },
+    { XIMPreeditNothing|XIMStatusNothing, IMSTYLE_SEPARATE },
+    { 0 },
+};
+
+#define NEST_NONE	0
+#define NEST_PREEDIT	1
+#define NEST_STATUS	2
+
+#define CHECK_ICATTR_SIZE(validsize, code) \
+	if (len != validsize) { badSizeError(icp, code); return -1; }
+
+#undef OP_C
+#undef OP_S
+#undef OP_G
+
+#define OP_C	1	/* Create */
+#define OP_S	2	/* SetValues */
+#define OP_G	4	/* GetValues */
+
+typedef struct {
+    char *name;		/* attribute name */
+    int type;		/* type of attribute value */
+    int valid_ops;	/* valid operations for this attribute */
+    int (*set_proc) _Pt_((IMIM *, char *, int));
+    int (*get_proc) _Pt_((IMIM *, unsigned int, int));
+} IMAttribute;
+
+typedef struct {
+    char *name;		/* attribute name */
+    int type;		/* type of attribute value */
+    int valid_ops;	/* valid operations for this attribute */
+    int (*set_proc) _Pt_((IMIC *, char *, int, int, int, int));
+    int (*get_proc) _Pt_((IMIC *, unsigned int, int, int, char *, int));
+} ICAttribute;
+
+
+/*
+ * IM attributes
+ */
+
+static int getQueryInputStyle _Pt_((IMIM *imp, unsigned int id, int offset));
+
+static IMAttribute imAttributes[] = {
+    { XNQueryInputStyle, TYPE_XIM_STYLES, OP_G,
+	  NULL, getQueryInputStyle },
+};
+
+static int numImAttributes = XtNumber(imAttributes);
+
+
+/*
+ * IC attributes
+ */
+
+static int setInputStyle _Pt_((IMIC *, char *, int, int, int, int));
+static int setClientWindow _Pt_((IMIC *, char *, int, int, int, int));
+static int setFocusWindow _Pt_((IMIC *, char *, int, int, int, int));
+static int setPreeditAttributes _Pt_((IMIC *, char *, int, int, int, int));
+static int setStatusAttributes _Pt_((IMIC *, char *, int, int, int, int));
+static int setArea _Pt_((IMIC *, char *, int, int, int, int));
+static int setAreaNeeded _Pt_((IMIC *, char *, int, int, int, int));
+static int setForeground _Pt_((IMIC *, char *, int, int, int, int));
+static int setBackground _Pt_((IMIC *, char *, int, int, int, int));
+static int setColormap _Pt_((IMIC *, char *, int, int, int, int));
+static int setBgPixmap _Pt_((IMIC *, char *, int, int, int, int));
+static int setLineSpace _Pt_((IMIC *, char *, int, int, int, int));
+static int setCursor _Pt_((IMIC *, char *, int, int, int, int));
+static int setSpotLocation _Pt_((IMIC *, char *, int, int, int, int));
+static int setStdColormap _Pt_((IMIC *, char *, int, int, int, int));
+static int setFontSet _Pt_((IMIC *, char *, int, int, int, int));
+static int setPreeditState _Pt_((IMIC *, char *, int, int, int, int));
+static int setResetState _Pt_((IMIC *, char *, int, int, int, int));
+
+static int getPreeditAttributes _Pt_((IMIC *, unsigned int, int, int, char *, int));
+static int getStatusAttributes _Pt_((IMIC *, unsigned int, int, int, char *, int));
+static int getInputStyle _Pt_((IMIC *, unsigned int, int, int, char *, int));
+static int getClientWindow _Pt_((IMIC *, unsigned int, int, int, char *, int));
+static int getFocusWindow _Pt_((IMIC *, unsigned int, int, int, char *, int));
+static int getFilterEvents _Pt_((IMIC *, unsigned int, int, int, char *, int));
+static int getArea _Pt_((IMIC *, unsigned int, int, int, char *, int));
+static int getAreaNeeded _Pt_((IMIC *, unsigned int, int, int, char *, int));
+static int getSpotLocation _Pt_((IMIC *, unsigned int, int, int, char *, int));
+static int getColormap _Pt_((IMIC *, unsigned int, int, int, char *, int));
+static int getStdColormap _Pt_((IMIC *, unsigned int, int, int, char *, int));
+static int getForeground _Pt_((IMIC *, unsigned int, int, int, char *, int));
+static int getBackground _Pt_((IMIC *, unsigned int, int, int, char *, int));
+static int getBgPixmap _Pt_((IMIC *, unsigned int, int, int, char *, int));
+static int getFontSet _Pt_((IMIC *, unsigned int, int, int, char *, int));
+static int getLineSpace _Pt_((IMIC *, unsigned int, int, int, char *, int));
+static int getCursor _Pt_((IMIC *, unsigned int, int, int, char *, int));
+static int getPreeditState _Pt_((IMIC *, unsigned int, int, int, char *, int));
+static int getResetState _Pt_((IMIC *, unsigned int, int, int, char *, int));
+
+static ICAttribute icAttributes[] = {
+    { XNInputStyle, TYPE_CARD32, OP_C|OP_G,
+	  setInputStyle, getInputStyle },
+    { XNClientWindow, TYPE_WINDOW, OP_C|OP_S|OP_G,
+	  setClientWindow, getClientWindow },
+    { XNFocusWindow, TYPE_WINDOW, OP_C|OP_S|OP_G,
+	  setFocusWindow, getFocusWindow },
+    { XNFilterEvents, TYPE_CARD32, OP_G,
+	  NULL, getFilterEvents },
+    { XNPreeditAttributes, TYPE_NESTED_LIST, OP_C|OP_S|OP_G,
+	  setPreeditAttributes, getPreeditAttributes },
+    { XNStatusAttributes, TYPE_NESTED_LIST, OP_C|OP_S|OP_G,
+	  setStatusAttributes, getStatusAttributes },
+    { XNArea, TYPE_XRECTANGLE, OP_C|OP_S|OP_G,
+	  setArea, getArea },
+    { XNAreaNeeded, TYPE_XRECTANGLE, OP_C|OP_S|OP_G,
+	  setAreaNeeded, getAreaNeeded },
+    { XNSpotLocation, TYPE_XPOINT, OP_C|OP_S|OP_G,
+	  setSpotLocation, getSpotLocation },
+    { XNColormap, TYPE_CARD32, OP_C|OP_S|OP_G,
+	  setColormap, getColormap },
+    { XNStdColormap, TYPE_CARD32, OP_C|OP_S|OP_G,
+	  setStdColormap, getStdColormap },
+    { XNForeground, TYPE_CARD32, OP_C|OP_S|OP_G,
+	  setForeground, getForeground },
+    { XNBackground, TYPE_CARD32, OP_C|OP_S|OP_G,
+	  setBackground, getBackground },
+    { XNBackgroundPixmap, TYPE_CARD32, OP_C|OP_S|OP_G,
+	  setBgPixmap, getBgPixmap },
+    { XNFontSet, TYPE_XFONTSET, OP_C|OP_S|OP_G,
+	  setFontSet, getFontSet },
+    { XNLineSpace, TYPE_CARD16, OP_C|OP_S|OP_G,	  /* should be TYPE_INT16 */
+	  setLineSpace, getLineSpace },
+    { XNCursor, TYPE_CARD32, OP_C|OP_S|OP_G,
+	  setCursor, getCursor },
+    { XNSeparatorofNestedList, TYPE_SEPARATOR, OP_G,
+	  NULL, NULL },
+    { XNPreeditState, TYPE_CARD32, OP_C|OP_S|OP_G,
+      setPreeditState, getPreeditState },
+    { XNResetState, TYPE_CARD32, OP_C|OP_S|OP_G,
+      setResetState, getResetState },
+};
+
+static int numIcAttributes = XtNumber(icAttributes);
+
+
+static unsigned int getC16 _Pt_((char *data, int order));
+static int getI16 _Pt_((char *data, int order));
+static unsigned long getC32 _Pt_((char *data, int order));
+static int validateClientWindow _Pt_((IMIC *icp));
+static int validateFocusWindow _Pt_((IMIC *icp));
+static void badSizeError _Pt_((IMIC *icp, int code));
+static void unnestedError _Pt_((IMIC *icp));
+static IMPSAttributes *getPSPtr _Pt_((IMIC *icp, int type));
+static int getIMValues _Pt_((IMIM *imp, char *data, int len, int offset));
+static int getICValues _Pt_((IMIC *icp, char *data, int len, int nest,
+			     int offset, int *sepp));
+static int setICValues _Pt_((IMIC *icp, char *data, int len,
+			     int major, int op));
+static int getPSAttributes _Pt_((IMIC *icp, unsigned int id, int nest,
+				 int offset, char *data, int len));
+static void changeFonts _Pt_((IMIC *icp, int preedit));
+static void fillCommonDefault _Pt_((IMIC *icp, unsigned long mask));
+static int getNaturalLineSpace _Pt_((IMIC *icp, int preedit));
+static void fillPSDefault _Pt_((IMIC *icp, int type, unsigned long mask));
+static int validateCommonAttr _Pt_((IMIC *icp, int checkonly));
+static int validatePSAttr _Pt_((IMIC *icp, int type, int checkonly));
+static void changeConversionAttributes _Pt_((IMIC *icp));
+static void computeAreaNeeded _Pt_((IMIC *icp));
+static void computeAreaForQuery _Pt_((IMIC *icp));
+
+
+/*
+ * Functions reading out numbers from byte buffer
+ */
+
+static unsigned int
+getC16(data, order)
+char *data;
+int order;
+{
+    unsigned char *p = (unsigned char *)data;
+    unsigned int x;
+
+    x = (order == ORDER_BIG) ? ((p[0] << 8) | p[1]) : (p[0] | p[1] << 8);
+    return x;
+}
+
+static int
+getI16(data, order)
+char *data;
+int order;
+{
+    unsigned char *p = (unsigned char *)data;
+    long l;
+
+    l = (order == ORDER_BIG) ? ((p[0] << 8) | p[1]) : (p[0] | p[1] << 8);
+    return (l < 32768) ? (int)l : (int)(l - 65536L);
+}
+
+static unsigned long
+getC32(data, order)
+char *data;
+int order;
+{
+    unsigned char *p = (unsigned char *)data;
+    unsigned long x;
+
+    if (order == ORDER_BIG) {
+	x = (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
+    } else {
+	x = p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
+    }
+    return x;
+}
+
+
+/*
+ * Functions that check the validity of resources.
+ */
+
+static int
+validateClientWindow(icp)
+IMIC *icp;
+{
+    return IMValidateWindow(XtDisplay(icp->im->connection->proto_widget),
+			    icp->common_attr.client, &icp->client_profile);
+}
+
+static int
+validateFocusWindow(icp)
+IMIC *icp;
+{
+    IMCommonAttributes *ap = &icp->common_attr;
+
+    /*
+     * This function assumes that the client window has already
+     * been validated.
+     */
+    if ((ap->set_mask & ATTR_MASK_CLIENT) && ap->focus == ap->client) {
+	icp->focus_profile = icp->client_profile;
+	return 1;
+    } else {
+	return IMValidateWindow(XtDisplay(icp->im->connection->proto_widget),
+				icp->common_attr.focus, &icp->focus_profile);
+    }
+}
+
+
+/*
+ * Functions submit errors
+ */
+
+static void
+badSizeError(icp, code)
+IMIC *icp;
+int code;
+{
+    DPRINT(("bad size error for IC #%d\n", icp->id));
+    IMSendError(icp->im->connection, code, icp->im->id, icp->id,
+		"invalid size of attribute value");
+}
+
+static void
+unnestedError(icp)
+IMIC *icp;
+{
+    DPRINT(("unnested error for IC #%d\n", icp->id));
+    IMSendError(icp->im->connection, IMBadSomething, icp->im->id, icp->id,
+		"either preedit or status specification required");
+}
+
+
+/*
+ * Functions getting IM attributes
+ */
+
+static IMPSAttributes *
+getPSPtr(icp, type)
+IMIC *icp;
+int type;
+{
+    switch (type) {
+    case NEST_PREEDIT: return &icp->preedit_attr;
+    case NEST_STATUS: return &icp->status_attr;
+    default: return NULL;
+    }
+}
+
+static int
+getIMValues(imp, data, len, offset)
+IMIM *imp;
+char *data;
+int len;
+int offset;		/* request offset */
+{
+    unsigned int id;		/* attribute ID */
+    IMAttribute *attrp;
+    IMConnection *conn = imp->connection;
+    int byte_order = conn->byte_order;
+
+    while (len >= 2) {
+	id = getC16(data, byte_order);
+	data += 2;
+	len -= 2;
+
+	if (id > numImAttributes) {
+	    /* invalid attribute ID */
+	    IMCancelRequest(conn, offset);
+	    IMSendError(conn, IMBadSomething, imp->id, 0,
+			"invalid IM attribute ID");
+	    return -1;
+	}
+
+	attrp = &imAttributes[id];
+	if (!(attrp->valid_ops & OP_G)) {
+	    IMCancelRequest(conn, offset);
+	    IMSendError(conn, IMBadSomething, imp->id, 0,
+			"invalid operation (IMGetValues) for this attribute");
+	    return -1;
+	}
+
+	if ((*attrp->get_proc)(imp, id, offset) < 0) return -1;
+    }
+    return 0;
+}
+
+/* ARGSUSED */
+static int
+getQueryInputStyle(imp, id, offset)
+IMIM *imp;
+unsigned int id;
+int offset;
+{
+    IMConnection *conn = imp->connection;
+    unsigned int num_styles, num_bytes;
+    InputStyle *stp;
+
+    TRACE(("imlib:getQueryInputStyle()\n"));
+
+    for (num_styles = 0, stp = styles; stp->xim_style != 0; stp++) {
+	num_styles++;
+    }
+    num_bytes = num_styles * 4 + 4;
+    IMPutC16(conn, id);
+    IMPutC16(conn, num_bytes);
+    IMPutC16(conn, num_styles);
+    IMPutC16(conn, 0);
+    for (stp = styles; stp->xim_style != 0; stp++) {
+	IMPutC32(conn, stp->xim_style);
+    }
+    return 0;
+}
+
+
+/*
+ * Functions setting IC attributes
+ */
+
+static int
+setICValues(icp, data, len, nest, op)
+IMIC *icp;
+char *data;
+int len;
+int nest;
+int op;
+{
+    IMConnection *conn = icp->im->connection;
+    unsigned int imid = icp->im->id;
+    unsigned int icid = icp->id;
+    unsigned int id;
+    unsigned int value_len;
+    unsigned int attr_len;
+    char *value;
+    ICAttribute *attrp;
+    int byte_order = icp->im->connection->byte_order;
+
+    TRACE(("imlib:setICValues()\n"));
+
+    while (len > 0) {
+	if (len < 4) {
+	    DPRINT(("attribute data length < 4\n"));
+	    IMSendError(conn, IMBadSomething, imid, icid, "Bad attribute data");
+	    return -1;
+	}
+	id = getC16(data, byte_order);
+	value_len = getC16(data + 2, byte_order);
+	attr_len = PAD4(4 + value_len);
+
+	if (attr_len > len) {
+	    DPRINT(("attribute data length > request length\n"));
+	    IMSendError(conn, IMBadSomething, imid, icid,
+			"Bad attribute length");
+	    return -1;
+	}
+	value = data + 4;
+
+	if (id > numIcAttributes) {
+	    DPRINT(("invalid IC attribute ID %d\n", id));
+	    IMSendError(conn, IMBadSomething, imid, icid,
+			"invalid IC attribute ID");
+	    return -1;
+	}
+	attrp = &icAttributes[id];
+	if (!(attrp->valid_ops & op)) {
+	    DPRINT(("invalid operation (%s) for IC attr %d\n",
+		    op == OP_C ? "create" : "set", id));
+	    IMSendError(conn, IMBadSomething, imid, icid,
+			"invalid operation for this attribute");
+	    return -1;
+	}
+
+	/*
+	 * Call attribute set procedure.
+	 */
+	if ((*attrp->set_proc)(icp, value, (int)value_len, byte_order, nest, op) < 0) {
+	    /*
+	     * Error has occured.  The set procedure has already sent
+	     * appropriate error message, so just return here.
+	     */
+	    return -1;
+	}
+
+	data += attr_len;
+	len -= attr_len;
+    }
+    return 0;
+}
+
+/* ARGSUSED */
+static int
+setInputStyle(icp, value, len, order, nest, op)
+IMIC *icp;
+char *value;
+int len;
+int order;
+int nest;
+int op;
+{
+    TRACE(("imlib:setInputStyle()\n"));
+
+    CHECK_ICATTR_SIZE(4, IMBadStyle);
+
+    /*
+     * InputStyle must be set with CreateIC.
+     */
+    if (op != OP_C) {
+	DPRINT(("trying to change input style through SetICValues\n"));
+	IMSendError(icp->im->connection, IMBadStyle,
+		    icp->im->id, icp->id,
+		    "InputStyle cannot be changed by SetICValues");
+	return -1;
+    }
+
+    icp->common_attr.input_style = (XIMStyle)getC32(value, order);
+    icp->common_attr.set_mask |= ATTR_MASK_INPUT_STYLE;
+    icp->common_attr.change_mask |= ATTR_MASK_INPUT_STYLE;
+    TRACE(("\tinput style: %ld\n", icp->common_attr.input_style));
+    return 0;
+}
+
+/* ARGSUSED */
+static int
+setClientWindow(icp, value, len, order, nest, op)
+IMIC *icp;
+char *value;
+int len;
+int order;
+int nest;
+int op;
+{
+    TRACE(("imlib:setClientWindow()\n"));
+
+    CHECK_ICATTR_SIZE(4, IMBadClientWindow);
+
+    /*
+     * ClientWindow cannot be changed.
+     */
+    if (icp->common_attr.set_mask & ATTR_MASK_CLIENT) {
+	DPRINT(("client window already specified\n"));
+	IMSendError(icp->im->connection, IMBadClientWindow,
+		    icp->im->id, icp->id, "ClientWindow already set");
+	return -1;
+    }
+
+    icp->common_attr.client = (Window)getC32(value, order);
+    TRACE(("\tclient window: %08lx\n", icp->common_attr.client));
+
+    icp->common_attr.set_mask |= ATTR_MASK_CLIENT;
+    icp->common_attr.change_mask |= ATTR_MASK_CLIENT;
+
+    return 0;
+}
+
+/* ARGSUSED */
+static int
+setFocusWindow(icp, value, len, order, nest, op)
+IMIC *icp;
+char *value;
+int len;
+int order;
+int nest;
+int op;
+{
+    Window focus;
+
+    TRACE(("imlib:setFocusWindow()\n"));
+
+    CHECK_ICATTR_SIZE(4, IMBadFocusWindow);
+
+    focus = (Window)getC32(value, order);
+    TRACE(("\tfocus window: %08lx\n", focus));
+
+    if (!(icp->common_attr.set_mask & ATTR_MASK_FOCUS) ||
+	focus != icp->common_attr.focus) {
+	icp->common_attr.change_mask |= ATTR_MASK_FOCUS;
+    }
+    icp->common_attr.focus = focus;
+    icp->common_attr.set_mask |= ATTR_MASK_FOCUS;
+    return 0;
+}
+
+/* ARGSUSED */
+static int
+setPreeditAttributes(icp, value, len, order, nest, op)
+IMIC *icp;
+char *value;
+int len;
+int order;
+int nest;
+int op;
+{
+    TRACE(("imlib:setPreeditAttributes()\n"));
+    return setICValues(icp, value, len, NEST_PREEDIT, op);
+}
+
+/* ARGSUSED */
+static int
+setStatusAttributes(icp, value, len, order, nest, op)
+IMIC *icp;
+char *value;
+int len;
+int order;
+int nest;
+int op;
+{
+    TRACE(("imlib:setStatusAttributes()\n"));
+    return setICValues(icp, value, len, NEST_STATUS, op);
+}
+
+/* ARGSUSED */
+static int
+setArea(icp, value, len, order, nest, op)
+IMIC *icp;
+char *value;
+int len;
+int order;
+int nest;
+int op;
+{
+    IMPSAttributes *ap;
+    XRectangle area;
+
+    TRACE(("imlib:setArea()\n"));
+
+    CHECK_ICATTR_SIZE(8, IMBadArea);
+
+    if ((ap = getPSPtr(icp, nest)) == NULL) {
+	unnestedError(icp);
+	return -1;
+    }
+
+    area.x = getI16(value, order);
+    area.y = getI16(value + 2, order);
+    area.width = getC16(value + 4, order);
+    area.height = getC16(value + 6, order);
+    TRACE(("\tarea: %d, %d, %d, %d\n",
+	   area.x, area.y, area.width, area.height));
+
+    if (!(ap->set_mask & ATTR_MASK_AREA) ||
+	area.x != ap->area.x ||
+	area.y != ap->area.y ||
+	area.width != ap->area.width ||
+	area.height != ap->area.height) {
+	ap->change_mask |= ATTR_MASK_AREA;
+    }
+
+    ap->area.x = area.x;
+    ap->area.y = area.y;
+    ap->area.width = area.width;
+    ap->area.height = area.height;
+    ap->set_mask |= ATTR_MASK_AREA;
+
+    return 0;
+}
+
+/* ARGSUSED */
+static int
+setAreaNeeded(icp, value, len, order, nest, op)
+IMIC *icp;
+char *value;
+int len;
+int order;
+int nest;
+int op;
+{
+    IMPSAttributes *ap;
+    XRectangle area;
+
+    TRACE(("imlib:setAreaNeeded()\n"));
+
+    CHECK_ICATTR_SIZE(8, IMBadArea);
+
+    if ((ap = getPSPtr(icp, nest)) == NULL) {
+	unnestedError(icp);
+	return -1;
+    }
+
+    area.width = getC16(value + 4, order);
+    area.height = getC16(value + 6, order);
+    TRACE(("\tarea needed: %d, %d\n", area.width, area.height));
+
+    if (!(ap->set_mask & ATTR_MASK_AREA_NEEDED) ||
+	area.width != ap->area_needed.width ||
+	area.height != ap->area_needed.height) {
+	ap->change_mask |= ATTR_MASK_AREA_NEEDED;
+    }
+
+    ap->area_needed.width = area.width;
+    ap->area_needed.height = area.height;
+    ap->set_mask |= ATTR_MASK_AREA_NEEDED;
+
+    return 0;
+}
+
+/* ARGSUSED */
+static int
+setForeground(icp, value, len, order, nest, op)
+IMIC *icp;
+char *value;
+int len;
+int order;
+int nest;
+int op;
+{
+    IMPSAttributes *ap;
+    Pixel fore;
+
+    TRACE(("imlib:setForeground()\n"));
+
+    CHECK_ICATTR_SIZE(4, IMBadForeground);
+
+    if ((ap = getPSPtr(icp, nest)) == NULL) {
+	unnestedError(icp);
+	return -1;
+    }
+
+    fore = getC32(value, order);
+    TRACE(("\tforeground: %ld\n", fore));
+
+    if (!(ap->set_mask & ATTR_MASK_FOREGROUND) || fore != ap->foreground) {
+	ap->change_mask |= ATTR_MASK_FOREGROUND;
+    }
+    ap->foreground = fore;
+    ap->set_mask |= ATTR_MASK_FOREGROUND;
+    return 0;
+}
+
+/* ARGSUSED */
+static int
+setBackground(icp, value, len, order, nest, op)
+IMIC *icp;
+char *value;
+int len;
+int order;
+int nest;
+int op;
+{
+    IMPSAttributes *ap;
+    Pixel back;
+
+    TRACE(("imlib:setBackground()\n"));
+
+    CHECK_ICATTR_SIZE(4, IMBadBackground);
+
+    if ((ap = getPSPtr(icp, nest)) == NULL) {
+	unnestedError(icp);
+	return -1;
+    }
+
+    back = getC32(value, order);
+    TRACE(("\tbackground: %ld\n", back));
+
+    if (!(ap->set_mask & ATTR_MASK_BACKGROUND) || back != ap->background) {
+	ap->change_mask |= ATTR_MASK_BACKGROUND;
+    }
+    ap->background = back;
+    ap->set_mask |= ATTR_MASK_BACKGROUND;
+    return 0;
+}
+
+/* ARGSUSED */
+static int
+setColormap(icp, value, len, order, nest, op)
+IMIC *icp;
+char *value;
+int len;
+int order;
+int nest;
+int op;
+{
+    IMPSAttributes *ap;
+    Colormap cmap;
+
+    TRACE(("imlib:setColormap()\n"));
+
+    CHECK_ICATTR_SIZE(4, IMBadColormap);
+
+    if ((ap = getPSPtr(icp, nest)) == NULL) {
+	unnestedError(icp);
+	return -1;
+    }
+
+    cmap = getC32(value, order);
+    TRACE(("\tcolormap: %08lx\n", cmap));
+
+    if (!(ap->set_mask & ATTR_MASK_COLORMAP) || cmap != ap->colormap) {
+	ap->change_mask |= ATTR_MASK_COLORMAP;
+    }
+    ap->colormap = cmap;
+    ap->set_mask |= ATTR_MASK_COLORMAP;
+    return 0;
+}
+
+/* ARGSUSED */
+static int
+setBgPixmap(icp, value, len, order, nest, op)
+IMIC *icp;
+char *value;
+int len;
+int order;
+int nest;
+int op;
+{
+    IMPSAttributes *ap;
+    Pixmap pixmap;
+
+    TRACE(("imlib:setBgPixmap()\n"));
+
+    CHECK_ICATTR_SIZE(4, IMBadPixmap);
+
+    if ((ap = getPSPtr(icp, nest)) == NULL) {
+	unnestedError(icp);
+	return -1;
+    }
+
+    pixmap = getC32(value, order);
+    TRACE(("\tbackground pixmap: %08lx\n", pixmap));
+
+    if (!(ap->set_mask & ATTR_MASK_BG_PIXMAP) || pixmap != ap->bg_pixmap) {
+	ap->change_mask |= ATTR_MASK_BG_PIXMAP;
+    }
+
+    ap->bg_pixmap = pixmap;
+    ap->set_mask |= ATTR_MASK_BG_PIXMAP;
+
+    return 0;
+}
+
+/* ARGSUSED */
+static int
+setLineSpace(icp, value, len, order, nest, op)
+IMIC *icp;
+char *value;
+int len;
+int order;
+int nest;
+int op;
+{
+    IMPSAttributes *ap;
+    int line_space;
+
+    TRACE(("imlib:setLineSpace()\n"));
+
+    CHECK_ICATTR_SIZE(2, IMBadSomething);
+
+    if ((ap = getPSPtr(icp, nest)) == NULL) {
+	unnestedError(icp);
+	return -1;
+    }
+
+    line_space = getI16(value, order);	/* ??? linespacing is 'int' */
+    TRACE(("\tline space: %d\n", line_space));
+
+    if (!(ap->set_mask & ATTR_MASK_LINESPACE) ||
+	line_space != ap->line_space) {
+	ap->change_mask |= ATTR_MASK_LINESPACE;
+    }
+    ap->line_space = line_space;
+    ap->set_mask |= ATTR_MASK_LINESPACE;
+    return 0;
+}
+
+/* ARGSUSED */
+static int
+setCursor(icp, value, len, order, nest, op)
+IMIC *icp;
+char *value;
+int len;
+int order;
+int nest;
+int op;
+{
+    IMPSAttributes *ap;
+    Cursor cursor;
+
+    TRACE(("imlib:setCursor()\n"));
+
+    CHECK_ICATTR_SIZE(4, IMBadCursor);
+
+    if ((ap = getPSPtr(icp, nest)) == NULL) {
+	unnestedError(icp);
+	return -1;
+    }
+
+    cursor = getC32(value, order);
+    TRACE(("\tcursor: %08lx\n", cursor));
+
+    if (!(ap->set_mask & ATTR_MASK_CURSOR) || cursor != ap->cursor) {
+	ap->change_mask |= ATTR_MASK_CURSOR;
+    }
+    ap->cursor = cursor;
+    ap->set_mask |= ATTR_MASK_CURSOR;
+    return 0;
+}
+
+/* ARGSUSED */
+static int
+setSpotLocation(icp, value, len, order, nest, op)
+IMIC *icp;
+char *value;
+int len;
+int order;
+int nest;
+int op;
+{
+    IMPSAttributes *ap;
+    XPoint spot;
+
+    TRACE(("imlib:setSpotLocation()\n"));
+
+    CHECK_ICATTR_SIZE(4, IMBadSpotLocation);
+
+    if (nest == NEST_STATUS) {
+	DPRINT(("spot location specified in a status attribute list\n"));
+	IMSendError(icp->im->connection, IMBadSpotLocation,
+		    icp->im->id, icp->id,
+		    "spot location isn't a status attribute");
+	return -1;
+    }
+
+    ap = &icp->preedit_attr;
+
+    spot.x = getI16(value, order);
+    spot.y = getI16(value + 2, order);
+    TRACE(("\tspot location: %d, %d\n", spot.x, spot.y));
+
+    if (!(ap->set_mask & ATTR_MASK_SPOT_LOCATION) ||
+	spot.x != ap->spot_location.x || spot.y != ap->spot_location.y) {
+	ap->change_mask |= ATTR_MASK_SPOT_LOCATION;
+    }
+    ap->spot_location.x = spot.x;
+    ap->spot_location.y = spot.y;
+    ap->set_mask |= ATTR_MASK_SPOT_LOCATION;
+    return 0;
+}
+
+/* ARGSUSED */
+static int
+setStdColormap(icp, value, len, order, nest, op)
+IMIC *icp;
+char *value;
+int len;
+int order;
+int nest;
+int op;
+{
+    IMPSAttributes *ap;
+    Atom colormap_name;
+    XStandardColormap *stdcolormaps;
+    Widget w = icp->im->connection->proto_widget;
+    Display *dpy = XtDisplay(w);
+    int ncolormaps;
+    Window root;
+    XAEHandle h;
+    int status;
+
+    TRACE(("imlib:setStdColormap()\n"));
+
+    CHECK_ICATTR_SIZE(4, IMBadAtom);
+
+    if ((ap = getPSPtr(icp, nest)) == NULL) {
+	unnestedError(icp);
+	return -1;
+    }
+
+    colormap_name = getC32(value, order);
+    if (icp->common_attr.set_mask & ATTR_MASK_CLIENT) {
+	root = icp->client_profile.root;
+    } else if (icp->common_attr.set_mask & ATTR_MASK_FOCUS) {
+	root = icp->focus_profile.root;
+    } else {
+	/*
+	 * Client has not specified client window yet.
+	 * Reading standard colormap property should been deffered
+	 * until the window is set, but for now...
+	 */
+	DDPRINT(2, ("std colormap specified, leaving client window unspecified\n"));
+	root = RootWindowOfScreen(XtScreen(w));
+    }
+
+    h = XAESetIgnoreErrors(dpy);
+    status = XGetRGBColormaps(dpy, root,
+			      &stdcolormaps, &ncolormaps, colormap_name);
+    XAEUnset(h);
+    if (!status || ncolormaps < 0) {
+	DPRINT(("can't get standard colormap (%ld)\n", colormap_name));
+	IMSendError(icp->im->connection, IMBadName, icp->im->id, icp->id,
+		"invalid standard colormap name");
+	return -1;
+    }
+
+    if (!(ap->set_mask & ATTR_MASK_STD_COLORMAP) ||
+	colormap_name != ap->std_colormap) {
+	ap->change_mask |= ATTR_MASK_STD_COLORMAP;
+    }
+    ap->std_colormap = colormap_name;
+    ap->colormap = stdcolormaps[0].colormap;
+    TRACE(("\tstandard colormap: %ld (colormap=%08lx)\n",
+	   colormap_name, ap->colormap));
+
+    ap->set_mask |= ATTR_MASK_STD_COLORMAP | ATTR_MASK_COLORMAP;
+    XFree((char *)stdcolormaps);
+    return 0;
+}
+
+/* ARGSUSED */
+static int
+setFontSet(icp, value, len, order, nest, op)
+IMIC *icp;
+char *value;
+int len;
+int order;
+int nest;
+int op;
+{
+    IMPSAttributes *ap;
+    unsigned int name_list_len;
+    char *name_list;
+
+    TRACE(("imlib:setFontSet()\n"));
+
+    if (len < 2) {
+	badSizeError(icp, IMBadName);
+	return -1;
+    }
+    name_list_len = getC16(value, order);
+    if (2 + name_list_len > len) {
+	badSizeError(icp, IMBadName);
+	return -1;
+    }
+
+    if ((ap = getPSPtr(icp, nest)) == NULL) {
+	unnestedError(icp);
+	return -1;
+    }
+
+    name_list = XtMalloc(name_list_len + 1);
+    bcopy(value + 2, name_list, name_list_len);
+    name_list[name_list_len] = '\0';
+    TRACE(("\tfontset: %s\n", name_list));
+
+    if (ap->set_mask & ATTR_MASK_FONT_SET) {
+	if (!strcmp(name_list, ap->font_set)) {
+	    XtFree(name_list);
+	} else {
+	    ap->change_mask |= ATTR_MASK_FONT_SET;
+	    if (ap->font_set != IMDefaultFontSet(icp->im)) {
+		XtFree(ap->font_set);
+	    }
+	    ap->font_set = name_list;
+	}
+    } else {
+	ap->font_set = name_list;
+	ap->set_mask |= ATTR_MASK_FONT_SET;
+	ap->change_mask |= ATTR_MASK_FONT_SET;
+    }
+    return 0;
+}
+
+/* ARGSUSED */
+static int
+setPreeditState(icp, value, len, order, nest, op)
+IMIC *icp;
+char *value;
+int len;
+int order;
+int nest;
+int op;
+{
+    IMCommonAttributes *ap = &icp->common_attr;
+    unsigned long preedit_state;
+
+    TRACE(("imlib:setPreeditState()\n"));
+
+    CHECK_ICATTR_SIZE(4, IMBadSomething);
+
+    if (nest == NEST_STATUS) {
+	DPRINT(("preedit state specified in a status attribute list\n"));
+	IMSendError(icp->im->connection, IMBadSomething,
+		    icp->im->id, icp->id,
+		    "preedit state isn't a status attribute");
+	return -1;
+    }
+
+    preedit_state = getC32(value, order);
+
+    ap->set_mask |= ATTR_MASK_PREEDIT_STATE;
+    ap->change_mask |= ATTR_MASK_PREEDIT_STATE;
+    ap->preedit_state = preedit_state;
+    return 0;
+}
+
+/* ARGSUSED */
+static int
+setResetState(icp, value, len, order, nest, op)
+IMIC *icp;
+char *value;
+int len;
+int order;
+int nest;
+int op;
+{
+    IMCommonAttributes *ap = &icp->common_attr;
+    unsigned long reset_state;
+
+    TRACE(("imlib:setResetState()\n"));
+
+    CHECK_ICATTR_SIZE(4, IMBadSomething);
+
+    reset_state = getC32(value, order);
+
+    ap->set_mask |= ATTR_MASK_RESET_STATE;
+    ap->change_mask |= ATTR_MASK_RESET_STATE;
+    ap->reset_state = reset_state;
+    return 0;
+}
+
+
+/*
+ * Functions getting IC attributes
+ */
+
+static int
+getICValues(icp, data, len, nest, offset, sepp)
+IMIC *icp;
+char *data;
+int len;
+int nest;		/* NEST_NONE, NEST_PREEDIT or NEST_STATUS */
+int offset;		/* request offset */
+int *sepp;		/* Out: true if ended with a nested list separator */
+{
+    unsigned int id;		/* attribute ID */
+    ICAttribute *attrp;
+    IMConnection *conn = icp->im->connection;
+    int byte_order = conn->byte_order;
+    char *org_data = data;
+    int r;
+
+    TRACE(("imlib:getICValues()\n"));
+
+    while (len >= 2) {
+	id = getC16(data, byte_order);
+	data += 2;
+	len -= 2;
+
+	if (id > numIcAttributes) {
+	    /* invalid attribute ID */
+	    DPRINT(("invalid IC attribute ID (%d) specified\n", id));
+	    IMCancelRequest(conn, offset);
+	    IMSendError(conn, IMBadSomething, icp->im->id, icp->id,
+			"invalid IC attribute ID");
+	    return -1;
+	}
+
+	attrp = &icAttributes[id];
+	if (attrp->type == TYPE_SEPARATOR) {
+	    /* nested list separator */
+	    *sepp = 1;
+	    return data - org_data;
+	}
+
+	if (!(attrp->valid_ops & OP_G)) {
+	    DPRINT(("invalid operation (get) for IC attr %d\n", id));
+	    IMCancelRequest(conn, offset);
+	    IMSendError(conn, IMBadSomething, icp->im->id, icp->id,
+			"invalid operation (ICGetValues) for this attribute");
+	    return -1;
+	}
+
+	r = (*attrp->get_proc)(icp, id, nest, offset, data, len);
+	/*
+	 * The return value of get_proc is usually 0, indicating success.
+	 * If it is less than 0, there are some errors.
+	 * If it is greater than 0,
+	 */
+	if (r < 0) return -1;
+
+	data += r;		/* r is extra offset */
+	len -= r;
+    }
+    *sepp = 0;
+    return data - org_data;
+}
+
+/* ARGSUSED */
+static int
+getPSAttributes(icp, id, nest, offset, data, len)
+IMIC *icp;
+unsigned int id;
+int nest;
+int offset;
+char *data;
+int len;
+{
+    IMConnection *conn = icp->im->connection;
+    unsigned int length;
+    int length_offset;
+    int attr_offset;
+    int nested_separator;
+    int r;
+
+    IMPutC16(conn, id);
+
+    length_offset = IMWritePos(conn);
+    IMPutC16(conn, 0);		/* dummy -- overwritten afterwards */
+
+    attr_offset = IMWritePos(conn);
+
+    r = getICValues(icp, data, len, nest, offset, &nested_separator);
+    if (r < 0) return -1;
+    if (!nested_separator) {
+	/* there's no nested list separator */
+	DPRINT(("nested list doesn't end with separator\n"));
+	/*
+	 * X11R6 Xlib sends nested attribute list which has no
+	 * separator at its end.  In order to accommodate to it,
+	 * don't send error for that.
+	 */
+#ifdef notdef
+	IMCancelRequest(conn, offset);
+	IMSendError(conn, IMBadSomething, icp->im->id, icp->id,
+		    "corrupted nested list");
+	return -1;
+#endif
+    }
+
+    /*
+     * Nested list is written on the output buffer.
+     * Calculate the length of the list.
+     */
+    length = IMWritePos(conn) - attr_offset;
+
+    /* rewrite attribute length field */
+    IMRewriteC16(conn, length_offset, length);
+    IMPutPad(conn);
+
+    return r;
+}
+
+/* ARGSUSED */
+static int
+getPreeditAttributes(icp, id, nest, offset, data, len)
+IMIC *icp;
+unsigned int id;
+int nest;		/* unused */
+int offset;
+char *data;
+int len;
+{
+    TRACE(("imlib:getPreeditAttributes()\n"));
+    return getPSAttributes(icp, id, NEST_PREEDIT, offset, data, len);
+}
+
+/* ARGSUSED */
+static int
+getStatusAttributes(icp, id, nest, offset, data, len)
+IMIC *icp;
+unsigned int id;
+int nest;		/* unused */
+int offset;
+char *data;
+int len;
+{
+    TRACE(("imlib:getStatusAttributes()\n"));
+    return getPSAttributes(icp, id, NEST_STATUS, offset, data, len);
+}
+
+/* ARGSUSED */
+static int
+getInputStyle(icp, id, nest, offset, data, len)
+IMIC *icp;
+unsigned int id;
+int nest;
+int offset;
+char *data;
+int len;
+{
+    IMConnection *conn = icp->im->connection;
+
+    TRACE(("imlib:getInputStyle()\n"));
+
+    /*
+     * Input style must have been specified, (and validated)
+     * at IC creation.  No need for checking.
+     */
+    IMPutC16(conn, id);		/* attribute ID */
+    IMPutC16(conn, 4);		/* value length */
+    IMPutC32(conn, icp->common_attr.input_style);
+    return 0;
+}
+
+/* ARGSUSED */
+static int
+getClientWindow(icp, id, nest, offset, data, len)
+IMIC *icp;
+unsigned int id;
+int nest;
+int offset;
+char *data;
+int len;
+{
+    IMConnection *conn = icp->im->connection;
+
+    TRACE(("imlib:getClientWindow()\n"));
+
+    if (icp->common_attr.set_mask & ATTR_MASK_CLIENT) {
+	IMPutC16(conn, id);		/* attribute ID */
+	IMPutC16(conn, 4);		/* value length */
+	IMPutC32(conn, icp->common_attr.client);
+	return 0;
+    } else {
+	/* no default is available */
+	DPRINT(("getClientWindow without setting client window previously\n"));
+	IMCancelRequest(conn, offset);
+	IMSendError(conn, IMBadClientWindow, icp->im->id, icp->id,
+		    "client window not specified yet");
+	return -1;
+    }
+}
+
+/* ARGSUSED */
+static int
+getFocusWindow(icp, id, nest, offset, data, len)
+IMIC *icp;
+unsigned int id;
+int nest;
+int offset;
+char *data;
+int len;
+{
+    IMConnection *conn = icp->im->connection;
+
+    TRACE(("imlib:getFocusWindow()\n"));
+
+    if (!(icp->common_attr.set_mask & ATTR_MASK_FOCUS)) {
+	/* fill default value */
+	fillCommonDefault(icp, (unsigned long)ATTR_MASK_FOCUS);
+    }
+
+    if (icp->common_attr.set_mask & ATTR_MASK_FOCUS) {
+	IMPutC16(conn, id);		/* attribute ID */
+	IMPutC16(conn, 4);		/* value length */
+	IMPutC32(conn, icp->common_attr.focus);
+	return 0;
+    } else {
+	/*
+	 * Couldn't get the default value.  That is, neither
+	 * focus window nor client window is specified yet.
+	 */
+	DPRINT(("getFocusWindow without setting focus/client window previously\n"));
+	IMCancelRequest(conn, offset);
+	IMSendError(conn, IMBadFocusWindow, icp->im->id, icp->id,
+		    "neither of client/focus window not specified yet");
+	return -1;
+    }
+}
+
+/* ARGSUSED */
+static int
+getFilterEvents(icp, id, nest, offset, data, len)
+IMIC *icp;
+unsigned int id;
+int nest;
+int offset;
+char *data;
+int len;
+{
+    IMConnection *conn = icp->im->connection;
+
+    TRACE(("imlib:getFilterEvents()\n"));
+
+    /* We need only Key events */
+    IMPutC16(conn, id);		/* attribute ID */
+    IMPutC16(conn, 4);		/* value length */
+    IMPutC32(conn, KeyPressMask | KeyReleaseMask);
+    return 0;
+}
+
+/* ARGSUSED */
+static int
+getArea(icp, id, nest, offset, data, len)
+IMIC *icp;
+unsigned int id;
+int nest;
+int offset;
+char *data;
+int len;
+{
+    IMConnection *conn = icp->im->connection;
+    IMPSAttributes *ap;
+
+    TRACE(("imlib:getArea()\n"));
+
+    if ((ap = getPSPtr(icp, nest)) == NULL) {
+	IMCancelRequest(conn, offset);
+	unnestedError(icp);
+	return -1;
+    }
+
+    if (!(ap->set_mask & ATTR_MASK_AREA)) {
+	fillPSDefault(icp, nest, (unsigned long)ATTR_MASK_AREA);
+    }
+
+    IMPutC16(conn, id);		/* attribute ID */
+    IMPutC16(conn, 8);		/* value length */
+    IMPutI16(conn, ap->area.x);
+    IMPutI16(conn, ap->area.y);
+    IMPutC16(conn, ap->area.width);
+    IMPutC16(conn, ap->area.height);
+    return 0;
+}
+
+/* ARGSUSED */
+static int
+getAreaNeeded(icp, id, nest, offset, data, len)
+IMIC *icp;
+unsigned int id;
+int nest;
+int offset;
+char *data;
+int len;
+{
+    IMConnection *conn = icp->im->connection;
+    IMPSAttributes *ap;
+
+    TRACE(("imlib:getAreaNeeded()\n"));
+
+    if ((ap = getPSPtr(icp, nest)) == NULL) {
+	IMCancelRequest(conn, offset);
+	unnestedError(icp);
+	return -1;
+    }
+
+    /*
+     * Always call fillPSDefault to get appropriate AreaNeeded value.
+     */
+    fillPSDefault(icp, nest, (unsigned long)ATTR_MASK_AREA_NEEDED);
+
+    TRACE(("\tarea needed: %d, %d, %d, %d\n",
+	   ap->area_needed.x, ap->area_needed.y,
+	   ap->area_needed.width, ap->area_needed.height));
+
+    IMPutC16(conn, id);		/* attribute ID */
+    IMPutC16(conn, 8);		/* value length */
+    IMPutI16(conn, ap->area_needed.x);
+    IMPutI16(conn, ap->area_needed.y);
+    IMPutC16(conn, ap->area_needed.width);
+    IMPutC16(conn, ap->area_needed.height);
+    return 0;
+}
+
+/* ARGSUSED */
+static int
+getSpotLocation(icp, id, nest, offset, data, len)
+IMIC *icp;
+unsigned int id;
+int nest;
+int offset;
+char *data;
+int len;
+{
+    IMConnection *conn = icp->im->connection;
+    IMPSAttributes *ap = &icp->preedit_attr;
+
+    TRACE(("imlib:getSpotLocation()\n"));
+
+    if (nest == NEST_STATUS) {
+	IMCancelRequest(conn, offset);
+	IMSendError(conn, IMBadSomething, icp->im->id, icp->id,
+		    "spot location isn't a status attribute");
+	return -1;
+    }
+
+    if (!(ap->set_mask & ATTR_MASK_SPOT_LOCATION)) {
+	fillPSDefault(icp, NEST_PREEDIT,
+		      (unsigned long)ATTR_MASK_SPOT_LOCATION);
+    }
+
+    IMPutC16(conn, id);		/* attribute ID */
+    IMPutC16(conn, 4);		/* value length */
+    IMPutI16(conn, ap->spot_location.x);
+    IMPutI16(conn, ap->spot_location.y);
+    return 0;
+}
+
+/* ARGSUSED */
+static int
+getColormap(icp, id, nest, offset, data, len)
+IMIC *icp;
+unsigned int id;
+int nest;
+int offset;
+char *data;
+int len;
+{
+    IMConnection *conn = icp->im->connection;
+    IMPSAttributes *ap;
+
+    TRACE(("imlib:getColormap()\n"));
+
+    if ((ap = getPSPtr(icp, nest)) == NULL) {
+	IMCancelRequest(conn, offset);
+	unnestedError(icp);
+	return -1;
+    }
+
+    if (!(ap->set_mask & ATTR_MASK_COLORMAP)) {
+	fillPSDefault(icp, nest, (unsigned long)ATTR_MASK_COLORMAP);
+    }
+
+    IMPutC16(conn, id);		/* attribute ID */
+    IMPutC16(conn, 4);		/* value length */
+    IMPutC32(conn, ap->colormap);
+    return 0;
+}
+
+/* ARGSUSED */
+static int
+getStdColormap(icp, id, nest, offset, data, len)
+IMIC *icp;
+unsigned int id;
+int nest;
+int offset;
+char *data;
+int len;
+{
+    IMConnection *conn = icp->im->connection;
+    IMPSAttributes *ap;
+    Atom colormap_name;
+
+    TRACE(("imlib:getStdColormap()\n"));
+
+    if ((ap = getPSPtr(icp, nest)) == NULL) {
+	IMCancelRequest(conn, offset);
+	unnestedError(icp);
+	return -1;
+    }
+
+    if (ap->set_mask & ATTR_MASK_STD_COLORMAP) {
+	colormap_name = ap->std_colormap;
+    } else {
+	/* what to do? */
+	colormap_name = None;
+	DPRINT(("client asks standard colormap, but not specified\n"));
+    }
+
+    IMPutC16(conn, id);		/* attribute ID */
+    IMPutC16(conn, 4);		/* value length */
+    IMPutC32(conn, colormap_name);
+    return 0;
+}
+
+/* ARGSUSED */
+static int
+getForeground(icp, id, nest, offset, data, len)
+IMIC *icp;
+unsigned int id;
+int nest;
+int offset;
+char *data;
+int len;
+{
+    IMConnection *conn = icp->im->connection;
+    IMPSAttributes *ap;
+
+    TRACE(("imlib:getForeground()\n"));
+
+    if ((ap = getPSPtr(icp, nest)) == NULL) {
+	IMCancelRequest(conn, offset);
+	unnestedError(icp);
+	return -1;
+    }
+
+    if (!(ap->set_mask & ATTR_MASK_FOREGROUND)) {
+	fillPSDefault(icp, nest, (unsigned long)ATTR_MASK_FOREGROUND);
+    }
+
+    IMPutC16(conn, id);		/* attribute ID */
+    IMPutC16(conn, 4);		/* value length */
+    IMPutC32(conn, ap->foreground);
+    return 0;
+}
+
+/* ARGSUSED */
+static int
+getBackground(icp, id, nest, offset, data, len)
+IMIC *icp;
+unsigned int id;
+int nest;
+int offset;
+char *data;
+int len;
+{
+    IMConnection *conn = icp->im->connection;
+    IMPSAttributes *ap;
+
+    TRACE(("imlib:getBackground()\n"));
+
+    if ((ap = getPSPtr(icp, nest)) == NULL) {
+	IMCancelRequest(conn, offset);
+	unnestedError(icp);
+	return -1;
+    }
+
+    if (!(ap->set_mask & ATTR_MASK_BACKGROUND)) {
+	fillPSDefault(icp, nest, (unsigned long)ATTR_MASK_BACKGROUND);
+    }
+
+    IMPutC16(conn, id);		/* attribute ID */
+    IMPutC16(conn, 4);		/* value length */
+    IMPutC32(conn, ap->background);
+    return 0;
+}
+
+/* ARGSUSED */
+static int
+getBgPixmap(icp, id, nest, offset, data, len)
+IMIC *icp;
+unsigned int id;
+int nest;
+int offset;
+char *data;
+int len;
+{
+    IMConnection *conn = icp->im->connection;
+    IMPSAttributes *ap;
+
+    TRACE(("imlib:getBgPixmap()\n"));
+
+    if ((ap = getPSPtr(icp, nest)) == NULL) {
+	IMCancelRequest(conn, offset);
+	unnestedError(icp);
+	return -1;
+    }
+
+    if (!(ap->set_mask & ATTR_MASK_BG_PIXMAP)) {
+	fillPSDefault(icp, nest, (unsigned long)ATTR_MASK_BG_PIXMAP);
+    }
+
+    IMPutC16(conn, id);		/* attribute ID */
+    IMPutC16(conn, 4);		/* value length */
+    IMPutC32(conn, ap->bg_pixmap);
+    return 0;
+}
+
+/* ARGSUSED */
+static int
+getFontSet(icp, id, nest, offset, data, len)
+IMIC *icp;
+unsigned int id;
+int nest;
+int offset;
+char *data;
+int len;
+{
+    IMConnection *conn = icp->im->connection;
+    IMPSAttributes *ap;
+    int name_len;
+
+    TRACE(("imlib:getFontSet()\n"));
+
+    if ((ap = getPSPtr(icp, nest)) == NULL) {
+	IMCancelRequest(conn, offset);
+	unnestedError(icp);
+	return -1;
+    }
+
+    if (!(ap->set_mask & ATTR_MASK_FONT_SET)) {
+	fillPSDefault(icp, nest, (unsigned long)ATTR_MASK_FONT_SET);
+    }
+
+    name_len = strlen(ap->font_set);
+
+    IMPutC16(conn, id);		/* attribute ID */
+
+    IMPutC16(conn, (unsigned int)name_len);	/* value length */
+    IMPutString(conn, ap->font_set, name_len);
+    IMPutPad(conn);
+    return 0;
+}
+
+/* ARGSUSED */
+static int
+getLineSpace(icp, id, nest, offset, data, len)
+IMIC *icp;
+unsigned int id;
+int nest;
+int offset;
+char *data;
+int len;
+{
+    IMConnection *conn = icp->im->connection;
+    IMPSAttributes *ap;
+
+    TRACE(("imlib:getLineSpace()\n"));
+
+    if ((ap = getPSPtr(icp, nest)) == NULL) {
+	IMCancelRequest(conn, offset);
+	unnestedError(icp);
+	return -1;
+    }
+
+    if (!(ap->set_mask & ATTR_MASK_LINESPACE)) {
+	fillPSDefault(icp, nest, (unsigned long)ATTR_MASK_LINESPACE);
+    }
+
+    IMPutC16(conn, id);		/* attribute ID */
+    IMPutC16(conn, 4);	/* value length */
+    IMPutC32(conn, (unsigned long)ap->line_space);
+    return 0;
+}
+
+/* ARGSUSED */
+static int
+getCursor(icp, id, nest, offset, data, len)
+IMIC *icp;
+unsigned int id;
+int nest;
+int offset;
+char *data;
+int len;
+{
+    IMConnection *conn = icp->im->connection;
+    IMPSAttributes *ap;
+
+    TRACE(("imlib:getCursor()\n"));
+
+    if ((ap = getPSPtr(icp, nest)) == NULL) {
+	IMCancelRequest(conn, offset);
+	unnestedError(icp);
+	return -1;
+    }
+
+    if (!(ap->set_mask & ATTR_MASK_CURSOR)) {
+	fillPSDefault(icp, nest, (unsigned long)ATTR_MASK_CURSOR);
+    }
+
+    IMPutC16(conn, id);		/* attribute ID */
+    IMPutC16(conn, 4);		/* value length */
+    IMPutC32(conn, ap->cursor);
+    return 0;
+}
+
+/* ARGSUSED */
+static int
+getPreeditState(icp, id, nest, offset, data, len)
+IMIC *icp;
+unsigned int id;
+int nest;
+int offset;
+char *data;
+int len;
+{
+    IMConnection *conn = icp->im->connection;
+
+    TRACE(("imlib:getPreeditState()\n"));
+
+    if (nest == NEST_STATUS) {
+	IMCancelRequest(conn, offset);
+	IMSendError(conn, IMBadSomething, icp->im->id, icp->id,
+		    "preedit state isn't a status attribute");
+	return -1;
+    }
+
+    IMPutC16(conn, id);		/* attribute ID */
+    IMPutC16(conn, 4);		/* value length */
+    IMPutC32(conn,
+	     (icp->state & IC_CONVERTING) ?
+	     XIMPreeditEnable : XIMPreeditDisable);
+    return 0;
+}
+
+/* ARGSUSED */
+static int
+getResetState(icp, id, nest, offset, data, len)
+IMIC *icp;
+unsigned int id;
+int nest;
+int offset;
+char *data;
+int len;
+{
+    IMConnection *conn = icp->im->connection;
+
+    TRACE(("imlib:getResetState()\n"));
+
+    IMPutC16(conn, id);		/* attribute ID */
+    IMPutC16(conn, 4);		/* value length */
+    IMPutC32(conn, icp->common_attr.reset_state);
+    return 0;
+}
+
+static void
+changeFonts(icp, preedit)
+IMIC *icp;
+int preedit;
+{
+    FontBank bank = IMFontBank(icp->im);
+    String font_set;
+    XFontStruct ***fontsp;
+    int *num_fontsp;
+
+    TRACE(("imlib:changeFonts()\n"));
+
+    if (preedit) {
+	font_set = icp->preedit_attr.font_set;
+	fontsp = &icp->fonts;
+	num_fontsp = &icp->num_fonts;
+    } else {
+	font_set = icp->status_attr.font_set;
+	fontsp = &icp->status_fonts;
+	num_fontsp = &icp->num_status_fonts;
+    }
+
+    /*
+     * Unload previous fonts.
+     */
+    if (*num_fontsp > 0) {
+	FontBankFreeFonts(bank, *fontsp, *num_fontsp);
+    }
+
+    /*
+     * Load new fonts and store them in the IC structure.
+     */
+    *fontsp = FontBankGet(bank, font_set, num_fontsp);
+}
+
+
+/*
+ * Functions computing default attribute values
+ */
+
+static void
+fillCommonDefault(icp, mask)
+IMIC *icp;
+unsigned long mask;
+{
+    IMCommonAttributes *ap = &icp->common_attr;
+
+    TRACE(("imlib:fillCommonDefault()\n"));
+
+    /*
+     * Don't bother with the attributes which have been set.
+     */
+    mask &= ~ap->set_mask;
+
+    /*
+     * The attributes that have default value are FocusWindow,
+     * PreeditState and ResetState.
+     */
+    if (mask & ATTR_MASK_FOCUS) {
+	/* if ClientWindow is not set... no way */
+	if (mask & ATTR_MASK_CLIENT) {
+	    ap->focus = ap->client;
+	    ap->set_mask |= ATTR_MASK_FOCUS;
+	    icp->focus_profile = icp->client_profile;
+	    TRACE(("\tdefault focus window: %08lx\n", ap->focus));
+	}
+    }
+    if (mask & ATTR_MASK_PREEDIT_STATE) {
+	/* unless the client specified, we should start with disabled state. */
+	ap->set_mask |= ATTR_MASK_PREEDIT_STATE;
+	ap->reset_state = XIMPreeditDisable;
+    }
+    if (mask & ATTR_MASK_RESET_STATE) {
+	/* the default reset state must be the initial state. */
+	ap->set_mask |= ATTR_MASK_RESET_STATE;
+	ap->reset_state = XIMInitialState;
+    }
+}
+
+static int
+getNaturalLineSpace(icp, preedit)
+IMIC *icp;
+int preedit;
+{
+    XFontStruct **fonts;
+    int num_fonts;
+    int max_ascent = 0, max_descent = 0;
+    int i;
+
+    changeFonts(icp, preedit);
+    if (preedit) {
+	fonts = icp->fonts;
+	num_fonts = icp->num_fonts;
+    } else {
+	fonts = icp->status_fonts;
+	num_fonts = icp->num_status_fonts;
+    }
+
+    for (i = 0; i < num_fonts; i++) {
+	XFontStruct *font = fonts[i];
+	if (max_ascent < font->ascent) max_ascent = font->ascent;
+	if (max_descent < font->descent) max_descent = font->descent;
+    }
+
+    if (max_ascent + max_descent < MIN_LINE_SPACING) {
+	return MIN_LINE_SPACING;
+    } else {
+	return max_ascent + max_descent;
+    }
+}
+
+static void
+fillPSDefault(icp, type, mask)
+IMIC *icp;
+int type;	/* NEST_PREEDIT or NEST_STATUS */
+unsigned long mask;
+{
+    IMPSAttributes *ap;
+    IMConnection *conn = icp->im->connection;
+    Widget pw = conn->proto_widget;
+    int preedit;
+#ifdef DEBUG
+    char *typename = (type == NEST_PREEDIT) ? "preedit" : "status";
+#endif
+
+    TRACE(("imlib:fillPSDefault(%s)\n", typename));
+
+    preedit = (type == NEST_PREEDIT);
+    ap = preedit ? &icp->preedit_attr : &icp->status_attr;
+
+    /*
+     * Don't bother with the attributes which have been set.
+     * But area_needed needs to be computed each time (to get
+     * correct X and Y coordinates).
+     */
+    mask &= ~ap->set_mask | ATTR_MASK_AREA_NEEDED;
+
+    if (mask & ATTR_MASK_AREA) {
+	computeAreaForQuery(icp);
+	ap->set_mask |= ATTR_MASK_AREA;
+	DDPRINT(5, ("\tdefault %s area: %d,%d,%d,%d\n", typename,
+		    ap->area.x, ap->area.y, ap->area.width, ap->area.height));
+    }
+    if (mask & ATTR_MASK_FOREGROUND) {
+	ap->foreground = IMDefaultForeground(pw);
+	ap->set_mask |= ATTR_MASK_FOREGROUND;
+	DDPRINT(5, ("\tdefault %s foreground: %ld\n",
+		    typename, ap->foreground));
+    }
+    if (mask & ATTR_MASK_BACKGROUND) {
+	ap->background = IMDefaultBackground(pw);
+	ap->set_mask |= ATTR_MASK_BACKGROUND;
+	DDPRINT(5, ("\tdefault %s background: %ld\n",
+		    typename, ap->background));
+    }
+    if (mask & ATTR_MASK_COLORMAP) {
+	ap->colormap = pw->core.colormap;
+	ap->set_mask |= ATTR_MASK_COLORMAP;
+	DDPRINT(5, ("\tdefault %s colormap: %08lx\n",
+		    typename, ap->colormap));
+    }
+    if (mask & ATTR_MASK_STD_COLORMAP) {
+	/* you can't fill default. what to do? */
+	DDPRINT(5, ("\tdefault %s std colormap: N/A\n", typename));
+    }
+    if (mask & ATTR_MASK_BG_PIXMAP) {
+	ap->bg_pixmap = None;
+	ap->set_mask |= ATTR_MASK_BG_PIXMAP;
+	DDPRINT(5, ("\tdefault %s background pixmap: None\n", typename));
+    }
+    if (mask & ATTR_MASK_LINESPACE) {
+	if (!(ap->set_mask & ATTR_MASK_FONT_SET)) {
+	    fillPSDefault(icp, type, (unsigned long)ATTR_MASK_FONT_SET);
+	}
+	ap->line_space = getNaturalLineSpace(icp, type == NEST_PREEDIT);
+	ap->set_mask |= ATTR_MASK_LINESPACE;
+	DDPRINT(5, ("\tdefault line space: %d\n", ap->line_space));
+    }
+    if (mask & ATTR_MASK_CURSOR) {
+	ap->cursor = None;
+	ap->set_mask |= ATTR_MASK_CURSOR;
+	DDPRINT(5, ("\tdefault %s cursor: None\n", typename));
+    }
+    if (mask & ATTR_MASK_AREA_NEEDED) {
+	computeAreaNeeded(icp);
+	DDPRINT(5, ("\t%s area_needed: %d,%d,%d,%d\n", typename,
+		    ap->area_needed.x, ap->area_needed.y,
+		    ap->area_needed.width, ap->area_needed.height));
+    }
+    if (mask & ATTR_MASK_FONT_SET) {
+	ap->font_set = IMDefaultFontSet(icp->im);
+	ap->set_mask |= ATTR_MASK_FONT_SET;
+	DDPRINT(5, ("\tdefault %s fontset: %s\n", typename, ap->font_set));
+    }
+    if (mask & ATTR_MASK_SPOT_LOCATION) {
+	ap->spot_location.x = ap->spot_location.y = 0;
+	ap->set_mask |= ATTR_MASK_SPOT_LOCATION;
+	DDPRINT(5, ("\tdefault spot location: %d, %d\n",
+		    ap->spot_location.x, ap->spot_location.y));
+    }
+}
+
+/*
+ * Function validating attribute values
+ */
+
+static int
+validateCommonAttr(icp, checkonly)
+IMIC *icp;
+int checkonly;
+{
+    IMCommonAttributes *ap = &icp->common_attr;
+    IMConnection *conn = icp->im->connection;
+    unsigned long mask = ap->change_mask;
+    int ret = 0;
+
+    TRACE(("imlib:validateCommonAttr()\n"));
+
+    mask &= ap->set_mask;
+
+#define SENDERROR(code, msg) \
+    if (!checkonly && ret == 0) { \
+	IMSendError(conn, code, icp->im->id, icp->id, msg); ret = -1; \
+    }
+
+    if (mask & ATTR_MASK_INPUT_STYLE) {
+	XIMStyle xstyle = icp->common_attr.input_style;
+	InputStyle *isp = styles;
+
+	while (isp->xim_style != 0) {
+	    if (isp->xim_style == xstyle) break;
+	    isp++;
+	}
+	if (isp->xim_style == 0) {
+	    DPRINT(("unsupported input style\n"));
+	    ap->set_mask &= ~ATTR_MASK_INPUT_STYLE;
+	    SENDERROR(IMBadStyle, "unsupported input style");
+	} else {
+	    icp->style = isp->conversion_style;
+	}
+    }
+    if (mask & ATTR_MASK_CLIENT) {
+	if (!validateClientWindow(icp)) {
+	    DPRINT(("invalid client window ID\n"));
+	    ap->set_mask &= ~ATTR_MASK_CLIENT;
+	    SENDERROR(IMBadClientWindow, "invalid client window ID");
+	}
+    }
+    if (mask & ATTR_MASK_FOCUS) {
+	if (!validateFocusWindow(icp)) {
+	    DPRINT(("invalid focus window ID\n"));
+	    ap->set_mask &= ~ATTR_MASK_FOCUS;
+	    SENDERROR(IMBadFocusWindow, "invalid focus window ID");
+	}
+    }
+    if (mask & ATTR_MASK_PREEDIT_STATE) {
+	unsigned long preedit_state = ap->preedit_state;
+
+	if (preedit_state != XIMPreeditEnable &&
+	    preedit_state != XIMPreeditDisable) {
+	    DPRINT(("invalid preedit state\n"));
+	    ap->set_mask &= ~ATTR_MASK_PREEDIT_STATE;
+	    SENDERROR(IMBadSomething, "invalid preedit state");
+	}
+    }
+    if (mask & ATTR_MASK_RESET_STATE) {
+	unsigned long reset_state = ap->reset_state;
+
+	if (reset_state != XIMInitialState &&
+	    reset_state != XIMPreserveState) {
+	    /*
+	     * Xlib document says invalid values should be interpreted as
+	     * XIMInitialState.
+	     */
+	    DPRINT(("invalid reset state -- forcing initial state\n"));
+	    ap->reset_state = XIMInitialState;
+	}
+    }
+
+    return ret;
+#undef SENDERROR
+}
+
+static int
+validatePSAttr(icp, type, checkonly)
+IMIC *icp;
+int type;
+int checkonly;
+{
+    /*
+     * Check validity of preedit/status attribute values.
+     * 'type' is either NEST_PREEDIT or NEST_STATUS, indicating
+     * whether preedit or status attributes are to be checked.
+     * 'mask' is the attribute mask to be checked.
+     * If all the attributes are valid, this function return 0.
+     * Otherwise it issues an error message for the first invalid
+     * value detected, and returns -1.
+     */
+    IMPSAttributes *ap;
+    IMConnection *conn = icp->im->connection;
+    unsigned long mask;
+    int preedit;
+    int ret = 0;
+
+    TRACE(("imlib:validatePSAttr()\n"));
+
+    preedit = (type == NEST_PREEDIT);
+    ap = preedit ? &icp->preedit_attr : &icp->status_attr;
+
+    /* do not check unset attributes */
+    mask = ap->change_mask & ap->set_mask;
+
+#define SENDERROR(code, msg) \
+    if (!checkonly && ret == 0) { \
+	IMSendError(conn, code, icp->im->id, icp->id, msg); ret = -1; \
+    }
+
+    if (mask & ATTR_MASK_AREA) {
+	if (ap->area.width == 0 || ap->area.height == 0) {
+	    ap->set_mask &= ~ATTR_MASK_AREA;
+	    DPRINT(("zero area width/height\n"));
+	    SENDERROR(IMBadArea, "invalid area width/height");
+	}
+    }
+
+#ifdef notdef
+    if (mask & ATTR_MASK_COLORMAP) {
+    }
+
+    if (mask & ATTR_MASK_BG_PIXMAP) {
+    }
+#endif
+
+    if (mask & ATTR_MASK_LINESPACE) {
+	if (ap->line_space < MIN_LINE_SPACING) {
+	    ap->set_mask &= ~ATTR_MASK_LINESPACE;
+	    /*
+	     * we don't send error message in this case, because
+	     * there exist some applications which send invalid line
+	     * spacing and we don't want to break them.
+	     */
+	    DPRINT(("line space too small %d\n", ap->line_space));
+	}
+    }
+
+#ifdef notdef
+    if (mask & ATTR_MASK_CURSOR) {
+	/* How should we check it? */
+    }
+#endif
+
+    if (mask & ATTR_MASK_AREA_NEEDED) {
+    }
+#ifdef notdef
+    if (mask & ATTR_MASK_FONT_SET) {
+    }
+    if (mask & ATTR_MASK_SPOT_LOCATION) {
+    }
+#endif
+
+    return ret;
+#undef SENDERROR
+}
+
+
+/*
+ * Functions to extract necessary attributes and make conversion argument
+ */
+
+static void
+changeConversionAttributes(icp)
+IMIC *icp;
+{
+    unsigned long mask;
+    ConversionAttributes attr;
+
+    TRACE(("imlib:changeConversionAttributes()\n"));
+    mask = IMMakeConvAttributes(icp, &attr);
+    CControlChangeAttributes(icp->conversion, mask, &attr);
+}
+
+static void
+computeAreaNeeded(icp)
+IMIC *icp;
+{
+    IMPSAttributes *pattr = &icp->preedit_attr;
+    IMPSAttributes *sattr = &icp->status_attr;
+    IMWindowProfile *cpr = &icp->client_profile;
+    int width, height;
+    int min_width, min_height;
+    int default_status_width;
+    int font_height;
+
+    TRACE(("computeAreaNeeded()\n"));
+
+    if (icp->style == IMSTYLE_SEPARATE) return;
+
+    /*
+     * Get the current dimension of the client window.
+     */
+    (void)validateClientWindow(icp);
+
+    /*
+     * Compute the dimensions of the status region.
+     */
+    fillPSDefault(icp, NEST_STATUS, (unsigned long)ATTR_MASK_LINESPACE);
+    font_height = sattr->line_space + 2;
+    width = height = 0;
+    if (sattr->set_mask & ATTR_MASK_AREA_NEEDED) {
+	width = sattr->area_needed.width;
+	height = sattr->area_needed.height;
+	TRACE(("\tstatus areaNeeded was: (%d,%d)\n", width, height));
+    }
+
+    min_width = font_height * 3;
+    min_height = font_height;
+    if (min_width < MIN_AREA_WIDTH) min_width = MIN_AREA_WIDTH;
+    if (min_height < MIN_AREA_HEIGHT) min_height = MIN_AREA_HEIGHT;
+
+    if (width == 0) {
+	default_status_width =
+	    IMStatusWidth(icp->im->connection->proto_widget);
+	if (default_status_width > 0) {
+	    width = default_status_width;
+	} else {
+	    width = cpr->width / 5;		/* wild guess */
+	}
+    }
+    
+    if (width < min_width) width = min_width;
+    if (height < min_height) height = min_height;
+    
+    sattr->area_needed.x = 0;
+    sattr->area_needed.y = cpr->height - height;
+    sattr->area_needed.width = width;
+    sattr->area_needed.height = height;
+    TRACE(("\tstatus areaNeeded is now: (%d, %d, %d, %d)\n",
+	    sattr->area_needed.x, sattr->area_needed.y, width, height));
+
+    /*
+     * Compute the dimensions of the pre-edit region.
+     */
+    if (icp->style != IMSTYLE_OFF_THE_SPOT) return;
+
+    fillPSDefault(icp, NEST_PREEDIT, (unsigned long)ATTR_MASK_LINESPACE);
+    font_height = pattr->line_space + 2;
+    width = height = 0;
+    if (pattr->set_mask & ATTR_MASK_AREA_NEEDED) {
+	width = pattr->area_needed.width;
+	height = pattr->area_needed.height;
+	TRACE(("\tpreedit areaNeeded was: (%d,%d)\n",
+		width, height));
+    }
+
+    min_width = (cpr->width - sattr->area_needed.width) / 2;
+    min_height = font_height;
+    if (min_width < MIN_AREA_WIDTH) min_width = MIN_AREA_WIDTH;
+    if (min_height < MIN_AREA_HEIGHT) min_height = MIN_AREA_HEIGHT;
+
+    if (width < min_width) width = min_width;
+    if (height < min_height) height = min_height;
+
+    pattr->area_needed.x = sattr->area_needed.width;
+    pattr->area_needed.y = cpr->height - height;
+    pattr->area_needed.width = width;
+    pattr->area_needed.height = height;
+    TRACE(("\tpreedit areaNeeded is now: (%d, %d, %d, %d)\n",
+	    pattr->area_needed.x, pattr->area_needed.y, width, height));
+}
+
+static void
+computeAreaForQuery(icp)
+IMIC *icp;
+{
+    IMPSAttributes *pattr = &icp->preedit_attr;
+    IMPSAttributes *sattr = &icp->status_attr;
+
+    TRACE(("computeAreaForQuery()\n"));
+
+    if (icp->style == IMSTYLE_SEPARATE) return;
+
+    computeAreaNeeded(icp);
+
+    if (!(pattr->set_mask & ATTR_MASK_AREA)) {
+	if (icp->style == IMSTYLE_OVER_THE_SPOT) {
+	    IMWindowProfile *fpr = &icp->focus_profile;
+
+	    pattr->area.x = 0;
+	    pattr->area.y = 0;
+	    pattr->area.width = fpr->width;
+	    pattr->area.height = fpr->height;
+	} else {	/* IMSTYLE_OFF_THE_SPOT */
+	    pattr->area = pattr->area_needed;
+	}
+    }
+
+    if (!(sattr->set_mask & ATTR_MASK_AREA)) {
+	sattr->area = sattr->area_needed;
+    }
+}
+
+
+/*
+ * Public functions
+ */
+
+/*- IMPutIMAttrList: write list of supported IM attributes to output buffer -*/
+void
+IMPutIMAttrList(imp)
+IMIM *imp;
+{
+    IMConnection *conn = imp->connection;
+    IMAttribute *iap;
+    int offset, list_start, list_end;
+    int n;
+
+    TRACE(("IMPutIMAttrList()\n"));
+
+    offset = IMWritePos(conn);
+    IMPutC16(conn, 0);		/* dummy. overwritten afterwards */
+
+    list_start = IMWritePos(conn);
+    for (n = 0, iap = imAttributes; n < numImAttributes; n++, iap++) {
+	int length;
+
+	IMPutC16(conn, (unsigned int)n);
+	IMPutC16(conn, (unsigned int)iap->type);
+	length = strlen(iap->name);
+	IMPutC16(conn, (unsigned int)length);
+	IMPutString(conn, iap->name, length);
+	IMPutPad(conn);
+    }
+    list_end = IMWritePos(conn);
+    IMRewriteC16(conn, offset, (unsigned int)(list_end - list_start));
+}
+
+/*- IMPutICAttrList: write list of supported IC attributes to output buffer -*/
+void
+IMPutICAttrList(imp)
+IMIM *imp;
+{
+    IMConnection *conn = imp->connection;
+    ICAttribute *iap;
+    int offset, list_start, list_end;
+    int n;
+
+    TRACE(("IMPutICAttrList()\n"));
+
+    offset = IMWritePos(conn);
+    IMPutC16(conn, 0);		/* dummy. overwritten afterwards */
+    IMPutC16(conn, 0);		/* unused */
+
+    list_start = IMWritePos(conn);
+    for (n = 0, iap = icAttributes; n < numIcAttributes; n++, iap++) {
+	int length;
+
+	IMPutC16(conn, (unsigned int)n);
+	IMPutC16(conn, (unsigned int)iap->type);
+	length = strlen(iap->name);
+	IMPutC16(conn, (unsigned int)length);
+	IMPutString(conn, iap->name, length);
+	IMPutPad(conn);
+    }
+    list_end = IMWritePos(conn);
+    IMRewriteC16(conn, offset, (unsigned int)(list_end - list_start));
+}
+
+/* ARGSUSED */
+int
+IMSetIMValues(imp, data, len, major)
+IMIM *imp;
+char *data;
+int len;
+int major;
+{
+    TRACE(("IMSetIMValues(): not supported yet\n"));
+    /* not supported yet */
+
+    IMSendError(imp->connection, IMBadProtocol, imp->id, 0,
+		"this protocol is not supported yet");
+    return -1;
+}
+
+int
+IMGetIMValues(imp, data, len, offset)
+IMIM *imp;
+char *data;
+int len;
+int offset;		/* request offset */
+{
+    IMConnection *conn = imp->connection;
+    int pos, list_start, list_end;
+
+    TRACE(("IMGetIMValues()\n"));
+
+    pos = IMWritePos(conn);
+    IMPutC16(conn, 0);		/* length of the list. to be overwritten. */
+
+    list_start = IMWritePos(conn);
+
+    if (getIMValues(imp, data, len, offset) < 0) return -1;
+
+    list_end = IMWritePos(conn);
+    IMRewriteC16(conn, pos, (unsigned int)(list_end - list_start));
+    return 0;
+}
+
+int
+IMSetICValues(icp, data, len, major)
+IMIC *icp;
+char *data;
+int len;
+int major;
+{
+    int r1, r2, r3;
+
+    TRACE(("IMSetICValues()\n"));
+
+    /* clear change mask */
+    icp->common_attr.change_mask = 0;
+    icp->preedit_attr.change_mask = 0;
+    icp->status_attr.change_mask = 0;
+
+    /* read the specified data and set attributes */
+    r1 = setICValues(icp, data, len, 0,
+		     (major == XIM_CREATE_IC) ? OP_C : OP_S);
+
+    /* validate attributes */
+    r2 = IMValidateICAttributes(icp, (r1 < 0));
+
+    /*
+     * If the operation is CREATE_IC, input style attribute must be set.
+     */
+    if (major == XIM_CREATE_IC &&
+	!(icp->common_attr.set_mask & ATTR_MASK_INPUT_STYLE) &&
+	r1 == 0 && r2 == 0) {
+	DPRINT(("input style not specified by CreateIC\n"));
+	IMSendError(icp->im->connection, IMBadSomething, icp->im->id, icp->id,
+		    "inputStyle resource must be set");
+	r3 = -1;
+    } else {
+	r3 = 0;
+    }
+
+    if (r1 == 0 && r2 == 0) {
+	/* if conversion is taking place... */
+	if (icp->state & IC_CONVERTING) {
+	    changeConversionAttributes(icp);
+	}
+
+	/* if preedit state is specified... */
+	if (icp->common_attr.change_mask & ATTR_MASK_PREEDIT_STATE) {
+	    TRACE(("changing preedit state to %s\n",
+		   (icp->common_attr.preedit_state == XIMPreeditEnable) ?
+		   "enabled" : "disabled"));
+	    if (icp->common_attr.preedit_state == XIMPreeditEnable) {
+		IMStartConversion(icp);
+	    } else {
+		IMStopConversion(icp);
+	    }
+	}
+    }
+
+    return (r1 < 0 || r2 < 0 || r3 < 0) ? -1 : 0;
+}
+
+int
+IMGetICValues(icp, data, len, offset)
+IMIC *icp;
+char *data;
+int len;
+int offset;		/* request offset */
+{
+    int nested_separator;
+    int r;
+    IMConnection *conn = icp->im->connection;
+    int pos, list_start, list_end;
+
+    TRACE(("IMGetICValues()\n"));
+
+    pos = IMWritePos(conn);
+    IMPutC16(conn, 0);		/* dummy. overwritten afterwards */
+    IMPutC16(conn, 0);		/* unused */
+
+    list_start = IMWritePos(conn);
+
+    r = getICValues(icp, data, len, NEST_NONE, offset, &nested_separator);
+    if (r < 0) return -1;
+
+    list_end = IMWritePos(conn);
+    IMRewriteC16(conn, pos, (unsigned int)(list_end - list_start));
+
+    if (nested_separator) {
+	/*
+	 * There must have been some unbalanced NestedListSeparator.
+	 */
+	DPRINT(("getICvalues: unmatched separator\n"));
+	IMCancelRequest(icp->im->connection, offset);
+	IMSendError(icp->im->connection, IMBadSomething, icp->im->id, icp->id,
+		    "corrupted nested list");
+	return -1;
+    }
+    return 0;
+}
+
+void
+IMFillDefault(icp, common_mask, preedit_mask, status_mask)
+IMIC *icp;
+unsigned long common_mask;
+unsigned long preedit_mask;
+unsigned long status_mask;
+{
+    TRACE(("IMFillDefault()\n"));
+
+    if (common_mask != 0) fillCommonDefault(icp, common_mask);
+    if (preedit_mask != 0) fillPSDefault(icp, NEST_PREEDIT, preedit_mask);
+    if (status_mask != 0) fillPSDefault(icp, NEST_STATUS, status_mask);
+}
+
+int
+IMValidateWindow(dpy, win, profilep)
+Display *dpy;
+Window win;
+IMWindowProfile *profilep;
+{
+    Window root;
+    int x, y;
+    unsigned int width, height, border, depth;
+    XAEHandle h;
+    int s;
+
+    TRACE(("IMValidateWindow(win: %08lx)\n", win));
+
+    h = XAESetIgnoreErrors(dpy);
+    s = XGetGeometry(dpy, win, &root, &x, &y,
+		     &width, &height, &border, &depth);
+    XAEUnset(h);
+
+    if (profilep != NULL && s) {
+	profilep->width = width;
+	profilep->height = width;
+	profilep->root = root;
+    }
+    return s;
+}
+
+int
+IMValidateICAttributes(icp, checkonly)
+IMIC *icp;
+int checkonly;
+{
+    int error_occured;
+    int r;
+
+    TRACE(("IMValidateICAttributes()\n"));
+
+    /*
+     * Be careful not to send multiple error messages.
+     */
+
+    error_occured = 0;
+
+    r = validateCommonAttr(icp, checkonly);
+    if (r < 0) error_occured = 1;
+
+    r = validatePSAttr(icp, NEST_PREEDIT, (checkonly && error_occured));
+    if (r < 0) error_occured = 1;
+
+    r = validatePSAttr(icp, NEST_STATUS, (checkonly && error_occured));
+    if (r < 0) error_occured = 1;
+
+    return error_occured ? -1 : 0;
+}
+
+void
+IMFreeICAttributes(icp)
+IMIC *icp;
+{
+    IMPSAttributes *pattr = &icp->preedit_attr;
+    IMPSAttributes *sattr = &icp->status_attr;
+    FontBank bank = IMFontBank(icp->im);
+
+    TRACE(("IMFreeICAttributes()\n"));
+
+    if (pattr->set_mask & ATTR_MASK_FONT_SET) XtFree(pattr->font_set);
+    if (sattr->set_mask & ATTR_MASK_FONT_SET) XtFree(sattr->font_set);
+
+    if (icp->num_fonts > 0) {
+	FontBankFreeFonts(bank, icp->fonts, icp->num_fonts);
+    }
+    if (icp->num_status_fonts > 0) {
+	FontBankFreeFonts(bank, icp->status_fonts, icp->num_status_fonts);
+    }
+}
+
+unsigned long
+IMMakeConvAttributes(icp, attr)
+IMIC *icp;
+ConversionAttributes *attr;
+{
+    IMCommonAttributes *cattr;
+    IMPSAttributes *pattr, *sattr;
+    unsigned long cmask;		/* changed attributes */
+    unsigned long mask;			/* attributes to be set */
+
+    TRACE(("IMMakeConvAttributes()\n"));
+    mask = 0L;
+
+    cattr = &icp->common_attr;
+    pattr = &icp->preedit_attr;
+    sattr = &icp->status_attr;
+
+    /*
+     * Check changes of common attributes.
+     */
+    cmask = cattr->change_mask;
+
+    /* focus window */
+    if (cmask & ATTR_MASK_FOCUS) {
+	attr->focuswindow = cattr->focus;
+	mask |= CAFocusWindow;
+    }
+
+    /*
+     * Check changes of preedit attributes.
+     */
+    cmask = pattr->change_mask;
+
+    /* client area */
+    if (cmask & ATTR_MASK_AREA) {
+	attr->clientarea.x = pattr->area.x;
+	attr->clientarea.y = pattr->area.y;
+	attr->clientarea.width = pattr->area.width;
+	attr->clientarea.height = pattr->area.height;
+	mask |= CAClientArea;
+    }
+
+    /* foreground/background */
+    if (cmask & ATTR_MASK_FOREGROUND) {
+	attr->foreground = pattr->foreground;
+	mask |= CAColor;
+    }
+    if (cmask & ATTR_MASK_BACKGROUND) {
+	attr->background = pattr->background;
+	mask |= CAColor;
+    }
+
+    /* colormap */
+    if (cmask & ATTR_MASK_COLORMAP) {
+	attr->colormap = pattr->colormap;
+	mask |= CAColormap;
+    }
+
+    /* background pixmap */
+    if (cmask & ATTR_MASK_BG_PIXMAP) {
+	attr->background_pixmap = pattr->bg_pixmap;
+	mask |= CABackgroundPixmap;
+    }
+
+    /* line spacing */
+    if (cmask & ATTR_MASK_LINESPACE) {
+	attr->linespacing = pattr->line_space;
+	mask |= CALineSpacing;
+    }
+
+    /* cursor */
+    if (cmask & ATTR_MASK_CURSOR) {
+	attr->cursor = pattr->cursor;
+	mask |= CACursor;
+    }
+
+    /* font */
+    if (cmask & ATTR_MASK_FONT_SET) {
+	changeFonts(icp, 1);
+	attr->fonts = icp->fonts;
+	attr->num_fonts = icp->num_fonts;
+	mask |= CAFonts;
+    }
+
+    /* spot location */
+    if ((cmask & ATTR_MASK_SPOT_LOCATION) &&
+	icp->style == IMSTYLE_OVER_THE_SPOT) {
+	attr->spotx = pattr->spot_location.x;
+	attr->spoty = pattr->spot_location.y;
+	mask |= CASpotLocation;
+    }
+
+    /*
+     * Check changes of status attributes.
+     */
+    cmask = sattr->change_mask;
+
+    /* status area */
+    if (cmask & ATTR_MASK_AREA) {
+	attr->statusarea.x = sattr->area.x;
+	attr->statusarea.y = sattr->area.y;
+	attr->statusarea.width = sattr->area.width;
+	attr->statusarea.height = sattr->area.height;
+	mask |= CAStatusArea;
+    }
+
+    /* font */
+    if (cmask & ATTR_MASK_FONT_SET) {
+	changeFonts(icp, 0);
+	attr->status_fonts = icp->status_fonts;
+	attr->num_status_fonts = icp->num_status_fonts;
+	mask |= CAStatusFonts;
+    }
+
+    return mask;
+}
+
+void
+IMMoveLocation(icp, x, y)
+IMIC *icp;
+int x;
+int y;
+{
+    TRACE(("IMMoveLocation()\n"));
+
+    icp->preedit_attr.spot_location.x = x;
+    icp->preedit_attr.spot_location.y = y;
+    icp->preedit_attr.set_mask |= ATTR_MASK_SPOT_LOCATION;
+    icp->preedit_attr.change_mask = ATTR_MASK_SPOT_LOCATION;
+    if (icp->state & IC_CONVERTING) {
+	changeConversionAttributes(icp);
+    }
+}