/* # skkinput (Simple Kana-Kanji Input)
 * Kinput.c --- Kinput Protocol
 * This file is part of skkinput.
 * Copyright (C) 1997
 * Takashi SAKAMOTO (sakamoto@yajima.kuis.kyoto-u.ac.jp)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with skkinput; see the file COPYING.  If not, write to
 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/IntrinsicP.h>
#include <X11/StringDefs.h>
#include <X11/Shell.h>

#include "commondef.h"
#include "KinputP.h"
#include "protocol.h"
#include "SeparateWin.h"
#include "OverWin.h"
#include "OffWin.h"
#include "attrs.h"
#include "MyDispatch.h"
#include "MyError.h"
#include "resrcs.h"
#include "HistMgr.h"
#include "FontMgr.h"

struct proprec {
  Atom prop ;
  struct proprec *prev ;
} ;

enum {
  PROTOCOL_TYPE_UNKNOWN = 0,
  PROTOCOL_TYPE_KINPUT1,
  PROTOCOL_TYPE_KINPUT2,
  PROTOCOL_TYPE_XIM,
} ;

/*
 * プロトタイプ宣言。
 */
/* KinputWidget のイベント処理関数たち。*/
static void KIP_ConversionRequestEventHandler
( Widget gw, XEvent *xevent, String *params, Cardinal *num_params ) ;
static void KIP_ConversionEndRequestEventHandler
( Widget gw, XEvent *xevent, String *params, Cardinal *num_params ) ;
static void KIP_ConversionAttributeNotifyEventHandler
( Widget gw, XEvent *xevent, String *params, Cardinal *num_params ) ;
static void KIP_SelectionRequestEventHandler
( Widget gw, XEvent *xevent, String *params, Cardinal *num_params ) ;
static void KIP_SelectionClearEventHandler
( Widget gw, XEvent *xevent, String *params, Cardinal *num_params ) ;

/* KinputWidget の骨組みとなる関数たち。*/
static void KIP_Initialize 
( Widget greq, Widget gnew, ArgList args, Cardinal *num_args ) ;
static void KIP_Realize
( Widget gw, XtValueMask *valueMask, XSetWindowAttributes *attrs ) ;
static Boolean KIP_SetValues
( Widget current, Widget request, Widget new, 
  ArgList args, Cardinal *num_args );
static void KIP_Destroy( Widget gw ) ;

/* それ以外の関数たち(まあ、肉とか内臓とかそういうのかな) */
static void KIP_GetAtoms( Widget gw ) ;
static void KIP_SetKinput2Property( Widget gw ) ;
static void KIP_AcceptConversion
( Widget gw, struct KinputClient *client ) ;
static void KIP_RejectConversion
( Widget gw, Window requestor, Atom conversion_atom ) ;
static void KIP_SendEndOfConversion
( struct KinputClient *client ) ;
static int KIP_PrepareConversion
( Widget gw, struct KinputClient *client, Window requestor,
  Atom selection, Atom property ) ;
static int KIP_GetAttributeFromPropertySub2
( struct KinputClient *client, Atom property,
  unsigned long **attributes ) ;
static int KIP_GetAttributeFromPropertySub
( struct KinputClient *client,   Atom property,
  struct proprec *prevprop, int fromevent, int is_widget_ok ) ;
static int KIP_GetAttributeFromProperty
( struct KinputClient *client, Atom property, int is_widget_ok ) ;
static int KIP_GetAttributeFromEvent
( struct KinputClient *client, XClientMessageEvent *xcev ) ;

static struct KinputClient *KIP_PrepareNewClient
( Widget gw, Window req_window, Atom conversion_atom ) ;
static struct KinputClient *KIP_FindKinputClient
( Widget gw, Window req_window ) ;
static void KIP_RemoveKinputClient
( Widget gw, struct KinputClient *client ) ;

/* kinputWidget が他所様に用意するコールバック関数たち。*/
static void KIP_SendMessageToRequestorCallback
( Widget gw, caddr_t client, caddr_t caller ) ;
static void KIP_EndMessageToRequestorCallback
( Widget gw, caddr_t client, caddr_t caller ) ;
static void KIP_KeyEventToRequestorCallback
( Widget gw, caddr_t client, caddr_t caller ) ;

/*
 * kinput.c の中には無い他所様が持っている関数のプロトタイプ宣言。
 */
/* ctext.c */
extern int string2ctext( struct myChar *string, unsigned char *cstr ) ;

/* FontMgr.c */
extern int prepare_font_from_atom
( Display *disp, XFontStruct *skkinput_fonts[ 2 ],
  Atom *fontatoms, Cardinal num_of_fonts ) ;
extern int close_fonts_of_skkinput
( Display *disp, XFontStruct *skkinput_fonts[ 2 ] ) ;
extern int close_one_font_of_skkinput
( Display *disp, XFontStruct *font ) ;

/*
 * グローバル変数。
 */

#define offset(field)  XtOffsetOf(KinputRec, kinput.field)
#define goffset(field) XtOffsetOf(WidgetRec, core.field)

/* リソース一覧。*/
static XtResource kip_resources[] = {
  /* これは kinput protocol を通すかどうか。*/
  { XtNenableKinput1, XtCEnableKinput1, XtRBoolean, sizeof(Boolean),
    offset(enable_kinput1), XtRString, (XtPointer)"True" },
  /* SeparateWindow, Over-The-Spot-Window …を初期化するコールバック。 *
   * これは KinputWidget がどう初期化したら良いのか知らないことなので、*
   * コールバックの形で親に初期化をお願いしている。*/
  { XtNsetupInputWindowNotify, XtCCallback, XtRCallback,
    sizeof( caddr_t ), offset( setupInputWindowCallback ), XtRCallback,
    ( caddr_t )NULL },
  /* Kinput Protocol Widget を閉ざす時の呼出すコールバック。*/
  { XtNserverCloseNotify, XtCCallback, XtRCallback, sizeof( caddr_t ),
    offset( serverCloseCallback ), XtRCallback, ( caddr_t )NULL },
  /* クライアントが誰か落とされようとしている？ */
  { XtNdestroyWindowEvent, XtCDestroyWindowEvent, XtRImmediate,
    sizeof( XDestroyWindowEvent * ),
    offset( destroyWindowEvent ), XtRImmediate, ( XtPointer )NULL },
  /* 辞書の更新云々。*/
  { XtNjisyoDirty, XtCJisyoDirty, XtRImmediate, sizeof (int),
    offset(jisyo_dirty), XtRImmediate, (XtPointer) FALSE },
} ;
#undef offset
#undef goffset

/* イベントに対するアクションのテーブル。*/
static XtActionsRec kip_actions_table[] = {
  /* 変換開始要求の時に呼ばれる関数。*/
  { "kip-conversion-request",	
    KIP_ConversionRequestEventHandler },
  /* 変換終了要求の時に呼ばれる関数。*/
  { "kip-conversion-end-request",
    KIP_ConversionEndRequestEventHandler },
  /* こっちは「属性を修正したから見てね」という要求を受ける。*/
  { "kip-conversion-attribute-notify",
    KIP_ConversionAttributeNotifyEventHandler },
  /* _JAPANESE_CONVERSION が欲しいと言われた時の処理を行う。*/
  { "kip-selection-request",
    KIP_SelectionRequestEventHandler },
  /* _JAPANESE_CONVERSION を失った時の処理を行う。*/
  { "kip-selection-clear",
    KIP_SelectionClearEventHandler },
};

/*- default translation -*/
static char default_kip_translations[] =
"<Message>CONVERSION_REQUEST:          kip-conversion-request()\n\
 <Message>CONVERSION_END_REQUEST:      kip-conversion-end-request()\n\
 <Message>CONVERSION_ATTRIBUTE_NOTIFY: kip-conversion-attribute-notify()\n\
 <SelReq>:                             kip-selection-request()\n\
 <SelClr>:                             kip-selection-clear()";

KinputClassRec kinputClassRec = {
    { /* core fields */
    /* superclass		*/	&widgetClassRec,
    /* class_name		*/	"Kinput",
    /* size			*/	sizeof( KinputRec ),
    /* class_initialize		*/	NULL,
    /* class_part_initialize	*/	NULL,
    /* class_inited		*/	FALSE,
    /* initialize		*/	KIP_Initialize,
    /* initialize_hook		*/	NULL,
    /* realize			*/	KIP_Realize,
    /* actions			*/	kip_actions_table,
    /* num_actions		*/	XtNumber( kip_actions_table ),
    /* resources		*/	kip_resources,
    /* num_resources		*/	XtNumber( kip_resources ),
    /* xrm_class		*/	NULLQUARK,
    /* compress_motion		*/	TRUE,
    /* compress_exposure	*/	TRUE,
    /* compress_enterleave	*/	TRUE,
    /* visible_interest		*/	FALSE,
    /* destroy			*/	KIP_Destroy,
    /* resize			*/	NULL,
    /* expose			*/	NULL,
    /* set_values		*/	KIP_SetValues,
    /* set_values_hook		*/	NULL,
    /* set_values_almost	*/	XtInheritSetValuesAlmost,
    /* get_values_hook		*/	NULL,
    /* accept_focus		*/	NULL,
    /* version			*/	XtVersion,
    /* callback_private		*/	NULL,
    /* tm_table			*/	default_kip_translations,
    /* query_geometry		*/	XtInheritQueryGeometry,
    }
};

WidgetClass kinputWidgetClass = (WidgetClass)&kinputClassRec;

/*
 * KinputWidgetClass の初期化関数。
 */
static void KIP_Initialize 
( Widget greq, Widget gnew, ArgList args, Cardinal *num_args )
{
  KinputWidget w = ( KinputWidget )gnew ;
#if 0
  /* デフォルトのフォントの大きさを求めておく…必要なのか？ */
  KIP_GetDefaultFontHeight( gnew ) ;
#endif
  /* 処理されているクライアントは何もない。*/
  w->kinput.client_list = NULL ;
  /* 変換に利用するアトムを作成しておく。*/
  KIP_GetAtoms( gnew ) ;
  return ;
}

/*
 * Kinput Protocol で Message 交換に利用される Atom を作成する関数。
 */
static void KIP_GetAtoms( Widget gw )
{
  Display *disp  = XtDisplay( gw ) ;
  KinputWidget w = ( KinputWidget )gw ;

  /* Kinput Protocol で必要とされる Atom を作成する。*/
#define MakeAtom(s)	XInternAtom(disp,s,False)
  w->kinput.convAtom       = MakeAtom( "_JAPANESE_CONVERSION" ) ;
  w->kinput.oldConvAtom    = MakeAtom( "JAPANESE_CONVERSION" ) ;
  w->kinput.ctextAtom      = MakeAtom( "COMPOUND_TEXT" ) ;
  w->kinput.convStringAtom = MakeAtom( "CONVERSION_STRING" ) ;
  w->kinput.convNotifyAtom = MakeAtom( "CONVERSION_NOTIFY" ) ;
  w->kinput.convEndAtom    = MakeAtom( "CONVERSION_END" ) ;
  w->kinput.convAttrAtom   = MakeAtom( "CONVERSION_ATTRIBUTE" ) ;
#undef MakeAtom
  return ;
}

/*
 * KinputWidget を実体化させる時に呼び出される関数。
 */
static void KIP_Realize
( Widget gw, XtValueMask *valueMask, XSetWindowAttributes *attrs )
{
  KinputWidget w        = ( KinputWidget )gw ;
  CoreWidgetClass super =
    ( CoreWidgetClass )XtClass( gw )->core_class.superclass ;

  /* 先に適切なウィンドウを用意させておかなければ、後の *
   * XChangeProperty は動作しないことになる。*/
  ( *super->core_class.realize )( gw, valueMask, attrs ) ;

  /* 次に CONVERSION_REQUEST の OWNER になる。*/
  XSetSelectionOwner
    ( XtDisplay( gw ), w->kinput.convAtom, XtWindow( gw ), CurrentTime ) ;
  /* 無事に Owner になれたかどうかを判定する。*/
  if( XGetSelectionOwner( XtDisplay( gw ), w->kinput.convAtom )
      != XtWindow( gw ) ){
    /* Owner になれなかった場合には…変換要求を受けとることはずっと *
     * できないので、さようならする？ */
    XtDestroyWidget( gw ) ;
    return ;
  }
  /* kinput1 protocol の方のオーナになるべきかどうか。*/
  if( w->kinput.enable_kinput1 != False ){
    /* 次に CONVERSION_REQUEST の OWNER になる。*/
    XSetSelectionOwner
      ( XtDisplay( gw ), w->kinput.oldConvAtom,
	XtWindow( gw ), CurrentTime ) ;
  }
  /* Property の設定を行っておく。*/
  KIP_SetKinput2Property( gw ) ;
  return ;
}

/*
 * 何か KinputWidget の持つ変数の値の変更をしたいという時に呼ばれる関
 * 数。
 * ------
 * これは、Widget へ外部からメッセージを送った時にどのような処理をする
 * のかということとみなすことができる。しかし、一々これで設定すると面
 * 倒だと思ってしまい、グローバル変数で渡したくなってしまうのだが…そ
 * れでは移植性が低くなる。
 */
static Boolean KIP_SetValues
( Widget current, Widget request, Widget new,
  ArgList args, Cardinal *num_args )
{
  KinputWidget curw = ( KinputWidget )current ;
  KinputWidget reqw = ( KinputWidget )request ;
  KinputWidget neww = ( KinputWidget )new ;
  struct KinputClient *client ;

  /* まさか誰かクライアントが落とされようとしている？！ */
  if( reqw->kinput.destroyWindowEvent != NULL ){
    XDestroyWindowEvent *xdwe   = reqw->kinput.destroyWindowEvent ;
    client = reqw->kinput.client_list ;
    while( client != NULL ){
      if( xdwe->window == client->req_window ||
	  xdwe->window == client->focus_window ){
	/* 落とされようとしている窓からの入力をはねるようにするけど… */
	/* 意味があるのかどうかは分からない。*/
	remove_allmyeventhandler
	  ( XtDisplay( new ), client->req_window ) ;
	XSafeSelectInput
	  ( XtDisplay( new ), client->req_window, NoEventMask ) ;
	remove_allmyeventhandler
	  ( XtDisplay( new ), client->focus_window ) ;
	XSafeSelectInput
	  ( XtDisplay( new ), client->focus_window, NoEventMask ) ;
	client->req_window = client->focus_window = None ;
	/* じゃあ、閉じましょう。*/
	XtDestroyWidget( client->skkinput ) ;
	break ;
      }
      client = client->next ;
    }
    curw->kinput.destroyWindowEvent =
      neww->kinput.destroyWindowEvent =
      reqw->kinput.destroyWindowEvent = NULL ;
    return( FALSE ) ;
  }
  if( reqw->kinput.jisyo_dirty != curw->kinput.jisyo_dirty ){
    client = neww->kinput.client_list ;
    while( client != NULL ){
      XtVaSetValues
	( client->skkinput, XtNjisyoDirty, reqw->kinput.jisyo_dirty, NULL ) ;
      client = client->next ;
    }
    curw->kinput.jisyo_dirty = neww->kinput.jisyo_dirty = 
      reqw->kinput.jisyo_dirty ;
    return ( FALSE ) ;
  }
  /* ここでは何もすることがない。*/
  return( FALSE ) ;
}

/*
 * KinputWidget を破棄する時に呼ばれる関数。
 */
static void KIP_Destroy( Widget gw )
{
  KinputWidget w = ( KinputWidget )gw ;
  struct KinputClient *node, *nextNode ;

  node = w->kinput.client_list ;
  while( node != NULL ){
    nextNode = node->next ;
    /* Ximp で処理されていたヒストリは全て不要になった。*/
    history_destroy( node->req_window ) ;
    /* そのクライアントは処理を停止する。*/
    XtDestroyWidget( node->skkinput ) ;
    /* このタイミングでノードがフリーされてしまっている可能性があるの */
    /* で、nextNode に避けていたのである。*/
    node = nextNode ;
  }
  /* めでたく空になりました。*/
  w->kinput.client_list = NULL ;
  
  /* 自分自身を破棄すべく、親ウィジェットに処理を要求する。*/
  XtCallCallbacks( gw, XtNserverCloseNotify, NULL ) ;
  /* Kinput Protocol のサーバとしての役目を停止する。*/
  return ;
}

/*
 * Kinput2 Protocol のサーバになった時に用意しなければならないデータを 
 * X のプロパティとして登録しておくのに使う関数。クライアントはこのプ
 * ロパティを見ていると思う。
 */ 
static void KIP_SetKinput2Property( Widget gw )
{
  Display *disp  = XtDisplay( gw ) ;
  Atom server_property, server_type ;
  unsigned long server_profile[ 10 ] ;
  
  /* プロフィール？ */
  server_property = XInternAtom( disp, CONVERSION_PROFILE, False ) ;
  /* 変換サーバの属性の型？ */
  server_type     = XInternAtom
    ( disp, CONVERSION_ATTRIBUTE_TYPE, False ) ;
  
  /* まずプロトコルのバージョンを設定する…。*/
  server_profile[ 0 ] = MAKE_CONVERSION_ATTRIBUTE
    ( CONVERSION_PROFILE_PROTOCOL_VERSION, 1 ) ;
  server_profile[ 1 ] = XInternAtom
    ( disp, PROTOCOL_VERSION, False ) ;
  /* 次に可能な窓の表示方法について知らしめる。*/
  server_profile[ 2 ] = MAKE_CONVERSION_ATTRIBUTE
    ( CONVERSION_PROFILE_SUPPORTED_STYLES, 1 ) ;
  server_profile[ 3 ] =
    CONVERSION_NYUURYOKU_HOUSHIKI_SEPARATEMADO |
    CONVERSION_NYUURYOKU_HOUSHIKI_OFFTHESPOT |
    CONVERSION_NYUURYOKU_HOUSHIKI_OVERTHESPOT ;

  /* 本当は次の３つを全部サポートしなければならないけど、今はプロト *
   * コルの動作実験をしたいので、一つだけにしてある。*/
  /* CONVERSION_NYUURYOKU_HOUSHIKI_SEPARATEMADO |
     CONVERSION_NYUURYOKU_HOUSHIKI_OFFTHESPOT |
     CONVERSION_NYUURYOKU_HOUSHIKI_OVERTHESPOT */
  
  /* これらの情報をサーバの情報としてクライアントが入手可能なように *
   * 登録しておく。*/
  XChangeProperty
    ( disp, XtWindow( gw ), server_property, server_type, 32,
      PropModeReplace, ( unsigned char *)server_profile, 4 ) ;
  return ;
}

/*
 * Kinput Protocol として正しいメッセージが送られて来たのかどうかを判
 * 定する関数。
 */
static int KIP_CorrectClientMessageP( Widget gw, XEvent *xevent )
{
  XClientMessageEvent *ev = &xevent->xclient ;
  KinputWidget         w  = ( KinputWidget )gw ;

#ifdef KIP_DEBUG
  fprintf( stderr, "Get Client Message. I check whether it is correct.\n" ) ;
  fflush( stderr ) ;
#endif
  /* 正しいメッセージが送られてきたのかどうかを判定する。*/
  if( ev->window != XtWindow( gw ) ||
      ev->format != 32 ||
      ( ev->data.l[ 0 ] != w->kinput.convAtom &&
	( w->kinput.enable_kinput1 &&
	  ev->data.l[ 0 ] != w->kinput.oldConvAtom ) ) ){
    /* 不正なメッセージが送られてきたみたいなので、無視しませう。*/
    return  False ;
  }
  return True ;
}

/*
 * 変換開始の要求が来た時の処理を行う関数。
 */
static void KIP_ConversionRequestEventHandler
( Widget gw, XEvent *xevent, String *params, Cardinal *num_params )
{
  KinputWidget w           = ( KinputWidget )gw ;
  XClientMessageEvent *xev = &( xevent->xclient ) ;
  Window requestor ;
  Atom conversion_atom, property ;
  struct KinputClient *client ;

#ifdef DEBUG
  fprintf( stderr, "Conversion Request from Window(%ld)\n",
	   ( unsigned long )xevent->xany.window ) ;
#endif
  /* 正しいイベントさんなのかどうかをチェック致します。*/
  if( !KIP_CorrectClientMessageP( gw, xevent ) ){
#ifdef KIP_DEBUG
    fprintf( stderr, "Illegal conversion-request message(Kinput2)\n" ) ;
#endif
   return ;
  }
  conversion_atom = ( Atom   )xev->data.l[ 0 ] ;
  requestor       = ( Window )xev->data.l[ 1 ] ;
  property        = ( Atom   )xev->data.l[ 4 ] ;

  /*
   * 現在、そのクライアントは処理されている最中ではないのか？
   */
  if( ( client = KIP_FindKinputClient( gw, requestor ) ) != NULL ){
    KIP_RejectConversion( gw, requestor, conversion_atom ) ;
    return ;
  }
  /* 新しくクライアントを処理するためのデータ(情報)を入れるためのノー */
  /* ドを用意する。*/
  client = KIP_PrepareNewClient( gw, requestor, conversion_atom ) ;
  if( client == NULL ){
    /* メモリが不足していた場合。exit しても良いとは思うけど。*/
    KIP_RejectConversion( gw, requestor, conversion_atom ) ;
    return ;
  }
  /*
   * ★★★ 変換クライアントに対する処理を開始する。★★★
   */
  if( property != None ){
    client->protocol     = PROTOCOL_TYPE_KINPUT2 ;
    client->window_style = WINDOW_TYPE_SEPARATE ;
    client->event_select = EVENT_HA_INPUTONLY ;
    /* Kinput2 ならではの属性設定があるから、そっちを見に行く。*/
    KIP_GetAttributeFromProperty( client, property, False ) ;
  } else {
    client->protocol     = PROTOCOL_TYPE_KINPUT1 ;
    client->window_style = WINDOW_TYPE_SEPARATE ;
    client->event_select = EVENT_HA_INPUTONLY ;
  }
  /* SKKINPUT WINDOW を作成する。失敗したら即座に抜ける。*/
  if( !KIP_PrepareConversion
      ( gw, client, requestor, conversion_atom, property ) ){
    KIP_RejectConversion( gw, requestor, conversion_atom ) ;
    return ;
  }
  /* INPUT WINDOW を作成した後の処理。それは SKKINPUT WINDOW の *
   * CALLBACK の設定その他である。*/
  client->target   = w->kinput.ctextAtom ;
  client->property = xev->data.l[ 3 ] ;
  if( client->property == None ){
    client->property = w->kinput.convStringAtom ;
  }
  /* 変換クライアントにサーバとの接続に成功した旨を伝える。*/
  KIP_AcceptConversion( gw, client ) ;
  return ;
}

/*
 * client からの要求に従って、変換用の Window を起こす関数。
 */
static int KIP_PrepareConversion
( Widget gw, struct KinputClient *client, Window requestor,
  Atom selection, Atom property )
{
  Window probe_window ;
  unsigned char *class_name ;
  WidgetClass class ;
  HistoryListNode *history ;

  /* どの窓から要求が来ていたのかを記憶しておく。*/
  client->req_window   = requestor ;
  if( client->focus_window == None )
    client->focus_window = requestor ;
  /* その窓が利用中であることを知らせる。*/
  client->probe        = True ;
  client->reserve      = True ;
  client->selection    = selection ;

  /* ヒストリの初期化。これを忘れると？ どうなるのだろうか。*/
  history = history_setup( client->skkinput, client->req_window ) ;

  /* 変換 Widget を作成する。*/
  switch( client->window_style ){
  case WINDOW_TYPE_OVERTHESPOT :
    class_name = "overthespot" ;
    class = overthespotWinWidgetClass ;
    break ;
  case WINDOW_TYPE_OFFTHESPOT :
    class_name = "offthespot" ;
    class = offthespotWinWidgetClass ;
    break ;
  case WINDOW_TYPE_SEPARATE :
  default :
    class_name = "separate" ;
    class = separateWinWidgetClass ;
    break ;
  }
  client->skkinput = XtVaCreateWidget
    ( class_name, class, gw, 
      XtNmappedWhenManaged, False,
      XtNclientWindow, client->req_window,
      XtNconversionHistory, history, NULL ) ;
  if( client->skkinput == NULL ){
    /* ヒストリを解放する。これを忘れると、メモリを占有しっぱなしに *
     * なるぞ、と。*/
    if( history != NULL )
      free( history ) ;
    return False ;
  }
  /* 親しか知らないセットアップ処理を行う。*/
  XtCallCallbacks
    ( gw, XtNsetupInputWindowNotify, client->skkinput ) ;

  /* コールバックの設定。realize してからでは設定できないので、ここに *
   * 入ることになる。*/
  XtAddCallback (client->skkinput, XtNfixNotify,
				 ( XtCallbackProc )KIP_SendMessageToRequestorCallback,
				 ( XtPointer )client ) ;
  XtAddCallback ( client->skkinput, XtNendNotify,
				  ( XtCallbackProc )KIP_EndMessageToRequestorCallback,
				  ( XtPointer )client ) ;
  XtAddCallback ( client->skkinput, XtNkeybackNotify,
				  ( XtCallbackProc )KIP_KeyEventToRequestorCallback,
				  ( XtPointer )client ) ;
  /* Widget を realize させてしまう前に属性の設定を完了させておく。*/
  if( client->protocol == PROTOCOL_TYPE_KINPUT2 ){
#if 0
    KIP_SetFocusWindowAttribute( client ) ;
#endif
    /* Kinput2 ならではの属性設定があるから、そっちを見に行く。*/
    KIP_GetAttributeFromProperty( client, property, True ) ;
  }
  if( client->event_select == EVENT_HA_INPUTONLY ){
    /* 要求された窓の上に透明なウィンドウを作成して、そこからキーを受 */
    /* けとっている。*/
    probe_window = client->probe_window = XCreateWindow
      ( XtDisplay( gw ), client->focus_window, 0, 0, 9999, 9999, 0, 0,
	InputOnly, (Visual *)CopyFromParent,
	0L, (XSetWindowAttributes *)NULL);
    XMapWindow( XtDisplay( gw ),  probe_window ) ;
    /* クライアントの destory event には注意していなければならない。勝 */
    /* 手に消えられてしまうと落ちてしまうから。*/
  } else if( client->event_select == EVENT_HA_FOCUS_KARA ){
    probe_window = client->focus_window ;
  } else {
    probe_window = None ;
  }
  /* Probe Window を設定する。*/
  XtVaSetValues
    ( client->skkinput, XtNprobeWindow, probe_window, NULL ) ;

  /* Window を Pop Up する。Exclusive にすると、他の Popup が起きて  *
   * いる時にキーイベントがとれなくなってしまうので、Nonexclusive で *
   * あること。*/
  XtRealizeWidget( client->skkinput ) ;
  XFlush( XtDisplay( gw ) ) ;

  /* Event Capture Method の処理を行う。*/
  if( client->event_select == EVENT_HA_INPUTONLY || 
      client->event_select == EVENT_HA_FOCUS_KARA ){
    add_myeventhandler
      ( XtDisplay( gw ), probe_window,
        XtWindow( client->skkinput ), KeyPress, KeyPressMask ) ;
    add_myeventhandler
      ( XtDisplay( gw ), probe_window,
        XtWindow( client->skkinput ), KeyRelease, KeyReleaseMask ) ;
  }
  add_myeventhandler
    ( XtDisplay( gw ), client->req_window, XtWindow( gw ),
      NO_EVENT_HANDLE, StructureNotifyMask ) ;
  return True ;
}

/*
 * Kinput Protocol 上で変換要求者への文字列送信を行うための関数。
 *-----
 * 文字列送信は SkkInputWidget から呼出されるため、callbackの形になっ
 * ている。ここは修正の余地がいっぱいあるけど…まあ、取り敢えず動くよ
 * うになってから直しませうか。
 */
static void KIP_SendMessageToRequestorCallback
( Widget gw, caddr_t client, caddr_t caller )
{
  struct KinputClient *kclient = ( struct KinputClient * )client ;
  struct myChar *text = ( struct myChar * )caller ;

  Atom	proptype ;
  int	propsize ;
  int	propformat ;
  caddr_t propvalue ;
  struct myChar *wptr ;

  /*
   * EUC 漢字コードで持っているテキストを Compound Text へと変換する。
   */
  for( wptr = text ; !IS_END_OF_STRING( *wptr ) ; wptr ++ ){
    if( IS_ASCII_EQUAL( *wptr, '\r' ) )
      wptr->chara = '\n' ;
  }
  proptype   = kclient->target ;
  propformat = 8 ;
  propsize   = string2ctext( text, NULL ) ;
  if ( propsize <= 0 )
    return ;
  propvalue = ( caddr_t )malloc
    ( propsize * sizeof( unsigned char ) * 4 ) ;
  string2ctext( text, ( unsigned char * )propvalue ) ;
  
  /* Property に結果をセットする */
  XChangeProperty
    ( XtDisplay( gw ), kclient->req_window,
      kclient->property, proptype, propformat,
      PropModeAppend, propvalue, propsize ) ;
  free( propvalue ) ;
  return ;
}					

/*
 *
 */
static void KIP_RemoveKinputClient
( Widget gw, struct KinputClient *client )
{
  KinputWidget w = ( KinputWidget )gw ;
  struct KinputClient *node, *pNode ;
  int i ;

  /* 該当するノードが存在しないかリストを探索する。*/
  pNode = node = w->kinput.client_list ;
  while( node != NULL ){
    if( node == client )
      break ;
    pNode = node ;
    node  = node->next ;
  }
  /* フォントを解放する。*/
  for( i = 0 ; i < NUMBER_OF_CHARSET ; i ++ ){
    fontMgr_FreeFont( XtDisplay( gw ), client->fontset[ i ] ) ;
    client->fontset[ i ] = NULL ;
  }
  /* そもそもリストが無いということか、与えられたノードはおかしな場所 */
  /* を指していて役に立たない。*/
  if( node == NULL ){
    free( client ) ;
    return ;
  }
  if( pNode == node ){
    /* リストの先頭だった場合ですか。*/
    w->kinput.client_list = node->next ;
  } else {
    /* 前のノードの次のノードは今消そうとしてるノードの次のノード。*/
    pNode->next = node->next ;
  }
  free( node ) ;
  return ;
}

static int KIP_EndOfConversionMain( struct KinputClient *client )
{
  /* 利用されなくなったことを知らせる。*/
  client->probe        = False ;
  client->reserve      = False ;

  /* 終了通知を送る。ただし、親が DestroyNotify によって破壊されてい *
   * る場合においてはその限りではない。その場合には req_window ==    *
   * None となっている。*/
  if( client->req_window != None ){
    /* クライアントに終了通知を送る。*/
    KIP_SendEndOfConversion( client ) ;
  }
  /* フォーカスウィンドウのキーも見ている可能性があるので消しておく。*/
  if( client->focus_window != None ){
    remove_myeventhandler
      ( XtDisplay( client->protocol_widget ),
        client->focus_window, KeyPress, KeyPressMask ) ;
    remove_myeventhandler
      ( XtDisplay( client->protocol_widget ),
        client->focus_window, KeyRelease, KeyReleaseMask ) ;
  }
  /* InputOnly だった場合には、透明な窓が出来ている筈なので、それを */
  /* 消しておく。*/
  if( client->probe_window != None ){
    remove_allmyeventhandler
      ( XtDisplay( client->protocol_widget ),
        client->probe_window ) ;
    XSafeDestroyWindow
      ( XtDisplay( client->protocol_widget ), client->probe_window ) ;
    client->probe_window = None ;
  }
  /* 文字列を変換要求者へ送りつける場合のcallbackを破棄。*/
  XtRemoveAllCallbacks( client->skkinput, XtNfixNotify ) ;
  /* 変換要求者へ変換の終了を通知する場合のcallbackを破棄。*/
  XtRemoveAllCallbacks( client->skkinput, XtNendNotify ) ;
  /* キー入力をクライアントウィンドウへと送る callback を破棄。*/
  XtRemoveAllCallbacks( client->skkinput, XtNkeybackNotify ) ;

  /* 最初にキューをフラッシュしてしまう。*/
  XSafeFlush( XtDisplay( client->protocol_widget ) ) ;

  /* 処理されているクライアントリストから除く。*/
  KIP_RemoveKinputClient( client->protocol_widget, client ) ;
  return 0 ;
}

/*
 * Kinput Protocol 上での変換要求者への変換終了を伝達する関数。
 *-----
 * ここで注意することは、この Widget は KinputWidget ではないというこ
 * とである。従って、この gw を参照して convEndAtom を見るなんてことし
 * たらやられます。はまりました。(;_;)
 */
static void KIP_EndMessageToRequestorCallback
( Widget gw, caddr_t client, caddr_t caller )
{
  struct KinputClient *kclient = ( struct KinputClient * )client ;
  /* history 情報を記憶するべきか否か。*/
  if( caller != NULL )
    history_close( XtDisplay( kclient->protocol_widget ), caller ) ;
  KIP_EndOfConversionMain( ( struct KinputClient * )client ) ;
  return ;
}

/*
 * Kinput Protocol で変換終了要求に従ってクライアントとの接続を終了す
 * る関数。
 */
static void KIP_SendEndOfConversion( struct KinputClient *client )
{
  XEvent event ;
  KinputWidget w = ( KinputWidget )client->protocol_widget ;

  event.xclient.type          = ClientMessage ;
  event.xclient.window        = client->req_window ;
  event.xclient.message_type  = w->kinput.convEndAtom ;
  event.xclient.format        = 32 ;
  event.xclient.data.l[ 0 ]   = ( long )client->selection ;
  event.xclient.data.l[ 1 ]   = ( long )XtWindow
    ( client->protocol_widget ) ;
  event.xclient.data.l[ 2 ]   = 0L ;
  event.xclient.data.l[ 3 ]   = 0L ;
  event.xclient.data.l[ 4 ]   = 0L ;
  
  XSafeSendEvent
    ( XtDisplay( client->protocol_widget ), client->req_window,
     False, NoEventMask, &event ) ;
  return ;
}

/*
 * Kinput Protocl 上で変換要求を出したクライアントに変換要求の受理を通
 * 知する関数。
 */
static void KIP_AcceptConversion
( Widget gw, struct KinputClient *client )
{
  XEvent event ;
  KinputWidget w = ( KinputWidget )gw ;

  event.xclient.type         = ClientMessage ;
  event.xclient.window       = client->req_window ;
  event.xclient.message_type = w->kinput.convNotifyAtom ;
  event.xclient.format       = 32 ;
  event.xclient.data.l[ 0 ]  = ( long )client->selection ;
  event.xclient.data.l[ 1 ]  = ( long )client->target ;
  event.xclient.data.l[ 2 ]  = ( long )client->property ;
  event.xclient.data.l[ 3 ]  = ( long )XtWindow( client->skkinput ) ;
  event.xclient.data.l[ 4 ]  = 0L ;

  XSendEvent
    ( XtDisplay( gw ), client->req_window, False, NoEventMask, &event ) ;
  return ;
}

/*
 * Kinput Protocl 上で変換要求を出したクライアントに変換要求の受理を通
 * 知する関数。
 */
static void KIP_RejectConversion
( Widget gw, Window requestor, Atom conversion_atom )
{
  XEvent event ;
  KinputWidget w = ( KinputWidget )gw ;

  event.xclient.type          = ClientMessage ;
  event.xclient.window        = requestor ;
  event.xclient.message_type  = w->kinput.convNotifyAtom ;
  event.xclient.format        = 32 ;
  event.xclient.data.l[ 0 ]   = conversion_atom ;
  event.xclient.data.l[ 1 ]   = None ;
  event.xclient.data.l[ 2 ]   = 0L ;
  event.xclient.data.l[ 3 ]   = 0L ;
  event.xclient.data.l[ 4 ]   = 0L ;

  XSendEvent
    ( XtDisplay( gw ), requestor, False, NoEventMask, &event ) ;
  return ;
}

static struct KinputClient *KIP_PrepareNewClient
( Widget gw, Window req_window, Atom conversion_atom )
{
  KinputWidget w = ( KinputWidget )gw ;
  struct KinputClient *client ;
  int i ;

  client = ( struct KinputClient *)malloc
    ( sizeof( struct KinputClient ) ) ;
  if( client == NULL ){
    return NULL ;
  }
  client->probe = False ;
  client->reserve = False ;
  client->chat_adapter = False ;
  client->eggnl = False ;
  client->req_window = req_window ;
  client->target = client->property = client->selection = None ;
  client->probe_window = None ;
  client->focus_window = None ;
  client->protocol_widget = gw ;
  client->attribute_mask = 0L ;

  /* フォントセットの初期化を行う。*/
  for( i = 0 ; i < NUMBER_OF_CHARSET ; i ++ )
    client->fontset[ i ] = NULL ;

  /* リストにつないでおく。*/
  client->next = w->kinput.client_list ;
  w->kinput.client_list = client ;
  return client ;
}

/*
 * 要求してきた窓は kinput protocol で処理されているのでしょうか、とチェッ
 * クする関数。
 */
static struct KinputClient *KIP_FindKinputClient
( Widget gw, Window req_window )
{
  KinputWidget w = ( KinputWidget )gw ;
  struct KinputClient *client ;

  client = w->kinput.client_list ;
  while( client != NULL ){
    if( client->req_window == req_window )
      return client ;
    client = client->next ;
  }
  return client ;
}

/*
 * 変換終了メッセージを受けた時の処理を行う関数。
 */
static void KIP_ConversionEndRequestEventHandler
( Widget gw, XEvent *xevent, String *params, Cardinal *num_params )
{
  XClientMessageEvent *cev = &( xevent->xclient ) ;
  struct KinputClient *client ;

  /* 不正なメッセージは丁重に無視して差し上げる。*/
  if( !KIP_CorrectClientMessageP( gw, xevent ) )
    return ;

  client = KIP_FindKinputClient( gw, ( Window )cev->data.l[ 1 ] ) ;
  if( client == NULL ){
    /* 知らない人からものを貰っちゃいけないので、丁重に無視して差し *
     * 上げませう。*/
    return ;
  }
  /* 変換窓を閉じて全てを終了させておく。*/
  XtDestroyWidget( client->skkinput ) ;
  return ;
}

/*
 * 変換クライアントから属性の変更通知が来た場合の処理を行う関数。
 */
static void KIP_ConversionAttributeNotifyEventHandler
( Widget gw, XEvent *xevent, String *params, Cardinal *num_params )
{
  XClientMessageEvent *xcev = &( xevent->xclient ) ;
  struct KinputClient *client ;

  /* 不正なメッセージは丁重に無視して差し上げる。*/
  if( !KIP_CorrectClientMessageP( gw, xevent ) )
    return ;

  client = KIP_FindKinputClient( gw, ( Window )xcev->data.l[ 1 ] ) ;
  if( client == NULL ){
    /* 知らない人からものを貰っちゃいけないので、丁重に無視して差し *
     * 上げませう。*/
    return ;
  }
  /* Kinput2 Protocol 以外でこのような要求を出してくる人がいたら、そ *
   * れはダミーなので無視します。*/
  if( client->protocol != PROTOCOL_TYPE_KINPUT2 || !client->probe )
    return ;
  /* 落とすタイミングによっては、ここに来た中で Window が落ちるから、
     その余波で XErrorEvent が発生し skkinput が落ちるのを避ける。*/
  KIP_GetAttributeFromEvent( client, xcev ) ;
  return ;
}

/*
 * 他の変換サーバが selection の要求をしてきた場合の処理を行う関数。
 */
static void KIP_SelectionRequestEventHandler
( Widget gw, XEvent *xevent, String *params, Cardinal *num_params )
{
  XSelectionRequestEvent *xsrev = &( xevent->xselectionrequest ) ;
  XEvent xev ;

  xev.xselection.type      = SelectionNotify ;
  xev.xselection.requestor = xsrev->requestor ;
  xev.xselection.selection = xsrev->selection ;
  xev.xselection.target    = xsrev->target ;
  xev.xselection.property  = None ;
  xev.xselection.time      = xsrev->time ;

  XSendEvent
    ( xsrev->display, xsrev->requestor, False, NoEventMask, &xev ) ;
  return ;
}

/*
 * 変換サーバたる権限を失った時の処理を行う関数。
 */
static void KIP_SelectionClearEventHandler
( Widget gw, XEvent *xevent, String *params, Cardinal *num_params )
{
  /* 自分自身を破壊して終了する。この時、自分自身が持っていたコール  * 
   * バックを全部破棄しなければならないけど…？ */
#ifdef DEBUG
  fprintf( stderr, "(Kinput2)Selection Lost....\n" ) ;
#endif
  XtDestroyWidget( gw ) ;
  return ;
}

/*
 * ここで XtSetValues をして変換クライアントに変更された部分を指示する
 * べきであると私は思う。
 */
static struct ConvAttrsMesg KIP_ReadAttribute
( struct KinputClient *client, unsigned long *attributes, 
  struct proprec *precp, int fromevent, int is_widget_ok )
{
	int attribute_code   = CODE_OF_ATTRIBUTE  ( *attributes ) ;
	int attribute_length = LENGTH_OF_ATTRIBUTE( *attributes ) ;
	struct ConvAttrsMesg camsg ;
	
	camsg.mask = 0L ;
#ifdef DEBUG
	fprintf( stderr, "Attribute Code : %d\n", attribute_code ) ;
#endif
	attributes ++ ;
	switch( attribute_code ){
		/* 属性変更は実はなされていなかった。*/
	case CONVERSION_ATTRIBUTE_NASHI :
		break ;
	case CONVERSION_ATTRIBUTE_HA_INDIRECT :
		if( attribute_length != 1 )
			break ;
		/* 変更された属性データがこれより大きいとか、変更された属性データ *
		 * が複数あるとき (この場合にはこのイベントを複数回送ってもいいの *
		 * だが) には属性データはプロパティに入れ、Indirect Attribute の機*
		 * 能を使ってそのプロパティを指定すればよい。*/
		KIP_GetAttributeFromPropertySub
			( client, attributes[ 0 ], precp, fromevent, is_widget_ok ) ;
		break ;
	case CONVERSION_ATTRIBUTE_FOCUS_WINDOW :
		if( attribute_length != 1 )
			break ;
		camsg.mask |= CAFocusWindow ;
		camsg.value.focus_window = attributes[ 0 ] ;
		client->focus_window = camsg.value.focus_window ;
		break ;
	case CONVERSION_ATTRIBUTE_BASHO_SHITEI :
		if( attribute_length != 1 )
			break ;
		camsg.mask |= CASpotLocation ;
		camsg.value.spot_x = USHORT_UPPER16BIT( attributes[ 0 ] ) ;
		camsg.value.spot_y = USHORT_LOWER16BIT( attributes[ 0 ] ) ;
#ifdef DEBUG
		fprintf
			( stderr, "Basho Shitei : (x,y) = (%d, %d)\n", 
			  camsg.value.spot_x, camsg.value.spot_y ) ;
#endif
		break ;
	case CONVERSION_ATTRIBUTE_CLIENT_AREA :
		if( attribute_length != 2 )
			break ;
		camsg.mask |= CAClientArea;
		camsg.value.client_area.x = 
			( unsigned short )ULONG_UPPER16BIT( attributes[ 0 ] ) ;
		camsg.value.client_area.y =
			( unsigned short )ULONG_LOWER16BIT( attributes[ 0 ] ) ;
		camsg.value.client_area.width  = ULONG_UPPER16BIT( attributes[ 1 ] ) ;
		camsg.value.client_area.height = ULONG_LOWER16BIT( attributes[ 1 ] ) ;
#ifdef DEBUG
		fprintf
			( stderr, "Client no ichi to ookisa : (x,y) = (%d, %d), %d x %d\n", 
			  camsg.value.client_area.x, camsg.value.client_area.y,
			  camsg.value.client_area.width, camsg.value.client_area.height ) ;
#endif
		break ;
	case CONVERSION_ATTRIBUTE_NYUURYOKU_HOUSHIKI :
		if( fromevent )
			break ;
		if( attribute_length != 1 )
			break ;
		if( attributes[ 0 ] ==
			CONVERSION_NYUURYOKU_HOUSHIKI_OVERTHESPOT ){
			client->window_style = WINDOW_TYPE_OVERTHESPOT ;
		} else if( attributes[ 0 ] ==
				   CONVERSION_NYUURYOKU_HOUSHIKI_OFFTHESPOT ){
			client->window_style = WINDOW_TYPE_SEPARATE ;
		} else {
			client->window_style = WINDOW_TYPE_SEPARATE ;
		}
		break ;
	case CONVERSION_ATTRIBUTE_EVENT_NO_TOBASHIKATA :
		if( fromevent )
			break ;
		if( attribute_length != 1 )
			break ;
		switch( attributes[ 0 ] ){
		case CONVERSION_EVENT_TOBASHI_NASHI :
			client->event_select = EVENT_NO_SHORINASHI ;
			break ;
		case CONVERSION_EVENT_TOBASHI_FOCUS_NO_HOUHE :
			client->event_select = EVENT_HA_FOCUS_KARA ;
			break ;
		case CONVERSION_EVENT_TOBASHI_ZENBU_SKKINPUT_HE :
		default :
			client->event_select = EVENT_HA_INPUTONLY ;
			break ;
		}
		break ;
		/* 変換クライアントの利用しているカラーマップ。*/
	case CONVERSION_ATTRIBUTE_COLORMAP :
		if( attribute_length != 1 )
			break ;
		camsg.mask |= CAColormap ;
		camsg.value.colormap = attributes[ 0 ] ;
		break ;
		/* 変換クライアントの fore/background カラーの情報。*/
	case CONVERSION_ATTRIBUTE_COLOR :
		if( attribute_length != 2 )
			break ;
		camsg.mask |= (CAForegroundPixel | CABackgroundPixel) ;
		camsg.value.foreground = attributes [0] ;
		camsg.value.background = attributes [1] ;
		break ;
		/* 変換クライアントのフォントの情報。*/
	case CONVERSION_ATTRIBUTE_FONT_ATOMS :
		/* 設定すべきものの実体が存在しない場合には抜ける。*/
		if( attribute_length < 1 )
			break ;
		fontMgr_PrepareFontByAtom
			( XtDisplay( client->protocol_widget ), client->fontset,
			  ( Atom *)attributes, ( Cardinal )attribute_length ) ;
		camsg.mask |= CAFonts ;
		camsg.value.fontset = client->fontset ;
		break ;
		/* off-the-spot 及び over-the-spot で用いられることになるステータ
		   ス領域の指定…なのだが、over-the-spot では実は使ってなかったり
		   する。(^^;;; */
	case CONVERSION_ATTRIBUTE_STATUS_AREA :
		/* 設定すべきものの実体が存在しない場合には抜ける。*/
		if( attribute_length != 2 )
			break ;
		camsg.mask |= CAStatusArea;
		camsg.value.status_area.x = USHORT_UPPER16BIT( attributes[ 0 ] ) ;
		camsg.value.status_area.y = USHORT_LOWER16BIT( attributes[ 0 ] ) ;
		camsg.value.status_area.width  = ULONG_UPPER16BIT( attributes[ 1 ] ) ;
		camsg.value.status_area.height = ULONG_LOWER16BIT( attributes[ 1 ] ) ;
		break ;
		/* フォントの高さを指定するものだと思うのだが、フォントが変更され
		   ても修正される気配が無いので、実は無視されてしまうパラメータに
		   なっている。*/
	case CONVERSION_ATTRIBUTE_LINE_SPACING :
		/* 設定すべきものの実体が存在しない場合には抜ける。*/
		if( attribute_length < 1 )
			break ;
		camsg.mask |= CALineSpacing  ;
		camsg.value.linespacing = attributes[ 0 ] ;
		break ;
		/* ここから下のアトリビュートは必要な局面が今一つ想像できないので、 */
		/* 実装は先の先に送ることにする。*/
	case CONVERSION_ATTRIBUTE_BACKGROUND_PIXMAP :
	case CONVERSION_ATTRIBUTE_CURSOR :
	default :
		break ;
	}
	return camsg ;
}

/*
 * ここで XtSetValues をして変換クライアントに変更された部分を指示する
 * べきであると私は思う。
 */
static int KIP_SetAttribute
( struct KinputClient *client, unsigned long *attributes, 
  struct proprec *precp, int fromevent, int really_set_p )
{
  KinputWidget w = ( KinputWidget )client->protocol_widget ;
  struct ConvAttrsMesg camsg ;
  Arg arg[ 2 ] ;

  camsg = KIP_ReadAttribute
    ( client, attributes, precp, fromevent, really_set_p ) ;
  /* 何か設定された場合の処理を行う。*/
  if( camsg.mask && really_set_p ){
    w->kinput.camsg = camsg ;
    XtSetArg( arg[ 0 ], XtNconversionAttribute, &w->kinput.camsg ) ;
    XtSetValues( client->skkinput, arg, 1 ) ;
  }
  return True ;
}

static int KIP_GetAttributeFromPropertySub2
( struct KinputClient *client, Atom property, unsigned long **attributes )
{
  KinputWidget w = ( KinputWidget )client->protocol_widget ;
  Atom type ;
  int format ;
  unsigned long nums, bytesafter ;

  /*
   * あああ、kinput2 そのままに見える…。でも、ここでエラー出されたら
   * 対処しようが無いんですよねえ。どうするのかな？
   */
  XSafeGetWindowProperty
    ( XtDisplay( client->protocol_widget ), client->req_window, property,
      0L, 1000L, False, w->kinput.convAttrAtom, 
      &type, &format, &nums, &bytesafter,
      ( unsigned char **)attributes ) ;

  /* 正しく登録されていたかを見ている。*/
  if( format != 32 || type != w->kinput.convAttrAtom ){
    /* free で良いのだろうか？ XFree じゃないと駄目な気がするが…。*/
    if( *attributes != NULL )
      XFree( ( char *)*attributes ) ;
    return 0 ;
  }
  return nums ;
}

static int KIP_GetAttributeFromPropertySub
( struct KinputClient *client,   Atom property,
  struct proprec *prevprop, int fromevent, int really_set_p )
{
  unsigned long *attributes, *attr_ptr ;
  int total_length_of_attributes ;
  struct proprec node ;

  node.prop = property ;
  node.prev = prevprop ;
  while( prevprop != NULL ){
    if( prevprop->prop == property ){
      return False ;
    }
    prevprop = prevprop->prev ;
  }

  attributes = NULL ;
  if( ( total_length_of_attributes = 
	KIP_GetAttributeFromPropertySub2
	( client, property, &attributes ) ) <= 0 )
    return False ;

  attr_ptr = attributes ;
  while( total_length_of_attributes > 0 ){
    int length_of_attributes =
      LENGTH_OF_ATTRIBUTE( attr_ptr[ 0 ] ) + 1 ;
    if( length_of_attributes > total_length_of_attributes )
      break ;
    KIP_SetAttribute( client, attr_ptr, &node, fromevent, really_set_p ) ;
    total_length_of_attributes -= length_of_attributes ;
    attr_ptr += length_of_attributes ;
  }
  /* XGetWindowProperty の結果なので XFree する。free ではない。*/
  XFree( attributes ) ;

  return True ;
}

/*
 * Property によって設定されている変換クライアントの属性を取り出す関数。
 *-----
 * ううむ。メッセージは XtSetValues で送りたいのだが不幸にして相手が存
 * 在しない場合というのがあり得る。どうしようか…。これは一番最初に 
 * Widget が起きる前に呼ばれると…どうしようもないのである。一回目は無
 * 視して２回目からは大丈夫とか無茶でもしましょうか？
 */
static int KIP_GetAttributeFromProperty
( struct KinputClient *client, Atom property, int really_set_p )
{
  KIP_GetAttributeFromPropertySub
    ( client, property, ( struct proprec *)NULL, False, really_set_p ) ;
  return 0 ;
}

static int KIP_GetAttributeFromEvent
( struct KinputClient *client, XClientMessageEvent *xcev )
{
  unsigned long *data ;
  int            length_of_attribute ;

  /* イベントのうしろに付いている属性を抜き出す。*/
  data = ( unsigned long * )( &( xcev->data.l[ 2 ] ) ) ;
  length_of_attribute = LENGTH_OF_ATTRIBUTE( data[ 0 ] ) ;
  /* どうやら一度に３つ以上設定するとプロトコル違反らしい。*/
  if( length_of_attribute >= 3 )
    return False ;
  /* 属性の設定を行うらしい。*/
  KIP_SetAttribute( client, data, NULL, True, True ) ;
  return True ;
}

/*
 * キーを押したというイベントを別の窓へと転送するのに利用される関数。
 */
static void KIP_KeyEventToRequestorCallback
( Widget gw, caddr_t client, caddr_t caller )
{
  struct KinputClient *kclient = ( struct KinputClient *)client ;
  XEvent *xevent = ( XEvent *)caller ;

  /* フォーカスされている窓へとメッセージを送る。*/
#if 0
  xevent->xkey.window    = kclient->focus_window ;
#else
  xevent->xkey.window    = kclient->req_window ;
#endif
  xevent->xkey.subwindow = None ;

  XSendEvent
    ( XtDisplay( gw ), xevent->xkey.window,
      False, NoEventMask, xevent ) ; 
  return ;
}
