/* # skkinput (Simple Kana-Kanji Input)
 * Ximp.c --- Ximp 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 "XimpP.h"
#include "SeparateWin.h"
#include "OverWin.h"
#include "OffWin.h"
#include "MyDispatch.h"
#include "MyError.h"
#include "resrcs.h"
#include "HistMgr.h"
#include "skkkey.h"
#include "FontMgr.h"

#define PROTOCOL_VERSION_STRING	"XIMP.3.5"
#define SERVER_NAME		"skkinput"
#define SERVER_VERSION		"1"
#define VENDOR_NAME		"KUIS"

#define MIN_LINE_SPACING	(2)
#define MIN_AREA_WIDTH		(16)
#define MIN_AREA_HEIGHT		(10)

/*
 * プロトタイプ宣言。
 */
static void Ximp_Initialize 
( Widget greq, Widget gnew, ArgList args, Cardinal *num_args ) ;
static void Ximp_Realize
( Widget gw, XtValueMask *valueMask, XSetWindowAttributes *attrs ) ;
static Boolean Ximp_SetValues
( Widget current, Widget request, Widget new,
  ArgList args, Cardinal *num_args ) ;
static void Ximp_Destroy( Widget gw ) ;

static void Ximp_getAtoms( Widget gw ) ;

static void Ximp_setServerProperty( Widget gw ) ;
static int Ximp_ownSelection( Widget gw ) ;
static int Ximp_setKeyProperty( Widget gw ) ;

static int Ximp_isCorrectConversionMessage
( Widget gw, XEvent *xevent ) ;

static void Ximp_conversionMessageEventHandler
( Widget gw, XEvent *xevent, String *args, Cardinal *num_of_args ) ;
static void Ximp_createMessageHandler
( Widget gw, XClientMessageEvent *xclme ) ;
static void Ximp_beginMessageHandler
( Widget gw, XClientMessageEvent *xclme ) ;
static void Ximp_endMessageHandler
( Widget gw, XClientMessageEvent *xclme ) ;
static void Ximp_setFocusMessageHandler
( Widget gw, XClientMessageEvent *xclme ) ;
static void Ximp_unsetFocusMessageHandler
( Widget gw, XClientMessageEvent *xclme ) ;
static void Ximp_moveMessageHandler
( Widget gw, XClientMessageEvent *xclme ) ;
static void Ximp_resetMessageHandler
( Widget gw, XClientMessageEvent *xclme ) ;
static void Ximp_changeMessageHandler
( Widget gw, XClientMessageEvent *xclme ) ;
static void Ximp_getValueMessageHandler
( Widget gw, XClientMessageEvent *xclme ) ;
static void Ximp_extensionMessageHandler
( Widget gw, XClientMessageEvent *xclme ) ;
static void Ximp_keypressMessageHandler
( Widget gw, XClientMessageEvent *xclme ) ;
static void Ximp_setvalueMessageHandler
( Widget gw, XClientMessageEvent *xclme ) ;
static void Ximp_SelectionRequestEventHandler
( Widget gw, XEvent *xevent, String *params, Cardinal *num_params ) ;
static void Ximp_SelectionClearEventHandler
( Widget gw, XEvent *xevent, String *params, Cardinal *num_params ) ;

static void Ximp_rejectCreateMessage( Widget gw, Window win ) ;
static void Ximp_sendErrorEvent
( struct XimpClient *ximpClient, int errorno ) ;
#if 0
static int Ximp_sendClientMessage8
( struct XimpClient *ximpClient, unsigned char *string, int length ) ;
#endif
static int Ximp_sendClientMessage32
( struct XimpClient *ximpClient, int type,
  unsigned long l1, unsigned long l2,
  unsigned long l3, unsigned long l4 ) ;

static void Ximp_getAttributes
( struct XimpClient *ximpClient, unsigned long mask ) ;
static int Ximp_getClientVersionProperty
( struct XimpClient *ximpClient ) ;

static struct XimpClient *Ximp_createClient
( Widget gw, Window requestor, XimpInputStyle *xis ) ;
static struct XimpClient *Ximp_findClient
( Widget gw, unsigned long id ) ;
#if 0
static struct XimpClient *Ximp_findClientWithWindowID
( Widget gw, Window win ) ;
#endif
static void Ximp_destroyClient( struct XimpClient *ximpClient ) ;

static void Ximp_getFonts
( struct XimpClient *ximpClient, unsigned char *fontnamelist ) ;

static void Ximp_fillInDefaultAttributes
( struct XimpClient *ximpClient ) ;
static unsigned long Ximp_makeConvAttributes
( struct XimpClient *ximpClient, struct ConvAttrs *attr ) ;

static void Ximp_SendMessageToRequestorCallback
( Widget gw, caddr_t client, caddr_t caller ) ;
static void Ximp_EndMessageToRequestorCallback
( Widget gw, caddr_t client, caddr_t caller ) ;
static void Ximp_KeyEventToRequestorCallback
( Widget gw, caddr_t client, caddr_t caller ) ;

static int Ximp_getProperty
( struct XimpClient *ximpClient, Atom property, Atom type, int format,
  unsigned char **rprop, unsigned long *rnitems ) ;
static void Ximp_getFocusProperty
( struct XimpClient *ximpClient ) ;
static void Ximp_getPreeditFontProperty
( struct XimpClient *ximpClient ) ;
static void Ximp_getStatusFontProperty
( struct XimpClient *ximpClient ) ;
static void Ximp_getPreeditProperty
( struct XimpClient *ximpClient, unsigned long mask ) ;
static void Ximp_getStatusProperty
( struct XimpClient *ximpClient, unsigned long mask ) ;

static void Ximp_setAttributes
( struct XimpClient *ximpClient, unsigned long mask ) ;
static void Ximp_setFocusProperty
( struct XimpClient *ximpClient ) ;
static void Ximp_setPreeditFontProperty
( struct XimpClient *ximpClient ) ;
static void Ximp_setStatusFontProperty
( struct XimpClient *ximpClient ) ;
static void Ximp_setPreeditProperty
( struct XimpClient *ximpClient, unsigned long mask ) ;
static void Ximp_setStatusProperty
( struct XimpClient *ximpClient, unsigned long mask ) ;
static void Ximp_setProperty
( struct XimpClient *ximpClient, Atom property, Atom type, int format, 
  unsigned char *data, int nelements ) ;
static void Ximp_unlinkClient( struct XimpClient *ximpClient ) ;
static void Ximp_freeClient( struct XimpClient *ximpClient ) ;

static void Ximp_ChangeFocusWindow
( struct XimpClient *ximpClient, Window focus_window ) ;

/*
 * 外部参照している関数のプロトタイプ宣言。
 */
/* ctext.c */
extern int string2ctext( struct myChar *string, unsigned char *cstr ) ;

/* parseStr.c */
extern int parseXrLikeKeyStrings
( unsigned char *string, struct XrLikeKey *keytbl ) ;

/*
 * グローバル変数の宣言。
 */
#define offset(field)  XtOffsetOf(XimpRec, ximp.field)
#define goffset(field) XtOffsetOf(WidgetRec, core.field)

/* リソース一覧。*/
static XtResource ximp_resources[] = {
  /* これは kinput protocol を通すかどうか。*/
  { XtNdefaultXimpServer, XtCDefaultXimpServer, XtRBoolean,
    sizeof( Boolean ), offset(ximp_defaultServer),
    XtRImmediate, (XtPointer)FALSE },
  /* SeparateWindow, Over-The-Spot-Window …を初期化するコールバック。*
   * これは XimpWidget がどう初期化したら良いのか知らないことなので、 *
   * コールバックの形で親に初期化をお願いしている。*/
  { XtNsetupInputWindowNotify, XtCCallback, XtRCallback,
    sizeof( caddr_t ), offset( setupInputWindowCallback ), XtRCallback,
    ( caddr_t )NULL },
  /* Ximp 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 },
  /* 変換を開始するキーを登録する。*/
  { XtNconversionStartKey, XtCConversionStartKey, XtRString,
    sizeof( String ), offset( conversionStartKey ), XtRImmediate, 
    ( XtPointer )"Shift<Key>space,Cntrl<Key>Kanji,Cntrl<Key>Henkan_Mode" },
} ;
#undef offset
#undef goffset

static XimpInputStyle ximpInputStyles[] = {
  { XIMPreeditPosition | XIMStatusArea, 
    WINDOW_TYPE_OVERTHESPOT, False },
  { XIMPreeditPosition | XIMStatusNothing, 
    WINDOW_TYPE_OVERTHESPOT, False },
  { XIMPreeditArea     | XIMStatusArea,
    WINDOW_TYPE_OFFTHESPOT, False },
  { XIMPreeditNothing  | XIMStatusNothing,
    WINDOW_TYPE_SEPARATE, True },
  { 0, 0, False },
} ;
#define XIMP_INPUT_STYLES_NUM	(5)

/* イベントに対するアクションのテーブル。*/
static XtActionsRec ximp_actions_table[] = {
  /* 変換開始終了その他属性の変更の要求を受ける。*/
  { "ximp-message",
    Ximp_conversionMessageEventHandler },
  /* _JAPANESE_CONVERSION が欲しいと言われた時の処理を行う。*/
  { "ximp-selection-request",
    Ximp_SelectionRequestEventHandler },
  /* _JAPANESE_CONVERSION を失った時の処理を行う。*/
  { "ximp-selection-clear",
    Ximp_SelectionClearEventHandler },
};

static char default_ximp_translations[] =
"<Message>_XIMP_PROTOCOL:              ximp-message()\n\
 <SelReq>:                             ximp-selection-request()\n\
 <SelClr>:                             ximp-selection-clear()";

XimpClassRec ximpClassRec = {
    { /* core fields */
    /* superclass		*/	&widgetClassRec,
    /* class_name		*/	"Ximp",
    /* size			*/	sizeof( XimpRec ),
    /* class_initialize		*/	NULL,
    /* class_part_initialize	*/	NULL,
    /* class_inited		*/	FALSE,
    /* initialize		*/	Ximp_Initialize,
    /* initialize_hook		*/	NULL,
    /* realize			*/	Ximp_Realize,
    /* actions			*/	ximp_actions_table,
    /* num_actions		*/	XtNumber( ximp_actions_table ),
    /* resources		*/	ximp_resources,
    /* num_resources		*/	XtNumber( ximp_resources ),
    /* xrm_class		*/	NULLQUARK,
    /* compress_motion		*/	TRUE,
    /* compress_exposure	*/	TRUE,
    /* compress_enterleave	*/	TRUE,
    /* visible_interest		*/	FALSE,
    /* destroy			*/	Ximp_Destroy,
    /* resize			*/	NULL,
    /* expose			*/	NULL,
    /* set_values		*/	Ximp_SetValues,
    /* set_values_hook		*/	NULL,
    /* set_values_almost	*/	XtInheritSetValuesAlmost,
    /* get_values_hook		*/	NULL,
    /* accept_focus		*/	NULL,
    /* version			*/	XtVersion,
    /* callback_private		*/	NULL,
    /* tm_table			*/	default_ximp_translations,
    /* query_geometry		*/	XtInheritQueryGeometry,
    }
} ;

WidgetClass ximpWidgetClass = ( WidgetClass )&ximpClassRec ;

/*
 * KinputWidgetClass の初期化関数。
 */
static void Ximp_Initialize 
( Widget greq, Widget gnew, ArgList args, Cardinal *num_args )
{
  XimpWidget w = ( XimpWidget )gnew ;

  w->ximp.localename  = "ja_JP" ;
  w->ximp.servername  = SERVER_NAME ;
  w->ximp.icid        = 1L ;
  w->ximp.property_id = 0L ;
  /* 処理されているクライアントは何もない。*/
  w->ximp.client_list = NULL ;
  w->ximp.status_width = 0 ;
  /* 変換に利用するアトムを作成しておく。*/
  Ximp_getAtoms( gnew ) ;
  return ;
}

static void Ximp_getAtoms( Widget gw )
{
  XimpWidget w = ( XimpWidget )gw ;
  Display *disp = XtDisplay( gw ) ;
  unsigned char buffer[ BUFSIZE ] ;

#define MAKE_ATOM(str)	XInternAtom(disp,(str),False)

  sprintf( buffer, "_XIMP_%s", w->ximp.localename ) ;
  w->ximp.ximp_selection1 = MAKE_ATOM( buffer ) ;
  sprintf
    ( buffer, "_XIMP_%s@%s.%d",
      w->ximp.localename, w->ximp.servername, 
      DefaultScreen( XtDisplay( gw ) ) ) ;
  w->ximp.ximp_selection2 = MAKE_ATOM( buffer ) ;
  w->ximp.compound_text      = MAKE_ATOM( "COMPOUND_TEXT" ) ;
  w->ximp.ximp_version       = MAKE_ATOM( "_XIMP_VERSION" ) ;
  w->ximp.ximp_inputstyle    = MAKE_ATOM( "_XIMP_STYLE" ) ;
  w->ximp.ximp_keys          = MAKE_ATOM( "_XIMP_KEYS" ) ;
  w->ximp.ximp_servername    = MAKE_ATOM( "_XIMP_SERVERNAME" ) ;
  w->ximp.ximp_serverversion = MAKE_ATOM( "_XIMP_SERVERVERSION" ) ;
  w->ximp.ximp_vendorname    = MAKE_ATOM( "_XIMP_VENDORNAME" ) ;
  w->ximp.ximp_extensions    = MAKE_ATOM( "_XIMP_EXTENSIONS" ) ;
  w->ximp.ximp_protocol      = MAKE_ATOM( "_XIMP_PROTOCOL" ) ;
  w->ximp.ximp_focus         = MAKE_ATOM( "_XIMP_FOCUS" ) ;
  w->ximp.ximp_preedit       = MAKE_ATOM( "_XIMP_PREEDIT" ) ;
  w->ximp.ximp_status        = MAKE_ATOM( "_XIMP_STATUS" ) ;
  w->ximp.ximp_preeditfont   = MAKE_ATOM( "_XIMP_PREEDITFONT" ) ;
  w->ximp.ximp_statusfont    = MAKE_ATOM( "_XIMP_STATUSFONT" ) ;
  w->ximp.ximp_extbackfront  = MAKE_ATOM( "_XIMP_EXT_XIMP_BACK_FRONT" ) ;
#undef MAKE_ATOM
  return ;
}

static void Ximp_Realize
( Widget gw, XtValueMask *valueMask, XSetWindowAttributes *attrs )
{
  CoreWidgetClass super =
    ( CoreWidgetClass )XtClass( gw )->core_class.superclass ;

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

  /* サーバとしての特性をセットしておく。*/
  Ximp_setServerProperty( gw ) ;
  /* サーバとなるべくセレクションのオーナになろうとする。*/
  if( !Ximp_ownSelection( gw ) ){
    XtDestroyWidget( gw ) ;
    return ;
  }
#ifdef DEBUG
  printf( "Ximp Server Window(%ld)\n", XtWindow( gw ) ) ;
#endif
  return ;
}

static void Ximp_setServerPropertySub
( Widget gw, Atom property, Atom type, int format, 
  unsigned char *data, int nelements )
{
  XChangeProperty
    ( XtDisplay( gw ), XtWindow( gw ), property, type, format,
      PropModeReplace, data, nelements ) ;
  return ;
}

/*
 * サーバとして何が可能か、どのようなサーバであるかを登録する関数。
 *-----
 * まず、XIMP サーバであるということを登録する。
 * 次にサーバのバージョンとかどのような入力スタイルをサポートしている
 * かとかを登録するわけである。
 */
static void Ximp_setServerProperty( Widget gw )
{
  XimpWidget w  = ( XimpWidget )gw ;
  XimpInputStyle *xinps = ximpInputStyles ;
  unsigned long styles[ XIMP_INPUT_STYLES_NUM ] ;
  unsigned long extensions[ 5 ] ;
  int number_of_styles, number_of_extensions ;

  Ximp_setServerPropertySub
    ( gw, w->ximp.ximp_version, XA_STRING, 8, 
      PROTOCOL_VERSION_STRING, strlen( PROTOCOL_VERSION_STRING ) ) ;
  Ximp_setServerPropertySub
    ( gw, w->ximp.ximp_servername, XA_STRING, 8,
      SERVER_NAME, strlen( SERVER_NAME ) ) ;
  Ximp_setServerPropertySub
    ( gw, w->ximp.ximp_serverversion, XA_STRING, 8,
      SERVER_VERSION, strlen( SERVER_VERSION ) ) ;
  Ximp_setServerPropertySub
    ( gw, w->ximp.ximp_vendorname, XA_STRING, 8, 
      VENDOR_NAME, strlen( VENDOR_NAME ) ) ;
  number_of_styles = 0 ;
  while( xinps->style != 0 ){
    styles[ number_of_styles ++ ] = xinps->style ;
    xinps ++ ;
  }
  Ximp_setServerPropertySub
    ( gw, w->ximp.ximp_inputstyle, w->ximp.ximp_inputstyle, 32, 
      ( unsigned char *)styles, number_of_styles ) ;

  number_of_extensions = 0 ;
  extensions[ number_of_extensions ++ ] = w->ximp.ximp_extbackfront ;
  Ximp_setServerPropertySub
    ( gw, w->ximp.ximp_extensions, w->ximp.ximp_extensions, 32, 
      ( unsigned char *)extensions, number_of_extensions ) ;
  Ximp_setKeyProperty( gw ) ;
  return ;
}

/*
 * 変換開始キーの登録を行う関数。
 *-----
 * 今は手を抜いているので、変換開始のキーはシフトスペースの固定である。
 */
static int Ximp_setKeyProperty( Widget gw )
{
  XimpWidget w = ( XimpWidget )gw ;
  unsigned long *keydata ;
  int num, i ;
  struct XrLikeKey *startkeys ;

  /* まず、何個用意しないといけないのかを数える。*/
  num = parseXrLikeKeyStrings( w->ximp.conversionStartKey, NULL ) ;
  if( num <= 0 )
    return False ;
  /* メモリを確保する。*/
  if( ( startkeys = malloc( sizeof( struct XrLikeKey ) * num ) ) == NULL )
    return False ;
  if( ( keydata = malloc( sizeof( unsigned long ) * 3 * num ) ) == NULL ){
    free( startkeys ) ;
    return False ;
  }
  /* 今度は実際にキーを読み、確保する。*/
  num = parseXrLikeKeyStrings( w->ximp.conversionStartKey, startkeys ) ;
  /* キーデータを登録していく…。*/
  for( i = 0 ; i < num ; i ++ ){
    keydata[ i * 3 + 0 ] = startkeys[ i ].checkModifiers ;
    keydata[ i * 3 + 1 ] = startkeys[ i ].modifiers ;
    keydata[ i * 3 + 2 ] = startkeys[ i ].keysym ;
  }
  /* property に登録する。*/
  XChangeProperty
    ( XtDisplay( gw ), XtWindow( gw ), 
      w->ximp.ximp_keys, w->ximp.ximp_keys, 32,
      PropModeReplace, ( unsigned char *)keydata, num * 3 ) ;
  /* 一時的に利用していたメモリを解放する。*/
  free( startkeys ) ;
  free( keydata ) ;
  return True ;
}

/*
 * 変換サーバたる名乗りを上げる関数。
 *-----
 * 一般に X Window System のサーバは…
 *
 * 「あるアトムを作成し、その持ち主になる」ということから始める。
 *
 * クライアントはそのアトムの持ち主が誰かを調べることによってサーバの
 * 存在を知ることができるのである。
 */
static int Ximp_ownSelection( Widget gw )
{
  XimpWidget w = ( XimpWidget )gw ;
  Display *disp = XtDisplay( gw ) ;

  if( w->ximp.ximp_defaultServer ||
      XGetSelectionOwner
      ( disp, w->ximp.ximp_selection1 ) == None ){
      XSetSelectionOwner
	( disp, w->ximp.ximp_selection1, XtWindow( gw ), CurrentTime ) ;
  }
  /* Selection2 の owner になる。こちらは言語毎に指定される Server で
     ある。*/
  XSetSelectionOwner
    ( disp, w->ximp.ximp_selection2, XtWindow( gw ), CurrentTime ) ;
  /* 無事 selection の owner になれたかどうかを返す。*/
  return 
    ( XGetSelectionOwner
      ( disp, w->ximp.ximp_selection2 ) == XtWindow( gw ) ) ;
}

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

  /* まさか誰かクライアントが落とされようとしている？！ */
  if( reqw->ximp.destroyWindowEvent != NULL ){
    XDestroyWindowEvent *xdwe     = reqw->ximp.destroyWindowEvent ;
    ximpClient = reqw->ximp.client_list ;
    while( ximpClient != NULL ){
      if( xdwe->window == ximpClient->request_window ||
	  xdwe->window == ximpClient->focus_window ){
	remove_allmyeventhandler
	  ( XtDisplay( new ), ximpClient->request_window ) ;
	XSafeSelectInput
	  ( XtDisplay( new ), ximpClient->request_window, NoEventMask ) ;
	remove_allmyeventhandler
	  ( XtDisplay( new ), ximpClient->focus_window ) ;
	XSafeSelectInput
	  ( XtDisplay( new ), ximpClient->focus_window, NoEventMask ) ;
	ximpClient->request_window =
	  ximpClient->focus_window = None ;
	/* じゃあ、閉じましょう。*/
	if( ximpClient->skkinput != NULL ){
	  Ximp_unlinkClient( ximpClient ) ;
	  ximpClient->to_destroy = True ;
	  XtDestroyWidget( ximpClient->skkinput ) ;
	} else {
	  Ximp_destroyClient( ximpClient ) ;
	}
	break ;
      }
      ximpClient = ximpClient->next ;
    }
    curw->ximp.destroyWindowEvent =
      neww->ximp.destroyWindowEvent =
	reqw->ximp.destroyWindowEvent = NULL ;
    return( FALSE ) ;
  }
  if( reqw->ximp.jisyo_dirty != curw->ximp.jisyo_dirty ){
    ximpClient = neww->ximp.client_list ;
    while( ximpClient != NULL ){
      if( ximpClient->skkinput != NULL )
	XtVaSetValues
	  ( ximpClient->skkinput,
	    XtNjisyoDirty, reqw->ximp.jisyo_dirty, NULL ) ;
      ximpClient = ximpClient->next ;
    }
    curw->ximp.jisyo_dirty = neww->ximp.jisyo_dirty = 
      reqw->ximp.jisyo_dirty ;
    return ( FALSE ) ;
  }
  /* ここでは何もすることがない。*/
  return( FALSE ) ;
}

/*
 * Ximp Widget を破棄するときに呼ばれる関数。
 */
static void Ximp_Destroy( Widget gw )
{
  /* 「あの？ここはどうなさったんでしょう？」 */
  /* 「ああ、ここはまだ何をして良いのかコードの全体が完成していないか
     らね。今は何もしないんだ。」 */
  /* (待つこと数日間…) */
  XimpWidget w = ( XimpWidget )gw ;
  struct XimpClient *node, *nextNode ;

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

/*
 * 変換クライアントから送られてきたクライアントメッセージが正しいメッ
 * セージであるかどうかを判断する関数。
 *----
 * 不正なメッセージであるのは、
 *   ・クライアントメッセージイベントでない。
 *   ・フォーマットが狂っている。
 *   ・宛先のウィンドウがサーバじゃない。
 *   ・プロトコルが違う。(プロトコルはメッセージ型で指定している。)
 */
static int Ximp_isCorrectConversionMessage
( Widget gw, XEvent *xevent )
{
  XimpWidget w = ( XimpWidget )gw ;
  /* ClientMessageEvent で無ければ無視する…そうじゃないことってあるの？*/
  if( xevent->type != ClientMessage )
    return False ;
  /* 正しい ClientMessageEvent であるかを判断する。チェックする項目は
     メッセージの種類と送り先、あとはフォーマット。*/
  if( xevent->xclient.window != XtWindow( gw ) ||
      xevent->xclient.message_type != w->ximp.ximp_protocol ||
      xevent->xclient.format != 32 )
    return False ;
  return True ;
}

/*
 * 変換クライアントが実は存在しないウィンドウだったりしないかを判定す
 * る関数。
 */
static int Ximp_isCorrectWindowP
( Widget gw, Window win, unsigned int *width, unsigned int *height )
{
  XWindowAttributes xwa ;
  /* たたり「ここは、SubWindow Mapping できるかどうかを判別する必要が *
   *         あるのでこのコードではまずい。」 *
   * 若葉  「そうなのですか？ 私、これでもかまわないと思ってました」* 
   * たたり「いや、Netscape がきちんとしてくれればいいんだよ…本当は」*/
  if( !isWindowExistIfExistsGetXWA( XtDisplay( gw ), win, &xwa ) )
    return False ;
  if( width != NULL && height != NULL ){
    *width  = xwa.width ;
    *height = xwa.height ;
  }
  return True ;
}

static void Ximp_SetNeededSize
( XimpAttributes *attr,
  unsigned int width, unsigned int height, 
  unsigned int max_width, unsigned int max_height )
{
  if( max_width  > 0 && width > max_width   )
    width  = max_width ;
  if( max_height > 0 && height > max_height )
    height = max_height ;
  if( width  < MIN_AREA_WIDTH  )
    width  = MIN_AREA_WIDTH ;
  if( height < MIN_AREA_HEIGHT )
    height = MIN_AREA_HEIGHT ;

  attr->needed_width  = width ;
  attr->needed_height = height ;
  return ;
}

static void Ximp_ComputeAreaForQuery
( struct XimpClient *ximpClient )
{
  XimpWidget xw = ( XimpWidget )ximpClient->protocol_widget ;
  unsigned int fontheight, width, height, max_width, max_height ;

  if( ximpClient->style->inputStyle != WINDOW_TYPE_SEPARATE ){
    /* Root Window Style でなければ、クライアントのウィンドウサイズを
     * 得る必要がある。*/
    Ximp_isCorrectWindowP
      ( ( Widget )xw,
	ximpClient->request_window, 
	&ximpClient->request_width, 
	&ximpClient->request_height ) ;
  }
  if( ximpClient->style->inputStyle != WINDOW_TYPE_SEPARATE ){
    /* OverTheSpot/OffTheSpot ともにステータスエリアを利用するので、そ
     * の大きさを得ておく。*/
    fontheight = ximpClient->status_attributes.line_spacing + 2 ;
    max_width = max_height = 0 ;
    if( ximpClient->attribute_mask & XIMP_STS_AREANEED_MASK ){
      max_width  = ximpClient->status_attributes.needed_width ;
      max_height = ximpClient->status_attributes.needed_height ;
    }
    if( xw->ximp.status_width > 0 ){
      width = xw->ximp.status_width ;
    } else {
      /* 大体横に40文字程度表示出来れば十分耐えられると独断と偏見から
       * 判断します。*/
      width = fontheight * 40 ;
      /* 要求された横幅よりもずっと大きいものでしたら、そっちにしませ
       * う。*/
      if( width > ximpClient->request_width ){
	width = ximpClient->request_width ;
      }
    }
    height = fontheight ;

    Ximp_SetNeededSize
      ( &ximpClient->status_attributes,
	width, height, max_width, max_height ) ;

    if( !( ximpClient->attribute_mask & XIMP_STS_AREA_MASK ) ){
      ximpClient->status_attributes.area_x = 0;
      ximpClient->status_attributes.area_y =
	ximpClient->request_height - 
	ximpClient->status_attributes.needed_height ;
      ximpClient->status_attributes.area_width =
	ximpClient->status_attributes.needed_width ;
      ximpClient->status_attributes.area_height =
	ximpClient->status_attributes.needed_height ;
    }
#ifdef DEBUG
    printf( "Status area : (x,y,w,h)=(%d,%d,%d,%d)\n",
	    ximpClient->status_attributes.area_x,
	    ximpClient->status_attributes.area_y,
	    ximpClient->status_attributes.area_width,
	    ximpClient->status_attributes.area_height ) ;
#endif
  }
  if( ximpClient->style->inputStyle == WINDOW_TYPE_OFFTHESPOT ){

    fontheight = ximpClient->preedit_attributes.line_spacing + 2 ;
    max_width = max_height = 0 ;
    if( ximpClient->attribute_mask & XIMP_PRE_AREANEED_MASK ){
      max_width  = ximpClient->preedit_attributes.needed_width ;
      max_height = ximpClient->preedit_attributes.needed_height ;
    }
    width = ximpClient->request_width -
      ximpClient->status_attributes.needed_width ;
    height = fontheight ;

    Ximp_SetNeededSize
      ( &ximpClient->preedit_attributes,
	width, height, max_width, max_height ) ;

    if( !( ximpClient->attribute_mask & XIMP_PRE_AREA_MASK ) ){
      ximpClient->preedit_attributes.area_x = 
	ximpClient->status_attributes.needed_width ;
      ximpClient->preedit_attributes.area_y =
	ximpClient->request_height - 
	ximpClient->preedit_attributes.needed_height ;
      ximpClient->preedit_attributes.area_width =
	ximpClient->preedit_attributes.needed_width ;
      ximpClient->preedit_attributes.area_height =
	ximpClient->preedit_attributes.needed_height ;
    }
  } else if( ximpClient->style->inputStyle == WINDOW_TYPE_OVERTHESPOT ){
    if( !( ximpClient->attribute_mask & XIMP_PRE_AREA_MASK ) ){
      ximpClient->preedit_attributes.area_x = 0 ;
      ximpClient->preedit_attributes.area_y = 0 ;
      ximpClient->preedit_attributes.area_width = 
	ximpClient->focus_width ;
      ximpClient->preedit_attributes.area_height = 
	ximpClient->focus_height ;
    }
  }
  return ;
}

static void Ximp_ComputeAreaForStartUp
( struct XimpClient *ximpClient )
{
  if( ximpClient->style->inputStyle == WINDOW_TYPE_SEPARATE ||
      ximpClient->style->inputStyle == WINDOW_TYPE_OVERTHESPOT )
    return ;
  if( ( ximpClient->attribute_mask & XIMP_STS_AREA_MASK ) &&
      ( ximpClient->attribute_mask & XIMP_PRE_AREA_MASK ) )
    return ;
  Ximp_ComputeAreaForQuery( ximpClient ) ;
  if( !( ximpClient->attribute_mask & XIMP_STS_AREA_MASK ) ){
    ximpClient->status_attributes.area_x = 0 ;
    ximpClient->status_attributes.area_y = ximpClient->request_height -
      ximpClient->status_attributes.needed_height ;
    ximpClient->status_attributes.area_width =
      ximpClient->status_attributes.needed_width ;
    ximpClient->status_attributes.area_height =
      ximpClient->status_attributes.needed_height ;
  }
  if( !( ximpClient->attribute_mask & XIMP_PRE_AREA_MASK ) ){
    ximpClient->preedit_attributes.area_x = 
      ximpClient->status_attributes.area_x + 
      ximpClient->status_attributes.area_width ;
    ximpClient->preedit_attributes.area_y = 
      ximpClient->request_height -
      ximpClient->preedit_attributes.needed_height ;
    ximpClient->preedit_attributes.area_width = 
      ximpClient->preedit_attributes.needed_width ;
    ximpClient->preedit_attributes.area_height = 
      ximpClient->preedit_attributes.needed_height ;
  }
  return ;
}

/*
 * 変換クライアントから変換サーバに接続要求が来た場合の処理。
 */
static void Ximp_createMessageHandler
( Widget gw, XClientMessageEvent *xclme )
{
  Window requestor ;
  XIMStyle inputStyle ;
  XimpInputStyle *xis ;
  struct XimpClient *ximpClient ;
  unsigned long attributemask ;
  unsigned int width, height ;

  /* 変換クライアントのウィンドウＩＤを抜き出しておく。*/
  requestor = ( Window )xclme->data.l[ 1 ] ;
  if( !Ximp_isCorrectWindowP( gw, requestor, &width, &height ) ){
    return ;
  }
#if 0
  /* 既に処理されている変換クライアントであるのか？ */
  if( Ximp_findClientWithWindowID( gw, requestor ) != NULL ){
    /* 既に処理されているのなら再度作成する必要は無いので閉じる。*/
    return ;
  }
#endif
  /* 入力スタイルを読みこむ。*/
  inputStyle = ( XIMStyle )xclme->data.l[ 2 ] ;
  
  /* サポートされている入力スタイルであるかどうかを見る。*/
  for( xis = ximpInputStyles ; xis->style != inputStyle ; xis ++ ){
    if( xis->style == 0 ){
      Ximp_rejectCreateMessage( gw, requestor ) ;
      return ;
    }
  }
#if defined(DEBUG)
  fprintf( stderr, "Ximp input style is \"%d\".\n", xis->inputStyle ) ;
#endif
  /* クライアントを処理するためのデータを確保する。*/
  ximpClient = Ximp_createClient( gw, requestor, xis ) ;
  ximpClient->request_width  = width ;
  ximpClient->request_height = height ;
  ximpClient->xevent = *xclme ;
  ximpClient->style  = xis ;

  attributemask = xclme->data.l[ 3 ] ;
  /* クライアントのバージョンを取得しておく。どれ程の意味があるのかは
     謎。*/
  Ximp_getClientVersionProperty( ximpClient ) ;
  /* クライアントの情報を取得する。*/
  Ximp_getAttributes( ximpClient, attributemask ) ;
  /* 変換クライアントが破棄されてしまうようなことがあったら検知できる
     ようにする。
     全国規模で田畑の収穫率がどの位かとか面積はどうかとかそういうのを
     調べることではない。まして太閣秀吉とは何の関係もないのでよろしく。*/
  add_myeventhandler
    ( XtDisplay( gw ), requestor, XtWindow( gw ),
      NO_EVENT_HANDLE, StructureNotifyMask) ;

  /* 無事に作ることが出来ましたよと、返事を書く。*/
  Ximp_sendClientMessage32
    ( ximpClient, XIMP_CREATE_RETURN, ximpClient->id, 0L, 0L, 0L ) ;
  return ;
}

/*
 * 変換クライアントから変換サーバに接続終了が来た場合の処理。
 */
static void Ximp_destroyMessageHandler
( Widget gw, XClientMessageEvent *xclme )
{
  struct XimpClient *ximpClient ;
  unsigned long id ;

  /* 要求のあったクライアントが存在するかどうかを調べる。*/
  id = xclme->data.l[ 1 ] ;
  if( ( ximpClient = Ximp_findClient( gw, id ) ) == NULL ){
    return ;
  }
  ximpClient->xevent = *xclme ;
  /* クライアントを破棄する。*/
  if( ximpClient->skkinput != NULL ){
    Ximp_unlinkClient( ximpClient ) ;
    ximpClient->to_destroy = True ;
    XtDestroyWidget( ximpClient->skkinput ) ;
  } else {
    Ximp_destroyClient( ximpClient ) ;
  }
  return ;
}

/*
 * 変換開始要求を受けた場合の処理を行う関数。
 */
static void Ximp_beginMessageHandler
( Widget gw, XClientMessageEvent *xclme )
{
  struct XimpClient *ximpClient ;
  unsigned long id ;
  Window probe ;
  unsigned char *class_name ;
  WidgetClass class ;
  HistoryListNode *history ;

  /* 既に create message が送られていなければならない。そうか？ */
  id = xclme->data.l[ 1 ] ;
  if( ( ximpClient = Ximp_findClient( gw, id ) ) == NULL ){
    return ;
  }
  ximpClient->xevent = *xclme ;
  /* 既に何か変換作業はされているのか？ */
  if( ximpClient->skkinput != NULL ){
    return ;
  }
  if( !Ximp_isCorrectWindowP
      ( gw, ximpClient->focus_window, NULL, NULL ) ||
      ( ximpClient->style->inputStyle == WINDOW_TYPE_OFFTHESPOT &&
	!( ximpClient->attribute_mask & XIMP_STS_AREA_MASK ) ) ){
    /* クライアントに終了通知を送る。*/
    Ximp_sendClientMessage32
      ( ximpClient, XIMP_PROCESS_END, ximpClient->id, 0L, 0L, 0L ) ;
    Ximp_destroyClient( ximpClient ) ;
    return ;
  }
  /* ヒストリの初期化。これを忘れると？ どうなるのだろうか。*/
  history = history_setup
    ( ximpClient->skkinput, ximpClient->request_window ) ;
  /* 変換のためのウィンドウを作成するぞ。*/
  switch( ximpClient->style->inputStyle ){
  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 ;
  }
  ximpClient->skkinput = XtVaCreateWidget
    ( class_name, class, gw,
      XtNmappedWhenManaged, False,
      XtNclientWindow, ximpClient->request_window, 
      XtNconversionHistory, history, NULL ) ;
  
  /* 親しか知らないセットアップ処理を行う。*/
  XtCallCallbacks
    ( gw, XtNsetupInputWindowNotify, ximpClient->skkinput ) ;
  /* コールバックの設定。realize してからでは設定できないので、ここに *
   * 入ることになる。*/
  XtAddCallback
    ( ximpClient->skkinput, XtNfixNotify,
      ( XtCallbackProc )Ximp_SendMessageToRequestorCallback,
      ( XtPointer )ximpClient ) ;
  XtAddCallback
    ( ximpClient->skkinput, XtNendNotify,
      ( XtCallbackProc )Ximp_EndMessageToRequestorCallback,
      ( XtPointer )ximpClient ) ;
  XtAddCallback
    ( ximpClient->skkinput, XtNkeybackNotify,
      ( XtCallbackProc )Ximp_KeyEventToRequestorCallback,
      ( XtPointer )ximpClient ) ;

  Ximp_fillInDefaultAttributes( ximpClient ) ;
  Ximp_ComputeAreaForStartUp( ximpClient ) ;
  ximpClient->camsg.mask = Ximp_makeConvAttributes
    ( ximpClient, &ximpClient->camsg.value ) ;
  XtVaSetValues
    ( ximpClient->skkinput, XtNconversionAttribute,
      &ximpClient->camsg, NULL ) ;

  /* Event Capture Method の処理を行う。*/
  if( ximpClient->event_capture_method == EVENT_HA_INPUTONLY ){
    /* 変換クライアントの上に透明のウィンドウを貼り付けてイベントを奪 *
     * えるようにするぞ。*/
    probe = XCreateWindow
      ( XtDisplay( gw ), ximpClient->focus_window, 0, 0, 9999, 9999, 0, 0,
	InputOnly, (Visual *)CopyFromParent,
	0L, (XSetWindowAttributes *)NULL ) ;
    ximpClient->probe_window = probe ;
    XMapWindow( XtDisplay( gw ),  probe ) ;
  } else {
    ximpClient->probe_window = None ;
    probe = ximpClient->focus_window ;
  }
  XtVaSetValues( ximpClient->skkinput, XtNprobeWindow, probe, NULL ) ;

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

  /*
   * focus もしくは透明ウィンドウからキーイベントを取る。
   */
  if( ximpClient->event_capture_method == EVENT_HA_FOCUS_KARA ||
      ximpClient->event_capture_method == EVENT_HA_INPUTONLY ){
    add_myeventhandler
      ( XtDisplay( gw ), probe,
	XtWindow( ximpClient->skkinput ), KeyPress, KeyPressMask ) ;
    add_myeventhandler
      ( XtDisplay( gw ), probe,
	XtWindow( ximpClient->skkinput ), KeyRelease, KeyReleaseMask ) ;
  }
  /* 変換クライアントが破棄されてしまった場合にそれを検知できるよう *
   * にするぞ。*/
  add_myeventhandler
    ( XtDisplay( gw ), ximpClient->request_window, XtWindow( gw ),
      NO_EVENT_HANDLE, StructureNotifyMask ) ;
  Ximp_sendClientMessage32
    ( ximpClient, XIMP_PROCESS_BEGIN, ximpClient->id, 0L, 0L, 0L ) ;
  return ;
}

static void Ximp_endMessageHandler
( Widget gw, XClientMessageEvent *xclme )
{
  unsigned long id = xclme->data.l[ 1 ] ;
  struct XimpClient *ximpClient ;

  /* まず変換を終了しようとしているクライアントが実際に処理されてい *
   * るのかどうかを見る。*/
  if( ( ximpClient = Ximp_findClient( gw, id ) ) == NULL ){
    return ;
  }
  ximpClient->xevent = *xclme ;
  if( ximpClient->skkinput == NULL )
    return ;
  /* 変換を停止する。*/
  ximpClient->to_destroy = False ;
  XtDestroyWidget( ximpClient->skkinput ) ;
  return ;
}

static void Ximp_setFocusMessageHandler
( Widget gw, XClientMessageEvent *xclme )
{
  unsigned long id = xclme->data.l[ 1 ] ;
  struct XimpClient *ximpClient ;

  /* まず変換を終了しようとしているクライアントが実際に処理されてい *
   * るのかどうかを見る。*/
  if( ( ximpClient = Ximp_findClient( gw, id ) ) == NULL ){
    return ;
  }
  ximpClient->xevent = *xclme ;
  if( ximpClient->skkinput != NULL )
    XtVaSetValues
      ( ximpClient->skkinput, XtNsetFocus, True, NULL ) ;
  return ;
}

static void Ximp_unsetFocusMessageHandler
( Widget gw, XClientMessageEvent *xclme )
{
  unsigned long id = xclme->data.l[ 1 ] ;
  struct XimpClient *ximpClient ;

  /* まず変換を終了しようとしているクライアントが実際に処理されてい *
   * るのかどうかを見る。*/
  if( ( ximpClient = Ximp_findClient( gw, id ) ) == NULL ){
    return ;
  }
  ximpClient->xevent = *xclme ;
  if( ximpClient->skkinput != NULL )
    XtVaSetValues
      ( ximpClient->skkinput, XtNunsetFocus, True, NULL ) ;
  return ;
}

static void Ximp_moveMessageHandler
( Widget gw, XClientMessageEvent *xclme )
{
  unsigned long id = xclme->data.l[ 1 ] ;
  struct XimpClient *ximpClient ;

  /* まず変換を終了しようとしているクライアントが実際に処理されてい *
   * るのかどうかを見る。*/
  if( ( ximpClient = Ximp_findClient( gw, id ) ) == NULL ){
    return ;
  }
  ximpClient->xevent = *xclme ;

  ximpClient->preedit_attributes.spot_x = xclme->data.l[ 2 ] ;
  ximpClient->preedit_attributes.spot_y = xclme->data.l[ 3 ] ;
  /* 変換開始前に情報がこちら経由で来るかもしれないので…注意。*/
  ximpClient->attribute_mask |= XIMP_PRE_SPOTL_MASK ;

  /* だから、ここで NULL かどうか見ている。*/
  if( ximpClient->skkinput != NULL ){
    ximpClient->camsg.mask = CASpotLocation ;
    ximpClient->camsg.value.spot_x = ximpClient->preedit_attributes.spot_x ;
    ximpClient->camsg.value.spot_y = ximpClient->preedit_attributes.spot_y ;
    XtVaSetValues
      ( ximpClient->skkinput, XtNconversionAttribute,
	&ximpClient->camsg, NULL ) ;
  }
  return ;
}

static void Ximp_resetMessageHandler
( Widget gw, XClientMessageEvent *xclme )
{
  XimpWidget w = ( XimpWidget )gw ;
  unsigned long id = xclme->data.l[ 1 ] ;
  struct XimpClient *ximpClient ;

  /* まず変換を終了しようとしているクライアントが実際に処理されてい *
   * るのかどうかを見る。*/
  if( ( ximpClient = Ximp_findClient( gw, id ) ) == NULL ){
    return ;
  }
  ximpClient->xevent = *xclme ;
  ximpClient->resetting = True ;
  /* 空の文字列を送る。(?)*/
  XChangeProperty
    ( XtDisplay( gw ), XtWindow( gw ),
      ximpClient->property, w->ximp.compound_text, 8,
      PropModeAppend, (unsigned char *)"", 0 ) ;
  /* リセットした旨を伝える。*/
  Ximp_sendClientMessage32
    ( ximpClient, XIMP_RESET_RETURN, ximpClient->id,
      ximpClient->property, 0L, 0L ) ;
  return ;
}

static void Ximp_setvalueMessageHandler
( Widget gw, XClientMessageEvent *xclme )
{
  unsigned long id = xclme->data.l[ 1 ] ;
  struct XimpClient *ximpClient ;
  unsigned long mask ;

  /* まず変換を終了しようとしているクライアントが実際に処理されている
   * のかどうかを見る。どうやら、Netscape はフォームの場所とかに関係せ
   * ずに、同一の Netscape なら同一の ID を用いようとするらしい…。そ
   * れは困ったことだと思う…。*/ 
  if( ( ximpClient = Ximp_findClient( gw, id ) ) == NULL ){
    return ;
  }
  mask = xclme->data.l[ 2 ] ;
  ximpClient->xevent = *xclme ;
#ifdef DEBUG
  printf( "[Ximp] SetValueMessage( mask = %lx )\n", mask ) ;
#endif
  Ximp_getAttributes( ximpClient, mask ) ;
  /* 既に変換開始されているのなら…。*/
  if( ximpClient->skkinput != NULL ){
    ximpClient->camsg.mask = Ximp_makeConvAttributes
      ( ximpClient, &ximpClient->camsg.value ) ;
    if( ximpClient->camsg.mask )
      XtVaSetValues
	( ximpClient->skkinput, XtNconversionAttribute,
	  &ximpClient->camsg, NULL ) ;
  }
  return ;
}

static void Ximp_changeMessageHandler
( Widget gw, XClientMessageEvent *xclme )
{
  XimpWidget w = ( XimpWidget )gw ;
  unsigned long id = xclme->data.l[ 1 ] ;
  struct XimpClient *ximpClient ;
  Atom atom ;
  unsigned long mask ;

  /* まず変換を終了しようとしているクライアントが実際に処理されてい *
   * るのかどうかを見る。*/
  if( ( ximpClient = Ximp_findClient( gw, id ) ) == NULL ){
    return ;
  }
  /* 変化した属性が ATOM の形で渡される。*/
  atom = xclme->data.l[ 2 ] ;
  ximpClient->xevent = *xclme ;
  /* どの属性が変化したのですか？ */
  if( atom == w->ximp.ximp_focus ){
    /* しかし、フォーカスしているウィンドウが変化したら腐ると思うの *
     * だが。ちがったかな？ コードを見るとそうなるのだが… */
    mask = XIMP_FOCUS_WIN_MASK ;
  } else if( atom == w->ximp.ximp_preedit ){
    mask = PREEDIT_MASK ;
  } else if( atom == w->ximp.ximp_status ){
    mask = STATUS_MASK ;
  } else if( atom == w->ximp.ximp_preeditfont ){
    mask = XIMP_PRE_FONT_MASK ;
  } else if( atom == w->ximp.ximp_statusfont ){
    mask = XIMP_STS_FONT_MASK ;
  } else {
    Ximp_sendErrorEvent( ximpClient, XIMP_BadProperty ) ;
    return ;
  }
  /* 変化した属性を再度取り直す。*/
  Ximp_getAttributes( ximpClient, mask ) ; 
  if( ximpClient->skkinput != NULL ){
    ximpClient->camsg.mask =
      Ximp_makeConvAttributes( ximpClient, &ximpClient->camsg.value ) ;
    XtVaSetValues
      ( ximpClient->skkinput, XtNconversionAttribute,
	&ximpClient->camsg, NULL ) ;
  }
  return ;
}

static void Ximp_getValueMessageHandler
( Widget gw, XClientMessageEvent *xclme )
{
  unsigned long id = xclme->data.l[ 1 ] ;
  struct XimpClient *ximpClient ;
  unsigned long mask ;

  /* まず変換を終了しようとしているクライアントが実際に処理されてい *
   * るのかどうかを見る。*/
  if( ( ximpClient = Ximp_findClient( gw, id ) ) == NULL ){
    return ;
  }
  ximpClient->xevent = *xclme ;
  mask = xclme->data.l[ 2 ] ;
#ifdef DEBUG
  printf( "[Ximp] GetValueMessage( mask = %lx )\n", mask ) ;
#endif
  Ximp_fillInDefaultAttributes( ximpClient ) ;
  Ximp_ComputeAreaForQuery( ximpClient ) ;
  Ximp_setAttributes( ximpClient, mask ) ;
  Ximp_sendClientMessage32
    ( ximpClient, XIMP_GETVALUE_RETURN, ximpClient->id, 0L, 0L, 0L ) ;
  return ;
}

static void Ximp_keypressMessageHandler
( Widget gw, XClientMessageEvent *xclme )
{
  unsigned long id = xclme->data.l[ 1 ] ;
  struct XimpClient *ximpClient ;
  XKeyEvent xkey ;

  /* まず変換を終了しようとしているクライアントが実際に処理されてい *
   * るのかどうかを見る。*/
  if( ( ximpClient = Ximp_findClient( gw, id ) ) == NULL ){
    return ;
  }
  ximpClient->xevent = *xclme ;

  if( ximpClient->skkinput == NULL )
    return ;

  /* キーイベントを fake する。*/
  xkey.type        = KeyPress ;
  xkey.serial      = xclme->serial ;
  xkey.send_event  = True ;
  xkey.display     = xclme->display ;
  xkey.window      = ximpClient->focus_window ;
  xkey.root        = DefaultRootWindow( xclme->display ) ;
  xkey.subwindow   = None ;
  xkey.time        = 0 ;
  xkey.x           = 0 ;
  xkey.y           = 0 ;
  xkey.x_root      = 0 ;
  xkey.y_root      = 0 ;
  xkey.state       = xclme->data.l[ 3 ] ;
  xkey.keycode     = xclme->data.l[ 2 ] ;
  xkey.same_screen = True ;

  /* 送りつける。*/
  XtCallActionProc
    ( ximpClient->skkinput, "KeyDownEventHandler",
      ( XEvent * )&xkey, ( String * )NULL, ( Cardinal )0 ) ;
  return ;
}

static void Ximp_extensionMessageHandler
( Widget gw, XClientMessageEvent *xclme )
{
  XimpWidget w = ( XimpWidget )gw ;
  unsigned long id = xclme->data.l[ 1 ] ;
  struct XimpClient *ximpClient ;
  Atom extension_atom ;

  /* まず変換を終了しようとしているクライアントが実際に処理されてい *
   * るのかどうかを見る。*/
  if( ( ximpClient = Ximp_findClient( gw, id ) ) == NULL ){
    return ;
  }
  ximpClient->xevent = *xclme ;
  extension_atom = xclme->data.l[ 2 ] ;

  if( extension_atom == w->ximp.ximp_extbackfront ){
    if( ximpClient->skkinput != NULL ){
      return ;
    }
    if( xclme->data.l[ 3 ] != 0 ){
      ximpClient->event_capture_method = EVENT_NO_SHORINASHI ;
    } else {
      ximpClient->event_capture_method = EVENT_HA_FOCUS_KARA ;
    }
  } else {
    Ximp_sendErrorEvent( ximpClient, XIMP_BadAtom ) ;
  }
  return ;
}

/*
 * 接続要求を出してきたクライアントのバージョンを取得する関数。
 *----
 * 別に取れなくても問題ないようだけど…？
 */
static int Ximp_getClientVersionProperty
( struct XimpClient *ximpClient )
{
  XimpWidget w = ( XimpWidget )ximpClient->protocol_widget ;
  unsigned char *version_string ;
  unsigned long length ;

  if( !Ximp_getProperty
      ( ximpClient, w->ximp.ximp_version, XA_STRING, 8, 
	&version_string, &length ) ){
    ximpClient->client_version = NULL ;
    return False ;
  }
  ximpClient->client_version = version_string ;
  return True ;
}

/*
 * 変換クライアントの情報を抜き出す関数だぞ。
 */
static void Ximp_getAttributes
( struct XimpClient *ximpClient, unsigned long mask )
{
  if( mask & XIMP_FOCUS_WIN_MASK ){
    Ximp_getFocusProperty( ximpClient ) ;
  }
  if( mask & XIMP_PRE_FONT_MASK ){
    Ximp_getPreeditFontProperty( ximpClient ) ;
  }
  if( mask & XIMP_STS_FONT_MASK ){
    Ximp_getStatusFontProperty( ximpClient ) ;
  }
  if( mask & PREEDIT_MASK ){
    Ximp_getPreeditProperty( ximpClient, mask & PREEDIT_MASK ) ;
  }
  if( mask & STATUS_MASK ){
    Ximp_getStatusProperty( ximpClient, mask & STATUS_MASK ) ;
  }
  return ;
}

/*
 * 変換クライアントの要求する属性というか特徴というか特性というか指示
 * のようなものを取り出す関数。
 */
static int Ximp_getProperty
( struct XimpClient *ximpClient, Atom property, Atom type, int format,
  unsigned char **rprop, unsigned long *rnitems )
{
  Atom actualtype ;
  int actualformat ;
  unsigned long rbytes_after ;

  *rprop = NULL ;
  /* 変換クライアントのウィンドウＩＤを利用して、そのプロパティを読む。*/
  XGetWindowProperty
    ( XtDisplay( ximpClient->protocol_widget ),
      ximpClient->request_window, 
      property, 0L, 1000L, True, type,
      &actualtype, &actualformat, rnitems,
      &rbytes_after, rprop ) ;
  /* 指示したプロパティが読めなければエラーを通知する。*/
  if( actualtype == None ){
    Ximp_sendErrorEvent( ximpClient, XIMP_BadProperty ) ;
    return False ;
  } else if( actualtype != type ){
    Ximp_sendErrorEvent( ximpClient, XIMP_BadPropertyType ) ;
    return False;
  } else if( actualformat != format ){
    if( *rprop != NULL )
      XFree( (char *)*rprop ) ;
    *rprop = NULL ;
    Ximp_sendErrorEvent( ximpClient, XIMP_BadPropertyType ) ;
    return False ;
  }
  /* 無事に読めればＯＫなのである。*/
  return True ;
}

static void Ximp_getFocusProperty( struct XimpClient *ximpClient )
{
  XimpWidget w = ( XimpWidget )ximpClient->protocol_widget ;
  unsigned char *data ;
  unsigned long len ;
  Window focus_window ;
  unsigned int width, height ;

  if( !Ximp_getProperty
      ( ximpClient, w->ximp.ximp_focus, XA_WINDOW, 32,
	&data, &len ) ){
    return ;
  } else if( len != 1 ){
    XFree( ( char * )data ) ;
    return ;
  }

  focus_window = *( Window * )data ;
  XFree( ( char * )data ) ;

  if( !Ximp_isCorrectWindowP( ( Widget )w, focus_window, &width, &height ) ){
    Ximp_sendErrorEvent( ximpClient, XIMP_BadFocusWindow ) ;
    return ;
  }
  /* フォーカスウィンドウを獲得する。*/
#ifdef DEBUG
  fprintf
    ( stderr, "(Ximp) FocusWindow:(%lu) %u x %u, RequestWindow:(%lu)\n",
      focus_window, width, height, ximpClient->request_window ) ;
#endif
  if( ximpClient->skkinput != NULL )
    Ximp_ChangeFocusWindow( ximpClient, focus_window ) ;

  ximpClient->focus_window    = focus_window ;
  ximpClient->attribute_mask |= XIMP_FOCUS_WIN_MASK ;
  ximpClient->focus_width     = width ;
  ximpClient->focus_height    = height ;
  return ;
}

static void Ximp_getPreeditFontProperty
( struct XimpClient *ximpClient )
{
  XimpWidget w = ( XimpWidget )ximpClient->protocol_widget ;
  unsigned char *fontlistname ;
  unsigned long length_of_fontlistname ;

#ifdef DEBUG
  printf( "(Ximp) GetPreeditFontProperty()\n" ) ;
#endif
  /* フォントの名前を抜き出す。*/
  if( !Ximp_getProperty
      ( ximpClient, w->ximp.ximp_preeditfont, XA_STRING, 8,
	&fontlistname, &length_of_fontlistname ) ){
    return ;
  }

  /* 既に何らかのフォントが割当てられているのならば、それを解放し新 *
   * しいフォントの名前を登録する。*/
  if( ximpClient->preedit_attributes.fontlist != NULL ){
    if( !strcmp( fontlistname, ximpClient->preedit_attributes.fontlist ) ){
      XFree( fontlistname ) ;
      return;
    }
    XFree( ximpClient->preedit_attributes.fontlist ) ;
  }
  ximpClient->preedit_attributes.fontlist = fontlistname ;
  ximpClient->attribute_mask |= XIMP_PRE_FONT_MASK ;
  Ximp_getFonts( ximpClient, fontlistname ) ;
  return ;
}

static void Ximp_getFonts
( struct XimpClient *ximpClient, unsigned char *fontnamelist )
{
#ifdef DEBUG
  fprintf( stdout, "(Ximp) fontset = \"%s\"\n", fontnamelist ) ;
  fflush( stdout ) ;
#endif
  /* フォントを確保する。*/
  fontMgr_PrepareFontByName
    ( XtDisplay( ximpClient->protocol_widget ), 
      ximpClient->fontset, fontnamelist ) ;
  return ;
}

static void Ximp_getStatusFontProperty
( struct XimpClient *ximpClient )
{
  XimpWidget w = ( XimpWidget )ximpClient->protocol_widget ;
  unsigned char *fontlistname ;
  unsigned long length_of_fontlistname ;

#ifdef DEBUG
  printf( "(Ximp) GetStatusFontProperty()\n" ) ;
#endif
  if( !Ximp_getProperty
      ( ximpClient, w->ximp.ximp_statusfont, XA_STRING, 8,
	&fontlistname, &length_of_fontlistname ) ){
    return ;
  }

  if( ximpClient->status_attributes.fontlist != NULL ){
    XFree( ximpClient->status_attributes.fontlist ) ;
  }
  ximpClient->status_attributes.fontlist = fontlistname ;
  ximpClient->attribute_mask |= XIMP_STS_FONT_MASK ;
  return ;
}

static void Ximp_getPreeditProperty
( struct XimpClient *ximpClient, unsigned long mask )
{
  XimpWidget w = ( XimpWidget )ximpClient->protocol_widget ;
  unsigned long *data ;
  unsigned long len ;

  if( !Ximp_getProperty
      ( ximpClient, w->ximp.ximp_preedit,
	w->ximp.ximp_preedit, 32, 
	( unsigned char ** )&data, &len ) ){
    return ;
  } else if ( len < 14 ){
    XFree( ( char * )data ) ;
    return ;
  }

  ximpClient->attribute_mask |= mask ;
#ifdef DEBUG
  printf( "(Ximp) GetPreeditProperty()\n" ) ;
#endif
  /* 変換クライアントの表示エリアの位置及び大きさを受け取る。*/
  if( mask & XIMP_PRE_AREA_MASK ){
    ximpClient->preedit_attributes.area_x = data[ 0 ] ;
    ximpClient->preedit_attributes.area_y = data[ 1 ] ;
    ximpClient->preedit_attributes.area_width  = data[ 2 ] ;
    ximpClient->preedit_attributes.area_height = data[ 3 ] ;
    if( data[ 2 ] == 0 || data[ 3 ] == 0 ){
      ximpClient->attribute_mask &= ~( XIMP_PRE_AREA_MASK ) ;
    }
#ifdef DEBUG
    printf( "\t\"PRE_AREA_MASK\" (x,y,w,h)=(%ld,%ld,%ld,%ld)\n",
	    data[0],data[1],data[2],data[3] ) ;
#endif
  }
  /* 変換クライアントの前景色を受け取る。*/
  if( mask & XIMP_PRE_FG_MASK ){
    ximpClient->preedit_attributes.foreground = data[ 4 ] ;
#ifdef DEBUG
    printf( "\t\"XIMP_PRE_FG_MASK\" foreground = %lx\n", data[4] ) ;
#endif
  }
  /* 変換クライアントの背景色を受け取る。*/
  if( mask & XIMP_PRE_BG_MASK ){ 
    ximpClient->preedit_attributes.background = data[ 5 ] ;
#ifdef DEBUG
    printf( "\t\"XIMP_PRE_BG_MASK\" background = %lx\n", data[5] ) ;
#endif
  }
  /* 変換クライアントの背景のピックスマップが何かを受け取る？ */
  if( mask & XIMP_PRE_COLORMAP_MASK ){
    ximpClient->preedit_attributes.colormap = data[ 6 ] ;
#ifdef DEBUG
    printf( "\t\"XIMP_PRE_COLORMAP_MASK\" colormap = %ld\n",data[6] ) ;
#endif
  }
  if( mask & XIMP_PRE_BGPIXMAP_MASK ){
    ximpClient->preedit_attributes.background_pixmap = data[ 7 ] ;
#ifdef DEBUG
    printf( "\t\"XIMP_PRE_BGPIXMAP_MASK\" bg_pixmap = %ld\n", data[7] ) ;
#endif
  }
  if( mask & XIMP_PRE_LINESP_MASK ){
    if( data[8] < MIN_LINE_SPACING ){
      ximpClient->attribute_mask &= ~XIMP_PRE_LINESP_MASK ;
    } else {
      ximpClient->preedit_attributes.line_spacing = data[ 8 ] ;
    }
#ifdef DEBUG
    printf( "\t\"XIMP_PRE_LINESP_MASK\" linespacing = %ld\n",data[ 8 ] ) ;
#endif
  }
  /* data[9]: Cursor */
  if( mask & XIMP_PRE_CURSOR_MASK ){
    ximpClient->preedit_attributes.cursor = data[ 9 ] ;
#ifdef DEBUG
    printf( "\t\"XIMP_PRE_CURSOR_MASK\" cursor = %lx\n", data[9] ) ;
#endif
  }
  if( mask & XIMP_PRE_AREANEED_MASK ){
    ximpClient->preedit_attributes.needed_width  = data[ 10 ] ;
    ximpClient->preedit_attributes.needed_height = data[ 11 ] ;
#ifdef DEBUG
    printf( "\t\"XIMP_PRE_AREANEED_MASK\" (w,h) = (%ld,%ld)\n",
	    data[10],data[11] ) ;
#endif
  }
  if( mask & XIMP_PRE_SPOTL_MASK ){
    ximpClient->preedit_attributes.spot_x = data[ 12 ] ;
    ximpClient->preedit_attributes.spot_y = data[ 13 ] ;
#ifdef DEBUG
    printf( "\t\"XIMP_PRE_SPOTL_MASK\" (x,y) = (%ld,%ld)\n",
	    data[12],data[13] ) ;
#endif
  }
  XFree( ( char * )data ) ;
  return ;
}

static void Ximp_getStatusProperty
( struct XimpClient *ximpClient, unsigned long mask )
{
  XimpWidget w = ( XimpWidget )ximpClient->protocol_widget ;
  unsigned long *data ;
  unsigned long len ;

  if( !Ximp_getProperty
      ( ximpClient, w->ximp.ximp_status,
	w->ximp.ximp_status, 32,
	( unsigned char ** )&data, &len ) ){
    return ;
  } else if( len < 12 ){
    XFree( ( char * )data ) ;
    return ;
  }

  ximpClient->attribute_mask |= mask ;
#ifdef DEBUG
  printf( "(Ximp) GetStatusProperty( mask = %lx )\n", mask ) ;
#endif
  if( mask & XIMP_STS_AREA_MASK ){
    if( data[ 2 ] > 0 && data[ 3 ] > 0 ){
      ximpClient->status_attributes.area_x = data[ 0 ] ;
      ximpClient->status_attributes.area_y = data[ 1 ] ;
      ximpClient->status_attributes.area_width  = data[ 2 ] ;
      ximpClient->status_attributes.area_height = data[ 3 ] ;
    } else {
      ximpClient->attribute_mask &= ~( XIMP_STS_AREA_MASK ) ;
    }
#ifdef DEBUG
    printf( "\t\"XIMP_STS_AREA_MASK\" (x,y,w,h) = (%ld,%ld,%ld,%ld)\n",
	    data[ 0 ], data[ 1 ],data[ 2 ], data[ 3 ] ) ;
#endif
  }
  if( mask & XIMP_STS_FG_MASK ){
    ximpClient->status_attributes.foreground = data[ 4 ] ;
#ifdef DEBUG
    printf( "\t\"XIMP_STS_FG_MASK\" foreground = %ld\n", data[4] ) ;
#endif
  }
  if( mask & XIMP_STS_BG_MASK ){
    ximpClient->status_attributes.background = data[ 5 ] ;
#ifdef DEBUG
    printf( "\t\"XIMP_STS_BG_MASK\" background = %ld\n", data[5] ) ;
#endif
  }
  if( mask & XIMP_STS_COLORMAP_MASK ){
    ximpClient->status_attributes.colormap = data[ 6 ] ;
#ifdef DEBUG
    printf( "\t\"XIMP_STS_COLORMAP_MASK\" colormap = %ld\n", data[6] ) ;
#endif
  }
  if( mask & XIMP_STS_BGPIXMAP_MASK ){
    ximpClient->status_attributes.background_pixmap = data[ 7 ] ;
#ifdef DEBUG
    printf( "\t\"XIMP_STS_BGPIXMAP_MASK\" bgpixmap = %ld\n", data[7] ) ;
#endif
  }
  if( mask & XIMP_STS_LINESP_MASK ){
    if( data[ 8 ] < MIN_LINE_SPACING ){
      ximpClient->attribute_mask &= ~( XIMP_STS_LINESP_MASK ) ;
    } else {
      ximpClient->status_attributes.line_spacing = data[ 8 ] ;
    }
#ifdef DEBUG
    printf( "\t\"XIMP_STS_LINESP_MASK\" linespacing = %ld\n", data[8] ) ;
#endif
  }
  if( mask & XIMP_STS_CURSOR_MASK ){
    ximpClient->status_attributes.cursor = data[ 9 ] ;
#ifdef DEBUG
    printf( "\t\"XIMP_STS_CURSOR_MASK\" cursor = %lx\n", data[9] ) ;
#endif
  }
  if( mask & XIMP_STS_AREANEED_MASK ){
    ximpClient->status_attributes.needed_width  = data[ 10 ] ;
    ximpClient->status_attributes.needed_height = data[ 11 ] ;
#ifdef DEBUG
    printf( "\t\"XIMP_STS_AREANEED_MASK\" (w,h) = (%ld,%ld)\n",
	    data[10],data[11] ) ;
#endif
  }
  if( len > 12 && ( mask & XIMP_STS_WINDOW_MASK ) ){
    ximpClient->status_window = None ;
#ifdef DEBUG
    printf( "\t\"XIMP_STS_WINDOW_MASK\" window = None\n" ) ;
#endif
  }
  XFree( ( char *)data ) ;
  return ;
}

/*
 * −−− ここから下は変換属性を変換クライアントに伝える関連 −−−
 */

static void Ximp_setAttributes
( struct XimpClient *ximpClient, unsigned long mask )
{
  if( mask & XIMP_FOCUS_WIN_MASK ){
    Ximp_setFocusProperty( ximpClient ) ;
  }
  if( mask & XIMP_PRE_FONT_MASK ){
    Ximp_setPreeditFontProperty( ximpClient ) ;
  }
  if( mask & XIMP_STS_FONT_MASK ){
    Ximp_setStatusFontProperty( ximpClient ) ;
  }
  if( mask & PREEDIT_MASK ){
    Ximp_setPreeditProperty( ximpClient, mask ) ;
  }
  if( mask & STATUS_MASK ){
    Ximp_setStatusProperty( ximpClient, mask ) ;
  }
  return ;
}

static void Ximp_setFocusProperty
( struct XimpClient *ximpClient )
{
  XimpWidget w = ( XimpWidget )ximpClient->protocol_widget ;

#ifdef DEBUG
  printf( "(Ximp) setFocusProperty() focus = %ld\n",
	  ximpClient->focus_window ) ;
#endif
  Ximp_setProperty
    ( ximpClient, w->ximp.ximp_focus, XA_WINDOW, 32,
      ( unsigned char * )&ximpClient->focus_window, 1 ) ;
  return ;
}

static void Ximp_setPreeditFontProperty
( struct XimpClient *ximpClient )
{
  XimpWidget w = ( XimpWidget )ximpClient->protocol_widget ;

#ifdef DEBUG
  printf( "(Ximp) setPreeditFont()\n" ) ;
#endif
  /* フォントまわりはデフォルトは Overthespot とかが中でもっているの *
   * で、変換クライアント側が指定しなければ NULL のまま。*/
  if( ximpClient->preedit_attributes.fontlist != NULL ){
    Ximp_setProperty
      ( ximpClient, w->ximp.ximp_preeditfont, XA_STRING, 8,
	( unsigned char * )ximpClient->preedit_attributes.fontlist,
	strlen( ximpClient->preedit_attributes.fontlist ) ) ;
  } else {
    Ximp_setProperty
      ( ximpClient, w->ximp.ximp_preeditfont, XA_STRING, 8,
	( unsigned char * )"", 0 ) ;
  }
  return ;
}

static void Ximp_setStatusFontProperty
( struct XimpClient *ximpClient )
{
  XimpWidget w = ( XimpWidget )ximpClient->protocol_widget ;

#ifdef DEBUG
  printf( "(Ximp) setStatusFont()\n" ) ;
#endif
  /* フォントまわりはデフォルトは Overthespot とかが中でもっているの *
   * で、変換クライアント側が指定しなければ NULL のまま。*/
  if( ximpClient->status_attributes.fontlist != NULL ){
    Ximp_setProperty
      ( ximpClient, w->ximp.ximp_statusfont, XA_STRING, 8,
	( unsigned char * )ximpClient->status_attributes.fontlist,
	strlen( ximpClient->status_attributes.fontlist ) ) ;
  } else {
    Ximp_setProperty
      ( ximpClient, w->ximp.ximp_statusfont, XA_STRING, 8,
	( unsigned char * )"", 0 ) ;
  }
  return ;
}

static void Ximp_setPreeditProperty
( struct XimpClient *ximpClient, unsigned long mask )
{
  XimpWidget w = ( XimpWidget )ximpClient->protocol_widget ;
  long data[ 14 ] ;

#ifdef DEBUG
  printf( "(Ximp) setPreeditProperty( mask = %lx )\n", mask ) ;
#endif
  /* data[0]-data[3]: Area.{x,y,width,height} */
  if( mask & XIMP_PRE_AREA_MASK ){
    data[ 0 ] = ximpClient->preedit_attributes.area_x ;
    data[ 1 ] = ximpClient->preedit_attributes.area_y ;
    data[ 2 ] = ximpClient->preedit_attributes.area_width ;
    data[ 3 ] = ximpClient->preedit_attributes.area_height ;
#ifdef DEBUG
    printf( "\t\"XIMP_PRE_AREA_MASK\" (x,y,w,h)=(%ld,%ld,%ld,%ld)\n",
	    data[0],data[1],data[2],data[3] ) ;
#endif
  }
  /* data[4]: Foreground */
  if( mask & XIMP_PRE_FG_MASK ){
    data[ 4 ] = ximpClient->preedit_attributes.foreground ;
#ifdef DEBUG
    printf( "\t\"XIMP_PRE_FG_MASK\" foreground = %lx\n", data[4] ) ;
#endif
  }
  /* data[5]: Background */
  if( mask & XIMP_PRE_BG_MASK ){
    data[ 5 ] = ximpClient->preedit_attributes.background ;
#ifdef DEBUG
    printf( "\t\"XIMP_PRE_BG_MASK\" background = %lx\n", data[5] ) ;
#endif
  }
  /* data[6]: Colormap */
  if( mask & XIMP_PRE_COLORMAP_MASK ){
    data[ 6 ] = ximpClient->preedit_attributes.colormap ;
#ifdef DEBUG
    printf( "\t\"XIMP_PRE_COLORMAP_MASK\" colormap = %ld\n",data[6] ) ;
#endif
  }
  /* data[7]: BackgroundPixmap */
  if( mask & XIMP_PRE_BGPIXMAP_MASK ){
    data[ 7 ] = ximpClient->preedit_attributes.background_pixmap ;
#ifdef DEBUG
    printf( "\t\"XIMP_PRE_BGPIXMAP_MASK\" bg_pixmap = %ld\n", data[7] ) ;
#endif
  }
  /* data[8]: Line_Spacing */
  if( mask & XIMP_PRE_LINESP_MASK ){
    data[ 8 ] = ximpClient->preedit_attributes.line_spacing ;
#ifdef DEBUG
    printf( "\t\"XIMP_PRE_LINESP_MASK\" linespacing = %ld\n",data[ 8 ] ) ;
#endif
  }
  /* data[9]: Cursor */
  if (mask & XIMP_PRE_CURSOR_MASK) {
    data[ 9 ] = ximpClient->preedit_attributes.cursor ;
#ifdef DEBUG
    printf( "\t\"XIMP_PRE_CURSOR_MASK\" cursor = %lx\n", data[9] ) ;
#endif
  }
  /* data[10]-data[11]: AreaNeeded.{width,height} */
  if( mask & XIMP_PRE_AREANEED_MASK ){
    data[ 10 ] = ximpClient->preedit_attributes.needed_width ;
    data[ 11 ] = ximpClient->preedit_attributes.needed_height ;
#ifdef DEBUG
    printf( "\t\"XIMP_PRE_AREANEED_MASK\" (w,h) = (%ld,%ld)\n",
	    data[10],data[11] ) ;
#endif
  }
  /* data[12]-data[13]: SpotLocation.{x,y} */
  if( mask & XIMP_PRE_SPOTL_MASK ){
    data[ 12 ] = ximpClient->preedit_attributes.spot_x ;
    data[ 13 ] = ximpClient->preedit_attributes.spot_y ;
#ifdef DEBUG
    printf( "\t\"XIMP_PRE_SPOTL_MASK\" (x,y) = (%ld,%ld)\n",
	    data[12],data[13] ) ;
#endif
  }
  Ximp_setProperty
    ( ximpClient, w->ximp.ximp_preedit, w->ximp.ximp_preedit, 32, 
      ( unsigned char * )data, 14 ) ;
  return ;
}

static void Ximp_setStatusProperty
( struct XimpClient *ximpClient, unsigned long mask )
{
  XimpWidget w = ( XimpWidget )ximpClient->protocol_widget ;
  long data[ 13 ] ;
#ifdef DEBUG
  printf( "(Ximp) SetStatusProperty( mask = %lx )\n", mask ) ;
#endif
  /* data[0]-data[3]: Area.{x,y,width,height} */
  if( mask & XIMP_STS_AREA_MASK ){
    data[ 0 ] = ximpClient->status_attributes.area_x ;
    data[ 1 ] = ximpClient->status_attributes.area_y ;
    data[ 2 ] = ximpClient->status_attributes.area_width ;
    data[ 3 ] = ximpClient->status_attributes.area_height ;
#ifdef DEBUG
    printf( "\t\"XIMP_STS_AREA_MASK\" (x,y,w,h) = (%ld,%ld,%ld,%ld)\n",
	    data[ 0 ], data[ 1 ],data[ 2 ], data[ 3 ] ) ;
#endif
  }
  /* data[4]: Foreground */
  if( mask & XIMP_STS_FG_MASK ){
    data[ 4 ] = ximpClient->status_attributes.foreground ;
#ifdef DEBUG
    printf( "\t\"XIMP_STS_FG_MASK\" foreground = %ld\n", data[4] ) ;
#endif
  }
  /* data[5]: Background */
  if( mask & XIMP_STS_BG_MASK ){
    data[ 5 ] = ximpClient->status_attributes.background ;
#ifdef DEBUG
    printf( "\t\"XIMP_STS_BG_MASK\" background = %ld\n", data[5] ) ;
#endif
  }
  /* data[6]: Colormap */
  if( mask & XIMP_STS_COLORMAP_MASK ){
    data[ 6 ] = ximpClient->status_attributes.colormap ;
#ifdef DEBUG
    printf( "\t\"XIMP_STS_COLORMAP_MASK\" colormap = %ld\n", data[6] ) ;
#endif
  }
  /* data[7]: BackgroundPixmap */
  if( mask & XIMP_STS_BGPIXMAP_MASK ){
    data[ 7 ] = ximpClient->status_attributes.background_pixmap ;
#ifdef DEBUG
    printf( "\t\"XIMP_STS_BGPIXMAP_MASK\" bgpixmap = %ld\n", data[7] ) ;
#endif
  }
  /* data[8]: Line_Spacing */
  if( mask & XIMP_STS_LINESP_MASK ){
    data[ 8 ] = ximpClient->status_attributes.line_spacing ;
#ifdef DEBUG
    printf( "\t\"XIMP_STS_LINESP_MASK\" linespacing = %ld\n", data[8] ) ;
#endif
  }
  /* data[9]: Cursor */
  if( mask & XIMP_STS_CURSOR_MASK ){
    data[ 9 ] = ximpClient->status_attributes.cursor ;
  }
  /* data[10]-data[11]: AreaNeeded.{width,height} */
  if( mask & XIMP_STS_AREANEED_MASK ){
    data[ 10 ] = ximpClient->status_attributes.needed_width ;
    data[ 11 ] = ximpClient->status_attributes.needed_height ;
#ifdef DEBUG
    printf( "\t\"XIMP_STS_AREANEED_MASK\" (w,h)=(%ld,%ld)\n",
	    data[10],data[11]) ;
#endif
  }
  /* data[12]: StatusWindowID -- not suppoted by kinput2 */
  if( mask & XIMP_STS_WINDOW_MASK ){
    data[ 12 ] = ximpClient->status_window ;
#ifdef DEBUG
    printf( "\t\"XIMP_STS_WINDOW_MASK\" window = %lx\n", data[12] ) ;
#endif
  }
  Ximp_setProperty
    ( ximpClient, w->ximp.ximp_status,
      w->ximp.ximp_status, 32, ( unsigned char * )data, 13 ) ;
  return ;
}

static void Ximp_setProperty
( struct XimpClient *ximpClient, Atom property, Atom type, int format, 
  unsigned char *data, int nelements )
{
  XChangeProperty
    ( XtDisplay( ximpClient->protocol_widget ),
      ximpClient->request_window, property, type, format,
      PropModeReplace, data, nelements ) ;
  return ;
}

/*
 * −−−− ここから下は skkinput が必要とする変換属性を用意したり、
 *          Ximp の変換属性を skkinput の分かる変換属性に変換したり
 *          する関数が並ぶ。−−−−
 */


static void Ximp_fillInDefaultAttributes
( struct XimpClient *ximpClient )
{
  if( ximpClient->already_initial_setup )
    return ;

  if( !( ximpClient->attribute_mask & XIMP_FOCUS_WIN_MASK ) ){
    ximpClient->focus_window = ximpClient->request_window ;
    ximpClient->focus_width  = ximpClient->request_width ;
    ximpClient->focus_height = ximpClient->request_height ;
  }

  if( !( ximpClient->attribute_mask & XIMP_PRE_FONT_MASK ) ){
    fontMgr_copyDefaultFontSet
      ( XtDisplay( ximpClient->protocol_widget ), ximpClient->fontset ) ;
    ximpClient->attribute_mask |= XIMP_PRE_FONT_MASK ;
  }
  if( !( ximpClient->attribute_mask & XIMP_PRE_LINESP_MASK ) ){
    Cardinal i ;
    XFontStruct *font ;
    int maxascent = 0, maxdescent = 0 ;
    
    for( i = 0 ; i < NUMBER_OF_CHARSET ; i++ ){
      if( ximpClient->fontset[ i ] == NULL )
	continue ;
      if( ( font = ( ( ximpClient->fontset[ i ] )->font ) ) != NULL ){
	if( maxascent < font->ascent )
	  maxascent  = font->ascent ;
	if( maxdescent < font->descent )
	  maxdescent = font->descent ;
      }
    }
    ximpClient->preedit_attributes.line_spacing =
      maxascent + maxdescent ;
    if( ximpClient->preedit_attributes.line_spacing <= 0 ){
      ximpClient->preedit_attributes.line_spacing = MIN_LINE_SPACING ;
    }
  }
  if( !( ximpClient->attribute_mask & XIMP_PRE_SPOTL_MASK ) ){
    ximpClient->preedit_attributes.spot_x = 0 ;
    ximpClient->preedit_attributes.spot_y = 0 ;
  }
  if( !( ximpClient->attribute_mask & XIMP_STS_LINESP_MASK ) ){
    ximpClient->status_attributes.line_spacing =
      ximpClient->preedit_attributes.line_spacing ;
  }
  if( !( ximpClient->attribute_mask & XIMP_STS_WINDOW_MASK ) ){
    ximpClient->status_window = None ;
  }
  ximpClient->already_initial_setup = True ;
  return ;
}

/*
 * 変換クライアントの情報を skkinput の widget に渡せるように変換する
 * のに使われる関数だぞ。
 */
static unsigned long Ximp_makeConvAttributes
( struct XimpClient *ximpClient, struct ConvAttrs *attr )
{
  unsigned long mask ;

  mask = 0L ;
  attr->focus_window = ximpClient->focus_window ;
  mask |= CAFocusWindow ;

  if( ximpClient->style->inputStyle == WINDOW_TYPE_SEPARATE )
    return mask ;

  /* 表示領域の設定。*/
  if( ximpClient->attribute_mask & XIMP_PRE_AREA_MASK ){
    attr->client_area.x      = ximpClient->preedit_attributes.area_x ;
    attr->client_area.y      = ximpClient->preedit_attributes.area_y ;
    attr->client_area.width  = ximpClient->preedit_attributes.area_width ;
    attr->client_area.height = ximpClient->preedit_attributes.area_height ;
    mask |= CAClientArea ;
  }
  /* 前景色と背景色を設定する。*/
  if( ximpClient->attribute_mask &
      ( XIMP_PRE_FG_MASK | XIMP_PRE_BG_MASK ) ){
    attr->foreground = ximpClient->preedit_attributes.foreground ;
    attr->background = ximpClient->preedit_attributes.background ;
    mask |= CAForegroundPixel | CABackgroundPixel ;
  }
  if( ximpClient->attribute_mask & XIMP_PRE_COLORMAP_MASK ){
    attr->colormap = ximpClient->preedit_attributes.colormap ;
    mask |= CAColormap ;
  }
#if 0
  /* 次のものは思いっきり未実装である。
   *  ・background pixmap
   *  ・line spacing
   *  ・cursor 
   */
#endif
  /* ステータス領域の設定。off-the-spot 変換の場合には必ずステータ *
   * ス領域が指定されていなければならない。*/
  if( ximpClient->attribute_mask & XIMP_STS_AREA_MASK ){
    attr->status_area.x      = ximpClient->status_attributes.area_x ;
    attr->status_area.y      = ximpClient->status_attributes.area_y ;
    attr->status_area.width  = ximpClient->status_attributes.area_width ;
    attr->status_area.height = ximpClient->status_attributes.area_height ;
    mask |= CAStatusArea ;
#if defined(DEBUG)
  } else {
    if( ximpClient->style->inputStyle == WINDOW_TYPE_OFFTHESPOT ){
      fprintf( stderr, "off-the-spot window style needs status area.\n" ) ;
    }
#endif
  }
  /* フォントが指定されていた場合の処理。必ずではない。無ければ、こっ*
   * ちで勝手にデフォルトのフォントを利用してしまう。*/
  if( ximpClient->attribute_mask & XIMP_PRE_FONT_MASK ){
    attr->fontset   = ximpClient->fontset ;
    mask |= CAFonts ;
  }
  /* over-the-spot 変換ならば、どの位置に現在カーソルがあるかの指定 *
   * をすることもできる…じゃなくてして欲しいけど…。*/
  if( ximpClient->style->inputStyle == WINDOW_TYPE_OVERTHESPOT &&
      ximpClient->attribute_mask & XIMP_PRE_SPOTL_MASK ){
    attr->spot_x = ximpClient->preedit_attributes.spot_x ;
    attr->spot_y = ximpClient->preedit_attributes.spot_y ;
    mask |= CASpotLocation ;
  }
  return mask ;
}

/*
 *−−−− ここから下は X のメッセージ(イベント)の送信(send)関係 −−−−
 */

#if 0
/*
 *
 */
static int Ximp_sendClientMessage8
( struct XimpClient *ximpClient, unsigned char *string, int length )
{
  XimpWidget w = ( XimpWidget )ximpClient->protocol_widget ;
  XEvent xevent ;
    
  xevent.xclient.type         = ClientMessage;
  xevent.xclient.window       = ximpClient->focus_window ;
  xevent.xclient.message_type = w->ximp.ximp_protocol ;
  xevent.xclient.format       = 8 ;

  /* どうやら ID をちぎって渡しているみたい。*/
  xevent.xclient.data.b[ 0 ] = ( ximpClient->id >> 24 ) & 0xff ;
  xevent.xclient.data.b[ 1 ] = ( ximpClient->id >> 16 ) & 0xff ;
  xevent.xclient.data.b[ 2 ] = ( ximpClient->id >> 8  ) & 0xff ;
  xevent.xclient.data.b[ 3 ] = ( ximpClient->id       ) & 0xff ;
  xevent.xclient.data.b[ 4 ] = length ;

  /* ほげ？ これはどういうこと？ */
  strncpy( &xevent.xclient.data.b[ 5 ], string, 20 - 5 ) ;

  return XSendEvent
    ( XtDisplay( ( Widget )w ), ximpClient->focus_window, 
      False, NoEventMask, &xevent ) ;
}
#endif

/*
 * 変換クライアントにメッセージを送るのに利用される関数。
 */
static int Ximp_sendClientMessage32
( struct XimpClient *ximpClient, int type,
  unsigned long l1, unsigned long l2,
  unsigned long l3, unsigned long l4 )
{
  XimpWidget w = ( XimpWidget )ximpClient->protocol_widget ;
  XEvent event ;

  event.xclient.type         = ClientMessage ;
  event.xclient.window       = ximpClient->focus_window ;
  event.xclient.message_type = w->ximp.ximp_protocol ;
  event.xclient.format       = 32 ;
  event.xclient.data.l[ 0 ]  = type ;
  event.xclient.data.l[ 1 ]  = l1 ;
  event.xclient.data.l[ 2 ]  = l2 ;
  event.xclient.data.l[ 3 ]  = l3 ;
  event.xclient.data.l[ 4 ]  = l4 ; 

  return ( int )XSendEvent
    ( XtDisplay( ( Widget )w ), ximpClient->focus_window, 
      False, NoEventMask, &event ) ;
}

/*
 * エラーが発生したことを変換クライアントに伝える関数。
 */
static void Ximp_sendErrorEvent
( struct XimpClient *ximpClient, int errorno )
{
  Ximp_sendClientMessage32
    ( ximpClient, XIMP_ERROR, ximpClient->id,
      ximpClient->xevent.serial, ( unsigned long )errorno, 0L ) ;
  return ;
}

/*
 * 変換クライアントに処理できないということを返す関数。
 */
static void Ximp_rejectCreateMessage( Widget gw, Window win )
{
  XimpWidget w = ( XimpWidget )gw ;
  XEvent xevent ;

  xevent.xclient.type         = ClientMessage ;
  xevent.xclient.window       = win ;
  xevent.xclient.message_type = w->ximp.ximp_protocol ;
  xevent.xclient.format       = 32 ;
  xevent.xclient.data.l[0]    = XIMP_CREATE_RETURN ;
  xevent.xclient.data.l[1]    = 0L ;
  xevent.xclient.data.l[2]    = 0L ;
  xevent.xclient.data.l[3]    = 0L ;
  xevent.xclient.data.l[4]    = 0L ;

  XSendEvent
    ( XtDisplay( gw ), win, False, NoEventMask, &xevent ) ;
  return ;
}


/*
 * −−−− ここから下はＸイベントの処理関数　−−−−−−
 */


/*
 * 変換クライアントから送られてきたメッセージを処理する関数。
 *----
 * 一口にメッセージというが、このメッセージには…
 *     ・変換開始のための予約。
 *     ・変換開始要求。
 *     ・変換終了要求。
 * などが含まれている。kinput2 protocol で言う…
 *   CONVERSION_REQUEST, CONVERSION_CLOSE なんかも入っているのである。
 */
static void Ximp_conversionMessageEventHandler
( Widget gw, XEvent *xevent, String *args, Cardinal *num_of_args )
{
  XClientMessageEvent *xclme = &( xevent->xclient ) ;
  struct XimpClient *ximpClient = NULL ;

#ifdef DEBUG
  printf( "[Ximp] Conversion message recieved :" ) ;
#endif
  /*
   * まず正しいメッセージであるかどうかを調べる。
   */
  if( !Ximp_isCorrectConversionMessage( gw, xevent ) ){
    return ;
  }
  /*
   * どのようなメッセージであるかを見て、その処理関数へ引き渡す。
   */
#ifdef DEBUG
  printf( "(%ld).\n", xclme->data.l[ 0 ] ) ;
  {
    ximpClient = Ximp_findClient
      ( gw, ( unsigned long )xclme->data.l[ 1 ] ) ;
    if( ximpClient != NULL ){
      printf( "[%ld] -----> xmask = %lx\n", 
	      xclme->data.l[1], ximpClient->attribute_mask ) ;
    }
  }
#endif
  switch( ( unsigned long )xclme->data.l[ 0 ] ){
  case XIMP_CREATE :
    Ximp_createMessageHandler( gw, xclme ) ;
    break ;
  case XIMP_DESTROY :
    Ximp_destroyMessageHandler( gw, xclme ) ;
    break ;
  case XIMP_BEGIN :
    Ximp_beginMessageHandler( gw, xclme ) ;
    break ;
  case XIMP_END :
    Ximp_endMessageHandler( gw, xclme ) ;
    break ;
  case XIMP_SETFOCUS :
    Ximp_setFocusMessageHandler( gw, xclme ) ;
    break ;
  case XIMP_UNSETFOCUS :
    Ximp_unsetFocusMessageHandler( gw, xclme ) ;
    break ;
  case XIMP_KEYPRESS :
    Ximp_keypressMessageHandler( gw, xclme ) ;
    break ;
  case XIMP_SETVALUE :
    Ximp_setvalueMessageHandler( gw, xclme ) ;
    break ;
  case XIMP_CHANGE :
    Ximp_changeMessageHandler( gw, xclme ) ;
    break ;
  case XIMP_GETVALUE :
    Ximp_getValueMessageHandler( gw, xclme ) ;
    break ;
  case XIMP_MOVE :
    Ximp_moveMessageHandler( gw, xclme ) ;
    break ;
  case XIMP_RESET :
    Ximp_resetMessageHandler( gw, xclme ) ;
    break ;
  case XIMP_EXTENSION :
    Ximp_extensionMessageHandler( gw, xclme ) ;
    break ;
  default :
    /* もしそれ以外のメッセージが来ていたら、エラーになってしまう。*/
    ximpClient = Ximp_findClient
      ( gw, ( unsigned long )xclme->data.l[ 1 ] ) ;
    if( ximpClient != NULL ){
      ximpClient->xevent = *xclme ;
      Ximp_sendErrorEvent( ximpClient, XIMP_BadProtocol ) ;
    }
    break ;
  }
#ifdef DEBUG
  if( ximpClient != NULL ){
    printf( "<-------- xmask = %lx\n", 
	    ximpClient->attribute_mask ) ;
  }
#endif
  return ;
}

static void Ximp_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 Ximp_SelectionClearEventHandler
( Widget gw, XEvent *xevent, String *params, Cardinal *num_params )
{
  /* 自分自身を破壊して終了する。この時、自分自身が持っていたコール  * 
   * バックを全部破棄しなければならないけど…？ */
#ifdef DEBUG
  fprintf( stderr, "(Ximp)Selection Lost....\n" ) ;
#endif
  XtDestroyWidget( gw ) ;
  return ;
}

/*
 * −−− ここから下はコールバック関数。−−−−
 */
static void Ximp_SendMessageToRequestorCallback
( Widget gw, caddr_t client, caddr_t caller )
{
  struct XimpClient *ximpClient = ( struct XimpClient * )client ;
  XimpWidget w = ( XimpWidget )ximpClient->protocol_widget ;
  struct myChar *text = ( struct myChar * )caller ;
  Atom	proptype ;
  int	propsize ;
  int	propformat ;
  caddr_t propvalue ;
  struct myChar *wptr ;

  /*
   * 内部コードで持っているテキストを Compound Text へと変換する。
   */
  for( wptr = text ; !IS_END_OF_STRING( *wptr ) ; wptr ++ )
    if( IS_ASCII_EQUAL( *wptr, '\r' ) )
      wptr->chara = '\n' ;
  proptype   = w->ximp.compound_text ;
  propformat = 8 ;
  propsize   = string2ctext( text, NULL ) ;
  if ( propsize <= 0 )
    return ;
  propvalue = 
    ( caddr_t )malloc( propsize * sizeof( unsigned char ) * 4 ) ;
  string2ctext( text, ( unsigned char * )propvalue ) ;

  XChangeProperty
    ( XtDisplay( ( Widget )w ), XtWindow( ( Widget )w ),
      ximpClient->property, proptype, propformat,
      PropModeAppend, propvalue, propsize ) ;
  if( !ximpClient->resetting ){
    Ximp_sendClientMessage32
      ( ximpClient, XIMP_READPROP,
	ximpClient->id, ximpClient->property, 0L, 0L ) ;
  }
  free( propvalue ) ;
  return ;
}

static int Ximp_EndOfConversionMain( struct XimpClient *ximpClient )
{
  /* 終了通知を送る。ただし、親が DestroyNotify によって破壊されてい *
   * る場合においてはその限りではない。その場合には req_window ==    *
   * None となっている。*/
  if( ximpClient->focus_window != None ){
    /* クライアントに終了通知を送る。*/
    Ximp_sendClientMessage32
      ( ximpClient, XIMP_PROCESS_END, ximpClient->id, 0L, 0L, 0L ) ;
    remove_myeventhandler
      ( XtDisplay( ximpClient->protocol_widget ),
        ximpClient->focus_window, 
	KeyPress, KeyPressMask ) ;
    remove_myeventhandler
      ( XtDisplay( ximpClient->protocol_widget ),
        ximpClient->focus_window, 
	KeyRelease, KeyReleaseMask ) ;
  } else {
    if( ( ximpClient->focus_window = ximpClient->request_window ) != None )
      Ximp_sendClientMessage32
	( ximpClient, XIMP_END, ximpClient->id, 0L, 0L, 0L ) ;
  }
  /* InputOnly だった場合には、透明な窓が出来ている筈なので、それを */
  /* 消しておく。*/
  if( ximpClient->probe_window != None ){
    remove_allmyeventhandler
      ( XtDisplay( ximpClient->protocol_widget ),
        ximpClient->probe_window ) ;
    XSafeDestroyWindow
      ( XtDisplay( ximpClient->protocol_widget ), ximpClient->probe_window ) ;
    ximpClient->probe_window = None ;
  }
  /* 文字列を変換要求者へ送りつける場合のcallbackを破棄。*/
  XtRemoveAllCallbacks( ximpClient->skkinput, XtNfixNotify ) ;
  /* 変換要求者へ変換の終了を通知する場合のcallbackを破棄。*/
  XtRemoveAllCallbacks( ximpClient->skkinput, XtNendNotify ) ;
  /* キー入力をクライアントウィンドウへと送る callback を破棄。*/
  XtRemoveAllCallbacks( ximpClient->skkinput, XtNkeybackNotify ) ;

  /* 閉じる前にイベントキューをフラッシュしておく。*/
  XSafeFlush( XtDisplay( ximpClient->protocol_widget ) ) ;
  ximpClient->skkinput = NULL ;
  return 0 ;
}

/*
 * skkinput 窓が閉じる時にサーバ側に終了を通知するために呼び出される関数。
 *(コールバック)
 */
static void Ximp_EndMessageToRequestorCallback
( Widget gw, caddr_t client, caddr_t caller )
{
  struct XimpClient *ximpClient = ( struct XimpClient * )client ;
  /* history 情報を記憶するべきか否か。*/
  if( caller != NULL )
    history_close( XtDisplay( ximpClient->protocol_widget ), caller ) ;
  /* 終了する。*/
  Ximp_EndOfConversionMain( ximpClient ) ;
  /* 変換クライアントリストから削除してはならない。何故なら END しても *
   * BEGIN 要求が来るからである。再度 CREATE されることはない。 */
  if( ximpClient->to_destroy ){
    Ximp_freeClient( ximpClient ) ;
  }
  return ;
}

static void Ximp_KeyEventToRequestorCallback
( Widget gw, caddr_t client, caddr_t caller )
{
  struct XimpClient *ximpClient = ( struct XimpClient * )client ;
  XKeyEvent *xkey = ( XKeyEvent * )caller ;

  Ximp_sendClientMessage32
    ( ximpClient, XIMP_KEYPRESS, ximpClient->id,
      ( unsigned long )xkey->keycode, ( unsigned long )xkey->state, 0L ) ;
  return ;
}

/*
 * その他…変換クライアントの操作について。
 */

static struct XimpClient *Ximp_createClient
( Widget gw, Window requestor, XimpInputStyle *xis )
{
  XimpWidget w = ( XimpWidget )gw ;
  struct XimpClient *ximpClient ;
  unsigned char buffer[ 25 ] ;
  int i ;

  if( ( ximpClient = malloc( sizeof( struct XimpClient ) ) ) == NULL )
    return NULL ;

  sprintf( buffer, "_XIMP_STRING_%ld", w->ximp.property_id ++ ) ;
  ximpClient->property = XInternAtom( XtDisplay( gw ), buffer, False ) ;

  ximpClient->id                    = w->ximp.icid ++ ;
  ximpClient->request_window        = requestor ;
  ximpClient->focus_window          = requestor ;
  ximpClient->probe_window          = None ;
  ximpClient->style                 = xis ;
  ximpClient->protocol_widget       = gw ;
  ximpClient->skkinput              = NULL ;
  ximpClient->event_capture_method  = EVENT_HA_FOCUS_KARA ;
  ximpClient->resetting             = False ;
  ximpClient->to_destroy            = False ;
  ximpClient->already_initial_setup = False ;
  ximpClient->attribute_mask        = 0L ;
  ximpClient->client_version        = NULL ;
  ximpClient->xevent.window         = None ;
  ximpClient->resetting             = False ;

  ximpClient->preedit_attributes.fontlist = NULL ;
  ximpClient->status_attributes.fontlist  = NULL ;

  /* fontset の初期化を行う。*/
  for( i = 0 ; i < NUMBER_OF_CHARSET ; i ++ ){
    ximpClient->fontset[ i ] = NULL ;
  }
  ximpClient->next    = w->ximp.client_list ;
  w->ximp.client_list = ximpClient ;
  return ximpClient ;
}

/*
 * 変換クライアントの持つＩＤからクライアントを捜す関数。
 */
static struct XimpClient *Ximp_findClient
( Widget gw, unsigned long id )
{
  XimpWidget w = ( XimpWidget )gw ;
  struct XimpClient *ximpClient ;

  ximpClient = w->ximp.client_list ;
  while( ximpClient != NULL ){
    if( ximpClient->id == id )
      break ;
    ximpClient = ximpClient->next ;
  }
  return ximpClient ;
}

#if 0
/*
 * ウィンドウＩＤから変換クライアントを捜す関数。
 */
static struct XimpClient *Ximp_findClientWithWindowID
( Widget gw, Window win )
{
  XimpWidget w = ( XimpWidget )gw ;
  struct XimpClient *ximpClient ;

  ximpClient = w->ximp.client_list ;
  while( ximpClient != NULL ){
    if( ximpClient->request_window == win ||
	ximpClient->focus_window == win )
      break ;
    ximpClient = ximpClient->next ;
  }
  return ximpClient ;
}
#endif

/*
 * 変換クライアントを処理しているデータ領域を破棄する関数。
 */
static void Ximp_destroyClient( struct XimpClient *ximpClient )
{
  Ximp_unlinkClient( ximpClient ) ;
  Ximp_freeClient( ximpClient ) ;
  return ;
}

static void Ximp_unlinkClient( struct XimpClient *ximpClient )
{
  XimpWidget w = ( XimpWidget )ximpClient->protocol_widget ;
  struct XimpClient *node = w->ximp.client_list ;

  /* その前に落ちてしまったのなら、リストからこっちで省かなければならない。*/
  if( node == ximpClient ){
    w->ximp.client_list = ximpClient->next ;
  } else {
    while( node->next != ximpClient )
      node = node->next ;
    node->next = ximpClient->next ;
  }
  ximpClient->next = NULL ;

  return ;
}

static void Ximp_freeClient( struct XimpClient *ximpClient )
{
  XimpWidget w = ( XimpWidget )ximpClient->protocol_widget ;
  int i ;
 
 /* フォントを解放する。*/
  for( i = 0 ; i < NUMBER_OF_CHARSET ; i ++ ){
    fontMgr_FreeFont
      ( XtDisplay( ( Widget )w ), ximpClient->fontset[ i ] ) ;
    ximpClient->fontset[ i ] = NULL ;
  }
  /* フォント名を解放する。*/
  if( ximpClient->preedit_attributes.fontlist != NULL )
    XFree( ximpClient->preedit_attributes.fontlist ) ;
  if( ximpClient->status_attributes.fontlist != NULL )
    XFree( ximpClient->status_attributes.fontlist ) ;
  /* クライアントのバージョン情報を解放する。*/
  if( ximpClient->client_version != NULL )
    XFree( ximpClient->client_version ) ;
  free( ximpClient ) ;
  return ;
}

static void Ximp_ChangeFocusWindow
( struct XimpClient *ximpClient, Window focus_window )
{
  Window probe ;

  if( ( ximpClient->event_capture_method != EVENT_HA_INPUTONLY &&
	ximpClient->event_capture_method != EVENT_HA_FOCUS_KARA ) ||
      focus_window == None ||
      focus_window == ximpClient->focus_window )
    return ;

  if( ximpClient->event_capture_method == EVENT_HA_INPUTONLY ){
    if( ximpClient->probe_window != None ){
      remove_myeventhandler
	( XtDisplay( ximpClient->protocol_widget ),
	  ximpClient->probe_window, 
	  KeyPress, KeyPressMask ) ;
      remove_myeventhandler
	( XtDisplay( ximpClient->protocol_widget ),
	  ximpClient->probe_window, 
	  KeyRelease, KeyReleaseMask ) ;
      XSafeDestroyWindow
	( XtDisplay( ximpClient->protocol_widget ),
	  ximpClient->probe_window ) ;
      ximpClient->probe_window = None ;
    } 
    probe = XCreateWindow
      ( XtDisplay( ximpClient->protocol_widget ),
	focus_window, 0, 0, 9999, 9999, 0, 0,
	InputOnly, (Visual *)CopyFromParent,
	0L, (XSetWindowAttributes *)NULL ) ;
    ximpClient->probe_window = probe ;
    XMapWindow( XtDisplay( ximpClient->protocol_widget ),  probe ) ;
  } else {
    if( ximpClient->focus_window != None ){
      remove_myeventhandler
	( XtDisplay( ximpClient->protocol_widget ),
	  ximpClient->focus_window, 
	  KeyPress, KeyPressMask ) ;
      remove_myeventhandler
	( XtDisplay( ximpClient->protocol_widget ),
	  ximpClient->focus_window, 
	  KeyRelease, KeyReleaseMask ) ;
    }
    probe = focus_window ;
  }
  add_myeventhandler
    ( XtDisplay( ximpClient->protocol_widget ), probe,
      XtWindow( ximpClient->skkinput ), KeyPress, KeyPressMask ) ;
  add_myeventhandler
    ( XtDisplay( ximpClient->protocol_widget ), probe,
      XtWindow( ximpClient->skkinput ), KeyRelease, KeyReleaseMask ) ;
  return ;
}
