$Id: protocol,v 1.2 1998/07/21 08:51:24 ishisone Exp $

** kinput2 プロトコル **


*はじめに

  kinput2 プロトコルはオリジナルの kinput が使用していたプロトコル (kinput 
プロトコルと呼ぶ) に対して上位互換性を持つ。

  kinput プロトコルでは変換開始のリクエスト時にオプションとしてプロパティ
名を指定できるようになっていた。これは将来の拡張のために用意されたものであ
り、プロトコルスペックではここに None を指定するように書かれていたが、将来
はここにあるデータが入ったプロパティ名を指定し、クライアントから変換サーバ
に対して変換の属性などを指定することを狙ったものであった。

  kinput2 プロトコルではこの拡張データの形式を定義し、かつ kinput プロトコ
ルを実装したものと区別がつくよう、プロトコルバージョンを追加した。

  拡張データに指定する変換属性は、ここに定義したスタンダードなものに加えて、
プライベートな拡張が可能なように設計されている。

  さらに変換途中でも属性の変更を伝えられるようにプロトコルの拡張を行なった。


* 概要

  まず、簡単にプロトコルの内容を説明しておくことにする。

  変換サーバは立ち上がると、変換サーバと同じディスプレイを共有する他のクラ
イアントに対して、変換サービスを提供することを宣言する。これにはセレクショ
ンのメカニズムを使用する。つまり、変換サーバは入力言語毎に決められたあるセ
レクションのオーナーとなる。一つのディスプレイで同じセレクション名に対して
複数のオーナーは存在できないため、変換サーバは各言語について一つずつしか存
在できない。

  変換サーバはセレクションのオーナーとなると同時に、サポートする入力スタイ
ルなどの情報をオーナーウィンドウのプロパティに格納する。

  ある言語の変換サービスを受けたいクライアントは、まずその言語に対応するセ
レクションのオーナーを探す。もしオーナーが見つかれば、まずプロパティをチェッ
クし、自分のリクエストしようとしている入力スタイルなどを変換サーバがサポー
トしているかをチェックすることができる。その後、オーナーに対して変換入力を
リクエストする。これはオーナーのウィンドウに対して ClientMessage イベント
を送ることで行なわれる。

  このリクエストイベントには、クライアントの希望する入力スタイルや、使用す
るフォントなどさまざまな属性が書かれたプロパティ名が含まれている。変換サー
バは、このイベントを受けとると同時にそのプロパティの内容を調べて変換の属性
を設定する。変換属性には次のようなものがある。

	* 変換テキストの表示開始位置
	* 表示フォント
	* 表示色

  その後変換をスタートさせ、同時にクライアントに対して変換サービスを始めた
ことを ClientMessage イベントで通知する。

  変換はすべて変換サーバ側で行なわれ、クライアントは変換結果が返ってくるの
を待つだけである。ただし変換中にフォントが変わったりして変換の属性が変化し
た場合には ClientMessage イベントを利用してそれを変換サーバに伝えることが
できる。

  一つ問題となるのは変換サーバの入力イベントの取得方法である。変換中はクラ
イアントにいく入力イベントを変換サーバが横取りしなくてはならない。これには 
3 種類の方法があり、クライアントは変換属性としてどれを使用するか指定するこ
とができる。

  一つは、kinput プロトコルで採用されていた、変換サーバがクライアントのウィ
ンドウの上に InputOnly ウィンドウを作り、そのウィンドウで入力イベントを
「盗む」方法である。クライアントにとってはこれが一番処理が楽 (自分では何も
しなくてもよい) が、フォーカスの関係で、変換サーバにキーイベントが来ないこ
とがある。

  次は変換サーバがクライアントのウィンドウ (正確にはフォーカスウィンドウ) 
で入力イベントをセレクトするという方法である。この場合イベントは変換サーバ
とクライアントの両方に送られるので、この方式を選択したクライアントは、変換
中は入力されたイベントを無視しなくてはならない。

  3番目は何もしない、という方法で、つまりこれは変換中はクライアントはフォー
カスウィンドウに来たキーイベントをすべて変換サーバに SendEvent で送る、と
いうことである。

  変換サーバは変換された文字列を適宜クライアントに送ることができる。これは
クライアントのウィンドウのプロパティに文字列を書き込むことで実現する。

  変換が終了すると、変換サーバはクライアントに ClientMessage を送り、知ら
せる。クライアントからも変換サーバに対して ClientMessage イベントを送って
変換の終了をリクエストすることができる。この場合も変換サーバは変換が終了し
たことをクライアントに知らせる。


* プロトコル詳説

  概要に引続きここでは kinput2 プロトコルについて kinput プロトコルと比べ
ながら解説していく。

* 変換サーバスタートアップ

** セレクション

  変換サーバは、クライアントに対して変換サービスを提供することを示すために
セレクションオーナーとなる。これは kinput プロトコルと全く同じだが、セレク
ションアトムとしては "<language>_CONVERSION" ではなく、正式に登録されたセ
レクション名である "_<language>_CONVERSION" (日本語であれば
"_JAPANESE_CONVERSION") を使用することにする。ただしバックワードコンパチビ
リティのため、オリジナル kinput が使用していたセレクション名を使うことも認
められる。

  変換サーバは SetSelectionOwner を利用してセレクションオーナーになる。

    SetSelectionOwner
	selection:  "_<language>_CONVERSION"
	owner:  変換サーバのウィンドウID
	time:  timestamp または CurrentTime

クライアントは GetSelectionOwner により、変換サーバの存在とウィンドウID を
知ることができる。

    GetSelectionOwner
	selection:  "_<language>_CONVERSION"
    =>
	owner:  変換サーバのウィンドウID


*** SelectionClear イベントの扱いについて

  もし変換サーバがセレクションオーナーになった後 SelectionClear イベントを
受けとった時には、現在変換中のクライアントがあればそれらすべてに対して変換
終了の処理、つまり、もしまだクライアントに送っていない変換テキストがあれば
送り、その後変換終了の ClientMessage イベントを送出する、という処理を行な
い、その後すみやかに exit する。

** 変換プロファイル

  これだけではクライアントは変換サーバが kinput のプロトコルをインプリメン
トしたものなのか、それとも kinput2 プロトコルをインプリメントしたものなの
かがわからない。また、変換サーバがどのような入力スタイルをサポートしている
かもわからない。

  そこでセレクションのオーナーウィンドウにそれらの情報が書かれたプロパティ
を置くことにする。

    ChangeProperty
	window:  セレクションのオーナーウィンドウ
	property:  Atom "_CONVERSION_PROFILE"
	type:  Atom "_CONVERSION_ATTRIBUTE_TYPE"
	format:  32
	mode:  Replace
	data:  後述

  これを変換プロファイルと呼ぶことにする。変換プロファイルが存在するかどう
かで、クライアントは変換サーバが kinput2 プロトコルをサポートしているのか、
それとも kinput のプロトコルしかサポートしていないのかを知ることができる。

  クライアントは GetProperty を使用してこのデータを読み出すことができる。

    GetProperty
	window:  セレクションのオーナーウィンドウ
	property:  Atom "_CONVERSION_PROFILE"
	type:  Atom "_CONVERSION_ATTRIBUTE_TYPE"
	format:  32
	delete:  False

変換プロファイルのフォーマットが 32 であることからもわかるように、このプロ
パティの内容は 32bit 値の配列である。変換プロファイルに含まれる各々の情報
は、情報の種類とデータ長を表す1ワードのヘッダとそれに続くデータ (データは
なくてもよい) から構成される。ヘッダの上位 16bit がその情報の種類を表し、
下位 16 bit は続くデータのワード数 (32bit 単位) を表す。

	+----------------+----------------+
	|  Code (16bit)  | Length (16bit) |
	+----------------+----------------+
	|              Data1              |
	+---------------------------------+
	|              .....              |
	+---------------------------------+
	|              DataN              |
	+---------------------------------+

定義されている情報は次の4つである。

  + Protocol Version (code = 1, length = 1)
	変換サーバが使用しているプロトコルのバージョン名を示す。データには
	プロトコルのバージョン名そのものではなく、それをアトム化した値が入
	る。このドキュメントは "PROTOCOL-2.0" というバージョンについて記述
	している。

  + Supported Styles (code = 2, length = 1)
	変換サーバがサポートしている入力スタイルを示す。定義されている入力
	スタイルは次の通りである。
		root window style:	1
		off-the-spot style:	2
		over-the-spot style:	4
	データには変換サーバがサポートするスタイルの bitwise-or 値が入る。
	
  + Supported Extensions (code = 3, length = N)
	変換サーバがサポートしている拡張を示す。データにはサポートしている
	拡張名をアトム化した値が入る。複数の拡張を入れることができる。

  + Extension Data (code = 4, length = N)
	拡張特有のプロファイルデータを示す。従ってここでデータの内容につい
	て記述することはできないが、ただ一つ決まっていることはデータの先頭
	はその拡張名をアトム化した値でなければならないということだけである。
	残りのデータの内容は完全に各拡張の自由であり、ここでは規定しない。

  クライアント側の無用の混乱を防ぐため、Protocol Version と Supported
Stylesの項目は必ずなければならない。また、Extension Data 以外はプロファイ
ルデータの中に同じコードのデータが複数あってはならない。


* 変換のリクエスト

  変換のリクエストの方法はオリジナルのプロトコルと同じである。つまり、クラ
イアントはまず XGetSelectionOwner() を使用して変換サーバを探す。見つかれば
オーナーウィンドウに対して変換開始をリクエストするイベントを送る。

    SendEvent
	destination:  セレクションオーナーウィンドウ
	propergate:  False
	event-mask:  NoEventMask
	event:  ClientMessage
		window: セレクションオーナー
		type: Atom "CONVERSION_REQUEST"
		format: 32
		data:
			l[0]: セレクションアトム
			l[1]: クライアントウィンドウID
			l[2]: 変換テキストのエンコーディングアトム
			l[3]: 変換テキスト用プロパティ or None
			l[4]: プロパティ名か None

  data.l[0] はセレクションアトムを入れる。data.l[1] はクライアントウィンド
ウID である。クライアントウィンドウは変換サーバが各変換セッションを区別す
るのに使われる。これは同一のクライアントウィンドウで複数の変換を同時に行な
うことができないことを意味する。

  data.l[2] には、変換結果文字列のエンコーディングとしてクライアントの希望
するエンコーディングのアトムを入れる。例えば COMPOUND_TEXT である。変換文
字列はプロパティでクライアントに送られるので、このエンコーディングというの
は実はそのプロパティのタイプである。ただしこれはあくまでも変換サーバに対す
るヒントであって、変換サーバはこれと異なるエンコーディングを使用するかもし
れない。ただし、変換サーバは COMPOUND_TEXT エンコーディングだけはサポート
しなくてはならない。つまり、もしクライアントが COMPOUND_TEXT エンコーディ
ングを希望した場合には、そのエンコーディングで文字列が返されることが保証さ
れる。

  data.l[3] には変換結果文字列を入れるプロパティ名を指定する。変換結果は
data.l[1] で指定したウィンドウの、data.l[3] で指定されるプロパティに送られ
る。もしプロパティ名として None を指定した場合、プロパティ名は変換サーバに
よって決定される。

  kinput プロトコルと異なる点は data.l[4] である。オリジナルではここには 
None を指定することになっていた。変換サーバは、もしここが None であればク
ライアントが kinput プロトコルを使用するものとして扱う。None でなければ、
これは変換属性のはいっているプロパティ名である。これはクライアントウィンド
ウ (data.l[1] に指定したもの) のプロパティで、タイプは変換プロファイルと同
じく "_CONVERSION_ATTRIBUTE_TYPE" である。従ってこの変換属性プロパティの構
造も変換プロファイルプロパティと同じで、1 ワードのヘッダとそれに続く 0 ワー
ド以上のデータとからなる。変換サーバは GetProperty を用いてこの変換属性を
読み出す。

    GetProperty
	window: クライアントウィンドウ
	property:  "CONVERSION_REQUEST" で指定されたプロパティ
	type:  Atom "_CONVERSION_ATTRIBUTE_TYPE"
	format:  32
	delete:  False


* 変換属性

  変換プロファイルと同じく、変換属性プロパティも各々の情報は、情報の種類と
データ長を表す 32bit 1ワードのヘッダとそれに続くデータ(データはなくてもよ
い) から構成される。ヘッダの上位 16bit が属性コードを表し、下位 16bit は続
くデータのワード数 (32bit 単位) を表す。

  属性コードとして 0 から 65535 までの値が使用できるが、これを次のような 2
つの領域に分ける。

	0 - 255:	標準属性コード
	256 - 65535:	拡張で使用する属性コード

さらに標準属性コードは2つの領域に分けられる。

	0 - 127:	変換の途中で値を変えてもよい属性のコード
	128 - 255:	変換開始時にしか設定できない属性のコード

定義されている属性を示す。

  + No Operation (code = 0, length = N)
	何もしない。length は任意なのでプロパティのある部分をスキップさせ
	るのに便利。
	
  + Indirect Attribute (code = 1, length = 1)
	属性の間接指定である。データにはプロパティ名が入っており、そのプロ
	パティに従って属性を設定する。指定したプロパティはクライアントウィ
	ンドウのプロパティでなければならない。CONVERSION_ATTRIBUTEイベント 
	(後述) で複数の属性データを設定したい時や、イベントに属性データが
	入り切らない時に使用する。

  + Focus Window (code = 2, length = 1)
	フォーカスウィンドウを指定する。データにはフォーカスウィンドウのウィ
	ンドウID が入る。この属性が指定されなかった時はクライアントウィン
	ドウがフォーカスウィンドウとなる。

  + Spot Location (code = 3, length = 1)
	スポットロケーションを指定する。ベースラインの開始点で指定する。デー
	タの上位 16bit が x 座標、下位 16bit が y 座標である。この属性は 
	over-the-spot style の変換の時しか意味を持たない。

  + Client Area (code = 4, length = 2)
	変換テキスト表示領域を指定する。データは 32bit 2ワードからなり、最
	初のワードの上位 16bit / 下位 16bit が領域の左上の x / y 座標、次
	のワードの上位 16bit / 下位 16bit が領域の幅と高さを表す。デフォル
	トはフォーカスウィンドウ全体である。

  + Status Area (code = 5, length = 2)
	ステータス表示領域を指定する。データのフォーマットは前と同じ。

  + Colormap (code = 6, length = 1)
	カラーマップを指定する。データにはカラーマップID が入る。デフォル
	トはスクリーンのデフォルトカラーマップである。

  + Color (code = 7, length = 2)
	フォアグラウンドとバックグラウンドのピクセル値を指定する。データは
	2ワードからなり、最初のワードがフォアグラウンドのピクセル値、次の
	ワードがバックグラウンドのピクセル値である。指定されなかった場合、
	変換サーバは適当な値を使用する。

  + Background Pixmap (code = 8, length = 1)
	バックグラウンドの Pixmap を指定する。データには Pixmap ID が入る。

  + Line Spacing (code = 9, length = 1)
	行間を指定する。ベースライン間の距離で指定する。指定されなかった場
	合、変換サーバは次の使用されるフォントから適当な値を算出して使用す
	る。

  + Font Atoms (code = 10, length = N)
	使用するフォントを指定する。データには各フォントの "FONT" プロパティ
	(これはそのフォントの正式名をアトム化したものになっている) のリス
	トを指定する。リスト中の順番は任意である。変換サーバは与えられたフォ
	ントのプロパティからキャラクタセットを調べ、必要なものを利用するか
	らである。指定されなかった場合、変換サーバは適当なフォントを使用す
	る。

  + Cursor (code = 11, length = 1)
	カーソルを指定する。データにはカーソル ID が入る。指定されなかった
	場合、変換サーバは適当なカーソルを使用する。

以降は変換開始時にしか指定できない属性である。

  + Input Style (code = 128, length = 1)
	入力方法を指定する。値は
		root window style:	1
		off-the-spot style:	2
		over-the-spot style:	4
	のどれかでなければならず、変換プロファイルの Supported Styles に入っ
	ているスタイルでなければならない。この属性が指定されなかった時のデ
	フォルトは Root Window Style である。

  + Event Capture Method (code = 129, length = 1)
	クライアントウィンドウからのイベントの取得方法を指定する。イベント
	の取得方法は3種類あり、それぞれデータに指定する値は
		なにもしない:			0
		InputOnly ウィンドウによる方法:	1
		フォーカスウィンドウをセレクト:	2
	である。デフォルトは InputOnly ウィンドウによる方法である。

  + Use Extension (code = 130, length = N)
	この属性設定で使用される拡張を指定する。ここで指定する拡張はサーバ
	がサポートしているもの、つまりプロファイルデータ中のSupported
	Extensions に書かれた拡張でなければならない。また、この設定は使用
	する拡張属性コードが属性データ中に現れる前になければならない。


* 変換開始の通知

  変換のリクエストを受けた変換サーバは変換属性の読み出しなど必要な処理を行
なった後、クライアントに対して変換開始の通知をするとともに実際の変換作業を
始める。

  変換開始の通知は ClientMessage イベントにより行なう。

    SendEvent
	destination:  クライアントウィンドウ
	propergate:  False
	event-mask:  NoEventMask
	event:  ClientMessage
		window: クライアントウィンドウ
		type: Atom "CONVERSION_NOTIFY"
		format: 32
		data:
			l[0]: セレクションアトム
			l[1]: 変換テキストのエンコーディングアトム
			l[2]: 変換テキスト用プロパティ or None
			l[3]: 変換ウィンドウID 
			l[4]: unused

  data.l[1] は変換文字列をクライアントに送る時の文字列のエンコーディングで
ある。data.l[2] はその文字列が送られるプロパティであり、
"CONVERSION_REQUEST" でクライアントが指定した場合にはそれがそのまま入り、
None を指定した場合には変換サーバは適当なプロパティを選んでそれを入れる。

  もし、なんらかの理由で変換サーバがそのクライアントのための変換サービスを
提供できない場合には data.l[2] には None を入れる。

  data.l[3] は実際に変換処理が行なわれるウィンドウID を指定する。これは、
クライアントが自分のところに来たキーイベントを変換サーバに送る時にその送り
先として使用するためのものである。

  このイベントを送った時にはすでに変換サーバは属性プロパティの内容を読んで
しまっているので、このイベントを受けとったクライアントは変換属性プロパティ
を消したり内容を変更したりしてよい。


* 変換属性の変更

  変換開始が通知された後、変換が終了するまでの間に変換属性の変化が起こった
場合、クライアントはそれを変換サーバに伝えることができる。

  クライアントは変換サーバに ClientMessage イベントを送ることで変換属性の
変化を通知する。

    SendEvent
	destination:  セレクションオーナーウィンドウ
	propergate:  False
	event-mask:  NoEventMask
	event:  ClientMessage
		window: セレクションオーナーウィンドウ
		type: Atom "CONVERSION_ATTRIBUTE_NOTIFY"
		format: 32
		data:
			l[0]: セレクションアトム
			l[1]: クライアントウィンドウID
			l[2]-l[4]: 属性データ

  data.l[2] から data.l[4] に変更された属性データを入れる。属性データの形
式は変換属性のプロパティの形式と全く同じである。ただし見ればわかるようにイ
ベントに入れられる属性データの大きさは最大3ワードである。従って変更された
属性データがこれより大きいとか、変更された属性データが複数あるとき (この場
合にはこのイベントを複数回送ってもいいのだが) には属性データはプロパティに
入れ、Indirect Attribute の機能を使ってそのプロパティを指定すればよい。


* 変換文字列の送信

  確定された変換文字列は、随時変換サーバからクライアントにプロパティを使用
して送られる。

    ChangeProperty
	window:  クライアントウィンドウ
	property:  "CONVERSION_NOTIFY" で指定したプロパティ
	type:  "CONVERSION_NOTIFY" で指定したエンコーディング
	format:  エンコーディングによる
	mode:  Append
	data:  変換文字列

  クライアントはあらかじめクライアントウィンドウの PropertyNotify イベント
をセレクトしておき、プロパティが変更されたら変換結果を取り出すようにする。

    GetProperty
	window:  クライアントウィンドウ
	property:  "CONVERSION_NOTIFY" で指定されたプロパティ
	type:  "CONVERSION_NOTIFY" で指定されたエンコーディング
	format: エンコーディングによる
	delete: True


* 変換終了の通知

  変換サーバは変換が終了するとクライアントに ClientMessage を送ってそれを
通知する。

    SendEvent
	destination:  クライアントウィンドウ
	propergate:  False
	event-mask:  NoEventMask
	event:  ClientMessage
		window: クライアントウィンドウ
		type: Atom "CONVERSION_END"
		format: 32
		data:
			l[0]: セレクションアトム
			l[1]: セレクションオーナーウィンドウID
			l[2]-l[4]: unused

* 変換終了のリクエスト

  クライアント側から変換サーバに対して変換の終了をリクエストすることもでき
る。クライアントは変換サーバに次のような ClientMessage を送る。

    SendEvent
	destination:  セレクションオーナーウィンドウ
	propergate:  False
	event-mask:  NoEventMask
	event:  ClientMessage
		window: セレクションオーナー
		type: Atom "CONVERSION_END_REQUEST"
		format: 32
		data:
			l[0]: セレクションアトム
			l[1]: クライアントウィンドウID
			l[2]-l[4]: unused

  変換サーバはこのイベントを受けとると、まだクライアントに送っていない変換
文字列があればそれを送り、その後 CONVERSION_END の ClientMessage を送る。


* 拡張について

  拡張には属性の拡張とプロトコル拡張とがある。属性の拡張は変換属性の追加で
あり、プロトコル拡張は変換サーバとクライアントの通信に新たなメッセージを追
加することである。もちろん一つの拡張がこの両方を含んでいてもよい。

  どちらのタイプの拡張をするにしてもまずその拡張に名前をつけなくてはならな
い。そしてその拡張を実装した変換サーバは変換プロファイルの Supported
Extensionsにその拡張名を入れ、クライアントに対してその拡張を実装しているこ
とを宣言しなくてはならない。

  拡張を使おうとするクライアントはまず変換サーバの変換プロファイルを調べ、
サーバがその拡張をサポートしているか確かめる必要がある。もしサーバがその拡
張をサポートしていなければ、そのサーバに対してその拡張を使ってはならない。

  属性の拡張の場合、新たに拡張属性コードを決めなくてはならない。このコード
についてはそのコードの範囲が 256 から 65535 でなければならないということを
除き、このプロトコルでは一切規定しない。拡張コードはこの範囲で勝手にコード
を決めることができる。この場合、複数の拡張で使うコードが重複することが考え
られる。しかしこの事態を避ける手段は一切提供されない。もし二つの拡張で定義
されたコードが重なってしまった場合にはその二つの拡張は同時に使えないだけの
ことである。

  このような不幸な事態を避けるために、拡張をしたらその拡張名とその拡張の詳
細を発表するのがよいだろうと思われるが、どこにどう発表するかなどの詳細は明
らかにプロトコルで規定すべきものではないのでここには書かない。だいたいこの
プロトコルを拡張しようなどというものはほとんどいないであろうが。
