X Window System コアプロトコル
X Window System コアプロトコル(英: X Window System core protocol)とは、X Window Systemの基本プロトコルである。X Window System はビットマップ・ディスプレイのためのネットワーク型ウィンドウシステムであり、UNIX系などのオペレーティングシステムのグラフィカルユーザインタフェースの基盤となっている。X Window System はクライアントサーバモデルに基づき、サーバがディスプレイやキーボード/マウスといった入出力ハードウェアを制御する。アプリケーションプログラムは全てクライアントとして動作し、サーバ経由でユーザーや他のクライアントとやり取りする。このやり取りを規定するのが X Window System コアプロトコルである。他にも X Window System に関連したプロトコルはあるが、それらには X Window System コアプロトコル上に構築されたものと全く別個のものがある。Xコアプロトコルと呼ばれることもある。
X Window System コアプロトコルでは、パケットは要求(Request)、応答(Reply)、イベント(Event)、エラー(Error)の4種類しかなく、非同期にネットワーク上を転送される。要求パケットはクライアントからサーバに何らかの操作を依頼し(例えば、新たなウィンドウの生成)、結果としてデータを返してもらうものである。応答パケットは、そのようなデータをサーバからクライアントに返すためにある。イベントパケットは、サーバからクライアントにユーザーの操作やその他の事象の発生を知らせる。エラーパケットは、サーバからクライアントに対して、要求パケットの内容を処理しているときにエラーが発生したことを知らせる。要求パケットによって、応答パケット、イベントパケット、エラーパケットが生成される可能性がある。しかし、それ以外ではプロトコル上はパケットの送受信の順序は指定されていない。コアプロトコルへの拡張がいくつか存在し、それぞれに固有の要求/応答/イベント/エラーパケットが定義されている。
X は1984年、マサチューセッツ工科大学(MIT)で生まれた(2006年現在のリリース X11 は 1987年9月に登場)。設計者 Bob Scheifler と Jim Gettys は設計にあたっての原則として、コアプロトコルは「機構を生成するものであって、ポリシーを生成するものではない」とした。結果として、コアプロトコルにはクライアント間やクライアントとユーザー間のやり取りが具体的には示されていない。これらのやり取りに関する規定は別途行う必要があり、ICCCM や freedesktop.org の仕様が作成された。また、特定のウィジェット・ツールキットを使うことで必然的にやり取りが規定される。
サーバとクライアント間の通信は、伝送路上でパケットを交換することでなされる。コネクションはクライアントが確立する(クライアントの起動についてはプロトコル上規定されていない)。クライアントは最初のパケットを送信する。これには、使用するエンディアン、プロトコルのバージョン、クライアントがサーバに期待する認証方式が指定されている。サーバは、コネクションを受け付けるか否かを示した応答パケットを送り返すか、認証のためのパケットを送る。コネクションが受け付けられる場合、その後のやり取りでクライアントが使うべきデータを格納したパケットが送られる。
コネクションが確立すると、クライアントとサーバ間で以下の四種類のパケットが交換される。
要求(Request): クライアントがサーバから情報を要求するか、サーバに何らかの実行を要求する。
応答(Reply): サーバへの要求に対する応答。全ての要求パケットに対して応答パケットが生成されるわけではない。
イベント(Event): サーバがクライアントに対して、キーボードやマウスからの入力、ウィンドウの移動、リサイズ、前面への露出などのイベントを知らせる。
エラー(Error): 要求が不正だった場合、サーバはエラーパケットを送る。要求はキューイングされるので、要求に対するエラーは即座に返ってくるとは限らない。
要求パケットと応答パケットは様々な長さのものがあるが、イベントパケットとエラーパケットは32バイトで固定されている。
要求パケットにはサーバ側で受信した時点で逐次的な番号が振られる。あるクライアントからの最初の要求パケットには 1、2番目のパケットには 2 といったようになる。この番号の最下位ビットから16ビットぶんが応答パケットやエラーパケットに格納され、どの要求に対するものかを示すようになっている。また、イベントパケットにも同様の情報が格納されており、イベント発生時に処理中(あるいは直前まで処理していた)の要求パケットの番号が示されている。
一般にグラフィカルユーザインタフェースで「ウィンドウ」と呼ぶものを、X Window System では「トップレベルウィンドウ」と呼ぶ。いわゆる「親ウィンドウ」の「サブウィンドウ」と呼ばれるウィンドウ内ウィンドウも「ウィンドウ」と呼ぶ。ボタン、メニュー、アイコンなどのグラフィカルな構成要素は、実際にはサブウィンドウとして認識されている。
クライアントはウィンドウ生成を要求できる。より正確に言えば、既存のウィンドウのサブウィンドウの生成を要求できる。結果として、クライアントが生成するウィンドウ群は一種の木(階層)を形成する。この木の根をルートウィンドウと呼び、サーバが起動したときに自動的に生成される特殊なウィンドウである。他の全ウィンドウはルートウィンドウの直接的または間接的なサブウィンドウである。トップレベルウィンドウは、ルートウィンドウの直接のサブウィンドウである。見た目で言えば、ルートウィンドウは画面全体と同じ大きさであり、他のウィンドウ群の一番背後にある。
ウィンドウの内容は常に保持されるとは限らない。特に、ウィンドウが移動されたり、リサイズされたり、他のウィンドウの影に隠れたり、完全に(あるいは部分的に)見えなくなったりすると、ウィンドウの内容は破壊される可能性がある。Xサーバが、ウィンドウの内容の「バッキングストア」を管理していない場合はウィンドウの内容は失われる。クライアントはバッキングストアの保持を要求することができるが、サーバが必ずそうするという保証はない。従って、クライアントはバッキングストアを当てにしてはいけない。見える状態のウィンドウの中身が不明の場合、そのクライアントに対してイベントが送られ、内容の再描画が行われる。
ウィンドウには、一連の「属性」があり、ウィンドウの大きさと位置、背景画像、バッキングストア要否などの情報が格納されている。これら属性を調べたり、変更したりする要求パケットが存在する。
ウィンドウは InputOutput と InputOnly のどちらかである。画面に表示され、何らかの内容が描画されるものは前者である。後者のウィンドウは画面には現れず、入力を受け取るためだけに存在する。
ウィンドウの外枠とタイトルバー(ボタンも含むことがある)はウィンドウマネージャが作成するものであって、そのウィンドウを作成したクライアントは関知しない。ウィンドウマネージャは、それらの部分についての入力も受け取り、ウィンドウのリサイズなどに対応する。クライアントは一般にウィンドウマネージャによるウィンドウの変化は関知しない。しかし、最近のウィンドウマネージャの多くはトップレベルウィンドウの親ウィンドウをルートウィンドウ以外に設定変更することが多く、この変更をクライアントが無視することはできない。Xコアプロトコルの観点では、ウィンドウマネージャもクライアントの一種であり、他のアプリケーションと何ら違いはない。
ウィンドウに関する情報は xwininfo プログラムを実行することで得られる。引数に -tree を付与すると、ウィンドウの親子関係をツリー形式で表示し、それぞれの識別子や位置データも表示する。
ピクスマップとは、描画に使われるメモリ領域である。ピクスマップの内容がそのままウィンドウとして表示されるわけではなく、ピクスマップの内容(の一部)がウィンドウに転送されたり、逆にウィンドウからピクスマップに内容が転送されたりする。これによりダブルバッファリングなどの技法が可能となっている。ウィンドウ上で可能なグラフィカルな操作の多くは、ピクスマップ上でも可能である。
ウィンドウとピクスマップを総称して「描画領域; drawables」と呼ぶ。描画領域の内容データはサーバ側に置かれる。クライアントはサーバに対して描画領域の内容を転送してもらうこともできるし、逆に内容をサーバに送ることもできる。
クライアントはいくつかのグラフィック操作を要求できる。例えば、領域の生成、領域のコピー、点/直線/多角形/テキストの描画などである。これらの操作は全て、任意の描画領域(ウィンドウもピクスマップも)に対して実施可能である。
グラフィック操作要求の多くは、「グラフィックコンテキスト」を含む。これは、グラフィック操作のパラメータを格納した構造体である。グラフィックコンテキストには、前景色、背景色、テキストのフォント、その他のグラフィックパラメータが含まれる。グラフィック操作を要求する場合、クライアントはグラフィックコンテキストを作成しなければならない。グラフィックコンテキストの全パラメータがグラフィック操作に影響するわけではない。例えば、直線描画要求では、フォント指定は影響しない。
Xコアプロトコルでは、サーバ側のフォントの使用を扱う。その場合のフォントはとして格納されており、サーバはローカルなに直接アクセスする場合もあるし、「フォントサーバ」と呼ばれるプログラムにネットワーク経由でアクセスする場合もある。クライアントは、そのサーバで利用可能なフォント一覧を要求でき、フォントのロード(まだロードされていない場合)やアンロード(他のクライアントが使っていない場合)を要求できる。クライアントはフォントについての汎用情報を要求でき、特定のテキストを特定のフォントで描画するときに必要なスペースを問い合わせることもできる。
フォント名はXコアプロトコルのレベルでは任意の文字列に過ぎない。X Logical Font Description Conventions により、フォントの属性に対応した命名規則が規定されている。この命名規則では、オプション的な属性も考慮するようになっている。
xlsfonts プログラムは、サーバが格納しているフォントの一覧を表示する。xfontsel プログラムは、フォントの字体を表示し、フォント名をコピー・アンド・ペーストで別のウィンドウに入力できる。
近年では、クライアント側でフォントを用意することが多くなり、サーバ側フォントの使用が廃れつつある。クライアント側フォントは、クライアント側でレンダリングするもので、XftやCairoライブラリを使ったり、XRender拡張を使ったりする。Xコアプロトコルにはクライアント側フォントに関する規定は存在しない。
ウィンドウ、ピクスマップ、フォントなどに関するデータは全てサーバに格納されている。クライアントはそれらオブジェクトの識別子を知っており、サーバにそれらについて要求する際に名前として使用する。例えば、クライアントがウィンドウを生成したいとき、サーバに対して識別子を指定してウィンドウ生成を要求する。その識別子は後で、例えばウィンドウの描画時などクライアントからの要求で使用される。以下のオブジェクトはサーバ側にあり、識別子を使ってクライアントから指定される。
Window(ウィンドウ)
Pixmap(ピクスマップ)
Font(フォント)
Colormap (色表、後述)
Graphic context(グラフィックコンテキスト)
これらのオブジェクトを「リソース; resources」と呼ぶ。クライアントがこのようなリソースの生成を要求する場合、その識別子を常に指定する。例えば、新しいウィンドウを生成する場合、クライアントはそのウィンドウの属性(親、幅、高さなど)とウィンドウに関連付ける識別子を指定する。
識別子は32ビット整数であり、最上位ビットから3ビットは常にゼロとされる。クライアントごとに識別子の集合を持ち、新しいリソース生成時に使用する。この集合はサーバがコネクションの受理を知らせるパケット内で、2つの整数としてクライアントに通知される。クライアントは指定された範囲から、重ならないように識別子を選択して使う。ウィンドウ、ピクスマップ、フォント、カラーマップ、グラフィックコンテキストについて、2つのオブジェクトが同じ識別子を持つことはできない。
あるリソースが生成されると、その識別子をクライアントが使い、サーバに対してそのリソースに関する何らかの操作を要求するときに指定する。操作には、リソースを変化させるもの(例えば、ウィンドウを移動させる要求)と、サーバからそのリソースに関するデータを得るもの(例えば、ウィンドウの属性を要求する場合)がある。
識別子はクライアント内だけでなく、サーバ内で一意である。例えば、2つのウィンドウがそれぞれ別のクライアントが生成したものであっても、同じ識別子を持つことはない。クライアントは識別子さえ指定できれば、任意のオブジェクトにアクセスでき、例えば別のクライアントが生成したリソースも識別子が分かっていればアクセスできる(サーバから与えられた範囲外の識別子をサーバへの要求パケットに指定可能である)。
結果として、同じサーバに接続された2つのクライアントがあるとき、識別子によって一意にリソースが指定される。例えば、あるクライアントが識別子 0x1e00021 のウィンドウを生成し、その番号 0x1e00021 を別のアプリケーションに渡したとする(手段は何でも良い。例えば、ファイルに番号を書き込み、それを別のアプリケーションが読むという方法でもよい)。すると、その別のアプリケーションが同じウィンドウに対して操作可能となる。これは例えば Ghostviewの X 版で利用されている。Ghostview はサブウィンドウを生成し、その識別子を環境変数にセットして Ghostscript を呼び出す。Ghostscript は PostScript ファイルの内容をそのウィンドウに描画する。
リソースは、それを生成したクライアントがサーバとのコネクションをクローズしたときに正常に削除(破壊)される。ただし、コネクションをクローズする前にクライアントから破壊を要求することもできる。
イベントとは、サーバからクライアントに送信されるパケットであり、クライアントが興味を持ちそうな事象が発生したことを通知するのに使われる。例えば、ユーザがキーを押下したり、マウスのボタンをクリックしたなどの事象である。イベントはユーザの入力だけではない。例えば、新たなサブウィンドウが生成された場合などにもイベントパケットが送信される。
イベントは常にいずれかのウィンドウに関連付けられている。例えば、マウスカーソルがあるウィンドウ上にあるときにクリックした場合、そのイベントはそのウィンドウと関連付けられる。イベントパケットにはそのウィンドウの識別子も含まれる。
クライアントはサーバに対してイベントパケットを他のクライアントに送るよう要求できる。これはクライアント間の通信に利用される。例えばマウスクリックで選択されたテキストをクライアントが要求する場合に、そのようなイベントが使われる。そのイベントは、その選択を保持しているウィンドウを制御しているクライアントに送られる。
Expose イベントは、ウィンドウの一部の内容が破壊された領域が画面上見えるようになったとき送信される。ウィンドウの内容はいくつかの条件が重なると破壊される。例えば、ウィンドウが別のウィンドウに隠され、サーバがバッキングストアを保持していなかった場合などである。サーバが Expose をクライアントに送信すると、クライアントはその部分を再描画しなければならない。
多くのイベントは、クライアントがそれについて興味があることを事前に表明していないと送信されない。これは、クライアントが必ずしも全てのイベントを必要としないためである。例えば、キーボード入力は受け付けるが、マウス関連のイベントは受け付けないといった状況が考えられる。イベントには、クライアントが明示的に要求していなくても必ず送信されるものもある。
クライアントは、ウィンドウの属性としてどのイベントを受け付けるかを設定できる。例えば、Expose イベントはウィンドウの再描画が必要な場合に送られるが、クライアント側でこれを受け付けない状態にすることもできる。これは、ウィンドウのイベントマスク属性を適切に設定することでなされる。
複数のクライアントが同じウィンドウのイベントを要求することもできる。その場合クライアント毎に異なるイベントマスクを設定できる。例えば、あるクライアントはキーボードのイベントだけを受け付け、別のクライアントがマウスのイベントだけを受け付けるといったことが可能である。サーバは各ウィンドウについてクライアント毎のイベントマスクを保持できるようになっている。ただし、ウィンドウ毎に1つのクライアントにしか送信できないイベントも存在する(特にマウスクリックについてのイベントやウィンドウ管理に関連した変化を知らせるイベント)。
xev プログラムは、ウィンドウに対応したイベントを表示する。特に xev -id WID とすれば、指定した識別子 WID に関わるイベントを全て表示する。
以下は、黒い四角形が描画されたウィンドウを生成し、キー押下で終了するクライアントとサーバ間のやり取りを例示したものである。この例では、クライアントの要求が応答を必要とするものではないため、サーバ側から応答パケットは送信されない。ただし、エラーパケットが生成される可能性はある。
クライアントがサーバとのコネクションを開き、使用するバイトオーダー(エンディアン)を指定した初期パケットを送信する。
サーバがコネクションを受理し(この例では認証は省略されている)、適切なパケットで応答する。このパケットにはルートウィンドウの識別子(例えば 0x0000002b)とこのクライアントが使える識別子の範囲などの情報が格納されている。
クライアントが識別子 0x00200000 でデフォルトのグラフィックコンテキスト生成を要求する(この要求パケットでは、この例に出てくる他の要求パケットと同じように、応答パケットは返信されない)。
クライアントがトップレベルウィンドウ(すなわち、親ウィンドウとしてルートウィンドウ 0x0000002b を指定)の生成を要求する。このとき、識別子 0x00200001、サイズ 200×200、位置 (10,10) などを指定。
クライアントがウィンドウ 0x00200001 の属性変更を要求し、興味のあるイベントとして Expose と KeyPress を受け付けることを宣言する。
クライアントがウィンドウ 0x00200001 の画面表示を要求する。
ウィンドウが画面に表示され、内容を描画しなければならなくなると、サーバはクライアントに Expose イベントを送信する。
このイベントを受けて、クライアントはウィンドウ 0x00200001 とグラフィックコンテキスト 0x00200000 を指定して PolyFillRectangle 要求を送信し、四角形の描画を要求する。
このウィンドウが別のウィンドウに覆われ、再び前面に戻った場合、バッキングストアをサーバが保持していないと、次のようになる。
サーバがクライアントに Expose イベントを送り、ウィンドウを再描画する必要があることを知らせる。
クライアントは PolyFillRectangle 要求を再度送信することでウィンドウを再描画する。
キーが押下されると、次のようになる。
サーバはクライアントに KeyPress イベントを送り、ユーザーがキーを押下したことを知らせる。
クライアントは、それに適切に対応する(この例の場合、終了する)。
プロトコルレベルでは、色は32ビットの符号なし整数で表され、ピクセル値(pixelvalue)と呼ばれる。以下のような要素から色の表現が影響を受ける。
カラーマップ(colormap)- 色を赤、緑、青 (RGB) の強さで表したテーブル
visual type - 上記テーブルを使ってどう色を表現するかを指定したもの
最も容易な場合、カラーマップは各行にRGBの値が格納されたテーブルとなる。ピクセル値 x は、そのテーブルのx-行目の色を意味する。クライアントがカラーマップの内容を変更できる場合、この表現方法は PseudoColor visual class である。StaticColor visual class では、クライアントがカラーマップの内容を変更できない。
visual class は全部で6種類存在する。それぞれ、RGB値とピクセル値の対応付け方法が異なる。そのうち、2つが上述の PseudoColor と StaticColor である。他に GrayScale と StaticGray があり、これらは白、黒、灰色のみが表示可能である(前者ではクライアントによるカラーマップ変更が可能)。
残る2種類の visual class はこれらとは異なり、ピクセル値がそのままRGB値に対応している。この場合のピクセル値からRGB値への変換は次の通り。
ピクセル値をビット列としてみる。
このビット列を3つの部分に分割する。
それら3つの部分がそれぞれ整数として認識され、それぞれ異なる3つのテーブルのインデックス値として使われる。
この場合、カラーマップは赤/緑/青の光の三原色それぞれに対応した3つのテーブルで構成される。結果として得られるのは三原色それぞれの強さである。このような表現を使う visual class として DirectColor と TrueColor があり、前者はクライアントがカラーマップを変更でき、後者は変更できない。
いずれの機構でも、色をピクセル値で表現するには追加のパラメータが必要である。それらパラメータが visual type として集約されており、visual class の種類、その他のパラメータが格納されている。Xサーバには利用可能な visual type が固定個存在し、それぞれに識別子が割り当てられている。その識別子は32ビットの符号なし整数だが、リソースやアトムの識別子と重なっていても構わない。
クライアントからのコネクションを受理したとき、サーバが送信する受理パケットには、一連のブロックが含まれ、各ブロックは1つの画面(スクリーン)に対応している。各画面に対応したブロックは、さらにその画面でサポートしている色深度ごとのブロックに分かれている。色深度ごとのブロックは可能な visual type のリストを含む。まとめると、各画面には複数の色深度が対応し、その色深度ごとに複数の visual type が対応する。ある visual type は異なる色深度の複数の画面で使える場合もある。
それぞれの visual type について、受理パケットには識別子と実際のパラメータ(visual class その他)が格納されている。後でこの情報を要求できないので、クライアントはこの情報を格納しておく。さらに言えば、クライアントは visual type を変更したり、新たに生成したりできない。新たなウィンドウ生成の要求時、そのウィンドウの色を表現するのに使う色深度と visual type の識別子を指定する。
カラーマップは、その画面を制御するハードウェア(例えばビデオカード)がカラーパレットを使っているかどうかとは無関係に使われる。ハードウェアがパレットを使う場合は、常に制限されたカラーマップがインストールされ、無制限にピクセル値とRGB値の対応付けを増やすことはできない。クライアントはサーバに対してカラーマップのインストールを要求できるが、その場合、別のカラーマップのアンインストールが必要となる。これは、画面を見たとき、アンインストールされたカラーマップを使っていたウィンドウの色が変に表示されるという結果をもたらす(この現象を color flashing あるいは technicolor と呼ぶ)。この問題は標準カラーマップ(standard colormap)を使うことで解決する。標準カラーマップはピクセル値と色の事前に決められた対応付けをする。このため、異なるアプリケーション間で標準カラーマップを共用することが可能となっている。
カラーマップ生成はICCCMで規定されている。標準カラーマップは ICCCM でも規定されているし、Xlib 仕様にも定義されている。
アトム(Atom)とは、文字列を表す32ビット整数である。文字列が任意の長さであるのに対してアトムは常に32ビット整数であり、プロトコル設計者は文字列を短い固定サイズで表すためにアトムを導入した。アトムは短いため、同じ文字列をパケットとして何度も送信するような場合に利用される。これによってネットワークの利用効率が向上する。アトムはまた、イベントパケットのような固定サイズのパケットで利用される。
正確には、アトムはサーバが保持する文字列の識別子である。リソース(ウィンドウやピクスマップ)の識別子に似ているが、2つの点で異なる。まず第一に、アトムの識別子はサーバが設定するのであって、クライアントは指定できない。つまり、クライアントが新たなアトム生成を要求するときは、サーバに文字列を送信するだけであり、識別子は指定しない。サーバはその文字列に識別子を割り当て、クライアントへの応答パケットでそれを返す。第二に、アトムは特定のクライアントとは関連しない。アトムは、生成されるとクライアントが終了しても存続し、サーバが終了するまで残る(リソースのデフォルトの振る舞いはそれとは異なる)。
アトムは識別子であるから、一意である。しかし、アトムとリソースの識別子は同じになることがある。アトムに対応した文字列は「アトム名; atom name」と呼ばれる。アトム名は生成後に変更することはできないし、二つのアトムが同じアトム名となることもない。従って、アトム名はアトムを指すのにも使える。「アトム ABCD」と言った場合、正確には「文字列 ABCD に対応するアトム」あるいは「名前が ABCD のアトム」を意味する。クライアントは、新たなアトム生成を要求することもできるし、ある文字列のアトム(識別子)を要求することもできる(これらは同じ意味である)。事前定義されたアトムもある(特定の識別子と文字列でサーバが生成する)。
アトムは様々な用途で使われ、主に同じサーバに接続した複数のクライアント間の通信でよく使われる。特に、後述するウィンドウのプロパティに関連して使われる。
サーバ内に保持されている全アトムの一覧は xlsatoms プログラムを使って表示できる。特に、このプログラムはそれぞれのアトム(識別番号)とその名前(対応する文字列)を表示できる。
各ウィンドウには、事前定義された属性群とプロパティ群があり、サーバに全て格納され、クライアントからは適当な要求でアクセスできる。属性はウィンドウに関するデータであり、サイズ、位置、背景色などがある。プロパティはウィンドウにアタッチされた任意のデータである。属性とは対照的に、プロパティはXコアプロトコルのレベルでは何の意味もない。クライアントはウィンドウのプロパティとして任意のデータを格納できる。
プロパティには、名前、データ型、値がある。プロパティは命令型プログラミングにおける変数に似ていて、クライアントが名前とデータ型を指定して生成でき、任意の値を格納できる。プロパティはウィンドウに対応しているため、同じプロパティ名を複数のウィンドウそれぞれで使い、それぞれ異なる型の異なる値を格納できる。