Mercurial > kinput2.yaz
diff lib/XimpProto.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 | 56c98768f86b 7a454839a6de |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/XimpProto.c Mon Mar 08 04:44:30 2010 +0900 @@ -0,0 +1,3013 @@ +#ifndef lint +static char *rcsid = "$Id: XimpProto.c,v 1.49 1999/05/18 08:53:21 ishisone Exp $"; +#endif +/*- + * Copyright (c) 1991 Software Research Associates, Inc. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Software Research Associates not be + * used in advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. Software Research + * Associates makes no representations about the suitability of this software + * for any purpose. It is provided "as is" without express or implied + * warranty. + * + * Author: Makoto Ishisone, Software Research Associates, Inc., Japan + */ + +#include <X11/Xos.h> +#include <X11/IntrinsicP.h> + +/* this widget needs X11R5 header files... */ +#if defined(XlibSpecificationRelease) && XlibSpecificationRelease >= 5 + +#include <X11/StringDefs.h> +#include <X11/Xatom.h> +#include <X11/Xmu/Atoms.h> +#include <X11/Xmu/CharSet.h> +#include "XIMProto.h" +#include "XimpProtoP.h" +#include "ConvMgr.h" +#include "InputConv.h" +#include "OverConv.h" +#include "OffConv.h" +#include "OnConv.h" +#include "MyDispatch.h" +#include "AsyncErr.h" +#include "ParseKey.h" + + +#define DEBUG_VAR debug_XimpProtocol +#include "DebugPrint.h" + + +#define PROTOCOL_VERSION_STR "XIMP.3.5" +#define SERVER_NAME "kinput2" +#define SERVER_VERSION "1" +#define VENDOR_NAME "SRA" + + +#define PREEDIT_MASK (XIMP_PRE_AREA_MASK|XIMP_PRE_FG_MASK|XIMP_PRE_BG_MASK|\ + XIMP_PRE_COLORMAP_MASK|XIMP_PRE_BGPIXMAP_MASK|\ + XIMP_PRE_LINESP_MASK|XIMP_PRE_CURSOR_MASK|\ + XIMP_PRE_AREANEED_MASK|XIMP_PRE_SPOTL_MASK) +#define STATUS_MASK (XIMP_STS_AREA_MASK|XIMP_STS_FG_MASK|XIMP_STS_BG_MASK|\ + XIMP_STS_COLORMAP_MASK|XIMP_STS_BGPIXMAP_MASK|\ + XIMP_STS_LINESP_MASK|XIMP_STS_CURSOR_MASK|\ + XIMP_STS_AREANEED_MASK|XIMP_STS_WINDOW_MASK) + +#define MIN_LINE_SPACING 2 +#define MIN_AREA_WIDTH 16 +#define MIN_AREA_HEIGHT 10 + + +/*- resource table -*/ +static XtResource resources[] = { +#define offset(field) XtOffset(XimpProtocolWidget, ximp.field) + { XtNlocaleName, XtCLocaleName, XtRString, sizeof(String), + offset(localename), XtRImmediate, (XtPointer)NULL }, + { XtNserverName, XtCServerName, XtRString, sizeof(String), + offset(servername), XtRString, (XtPointer)SERVER_NAME }, + { XtNforceDefaultServer, XtCForceDefaultServer, XtRBoolean, sizeof(Boolean), + offset(forceDefaultServer), XtRImmediate, (XtPointer)False }, + { XtNconversionStartKeys, XtCConversionStartKeys, XtRString, sizeof(String), + offset(convkeys), XtRImmediate, (XtPointer)NULL }, + { XtNinputObjectClass, XtCClass, XtRPointer, sizeof(WidgetClass), + offset(inputObjClass), XtRImmediate, (XtPointer)NULL }, + { XtNdisplayObjectClass, XtCClass, XtRPointer, sizeof(WidgetClass), + offset(displayObjClass), XtRImmediate, (XtPointer)NULL }, + { XtNdefaultFontList, XtCFontList, XtRString, sizeof(String), + offset(defaultfontlist), XtRImmediate, (XtPointer)NULL }, + { XtNforeground, XtCForeground, XtRPixel, sizeof (Pixel), + offset(foreground), XtRString, XtDefaultForeground }, + { XtNstatusWidth, XtCStatusWidth, XtRDimension, sizeof(Dimension), + offset(statuswidth), XtRImmediate, (XtPointer)0 }, +#undef offset +}; + +static void XimpMessageProc(); +static void SelectionRequestProc(); +static void SelectionClearProc(); + +/*- action table -*/ +static XtActionsRec actions[] = { + { "ximp-message", XimpMessageProc }, + { "selection-request", SelectionRequestProc }, + { "selection-clear", SelectionClearProc }, +}; + +/*- default translation -*/ +static char translations[] = + "<Message>_XIMP_PROTOCOL: ximp-message()\n\ + <SelReq>: selection-request()\n\ + <SelClr>: selection-clear()"; + + +/*- static function declarations -*/ +static void ClassInitialize(); +static void Initialize(); +static void Destroy(); +static void Realize(); + +static void getAtoms(); +static int ownSelection(); + +static ConvClient *findClient(); +static ConvClient *newClient(); +static Widget attachConverter(); +static void detachConverter(); +static void deleteClient(); + +static Boolean isCorrectClientEvent(); +static Boolean isCorrectWindowID(); +static void initializeError(); +static void checkLocale(); +static void fillInDefaultAttributes(); +static void computeAreaForStartup(); +static void computeAreaForQuery(); +static unsigned long makeConvAttributes(); +static void getFonts(); + +static void setProperty(); +static void setKeyProperty(); +static void getAttributes(); +static void getFocusProperty(); +static void getPreeditFontProperty(); +static void getStatusFontProperty(); +static void getPreeditProperty(); +static void getStatusProperty(); +static Boolean readProperty(); +static void setAttributes(); +static void setFocusProperty(); +static void setPreeditFontProperty(); +static void setStatusFontProperty(); +static void setPreeditProperty(); +static void setStatusProperty(); +static void writeProperty(); + +static void sendClientMessage8(); +static void sendClientMessage32(); +static void sendKeyEvent(); +static void sendErrorEvent(); +static void sendCreateRefusal(); + +static void fixCallback(); +static void fixProc(); +static void endCallback(); +static void endProc(); +static void unusedEventCallback(); + +static void ximpCreateMessageProc(); +static void ximpDestroyMessageProc(); +static void ximpBeginMessageProc(); +static void ximpEndMessageProc(); +static void ximpSetFocusMessageProc(); +static void ximpUnsetFocusMessageProc(); +static void ximpMoveMessageProc(); +static void ximpResetMessageProc(); +static void ximpSetValueMessageProc(); +static void ximpGetValueMessageProc(); +static void ximpKeyPressMessageProc(); +static void ximpExtensionMessageProc(); + +static void ClientDead(); + +static void preeditStartCallback(); +static void preeditDoneCallback(); +static void preeditDrawCallback(); +static void preeditCaretCallback(); +static void statusStartCallback(); +static void statusDoneCallback(); +static void statusDrawCallback(); +static void preeditStart(); +static void preeditDone(); +static void preeditDraw(); +static void preeditCaret(); +static void statusStart(); +static void statusDone(); +static void statusDraw(); + +/*- XimpProtocolClassRec -*/ +XimpProtocolClassRec ximpProtocolClassRec = { + { /* core fields */ + /* superclass */ (WidgetClass) &widgetClassRec, + /* class_name */ "XimpProtocol", + /* widget_size */ sizeof(XimpProtocolRec), + /* class_initialize */ ClassInitialize, + /* class_part_initialize */ NULL, + /* class_inited */ FALSE, + /* initialize */ Initialize, + /* initialize_hook */ NULL, + /* realize */ Realize, + /* actions */ actions, + /* num_actions */ XtNumber(actions), + /* resources */ resources, + /* num_resources */ XtNumber(resources), + /* xrm_class */ NULLQUARK, + /* compress_motion */ TRUE, + /* compress_exposure */ TRUE, + /* compress_enterleave */ TRUE, + /* visible_interest */ FALSE, + /* destroy */ Destroy, + /* resize */ NULL, + /* expose */ NULL, + /* set_values */ NULL, + /* set_values_hook */ NULL, + /* set_values_almost */ XtInheritSetValuesAlmost, + /* get_values_hook */ NULL, + /* accept_focus */ NULL, + /* version */ XtVersion, + /* callback_private */ NULL, + /* tm_table */ translations, + /* query_geometry */ XtInheritQueryGeometry, + /* display_accelerator */ XtInheritDisplayAccelerator, + /* extension */ NULL + }, + { /* ximpprotocol fields */ + /* ximp_dummy */ 0 + } +}; + +WidgetClass ximpProtocolWidgetClass = (WidgetClass)&ximpProtocolClassRec; + +static XimpInputStyle XimpStyles[] = { + { XIMPreeditPosition|XIMStatusArea, overthespot_style }, + { XIMPreeditPosition|XIMStatusNothing, overthespot_style }, + { XIMPreeditArea|XIMStatusArea, offthespot_style }, + { XIMPreeditCallbacks|XIMStatusCallbacks, onthespot_style }, + { XIMPreeditCallbacks|XIMStatusNothing, onthespot_style }, + { XIMPreeditNothing|XIMStatusNothing, separate_style }, + { 0 }, +}; + +/* + *+ Core class methods + */ + +/*- ClassInitialize: set supported locale list -*/ +static void +ClassInitialize() +{ +} + +/*- Initialize: intern Atoms, get default fonts, etc. -*/ +/* ARGSUSED */ +static void +Initialize(req, new, args, num_args) +Widget req; +Widget new; +ArgList args; +Cardinal *num_args; +{ + XimpProtocolWidget xpw = (XimpProtocolWidget)new; + + if (xpw->ximp.localename == NULL) { + initializeError(new, XtNlocaleName); + } else if (xpw->ximp.inputObjClass == NULL) { + initializeError(new, XtNinputObjectClass); + } else if (xpw->ximp.displayObjClass == NULL) { + initializeError(new, XtNdisplayObjectClass); + } + + checkLocale(xpw, xpw->ximp.localename); + xpw->ximp.localename = XtNewString(xpw->ximp.localename); + + xpw->ximp.servername = XtNewString(xpw->ximp.servername); + + xpw->ximp.clients = NULL; + xpw->ximp.freeclients = NULL; + xpw->ximp.icid = 1; + xpw->ximp.propid = 0; + xpw->ximp.callbackpropid = 0; + + if (xpw->ximp.defaultfontlist != NULL) { + TRACE(("enter default fontlist <%s> into cache\n", xpw->ximp.defaultfontlist)); + /* extract fonts from default font list and enter them into cache */ + xpw->ximp.defaultfontlist = XtNewString(xpw->ximp.defaultfontlist); + xpw->ximp.deffonts = FontBankGet(xpw->ximp.fontbank, + xpw->ximp.defaultfontlist, + &xpw->ximp.numdeffonts); + } else { + xpw->ximp.deffonts = NULL; + xpw->ximp.numdeffonts = 0; + } + + getAtoms(xpw); +} + +/*- Destroy: free allocated memory -*/ +static void +Destroy(w) +Widget w; +{ + XimpProtocolWidget xpw = (XimpProtocolWidget)w; + ConvClient *client; + + XtFree(xpw->ximp.localename); + XtFree(xpw->ximp.servername); + if (xpw->ximp.defaultfontlist != NULL) XtFree(xpw->ximp.defaultfontlist); + + while (xpw->ximp.clients != NULL) { + statusDone(xpw->ximp.clients); + endProc(xpw->ximp.clients, False); + deleteClient(xpw->ximp.clients); + /* + * since deleteClient() removes the given client from client list + * and insert it in free list, following commented statement is + * not necessary. + * + * xpw->ximp.clients = xpw->ximp.clients->next; + */ + } + + /* + * now, all the clients are deleted and moved into free list. + */ + client = xpw->ximp.freeclients; + while (client != NULL) { + ConvClient *ccp = client; + client = client->next; + XtFree((char *)ccp); + } + + /* + * free cached fontlist + */ + if (xpw->ximp.numdeffonts > 0) { + FontBankFreeFonts(xpw->ximp.fontbank, + xpw->ximp.deffonts, + xpw->ximp.numdeffonts); + } + FontBankDestroy(xpw->ximp.fontbank); +} + +/*- Realize: own selection -*/ +static void +Realize(w, mask, value) +Widget w; +XtValueMask *mask; +XSetWindowAttributes *value; +{ + XimpProtocolWidget xpw = (XimpProtocolWidget)w; + CoreWidgetClass super = (CoreWidgetClass)XtClass(w)->core_class.superclass; + + (*super->core_class.realize)(w, mask, value); + + setProperty(xpw); + + if (!ownSelection(xpw)) { + String params[1]; + Cardinal num_params; + + params[0] = XtClass(w)->core_class.class_name; + num_params = 1; + XtAppWarningMsg(XtWidgetToApplicationContext(w), + "selectionError", "ownSelection", "WidgetError", + "%s: can't own selection", params, &num_params); + + XtDestroyWidget(w); + } else { + DPRINT(("\tselection owner: 0x%lx (%ld)\n", XtWindow(w), XtWindow(w))); + } +} + +/* + *+ atom handling + */ + +/*- getAtoms: intern atoms -*/ +static void +getAtoms(xpw) +XimpProtocolWidget xpw; +{ + Display *dpy = XtDisplay((Widget)xpw); + char buf[256]; + +#define MAKEATOM(s) XInternAtom(dpy, s, False) + (void)sprintf(buf, "_XIMP_%s", xpw->ximp.localename); + xpw->ximp.selAtom1 = MAKEATOM(buf); + (void)sprintf(buf, "_XIMP_%s@%s.%d", + xpw->ximp.localename, + xpw->ximp.servername, + DefaultScreen(XtDisplay((Widget)xpw))); + xpw->ximp.selAtom2 = MAKEATOM(buf); + + xpw->ximp.ctextAtom = XA_COMPOUND_TEXT(dpy); + + xpw->ximp.ximpVersionAtom = MAKEATOM("_XIMP_VERSION"); + xpw->ximp.ximpStyleAtom = MAKEATOM("_XIMP_STYLE"); + xpw->ximp.ximpKeysAtom = MAKEATOM("_XIMP_KEYS"); + xpw->ximp.ximpServerNameAtom = MAKEATOM("_XIMP_SERVERNAME"); + xpw->ximp.ximpServerVersionAtom = MAKEATOM("_XIMP_SERVERVERSION"); + xpw->ximp.ximpVendorNameAtom = MAKEATOM("_XIMP_VENDORNAME"); + xpw->ximp.ximpExtensionsAtom = MAKEATOM("_XIMP_EXTENSIONS"); + xpw->ximp.ximpProtocolAtom = MAKEATOM("_XIMP_PROTOCOL"); + xpw->ximp.ximpFocusAtom = MAKEATOM("_XIMP_FOCUS"); + xpw->ximp.ximpPreeditAtom = MAKEATOM("_XIMP_PREEDIT"); + xpw->ximp.ximpStatusAtom = MAKEATOM("_XIMP_STATUS"); + xpw->ximp.ximpPreeditFontAtom = MAKEATOM("_XIMP_PREEDITFONT"); + xpw->ximp.ximpStatusFontAtom = MAKEATOM("_XIMP_STATUSFONT"); + xpw->ximp.ximpExtXimpBackFrontAtom = MAKEATOM("_XIMP_EXT_XIMP_BACK_FRONT"); + xpw->ximp.ximpPreeditDrawDataAtom = MAKEATOM("_XIMP_PREEDIT_DRAW_DATA"); + xpw->ximp.ximpFeedbacksAtom = MAKEATOM("_XIMP_FEEDBACKS"); + +#undef MAKEATOM +} + +/*- ownSelection: own conversion selection -*/ +static int +ownSelection(xpw) +XimpProtocolWidget xpw; +{ + Display *dpy = XtDisplay((Widget)xpw); + Window w = XtWindow((Widget)xpw); + + TRACE(("ximpProtocol:ownSelection()\n")); + + if (xpw->ximp.forceDefaultServer || + XGetSelectionOwner(dpy, xpw->ximp.selAtom1) == None) { + TRACE(("\tdefault server\n")); + XSetSelectionOwner(dpy, xpw->ximp.selAtom1, w, CurrentTime); + } + TRACE(("\tspecific server\n")); + XSetSelectionOwner(dpy, xpw->ximp.selAtom2, w, CurrentTime); + + return XGetSelectionOwner(dpy, xpw->ximp.selAtom2) == w; +} + +/* + *+ client data handling + */ + +/*- findClient: get clientdata of given client -*/ +static ConvClient * +findClient(xpw, id) +XimpProtocolWidget xpw; +int id; +{ + register ConvClient *ccp = xpw->ximp.clients; + + while (ccp != NULL) { + if (ccp->id == id) return ccp; + ccp = ccp->next; + } + + return NULL; +} + +/*- newClient: get a clientdata for new client -*/ +static ConvClient * +newClient(xpw, client) +XimpProtocolWidget xpw; +Window client; +{ + ConvClient *ccp; + + if (xpw->ximp.freeclients != NULL) { + /* get one from free list */ + ccp = xpw->ximp.freeclients; + xpw->ximp.freeclients = ccp->next; + } else { + char buf[30]; + + ccp = XtNew(ConvClient); + (void)sprintf(buf, "_XIMP_STRING_%d", xpw->ximp.propid++); + ccp->property = XInternAtom(XtDisplay((Widget)xpw), buf, False); + (void)sprintf(buf, "_XIMP_CALLBACKS_%d", xpw->ximp.callbackpropid++); + ccp->preeditdata = XInternAtom(XtDisplay((Widget)xpw), buf, False); + (void)sprintf(buf, "_XIMP_CALLBACKS_%d", xpw->ximp.callbackpropid++); + ccp->preedittext = XInternAtom(XtDisplay((Widget)xpw), buf, False); + (void)sprintf(buf, "_XIMP_CALLBACKS_%d", xpw->ximp.callbackpropid++); + ccp->preeditfeedback = XInternAtom(XtDisplay((Widget)xpw), buf, False); + (void)sprintf(buf, "_XIMP_CALLBACKS_%d", xpw->ximp.callbackpropid++); + ccp->statustext = XInternAtom(XtDisplay((Widget)xpw), buf, False); + (void)sprintf(buf, "_XIMP_CALLBACKS_%d", xpw->ximp.callbackpropid++); + ccp->statusfeedback = XInternAtom(XtDisplay((Widget)xpw), buf, False); + } + ccp->id = xpw->ximp.icid++; + + ccp->version = NULL; + ccp->style = separate_style; /* default */ + ccp->protocolwidget = (Widget)xpw; + ccp->conversion = NULL; + ccp->reqwin = client; + ccp->focuswin = client; /* default */ + ccp->xpattrs.fontlist = NULL; + ccp->xsattrs.fontlist = NULL; + ccp->xattrmask = 0L; + ccp->defaultsfilledin = False; + ccp->esm = ESMethodSelectFocus; /* default */ + ccp->fonts = NULL; + ccp->num_fonts = 0; + ccp->status_fonts = NULL; + ccp->num_status_fonts = 0; + ccp->resetting = False; + ccp->event = NULL; + ccp->ximstyle = XIMPreeditNothing|XIMStatusNothing; /* default */ + ccp->in_preedit = False; + ccp->in_status = False; + + ccp->next = xpw->ximp.clients; + xpw->ximp.clients = ccp; + + return ccp; +} + +/*- attachConverter: attach converter to the client -*/ +static Widget +attachConverter(ccp) +ConvClient *ccp; +{ + WidgetClass class; + XimpProtocolWidget xpw = (XimpProtocolWidget)ccp->protocolwidget; + + TRACE(("attachConverter(client window=0x%lx)\n", ccp->reqwin)); + if (ccp->conversion != NULL) return ccp->conversion; + + if (ccp->style == overthespot_style) { + class = overTheSpotConversionWidgetClass; + } else if (ccp->style == onthespot_style) { + class = onTheSpotConversionWidgetClass; + } else if (ccp->style == offthespot_style) { + class = offTheSpotConversionWidgetClass; + } else { + class = separateConversionWidgetClass; + } + + ccp->conversion = CMGetConverter(XtParent(ccp->protocolwidget), + ccp->reqwin, class, + xpw->ximp.inputObjClass, + xpw->ximp.displayObjClass); + + return ccp->conversion; +} + +/*- detachConverter: detach converter from client -*/ +static void +detachConverter(ccp) +ConvClient *ccp; +{ + TRACE(("detachConverter(client window=0x%lx)\n", ccp->reqwin)); + + XtRemoveCallback(ccp->conversion, XtNtextCallback, + fixCallback, (XtPointer)ccp); + XtRemoveCallback(ccp->conversion, XtNendCallback, + endCallback, (XtPointer)ccp); + XtRemoveCallback(ccp->conversion, XtNunusedEventCallback, + unusedEventCallback, (XtPointer)ccp); + if (ccp->style == onthespot_style) { + XtRemoveCallback(ccp->conversion, XtNpreeditStartCallback, + preeditStartCallback, (XtPointer)ccp); + XtRemoveCallback(ccp->conversion, XtNpreeditDoneCallback, + preeditDoneCallback, (XtPointer)ccp); + XtRemoveCallback(ccp->conversion, XtNpreeditDrawCallback, + preeditDrawCallback, (XtPointer)ccp); + XtRemoveCallback(ccp->conversion, XtNpreeditCaretCallback, + preeditCaretCallback, (XtPointer)ccp); + XtRemoveCallback(ccp->conversion, XtNstatusStartCallback, + statusStartCallback, (XtPointer)ccp); + XtRemoveCallback(ccp->conversion, XtNstatusDoneCallback, + statusDoneCallback, (XtPointer)ccp); + XtRemoveCallback(ccp->conversion, XtNstatusDrawCallback, + statusDrawCallback, (XtPointer)ccp); + } + + CMReleaseConverter(XtParent(ccp->protocolwidget), ccp->conversion); + ccp->conversion = NULL; +} + +/*- deleteClient: delete specified client -*/ +static void +deleteClient(client) +ConvClient *client; +{ + XimpProtocolWidget xpw = (XimpProtocolWidget)client->protocolwidget; + ConvClient *ccp, *ccp0; + + TRACE(("deleteClient(client window=0x%lx)\n", client->reqwin)); + + if (client->conversion != NULL) detachConverter(client); + if (client->num_fonts > 0) { + FontBankFreeFonts(xpw->ximp.fontbank, + client->fonts, client->num_fonts); + } + if (client->num_status_fonts > 0) { + FontBankFreeFonts(xpw->ximp.fontbank, + client->status_fonts, client->num_status_fonts); + } + if (client->xpattrs.fontlist != NULL && + client->xpattrs.fontlist != xpw->ximp.defaultfontlist) { + XFree(client->xpattrs.fontlist); + } + if (client->xsattrs.fontlist != NULL) { + XFree(client->xsattrs.fontlist); + } + if (client->version != NULL) XtFree(client->version); + + for (ccp = xpw->ximp.clients, ccp0 = NULL; + ccp != NULL; + ccp0 = ccp, ccp = ccp->next) { + if (ccp == client) { + if (ccp0 == NULL) { + xpw->ximp.clients = ccp->next; + } else { + ccp0->next = ccp->next; + } + /* put it back to free list */ + client->next = xpw->ximp.freeclients; + xpw->ximp.freeclients = client; + return; + } + } + DPRINT(("deleteClient: cannot find the client in the client list!\n")); +} + +/* + *+ utility functions + */ + +/*- isCorrectClientEvent: is the event in correct format? -*/ +static Boolean +isCorrectClientEvent(xpw, event) +XimpProtocolWidget xpw; +XEvent *event; +{ + XClientMessageEvent *ev = &(event->xclient); + + return (event->type == ClientMessage && + ev->window == XtWindow((Widget)xpw) && + ev->message_type == xpw->ximp.ximpProtocolAtom && + ev->format == 32); +} + +/*- isCorrectWindowID: is the given window ID valid? -*/ +static Boolean +isCorrectWindowID(w, window, widthp, heightp) +Widget w; +Window window; +Dimension *widthp; +Dimension *heightp; +{ + XWindowAttributes attr; + int status; + XAEHandle h; + + h = XAESetIgnoreErrors(XtDisplay(w)); + status = XGetWindowAttributes(XtDisplay(w), window, &attr); + XAEUnset(h); + + if (status == 0) return False; + + if (widthp != NULL) *widthp = attr.width; + if (heightp != NULL) *heightp = attr.height; + return True; +} + +/*- initializeError: display error message when resource isn't specified -*/ +static void +initializeError(w, resname) +Widget w; +String resname; +{ + String params[2]; + Cardinal num_params; + + params[0] = XtClass(w)->core_class.class_name; + params[1] = resname; + num_params = 2; + XtAppErrorMsg(XtWidgetToApplicationContext(w), + "initializeError", "noResource", "WidgetError", + "%s: resource %s must be specified at widget creation", + params, &num_params); +} + +/*- checkLocale: check specified locale is supported -*/ +static void +checkLocale(xpw, name) +XimpProtocolWidget xpw; +String name; +{ + XimpProtocolWidgetClass class = (XimpProtocolWidgetClass)XtClass((Widget)xpw); + FontBank fontbank; + + TRACE(("checkLocale(localename=%s)\n", name)); + + fontbank = FontBankCreate(XtDisplay((Widget)xpw), name); + if (fontbank == NULL) { + /* not supported locale name */ + String params[2]; + Cardinal num_params; + + params[0] = class->core_class.class_name; + params[1] = name; + num_params = 2; + XtAppErrorMsg(XtWidgetToApplicationContext((Widget)xpw), + "initializeError", "localeNotSupported", "WidgetError", + "%s: specified locale %s is not supported (yet)", + params, &num_params); + } else { + xpw->ximp.fontbank = fontbank; + } +} + +/*- fillInDefaultAttributes: fill in unspecified attributes -*/ +static void +fillInDefaultAttributes(client) +ConvClient *client; +{ + XimpProtocolWidget xpw = (XimpProtocolWidget)client->protocolwidget; + XimpPreEditAttributes *xpattr = &client->xpattrs; + XimpStatusAttributes *xsattr = &client->xsattrs; + unsigned long xmask = client->xattrmask; + + if (client->defaultsfilledin) return; + + /* + * Compute reasonable default values for the unspecified + * attributes except Area and AreaNeeded. + */ + TRACE(("fillInDefaultAttributes()\n")); + if (!(xmask & XIMP_FOCUS_WIN_MASK)) { + client->focuswin = client->reqwin; + client->focuswidth = client->reqwinwidth; + client->focusheight = client->reqwinheight; + } + + if (!(xmask & XIMP_PRE_FONT_MASK)) { + xpattr->fontlist = xpw->ximp.defaultfontlist; + getFonts(client, xpattr->fontlist, 1); + client->xattrmask |= XIMP_PRE_FONT_MASK; + } + if (!(xmask & XIMP_STS_FONT_MASK)) { + /* Default is same as preedit font */ + xsattr->fontlist = XtNewString(xpattr->fontlist); + getFonts(client, xsattr->fontlist, 0); + client->xattrmask |= XIMP_STS_FONT_MASK; + } + + if (!(xmask & XIMP_PRE_FG_MASK)) { + xpattr->foreground = xpw->ximp.foreground; + } + if (!(xmask & XIMP_PRE_BG_MASK)) { + xpattr->background = xpw->core.background_pixel; + } + if (!(xmask & XIMP_PRE_COLORMAP_MASK)) { + xpattr->colormap = xpw->core.colormap; + } + if (!(xmask & XIMP_PRE_BGPIXMAP_MASK)) { + xpattr->bgpixmap = None; + } + if (!(xmask & XIMP_PRE_LINESP_MASK)) { + Cardinal i; + XFontStruct *font; + int maxascent = 0, maxdescent = 0; + + for (i = 0; i < client->num_fonts; i++) { + font = client->fonts[i]; + if (maxascent < font->ascent) maxascent = font->ascent; + if (maxdescent < font->descent) maxdescent = font->descent; + } + xpattr->linespacing = maxascent + maxdescent; + } + if (!(xmask & XIMP_PRE_CURSOR_MASK)) { + xpattr->cursor = None; /* ie use parent's cursor */ + } + if (!(xmask & XIMP_PRE_SPOTL_MASK)) { + xpattr->spotx = xpattr->spoty = 0; + } + if (!(xmask & XIMP_STS_FG_MASK)) { + xsattr->foreground = xpattr->foreground; + } + if (!(xmask & XIMP_STS_BG_MASK)) { + xsattr->background = xpattr->background; + } + if (!(xmask & XIMP_STS_COLORMAP_MASK)) { + xsattr->colormap = xpattr->colormap; + } + if (!(xmask & XIMP_STS_BGPIXMAP_MASK)) { + xsattr->bgpixmap = xpattr->bgpixmap; + } + if (!(xmask & XIMP_STS_LINESP_MASK)) { + xsattr->linespacing = xpattr->linespacing; + } + if (!(xmask & XIMP_STS_CURSOR_MASK)) { + xsattr->cursor = xpattr->cursor; + } + if (!(xmask & XIMP_STS_WINDOW_MASK)) { + xsattr->statuswin = None; + } + + client->defaultsfilledin = True; +} + +/*- computeAreaForStartup: compute Area for conversion startup -*/ +static void +computeAreaForStartup(client) +ConvClient *client; +{ + XimpPreEditAttributes *xpattr = &client->xpattrs; + XimpStatusAttributes *xsattr = &client->xsattrs; + unsigned long mask = client->xattrmask; + + TRACE(("computeAreaForStartup(client=0x%lx)\n", client->reqwin)); + + if (client->style == separate_style || + client->style == onthespot_style || + client->style == overthespot_style) { + /* + * These styles don't need status nor preedit area. + * The separate style simpley ignores them, and the + * over-the-spot style uses default value if not specified. + */ + return; + } + + if ((mask & XIMP_STS_AREA_MASK) && (mask & XIMP_PRE_AREA_MASK)) return; + + /* + * Compute default status/pre-edit area based on the AreaNeeded values. + */ + computeAreaForQuery(client); + + if (!(mask & XIMP_STS_AREA_MASK)) { + xsattr->areax = 0; + xsattr->areay = client->reqwinheight - xsattr->neededheight; + xsattr->areawidth = xsattr->neededwidth; + xsattr->areaheight = xsattr->neededheight; + } + if (!(mask & XIMP_PRE_AREA_MASK)) { + xpattr->areax = xsattr->areax + xsattr->areawidth; + xpattr->areay = client->reqwinheight - xpattr->neededheight; + xpattr->areawidth = xpattr->neededwidth; + xpattr->areaheight = xpattr->neededheight; + } +} + +/*- computeAreaForQuery: compute Area and AreaNeeded for query from clients -*/ +static void +computeAreaForQuery(client) +ConvClient *client; +{ + XimpProtocolWidget xpw = (XimpProtocolWidget)client->protocolwidget; + XimpPreEditAttributes *xpattr = &client->xpattrs; + XimpStatusAttributes *xsattr = &client->xsattrs; + unsigned long mask = client->xattrmask; + int width, height; + int maxwidth, maxheight; + int fontheight; + + TRACE(("computeAreaForQuery(client=0x%lx)\n", client->reqwin)); + + if (client->style == overthespot_style || + client->style == offthespot_style) { + + /* + * Get the size of the request window again. + * The size was checked when XIMP_CREATE message was received, + * but it is possible that the size has changed since then. + */ + (void)isCorrectWindowID(client->protocolwidget, client->reqwin, + &client->reqwinwidth, &client->reqwinheight); + } + + /* + * Compute the dimensions of the status region. + */ + if (client->style == overthespot_style || + client->style == offthespot_style) { + + /* + * Compute AreaNeeded value. + */ + fontheight = xsattr->linespacing + 2; + maxwidth = maxheight = 0; + if (mask & XIMP_STS_AREANEED_MASK) { + maxwidth = xsattr->neededwidth; + maxheight = xsattr->neededheight; + TRACE(("\tstatus areaNeeded was: (%d,%d)\n", maxwidth, maxheight)); + } + + if (xpw->ximp.statuswidth > 0) { + width = xpw->ximp.statuswidth; + } else { + width = client->reqwinwidth / 5; /* wild guess */ + if (width < fontheight * 3) { + width = fontheight * 3; /* another wild guess */ + } + } + height = fontheight; + + if (maxwidth > 0 && width > maxwidth) width = maxwidth; + if (maxheight > 0 && height > maxheight) height = maxheight; + if (width < MIN_AREA_WIDTH) width = MIN_AREA_WIDTH; + if (height < MIN_AREA_HEIGHT) height = MIN_AREA_HEIGHT; + + xsattr->neededwidth = width; + xsattr->neededheight = height; + TRACE(("\tstatus areaNeeded is now: (%d, %d)\n", width, height)); + + /* + * If client has not specified the status area yet, + * supply default value. + */ + if (!(mask & XIMP_STS_AREA_MASK)) { + xsattr->areax = 0; + xsattr->areay = client->reqwinheight - xsattr->neededheight; + xsattr->areawidth = xsattr->neededwidth; + xsattr->areaheight = xsattr->neededheight; + } + } + + /* + * Compute the dimensions of the pre-edit region. + */ + if (client->style == offthespot_style) { + /* + * Compute AreaNeeded value. + */ + fontheight = xpattr->linespacing + 2; + maxwidth = maxheight = 0; + if (mask & XIMP_PRE_AREANEED_MASK) { + maxwidth = xpattr->neededwidth; + maxheight = xpattr->neededheight; + TRACE(("\tpreedit areaNeeded was: (%d,%d)\n", maxwidth, maxheight)); + } + + width = client->reqwinwidth - xsattr->neededwidth; + height = fontheight; + + if (maxwidth > 0 && width > maxwidth) width = maxwidth; + if (maxheight > 0 && height > maxheight) height = maxheight; + if (width < MIN_AREA_WIDTH) width = MIN_AREA_WIDTH; + if (height < MIN_AREA_HEIGHT) height = MIN_AREA_HEIGHT; + + xpattr->neededwidth = width; + xpattr->neededheight = height; + TRACE(("\tpreedit areaNeeded is now: (%d, %d)\n", width, height)); + + /* + * If client has not specified the status area yet, + * supply default value. + */ + if (!(mask & XIMP_PRE_AREA_MASK)) { + xpattr->areax = xsattr->neededwidth; + xpattr->areay = client->reqwinheight - xpattr->neededheight; + xpattr->areawidth = xpattr->neededwidth; + xpattr->areaheight = xpattr->neededheight; + } + } else if (client->style == overthespot_style) { + /* + * No need to calculate AreaNeeded value, which is + * ignored by the client. Just calculate default + * Area if not specified. + */ + if (!(mask & XIMP_PRE_AREA_MASK)) { + xpattr->areax = 0; + xpattr->areay = 0; + xpattr->areawidth = client->focuswidth; + xpattr->areaheight = client->focusheight; + } + } +} + +/*- makeConvAttributes: -*/ +static unsigned long +makeConvAttributes(client, attr) +ConvClient *client; +ConversionAttributes *attr; +{ + XimpPreEditAttributes *xpattr = &client->xpattrs; + XimpStatusAttributes *xsattr = &client->xsattrs; + unsigned long xmask = client->xattrmask; + unsigned long mask; + + TRACE(("makeConvAttributes()\n")); + mask = 0L; + + /* focus window */ + attr->focuswindow = client->focuswin; + mask |= CAFocusWindow; + + if (client->style == overthespot_style || + client->style == offthespot_style) { + + /* client area */ + if (client->style == offthespot_style || + (xmask & XIMP_PRE_AREA_MASK)) { + attr->clientarea.x = xpattr->areax; + attr->clientarea.y = xpattr->areay; + attr->clientarea.width = xpattr->areawidth; + attr->clientarea.height = xpattr->areaheight; + mask |= CAClientArea; + } + + /* foreground/background */ + attr->foreground = xpattr->foreground; + attr->background = xpattr->background; + mask |= CAColor; + + /* colormap */ + if (xmask & XIMP_PRE_COLORMAP_MASK) { + attr->colormap = xpattr->colormap; + mask |= CAColormap; + } + + /* background pixmap */ + if (xmask & XIMP_PRE_BGPIXMAP_MASK) { + attr->background_pixmap = xpattr->bgpixmap; + mask |= CABackgroundPixmap; + } + + /* line spacing */ + if (xmask & XIMP_PRE_LINESP_MASK) { + attr->linespacing = xpattr->linespacing; + mask |= CALineSpacing; + } + + /* cursor */ + if (xmask & XIMP_PRE_CURSOR_MASK) { + attr->cursor = xpattr->cursor; + mask |= CACursor; + } + + /* status area */ + /* offTheSpotConversion doesn't allow status area left unspecified */ + if (client->style == offthespot_style || + (xmask & XIMP_STS_AREA_MASK)) { + attr->statusarea.x = xsattr->areax; + attr->statusarea.y = xsattr->areay; + attr->statusarea.width = xsattr->areawidth; + attr->statusarea.height = xsattr->areaheight; + mask |= CAStatusArea; + } + + /* font */ + attr->fonts = client->fonts; + attr->num_fonts = client->num_fonts; + attr->status_fonts = client->status_fonts; + attr->num_status_fonts = client->num_status_fonts; + mask |= CAFonts|CAStatusFonts; + } + + if (client->style == overthespot_style) { + /* spot location */ + if (xmask & XIMP_PRE_SPOTL_MASK) { + attr->spotx = xpattr->spotx; + attr->spoty = xpattr->spoty; + mask |= CASpotLocation; + } + } + return mask; +} + +/*- getFonts: get fonts from specified fontnamelist -*/ +static void +getFonts(client, fontnamelist, preedit) +ConvClient *client; +String fontnamelist; +int preedit; +{ + XimpProtocolWidget xpw = (XimpProtocolWidget)client->protocolwidget; + XFontStruct **fonts; + int num_fonts; + + TRACE(("getFonts(%s)\n", preedit ? "preedit" : "status")); + TRACE(("\tfontnamelist: %s\n", fontnamelist)); + + fonts = FontBankGet(xpw->ximp.fontbank, fontnamelist, &num_fonts); + if (preedit) { + client->fonts = fonts; + client->num_fonts = num_fonts; + } else { + client->status_fonts = fonts; + client->num_status_fonts = num_fonts; + } +} + + +/* + *+ property handling + */ + +/*- setProperty: set information properties -*/ +static void +setProperty(xpw) +XimpProtocolWidget xpw; +{ + Display *dpy = XtDisplay((Widget)xpw); + Window win = XtWindow((Widget)xpw); + XimpInputStyle *xisp; + unsigned long styles[20]; + Cardinal nstyles; + unsigned long extensions[10]; + Cardinal nextensions; + + TRACE(("setProperty()\n")); + +#define SETPROPERTY(p, t, f, d, n) \ + XChangeProperty(dpy, win, p, t, f, PropModeReplace, (unsigned char *)d, n) + + SETPROPERTY(xpw->ximp.ximpVersionAtom, XA_STRING, 8, + PROTOCOL_VERSION_STR, strlen(PROTOCOL_VERSION_STR)); + SETPROPERTY(xpw->ximp.ximpServerNameAtom, XA_STRING, 8, + SERVER_NAME, strlen(xpw->ximp.servername)); + SETPROPERTY(xpw->ximp.ximpServerVersionAtom, XA_STRING, 8, + SERVER_VERSION, strlen(SERVER_VERSION)); + SETPROPERTY(xpw->ximp.ximpVendorNameAtom, XA_STRING, 8, + VENDOR_NAME, strlen(VENDOR_NAME)); + + for (xisp = XimpStyles, nstyles = 0; xisp->ximstyle != 0; + xisp++, nstyles++) { + styles[nstyles] = xisp->ximstyle; + } + SETPROPERTY(xpw->ximp.ximpStyleAtom, xpw->ximp.ximpStyleAtom, 32, + styles, nstyles); + + nextensions = 0; + extensions[nextensions++] = xpw->ximp.ximpExtXimpBackFrontAtom; + SETPROPERTY(xpw->ximp.ximpExtensionsAtom, xpw->ximp.ximpExtensionsAtom, 32, + extensions, nextensions); + + setKeyProperty(xpw); + +#undef SETPROPERTY +} + +/*- setKeyProperty: set _XIM_KEYS property -*/ +static void +setKeyProperty(xpw) +XimpProtocolWidget xpw; +{ + long data[100]; /* enough */ + char line[256]; /* enough */ + Display *dpy = XtDisplay((Widget)xpw); + int nkeys = 0; + String p, q; + int c, n; + ICTriggerKey *keys, *ekeys; + + if ((p = xpw->ximp.convkeys) != NULL) { + TRACE(("setKeyProperty(%s)\n", p)); + do { + KeySym keysym; + long mods, chk_mods; + + q = line; + while ((c = *p++) != '\0' && c != '\n') { + *q++ = c; + } + *q = '\0'; + if (ParseKeyEvent(line, &keysym, &mods, &chk_mods)) { + data[nkeys * 3] = mods; + data[nkeys * 3 + 1] = chk_mods; + data[nkeys * 3 + 2] = keysym; + nkeys++; + } + } while (c != '\0'); + } + + n = ICGetTriggerKeysOfInputObjectClass(xpw->ximp.inputObjClass, &keys); + for (ekeys = keys + n ; + keys < ekeys && nkeys < (sizeof(data) / sizeof(long)) / 3 ; keys++) { + data[nkeys * 3] = keys->modifiers; + data[nkeys * 3 + 1] = keys->modifiermask; + data[nkeys * 3 + 2] = keys->keysym; + nkeys++; + } + + XChangeProperty(dpy, XtWindow((Widget)xpw), xpw->ximp.ximpKeysAtom, + xpw->ximp.ximpKeysAtom, 32, PropModeReplace, + (unsigned char *)data, nkeys * 3); +} + +/*- getVersionProperty: get _XIMP_VERSION property -*/ +static void +getVersionProperty(client) +ConvClient *client; +{ + XimpProtocolWidget xpw = (XimpProtocolWidget)client->protocolwidget; + String data; + unsigned long len; + + TRACE(("getVersionProperty()\n")); + if (!readProperty(client, xpw->ximp.ximpVersionAtom, XA_STRING, 8, + (unsigned char **)&data, &len)) { + DPRINT(("can't read _XIMP_VERSION property\n")); + client->version = NULL; + return; + } + TRACE(("\tclient version is %s\n", data)); + + /* what to do? */ + + client->version = data; +} + +/*- getAttributes: read properties and set conversion attributes -*/ +static void +getAttributes(client, mask) +ConvClient *client; +unsigned long mask; +{ + if (mask & XIMP_FOCUS_WIN_MASK) { + getFocusProperty(client); + } + if (mask & XIMP_PRE_FONT_MASK) { + getPreeditFontProperty(client); + } + if (mask & XIMP_STS_FONT_MASK) { + getStatusFontProperty(client); + } + if (mask & PREEDIT_MASK) { + getPreeditProperty(client, mask & PREEDIT_MASK); + } + if (mask & STATUS_MASK) { + getStatusProperty(client, mask & STATUS_MASK); + } +} + +/*- getFocusProperty: get _XIMP_FOCUS property -*/ +static void +getFocusProperty(client) +ConvClient *client; +{ + XimpProtocolWidget xpw = (XimpProtocolWidget)client->protocolwidget; + unsigned char *data; + unsigned long len; + Window focus; + Dimension w, h; + + TRACE(("getFocusProperty()\n")); + if (!readProperty(client, xpw->ximp.ximpFocusAtom, XA_WINDOW, 32, + &data, &len)) { + DPRINT(("can't read _XIMP_FOCUS property\n")); + return; + } else if (len != 1) { + DPRINT(("length of _XIMP_FOCUS property is not 1\n")); + XtFree((char *)data); + return; + } + + focus = *(Window *)data; + XtFree((char *)data); + TRACE(("\tfocus window=0x%lx\n", focus)); + + if (!isCorrectWindowID((Widget)xpw, focus, &w, &h)) { + DPRINT(("specified focus window doesn't exist\n")); + sendErrorEvent(client, XIMP_BadFocusWindow); + return; + } + + client->focuswin = focus; + client->focuswidth = w; + client->focusheight = h; + client->xattrmask |= XIMP_FOCUS_WIN_MASK; +} + +/*- getPreeditFontProperty: get _XIMP_PREEDITFONT property -*/ +static void +getPreeditFontProperty(client) +ConvClient *client; +{ + XimpProtocolWidget xpw = (XimpProtocolWidget)client->protocolwidget; + char *data; + unsigned long len; + + TRACE(("getPreeditFontProperty()\n")); + if (!readProperty(client, xpw->ximp.ximpPreeditFontAtom, XA_STRING, 8, + (unsigned char **)&data, &len)) { + DPRINT(("can't read _XIMP_PREEDITFONT property\n")); + return; + } + + if (client->xpattrs.fontlist != NULL) { + if (!strcmp(data, client->xpattrs.fontlist)) { + XtFree(data); + return; + } + if (client->xpattrs.fontlist != xpw->ximp.defaultfontlist) { + XtFree(client->xpattrs.fontlist); + } + } + client->xpattrs.fontlist = data; + client->xattrmask |= XIMP_PRE_FONT_MASK; + + /* extract fonts to be used */ + getFonts(client, data, 1); +} + +/*- getStatusFontProperty: get _XIMP_STATUSFONT property -*/ +static void +getStatusFontProperty(client) +ConvClient *client; +{ + XimpProtocolWidget xpw = (XimpProtocolWidget)client->protocolwidget; + unsigned char *data; + unsigned long len; + + TRACE(("getStatusFontProperty()\n")); + if (!readProperty(client, xpw->ximp.ximpStatusFontAtom, XA_STRING, 8, + &data, &len)) { + DPRINT(("can't read _XIMP_STATUSFONT property\n")); + return; + } + + if (client->xsattrs.fontlist != NULL) { + if (!strcmp(data, client->xsattrs.fontlist)) { + XtFree(data); + return; + } + if (client->xsattrs.fontlist != xpw->ximp.defaultfontlist) { + XtFree(client->xsattrs.fontlist); + } + } + client->xsattrs.fontlist = (String)data; + client->xattrmask |= XIMP_STS_FONT_MASK; + + /* extract fonts to be used */ + getFonts(client, data, 0); +} + +/*- getPreeditProperty: get _XIMP_PREEDIT property -*/ +static void +getPreeditProperty(client, mask) +ConvClient *client; +unsigned long mask; +{ + XimpProtocolWidget xpw = (XimpProtocolWidget)client->protocolwidget; + XimpPreEditAttributes *xpattr = &client->xpattrs; + unsigned long *data; + unsigned long len; + + TRACE(("getPreeditProperty()\n")); + if (!readProperty(client, xpw->ximp.ximpPreeditAtom, + xpw->ximp.ximpPreeditAtom, 32, + (unsigned char **)&data, &len)) { + DPRINT(("can't read _XIMP_PREEDIT property\n")); + return; + } else if (len < 14) { + DPRINT(("length of _XIMP_PREEDIT property is less than 14(%ld)\n",len)); + XtFree((char *)data); + return; + } + + client->xattrmask |= mask; + + /* data[0]-data[3]: Area.{x,y,width,height} */ + if (mask & XIMP_PRE_AREA_MASK) { + xpattr->areax = data[0]; + xpattr->areay = data[1]; + xpattr->areawidth = data[2]; + xpattr->areaheight = data[3]; + if (xpattr->areawidth == 0 || xpattr->areaheight == 0) { + client->xattrmask &= ~XIMP_PRE_AREA_MASK; + DPRINT(("invalid area specified:\n")); + } + TRACE(("\tArea: (%ld,%ld)-(%ld,%ld)\n",data[0],data[1],data[2],data[3])); + } + /* data[4]: Foreground */ + if (mask & XIMP_PRE_FG_MASK) { + xpattr->foreground = data[4]; + TRACE(("\tForeground: %ld\n", data[4])); + } + /* data[5]: Background */ + if (mask & XIMP_PRE_BG_MASK) { + xpattr->background = data[5]; + TRACE(("\tBackground: %ld\n", data[5])); + } + /* data[6]: Colormap */ + if (mask & XIMP_PRE_COLORMAP_MASK) { + xpattr->colormap = data[6]; + TRACE(("\tColormap: 0x%lx\n", data[6])); + } + /* data[7]: BackgroundPixmap */ + if (mask & XIMP_PRE_BGPIXMAP_MASK) { + xpattr->bgpixmap = data[7]; + TRACE(("\tBackgroundPixmap: 0x%lx\n", data[7])); + } + /* data[8]: LineSpacing */ + if (mask & XIMP_PRE_LINESP_MASK) { + if (data[8] < MIN_LINE_SPACING) { + client->xattrmask &= ~XIMP_PRE_LINESP_MASK; + DPRINT(("specified line spacing is too small (%ld)\n", data[8])); + } else { + xpattr->linespacing = data[8]; + TRACE(("\tLineSpacing: %ld\n", data[8])); + } + } + /* data[9]: Cursor */ + if (mask & XIMP_PRE_CURSOR_MASK) { + xpattr->cursor = data[9]; + TRACE(("\tCursor: 0x%lx\n", data[9])); + } + /* data[10]-data[11]: AreaNeeded.{width,height} */ + if (mask & XIMP_PRE_AREANEED_MASK) { + xpattr->neededwidth = data[10]; + xpattr->neededheight = data[11]; + TRACE(("\tAreaNeeded: %ld,%ld\n", data[10], data[11])); + } + /* data[12]-data[13]: SpotLocation.{x,y} */ + if (mask & XIMP_PRE_SPOTL_MASK) { + xpattr->spotx = data[12]; + xpattr->spoty = data[13]; + TRACE(("\tSpotLocation: %ld,%ld\n", data[12], data[13])); + } + + XtFree((char *)data); +} + +/*- getStatusProperty: get _XIMP_STATUS property -*/ +static void +getStatusProperty(client, mask) +ConvClient *client; +unsigned long mask; +{ + XimpProtocolWidget xpw = (XimpProtocolWidget)client->protocolwidget; + XimpStatusAttributes *xsattr = &client->xsattrs; + unsigned long *data; + unsigned long len; + + TRACE(("getStatusProperty()\n")); + if (!readProperty(client, xpw->ximp.ximpStatusAtom, + xpw->ximp.ximpStatusAtom, 32, + (unsigned char **)&data, &len)) { + DPRINT(("can't read _XIMP_STATUS property\n")); + return; + } else if (len < 12) { + DPRINT(("length of _XIMP_STATUS property is less than 12(%ld)\n",len)); + XtFree((char *)data); + return; + } + + client->xattrmask |= mask; + + /* data[0]-data[3]: Area.{x,y,width,height} */ + if (mask & XIMP_STS_AREA_MASK) { + xsattr->areax = data[0]; + xsattr->areay = data[1]; + xsattr->areawidth = data[2]; + xsattr->areaheight = data[3]; + if (xsattr->areawidth == 0 || xsattr->areaheight == 0) { + client->xattrmask &= ~XIMP_STS_AREA_MASK; + DPRINT(("invalid area specified:\n")); + } + TRACE(("\tArea: (%ld,%ld)-(%ld,%ld)\n", + data[0],data[1],data[2],data[3])); + } + /* data[4]: Foreground */ + if (mask & XIMP_STS_FG_MASK) { + xsattr->foreground = data[4]; + TRACE(("\tForeground: %ld\n", data[4])); + } + /* data[5]: Background */ + if (mask & XIMP_STS_BG_MASK) { + xsattr->background = data[5]; + TRACE(("\tBackground: %ld\n", data[5])); + } + /* data[6]: Colormap */ + if (mask & XIMP_STS_COLORMAP_MASK) { + xsattr->colormap = data[6]; + TRACE(("\tColormap: 0x%lx\n", data[6])); + } + /* data[7]: BackgroundPixmap */ + if (mask & XIMP_STS_BGPIXMAP_MASK) { + xsattr->bgpixmap = data[7]; + TRACE(("\tBackgroundPixmap: 0x%lx\n", data[7])); + } + /* data[8]: LineSpacing */ + if (mask & XIMP_STS_LINESP_MASK) { + if (data[8] < MIN_LINE_SPACING) { + client->xattrmask &= ~XIMP_STS_LINESP_MASK; + DPRINT(("specified line spacing is too small (%ld)\n", data[8])); + } else { + xsattr->linespacing = data[8]; + TRACE(("\tLineSpacing: %ld\n", data[8])); + } + } + /* data[9]: Cursor */ + if (mask & XIMP_STS_CURSOR_MASK) { + xsattr->cursor = data[9]; + TRACE(("\tCursor: 0x%lx\n", data[9])); + } + /* data[10]-data[11]: AreaNeeded.{width,height} */ + if (mask & XIMP_STS_AREANEED_MASK) { + xsattr->neededwidth = data[10]; + xsattr->neededheight = data[11]; + TRACE(("\tAreaNeeded: %ld,%ld\n", data[10], data[11])); + } + /* data[12]: StatusWindowID -- not suppoted by kinput2 */ + if (len > 12 && (mask & XIMP_STS_WINDOW_MASK)) { + xsattr->statuswin = None; /* ignore specified value */ + TRACE(("\tStatusWindow(not supported): 0x%lx\n", data[12])); + } + + XtFree((char *)data); +} + +/*- readProperty: read specified property of the client window -*/ +static Boolean +readProperty(client, prop, type, format, datapp, lenp) +ConvClient *client; +Atom prop; +Atom type; +int format; +unsigned char **datapp; +unsigned long *lenp; +{ + Atom realtype; + int realformat; + unsigned long bytesafter; + + *datapp = NULL; + /* + * generally, XGetWindowProperty can generate BadAtom, BadValue and + * BadWindow errors. but in this case, none of those errors can occur. + * atoms are valid, offset 0 won't cause BadValue, and window ID is + * already validated. (strictly speaking, there's a chance of getting + * BadWindow if the client window destroyed after it was validated. + * let's forget it for a while :-) so we don't have to be careful to + * errors. + */ + (void)XGetWindowProperty(XtDisplay(client->protocolwidget), + client->reqwin, + prop, 0L, 1000L, True, type, + &realtype, &realformat, lenp, + &bytesafter, datapp); + if (realtype == None) { + /* specified property doesn't exist */ + sendErrorEvent(client, XIMP_BadProperty); + return False; + } else if (realtype != type) { + /* wrong type */ + sendErrorEvent(client, XIMP_BadPropertyType); + return False; + } else if (realformat != format) { + /* wrong format */ + if (*datapp != NULL) XtFree((char *)*datapp); + *datapp = NULL; + /* there's no XIMP_BadFormat error. use XIMP_BadPropertyType instead */ + sendErrorEvent(client, XIMP_BadPropertyType); + return False; + } + return True; +} + +/*- setAttributes: set properties according to the conversion attributes -*/ +static void +setAttributes(client, mask) +ConvClient *client; +unsigned long mask; +{ + if (mask & XIMP_FOCUS_WIN_MASK) { + setFocusProperty(client); + } + if (mask & XIMP_PRE_FONT_MASK) { + setPreeditFontProperty(client); + } + if (mask & XIMP_STS_FONT_MASK) { + setStatusFontProperty(client); + } + if (mask & PREEDIT_MASK) { + setPreeditProperty(client, mask); + } + if (mask & STATUS_MASK) { + setStatusProperty(client, mask); + } +} + +/*- setFocusProperty: set _XIMP_FOCUS property -*/ +static void +setFocusProperty(client) +ConvClient *client; +{ + XimpProtocolWidget xpw = (XimpProtocolWidget)client->protocolwidget; + + TRACE(("setFocusProperty()\n")); + writeProperty(client, xpw->ximp.ximpFocusAtom, XA_WINDOW, 32, + (unsigned char *)&client->focuswin, 1); +} + +/*- setPreeditFontProperty: set _XIMP_PREEDITFONT property -*/ +static void +setPreeditFontProperty(client) +ConvClient *client; +{ + XimpProtocolWidget xpw = (XimpProtocolWidget)client->protocolwidget; + + TRACE(("setPreeditFontProperty()\n")); + writeProperty(client, xpw->ximp.ximpPreeditFontAtom, XA_STRING, 8, + (unsigned char *)client->xpattrs.fontlist, + strlen(client->xpattrs.fontlist)); +} + +/*- setStatusFontProperty: set _XIMP_STATUSFONT property -*/ +static void +setStatusFontProperty(client) +ConvClient *client; +{ + XimpProtocolWidget xpw = (XimpProtocolWidget)client->protocolwidget; + + TRACE(("setStatusFontProperty()\n")); + writeProperty(client, xpw->ximp.ximpStatusFontAtom, XA_STRING, 8, + (unsigned char *)client->xsattrs.fontlist, + strlen(client->xsattrs.fontlist)); +} + +/*- setPreeditProperty: set _XIMP_PREEDIT property -*/ +static void +setPreeditProperty(client, mask) +ConvClient *client; +unsigned long mask; +{ + XimpProtocolWidget xpw = (XimpProtocolWidget)client->protocolwidget; + XimpPreEditAttributes *xpattr = &client->xpattrs; + long data[14]; + + TRACE(("setPreeditProperty()\n")); + + /* data[0]-data[3]: Area.{x,y,width,height} */ + if (mask & XIMP_PRE_AREA_MASK) { + data[0] = xpattr->areax; + data[1] = xpattr->areay; + data[2] = xpattr->areawidth; + data[3] = xpattr->areaheight; + } + /* data[4]: Foreground */ + if (mask & XIMP_PRE_FG_MASK) { + data[4] = xpattr->foreground; + } + /* data[5]: Background */ + if (mask & XIMP_PRE_BG_MASK) { + data[5] = xpattr->background; + } + /* data[6]: Colormap */ + if (mask & XIMP_PRE_COLORMAP_MASK) { + data[6] = xpattr->colormap; + } + /* data[7]: BackgroundPixmap */ + if (mask & XIMP_PRE_BGPIXMAP_MASK) { + data[7] = xpattr->bgpixmap; + } + /* data[8]: LineSpacing */ + if (mask & XIMP_PRE_LINESP_MASK) { + data[8] = xpattr->linespacing; + } + /* data[9]: Cursor */ + if (mask & XIMP_PRE_CURSOR_MASK) { + data[9] = xpattr->cursor; + } + /* data[10]-data[11]: AreaNeeded.{width,height} */ + if (mask & XIMP_PRE_AREANEED_MASK) { + data[10] = xpattr->neededwidth; + data[11] = xpattr->neededheight; + } + /* data[12]-data[13]: SpotLocation.{x,y} */ + if (mask & XIMP_PRE_SPOTL_MASK) { + data[12] = xpattr->spotx; + data[13] = xpattr->spoty; + } + + writeProperty(client, xpw->ximp.ximpPreeditAtom, + xpw->ximp.ximpPreeditAtom, 32, + (unsigned char *)data, 14); +} + +/*- setStautsProperty: set _XIMP_STATUS property -*/ +static void +setStatusProperty(client, mask) +ConvClient *client; +unsigned long mask; +{ + XimpProtocolWidget xpw = (XimpProtocolWidget)client->protocolwidget; + XimpStatusAttributes *xsattr = &client->xsattrs; + long data[13]; + + TRACE(("setStatusProperty()\n")); + + /* data[0]-data[3]: Area.{x,y,width,height} */ + if (mask & XIMP_STS_AREA_MASK) { + data[0] = xsattr->areax = data[0]; + data[1] = xsattr->areay = data[1]; + data[2] = xsattr->areawidth = data[2]; + data[3] = xsattr->areaheight = data[3]; + } + /* data[4]: Foreground */ + if (mask & XIMP_STS_FG_MASK) { + data[4] = xsattr->foreground; + } + /* data[5]: Background */ + if (mask & XIMP_STS_BG_MASK) { + data[5] = xsattr->background; + } + /* data[6]: Colormap */ + if (mask & XIMP_STS_COLORMAP_MASK) { + data[6] = xsattr->colormap; + } + /* data[7]: BackgroundPixmap */ + if (mask & XIMP_STS_BGPIXMAP_MASK) { + data[7] = xsattr->bgpixmap; + } + /* data[8]: LineSpacing */ + if (mask & XIMP_STS_LINESP_MASK) { + data[8] = xsattr->linespacing; + } + /* data[9]: Cursor */ + if (mask & XIMP_STS_CURSOR_MASK) { + data[9] = xsattr->cursor; + } + /* data[10]-data[11]: AreaNeeded.{width,height} */ + if (mask & XIMP_STS_AREANEED_MASK) { + data[10] = xsattr->neededwidth; + data[11] = xsattr->neededheight; + } + /* data[12]: StatusWindowID -- not suppoted by kinput2 */ + if (mask & XIMP_STS_WINDOW_MASK) { + data[12] = xsattr->statuswin; + } + + writeProperty(client, xpw->ximp.ximpStatusAtom, + xpw->ximp.ximpStatusAtom, 32, + (unsigned char *)data, 13); +} + +/*- writeProperty: write specified property of the client window -*/ +static void +writeProperty(client, prop, type, format, datap, len) +ConvClient *client; +Atom prop; +Atom type; +int format; +unsigned char *datap; +int len; +{ + /* + * generally, XChangeWindowProperty can generate BadAlloc, BadAtom, + * BadMatch, BadValue and BadWindow errors. but in this case, none of + * those errors except BadAlloc can occur. atoms and values to be + * specified are valid (at least if the program is correct :-), mode + * PropModeReplace won't cause BadMatch, and window ID is already + * validated. so, if we assume amount of memory is infinite :-), we + * don't have to be careful to errors. + */ + (void)XChangeProperty(XtDisplay(client->protocolwidget), + client->reqwin, prop, type, format, + PropModeReplace, datap, len); +} + +/* + *+ event sending + */ + +/*- sendClientMessage8: send a clientmessage event (format=8) -*/ +static void +sendClientMessage8(client, str, len) +ConvClient *client; +char *str; +int len; +{ + XimpProtocolWidget xpw = (XimpProtocolWidget)client->protocolwidget; + XEvent event; + + event.xclient.type = ClientMessage; + event.xclient.window = client->focuswin; + event.xclient.message_type = xpw->ximp.ximpProtocolAtom; + event.xclient.format = 8; + + /* client ID must be stored in network byte order (ie MSB first) */ + event.xclient.data.b[0] = (client->id >> 24) & 0xff; + event.xclient.data.b[1] = (client->id >> 16) & 0xff; + event.xclient.data.b[2] = (client->id >> 8) & 0xff; + event.xclient.data.b[3] = client->id & 0xff; + + event.xclient.data.b[4] = len; + + (void)strncpy(&event.xclient.data.b[5], str, 20 - 5); + + XSendEvent(XtDisplay((Widget)xpw), event.xclient.window, + False, NoEventMask, &event); +} + +/*- sendClientMessage32: send a clientmessage event (format=32) -*/ +static void +sendClientMessage32(client, type, l1, l2, l3, l4) +ConvClient *client; +int type; +unsigned long l1, l2, l3, l4; +{ + XimpProtocolWidget xpw = (XimpProtocolWidget)client->protocolwidget; + XEvent event; + + event.xclient.type = ClientMessage; + event.xclient.window = client->focuswin; + event.xclient.message_type = xpw->ximp.ximpProtocolAtom; + event.xclient.format = 32; + event.xclient.data.l[0] = type; + event.xclient.data.l[1] = l1; + event.xclient.data.l[2] = l2; + event.xclient.data.l[3] = l3; + event.xclient.data.l[4] = l4; + + XSendEvent(XtDisplay((Widget)xpw), event.xclient.window, + False, NoEventMask, &event); +} + +/*- sendKeyEvent: send unused keypress event via clientmessage event -*/ +static void +sendKeyEvent(client, keyevent) +ConvClient *client; +XKeyEvent *keyevent; +{ + TRACE(("sendKeyEvent()\n")); + sendClientMessage32(client, XIMP_KEYPRESS, client->id, + (unsigned long)keyevent->keycode, + (unsigned long)keyevent->state, 0L); +} + +/*- sendErrorEvent: send error event via clientmessage event -*/ +static void +sendErrorEvent(client, error) +ConvClient *client; +int error; +{ + sendClientMessage32(client, XIMP_ERROR, client->id, + client->event->serial, (unsigned long)error, 0L); +} + +/*- sendCreateRefusal: send rejecting message to a CREATE request -*/ +static void +sendCreateRefusal(xpw, window) +XimpProtocolWidget xpw; +Window window; +{ + XEvent event; + + event.xclient.type = ClientMessage; + event.xclient.window = window; + event.xclient.message_type = xpw->ximp.ximpProtocolAtom; + event.xclient.format = 32; + event.xclient.data.l[0] = XIMP_CREATE_RETURN; + event.xclient.data.l[1] = 0L; + event.xclient.data.l[2] = 0L; + event.xclient.data.l[3] = 0L; + event.xclient.data.l[4] = 0L; + + XSendEvent(XtDisplay((Widget)xpw), window, False, NoEventMask, &event); +} + +/* + *+ callback procedures + */ + +/*- fixCallback: fix callback -*/ +/* ARGSUSED */ +static void +fixCallback(w, client_data, call_data) +Widget w; +XtPointer client_data; +XtPointer call_data; +{ + CCTextCallbackArg *arg = (CCTextCallbackArg *)call_data; + ConvClient *ccp = (ConvClient *)client_data; + + TRACE(("fixCallback(reqwin=0x%lx, length=%d)\n",ccp->reqwin, arg->length)); + fixProc(ccp, arg); +} + +/*- fixProc: do actual fix processing -*/ +static void +fixProc(client, arg) +ConvClient *client; +CCTextCallbackArg *arg; +{ + XimpProtocolWidget xpw = (XimpProtocolWidget)client->protocolwidget; + + /* check encoding and format */ + if (arg->encoding != xpw->ximp.ctextAtom || arg->format != 8) { + /* + * since every conversion object must support COMPOUND_TEXT, + * it is a serious error. + */ + String params[2]; + Cardinal num_params; + + params[0] = XtClass((Widget)xpw)->core_class.class_name; + params[1] = xpw->ximp.inputObjClass->core_class.class_name; + num_params = 2; + + XtAppErrorMsg(XtWidgetToApplicationContext(client->protocolwidget), + "encodingError", "convertedString", "WidgetError", + "%s: encoding of the converted string is not COMPOUND_STRING. check inputObject %s", + params, &num_params); + } + + /* + * normaly, converted string can be sent to client either via + * ClientMessage event or via property. + * the strategy used here is as follows: + * if the string is short enough to fit in a event, + * use ClientMessage. else, use property. + * however in case of reset, the string must be sent via property. + */ +#define MAX_BYTES_IN_A_EVENT (20 - 4 - 1) + + if (!client->resetting && arg->length <= MAX_BYTES_IN_A_EVENT) { + TRACE(("\tsending string via event\n")); + sendClientMessage8(client, arg->text, arg->length); + } else { + TRACE(("\tsending string via property\n")); + XChangeProperty(XtDisplay((Widget)xpw), XtWindow((Widget)xpw), + client->property, arg->encoding, arg->format, + PropModeAppend, (unsigned char *)arg->text, arg->length); + /* when resetting, XIMP_READPROP event should not be sent */ + if (!client->resetting) { + TRACE(("\tsending XIMP_READPROP message\n")); + sendClientMessage32(client, XIMP_READPROP, + client->id, client->property, 0L, 0L); + } + } +#undef MAX_BYTES_IN_A_EVENT +} + +/*- endCallback: conversion end callback -*/ +/* ARGSUSED */ +static void +endCallback(w, client_data, call_data) +Widget w; +XtPointer client_data; +XtPointer call_data; +{ + ConvClient *ccp = (ConvClient *)client_data; + int abort = (int)call_data; + + TRACE(("endCallback(reqwin=0x%lx,abort=%s)\n", ccp->reqwin, abort?"True":"False")); + endProc(ccp, abort); +} + +/*- endProc: conversion end processing -*/ +static void +endProc(client, abort) +ConvClient *client; +int abort; +{ + if (client->conversion == NULL) return; + + preeditDone(client); + + if (!abort) { + TRACE(("\tsending XIMP_PROCESS_END message\n")); + sendClientMessage32(client, XIMP_PROCESS_END, + client->id, 0L, 0L, 0L); + } + detachConverter(client); +} + +/*- unusedEventCallback: unused key event callback -*/ +/* ARGSUSED */ +static void +unusedEventCallback(w, client_data, call_data) +Widget w; +XtPointer client_data; +XtPointer call_data; +{ + ConvClient *ccp = (ConvClient *)client_data; + XKeyEvent *ev = (XKeyEvent *)call_data; + + TRACE(("unusedEventCallback(reqwin=0x%lx)\n", ccp->reqwin)); + sendKeyEvent(ccp, ev); +} + +/* + *+ ClientMessage event handler + */ + +/*- ximpCreateMessageProc: XIMP_CREATE message handler -*/ +static void +ximpCreateMessageProc(xpw, ev) +XimpProtocolWidget xpw; +XClientMessageEvent *ev; +{ + Window reqwin; + XIMStyle inputstyle; + unsigned long attrmask; + ConvClient *client; + XimpInputStyle *styles = XimpStyles; + Dimension w, h; + + TRACE(("ximpCreateMessageProc(window=0x%lx)\n", ev->data.l[1])); + + reqwin = ev->data.l[1]; + + /* check validity of the client window ID */ + if (!isCorrectWindowID((Widget)xpw, reqwin, &w, &h)) { + DPRINT(("\tspecified window doesn't exist!\n")); + return; + } + + inputstyle = ev->data.l[2]; + + /* check specified input style */ + while (styles->ximstyle != inputstyle) { + if (styles->ximstyle == 0L) { + /* + * client is requesting an input style which kinput2 + * doesn't support + */ + DPRINT(("\tclient wants unspported input style\n")); + sendCreateRefusal(xpw, reqwin); + return; + } + styles++; + } + +#ifdef DEBUG + if (DEBUG_CONDITION) { + printf("\tinputstyle: Preedit"); + if (styles->ximstyle & XIMPreeditArea) printf("Area"); + if (styles->ximstyle & XIMPreeditCallbacks) printf("Callbacks"); + if (styles->ximstyle & XIMPreeditPosition) printf("Position"); + if (styles->ximstyle & XIMPreeditNothing) printf("Nothing"); + if (styles->ximstyle & XIMPreeditNone) printf("None"); + printf(", Status"); + if (styles->ximstyle & XIMStatusArea) printf("Area"); + if (styles->ximstyle & XIMStatusCallbacks) printf("Callbacks"); + if (styles->ximstyle & XIMStatusNothing) printf("Nothing"); + if (styles->ximstyle & XIMStatusNone) printf("None"); + printf("\n"); + } +#endif + client = newClient(xpw, reqwin); + client->reqwinwidth = w; + client->reqwinheight = h; + client->event = ev; + client->style = styles->cstyle; + client->ximstyle = styles->ximstyle; + + attrmask = ev->data.l[3]; + + getVersionProperty(client); + + /* get conversion attributes */ + getAttributes(client, attrmask); + + /* watch for client destroy */ + MyAddEventHandler(XtDisplay((Widget)xpw), reqwin, + DestroyNotify, StructureNotifyMask, + ClientDead, (XtPointer)client); + + TRACE(("\tsending XIMP_CREATE_RETURN message\n")); + sendClientMessage32(client, XIMP_CREATE_RETURN, client->id, 0L, 0L, 0L); + + statusStart(client); +} + +/*- ximpDestroyMessageProc: XIMP_DESTROY message handler -*/ +static void +ximpDestroyMessageProc(xpw, ev) +XimpProtocolWidget xpw; +XClientMessageEvent *ev; +{ + ConvClient *client; + int id; + + TRACE(("ximpDestroyMessageProc()\n")); + + id = ev->data.l[1]; + if ((client = findClient(xpw, id)) == NULL) { + DPRINT(("\tinvalid ID\n")); + return; + } + client->event = ev; + + MyRemoveEventHandler(XtDisplay((Widget)xpw), client->reqwin, DestroyNotify, + ClientDead, (XtPointer)client); + + statusDone(client); + if (client->conversion != NULL) { + CControlEndConversion(client->conversion); + endProc(client, False); + } + deleteClient(client); +} + +/*- ximpBeginMessageProc: XIMP_BEGIN message handler -*/ +static void +ximpBeginMessageProc(xpw, ev) +XimpProtocolWidget xpw; +XClientMessageEvent *ev; +{ + ConvClient *client; + int id; + ConversionAttributes attrs; + unsigned long attrmask; + + TRACE(("ximpBeginMessageProc()\n")); + + id = ev->data.l[1]; + if ((client = findClient(xpw, id)) == NULL) { + DPRINT(("\tinvalid ID\n")); + return; + } + client->event = ev; + if (client->conversion != NULL) { + /* already converting */ + DPRINT(("\tclient already in conversion mode\n")); + /* should we send XIMP_BadProtocol error event? */ + return; + } + + if (attachConverter(client) == NULL) { + /* + * since no appropriate error code is defined, + * use BadAlloc... + */ + sendErrorEvent(client, XIMP_BadAlloc); + /* + * to let the client select key events again, + * send XIMP_PROCESS_END message. + */ + sendClientMessage32(client, XIMP_PROCESS_END, + client->id, 0L, 0L, 0L); + return; + } + + XtAddCallback(client->conversion, XtNtextCallback, + fixCallback, (XtPointer)client); + XtAddCallback(client->conversion, XtNendCallback, + endCallback, (XtPointer)client); + XtAddCallback(client->conversion, XtNunusedEventCallback, + unusedEventCallback, (XtPointer)client); + if (client->style == onthespot_style) { + XtAddCallback(client->conversion, XtNpreeditStartCallback, + preeditStartCallback, (XtPointer)client); + XtAddCallback(client->conversion, XtNpreeditDoneCallback, + preeditDoneCallback, (XtPointer)client); + XtAddCallback(client->conversion, XtNpreeditDrawCallback, + preeditDrawCallback, (XtPointer)client); + XtAddCallback(client->conversion, XtNpreeditCaretCallback, + preeditCaretCallback, (XtPointer)client); + XtAddCallback(client->conversion, XtNstatusStartCallback, + statusStartCallback, (XtPointer)client); + XtAddCallback(client->conversion, XtNstatusDoneCallback, + statusDoneCallback, (XtPointer)client); + XtAddCallback(client->conversion, XtNstatusDrawCallback, + statusDrawCallback, (XtPointer)client); + } + + fillInDefaultAttributes(client); + computeAreaForStartup(client); + attrmask = makeConvAttributes(client, &attrs); + + /* start conversion */ + XtVaSetValues(client->conversion, XtNeventSelectMethod, client->esm, NULL); + CControlStartConversion(client->conversion, client->reqwin, + attrmask, &attrs); + + TRACE(("\tsending XIMP_PROCESS_BEGIN message\n")); + sendClientMessage32(client, XIMP_PROCESS_BEGIN, client->id, 0L, 0L, 0L); + + preeditStart(client); +} + +/*- ximpEndMessageProc: XIMP_END message handler -*/ +static void +ximpEndMessageProc(xpw, ev) +XimpProtocolWidget xpw; +XClientMessageEvent *ev; +{ + ConvClient *client; + int id; + + TRACE(("ximpEndMessageProc()\n")); + + id = ev->data.l[1]; + if ((client = findClient(xpw, id)) == NULL) { + DPRINT(("\tinvalid ID\n")); + return; + } + client->event = ev; + if (client->conversion == NULL) { + /* not converting now */ + DPRINT(("\tclient isn't in conversion mode\n")); + /* should we send XIMP_BadProtocol error event? */ + return; + } + + CControlEndConversion(client->conversion); + endProc(client, False); +} + +/*- ximpSetFocusMessageProc: XIMP_SETFOCUS message handler -*/ +static void +ximpSetFocusMessageProc(xpw, ev) +XimpProtocolWidget xpw; +XClientMessageEvent *ev; +{ + ConvClient *client; + int id; + + TRACE(("ximpSetFocusMessageProc()\n")); + + id = ev->data.l[1]; + if ((client = findClient(xpw, id)) == NULL) { + DPRINT(("\tinvalid ID\n")); + return; + } + client->event = ev; + /* what to do? */ + + statusStart(client); + if (client->conversion != NULL) { + CControlChangeFocus(client->conversion, 1); + } +} + +/*- ximpUnsetFocusMessageProc: XIMP_UNSETFOCUS message handler -*/ +static void +ximpUnsetFocusMessageProc(xpw, ev) +XimpProtocolWidget xpw; +XClientMessageEvent *ev; +{ + ConvClient *client; + int id; + + TRACE(("ximpUnsetFocusMessageProc()\n")); + + id = ev->data.l[1]; + if ((client = findClient(xpw, id)) == NULL) { + DPRINT(("\tinvalid ID\n")); + return; + } + client->event = ev; + /* what to do? */ + + if (client->conversion != NULL) { + CControlChangeFocus(client->conversion, 0); + } + statusDone(client); +} + +/*- ximpMoveMessageProc: XIMP_MOVE message handler -*/ +static void +ximpMoveMessageProc(xpw, ev) +XimpProtocolWidget xpw; +XClientMessageEvent *ev; +{ + ConvClient *client; + int id; + ConversionAttributes attrs; + + TRACE(("ximpMoveMessageProc()\n")); + + id = ev->data.l[1]; + if ((client = findClient(xpw, id)) == NULL) { + DPRINT(("\tinvalid ID\n")); + return; + } + client->event = ev; + client->xpattrs.spotx = ev->data.l[2]; + client->xpattrs.spoty = ev->data.l[3]; + client->xattrmask |= XIMP_PRE_SPOTL_MASK; + if (client->conversion != NULL) { + attrs.spotx = client->xpattrs.spotx; + attrs.spoty = client->xpattrs.spoty; + CControlChangeAttributes(client->conversion, CASpotLocation, &attrs); + } +} + +/*- ximpResetMessageProc: XIMP_RESET message handler -*/ +static void +ximpResetMessageProc(xpw, ev) +XimpProtocolWidget xpw; +XClientMessageEvent *ev; +{ + ConvClient *client; + int id; + Widget inputobj; + + TRACE(("ximpResetMessageProc()\n")); + + id = ev->data.l[1]; + if ((client = findClient(xpw, id)) == NULL) { + DPRINT(("\tinvalid ID\n")); + return; + } + client->event = ev; + client->resetting = True; + + if (client->conversion == NULL) { + XChangeProperty(XtDisplay((Widget)xpw), XtWindow((Widget)xpw), + client->property, xpw->ximp.ctextAtom, 8, + PropModeAppend, (unsigned char *)"", 0); + } else { + /* + * get input object by asking conversion widget of XtNinputObject + * resource. however, it is not recommended since protocol widget + * should interact with input object only through conversion + * widget. + */ + CCTextCallbackArg arg; + + XtVaGetValues(client->conversion, XtNinputObject, &inputobj, NULL); + arg.encoding = xpw->ximp.ctextAtom; + if (ICGetConvertedString(inputobj, &arg.encoding, &arg.format, + &arg.length, &arg.text) >= 0) { + fixProc(client, &arg); + } else { + /* there's no string */ + XChangeProperty(XtDisplay((Widget)xpw), XtWindow((Widget)xpw), + client->property, xpw->ximp.ctextAtom, 8, + PropModeAppend, (unsigned char *)"", 0); + } + ICClearConversion(inputobj); + } + + TRACE(("\tsending XIMP_RESET_RETURN message\n")); + sendClientMessage32(client, XIMP_RESET_RETURN, client->id, + client->property, 0L, 0L); +} + +/*- ximpSetValueMessageProc: XIMP_SETVALUE message handler -*/ +static void +ximpSetValueMessageProc(xpw, ev) +XimpProtocolWidget xpw; +XClientMessageEvent *ev; +{ + ConvClient *client; + unsigned long mask; + ConversionAttributes attrs; + unsigned long attrmask; + int id; + + TRACE(("ximpSetValueMessageProc()\n")); + + id = ev->data.l[1]; + if ((client = findClient(xpw, id)) == NULL) { + DPRINT(("\tinvalid ID\n")); + return; + } + mask = ev->data.l[2]; + client->event = ev; + getAttributes(client, mask); + if (client->conversion != NULL) { + attrmask = makeConvAttributes(client, &attrs); + CControlChangeAttributes(client->conversion, attrmask, &attrs); + } +} + +/*- ximpChangeMessageProc: XIMP_CHANGE message handler -*/ +static void +ximpChangeMessageProc(xpw, ev) +XimpProtocolWidget xpw; +XClientMessageEvent *ev; +{ + ConvClient *client; + Atom atom; + unsigned long mask; + ConversionAttributes attrs; + unsigned long attrmask; + int id; + + TRACE(("ximpChangeMessageProc()\n")); + + id = ev->data.l[1]; + if ((client = findClient(xpw, id)) == NULL) { + DPRINT(("\tinvalid ID\n")); + return; + } + atom = (Atom)ev->data.l[2]; + + if (atom == xpw->ximp.ximpFocusAtom) { + mask = XIMP_FOCUS_WIN_MASK; + } else if (atom == xpw->ximp.ximpPreeditAtom) { + mask = PREEDIT_MASK; + } else if (atom == xpw->ximp.ximpStatusAtom) { + mask = STATUS_MASK; + } else if (atom == xpw->ximp.ximpPreeditFontAtom) { + mask = XIMP_PRE_FONT_MASK; + } else if (atom == xpw->ximp.ximpStatusFontAtom) { + mask = XIMP_STS_FONT_MASK; + } else { + /* invalid property name */ + sendErrorEvent(client, XIMP_BadProperty); + return; + } + + client->event = ev; + getAttributes(client, mask); + if (client->conversion != NULL) { + attrmask = makeConvAttributes(client, &attrs); + CControlChangeAttributes(client->conversion, attrmask, &attrs); + } +} + +/*- ximpGetValueMessageProc: XIMP_GETVALUE message handler -*/ +static void +ximpGetValueMessageProc(xpw, ev) +XimpProtocolWidget xpw; +XClientMessageEvent *ev; +{ + ConvClient *client; + unsigned long mask; + int id; + + TRACE(("ximpGetValueMessageProc()\n")); + + id = ev->data.l[1]; + if ((client = findClient(xpw, id)) == NULL) { + DPRINT(("\tinvalid ID\n")); + return; + } + mask = ev->data.l[2]; + client->event = ev; + + fillInDefaultAttributes(client); + computeAreaForQuery(client); + setAttributes(client, mask); + + TRACE(("\tsending XIMP_GETVALUE_RETURN message\n")); + sendClientMessage32(client, XIMP_GETVALUE_RETURN, client->id, 0L, 0L, 0L); +} + +/*- ximpKeyPressMessageProc: XIMP_KEYPRESS message handler -*/ +static void +ximpKeyPressMessageProc(xpw, ev) +XimpProtocolWidget xpw; +XClientMessageEvent *ev; +{ + ConvClient *client; + int id; + XKeyEvent keyevent; + + TRACE(("ximpKeyPressMessageProc()\n")); + + id = ev->data.l[1]; + if ((client = findClient(xpw, id)) == NULL) { + DPRINT(("\tinvalid ID\n")); + return; + } + client->event = ev; + + if (client->conversion == NULL) return; + + /* make a fake keypress event */ + keyevent.type = KeyPress; + keyevent.serial = ev->serial; + keyevent.send_event = True; + keyevent.display = ev->display; + keyevent.window = client->focuswin; + keyevent.root = DefaultRootWindow(ev->display); + /* hope conversion object won't check this field */ + keyevent.subwindow = None; + /* hope conversion object won't check this field */ + keyevent.time = 0; /* hope conversion object won't check this field */ + keyevent.x = 0; + keyevent.y = 0; + keyevent.x_root = 0; + keyevent.y_root = 0; + keyevent.state = ev->data.l[3]; + keyevent.keycode = ev->data.l[2]; + keyevent.same_screen = True; + + /* call action routine */ + XtCallActionProc(client->conversion, "to-inputobj", (XEvent *)&keyevent, + (String *)NULL, (Cardinal)0); +} + +/*- ximpExtensionMessageProc: XIMP_Extension message handler -*/ +static void +ximpExtensionMessageProc(xpw, ev) +XimpProtocolWidget xpw; +XClientMessageEvent *ev; +{ + ConvClient *client; + Atom extatom; + int id; + + TRACE(("ximpExtensionMessageProc()\n")); + + id = ev->data.l[1]; + if ((client = findClient(xpw, id)) == NULL) { + DPRINT(("\tinvalid ID\n")); + return; + } + extatom = ev->data.l[2]; /* extension name */ + client->event = ev; + + if (extatom == xpw->ximp.ximpExtXimpBackFrontAtom) { + TRACE(("\t_XIMP_EXT_XIMP_BACK_FRONT extension -- ")); + if (client->conversion != NULL) { + /* invalid */ + DPRINT(("invalid (now in conversion mode)\n")); + return; + } + if (ev->data.l[3] != 0) { + /* backend method */ + TRACE(("backend\n")); + client->esm = ESMethodNone; + } else { + TRACE(("frontend\n")); + client->esm = ESMethodSelectFocus; + } + } else { + DPRINT(("\tunknown extension atom -- %ld", extatom)); + sendErrorEvent(client, XIMP_BadAtom); + } +} + +/*- XimpMessageProc: _XIMP_PROTOCOL message event handler -*/ +/* ARGSUSED */ +static void +XimpMessageProc(w, event, args, num_args) +Widget w; +XEvent *event; +String *args; +Cardinal *num_args; +{ + XimpProtocolWidget xpw = (XimpProtocolWidget)w; + XClientMessageEvent *ev = &event->xclient; + ConvClient *client; + + TRACE(("XimpMessageProc()\n")); + /* is it a correct event? */ + if (!isCorrectClientEvent(xpw, event)) { + /*ignore */ + DPRINT(("got invalid clientmessage event.\n")); + return; + } + switch((int)ev->data.l[0]) { + case XIMP_CREATE: ximpCreateMessageProc(xpw, ev); break; + case XIMP_DESTROY: ximpDestroyMessageProc(xpw, ev); break; + case XIMP_BEGIN: ximpBeginMessageProc(xpw, ev); break; + case XIMP_END: ximpEndMessageProc(xpw, ev); break; + case XIMP_SETFOCUS: ximpSetFocusMessageProc(xpw, ev); break; + case XIMP_UNSETFOCUS: ximpUnsetFocusMessageProc(xpw, ev); break; + case XIMP_KEYPRESS: ximpKeyPressMessageProc(xpw, ev); break; + case XIMP_SETVALUE: ximpSetValueMessageProc(xpw, ev); break; + case XIMP_CHANGE: ximpChangeMessageProc(xpw, ev); break; + case XIMP_GETVALUE: ximpGetValueMessageProc(xpw, ev); break; + case XIMP_MOVE: ximpMoveMessageProc(xpw, ev); break; + case XIMP_RESET: ximpResetMessageProc(xpw, ev); break; + case XIMP_EXTENSION: ximpExtensionMessageProc(xpw, ev); break; + case XIMP_PREEDITSTART_RETURN: break; + case XIMP_PREEDITCARET_RETURN: break; + default: + DPRINT(("\tunknown command code (%ld) ignored\n", ev->data.l[0])); + /* assume ev->data.l[1] contains ICID */ + if ((client = findClient(xpw, (int)ev->data.l[1])) != NULL) { + client->event = ev; + sendErrorEvent(client, XIMP_BadProtocol); + } + break; + } +} + +/* + *+ other event handler + */ + +/*- SelectionRequestProc: SelectionRequest event handler -*/ +/*ARGSUSED*/ +static void +SelectionRequestProc(w, event, args, num_args) +Widget w; +XEvent *event; +String *args; /* not used */ +Cardinal *num_args; /* not used */ +{ + XSelectionRequestEvent *ev = &(event->xselectionrequest); + XEvent repl; + String params[1]; + Cardinal num_params; + + repl.xselection.type = SelectionNotify; + repl.xselection.requestor = ev->requestor; + repl.xselection.selection = ev->selection; + repl.xselection.target = ev->target; + repl.xselection.property = None; + repl.xselection.time = ev->time; + + params[0] = XtClass(w)->core_class.class_name; + num_params = 1; + XtAppWarningMsg(XtWidgetToApplicationContext(w), + "selectionError", "SelectionRequest", "WidgetError", + "%s: SelectionRequest event received", + params, &num_params); + + XSendEvent(ev->display, ev->requestor, False, NoEventMask, &repl); +} + +/*- SelectionClearProc: SelectionClear event handler -*/ +/* ARGSUSED */ +static void +SelectionClearProc(w, event, args, num_args) +Widget w; +XEvent *event; +String *args; +Cardinal *num_args; +{ + XimpProtocolWidget xpw = (XimpProtocolWidget)w; + XSelectionClearEvent *ev = (XSelectionClearEvent *)event; + ConvClient *ccp; + String params[1]; + Cardinal num_params; + + /* Selection owner changed */ + + if (ev->selection == xpw->ximp.selAtom1) { + /* someone has become a new default server */ + xpw->ximp.selAtom1 = None; + return; + } else if (ev->selection != xpw->ximp.selAtom2) { + TRACE(("XimpProtocol:SelectionClearProc() SelectionClear event for unknown selection received\n")); + return; + } + + /* + * send ConversionEnd event to the clients before exit + */ + for (ccp = xpw->ximp.clients; ccp; ccp = ccp->next) { + if (ccp->reqwin != None) { + statusDone(ccp); + endProc(ccp, False); + } + } + + params[0] = XtClass(w)->core_class.class_name; + num_params = 1; + XtAppWarningMsg(XtWidgetToApplicationContext(w), + "selectionError", "SelectionClear", "WidgetError", + "%s: SelectionClear event received", + params, &num_params); + + XtDestroyWidget(w); +} + +/*- ClientDead: DestroyNotify event handler -*/ +static void +ClientDead(ev, data) +XEvent *ev; +XtPointer data; +{ + ConvClient *ccp = (ConvClient *)data; + + TRACE(("ClientDead(window=0x%lx)\n", ev->xdestroywindow.window)); + if (ev->type != DestroyNotify || + ev->xdestroywindow.window != ccp->reqwin) return; + + MyRemoveAllEventHandler(ev->xany.display, ccp->reqwin); + deleteClient(ccp); +} + +/* + *+ on-the-spot callback procedures + */ + +/*- preeditStartCallback: preedit start -*/ +/* ARGSUSED */ +static void +preeditStartCallback(w, client_data, call_data) +Widget w; +XtPointer client_data; +XtPointer call_data; +{ + ConvClient *ccp = (ConvClient *)client_data; + TRACE(("preeditStartCallback(reqwin=0x%lx)\n", ccp->reqwin)); + preeditStart(ccp); +} + +/*- preeditDoneCallback: preedit done -*/ +/* ARGSUSED */ +static void +preeditDoneCallback(w, client_data, call_data) +Widget w; +XtPointer client_data; +XtPointer call_data; +{ + ConvClient *ccp = (ConvClient *)client_data; + TRACE(("preeditDoneCallback(reqwin=0x%lx)\n", ccp->reqwin)); + preeditDone(ccp); +} + +/*- preeditDrawCallback: preedit draw -*/ +/* ARGSUSED */ +static void +preeditDrawCallback(w, client_data, call_data) +Widget w; +XtPointer client_data; +XtPointer call_data; +{ + ConvClient *ccp = (ConvClient *)client_data; + OCCPreeditDrawArg *arg = (OCCPreeditDrawArg *)call_data; + TRACE(("preeditDrawCallback(reqwin=0x%lx, length=%d)\n", ccp->reqwin, arg->text_length)); + preeditDraw(ccp, arg); +} + +/*- preeditCaretCallback: preedit caret -*/ +/* ARGSUSED */ +static void +preeditCaretCallback(w, client_data, call_data) +Widget w; +XtPointer client_data; +XtPointer call_data; +{ + ConvClient *ccp = (ConvClient *)client_data; + int caret = (int)call_data; + TRACE(("preeditCaretCallback(reqwin=0x%lx, caret=%d)\n",ccp->reqwin,caret)); + preeditCaret(ccp, caret); +} + +/*- statusStartCallback: status start -*/ +/* ARGSUSED */ +static void +statusStartCallback(w, client_data, call_data) +Widget w; +XtPointer client_data; +XtPointer call_data; +{ + ConvClient *ccp = (ConvClient *)client_data; + TRACE(("statusStartCallback(reqwin=0x%lx)\n", ccp->reqwin)); + statusStart(ccp); +} + +/*- statusDoneCallback: status done -*/ +/* ARGSUSED */ +static void +statusDoneCallback(w, client_data, call_data) +Widget w; +XtPointer client_data; +XtPointer call_data; +{ + ConvClient *ccp = (ConvClient *)client_data; + TRACE(("statusDoneCallback(reqwin=0x%lx)\n", ccp->reqwin)); + statusDone(ccp); +} + +/*- statusDrawCallback: status draw -*/ +/* ARGSUSED */ +static void +statusDrawCallback(w, client_data, call_data) +Widget w; +XtPointer client_data; +XtPointer call_data; +{ + ConvClient *ccp = (ConvClient *)client_data; + OCCPreeditDrawArg *arg = (OCCPreeditDrawArg *)call_data; + TRACE(("statusDrawCallback(reqwin=0x%lx, length=%d)\n", ccp->reqwin, arg->text_length)); + statusDraw(ccp, arg); +} + +/*- preeditStart: do preedit start -*/ +static void +preeditStart(client) +ConvClient *client; +{ + TRACE(("preeditStart(reqwin=0x%lx)\n", client->reqwin)); + if (!(client->ximstyle & XIMPreeditCallbacks)) + return; + if (!client->in_preedit) { + TRACE(("\tsending XIMP_PREEDITSTART message\n")); + sendClientMessage32(client, XIMP_PREEDITSTART, client->id, 0L, 0L, 0L); + client->in_preedit = True; + } +} + +/*- preeditDone: do preedit done -*/ +static void +preeditDone(client) +ConvClient *client; +{ + TRACE(("preeditDone(reqwin=0x%lx)\n", client->reqwin)); + if (!(client->ximstyle & XIMPreeditCallbacks)) + return; + if (client->in_preedit) { + TRACE(("\tsending XIMP_PREEDITDONE message\n")); + sendClientMessage32(client, XIMP_PREEDITDONE, client->id, 0L, 0L, 0L); + client->in_preedit = False; + } +} + +/*- preeditDraw: do actual preedit draw -*/ +static void +preeditDraw(client, data) +ConvClient *client; +OCCPreeditDrawArg *data; +{ + XimpProtocolWidget xpw = (XimpProtocolWidget)client->protocolwidget; + Display *xd = XtDisplay((Widget)xpw); + Window xw = XtWindow((Widget)xpw); + unsigned long *feedbacks; + Boolean allsamefb; + int i; + + if (!(client->ximstyle & XIMPreeditCallbacks)) + return; + + preeditStart(client); + + /* check encoding and format */ + if (data->encoding != xpw->ximp.ctextAtom || data->format != 8) { + /* + * since every conversion object must support COMPOUND_TEXT, + * it is a serious error. + */ + String params[2]; + Cardinal num_params; + + params[0] = XtClass((Widget)xpw)->core_class.class_name; + params[1] = xpw->ximp.inputObjClass->core_class.class_name; + num_params = 2; + + XtAppErrorMsg(XtWidgetToApplicationContext(client->protocolwidget), + "encodingError", "preeditString", "WidgetError", + "%s: encoding of the preedit string is not COMPOUND_STRING. check inputObject %s", + params, &num_params); + } + + feedbacks = data->attrs; + allsamefb = True; + if (data->attrs_length > 0) { + unsigned long check = data->attrs[0]; + for (i = 0; i < data->attrs_length; i++) { + if (feedbacks[i] != check) allsamefb = False; + } + } + +#define MAX_BYTES_IN_A_EVENT (20 - 4 - 1) + if (data->text_length <= MAX_BYTES_IN_A_EVENT) { + if (!allsamefb) { + TRACE(("\tsending feedbacks via property\n")); + XChangeProperty(xd, xw, client->preeditfeedback, + xpw->ximp.ximpFeedbacksAtom, 32, PropModeAppend, + (unsigned char *)feedbacks, data->attrs_length); + } + if (!client->resetting) { + unsigned long fb = (data->attrs_length > 0 ? feedbacks[0] : 0); + int status = 0; + + if (data->text_length == 0) + status |= 0x1; /* no_text */ + if (data->attrs_length == 0) + status |= 0x2; /* no_feedback */ + if (!allsamefb) + status |= 0x4; /* feedbacks_via_property */ + + TRACE(("\tsending XIMP_PREEDITDRAW_CM message\n")); + sendClientMessage32( + client, XIMP_PREEDITDRAW_CM, client->id, + (status << 16) | (data->caret & 0xffff), + (data->chg_first << 16) | (data->chg_length & 0xffff), + (allsamefb ? fb : client->preeditfeedback)); + + if (data->text_length > 0) { + TRACE(("\tsending string via event\n")); + sendClientMessage8(client, data->text, data->text_length); + } + } + } + else { + long atoms[3]; + atoms[0] = data->caret; + atoms[1] = data->chg_first; + atoms[2] = data->chg_length; + TRACE(("\tsending preedit data via property\n")); + XChangeProperty(xd, xw, client->preeditdata, + xpw->ximp.ximpPreeditDrawDataAtom, 32, PropModeAppend, + (unsigned char *)atoms, 3); + TRACE(("\tsending string via property\n")); + XChangeProperty(xd, xw, client->preedittext, + data->encoding, data->format, PropModeAppend, + (unsigned char *)data->text, data->text_length); + TRACE(("\tsending feedbacks via property\n")); + XChangeProperty(xd, xw, client->preeditfeedback, + xpw->ximp.ximpFeedbacksAtom, 32, PropModeAppend, + (unsigned char *)feedbacks, data->attrs_length); + if (!client->resetting) { + TRACE(("\tsending XIMP_PREEDITDRAW message\n")); + sendClientMessage32(client, XIMP_PREEDITDRAW, client->id, + client->preeditdata, client->preedittext, + client->preeditfeedback); + } + } +#undef MAX_BYTES_IN_A_EVENT +} + +/*- preeditCaret: do actual preedit caret -*/ +static void +preeditCaret(client, caret) +ConvClient *client; +int caret; +{ + XimpProtocolWidget xpw = (XimpProtocolWidget)client->protocolwidget; + + TRACE(("\tsending XIMP_PREEDITCARET message\n")); + if (!(client->ximstyle & XIMPreeditCallbacks)) + return; + sendClientMessage32(client, XIMP_PREEDITCARET, client->id, + caret, (long)XIMAbsolutePosition, (long)XIMPrimary); +} + +/*- statusStart: do status start -*/ +static void +statusStart(client) +ConvClient *client; +{ + TRACE(("statusStart(reqwin=0x%lx)\n", client->reqwin)); + if (!(client->ximstyle & XIMStatusCallbacks)) + return; + if (!client->in_status) { + TRACE(("\tsending XIMP_STATUSSTART message\n")); + sendClientMessage32(client, XIMP_STATUSSTART, client->id, 0L, 0L, 0L); + client->in_status = True; + } +} + +/*- statusDone: do status done -*/ +static void +statusDone(client) +ConvClient *client; +{ + TRACE(("statusDone(reqwin=0x%lx)\n", client->reqwin)); + if (!(client->ximstyle & XIMStatusCallbacks)) + return; + if (client->in_status) { + TRACE(("\tsending XIMP_STATUSDONE message\n")); + sendClientMessage32(client, XIMP_STATUSDONE, client->id, 0L, 0L, 0L); + client->in_status = False; + } +} + +/*- statusDraw: do actual status draw -*/ +static void +statusDraw(client, data) +ConvClient *client; +OCCPreeditDrawArg *data; +{ + XimpProtocolWidget xpw = (XimpProtocolWidget)client->protocolwidget; + Display *xd = XtDisplay((Widget)xpw); + Window xw = XtWindow((Widget)xpw); + int type = 0; /* text type */ + + if (!(client->ximstyle & XIMStatusCallbacks)) + return; + + statusStart(client); + + /* check encoding and format */ + if (data->encoding != xpw->ximp.ctextAtom || data->format != 8) { + /* + * since every conversion object must support COMPOUND_TEXT, + * it is a serious error. + */ + String params[2]; + Cardinal num_params; + + params[0] = XtClass((Widget)xpw)->core_class.class_name; + params[1] = xpw->ximp.inputObjClass->core_class.class_name; + num_params = 2; + + XtAppErrorMsg(XtWidgetToApplicationContext(client->protocolwidget), + "encodingError", "statusString", "WidgetError", + "%s: encoding of the status string is not COMPOUND_STRING. check inputObject %s", + params, &num_params); + } + +#define MAX_BYTES_IN_A_EVENT (20 - 4 - 1) + if (data->text_length <= MAX_BYTES_IN_A_EVENT) { + int feedback = 0; + if (!client->resetting) { + TRACE(("\tsending XIMP_STATUSDRAW_CM message\n")); + sendClientMessage32(client, XIMP_STATUSDRAW_CM, client->id, + type, feedback, 0L); + TRACE(("\tsending string via event\n")); + sendClientMessage8(client, data->text, data->text_length); + } + } + else { + int i; + int *feedbacks = (int *)XtMalloc(data->attrs_length * sizeof(int)); + for (i = 0; i < data->attrs_length; i++) feedbacks[i] = 0; + TRACE(("\tsending string via property\n")); + XChangeProperty(xd, xw, client->statustext, + data->encoding, data->format, PropModeAppend, + (unsigned char *)data->text, data->text_length); + TRACE(("\tsending feedbacks via property\n")); + XChangeProperty(xd, xw, client->statusfeedback, + xpw->ximp.ximpFeedbacksAtom, 32, PropModeAppend, + (unsigned char *)feedbacks, data->attrs_length); + if (!client->resetting) { + TRACE(("\tsending XIMP_STATUSDRAW message\n")); + sendClientMessage32(client, XIMP_STATUSDRAW, client->id, + type, client->statustext, + client->statusfeedback); + } + /* feedbacks を free しておく */ + XtFree((char *)feedbacks); + } +#undef MAX_BYTES_IN_A_EVENT +} + + +#endif /* defined(XlibSpecificationRelease) && XlibSpecificationRelease >= 5 */