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