Mercurial > kinput2.yaz
diff lib/KIProto.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 | 7a454839a6de |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/KIProto.c Mon Mar 08 04:44:30 2010 +0900 @@ -0,0 +1,2351 @@ +#ifndef lint +static char *rcsid = "$Id: KIProto.c,v 1.49 1999/01/07 03:12:57 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/IntrinsicP.h> +#include <X11/StringDefs.h> +#include <X11/Xatom.h> +#include <X11/Xmu/Atoms.h> +#include <X11/Xmu/CharSet.h> +#include "KIProtoP.h" +#include "ConvMgr.h" +#include "OverConv.h" +#include "OffConv.h" +#include "ConvDisp.h" +#include "MyDispatch.h" +#include "CachedFont.h" +#include "ConvProto.h" +#include "AsyncErr.h" + +#define DEBUG_VAR debug_KinputProtocol +#include "DebugPrint.h" + +#define JINPUT_PROTOCOL_VERSION 0x20000L /* version 2 */ + +#define XLC_AUTO_REPLACE 1 +#define XLC_ALL_INFORMATION 2 +#define XLC_FRAME_INFORMATION 4 +#define XLC_OFFSET_INFORMATION 8 + +/*- resource table -*/ +static XtResource resources[] = { +#define offset(field) XtOffset(KinputProtocolWidget, kinput.field) + { XtNlanguage, XtCLanguage, XtRString, sizeof(String), + offset(language), XtRString, (XtPointer)NULL }, + { XtNinputObjectClass, XtCClass, XtRPointer, sizeof(WidgetClass), + offset(inputObjClass), XtRImmediate, (XtPointer)NULL }, + { XtNdisplayObjectClass, XtCClass, XtRPointer, sizeof(WidgetClass), + offset(displayObjClass), XtRImmediate, (XtPointer)NULL }, + { XtNbackwardCompatible, XtCBackwardCompatible, XtRBoolean, sizeof(Boolean), + offset(backward_compatible), XtRString, (XtPointer)"False" }, + { XtNxlcConversionStartKey, XtCXlcConversionStartKey, XtRString, sizeof(String), + offset(xlcstartkey), XtRString, (XtPointer)NULL }, +#undef offset +}; + +static void ConversionRequestProc(); +static void ConversionEndRequestProc(); +static void ConversionOpenRequestProc(); +static void ConversionCloseRequestProc(); +static void ConversionCloseProc(); +static void ConversionXYNotifyProc(); +static void ConversionColorNotifyProc(); +static void ConversionFontsNotifyProc(); +static void ConversionAttributeNotifyProc(); +static void SelectionRequestProc(); +static void SelectionClearProc(); +static void XlcOnTheSpotChangedProc(); + +/*- action table -*/ +static XtActionsRec actions[] = { + { "conversion-request", ConversionRequestProc }, + { "conversion-end-request", ConversionEndRequestProc }, + { "conversion-open-request", ConversionOpenRequestProc }, + { "conversion-close-request", ConversionCloseRequestProc }, + { "conversion-close", ConversionCloseProc }, + { "conversion-xy-notify", ConversionXYNotifyProc }, + { "conversion-color-notify", ConversionColorNotifyProc }, + { "conversion-fonts-notify", ConversionFontsNotifyProc }, + { "conversion-attribute-notify", ConversionAttributeNotifyProc }, + { "xlc-on-the-spot-changed", XlcOnTheSpotChangedProc }, + { "selection-request", SelectionRequestProc }, + { "selection-clear", SelectionClearProc }, +}; + +/*- default translation -*/ +static char translations[] = + "<Message>CONVERSION_REQUEST: conversion-request()\n\ + <Message>CONVERSION_END_REQUEST: conversion-end-request()\n\ + <Message>CONVERSION_OPEN_REQUEST: conversion-open-request()\n\ + <Message>CONVERSION_CLOSE_REQUEST: conversion-close-request()\n\ + <Message>CONVERSION_CLOSE: conversion-close()\n\ + <Message>CONVERSION_XY_NOTIFY: conversion-xy-notify()\n\ + <Message>CONVERSION_COLOR_NOTIFY: conversion-color-notify()\n\ + <Message>CONVERSION_FONTS_NOTIFY: conversion-fonts-notify()\n\ + <Message>CONVERSION_ATTRIBUTE_NOTIFY: conversion-attribute-notify()\n\ + <Prop>_XLC_ON_THE_SPOT: xlc-on-the-spot-changed()\n\ + <SelReq>: selection-request()\n\ + <SelClr>: selection-clear()"; + +/*- static function declarations -*/ +static void Initialize(), Destroy(); +static void Realize(); +static Boolean SetValues(); + +static void getAtoms(); +static Boolean ownSelection(); + +static ConvClient *findClient(); +static ConvClient *newClient(); +static Widget attachConverter(); +static void detachConverter(); +static void jinputDetach(); +static void deleteClient(); + +static Boolean isCorrectClientEvent(); +static Boolean isCorrectWindowID(); + +static void myStartConversion(); +static void myChangeAttributes(); + +static void getJinputInitialProperty(); +static void jinputSendReq(); +static void jinputFreeResources(); +static void kinput2FreeResources(); +static void xlcFreeResources(); +static void setAttribute(); +static int getFontsByFontAtoms(); +static int safeGetAttributeProperty(); +static void getAttributeFromProperty1(); +static void getAttributeFromProperty(); +static void getAttributeFromEvent(); +static ConvClient *getXlcDataFromProperty(); +static void initializeError(); +static int parseKeyEvent(); +static char *mystrstr(); +static void getDefaultFontHeight(); + +static void setJinputProperty(); +static void setKinput2Property(); +static void setXlcProperty(); +static void setXlcStatusProperty(); +static void setXlcBCKey(); + +static void sendClientMessage(); +static void sendNegativeConversionNotify(); +static void sendConversionNotify(); +static void sendNegativeConversionOpenNotify(); +static void sendConversionOpenNotify(); +static void sendColorRequest(); +static void sendFontRequest(); +static void sendXYRequest(); + +static void fixCallback(); +static void fixProc(); +static void jinputFix(); +static void endCallback(); +static void endProc(); +static void jinputEnd(); +static void jinputNewTextCallback(); +static void xlcEnd(); + +static void ClientDead(); + +/*- KinputProtocolClassRec -*/ +KinputProtocolClassRec kinputProtocolClassRec = { + { /* core fields */ + /* superclass */ (WidgetClass) &widgetClassRec, + /* class_name */ "KinputProtocol", + /* widget_size */ sizeof(KinputProtocolRec), + /* class_initialize */ NULL, + /* 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 */ SetValues, + /* 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 + }, + { /* kinputprotocol fields */ + /* empty */ 0 + } +}; + +WidgetClass kinputProtocolWidgetClass = (WidgetClass)&kinputProtocolClassRec; + +/* + *+ Core class methods + */ + +/*- Initialize: intern Atoms -*/ +/* ARGSUSED */ +static void +Initialize(req, new, args, num_args) +Widget req; +Widget new; +ArgList args; +Cardinal *num_args; +{ + KinputProtocolWidget kpw = (KinputProtocolWidget)new; + + if (kpw->kinput.language == NULL) { + initializeError(new, XtNlanguage); + } else if (kpw->kinput.inputObjClass == NULL) { + initializeError(new, XtNinputObjectClass); + } else if (kpw->kinput.displayObjClass == NULL) { + initializeError(new, XtNdisplayObjectClass); + } + kpw->kinput.language = XtNewString(kpw->kinput.language); + kpw->kinput.clients = (ConvClient *)NULL; + getDefaultFontHeight(kpw); + getAtoms(kpw); +} + +/*- Destroy: free allocated memory -*/ +static void +Destroy(w) +Widget w; +{ + KinputProtocolWidget kpw = (KinputProtocolWidget)w; + + XtFree(kpw->kinput.language); +} + +/*- Realize: own selection -*/ +static void +Realize(w, mask, value) +Widget w; +XtValueMask *mask; +XSetWindowAttributes *value; +{ + KinputProtocolWidget kpw = (KinputProtocolWidget)w; + CoreWidgetClass super = (CoreWidgetClass)XtClass(w)->core_class.superclass; + + (*super->core_class.realize)(w, mask, value); + + setJinputProperty(kpw); + setKinput2Property(kpw); + setXlcProperty(kpw); + + if (!ownSelection(kpw)) { + 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 { + CMPrepareConverter(XtParent(w), XtScreen(w), + separateConversionWidgetClass, + kpw->kinput.inputObjClass, + kpw->kinput.displayObjClass); + } +} + +/*- SetValues: not implemented yet -*/ +/* ARGSUSED */ +static Boolean +SetValues(cur, req, wid, args, num_args) +Widget cur; +Widget req; +Widget wid; +ArgList args; +Cardinal *num_args; +{ +#ifdef notdef + KinputProtocolWidget old = (KinputProtocolWidget)cur; + KinputProtocolWidget new = (KinputProtocolWidget)wid; + Boolean redisplay = False; +#endif + + return False; +} + +/* + *+ atom handling + */ + +/*- getAtoms: intern atoms -*/ +static void +getAtoms(kpw) +KinputProtocolWidget kpw; +{ + Display *dpy = XtDisplay((Widget)kpw); + char buf[256]; + + (void)sprintf(buf, "_%s_CONVERSION", kpw->kinput.language); + kpw->kinput.convAtom = XInternAtom(dpy, buf, False); + if (kpw->kinput.backward_compatible) { + (void)sprintf(buf, "%s_CONVERSION", kpw->kinput.language); + kpw->kinput.oldConvAtom = XInternAtom(dpy, buf, False); + } else { + kpw->kinput.oldConvAtom = None; + } + + kpw->kinput.ctextAtom = XA_COMPOUND_TEXT(dpy); + +#define MAKEATOM(s) XInternAtom(dpy, s, False) + + kpw->kinput.convStringAtom = MAKEATOM("CONVERSION_STRING"); + kpw->kinput.convNotifyAtom = MAKEATOM("CONVERSION_NOTIFY"); + kpw->kinput.convEndAtom = MAKEATOM("CONVERSION_END"); + + (void)sprintf(buf, "%s_CONVERSION_VERSION", kpw->kinput.language); + kpw->kinput.convVersionAtom = XInternAtom(dpy, buf, False); + kpw->kinput.convInitialTypeAtom = MAKEATOM("CONVERSION_INITIAL_TYPE"); + kpw->kinput.convOpenNotifyAtom = MAKEATOM("CONVERSION_OPEN_NOTIFY"); + kpw->kinput.convXYRequestAtom = MAKEATOM("CONVERSION_XY_REQUEST"); + kpw->kinput.convFontsRequestAtom = MAKEATOM("CONVERSION_FONTS_REQUEST"); + kpw->kinput.convColorRequestAtom = MAKEATOM("CONVERSION_COLOR_REQUEST"); + kpw->kinput.convCloseNotifyAtom = MAKEATOM("CONVERSION_CLOSE_NOTIFY"); + kpw->kinput.convAttributeAtom = MAKEATOM("CONVERSION_ATTRIBUTE"); + kpw->kinput.xlcStatusAtom = MAKEATOM("_XLC_STATUS"); + kpw->kinput.xlcOnTheSpotAtom = MAKEATOM("_XLC_ON_THE_SPOT"); + kpw->kinput.xlcBcModifierAtom = MAKEATOM("_XLC_BC_MODIFIER"); + kpw->kinput.xlcBcKeycodeAtom = MAKEATOM("_XLC_BC_KEYCODE"); + + /* + * some of the clients who speak Xlc protocol check the existence + * of atom "CONVERSION_REQUEST" before starting conversion. + * so make it at initialization time. + */ + (void)MAKEATOM("CONVERSION_REQUEST"); + +#undef MAKEATOM +} + +/*- ownSelection: own conversion selection -*/ +static Boolean +ownSelection(kpw) +KinputProtocolWidget kpw; +{ + Display *dpy = XtDisplay((Widget)kpw); + Window w = XtWindow((Widget)kpw); + Boolean res; + + TRACE(("kinputProtocol:ownSelection()\n")); + XSetSelectionOwner(dpy, kpw->kinput.convAtom, w, CurrentTime); + /* $B0l1~(B SetSelectionOwner $B$7$?8e(B GetSelectionOwner $B$7$F$_$F3N$+$a$k(B */ + res = XGetSelectionOwner(XtDisplay((Widget)kpw), kpw->kinput.convAtom) == w; + + if (kpw->kinput.backward_compatible) { + XSetSelectionOwner(dpy, kpw->kinput.oldConvAtom, w, CurrentTime); + } + + return res; +} + +/* + *+ client data handling + */ + +/*- findClient: get clientdata of given client -*/ +static ConvClient * +findClient(kpw, client) +KinputProtocolWidget kpw; +Window client; +{ + register ConvClient *ccp = kpw->kinput.clients; + + while (ccp != NULL) { + if (ccp->reqwin == client) return ccp; + ccp = ccp->next; + } + + return NULL; +} + +/*- newClient: get a clientdata for new client -*/ +static ConvClient * +newClient(kpw, client, selection) +KinputProtocolWidget kpw; +Window client; +Atom selection; +{ + ConvClient *ccp; + + ccp = XtNew(ConvClient); + ccp->protocol = unresolved_protocol; + ccp->style = separate_style; /* default */ + ccp->protocolwidget = (Widget)kpw; + ccp->conversion = NULL; + ccp->reqwin = client; + ccp->selection = selection; + ccp->target = None; + ccp->property = None; + ccp->esm = ESMethodNone; + ccp->data = NULL; + ccp->attrmask = 0L; + ccp->validattrmask = 0L; + ccp->start_proc = NULL; + ccp->detach_proc = NULL; + ccp->fix_proc = NULL; + ccp->end_proc = NULL; + ccp->free_resources = NULL; + + ccp->next = kpw->kinput.clients; + kpw->kinput.clients = ccp; + + return ccp; +} + +/*- attachConverter: attach converter to the client -*/ +static Widget +attachConverter(ccp) +ConvClient *ccp; +{ + WidgetClass class; + KinputProtocolWidget kpw = (KinputProtocolWidget)ccp->protocolwidget; + + if (ccp->conversion != NULL) return ccp->conversion; + + if (ccp->protocol == unresolved_protocol) { + ccp->protocol = kinput1_protocol; + ccp->style = separate_style; + } + + if (ccp->style == overthespot_style) { + class = overTheSpotConversionWidgetClass; + } else if (ccp->style == offthespot_style) { + class = offTheSpotConversionWidgetClass; + } else { + class = separateConversionWidgetClass; + } + + ccp->conversion = CMGetConverter(XtParent(ccp->protocolwidget), + ccp->reqwin, class, + kpw->kinput.inputObjClass, + kpw->kinput.displayObjClass); + + return ccp->conversion; +} + +/*- detachConverter: detach converter from client -*/ +static void +detachConverter(ccp) +ConvClient *ccp; +{ + TRACE(("detachConverter(window=0x%lx)\n", ccp->reqwin)); + + XtRemoveCallback(ccp->conversion, XtNtextCallback, + fixCallback, (XtPointer)ccp); + XtRemoveCallback(ccp->conversion, XtNendCallback, + endCallback, (XtPointer)ccp); + + /* call additional detach proc */ + if (ccp->detach_proc != NULL) (*ccp->detach_proc)(ccp); + + CMReleaseConverter(XtParent(ccp->protocolwidget), ccp->conversion); + ccp->conversion = NULL; +} + +/*- jinputDetach: jinput protocol specific detach processing -*/ +static void +jinputDetach(client) +ConvClient *client; +{ + XtRemoveCallback(client->conversion, XtNnewTextCallback, + jinputNewTextCallback, (XtPointer)client); +} + +/*- deleteClient: delete specified client -*/ +static void +deleteClient(client) +ConvClient *client; +{ + KinputProtocolWidget kpw = (KinputProtocolWidget)client->protocolwidget; + ConvClient *ccp, *ccp0; + + TRACE(("deleteClient(window=0x%lx)\n", client->reqwin)); + if (client->conversion != NULL) detachConverter(client); + if (client->free_resources != NULL) (*client->free_resources)(client); + + for (ccp = kpw->kinput.clients, ccp0 = NULL; + ccp != NULL; + ccp0 = ccp, ccp = ccp->next) { + if (ccp == client) { + if (ccp0 == NULL) { + kpw->kinput.clients = ccp->next; + } else { + ccp0->next = ccp->next; + } + XtFree((char *)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(kpw, event) +KinputProtocolWidget kpw; +XEvent *event; +{ + XClientMessageEvent *ev = &(event->xclient); + Atom atom = (Atom)ev->data.l[0]; + + return (ev->window == XtWindow((Widget)kpw) && + ev->format == 32 && + (atom == kpw->kinput.convAtom || + kpw->kinput.backward_compatible && atom == kpw->kinput.oldConvAtom)); +} + +/*- isCorrectWindowID: is the given window ID valid? -*/ +static Boolean +isCorrectWindowID(w, window) +Widget w; +Window window; +{ + int status; + Window root; + int x, y; + unsigned int width, height, bwidth, depth; + XAEHandle h; + + /* + * previous version uses XGetWindowAttributes(), which + * issues both GetWindowAttributes and GetGeometry request. + * since it is a bit inefficient to issue two requests just + * for checking if a window exists, I've changed to use + * XGetGeometry which issues only GetGeometry. + */ + h = XAESetIgnoreErrors(XtDisplay(w)); + status = XGetGeometry(XtDisplay(w), window, &root, &x, &y, + &width, &height, &bwidth, &depth); + XAEUnset(h); + return status == 0 ? False : True; +} + +/*- myStartConversion: custom version of CControlStartConversion -*/ +static void +myStartConversion(w, clwin, mask, attrs) +Widget w; +Window clwin; +unsigned long mask; +ConversionAttributes *attrs; +{ + if (mask & CAFonts) { + mask |= CAStatusFonts; + attrs->status_fonts = attrs->fonts; + attrs->num_status_fonts = attrs->num_fonts; + } else { + mask &= ~CAStatusFonts; + } + CControlStartConversion(w, clwin, mask, attrs); +} + +/*- myChangeAttributes: custom version of CControlChangeAttributes -*/ +static void +myChangeAttributes(w, mask, attrs) +Widget w; +unsigned long mask; +ConversionAttributes *attrs; +{ + if (mask & CAFonts) { + mask |= CAStatusFonts; + attrs->status_fonts = attrs->fonts; + attrs->num_status_fonts = attrs->num_fonts; + } else { + mask &= ~CAStatusFonts; + } + CControlChangeAttributes(w, mask, attrs); +} + +/*- getJinputInitialProperty: get property for jinput protocol and set data -*/ +static void +getJinputInitialProperty(kpw, ccp, prop, reqtype) +KinputProtocolWidget kpw; +ConvClient *ccp; +Atom prop; +Atom reqtype; +{ + Atom type; + int format; + unsigned long nitems; + long *data; + unsigned long bytesafter; + JinputData *jdp = (JinputData *)ccp->data; + + XGetWindowProperty(XtDisplay((Widget)kpw), ccp->reqwin, prop, 0L, 11L, + False, reqtype, &type, &format, &nitems, + &bytesafter, (unsigned char **)&data); + + if (type != reqtype) { + DPRINT(("getJinputInitialProperty(): unknown property type (%ld)\n", type)); + return; + } + + /* data[0]: version number */ + if (data[0] != JINPUT_PROTOCOL_VERSION) { + DPRINT(("getJinputInitialProperty(): unknown version (0x%lx)\n", data[0])); + return; + } + /* data[1]: requestor window */ + if (data[1] != ccp->reqwin) { + return; + } + /* data[2]: preedit type */ + if (data[2] == 3) { + /* over-the-spot */ + ccp->style = overthespot_style; + } else { + ccp->style = separate_style; + } + /* data[3]: fold mode (ignore) */ + /* data[4]: clip mode (ignore) */ + /* data[5]: multi color mode */ + if (data[5] == 0) { + /* fixed color */ + ccp->attrmask |= CAColor; + /* data[6]: background */ + ccp->attrs.background = data[6]; + /* data[7]: foreground */ + ccp->attrs.foreground = data[7]; + } else { + /* query */ + DPRINT(("getJinputInitialProperty(): multi-color mode\n")); + jdp->state |= JINPUT_MULTI_COLOR; + } + /* data[8]: multi font mode */ + if (data[8] == 0) { + ccp->attrmask |= CAFonts; + ccp->attrs.num_fonts = 2; + ccp->attrs.fonts = (XFontStruct **)XtMalloc(sizeof(XFontStruct *) * 2); + /* data[9]: font1 */ + ccp->attrs.fonts[0] = XQueryFont(XtDisplay((Widget)kpw), + (XID)data[9]); /* should make it safe */ + /* data[10]: font2 */ + ccp->attrs.fonts[1] = XQueryFont(XtDisplay((Widget)kpw), + (XID)data[10]); + } else { + /* query */ + TRACE(("getJinputInitialProperty(): multi-font mode\n")); + jdp->state |= JINPUT_MULTI_FONT; + } +} + +/*- jinputSendReq: jinput protocol specific startup processing -*/ +static void +jinputSendReq(client) +ConvClient *client; +{ + JinputData *jdp = (JinputData *)client->data; + + XtAddCallback(client->conversion, XtNnewTextCallback, + jinputNewTextCallback, (XtPointer)client); + + if (jdp->state & JINPUT_MULTI_COLOR) sendColorRequest(client); + if (jdp->state & JINPUT_MULTI_FONT) sendFontRequest(client); + if (client->style != separate_style) sendXYRequest(client); +} + +/*- jinputFreeResources: free resources for jinput protocol client -*/ +static void +jinputFreeResources(client) +ConvClient *client; +{ + XtFree(client->data); + if (client->attrmask & CAFonts) { + /* free fonts */ + XFreeFontInfo((char **)NULL, client->attrs.fonts[0], 1); + XFreeFontInfo((char **)NULL, client->attrs.fonts[1], 1); + XtFree((char *)client->attrs.fonts); + } +} + +/*- xlcFreeResources: free resources for xlc protocol client -*/ +static void +xlcFreeResources(client) +ConvClient *client; +{ + if (client->validattrmask & CAFonts) { + /* free fonts */ + XFreeFontInfo((char **)NULL, client->attrs.fonts[0], 1); + XFreeFontInfo((char **)NULL, client->attrs.fonts[1], 1); + XtFree((char *)client->attrs.fonts); + } +} + +/*- kinput2FreeResources: free resources for kinput2 protocol client -*/ +static void +kinput2FreeResources(client) +ConvClient *client; +{ + if (client->attrmask & CAFonts) { /* free fonts */ + Cardinal i; + for (i = 0; i < client->attrs.num_fonts; i++) { + CachedFreeFont(XtDisplay(client->protocolwidget), + client->attrs.fonts[i]); + } + XtFree((char *)client->attrs.fonts); + } +} + +struct proprec { + Atom prop; + struct proprec *prev; +}; + +/*- setAttribute: set conversion attribute (kinput2 only) -*/ +static void +setAttribute(client, data, precp, fromevent) +ConvClient *client; +unsigned long *data; +struct proprec *precp; /* to prevent possible inifinite loop */ +Boolean fromevent; +{ + int code = CODE_OF_ATTR(*data); + int len = LENGTH_OF_ATTR(*data); + +#define CHECK_LENGTH(num, name) \ + if (len != num) { DPRINT(("\tlength of %s attribute is wrong\n", name)); break; } + + TRACE(("setAttribute()\n")); + data++; + switch (code) { + case CONVATTR_NONE: + TRACE(("\tNone:\n")); + break; + case CONVATTR_INDIRECT: + CHECK_LENGTH(1, "IndirectAttribute"); + TRACE(("\tIndirectAttribute:\n")); + getAttributeFromProperty1(client, data[0], precp, fromevent); + break; + case CONVATTR_FOCUS_WINDOW: + CHECK_LENGTH(1, "FocusWindow"); + client->attrmask |= CAFocusWindow; + client->attrs.focuswindow = data[0]; + TRACE(("\tFocusWindow: 0x%lx\n", data[0])); + break; + case CONVATTR_SPOT_LOCATION: + CHECK_LENGTH(1, "SpotLocation"); + client->attrmask |= CASpotLocation; + client->attrs.spotx = UPPER16S(data[0]); + client->attrs.spoty = LOWER16S(data[0]); + TRACE(("\tSpotLocation: x=%d, y=%d\n", client->attrs.spotx, client->attrs.spoty)); + break; + case CONVATTR_CLIENT_AREA: + CHECK_LENGTH(2, "ClientArea"); + client->attrmask |= CAClientArea; + client->attrs.clientarea.x = UPPER16S(data[0]); + client->attrs.clientarea.y = LOWER16S(data[0]); + client->attrs.clientarea.width = UPPER16U(data[1]); + client->attrs.clientarea.height = LOWER16U(data[1]); + TRACE(("\tClientArea: %d,%d-%d,%d\n", client->attrs.clientarea.x,client->attrs.clientarea.y,client->attrs.clientarea.width,client->attrs.clientarea.height)); + break; + case CONVATTR_STATUS_AREA: + CHECK_LENGTH(2, "StatusArea"); + client->attrmask |= CAStatusArea; + client->attrs.statusarea.x = UPPER16S(data[0]); + client->attrs.statusarea.y = LOWER16S(data[0]); + client->attrs.statusarea.width = UPPER16U(data[1]); + client->attrs.statusarea.height = LOWER16U(data[1]); + TRACE(("\tStatusArea: %d,%d-%d,%d\n", client->attrs.statusarea.x,client->attrs.statusarea.y,client->attrs.statusarea.width,client->attrs.statusarea.height)); + break; + case CONVATTR_COLORMAP: + CHECK_LENGTH(1, "Colormap"); + client->attrmask |= CAColormap; + client->attrs.colormap = data[0]; + TRACE(("\tColormap: 0x%lx\n", data[0])); + break; + case CONVATTR_COLOR: + CHECK_LENGTH(2, "Color"); + client->attrmask |= CAColor; + client->attrs.foreground = data[0]; + client->attrs.background = data[1]; + TRACE(("\tColor: fg=%ld,bg=%ld\n", data[0], data[1])); + break; + case CONVATTR_BACKGROUND_PIXMAP: + CHECK_LENGTH(1, "BackgroundPixmap"); + client->attrmask |= CABackgroundPixmap; + client->attrs.background_pixmap = data[0]; + TRACE(("\tBackgroundPixmap: 0x%lx\n", data[0])); + break; + case CONVATTR_LINE_SPACING: + CHECK_LENGTH(1, "LineSpacing"); + client->attrmask |= CALineSpacing; + client->attrs.linespacing = data[0]; + TRACE(("\tLineSpacing: %ld\n", data[0])); + break; + case CONVATTR_FONT_ATOMS: + if (len < 1) { + DPRINT(("length of FontAtoms attribute is less than 1\n")); + break; + } + if (client->attrmask & CAFonts) { + /* fonts set more than once -- free previously specified fonts */ + Cardinal i; + for (i = 0; i < client->attrs.num_fonts; i++) { + CachedFreeFont(XtDisplay(client->protocolwidget), + client->attrs.fonts[i]); + } + XtFree((char *)client->attrs.fonts); + } + client->attrmask |= CAFonts; + client->attrs.num_fonts = + getFontsByFontAtoms(XtDisplay(client->protocolwidget), + (Atom *)data, (Cardinal)len, + &client->attrs.fonts); + break; + case CONVATTR_CURSOR: + CHECK_LENGTH(1, "Cursor"); + client->attrmask |= CACursor; + client->attrs.cursor = data[0]; + TRACE(("\tCursor: 0x%lx\n", data[0])); + break; + case CONVATTR_INPUT_STYLE: + if (fromevent) { + DPRINT(("InputStyle can't change during conversion\n")); + break; + } + CHECK_LENGTH(1, "InputStyle"); + if (data[0] == CONVARG_OVERTHESPOT) { + client->style = overthespot_style; + } else if (data[0] == CONVARG_OFFTHESPOT) { + client->style = offthespot_style; + } else { + client->style = separate_style; + } + TRACE(("\tInputStyle: %ld\n", data[0])); + break; + case CONVATTR_EVENT_CAPTURE_METHOD: + if (fromevent) { + DPRINT(("EventCaptureMethod can't change during conversion\n")); + break; + } + CHECK_LENGTH(1, "EventCaptureMethod"); + if (data[0] == CONVARG_NONE) { + client->esm = ESMethodNone; + } else if (data[0] == CONVARG_SELECT_FOCUS_WINDOW) { + client->esm = ESMethodSelectFocus; + } else { + client->esm = ESMethodInputOnly; + } + TRACE(("\tEventCaptureMethod: %ld\n", data[0])); + break; + case CONVATTR_USE_EXTENSION: + if (fromevent) { + DPRINT(("UseExtension must be specified at conversion startup\n")); + break; + } + TRACE(("\tUseExtension: not supported\n")); + break; + default: + if (code > 255) { + /* private extension code */ + DPRINT(("\tPrivateExtension (code=%d): not supported\n", code)); + } + } +#undef CHECK_LENGTH +} + +/*- getFontsByFontAtoms: get fonts from 'FONT' atom list (kinput2 only) -*/ +static int +getFontsByFontAtoms(dpy, atoms, len, fontsp) +Display *dpy; +Atom *atoms; +Cardinal len; +XFontStruct *** fontsp; +{ + XFontStruct **fonts; + Cardinal nf, i; + + fonts = (XFontStruct **)XtMalloc(sizeof(XFontStruct *) * len); + for (nf = 0, i = 0; i < len; i++) { + if ((fonts[nf] = CachedLoadQueryFontByProp(dpy, atoms[i])) == NULL) { + TRACE(("\tcan't load font (atom=%ld)\n", atoms[i])); + } else { + TRACE(("\tfont loaded (atom=%ld)\n", atoms[i])); + nf++; + } + } + + *fontsp = fonts; + return nf; +} + +/*- safeGetAttributeProperty: get attribute property safely -*/ +static int +safeGetAttributeProperty(client, prop, data) +ConvClient *client; +Atom prop; +unsigned long **data; +{ + KinputProtocolWidget kpw = (KinputProtocolWidget)client->protocolwidget; + int err; + Atom type; + int format; + unsigned long nitems; + unsigned long bytesafter; + XAEHandle h; + + *data = NULL; + + h = XAESetIgnoreErrors(XtDisplay((Widget)kpw)); + err = XGetWindowProperty(XtDisplay((Widget)kpw), client->reqwin, prop, + 0L, 1000L, False, + kpw->kinput.convAttributeAtom, + &type, &format, &nitems, + &bytesafter, (unsigned char **)data); + XAEUnset(h); + + if (err) { + DPRINT(("\tcan't get property value. bad property (0x%lx)?\n", prop)); + return 0; + }else if (format != 32 || type != kpw->kinput.convAttributeAtom) { + DPRINT(("\twrong format or type\n")); + if (*data != NULL) XtFree((char *)*data); + return 0; + } + + TRACE(("safeGetAttributeProperty(): returns %ld\n", nitems)); + return nitems; +} + +/*- getAttributeFromProperty1: get and set attribute data (kinput2 only) -*/ +static void +getAttributeFromProperty1(client, prop, precp, fromevent) +ConvClient *client; +Atom prop; +struct proprec *precp; +Boolean fromevent; +{ + unsigned long *data, *datap; + int nitems; + struct proprec prec; + + prec.prop = prop; + prec.prev = precp; + + /* try to detect loop */ + while (precp != NULL) { + if (precp->prop == prop) { + DPRINT(("loop detected. property=0x%lx\n", prop)); + return; + } + precp = precp->prev; + } + + data = NULL; + if ((nitems = safeGetAttributeProperty(client, prop, &data)) <= 0) { + return; + } + + datap = data; + while (nitems > 0) { + int alen = LENGTH_OF_ATTR(datap[0]) + 1; /* 1 is for the header */ + + if (alen > nitems) break; + setAttribute(client, datap, &prec, fromevent); + nitems -= alen; + datap += alen; + } + XtFree((char *)data); +} + +/*- getAttributeFromProperty: get and set attribute data (kinput2 only) -*/ +static void +getAttributeFromProperty(client, prop) +ConvClient *client; +Atom prop; +{ + TRACE(("getAttributeFromProperty(reqwin=0x%lx, prop=%ld)\n", client->reqwin, prop)); + getAttributeFromProperty1(client, prop, (struct proprec *)NULL, False); +} + +/*- getAttributeFromEvent: set attribute data specified in an event (kinput2 only) -*/ +static void +getAttributeFromEvent(client, ev) +ConvClient *client; +XEvent *ev; +{ + XClientMessageEvent *event = &(ev->xclient); + unsigned long *data; + int alen; + + TRACE(("getAttributeFromEvent(client=0x%lx)\n", client->reqwin)); + + data = (unsigned long *)&(event->data.l[2]); + alen = LENGTH_OF_ATTR(data[0]); + if (alen >= 3) return; + + setAttribute(client, data, (struct proprec *)NULL, True); +} + +/*- getXlcDataFromProperty: get and set attribute data (xlc only) -*/ +static ConvClient * +getXlcDataFromProperty(kpw, client, initial) +KinputProtocolWidget kpw; +ConvClient *client; +Boolean initial; +{ + unsigned long *data; + Atom type; + int format; + unsigned long nitems; + unsigned long bytesafter; + int flag; + int err; + + TRACE(("getXlcDataFromProperty(initial=%d)\n", initial)); + data = NULL; + err = XGetWindowProperty(XtDisplay((Widget)kpw), XtWindow((Widget)kpw), + kpw->kinput.xlcOnTheSpotAtom, + 0L, 1000L, True, + kpw->kinput.xlcOnTheSpotAtom, + &type, &format, &nitems, + &bytesafter, (unsigned char **)&data); + + if (err) { + DPRINT(("\tcan't get property value\n")); + return NULL; + } else if (/* format != 32 || */ type != kpw->kinput.xlcOnTheSpotAtom) { + DPRINT(("\twrong format(%d) or type(%ld)\n", format, type)); + if (data != NULL) XtFree((char *)data); + return NULL; + } else if (nitems < 17) { + /* it seems that the current implementation of Xlc library + * (client-side library for XLC protocol) has a bug. + * it seems to set number of bytes instead of number of items + * when calling XChangeProperty(). + */ + DPRINT(("\twrong length (%ld)\n", nitems)); + return NULL; + } + + if (client == NULL) { + if ((client = findClient(kpw, (Window)data[1])) == NULL) { + TRACE(("\tcreate new client rec. for the window ID\n")); + /* check validity of the client window ID */ + if (!isCorrectWindowID(kpw, (Window)data[1])) { + DPRINT(("getXlcDataFromProperty(): client window doesn't exist\n")); + return NULL; + } + client = newClient(kpw, (Window)data[1], None); + client->protocol = xlc_protocol; + client->style = overthespot_style; + client->esm = ESMethodInputOnly; + client->end_proc = xlcEnd; + client->free_resources = xlcFreeResources; + MyAddEventHandler(XtDisplay((Widget)kpw), client->reqwin, + DestroyNotify, StructureNotifyMask, + ClientDead, (XtPointer)client); + initial = True; + } + } else { + if (data[1] != client->reqwin) { + DPRINT(("\twindow ID isn't the requestor window ID\n")); + return NULL; + } + } + TRACE(("\t%ld items read\n", nitems)); + + flag = data[0]; + if (flag & XLC_AUTO_REPLACE) { + /* set auto spot forwarding */ + /* XXX */ + TRACE(("\tauto spot forwarding\n")); + } + if (flag & XLC_ALL_INFORMATION) { + /* foreground / background colors */ + if (!initial && + (client->validattrmask & CAColor) && + client->attrs.foreground == data[5] && + client->attrs.background == data[6]) { + client->attrmask &= ~CAColor; + } else { + client->attrs.foreground = data[5]; + client->attrs.background = data[6]; + client->attrmask |= CAColor; + } + client->validattrmask |= CAColor; + TRACE(("\tColor: fg=%ld,bg=%ld\n", data[5], data[6])); + + /* fonts */ + if (data[7] == 0L || data[8] == 0L) { + /* invalid */ + if (client->validattrmask & CAFonts) { + XFreeFontInfo((char **)NULL, client->attrs.fonts[0], 1); + XFreeFontInfo((char **)NULL, client->attrs.fonts[2], 1); + XFree((char *)client->attrs.fonts); + client->validattrmask &= ~CAFonts; + } + client->attrmask &= ~CAFonts; + } else { + if (!initial && + (client->validattrmask & CAFonts) && + client->attrs.fonts[0]->fid == data[7] && + client->attrs.fonts[1]->fid == data[8]) { + client->attrmask &= ~CAFonts; + } else if (client->validattrmask & CAFonts) { + if (client->attrs.fonts[0]->fid != data[7]) { + XFreeFontInfo((char **)NULL, client->attrs.fonts[0], 1); + client->attrs.fonts[0] = XQueryFont(XtDisplay((Widget)kpw), + (XID)data[7]); + } + if (client->attrs.fonts[1]->fid != data[8]) { + XFreeFontInfo((char **)NULL, client->attrs.fonts[1], 1); + client->attrs.fonts[1] = XQueryFont(XtDisplay((Widget)kpw), + (XID)data[8]); + } + client->attrmask |= CAFonts; + } else { + client->attrs.num_fonts = 2; + client->attrs.fonts = + (XFontStruct **)XtMalloc(sizeof(XFontStruct *) * 2); + client->attrs.fonts[0] = XQueryFont(XtDisplay((Widget)kpw), + (XID)data[7]); + client->attrs.fonts[1] = XQueryFont(XtDisplay((Widget)kpw), + (XID)data[8]); + client->attrmask |= CAFonts; + } + client->validattrmask |= CAFonts; + TRACE(("\tFonts: 0x%lx, 0x%lx\n", data[7], data[8])); + } + } + if (flag & (XLC_ALL_INFORMATION | XLC_FRAME_INFORMATION)) { + /* frame (client area) */ + if (!initial && + (client->validattrmask & CAClientArea) && + client->attrs.clientarea.x == data[10] && + client->attrs.clientarea.y == data[11] && + client->attrs.clientarea.width == data[12] && + client->attrs.clientarea.height == data[13]) { + client->attrmask &= ~CAClientArea; + } else { + client->attrs.clientarea.x = data[10]; + client->attrs.clientarea.y = data[11]; + client->attrs.clientarea.width = data[12]; + client->attrs.clientarea.height = data[13]; + if (data[12] == 0 || data[13] == 0) { + client->attrmask &= ~CAClientArea; + } else { + client->attrmask |= CAClientArea; + } + } + client->validattrmask |= CAClientArea; + TRACE(("\tClientArea: %ld,%ld-%ld,%ld\n", + data[10],data[11],data[12],data[13])); + } + if (flag & (XLC_ALL_INFORMATION | XLC_FRAME_INFORMATION | XLC_OFFSET_INFORMATION)) { + /* spot location */ + client->attrmask |= CASpotLocation; /* always set spot location */ + client->attrs.spotx = data[14]; + client->attrs.spoty = data[15] + + ((client->validattrmask & CAFonts) ? + client->attrs.fonts[0]->ascent : kpw->kinput.defaultascent); + client->validattrmask |= CASpotLocation; + TRACE(("\tSpotLocation: x=%ld, y=%ld\n", data[14], data[15])); + + /* line spacing */ + if (!initial && + (client->validattrmask & CALineSpacing) && + client->attrs.linespacing == data[16]) { + client->attrmask &= ~CALineSpacing; + } else { + client->attrmask |= CALineSpacing; + client->attrs.linespacing = data[16]; + } + client->validattrmask |= CALineSpacing; + TRACE(("\tLineSpacing: %ld\n", data[16])); + } + + XtFree((char *)data); + return client; +} + +/*- 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); +} + +/*- parseKeyEvent: parses key event description and get keycode and modmask -*/ +static int +parseKeyEvent(dpy, s, codep, modmaskp) +Display *dpy; +char *s; +long *codep; +long *modmaskp; +{ + char *mod, *key; + char *p; + KeySym keysym; + char buf[128]; + + /* + * keyevent description (stored in argument 's') must be of the + * following format: + * modifier-list<Key>keysym + * modifier-list is a combination of: + * Ctrl, Shift, Lock, Meta, Alt, Mod1, Mod2, Mod3, Mod4, Mod5 + */ + + strcpy(buf, s); + + /* find "<Key>" */ + if ((p = mystrstr(buf, "<Key>")) != NULL) { + key = p + 5; /* p + strlen("<Key>") */ + } else if ((p = mystrstr(buf, "<KeyPress>")) != NULL) { + key = p + 10; /* p + strlen("<KeyPress>") */ + } else if ((p = mystrstr(buf, "<KeyDown>")) != NULL) { + key = p + 9; /* p + strlen("<KeyDown>") */ + } else { + return 0; + } + mod = buf; + *p = '\0'; + + /* get modifier mask */ + *modmaskp = 0; + if (mystrstr(mod, "Shift")) *modmaskp |= ShiftMask; + if (mystrstr(mod, "Lock")) *modmaskp |= LockMask; + if (mystrstr(mod, "Ctrl")) *modmaskp |= ControlMask; + if (mystrstr(mod, "Mod1")) *modmaskp |= Mod1Mask; + if (mystrstr(mod, "Mod2")) *modmaskp |= Mod2Mask; + if (mystrstr(mod, "Mod3")) *modmaskp |= Mod3Mask; + if (mystrstr(mod, "Mod4")) *modmaskp |= Mod4Mask; + if (mystrstr(mod, "Mod5")) *modmaskp |= Mod5Mask; + if (mystrstr(mod, "Meta")) *modmaskp |= Mod1Mask; + if (mystrstr(mod, "Alt")) *modmaskp |= Mod1Mask; + + /* get keycode */ + if ((keysym = XStringToKeysym(key)) == NoSymbol) return 0; + if ((*codep = (long)XKeysymToKeycode(dpy, keysym)) == 0) return 0; + return 1; +} + +/*- mystrstr: not-so-good implementaion of ANSI strstr() -*/ +static char * +mystrstr(s1, s2) +char *s1; +char *s2; +{ + char *p, *q; + + while (*(p = s1++) != '\0') { + q = s2; + do { + if (*q == '\0') return s1 - 1; + } while (*p++ == *q++); + } + return NULL; +} + +/*- getDefaultFontHeight: get default font ascent (for xlc protocol) -*/ +static void +getDefaultFontHeight(kpw) +KinputProtocolWidget kpw; +{ + Widget obj; + + obj = XtCreateWidget("displayObj", kpw->kinput.displayObjClass, + (Widget)kpw, NULL, 0); + (void)CDLineHeight(obj, &kpw->kinput.defaultascent); + XtDestroyWidget(obj); +} + +/* + *+ property handling + */ + +/*- setJinputProperty: set version property for jinput protocol -*/ +static void +setJinputProperty(kpw) +KinputProtocolWidget kpw; +{ + long version; + + version = JINPUT_PROTOCOL_VERSION; + XChangeProperty(XtDisplay((Widget)kpw), XtWindow((Widget)kpw), + kpw->kinput.convVersionAtom, XA_INTEGER, 32, + PropModeReplace, (unsigned char *)&version, 1); +} + +/*- setKinput2Property: set version property for kinput2 protocol -*/ +static void +setKinput2Property(kpw) +KinputProtocolWidget kpw; +{ + Display *dpy = XtDisplay((Widget)kpw); + Atom property; + Atom type; + unsigned long profile[10]; + + property = XInternAtom(dpy, CONVERSION_PROFILE, False); + type = XInternAtom(dpy, CONVERSION_ATTRIBUTE_TYPE, False); + + profile[0] = CONV_ATTR(CONVPROF_PROTOCOL_VERSION, 1); + profile[1] = XInternAtom(dpy, PROTOCOL_VERSION, False); + profile[2] = CONV_ATTR(CONVPROF_SUPPORTED_STYLES, 1); + profile[3] = CONVARG_ROOTWINDOW|CONVARG_OFFTHESPOT|CONVARG_OVERTHESPOT; + + XChangeProperty(dpy, XtWindow((Widget)kpw), + property, type, 32, PropModeReplace, + (unsigned char *)profile, 4); +} + +/*- setXlcProperty: set initial status property for xlc protocol -*/ +static void +setXlcProperty(kpw) +KinputProtocolWidget kpw; +{ + setXlcStatusProperty(kpw, 0); + setXlcBCKey(kpw); +} + +/*- setXlcStatusProperty: set status property for xlc protocol -*/ +static void +setXlcStatusProperty(kpw, status) +KinputProtocolWidget kpw; +int status; +{ + XChangeProperty(XtDisplay((Widget)kpw), XtWindow((Widget)kpw), + kpw->kinput.xlcStatusAtom, XA_INTEGER, 32, + PropModeReplace, (unsigned char *)&status, 1); +} + +/*- setXlcBCKey: set conversion start key code property for xlc protocol -*/ +static void +setXlcBCKey(kpw) +KinputProtocolWidget kpw; +{ + long code; + long mask; + + if (kpw->kinput.xlcstartkey == NULL) return; + if (parseKeyEvent(XtDisplay((Widget)kpw), kpw->kinput.xlcstartkey, + &code, &mask) == 0) { + String params[1]; + Cardinal num_params; + + params[0] = XtClass((Widget)kpw)->core_class.class_name; + num_params = 1; + XtAppWarningMsg(XtWidgetToApplicationContext((Widget)kpw), + "parseError", XtNxlcConversionStartKey, "WidgetError", + "%s: can't parse coversion start key string", + params, &num_params); + return; + } + + TRACE(("setXlcBCKey(): keycode=%ld, mask=0x%lx\n", code, mask)); + + XChangeProperty(XtDisplay((Widget)kpw), XtWindow((Widget)kpw), + kpw->kinput.xlcBcModifierAtom, XA_INTEGER, 32, + PropModeReplace, (unsigned char *)&mask, 1); + XChangeProperty(XtDisplay((Widget)kpw), XtWindow((Widget)kpw), + kpw->kinput.xlcBcKeycodeAtom, XA_INTEGER, 32, + PropModeReplace, (unsigned char *)&code, 1); +} + +/* + *+ event sending + */ + +/*- sendClientMessage: send a clientmessage event -*/ +static void +sendClientMessage(dpy, window, type, l0, l1, l2, l3, l4) +Display *dpy; +Window window; +Atom type; +long l0, l1, l2, l3, l4; +{ + XEvent event; + + event.xclient.type = ClientMessage; + event.xclient.window = window; + event.xclient.message_type = type; + event.xclient.format = 32; + event.xclient.data.l[0] = l0; + 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(dpy, window, False, NoEventMask, &event); +} + +/*- sendNegativeConversionNotify: send negative conversion-notify event -*/ +static void +sendNegativeConversionNotify(kpw, window, selection) +KinputProtocolWidget kpw; +Window window; +Atom selection; +{ + TRACE(("sendNegativeConversionNotify(window=0x%lx)\n", window)); + sendClientMessage(XtDisplay((Widget)kpw), window, + kpw->kinput.convNotifyAtom, + (long)selection, (long)None, 0L, 0L, 0L); +} + +/*- sendConversionNotify: send conversion-notify event -*/ +static void +sendConversionNotify(ccp) +ConvClient *ccp; +{ + KinputProtocolWidget kpw = (KinputProtocolWidget)ccp->protocolwidget; + long l4; + + TRACE(("sendConversionNotify(reqwin=0x%lx)\n", ccp->reqwin)); + if (ccp->protocol == xlc_protocol) { + l4 = (long)kpw->kinput.xlcOnTheSpotAtom; + } else { + l4 = 0L; + } + sendClientMessage(XtDisplay((Widget)kpw), ccp->reqwin, + kpw->kinput.convNotifyAtom, + (long)ccp->selection, (long)ccp->target, + (long)ccp->property, (long)XtWindow(ccp->conversion), + l4); +} + +/*- sendNegativeConversionOpenNotify: send negative conversion-open-notify event -*/ +static void +sendNegativeConversionOpenNotify(kpw, window, selection) +KinputProtocolWidget kpw; +Window window; +Atom selection; +{ + TRACE(("sendNegativeConversionOpenNotify(window=0x%lx)\n", window)); + sendClientMessage(XtDisplay((Widget)kpw), window, + kpw->kinput.convOpenNotifyAtom, + (long)selection, 0L, (long)XtWindow(kpw), 0L, 0L); +} + +/*- sendConversionOpenNotify: send conversion-open-notify event -*/ +static void +sendConversionOpenNotify(ccp) +ConvClient *ccp; +{ + KinputProtocolWidget kpw = (KinputProtocolWidget)ccp->protocolwidget; + + TRACE(("sendConversionOpenNotify(reqwin=0x%lx)\n", ccp->reqwin)); + sendClientMessage(XtDisplay((Widget)kpw), ccp->reqwin, + kpw->kinput.convOpenNotifyAtom, + (long)ccp->selection, JINPUT_PROTOCOL_VERSION, + (long)XtWindow((Widget)kpw), 1L, 0L); +} + +/*- sendColorRequest: send color-request event (jinput only) -*/ +static void +sendColorRequest(ccp) +ConvClient *ccp; +{ + KinputProtocolWidget kpw = (KinputProtocolWidget)ccp->protocolwidget; + + TRACE(("sendColorRequest(reqwin=0x%lx)\n", ccp->reqwin)); + sendClientMessage(XtDisplay((Widget)kpw), ccp->reqwin, + kpw->kinput.convColorRequestAtom, + (long)ccp->selection, JINPUT_PROTOCOL_VERSION, + (long)XtWindow((Widget)kpw), 0L, 0L); +} + +/*- sendFontRequest: send font-request event (jinput only) -*/ +static void +sendFontRequest(ccp) +ConvClient *ccp; +{ + KinputProtocolWidget kpw = (KinputProtocolWidget)ccp->protocolwidget; + + TRACE(("sendFontRequest(reqwin=0x%lx)\n", ccp->reqwin)); + sendClientMessage(XtDisplay((Widget)kpw), ccp->reqwin, + kpw->kinput.convFontsRequestAtom, + (long)ccp->selection, JINPUT_PROTOCOL_VERSION, + (long)XtWindow((Widget)kpw), 0L, 0L); +} + +/*- sendXYRequest: send XY-request event (jinput only) -*/ +static void +sendXYRequest(ccp) +ConvClient *ccp; +{ + KinputProtocolWidget kpw = (KinputProtocolWidget)ccp->protocolwidget; + + TRACE(("sendXYRequest(reqwin=0x%lx)\n", ccp->reqwin)); + sendClientMessage(XtDisplay((Widget)kpw), ccp->reqwin, + kpw->kinput.convXYRequestAtom, + (long)ccp->selection, JINPUT_PROTOCOL_VERSION, + (long)XtWindow((Widget)kpw), 0L, 0L); +} + +/* + *+ 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; +{ + /* Property $B$K7k2L$r%;%C%H$9$k(B */ + XChangeProperty(XtDisplay(client->conversion), client->reqwin, + client->property, arg->encoding, arg->format, + PropModeAppend, (unsigned char *)arg->text, arg->length); + + /* call protocol dependent proc */ + if (client->fix_proc != NULL) (*client->fix_proc)(client, arg); +} + +/*- jinputFix: jinput protocol specific fix processing -*/ +static void +jinputFix(client) +ConvClient *client; +{ + JinputData *jdp = (JinputData *)client->data; + + /* spotlocation information is no longer valid */ + client->attrmask &= ~CASpotLocation; + + if (jdp->state & JINPUT_MULTI_COLOR) sendColorRequest(client); + if (jdp->state & JINPUT_MULTI_FONT) sendFontRequest(client); + if (client->style != separate_style) sendXYRequest(client); +} + +/*- 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; +{ + KinputProtocolWidget kpw = (KinputProtocolWidget)client->protocolwidget; + + if (!abort) { + /* $B%/%i%$%"%s%H$K(B ClientMessage $B$rAw$C$FCN$i$;$k(B + * $B%$%Y%s%H$N%U%)!<%^%C%H$O!"(B + * window: requestor window + * message_type: "CONVERSION_END" + * format: 32 + * data.l[0]: selection + * data.l[1]: selection-owner window + */ + sendClientMessage(XtDisplay((Widget)kpw), client->reqwin, + kpw->kinput.convEndAtom, + (long)client->selection, (long)XtWindow((Widget)kpw), + 0L, 0L, 0L); + } + if (client->conversion != NULL) { + XtRemoveCallback(client->conversion, XtNtextCallback, + fixCallback, (XtPointer)client); + XtRemoveCallback(client->conversion, XtNendCallback, + endCallback, (XtPointer)client); + } + + /* call protocol dependent proc */ + if (client->end_proc != NULL) { + (*client->end_proc)(client, abort); + } else { + deleteClient(client); + } +} + +/*- jinputEnd: jinput specific conversion end processing -*/ +/* ARGSUSED */ +static void +jinputEnd(client, abort) +ConvClient *client; +Boolean abort; /* UNUSED */ +{ + /* don't delete client until CONVERSION_CLOSE_REQUEST or client dead */ + detachConverter(client); + + client->attrmask &= ~CASpotLocation; +} + +/*- jinputNewTextCallback: jinput protocol specific new text callback -*/ +/* ARGSUSED */ +static void +jinputNewTextCallback(w, client_data, call_data) +Widget w; +XtPointer client_data; +XtPointer call_data; +{ + ConvClient *ccp = (ConvClient *)client_data; + + TRACE(("jinputNewTextCallback(reqwin=0x%lx)\n",ccp->reqwin)); + if (ccp->style != separate_style) sendXYRequest(ccp); +} + +/*- xlcEnd: xlc specific conversion end processing -*/ +/* ARGSUSED */ +static void +xlcEnd(client, abort) +ConvClient *client; +Boolean abort; /* UNUSED */ +{ + /* don't delete client until CONVERSION_CLOSE or client dead */ + detachConverter(client); + + client->attrmask &= ~CASpotLocation; +} + +/* + *+ ClientMessage event handler + */ + +/*- ConversionOpenRequestProc: CONVERSION_OPEN_REQUEST event handler -*/ +/* ARGSUSED */ +static void +ConversionOpenRequestProc(w, event, args, num_args) +Widget w; +XEvent *event; +String *args; +Cardinal *num_args; +{ + KinputProtocolWidget kpw = (KinputProtocolWidget)w; + XClientMessageEvent *ev = &event->xclient; + int version; + Window reqwin; + Atom convatom; + Atom initproperty; + Atom initpropertytype; + ConvClient *ccp; + JinputData *jdp; + + TRACE(("ConversionOpenRequestProc(window=0x%lx)\n", ev->data.l[2])); + /* is it a correct event? */ + if (!isCorrectClientEvent(kpw, event)) { + /*ignore */ + DPRINT(("got invalid clientmessage event.\n")); + return; + } + + convatom = (Atom)ev->data.l[0]; + version = ev->data.l[1]; + reqwin = (Window)ev->data.l[2]; + initproperty = ev->data.l[3]; + initpropertytype = ev->data.l[4]; + + /* check the protocol version & initial property type */ + /* also check if the client already opened connection */ + if (version != JINPUT_PROTOCOL_VERSION || + initpropertytype != kpw->kinput.convInitialTypeAtom || + findClient(kpw, reqwin) != NULL) { + DPRINT(("ConversionOpenRequestProc(): open denied\n")); + sendNegativeConversionOpenNotify(kpw, reqwin, convatom); + return; + } + /* check validity of the client window ID */ + if (!isCorrectWindowID(w, reqwin)) { + DPRINT(("ConversionOpenRequestProc(): requestor window doesn't exist\n")); + /* nothing to do */ + return; + } + + ccp = newClient(kpw, reqwin, convatom); + ccp->protocol = jinput_protocol; + ccp->esm = ESMethodInputOnly; + ccp->start_proc = jinputSendReq; + ccp->detach_proc = jinputDetach; + ccp->fix_proc = jinputFix; + ccp->end_proc = jinputEnd; + ccp->free_resources = jinputFreeResources; + jdp = XtNew(JinputData); + jdp->state = 0; + ccp->data = (XtPointer)jdp; + + getJinputInitialProperty(kpw, ccp, initproperty, initpropertytype); + + /* watch for client destroy */ + MyAddEventHandler(XtDisplay(w), reqwin, DestroyNotify, StructureNotifyMask, + ClientDead, (XtPointer)ccp); + + sendConversionOpenNotify(ccp); +} + +/*- ConversionRequestProc: CONVERSION_REQUEST event handler -*/ +/* ARGSUSED */ +static void +ConversionRequestProc(w, event, args, num_args) +Widget w; +XEvent *event; +String *args; +Cardinal *num_args; +{ + KinputProtocolWidget kpw = (KinputProtocolWidget)w; + XClientMessageEvent *ev = &event->xclient; + Window reqwin; + Atom convatom; + Atom prop; + ConvClient *ccp; + + TRACE(("ConversionRequestProc(window=0x%lx)\n", ev->data.l[1])); + /* is it a correct event? */ + if (!isCorrectClientEvent(kpw, event)) { + /*ignore */ + DPRINT(("got invalid clientmessage event.\n")); + return; + } + + convatom = (Atom)ev->data.l[0]; + reqwin = (Window)ev->data.l[1]; + prop = (Atom)ev->data.l[4]; + TRACE(("\tatom=0x%lx, reqwin=0x%lx\n", convatom, reqwin)); + + if ((ccp = findClient(kpw, reqwin)) == NULL) { + /* check validity of the client window ID */ + if (!isCorrectWindowID(w, reqwin)) { + DPRINT(("ConversionRequestProc(): requestor window doesn't exist\n")); + /* nothing to do */ + return; + } + ccp = newClient(kpw, reqwin, convatom); + if (prop == kpw->kinput.xlcOnTheSpotAtom) { + /* xlc protocol */ + TRACE(("\txlc protocol\n")); + if (getXlcDataFromProperty(kpw, ccp, True)) { + ccp->protocol = xlc_protocol; + ccp->style = overthespot_style; + ccp->esm = ESMethodInputOnly; + ccp->end_proc = xlcEnd; + ccp->free_resources = xlcFreeResources; + MyAddEventHandler(XtDisplay(w), reqwin, + DestroyNotify, StructureNotifyMask, + ClientDead, (XtPointer)ccp); + } else { + TRACE(("\tchanged to kinput protocol\n")); + ccp->protocol = kinput1_protocol; + ccp->style = separate_style; + ccp->esm = ESMethodInputOnly; + } + } else if (prop != None) { + /* kinput2 protocol */ + TRACE(("\tkinput2 protocol\n")); + ccp->protocol = kinput2_protocol; + ccp->style = separate_style; + ccp->esm = ESMethodInputOnly; + ccp->free_resources = kinput2FreeResources; + /* ccp->fix_proc = sendKeyCode0 */ + getAttributeFromProperty(ccp, prop); + } else { + /* old protocol */ + ccp->protocol = kinput1_protocol; + ccp->style = separate_style; + ccp->esm = ESMethodInputOnly; + } + } else if (ccp->conversion != NULL) { + /* now converting */ + if (ccp->protocol == xlc_protocol) { + /* + * xlc protocol uses CONVERSION_REQUEST event to notify + * the frontend of changing conversion attribute + */ + return; + } + sendNegativeConversionNotify(kpw, reqwin, convatom); + return; + } else if (ccp->protocol == xlc_protocol) { + if (prop == kpw->kinput.xlcOnTheSpotAtom) { + /* reread property before starting conversion */ + (void)getXlcDataFromProperty(kpw, ccp, False); + } + } + + /* set convatom */ + if (ccp->selection == None) ccp->selection = convatom; + + if (attachConverter(ccp) == NULL) { + sendNegativeConversionNotify(kpw, reqwin, convatom); + return; + } + + /* set target type (ignore client's request) */ + ccp->target = kpw->kinput.ctextAtom; + + /* use default property if not specified */ + if ((ccp->property = ev->data.l[3]) == None) { + ccp->property = kpw->kinput.convStringAtom; + } + + XtAddCallback(ccp->conversion, XtNtextCallback, + fixCallback, (XtPointer)ccp); + XtAddCallback(ccp->conversion, XtNendCallback, + endCallback, (XtPointer)ccp); + + /* startup the conversion window */ + XtVaSetValues(ccp->conversion, XtNeventSelectMethod, ccp->esm, NULL); + myStartConversion(ccp->conversion, ccp->reqwin, + ccp->attrmask, &ccp->attrs); + + /* send ConversionNotify to the client */ + sendConversionNotify(ccp); + + if (ccp->start_proc != NULL) (*ccp->start_proc)(ccp); +} + +/*- ConversionEndRequestProc: CONVERSION_END_REQUEST event handler -*/ +/* ARGSUSED */ +static void +ConversionEndRequestProc(w, event, args, num_args) +Widget w; +XEvent *event; +String *args; +Cardinal *num_args; +{ + KinputProtocolWidget kpw = (KinputProtocolWidget)w; + XClientMessageEvent *ev = &event->xclient; + Window reqwin; + ConvClient *ccp; + + TRACE(("ConversionEndRequestProc(window=0x%lx)\n", ev->data.l[1])); + /* is it a correct event? */ + if (!isCorrectClientEvent(kpw, event)) { + /*ignore */ + DPRINT(("got invalid clientmessage event.\n")); + return; + } + + reqwin = (Window)ev->data.l[1]; + if ((ccp = findClient(kpw, reqwin)) == NULL) { + /* request from unknown client. just ignore */ + DPRINT(("got conversion end request from unknown window\n")); + return; + } + + if (ccp->conversion != NULL) { + CControlEndConversion(ccp->conversion); + endProc(ccp, False); + } +} + +/*- ConversionCloseRequestProc: CONVERSION_CLOSE_REQUEST event handler (jinput only) -*/ +/* ARGSUSED */ +static void +ConversionCloseRequestProc(w, event, args, num_args) +Widget w; +XEvent *event; +String *args; +Cardinal *num_args; +{ + KinputProtocolWidget kpw = (KinputProtocolWidget)w; + XClientMessageEvent *ev = &event->xclient; + Window reqwin; + ConvClient *ccp; + + TRACE(("ConversionCloseRequestProc(window=0x%lx)\n", ev->data.l[2])); + /* is it a correct event? */ + if (!isCorrectClientEvent(kpw, event)) { + /*ignore */ + DPRINT(("got invalid clientmessage event.\n")); + return; + } + if (ev->data.l[1] != JINPUT_PROTOCOL_VERSION) { + /* wrong version number */ + DPRINT(("ConversionCloseRequestProc(): unknown version number (0x%lx)\n", ev->data.l[1])); + return; + } + reqwin = ev->data.l[2]; + if ((ccp = findClient(kpw, reqwin)) == NULL) { + /* request from unknown client. just ignore */ + DPRINT(("got conversion end request from unknown window\n")); + return; + } + if (ccp->protocol != jinput_protocol) { + DPRINT(("got jinput-specific event from a client that use other protocol\n")); + return; + } + + MyRemoveEventHandler(XtDisplay(w), reqwin, DestroyNotify, + ClientDead, (XtPointer)ccp); + + deleteClient(ccp); +} + +/*- ConversionXYNotifyProc: CONVERSION_XY_NOTIFY event handler (jinput only) -*/ +/* ARGSUSED */ +static void +ConversionXYNotifyProc(w, event, args, num_args) +Widget w; +XEvent *event; +String *args; +Cardinal *num_args; +{ + KinputProtocolWidget kpw = (KinputProtocolWidget)w; + XClientMessageEvent *ev = &event->xclient; + Window reqwin; + ConvClient *ccp; + JinputData *jdp; + + TRACE(("ConversionXYNotifyProc(window=0x%lx)\n", ev->data.l[2])); + /* is it a correct event? */ + if (!isCorrectClientEvent(kpw, event)) { + /*ignore */ + DPRINT(("got invalid clientmessage event.\n")); + return; + } + if (ev->data.l[1] != JINPUT_PROTOCOL_VERSION) { + /* wrong version number */ + DPRINT(("ConversionXYNotifyProc(): unknown version number (0x%lx)\n", ev->data.l[1])); + return; + } + reqwin = ev->data.l[2]; + if ((ccp = findClient(kpw, reqwin)) == NULL) { + /* request from unknown client. just ignore */ + DPRINT(("got conversion end request from unknown window\n")); + return; + } + if (ccp->protocol != jinput_protocol) { + DPRINT(("got jinput-specific event from a client that use other protocol\n")); + return; + } + jdp = (JinputData *)ccp->data; + jdp->rawspotx = ev->data.l[3]; + jdp->rawspoty = ev->data.l[4]; + + ccp->attrmask |= CASpotLocation; + ccp->attrs.spotx = jdp->rawspotx; + ccp->attrs.spoty = jdp->rawspoty + + ((ccp->attrmask & CAFonts) ? ccp->attrs.fonts[0]->ascent : 0); + + if (ccp->conversion != NULL) { + myChangeAttributes(ccp->conversion, CASpotLocation, &ccp->attrs); + } +} + +/*- ConversionColorNotifyProc: CONVERSION_COLOR_NOTIFY event handler (jinput only) -*/ +/* ARGSUSED */ +static void +ConversionColorNotifyProc(w, event, args, num_args) +Widget w; +XEvent *event; +String *args; +Cardinal *num_args; +{ + KinputProtocolWidget kpw = (KinputProtocolWidget)w; + XClientMessageEvent *ev = &event->xclient; + Window reqwin; + ConvClient *ccp; + + TRACE(("ConversionColorNotifyProc(window=0x%lx)\n", ev->data.l[2])); + /* is it a correct event? */ + if (!isCorrectClientEvent(kpw, event)) { + /*ignore */ + DPRINT(("got invalid clientmessage event.\n")); + return; + } + if (ev->data.l[1] != JINPUT_PROTOCOL_VERSION) { + /* wrong version number */ + DPRINT(("ConversionColorNotifyProc(): unknown version number (0x%lx)\n", ev->data.l[1])); + return; + } + reqwin = ev->data.l[2]; + if ((ccp = findClient(kpw, reqwin)) == NULL) { + /* request from unknown client. just ignore */ + DPRINT(("got conversion end request from unknown window\n")); + return; + } + if (ccp->protocol != jinput_protocol) { + DPRINT(("got jinput-specific event from a client that use other protocol\n")); + return; + } + ccp->attrmask |= CAColor; + ccp->attrs.background = ev->data.l[3]; + ccp->attrs.foreground = ev->data.l[4]; + + if (ccp->conversion != NULL) { + myChangeAttributes(ccp->conversion, CAColor, &ccp->attrs); + } +} + +/*- ConversionFontsNotifyProc: CONVERSION_FONTS_NOTIFY event handler (jinput only) -*/ +/* ARGSUSED */ +static void +ConversionFontsNotifyProc(w, event, args, num_args) +Widget w; +XEvent *event; +String *args; +Cardinal *num_args; +{ + KinputProtocolWidget kpw = (KinputProtocolWidget)w; + XClientMessageEvent *ev = &event->xclient; + Window reqwin; + ConvClient *ccp; + unsigned long attrmask; + + TRACE(("ConversionFontsNotifyProc(window=0x%lx)\n", ev->data.l[2])); + /* is it a correct event? */ + if (!isCorrectClientEvent(kpw, event)) { + /*ignore */ + DPRINT(("got invalid clientmessage event.\n")); + return; + } + if (ev->data.l[1] != JINPUT_PROTOCOL_VERSION) { + /* wrong version number */ + DPRINT(("ConversionFontsNotifyProc(): unknown version number (0x%lx)\n", ev->data.l[1])); + return; + } + reqwin = ev->data.l[2]; + if ((ccp = findClient(kpw, reqwin)) == NULL) { + /* request from unknown client. just ignore */ + DPRINT(("got conversion end request from unknown window\n")); + return; + } + if (ccp->protocol != jinput_protocol) { + DPRINT(("got jinput-specific event from a client that use other protocol\n")); + return; + } + + attrmask = CAFonts; + if (ccp->attrmask & CAFonts) { + if (ccp->attrs.fonts[0]->fid != ev->data.l[3]) { + XFreeFontInfo((char **)NULL, ccp->attrs.fonts[0], 1); + ccp->attrs.fonts[0] = XQueryFont(XtDisplay((Widget)kpw), + (XID)ev->data.l[3]); + } + if (ccp->attrs.fonts[1]->fid != ev->data.l[4]) { + XFreeFontInfo((char **)NULL, ccp->attrs.fonts[1], 1); + ccp->attrs.fonts[1] = XQueryFont(XtDisplay((Widget)kpw), + (XID)ev->data.l[4]); + } + } else { + ccp->attrmask |= CAFonts; + ccp->attrs.num_fonts = 2; + ccp->attrs.fonts = (XFontStruct **)XtMalloc(sizeof(XFontStruct *) * 2); + ccp->attrs.fonts[0] = XQueryFont(XtDisplay((Widget)kpw), + (XID)ev->data.l[3]); + ccp->attrs.fonts[1] = XQueryFont(XtDisplay((Widget)kpw), + (XID)ev->data.l[4]); + } + if (ccp->attrmask & CASpotLocation) { + JinputData *jdp = (JinputData *)ccp->data; + ccp->attrs.spotx = jdp->rawspotx; + ccp->attrs.spoty = jdp->rawspoty + ccp->attrs.fonts[0]->ascent; + attrmask |= CASpotLocation; + } + + if (ccp->conversion != NULL) { + myChangeAttributes(ccp->conversion, attrmask, &ccp->attrs); + } +} + +/*- ConversionAttributeNotifyProc: CONVERSION_ATTRIBUTE_NOTIFY event handler (kinput2 only) -*/ +/* ARGSUSED */ +static void +ConversionAttributeNotifyProc(w, event, args, num_args) +Widget w; +XEvent *event; +String *args; +Cardinal *num_args; +{ + KinputProtocolWidget kpw = (KinputProtocolWidget)w; + XClientMessageEvent *ev = &event->xclient; + Window reqwin; + ConvClient *ccp; + XFontStruct **fonts; + Cardinal nfonts; + + TRACE(("ConversionAttributeNotifyProc(window=0x%lx)\n", ev->data.l[1])); + /* is it a correct event? */ + if (!isCorrectClientEvent(kpw, event)) { + /*ignore */ + DPRINT(("got invalid clientmessage event.\n")); + return; + } + + reqwin = (Window)ev->data.l[1]; + TRACE(("\treqwin=0x%lx\n", reqwin)); + + if ((ccp = findClient(kpw, reqwin)) == NULL) { + /* request from unknown client. just ignore */ + DPRINT(("got conversion attribute request from unknown window\n")); + return; + } else if (ccp->protocol != kinput2_protocol) { + DPRINT(("get conversion attribute request from a client that doesn't use kinput2 protocol\n")); + return; + } else if (ccp->conversion == NULL) { + /* not converting (this should not happen) */ + DPRINT(("got conversion attribute request before conversion start\n")); + return; + } + + /* + * special treat for fonts -- because if they are changed, + * you should release old fonts. + */ + if (ccp->attrmask & CAFonts) { + fonts = ccp->attrs.fonts; + nfonts = ccp->attrs.num_fonts; + } else { + fonts = NULL; + nfonts = 0; + } + + ccp->attrmask = 0L; + getAttributeFromEvent(ccp, event); + + /* change it */ + myChangeAttributes(ccp->conversion, ccp->attrmask, &ccp->attrs); + + if (ccp->attrmask & CAFonts) { + if (fonts != NULL) { + /* fonts changed -- free old fonts */ + Cardinal i; + for (i = 0; i < nfonts; i++) { + CachedFreeFont(XtDisplay(w), fonts[i]); + } + XtFree((char *)fonts); + } + } else { + /* restore fonts data */ + ccp->attrmask = CAFonts; + ccp->attrs.fonts = fonts; + ccp->attrs.num_fonts = nfonts; + } +} + +/*- ConversionCloseProc: CONVERSION_CLOSE event handler (xlc only) -*/ +/* ARGSUSED */ +static void +ConversionCloseProc(w, event, args, num_args) +Widget w; +XEvent *event; +String *args; +Cardinal *num_args; +{ + KinputProtocolWidget kpw = (KinputProtocolWidget)w; + XClientMessageEvent *ev = &event->xclient; + Window reqwin; + ConvClient *ccp; + + TRACE(("ConversionCloseProc(window=0x%lx)\n", ev->data.l[2])); + /* is it a correct event? */ + if (!isCorrectClientEvent(kpw, event)) { + /*ignore */ + DPRINT(("got invalid clientmessage event.\n")); + return; + } + reqwin = ev->data.l[1]; + if ((ccp = findClient(kpw, reqwin)) == NULL) { + /* request from unknown client. just ignore */ + DPRINT(("got conversion end from unknown window\n")); + return; + } + + if (ccp->protocol != xlc_protocol && ccp->protocol != kinput1_protocol) { + /* + * Only XLC protocol uses this event (CONVERSION_CLOSE ClientMessage), + * so the checking for kinput1 protocol seems to be unnecessary. + * ...Wrong. A client using kinput1 protocol and a client using + * XLC protocol with off-the-spot mode can't be distinguishable + * until you get this event. + */ + DPRINT(("got xlc-specific event from a client that use other protocol\n")); + return; + } + + MyRemoveEventHandler(XtDisplay(w), reqwin, DestroyNotify, + ClientDead, (XtPointer)ccp); + if (ccp->conversion != NULL) { + CControlEndConversion(ccp->conversion); + endProc(ccp, False); + } + if (ccp->protocol == xlc_protocol) deleteClient(ccp); +} + +/* + *+ other event handler + */ + +/*- XlcOnTheSpotChangedProc: ProptyNotify of "_XLC_ON_THE_SPOT" handler -*/ +/* ARGSUSED */ +static void +XlcOnTheSpotChangedProc(w, event, args, num_args) +Widget w; +XEvent *event; +String *args; /* not used */ +Cardinal *num_args; /* not used */ +{ + KinputProtocolWidget kpw = (KinputProtocolWidget)w; + XPropertyEvent *ev = &(event->xproperty); + ConvClient *client; + + TRACE(("XlcOnTheSpotChangedProc(window=0x%lx)\n", ev->window)); + + if (ev->window != XtWindow(w) || + ev->atom != kpw->kinput.xlcOnTheSpotAtom) { + DPRINT(("\tgot invalid PropertyNotify event.\n")); + return; + } else if (ev->state != PropertyNewValue) { + return; + } + client = getXlcDataFromProperty(kpw, (ConvClient *)NULL, False); + if (client == NULL) return; + + if (client->conversion != NULL) { + myChangeAttributes(client->conversion, + client->attrmask, &client->attrs); + } +} + +/*- 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; +{ + KinputProtocolWidget kpw = (KinputProtocolWidget)w; + ConvClient *ccp; + String params[1]; + Cardinal num_params; + + /* Selection owner changed. kill myself */ + + /* + * send ConversionEnd event to the clients before exit + */ + for (ccp = kpw->kinput.clients; ccp; ccp = ccp->next) { + if (ccp->reqwin != None) { + if (ccp->conversion != NULL) { + CControlEndConversion(ccp->conversion); + } + 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 (jinput and xlc) -*/ +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); +}