Logo Search packages:      
Sourcecode: tcltk8.0-ja version File versions  Download package

tkKinput2.c

/* 
 * tkKinput2.c --
 *
 *    This file contains modules to implement the kanji
 *    input with kinput2.
 *
 * Copyright 1988,1995 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.
 */

#ifndef lint
static char rcsid[] = "$Header: /home/m-hirano/cvsroot/tcltk/tk8/unix/tkKinput2.c,v 1.7 1998/12/04 20:01:24 m-hirano Exp $";
#endif

#if defined(KANJI) && defined(KINPUT2)

#include "tkPort.h"
#include "tkInt.h"

#include "tkFont.h"

/*
 * For each kinput2 server, there is a structure of the following type:
 */

typedef struct {
    int specified;
    char *value;
} KI2Attr;

typedef struct {
    char *variable;
    int num;
    KI2Attr inputStyle;
    KI2Attr focusWindow;
    KI2Attr spot;
    KI2Attr foreground;
    KI2Attr background;
    KI2Attr eventCaptureMethod;
    KI2Attr lineSpacing;
    KI2Attr clientArea;
    KI2Attr statusArea;
    KI2Attr cursor;
    KI2Attr fonts;
} Kinput2Info;

/*
 * Atoms used in this module. (Japanese conversion only)
 */

static Atom       japanese_conversion_atom;
static Atom       compound_text_atom;

/*
 * Have atoms in this module been initialized?
 */

static int        atom_initialized = 0;

/*
 * Information about the current server. (Assuming only one server)
 */

static Tcl_HashTable    ki2infoTable;
static int ki2_initialized = 0;

/*
 * Forward declarations for procedures defined later in this file:
 */

static void       Kinput2InfoInit _ANSI_ARGS_ ((void));
static void       Kinput2InputString _ANSI_ARGS_ ((Tcl_Interp *interp,
                      Tk_Window tkwin, Atom selection, Atom type,
                      int format, unsigned long size, unsigned char *str,
                      ClientData clientData));
static void       Kinput2StartendProc _ANSI_ARGS_ ((Tcl_Interp *interp,
                      Tk_Window tkwin, Atom selection, int state,
                      ClientData clientData));
static void       beginConversion _ANSI_ARGS_ ((Tcl_Interp *interp,
                      Tk_Window tkwin, Atom catom, Atom tatom,
                      void (*inputproc)(), void (*startendproc)(),
                      ClientData clientData, Kinput2Info *ki2Ptr));
static void       endConversion _ANSI_ARGS_ ((Tcl_Interp *interp,
                      Tk_Window tkwin, Atom catom, int throwaway));
static void       changeConversionAttributes _ANSI_ARGS_ ((Tcl_Interp *interp,
                      Tk_Window tkwin, Atom catom, Kinput2Info *ki2Ptr));
static int        parseAttributes _ANSI_ARGS_ ((Tcl_Interp *interp,
                      int argc, char **argv, Kinput2Info *ki2Ptr));
static char *           formatAttributeInfo _ANSI_ARGS_ ((Kinput2Info *ki2Ptr,
                      char *attrName));

/*
 *--------------------------------------------------------------
 *
 * Tk_Kinput2Start --
 *
 *    This procedure is invoked to start the kanji conversion
 *    using kinput2.
 *
 * Results:
 *    A standard Tcl result.
 *
 * Side effects:
 *    See the user documentation.
 *
 *--------------------------------------------------------------
 */

int
Tk_Kinput2Start(interp, tkwin, argc, argv)
    Tcl_Interp *interp;       /* Current interpreter. */
    Tk_Window tkwin;          /* Focus window. */
    int argc;                 /* Number of arguments. */
    char **argv;        /* Argument strings. */
{
    Tcl_HashEntry *ki2infoHashPtr;
    int new;
    register Kinput2Info *ki2Ptr;
    char *variable = NULL;

    if (!atom_initialized) {
      japanese_conversion_atom = Tk_InternAtom(tkwin, "_JAPANESE_CONVERSION");
      compound_text_atom       = Tk_InternAtom(tkwin, "COMPOUND_TEXT");
      atom_initialized = 1;
    }
    if (!ki2_initialized) Kinput2InfoInit();

    /*
     * Get the Kinput2Info for the focus window.
     */

    ki2infoHashPtr = Tcl_CreateHashEntry(&ki2infoTable, (char *) tkwin, &new);
    if (!new) {
      ki2Ptr = (Kinput2Info *) Tcl_GetHashValue(ki2infoHashPtr);
    } else {
      ki2Ptr = (Kinput2Info *) ckalloc(sizeof(Kinput2Info));
      ki2Ptr->variable = NULL;
      ki2Ptr->num = 0;
      ki2Ptr->inputStyle.specified = None;
      ki2Ptr->inputStyle.value = NULL;
      ki2Ptr->focusWindow.specified = None;
      ki2Ptr->focusWindow.value = NULL;
      ki2Ptr->spot.specified = None;
      ki2Ptr->spot.value = NULL;
      ki2Ptr->foreground.specified = None;
      ki2Ptr->foreground.value = NULL;
      ki2Ptr->background.specified = None;
      ki2Ptr->background.value = NULL;
      ki2Ptr->eventCaptureMethod.specified = None;
      ki2Ptr->eventCaptureMethod.value = NULL;
      ki2Ptr->lineSpacing.specified = None;
      ki2Ptr->lineSpacing.value = NULL;
      ki2Ptr->clientArea.specified = None;
      ki2Ptr->clientArea.value = NULL;
      ki2Ptr->statusArea.specified = None;
      ki2Ptr->statusArea.value = NULL;
      ki2Ptr->cursor.specified = None;
      ki2Ptr->cursor.value = NULL;
      ki2Ptr->fonts.specified = None;
      ki2Ptr->fonts.value = NULL;

      Tcl_SetHashValue(ki2infoHashPtr, ki2Ptr);
    }

    /*
     * Parse the arguments and update the Kinput2Info.
     */

    if (parseAttributes(interp, argc, argv, ki2Ptr) == TCL_ERROR) {
      return TCL_ERROR;
    }

    if (ki2Ptr->variable) {
      variable = (char *) ckalloc((unsigned)(strlen(ki2Ptr->variable) + 1));
      strcpy(variable, ki2Ptr->variable);
    }

    beginConversion(interp, tkwin, japanese_conversion_atom, compound_text_atom,
      Kinput2InputString, Kinput2StartendProc, (ClientData) variable, ki2Ptr);

    return (strlen(interp->result) == 0) ? TCL_OK : TCL_ERROR;
}

/*
 *--------------------------------------------------------------
 *
 * Tk_Kinput2End --
 *
 *    This procedure is invoked to end the kanji conversion
 *    using kinput2.
 *
 * Results:
 *    A standard Tcl result.
 *
 * Side effects:
 *    See the user documentation.
 *
 *--------------------------------------------------------------
 */

int
Tk_Kinput2End(interp, tkwin)
    Tcl_Interp *interp;       /* Current interpreter. */
    Tk_Window tkwin;          /* Focus window.*/
{
    if (!atom_initialized) {
      Tcl_SetResult(interp, "kanjiInput is never started.", TCL_VOLATILE);
      return TCL_ERROR;
    }

    endConversion(interp, tkwin, japanese_conversion_atom, True);

    return (strlen(interp->result) == 0) ? TCL_OK : TCL_ERROR;
}

/*
 *--------------------------------------------------------------
 *
 * Tk_Kinput2Attribute --
 *
 *    This procedure is invoked to change the attributes for
 *    the kanji conversion using kinput2.
 *
 * Results:
 *    A standard Tcl result.
 *
 * Side effects:
 *    See the user documentation.
 *
 *--------------------------------------------------------------
 */

int
Tk_Kinput2Attribute(interp, tkwin, argc, argv)
    Tcl_Interp *interp;       /* Current interpreter. */
    Tk_Window tkwin;          /* Focus window. */
    int argc;                 /* Number of arguments. */
    char **argv;        /* Argument strings. */
{
    Tcl_HashEntry *ki2infoHashPtr;
    register Kinput2Info *ki2Ptr;
    int saved1, saved2;

    if (!ki2_initialized) {
      Tcl_SetResult(interp, "kanjiInput is never started.", TCL_VOLATILE);
      return TCL_ERROR;
    }

    ki2infoHashPtr = Tcl_FindHashEntry(&ki2infoTable, (char *) tkwin);
    if (ki2infoHashPtr == NULL) {
      Tcl_SetResult(interp,
          "No hash entry: kanjiInput 'attribute' is invoked before 'start'",
          TCL_VOLATILE);
      return TCL_ERROR;
    }
    ki2Ptr = (Kinput2Info *) Tcl_GetHashValue(ki2infoHashPtr);

    /*
     * Parse the arguments and update the Kinput2Info.
     */

    if (parseAttributes(interp, argc, argv, ki2Ptr) == TCL_ERROR) {
      return TCL_ERROR;
    }
    saved1 = ki2Ptr->inputStyle.specified;
    saved2 = ki2Ptr->eventCaptureMethod.specified;
    ki2Ptr->inputStyle.specified = None;
    ki2Ptr->eventCaptureMethod.specified = None;

    changeConversionAttributes(interp, tkwin, japanese_conversion_atom, ki2Ptr);

    ki2Ptr->inputStyle.specified = saved1;
    ki2Ptr->eventCaptureMethod.specified = saved2;

    return (strlen(interp->result) == 0) ? TCL_OK : TCL_ERROR;
}

/*
 *--------------------------------------------------------------
 *
 * Tk_Kinput2AttributeInfo --
 *
 *    This procedure is invoked to show the attributes for
 *    the kanji conversion using kinput2.
 *
 * Results:
 *    A standard Tcl result.
 *
 * Side effects:
 *    See the user documentation.
 *
 *--------------------------------------------------------------
 */

int
Tk_Kinput2AttributeInfo(interp, tkwin, attrName)
    Tcl_Interp *interp;       /* Current interpreter. */
    Tk_Window tkwin;          /* Window to focus. */
    char *attrName;           /* If non-NULL, indicates a single option
                         * whose info is to be returned.  Otherwise
                         * info is returned for all options. */
{
    Tcl_HashEntry *ki2infoHashPtr;
    register Kinput2Info *ki2Ptr;
    char *list;

    if (!ki2_initialized) {
      Tcl_SetResult(interp, "kanjiInput is never started.", TCL_VOLATILE);
      return TCL_ERROR;
    }

    ki2infoHashPtr = Tcl_FindHashEntry(&ki2infoTable, (char *) tkwin);
    if (ki2infoHashPtr == NULL) {
      Tcl_SetResult(interp,
          "No hash entry: kanjiInput 'attribute' is invoked before 'start'",
          TCL_VOLATILE);
      return TCL_ERROR;
    }
    ki2Ptr = (Kinput2Info *) Tcl_GetHashValue(ki2infoHashPtr);

    /*
     * Create a valid Tcl list holding the attributes of the kinput2.
     */

    Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);

    if (attrName != NULL) {
      list = formatAttributeInfo(ki2Ptr, attrName);
      if (list == NULL) {
          Tcl_AppendResult(interp, "unknown attribute \"", attrName, "\"",
                (char *) NULL);
          return TCL_ERROR;
      }
      interp->result = list;
      interp->freeProc = TCL_DYNAMIC;
    } else {
      list = formatAttributeInfo(ki2Ptr, "-variable");
      Tcl_AppendResult(interp, "{", list, "}", (char *) NULL);
      ckfree(list);
      list = formatAttributeInfo(ki2Ptr, "-inputStyle");
      Tcl_AppendResult(interp, " {", list, "}", (char *) NULL);
      ckfree(list);
      list = formatAttributeInfo(ki2Ptr, "-focusWindow");
      Tcl_AppendResult(interp, " {", list, "}", (char *) NULL);
      ckfree(list);
      list = formatAttributeInfo(ki2Ptr, "-spot");
      Tcl_AppendResult(interp, " {", list, "}", (char *) NULL);
      ckfree(list);
      list = formatAttributeInfo(ki2Ptr, "-foreground");
      Tcl_AppendResult(interp, " {", list, "}", (char *) NULL);
      ckfree(list);
      list = formatAttributeInfo(ki2Ptr, "-background");
      Tcl_AppendResult(interp, " {", list, "}", (char *) NULL);
      ckfree(list);
      list = formatAttributeInfo(ki2Ptr, "-eventCaptureMethod");
      Tcl_AppendResult(interp, " {", list, "}", (char *) NULL);
      ckfree(list);
      list = formatAttributeInfo(ki2Ptr, "-lineSpacing");
      Tcl_AppendResult(interp, " {", list, "}", (char *) NULL);
      ckfree(list);
      list = formatAttributeInfo(ki2Ptr, "-clientArea");
      Tcl_AppendResult(interp, " {", list, "}", (char *) NULL);
      ckfree(list);
      list = formatAttributeInfo(ki2Ptr, "-statusArea");
      Tcl_AppendResult(interp, " {", list, "}", (char *) NULL);
      ckfree(list);
      list = formatAttributeInfo(ki2Ptr, "-cursor");
      Tcl_AppendResult(interp, " {", list, "}", (char *) NULL);
      ckfree(list);
      list = formatAttributeInfo(ki2Ptr, "-fonts");
      Tcl_AppendResult(interp, " {", list, "}", (char *) NULL);
      ckfree(list);
    }

    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * Kinput2InfoInit --
 *
 *    Initialize the structures used for Kinput2Info management.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Read the code.
 *
 *----------------------------------------------------------------------
 */

static void
Kinput2InfoInit()
{
    ki2_initialized = 1;
    Tcl_InitHashTable(&ki2infoTable, TCL_ONE_WORD_KEYS);
}

/*
 *--------------------------------------------------------------
 *
 * Kinput2InputString --
 *
 *    This procedure is invoked when the application receives
 *    the kanji string from kinput2 server.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    See the user documentation.
 *
 *--------------------------------------------------------------
 */

static void
Kinput2InputString(interp, tkwin, selection, type, format, size, str, clientData)
    Tcl_Interp *interp;
    Tk_Window tkwin;
    Atom selection;
    Atom type;
    int format;
    unsigned long size;
    unsigned char *str;
    ClientData clientData;
{
    if (str == NULL) {
      return;
    } else {
      int kanjiCode = Tcl_KanjiCode(interp);
      char *variable = (char *) clientData;
      int len;
      wchar *wstr;
      char *kanjiStr;

      if (variable == NULL) return;

      wstr = Tk_CtextToWStr(str, (int)size);
      if (wstr == NULL) return;

      len = Tcl_KanjiDecode(kanjiCode, wstr, NULL);
      kanjiStr = (char *) ckalloc((unsigned)(len + 1));
      (void) Tcl_KanjiDecode(kanjiCode, wstr, kanjiStr);

      Tcl_SetVar(interp, variable, kanjiStr, TCL_GLOBAL_ONLY);

      ckfree((char *)wstr);
      ckfree(kanjiStr);
    }
}

/*
 *--------------------------------------------------------------
 *
 * Kinput2StartendProc --
 *
 *    This procedure is invoked when a conversion starts, ends,
 *    or aborts.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    See the user documentation.
 *
 *--------------------------------------------------------------
 */

static void
Kinput2StartendProc(interp, tkwin, selection, state, clientData)
    Tcl_Interp *interp;
    Tk_Window tkwin;
    Atom selection;
    int state;
    ClientData clientData;
{
    switch (state) {
    case 0: /* start */
      break;
    case 1: /* end */
      /* fall through */
    default:      /* error */
      /* free memory for the variable name */
      if (clientData != NULL) ckfree((char *)clientData);
    }
}

/*
 * Procedures for kanji conversion with kinput2.
 */

#include "tkKinput2.h"

typedef struct {
    Display *display;
    Atom    profileAtom;      /* "_CONVERSION_PROFILE" */
    Atom    typeAtom;   /* "_CONVERSION_ATTRIBUTE_TYPE" */
    Atom    versionAtom;      /* "PROTOCOL-2.0" */
    Atom    reqAtom;    /* "CONVERSION_REQUEST" */
    Atom    notifyAtom; /* "CONVERSION_NOTIFY" */
    Atom    endAtom;    /* "CONVERSION_END" */
    Atom    endReqAtom; /* "CONVERSION_END_REQUEST" */
    Atom    attrAtom;   /* "CONVERSION_ATTRIBUTE" */
    Atom    attrNotifyAtom;   /* "CONVERSION_ATTRIBUTE_NOTIFY" */
} ConversionAtoms;

typedef struct {
    Tcl_Interp *interp;
    Tk_Window     tkwin;
    Atom    convatom;
    Window  convowner;
    Window  forwardwin;
    Atom    property;
    void    (*inputproc)();
    void    (*startendproc)();
    ClientData    clientData;
} ConversionContext;

static XContext   convertPrivContext;

/*
 * Forward declarations for procedures defined later in this file:
 */

static void       finishConversion _ANSI_ARGS_ ((Tk_Window tkwin,
                        ConversionContext *context));
static int        recvConvAck _ANSI_ARGS_((ClientData clientData,
                      XEvent *eventPtr));
static int        getConv _ANSI_ARGS_((ClientData clientData,
                      XEvent *eventPtr));
static void       callStart _ANSI_ARGS_((Tk_Window tkwin,
                      ConversionContext *context));
static void       callFail _ANSI_ARGS_((Tk_Window tkwin,
                      ConversionContext *context));
static void       callEnd _ANSI_ARGS_((Tk_Window tkwin,
                      ConversionContext *context));
static ConversionAtoms *getAtoms _ANSI_ARGS_ ((Tk_Window tkwin));
static ConversionContext *getConversionContext _ANSI_ARGS_ ((Tk_Window tkwin));
static int        makeAttrData _ANSI_ARGS_ ((Tcl_Interp *interp,
                      Tk_Window tkwin, Kinput2Info *ki2Ptr,
                      unsigned long **datap));
static void       forwardKeyEvent _ANSI_ARGS_ ((ClientData clientdata,
                                          XEvent *event));
static int        stopForwarding _ANSI_ARGS_ ((ClientData clientdata,
                                         XErrorEvent *errEvent));

/*
 *--------------------------------------------------------------
 *
 * beginConversion --
 *
 *    Find a kanji conversion server and invoke it.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *--------------------------------------------------------------
 */

static void
beginConversion(interp, tkwin, catom, tatom, inputproc, startendproc, clientData, ki2Ptr)
    Tcl_Interp *interp;
    Tk_Window tkwin;
    Atom catom;               /* Selection Atom e.g. JAPANESE_CONVERSION */
    Atom tatom;               /* Property Type Atom e.g. COMPOUND_TEXT */
    void (*inputproc)();      /* conversion text callback function */
    void (*startendproc)();   /* conversion start/end callback function */
    ClientData clientData;    /* client_data passed to callback function */
    Kinput2Info *ki2Ptr;
{
    Window owner;
    XEvent event;
    ConversionAtoms *cap;
    ConversionContext *context;
    int anyattr = False;
    static int checkProtocols _ANSI_ARGS_ ((Display *dpy,
                   Window window, ConversionAtoms *cap));

    cap = getAtoms(tkwin);

    /* 変換サーバを探す */
    if ((owner = XGetSelectionOwner(Tk_Display(tkwin), catom)) == None) {
      /* ない
       * もしも変換中だったら変換を中止する
       */
      Tcl_SetResult(interp, "Conversion Server not found", TCL_VOLATILE);
      if ((context = getConversionContext(tkwin)) != NULL) {
          callEnd(tkwin, context);
          finishConversion(tkwin, context);
          ckfree((char *)context);
      }
      return;
    }

    /*
     * 今すでに変換中かどうか調べる
     * 変換中なら何もせずにリターンする…わけにはいかない
     * なぜかというと、変換サーバが何らかの事情で途中で死んだ場合
     * CONVERSION_END がクライアントに来ないことがあるからである
     * そこで、変換中の場合でも SelectionOwner を探して、それが
     * 最初に _beginConversion() が呼ばれた時と WindowID が同じか
     * どうか確認する
     * 本当は SelectionOwner になった時間もチェックしたいのだが
     * ICCCM に述べられているように、GetSelectionOwner では
     * それがわからないのであきらめる
     */
    if ((context = getConversionContext(tkwin)) != NULL) {
      Window curOwner;
      curOwner = (catom == context->convatom) ? owner :
        XGetSelectionOwner(Tk_Display(tkwin), context->convatom);
      if (curOwner == context->convowner) {
          /* 何もせずにリターン */
          return;
      }
      /* SelectionOwner が変わっている
       * これは途中で変換サーバがクラッシュしたに違いない
       * ということで CONVERSION_END が来た時と同じような
       * 処理をする
       */
      callEnd(tkwin, context);
      finishConversion(tkwin, context);
      ckfree((char *)context);
    }

    /*
     * サーバからの CONVERSION_NOTIFY 用のイベントハンドラを
     * 登録する
     */
    Tk_CreateGenericHandler((Tk_GenericProc *)recvConvAck, (ClientData)tkwin);

    /*
     * コンテキストをつくって必要な情報を登録する
     */
    context = (ConversionContext *) ckalloc((unsigned)sizeof(ConversionContext));
    context->interp = interp;
    context->tkwin = tkwin;
    context->convatom = catom;
    context->convowner = owner;
    context->forwardwin = None;
    context->property = None; /* これは CONVERSION_NOTIFY が来た時に
                         * 正しく設定される */
    context->inputproc = inputproc;
    context->startendproc = startendproc;
    context->clientData = clientData;
    XSaveContext(Tk_Display(tkwin), Tk_WindowId(tkwin),
             convertPrivContext, (caddr_t)context);

    /*
     * 変換属性リストが指定されていればプロパティにそれを登録する
     */
    if (ki2Ptr->num > 0 && checkProtocols(Tk_Display(tkwin), owner, cap)) {
      unsigned long *data;
      int len;

      if ((len = makeAttrData(interp, tkwin, ki2Ptr, &data)) > 0) {
          XChangeProperty(Tk_Display(tkwin), Tk_WindowId(tkwin),
                      cap->attrAtom, cap->attrAtom, 32,
                      PropModeReplace, (unsigned char *) data, len);
          ckfree((char *) data);
          anyattr = True;
      }
    }

    /*
     * ClientMessage イベントを使って日本語入力をリクエストする
     */
    event.xclient.type = ClientMessage;
    event.xclient.window = owner;
    event.xclient.message_type = cap->reqAtom;
    event.xclient.format = 32;
    event.xclient.data.l[0] = catom;
    event.xclient.data.l[1] = Tk_WindowId(tkwin);
    event.xclient.data.l[2] = tatom;
    /* 結果をストアするプロパティ名は、多言語を同時に使用することを
     * 考えて、selection atom を使用することにする
     */
    event.xclient.data.l[3] = catom;
    event.xclient.data.l[4] = anyattr ? cap->attrAtom : None;
    XSendEvent(Tk_Display(tkwin), owner, False, NoEventMask, &event);
}

/*
 *--------------------------------------------------------------
 *
 * endConversion --
 *
 *    Terminate kanji conversion.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *--------------------------------------------------------------
 */

static void
endConversion(interp, tkwin, catom, throwaway)
    Tcl_Interp *interp;
    Tk_Window tkwin;
    Atom catom;         /* Selection Atom */
    int throwaway;
{
    XEvent event;
    ConversionAtoms *cap;
    ConversionContext *context;

    cap = getAtoms(tkwin);
    context = getConversionContext(tkwin);

    if (context == NULL || (catom != None && catom != context->convatom)) {
      return;
    }

    if (XGetSelectionOwner(Tk_Display(tkwin), context->convatom) !=
      context->convowner) {
      /* コールバックを呼ぶ */
      callEnd(tkwin, context);
      finishConversion(tkwin, context);
      ckfree((char *)context);
      return;
    }

    if (throwaway) context->inputproc = NULL;

    event.xclient.type = ClientMessage;
    event.xclient.window = context->convowner;
    event.xclient.message_type = cap->endReqAtom;
    event.xclient.format = 32;
    event.xclient.data.l[0] = context->convatom;
    event.xclient.data.l[1] = Tk_WindowId(tkwin);

    XSendEvent(Tk_Display(tkwin), context->convowner, False, NoEventMask, &event);
}

/*
 *--------------------------------------------------------------
 *
 * changeConversionAttributes --
 *
 *    Change attributes of a conversion server.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *--------------------------------------------------------------
 */

static void
changeConversionAttributes(interp, tkwin, catom, ki2Ptr)
    Tcl_Interp *interp;
    Tk_Window tkwin;
    Atom catom;
    Kinput2Info *ki2Ptr;
{
    XEvent event;
    ConversionAtoms *cap;
    ConversionContext *context;
    unsigned long *data;
    int len;

    if (ki2Ptr->num == 0) return;

    cap = getAtoms(tkwin);
    context = getConversionContext(tkwin);

    if (context == NULL || (catom != None && catom != context->convatom)) {
      return;
    }

    if (XGetSelectionOwner(Tk_Display(tkwin), context->convatom) !=
      context->convowner) {
      callEnd(tkwin, context);
      finishConversion(tkwin, context);
      ckfree((char *)context);
      return;
    }

    data = NULL;
    if ((len = makeAttrData(interp, tkwin, ki2Ptr, &data)) == 0) return;

    event.xclient.type = ClientMessage;
    event.xclient.window = context->convowner;
    event.xclient.message_type = cap->attrNotifyAtom;
    event.xclient.format = 32;
    event.xclient.data.l[0] = context->convatom;
    event.xclient.data.l[1] = Tk_WindowId(tkwin);
    if (len <= 3 && len == LENGTH_OF_ATTR(data[0]) + 1) {
      int i;
      /* イベントの中に収まる */
      for (i = 0; i < len; i++) {
          event.xclient.data.l[2 + i] = data[i];
      }
    } else {
      XChangeProperty(Tk_Display(tkwin), Tk_WindowId(tkwin),
                  cap->attrAtom, cap->attrAtom, 32,
                  PropModeReplace, (unsigned char *)data, len);
      event.xclient.data.l[2] = CONV_ATTR(CONVATTR_INDIRECT, 1);
      event.xclient.data.l[3] = cap->attrAtom;
    }

    XSendEvent(Tk_Display(tkwin), context->convowner, False, NoEventMask, &event);

    if (data != NULL) ckfree((char *)data);
}

/*
 *--------------------------------------------------------------
 *
 * parseAttributes --
 *
 *    Parse attributes.
 *
 * Results:
 *    Return TCL_OK if every attributes can be parsed without
 *      any problems, otherwise return TCL_ERROR.
 *
 * Side effects:
 *    None.
 *
 *--------------------------------------------------------------
 */

static int
parseAttributes(interp, argc, argv, ki2Ptr)
    Tcl_Interp *interp;
    int argc;
    char **argv;
    Kinput2Info *ki2Ptr;
{
    int result = TCL_OK;

    ki2Ptr->num = 0;
    for ( ; argc > 1; ) {
      if (argv[0][0] != '-') {
          Tcl_AppendResult(interp, "Warning: expected attribute name, but got \"",
                *argv, "\".\n", (char *) NULL);
          result = TCL_ERROR;
          argv++;
          argc--;
          continue;
      } else {
          char c;
          unsigned int len;
          char *name = *argv + 1;
          char *value = *(argv + 1);
          char *new, *old;

          c = name[0];
          len = strlen(name);
          new = (char *) ckalloc((unsigned) (strlen(value) + 1));
          strcpy(new, value);
          if ((c == 'b') && (strncmp(name, "background", len) == 0)) {
            ki2Ptr->background.specified = !None;
            old = ki2Ptr->background.value;
            ki2Ptr->background.value = new;
            ki2Ptr->num++;
          } else if ((c == 'c') && (strncmp(name, "clientArea", len) == 0)
              && (len > 1)) {
            ki2Ptr->clientArea.specified = !None;
            old = ki2Ptr->clientArea.value;
            ki2Ptr->clientArea.value = new;
            ki2Ptr->num++;
          } else if ((c == 'c') && (strncmp(name, "cursor", len) == 0)
            && (len > 1)) {
            ki2Ptr->cursor.specified = !None;
            old = ki2Ptr->cursor.value;
            ki2Ptr->cursor.value = new;
            ki2Ptr->num++;
          } else if ((c == 'e') && (strncmp(name, "eventCaptureMethod", len) == 0)) {
            old = ki2Ptr->eventCaptureMethod.value;
            ki2Ptr->eventCaptureMethod.specified = !None;
            ki2Ptr->eventCaptureMethod.value = new;
            ki2Ptr->num++;
          } else if ((c == 'f') && (strncmp(name, "focusWindow", len) == 0)
            && (len > 2)) {
            ki2Ptr->focusWindow.specified = !None;
            old = ki2Ptr->focusWindow.value;
            ki2Ptr->focusWindow.value = new;
            ki2Ptr->num++;
          } else if ((c == 'f') && (strncmp(name, "fonts", len) == 0)
            && (len > 2)) {
            ki2Ptr->fonts.specified = !None;
            old = ki2Ptr->fonts.value;
            ki2Ptr->fonts.value = new;
            ki2Ptr->num++;
          } else if ((c == 'f') && (strncmp(name, "foreground", len) == 0)
            && (len > 2)) {
            ki2Ptr->foreground.specified = !None;
            old = ki2Ptr->foreground.value;
            ki2Ptr->foreground.value = new;
            ki2Ptr->num++;
          } else if ((c == 'i') && (strncmp(name, "inputStyle", len) == 0)) {
            ki2Ptr->inputStyle.specified = !None;
            old = ki2Ptr->inputStyle.value;
            ki2Ptr->inputStyle.value = new;
            ki2Ptr->num++;
          } else if ((c == 'l') && (strncmp(name, "lineSpacing", len) == 0)) {
            ki2Ptr->lineSpacing.specified = !None;
            old = ki2Ptr->lineSpacing.value;
            ki2Ptr->lineSpacing.value = new;
            ki2Ptr->num++;
          } else if ((c == 's') && (strncmp(name, "spot", len) == 0)
            && (len > 1)) {
            ki2Ptr->spot.specified = !None;
            old = ki2Ptr->spot.value;
            ki2Ptr->spot.value = new;
            ki2Ptr->num++;
          } else if ((c == 's') && (strncmp(name, "statusArea", len) == 0)
            && (len > 1)) {
            ki2Ptr->statusArea.specified = !None;
            old = ki2Ptr->statusArea.value;
            ki2Ptr->statusArea.value = new;
            ki2Ptr->num++;
          } else if ((c == 'v') && (strncmp(name, "variable", len) == 0)) {
            old = ki2Ptr->variable;
            ki2Ptr->variable = new;
          } else {
            Tcl_AppendResult(interp, "Warning: unknown attribute name \"",
                  *argv, "\".\n", (char *) NULL);
            result = TCL_ERROR;
            argv++;
            argc--;
            continue;
          }
          if (old) ckfree(old);
          argv += 2;
          argc -= 2;
      }
    } 
    if (argc == 1) {
      Tcl_AppendResult(interp, "Warning: no attribute value for \"",
            *argv, "\".\n", (char *) NULL);
      result = TCL_ERROR;
    }

    return result;
}

/*
 *--------------------------------------------------------------
 *
 * formatAttributeInfo --
 *
 *    Create a valid Tcl list holding the attribute informattion
 *    for a sigle attribute option.
 *
 * Results:
 *    A Tcl list, dynamically allocated.  The caller is expected to
 *    arrange for this list to be freed eventually.
 *
 * Side effects:
 *    Memory is allocated.
 *
 *--------------------------------------------------------------
 */

static char *
formatAttributeInfo(ki2Ptr, name)
    Kinput2Info *ki2Ptr;
    char *name;
{
    char c;
    unsigned int len;
    char *argv[2];

    if (*name != '-') return NULL;

    name++;
    c = name[0];
    len = strlen(name);
    if ((c == 'b') && (strncmp(name, "background", len) == 0)) {
      argv[0] = "-background";
      argv[1] = (ki2Ptr->background.specified) ? ki2Ptr->background.value : "";
    } else if ((c == 'c') && (strncmp(name, "clientArea", len) == 0) && (len > 1)) {
      argv[0] = "-clientArea";
      argv[1] = (ki2Ptr->clientArea.specified) ? ki2Ptr->clientArea.value : "";
    } else if ((c == 'c') && (strncmp(name, "cursor", len) == 0) && (len > 1)) {
      argv[0] = "-cursor";
      argv[1] = (ki2Ptr->cursor.specified) ? ki2Ptr->cursor.value : "";
    } else if ((c == 'e') && (strncmp(name, "eventCaptureMethod", len) == 0)) {
      argv[0] = "-eventCaptureMethod";
      argv[1] = (ki2Ptr->eventCaptureMethod.specified) ? ki2Ptr->eventCaptureMethod.value : "";
    } else if ((c == 'f') && (strncmp(name, "focusWindow", len) == 0) && (len > 2)) {
      argv[0] = "-focusWindow";
      argv[1] = (ki2Ptr->focusWindow.specified) ? ki2Ptr->focusWindow.value : "";
    } else if ((c == 'f') && (strncmp(name, "fonts", len) == 0) && (len > 2)) {
      argv[0] = "-fonts";
      argv[1] = (ki2Ptr->fonts.specified) ? ki2Ptr->fonts.value : "";
    } else if ((c == 'f') && (strncmp(name, "foreground", len) == 0) && (len > 2)) {
      argv[0] = "-foreground";
      argv[1] = (ki2Ptr->foreground.specified) ? ki2Ptr->foreground.value : "";
    } else if ((c == 'i') && (strncmp(name, "inputStyle", len) == 0)) {
      argv[0] = "-inputStyle";
      argv[1] = (ki2Ptr->inputStyle.specified) ? ki2Ptr->inputStyle.value : "";
    } else if ((c == 'l') && (strncmp(name, "lineSpacing", len) == 0)) {
      argv[0] = "-lineSpacing";
      argv[1] = (ki2Ptr->lineSpacing.specified) ? ki2Ptr->lineSpacing.value : "";
    } else if ((c == 's') && (strncmp(name, "spot", len) == 0) && (len > 1)) {
      argv[0] = "-spot";
      argv[1] = (ki2Ptr->spot.specified) ? ki2Ptr->spot.value : "";
    } else if ((c == 's') && (strncmp(name, "statusArea", len) == 0) && (len > 1)) {
      argv[0] = "-statusArea";
      argv[1] = (ki2Ptr->statusArea.specified) ? ki2Ptr->statusArea.value : "";
    } else if ((c == 'v') && (strncmp(name, "variable", len) == 0)) {
      argv[0] = "-variable";
      argv[1] = (ki2Ptr->variable) ? ki2Ptr->variable : "";
    } else {
      return NULL;
    }
    return Tcl_Merge(2, argv);
}


static int
checkProtocols(dpy, window, cap)
Display *dpy;
Window window;
ConversionAtoms *cap;
{
    Atom type;
    int format;
    unsigned long nitems;
    unsigned long bytesafter;
    unsigned long *data, *saveddata;
    int err;
    int ret;

    data = NULL;
    err = XGetWindowProperty(dpy, window, cap->profileAtom,
                       0L, 100L, False,
                       cap->typeAtom,
                       &type, &format, &nitems,
                       &bytesafter, (unsigned char **)&data);
    if (err) return False;
    if (format != 32 || type != cap->typeAtom) {
      if (data != NULL) free((char *)data);
      return False;
    }

    ret = False;
    saveddata = data;
    while (nitems > 0) {
      int code = CODE_OF_ATTR(*data);
      int len = LENGTH_OF_ATTR(*data);

      data++;
      nitems--;
      if (nitems < len) break;

      switch (code) {
      case CONVPROF_PROTOCOL_VERSION:
          if (*data == cap->versionAtom) ret = True;
          break;
      case CONVPROF_SUPPORTED_STYLES:
          break;  /* XXX for now */
      default:
          break;
      }
      data += len;
      nitems -= len;
    }
    free((char *)saveddata);

    return ret;
}


/*
 *--------------------------------------------------------------
 *
 * finishConversion --
 *
 *    This procedure stops the current input conversion
 *    associated with the specified conversion context.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *--------------------------------------------------------------
 */

static void
finishConversion(tkwin, context)
Tk_Window tkwin;
ConversionContext *context;
{
    Tk_DeleteGenericHandler(recvConvAck, (ClientData)tkwin);
    Tk_DeleteGenericHandler(getConv, (ClientData)tkwin);
    Tk_DeleteEventHandler(tkwin, KeyPressMask|KeyReleaseMask,
                    forwardKeyEvent, (ClientData)context);
    XDeleteContext(Tk_Display(tkwin), Tk_WindowId(tkwin), convertPrivContext);
}

/* ARGSUSED */
static int
recvConvAck(clientData, eventPtr)
    ClientData clientData;
    XEvent *eventPtr;
{
    Tk_Window tkwin = (Tk_Window) clientData;
    XClientMessageEvent *cev = &(eventPtr->xclient);
    ConversionAtoms *cap;
    ConversionContext *context;

    if (eventPtr->type != ClientMessage) return 0;

    cap = getAtoms(tkwin);
    context = getConversionContext(tkwin);

    /* 正しいイベントかどうかチェックする */
    if (cev->window != Tk_WindowId(tkwin) ||
      cev->message_type != cap->notifyAtom ||
      cev->data.l[0] != context->convatom) {
      return 0;
    }

    /*
     * このハンドラはもう用済みなので外す
     */
    Tk_DeleteGenericHandler((Tk_GenericProc *)recvConvAck, (ClientData)tkwin);

    if (cev->data.l[2] == None) {
      Tcl_AppendResult(context->interp, "Warning: selection request failed",
            (char *) NULL);
      callFail(tkwin, context);
      finishConversion(tkwin, context);
      ckfree((char *)context);
      return 1;
    }

    context->forwardwin = (Window)cev->data.l[3];
    callStart(tkwin, context);

    /*
     * PropertyNotify と CONVERSION_END 用のイベントハンドラを
     * 登録する
     */
    Tk_CreateGenericHandler((Tk_GenericProc *)getConv, (ClientData)tkwin);

    /*
     * キーイベントを入力サーバに送るためのイベントハンドラを登録する
     */
    Tk_CreateEventHandler(tkwin, KeyPressMask|KeyReleaseMask,
                    forwardKeyEvent, (ClientData)context);

    /* プロパティ名をストアする */
    context->property = cev->data.l[2];
    return 1;
}


/* ARGSUSED */
static int
getConv(clientData, eventPtr)
    ClientData clientData;
    XEvent *eventPtr;
{
    Tk_Window tkwin = (Tk_Window) clientData;
    ConversionAtoms *cap;
    ConversionContext *context;

    /* PropertyNotify, ClientMessage, DestroyNotify 以外は無視する */
    if (eventPtr->type != PropertyNotify && eventPtr->type != ClientMessage
      && eventPtr->type != DestroyNotify)
      return 0;

    /* ウィンドウ ID のチェック */
    if (eventPtr->xany.window != Tk_WindowId(tkwin)) return 0;

    cap = getAtoms(tkwin);
    context = getConversionContext(tkwin);

    if (eventPtr->type == ClientMessage) {
      XClientMessageEvent *cev = &(eventPtr->xclient);

      /*
       * 本当に入力終了のイベントかどうかチェックする
       */
      if (cev->message_type == cap->endAtom &&
          cev->format == 32 &&
          cev->data.l[0] == context->convatom) {
          callEnd(tkwin, context);
          finishConversion(tkwin, context);
          ckfree((char *)context);
          return 1;
      }
    } else if (eventPtr->type == DestroyNotify) {
      XDestroyWindowEvent *dev = &(eventPtr->xdestroywindow);

      if (dev->window == Tk_WindowId(tkwin)) {
          callEnd(tkwin, context);
          finishConversion(tkwin, context);
          ckfree((char *)context);
          return 0;
      }
    } else {                  /* PropertyNotify */
      XPropertyEvent *pev = &(eventPtr->xproperty);
      Atom proptype;
      int propformat;
      unsigned long propsize, rest;
      unsigned char *propvalue;

      if (context->property == None) return 0;

      /* 正しいイベントかどうかのチェック */
      if (pev->window != Tk_WindowId(tkwin) ||
          pev->atom != context->property ||
          pev->state != PropertyNewValue) {
          return 0;
      }

      /* もしコールバック関数 context->inputproc が
       * NULL ならばプロパティを削除するだけ
       */
      if (context->inputproc == NULL) {
          XDeleteProperty(Tk_Display(tkwin), Tk_WindowId(tkwin), context->property);
          return 1;
      }

      /* プロパティから変換文字列を取り出す */
      XGetWindowProperty(Tk_Display(tkwin), Tk_WindowId(tkwin),
                     context->property,
                     0L, 100000L, True, AnyPropertyType,
                     &proptype, &propformat, &propsize, &rest,
                     &propvalue);

      /* プロパティのタイプ・フォーマットのチェック */
      if (proptype == None) {
          /* プロパティが存在しなかった
           * これは連続して何回もプロパティにデータが
           * 入れられた時、一回の GetWindowProperty で
           * 複数のデータをとってしまったあとに起きる
           * 従ってこれはエラーではない
           */
          return 1;
      }

      /* コールバックを呼ぶ */
      (*context->inputproc)(context->interp, tkwin, context->convatom,
                        proptype, propformat,
                        propsize, propvalue,
                        context->clientData);

      if (propvalue != NULL) XFree((char *)propvalue);

      return 1;
    }
    return 0;
}


static void
callStart(tkwin, context)
    Tk_Window tkwin;
    ConversionContext *context;
{
    if (context->startendproc != NULL) {
      (*context->startendproc)(context->interp, tkwin, context->convatom,
                         0, context->clientData);
    }
}


static void
callFail(tkwin, context)
    Tk_Window tkwin;
    ConversionContext *context;
{
    if (context->startendproc != NULL) {
      (*context->startendproc)(context->interp, tkwin, context->convatom,
                         -1, context->clientData);
    }
}


static void
callEnd(tkwin, context)
    Tk_Window tkwin;
    ConversionContext *context;
{
    if (context->startendproc != NULL) {
      (*context->startendproc)(context->interp, tkwin, context->convatom,
                         1, context->clientData);
    }
}


static ConversionAtoms *
getAtoms(tkwin)
    Tk_Window tkwin;
{
    int i;
    Display *disp = Tk_Display(tkwin);
    ConversionAtoms *cap;
    static ConversionAtoms *convatomp;
    static int ndisp = 0;
#define nalloc    2

    /*
     * アトムはディスプレイごとに違うので、
     * ディスプレイごとに作らなくてはならない
     */

    /* すでにアトムが作られているかどうか調べる */
    cap = convatomp;
    for (i = 0; i < ndisp; i++, cap++) {
      if (cap->display == disp) return cap;
    }

    /*
     * まだ作られていないので新しく作る
     */
    if (ndisp == 0) {
      /* 最初なので Context も同時に作る */
      convertPrivContext = XUniqueContext();
      convatomp = (ConversionAtoms *)
        malloc(sizeof(ConversionAtoms) * nalloc);
      cap = convatomp;
    } else if (ndisp % nalloc == 0) {
      /* サイズを増やす */
      convatomp = (ConversionAtoms *)
        realloc((char *)convatomp,
                sizeof(ConversionAtoms) * (ndisp + nalloc));
      cap = convatomp + ndisp;
    } else {
      cap = convatomp + ndisp;
    }

    /* ディスプレイの登録 */
    cap->display = disp;

    /* Atom の作成 */
    cap->profileAtom    = Tk_InternAtom(tkwin, CONVERSION_PROFILE);
    cap->typeAtom = Tk_InternAtom(tkwin, CONVERSION_ATTRIBUTE_TYPE);
    cap->versionAtom    = Tk_InternAtom(tkwin, PROTOCOL_VERSION);
    cap->reqAtom  = Tk_InternAtom(tkwin, "CONVERSION_REQUEST");
    cap->notifyAtom     = Tk_InternAtom(tkwin, "CONVERSION_NOTIFY");
    cap->endAtom  = Tk_InternAtom(tkwin, "CONVERSION_END");
    cap->endReqAtom     = Tk_InternAtom(tkwin, "CONVERSION_END_REQUEST");
    cap->attrAtom = Tk_InternAtom(tkwin, "CONVERSION_ATTRIBUTE");
    cap->attrNotifyAtom = Tk_InternAtom(tkwin, "CONVERSION_ATTRIBUTE_NOTIFY");

    ndisp++;

    return cap;
}


static ConversionContext *
getConversionContext(tkwin)
    Tk_Window tkwin;
{
    ConversionContext *context;

    if (XFindContext(Tk_Display(tkwin), Tk_WindowId(tkwin),
                 convertPrivContext, (caddr_t *)&context)) {
      /* error -- 多分コンテキストが見つからなかったため */
      return NULL;
    } else {
      return context;
    }
}


static long
getInputStyle(s)
char *s;
{
    char c;
    unsigned int len;

    c = s[0];
    len = strlen(s);

    if ((c == 'o') && (strncmp(s, "off", len) == 0) && (len > 2)) {
      return CONVARG_OFFTHESPOT;
    } else if ((c == 'o') && (strncmp(s, "over", len) == 0) && (len > 2)) {
      return CONVARG_OVERTHESPOT;
    } else if ((c == 'r') && (strncmp(s, "root", len) == 0)) {
      return CONVARG_ROOTWINDOW;
    }
    return 0L;
}


static long
getCaptureMethod(s)
char *s;
{
    char c;
    unsigned int len;

    c = s[0];
    len = strlen(s);


    if ((c == 'n') && (strncmp(s, "none", len) == 0)) {
      return CONVARG_NONE;
    } else if ((c == 'i') && (strncmp(s, "inputOnly", len) == 0)) {
      return CONVARG_CREATE_INPUTONLY;
    } else if ((c == 'f') && (strncmp(s, "focusSelect", len) == 0)) {
      return CONVARG_SELECT_FOCUS_WINDOW;
    }
    return 0L;
}


static int
makeAttrData(interp, tkwin, ki2Ptr, datap)
    Tcl_Interp *interp;
    Tk_Window tkwin;
    Kinput2Info *ki2Ptr;
    unsigned long **datap;
{
    static int max_length = 0;
#ifdef STATIC_ATTR
    unsigned long buf[4096];
#else
    static unsigned long *buf = NULL;
#endif /* STATIC_ATTR */
    int length = 0;

#ifdef STATIC_ATTR
#define ALLOC(n) ;;
#else
#ifdef ATTR_USE_REALLOC
#define ALLOC(n) \
    if (length + (n) > max_length ) {                             \
      max_length += (n);                                    \
      buf = (unsigned long *)ckrealloc(buf, (unsigned)(max_length * 4));      \
    }
#else
#define ALLOC(n) \
    if (length + (n) > max_length ) {                             \
      unsigned long *tmp;                                   \
      max_length += (n);                                    \
      tmp = (unsigned long *) ckalloc((unsigned)(max_length * 4));      \
      memcpy((VOID *) tmp, (VOID *) buf, (unsigned int)(length * 4));   \
      ckfree((char *) buf);                                 \
      buf = tmp;                                      \
    }
#endif /* ATTR_USE_REALLOC */
#endif /* STATIC_ATTR */

#ifndef STATIC_ATTR
    if (buf == NULL) {
      buf = (unsigned long *) ckalloc((unsigned)(max_length * 4));
    }
#endif /* !STATIC_ATTR */

    if (ki2Ptr->inputStyle.specified) {
      long style = getInputStyle(ki2Ptr->inputStyle.value);

      if (style == 0L) {
          Tcl_AppendResult(interp, "Warning: bad inputStyle - \"",
                ki2Ptr->inputStyle.value, "\"\n", (char *) NULL);
          ki2Ptr->inputStyle.specified = None;
      } else {
          ALLOC(2);
          buf[length] = CONV_ATTR(CONVATTR_INPUT_STYLE, 1);
          buf[length+1] = style;
          length += 2;
      }
    }
    if (ki2Ptr->focusWindow.specified) {
      Tk_Window win = Tk_NameToWindow(interp, ki2Ptr->focusWindow.value, tkwin);

      if (win == NULL ) {
          Tcl_AppendResult(interp, "Warning: bad focusWindow - \"",
                ki2Ptr->focusWindow.value, "\"\n", (char *) NULL);
          ki2Ptr->focusWindow.specified = None;
      } else {
          ALLOC(2);
          buf[length] = CONV_ATTR(CONVATTR_FOCUS_WINDOW, 1);
          buf[length+1] = (unsigned long) Tk_WindowId(win);
          length += 2;
      }
    }
    if (ki2Ptr->spot.specified) {
      int xargc;
      char **xargv;
      int spotX, spotY;

      if (Tcl_SplitList(interp, ki2Ptr->spot.value, &xargc, &xargv) != TCL_OK) {
          ki2Ptr->spot.specified = None;
      } else {
          if (xargc != 2
            || Tcl_GetInt(interp, xargv[0], &spotX) != TCL_OK
            || Tcl_GetInt(interp, xargv[1], &spotY) != TCL_OK) {
            Tcl_AppendResult(interp, "Warning: bad spot - \"",
                  ki2Ptr->spot.value, "\"\n", (char *) NULL);
            ki2Ptr->spot.specified = None;
          } else {
            ALLOC(2);
            buf[length] = CONV_ATTR(CONVATTR_SPOT_LOCATION, 1);
            buf[length+1] = (spotX << 16) | (spotY & 0xffff);
            length += 2;
          }
          ckfree((char *) xargv);
      }
    }
    if (ki2Ptr->foreground.specified && ki2Ptr->background.specified) {
      XColor *fgPtr, *bgPtr;

      fgPtr = Tk_GetColor(interp, tkwin, Tk_GetUid(ki2Ptr->foreground.value));
      bgPtr = Tk_GetColor(interp, tkwin, Tk_GetUid(ki2Ptr->background.value));
      if (fgPtr == NULL) {
          Tcl_AppendResult(interp, "Warning: bad foreground - \"",
                ki2Ptr->foreground.value, "\"\n", (char *) NULL);
          ki2Ptr->foreground.specified = None;
      }
      if (bgPtr == NULL) {
          Tcl_AppendResult(interp, "Warning: bad background - \"",
                ki2Ptr->background.value, "\"\n", (char *) NULL);
          ki2Ptr->background.specified = None;
      }
      if (fgPtr && bgPtr) {
          ALLOC(3);
          buf[length] = CONV_ATTR(CONVATTR_COLOR, 2);
          buf[length+1] = fgPtr->pixel;
          buf[length+2] = bgPtr->pixel;
          length += 3;
      }
    }
    if (ki2Ptr->eventCaptureMethod.specified) {
      long method = getCaptureMethod(ki2Ptr->eventCaptureMethod.value);

      if (method == 0L) {
          Tcl_AppendResult(interp, "Warning: bad eventCaptureMethod - \"",
                ki2Ptr->eventCaptureMethod.value, "\"\n", (char *) NULL);
          ki2Ptr->eventCaptureMethod.specified = None;
      } else {
          ALLOC(2);
          buf[length] = CONV_ATTR(CONVATTR_EVENT_CAPTURE_METHOD, 1);
          buf[length+1] = method;
          length += 2;
      }
    }
    if (ki2Ptr->lineSpacing.specified) {
      int spacing;

      if (Tcl_GetInt(interp, ki2Ptr->lineSpacing.value, &spacing) != TCL_OK) {
          Tcl_AppendResult(interp, "Warning: bad lineSpacing - \"",
                ki2Ptr->lineSpacing.value, "\"\n", (char *) NULL);
          ki2Ptr->lineSpacing.specified = None;
      } else {
          ALLOC(2);
          buf[length] = CONV_ATTR(CONVATTR_LINE_SPACING, 1);
          buf[length+1] = spacing;
          length += 2;
      }
    }
    if (ki2Ptr->clientArea.specified) {
      int xargc;
      char **xargv;
      int x, y, width, height;

      if (Tcl_SplitList(interp, ki2Ptr->clientArea.value, &xargc, &xargv) != TCL_OK) {
          ki2Ptr->clientArea.specified = None;
      } else {
          if (xargc != 4
            || Tcl_GetInt(interp, xargv[0], &x) != TCL_OK
            || Tcl_GetInt(interp, xargv[1], &y) != TCL_OK
            || Tcl_GetInt(interp, xargv[2], &width) != TCL_OK
            || Tcl_GetInt(interp, xargv[3], &height) != TCL_OK) {
            Tcl_AppendResult(interp, "Warning: bad clientArea - \"",
                  ki2Ptr->clientArea.value, "\"\n", (char *) NULL);
            ki2Ptr->clientArea.specified = None;
          } else {
            ALLOC(3);
            buf[length] = CONV_ATTR(CONVATTR_CLIENT_AREA, 2);
            buf[length+1] = (x << 16) | (y & 0xffff);
            buf[length+2] = (width << 16) | (height & 0xffff);
            length += 3;
          }
          ckfree((char *) xargv);
      }
    }
    if (ki2Ptr->statusArea.specified) {
      int xargc;
      char **xargv;
      int x, y, width, height;

      if (Tcl_SplitList(interp, ki2Ptr->statusArea.value, &xargc, &xargv) != TCL_OK) {
          ki2Ptr->statusArea.specified = None;
      } else {
          if (xargc != 4
            || Tcl_GetInt(interp, xargv[0], &x) != TCL_OK
            || Tcl_GetInt(interp, xargv[1], &y) != TCL_OK
            || Tcl_GetInt(interp, xargv[2], &width) != TCL_OK
            || Tcl_GetInt(interp, xargv[3], &height) != TCL_OK) {
            Tcl_AppendResult(interp, "Warning: bad statusArea - \"",
                    ki2Ptr->statusArea.value, "\"\n", (char *) NULL);
            ki2Ptr->statusArea.specified = None;
          } else {
            ALLOC(3);
            buf[length] = CONV_ATTR(CONVATTR_STATUS_AREA, 2);
            buf[length+1] = (x << 16) | (y & 0xffff);
            buf[length+2] = (width << 16) | (height & 0xffff);
            length += 3;
          }
          ckfree((char *) xargv);
      }
    }
    if (ki2Ptr->cursor.specified) {
      Cursor cursor = (Cursor) Tk_GetCursor(interp, tkwin,
                              Tk_GetUid(ki2Ptr->cursor.value));

      if (cursor == None) {
          Tcl_AppendResult(interp, "Warning: bad cursor - \"",
                ki2Ptr->cursor.value, "\"\n", (char *) NULL);
          ki2Ptr->cursor.specified = None;
      } else {
          ALLOC(2);
          buf[length] = CONV_ATTR(CONVATTR_CURSOR, 1);
          buf[length+1] = cursor;
          length += 2;
      }
    }
    if (ki2Ptr->fonts.specified) {
      int xargc;
      char **xargv;

      if (Tcl_SplitList(interp, ki2Ptr->fonts.value, &xargc, &xargv) == TCL_OK) {
          XFontStruct *fontPtr;
          int nfonts, i;
          unsigned long atom;

#ifdef OLD_TK4X
          ALLOC(xargc+1);
#endif /* OLD_TK4X */
          nfonts = 0;
          for (i = 0; i < xargc; i++) {
#ifdef OLD_TK4X
            fontPtr = Tk_GetFontStruct(interp, tkwin, Tk_GetUid(xargv[i]));
            if (fontPtr != NULL
                && XGetFontProperty(fontPtr, XA_FONT, &atom)) {
                buf[length + ++nfonts] = atom;
            } else {
                Tcl_AppendResult(interp, "Warning: bad font - \"",
                      xargv[i], "\"\n", (char *) NULL);
            }
#else

#define doALLOC(n) \
    if (nfonts == 0) { \
        ALLOC((n)+1); \
        length += 2; \
    } else { \
        ALLOC(1); \
        length++; \
    } \
    nfonts++; \
    buf[oLength] = CONV_ATTR(CONVATTR_FONT_ATOMS, nfonts); \
    buf[oLength + nfonts] = atom;

            int oLength = length;
            Tk_Font tkfont = Tk_GetFont(interp, tkwin, Tk_GetUid(xargv[i]));
            if (tkfont != NULL) {
                if (Tk_FontType(tkfont) == TK_FONT_COMPOUND) {
                  fontPtr = TkpGetAsciiFontStruct(tkfont);
                  if (fontPtr != NULL
                      && XGetFontProperty(fontPtr, XA_FONT, &atom)) {
                      doALLOC(1);
                  }
                  fontPtr = TkpGetKanjiFontStruct(tkfont);
                  if (fontPtr != NULL
                      && XGetFontProperty(fontPtr, XA_FONT, &atom)) {
                      doALLOC(1);
                  }
                } else {
                  Tk_Font defFont;
                  fontPtr = TkpGetFontStruct(tkfont);
                  if (fontPtr != NULL
                      && XGetFontProperty(fontPtr, XA_FONT, &atom)) {
                      doALLOC(1);
                  }
                  defFont = TkpGetDefaultFontByDisplay(Tk_Display(tkwin));
                  if (defFont != NULL) {
                      fontPtr = TkpGetFontStruct(defFont);
                      if (fontPtr != NULL
                        && XGetFontProperty(fontPtr, XA_FONT, &atom)) {
                        doALLOC(1);
                      }
                  }   
                }
            } else {
                Tcl_AppendResult(interp, "Warning: bad font - \"",
                             xargv[i], "\"\n", (char *) NULL);
            }
            Tk_FreeFont(tkfont);
#undef doALLOC
#endif /* OLD_TK4X */
          }
#ifdef OLD_TK4X
          if (nfonts == 0) {
            ki2Ptr->fonts.specified = None;
          } else {
            buf[length] = CONV_ATTR(CONVATTR_FONT_ATOMS, nfonts);
            length += nfonts+1;
          }
#else
          if (nfonts == 0) {
            ki2Ptr->fonts.specified = None;
          }
#endif /* OLD_TK4X */
          ckfree((char *) xargv);
      }
    }

    *datap = (unsigned long *) ckalloc((unsigned)(length * 4));
    memcpy((VOID *) *datap, (VOID *) buf, (unsigned int)(length * 4));

    return length;
#undef ALLOC    
}

/*
 *--------------------------------------------------------------
 *
 * forwardKeyEvent --
 *
 *    This procedure forwards the key events to the input server
 *    (kinput2).
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    This procedure may change the contents of the event.
 *
 *--------------------------------------------------------------
 */

static void
forwardKeyEvent(clientdata, event)
ClientData clientdata;
XEvent *event;
{
    ConversionContext *context = (ConversionContext *)clientdata;
    Display *dpy = event->xany.display;
    Window w = context->forwardwin;
    Tk_ErrorHandler handle;

    /*
     * If the event is a non-synthetic Key event and the target
     * window of the input server is not None, we forward the event
     * to the window.
     * Checking whether the event is synthetic or not is necessary
     * because the input server might send back the event we've just
     * forwarded, and forwarding those events sent back by the server
     * can cause an infinite event loop.
     */
    if (w != None && !event->xany.send_event &&
      (event->type == KeyPress || event->type == KeyRelease)) {
      Window orig_win = event->xkey.window;

      event->xkey.window = w;
      handle = Tk_CreateErrorHandler(dpy, -1, -1, -1,
                               (Tk_ErrorProc *)stopForwarding,
                               clientdata);
      (void)XSendEvent(dpy, w, False, KeyPressMask, event);
      Tk_DeleteErrorHandler(handle);
      event->xkey.window = orig_win;

      /*
       * Since this event has been sent to the input server,
       * we must stop the further processing of the event.
       * We have determined to change the keycode field of
       * the event to an invalid value, because there's no
       * appropriate way to do it.
       * The range of the valid keycode is 8-255, and keycode 0
       * is used by the X11R5 I18N as a special code, so we've
       * chosen 1.
       */
      event->xkey.keycode = 1;
    }
}

/*
 *--------------------------------------------------------------
 *
 * stopForwarding --
 *
 *    This procedure stops forwarding the key events.
 *    It is intended to use as an error handler which is
 *    installed by Tk_CreateErrorHandler().
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *--------------------------------------------------------------
 */

static int
stopForwarding(clientdata, errEvent)
ClientData clientdata;
XErrorEvent *errEvent;
{
    ConversionContext *context = (ConversionContext *)clientdata;

    if (errEvent->type == BadWindow) {
      callEnd(context->tkwin, context);
      finishConversion(context->tkwin, context);
      ckfree((char *)context);
    }
    return 0;
}

#endif /* KANJI && KINPUT2 */

Generated by  Doxygen 1.6.0   Back to index