ログイン
編集不可のページディスカッション情報添付ファイル
clear/wm_devel/2013-03-12

MMA

ウィンドウ配置に関しては色々と考慮しなければならないことが多い。

フルスクリーン/最大化

フルスクリーンと最大化は似ているが異なる。共通している点は「ウィンドウを可能な範囲内いっぱいに表示する」という点だが、この「可能な範囲」がそれぞれの場合で異なっている。

フルスクリーン

最大化

ウィンドウを広げてよい範囲という意味では、タイル化も最大化に近い(単にウィンドウ数が1かそれ以上かという違い)。

_NET_WM_STRUT

上述の通り、最大化/タイル化の際にはタスクバーやdockの類を避けないといけない。そのためにはそれらのウィンドウが画面上のどの領域を占めているのかについての情報が必要で、それを表すプロパティがEWMHの_NET_WM_STRUT(_PARTIAL)である。_PARTIALの方が後から出てきたもので、より詳しい指定が可能となっている。そのため、_NET_WM__STRUT_PARTIALがある場合は_NET_WM_STRUTを無視しなければならない。

_NET_WM_STRUT_PARTIALはこんな感じになっている。大きい四角が画面で、画面端の長方形がタスクバー/dock。

net_wm_strut.jpg

各所の値は次のような意味である:

_NET_WM_STRUTNET_STRUT_PARTIALから始点と終点の情報を除いたもの(left,right,top,bottomだけ)である。この場合、タスクバーやdockの幅/高さは画面の幅/高さと一致する。

写真の例だと、最大化で広げてよい範囲は(left,top)-(W-right,H-bottom)の範囲となる(W,Hは画面の幅と高さ)。この例から分かる通り、実は最大化やタイル化だけなら始点や終点の情報は必要ない(空いていても使えない)。

画面全体からSTRUTで指定された領域を除いたものがワークエリア(_NET_WM_WORKAREA)となる。デスクトップにアイコンを表示したりする場合はワークエリアがアイコンを置いて良い範囲、ということになる。

タスクバーやdockと一般のウィンドウが被っても良いか、というのはWMのポリシーによる(一般的には被らせない)。被らせないようにするなら、初期配置からユーザの操作による移動/リサイズに至るまでSTRUTを考慮する必要がある。

_NET_WM_WINDOW_TYPE

ウィンドウの機能的な種類を示すプロパティとしてEWMHには_NET_WINDOW_TYPEが用意されている。これによって、特別な役割を持つウィンドウは特別に扱う、ということが可能になる。十数個定義されているので重要な(特別に対処しないとまずい/したほうが良い)ものだけ触れる。

_NET_WM_WINDOW_TYPE_DESKTOP

デスクトップ機能を提供することを表す。これについてはXにおけるデスクトップがどうなっているかを確認する必要がある。

そもそもX自体ではUIの詳細が一切規定されていないので、X自体には「デスクトップ」という概念はない。なので、デスクトップ機能を提供するクライアントが必ずどこかに居る(ここでいう「デスクトップ機能」は壁紙を設定2したりアイコン等を表示する機能のことで、WMが提供する仮想デスクトップとは別。Windowsだとエクスプローラが担当している)。たいていはファイルマネージャがこの役割を担っている(NautilusとかKonquererとかthunarとか)。

デスクトップ機能を提供する上で最大の問題となるのはルートウィンドウが好き勝手にいじれるものではないということで、例えばルートウィンドウ上に何かを描画しても他のウィンドウによってすぐに消されるし、マウスイベントを拾おうにも他のクライアントに取られているかもしれない3。アイコンを表示する方法として、直接描画する代わりに「アイコンウィンドウ」を作って配置する方法もあるが、増えてくると恐ろしく非効率になる4

以上の問題を解決する方法として、画面と同サイズの「デスクトップウィンドウ」を作り、その中にアイコンを描画する方法が用いられている。自前のウィンドウなのでマウスイベントも確実に拾える。

デスクトップは以上のようにして実現されているが、「デスクトップウィンドウ」はWMから見れば他のウィンドウと変わらない。そのため下手をすればタイトルバーがついて移動/リサイズができたり、下手をすると最小化されてしまう可能性すらある。加えて、デスクトップが他のウィンドウより前面に出てきたりすると最早訳が分からない5

このような問題が発生しないように、WMに対して「デスクトップウィンドウである」ことを明示するのが_NET_WM_WINDOW_TYPE_DESKTOPである。このタイプのウィンドウについては次のように扱う必要がある。

これを実現する一番簡単な方法は「完全に無視する(WMの管理下に置かない)」ことである。この手のウィンドウは自分で最下層に行くようなので、放っておけば常に最下層に置かれる6。また、無視すれば装飾をすることもないし、移動やリサイズの対象にも成り得ない。

_NET_WM_WINDOW_TYPE_DOCK

dockあるいはパネル機能を提供することを表す。STRUTプロパティを持っているのは99%このタイプと言ってよさそう(だが、このプロパティを持っていることとSTRUT自体は全く別の要素)。この手のウィンドウは次のように扱うのが自然。

さらに、EWMHには典型的な扱いとして「他のウィンドウより上層に置く(覆い隠されないように)」ということも書いてある。

装飾や移動/リサイズを認めないということからデスクトップウィンドウのように完全に無視してしまえば良い…と考えられるが、それでは上手く行かない。 何故ならこれらのウィンドウはほとんどの場合STRUTプロパティを持っていて、これの変更(PropertyNotify)を追跡する必要があるからである。他のウィンドウより前面に配置することを考える場合もやはり無視するわけには行かない(新しいウィンドウは普通最前面に出てくる)。dock/パネルは無視しつつ、クライアントウィンドウの集合とは別に「STRUTを持つウィンドウ」の集合を持つ手もあるにはあるが、色々とややこしくなる(主にイベント処理が)し、最前面に配置する場合はどうしても無視できない。

ということでこのタイプのウィンドウを管理する…ことを考えだした途端に恐ろしく面倒になる。

などなど、あらゆる面で普通のクライアントと異なる扱いをしないといけない。

_NET_WM_WINDOW_TYPE_SPLASH

スプラッシュウィンドウであることを示す。eclipseとかlibreofficeとかgimpを起動したときに出てくるアレのことである。とりあえず

などの対処をするのが自然。基本的にすぐ消えるウィンドウなので、デスクトップウィンドウと同様に完全に無視してしまって構わないはず。

_NET_WM_WINDOW_TYPE_DIALOG

ダイアログボックスであることを示す。_NET_WM_WINDOW_TYPEが設定されていない場合でも、WM_TRANSIENT_FORプロパティを持つものはこのタイプとして扱う。対処としては、本体であるウィンドウ(WM_TRANSIENT_FORが指すウィンドウ。親ではない)の近くに置くのが一般的。

X自体にはダイアログボックスのモーダル/モードレスの概念が存在しない(そもそもダイアログボックスという概念自体、ツールキットのレベルまで上がらないとないので当然)。モーダルなウィンドウは_NET_WM_STATE_MODELWM_TRANSINT_FORの組み合わせで表す。

その他

その他いろいろあるが、override_redirectなウィンドウ7に対するものが多いのでとりあえずは考えなくて大丈夫。

  1. よく見たら写真はlsxになってる気がするが気にしない方向で (1)

  2. xsetrootとかdisplayとかnitrogenみたいな壁紙だけを設定するものはまた別 (2)

  3. ButtonPressは1つのウィンドウに対して1つのクライアントしか拾うことができない (3)

  4. アイコンの形に応じた非矩形ウィンドウにする場合は特に (4)

  5. デスクトップウィンドウを感知しないWM(ほとんどのminimalisticなWM)だと実際にそういうことになる (5)

  6. 逆にクライアントウィンドウを最下層に移動するときは注意が必要で、単にXLowerWindow()してはいけない。evilwmは「一番下にあるクライアントウィンドウの下に移動する」方法を取っていた (6)

  7. 通常WMにリダイレクトされる要求がリダイレクトされない(要するに完全に自分で配置を制御する)ウィンドウで、例としてはごく短時間だけ存在するウィンドウ(かな漢字変換の候補ウィンドウとか、通知ウィンドウとか)や単独で存在するツールバーなど。コンポジット型でないWMはこの手のウィンドウを完全に無視する (7)

clear/wm_devel/2013-03-12 (最終更新日時 2013-03-12 13:35:03 更新者 clear)