ウィンドウ配置に関しては色々と考慮しなければならないことが多い。 = フルスクリーン/最大化 = フルスクリーンと最大化は似ているが異なる。共通している点は「ウィンドウを可能な範囲内いっぱいに表示する」という点だが、この「可能な範囲」がそれぞれの場合で異なっている。 == フルスクリーン == * こちらの方が単純で、本当に画面全体に広げる * ウィンドウの装飾(タイトルバーとか枠)は除去する == 最大化 == * 基本的に画面全体に広げるが、タスクバーやdockがある場合は被らないようにするのが一般的 * ウインドウの装飾は残す(枠については除くWMもある) ウィンドウを広げてよい範囲という意味では、タイル化も最大化に近い(単にウィンドウ数が1かそれ以上かという違い)。 = _NET_WM_STRUT = 上述の通り、最大化/タイル化の際にはタスクバーやdockの類を避けないといけない。そのためにはそれらのウィンドウが画面上のどの領域を占めているのかについての情報が必要で、それを表すプロパティがEWMHの`_NET_WM_STRUT(_PARTIAL)`である。`_PARTIAL`の方が後から出てきたもので、より詳しい指定が可能となっている。そのため、`_NET_WM__STRUT_PARTIAL`がある場合は`_NET_WM_STRUT`を無視しなければならない。 `_NET_WM_STRUT_PARTIAL`はこんな感じになっている。大きい四角が画面で、画面端の長方形がタスクバー/dock。 {{attachment:net_wm_strut.jpg}} 各所の値は次のような意味である: * 画面端からの距離(left,right,top,bottom) * 始点と終点はどこか(例: lsy<> = left_start_y, bex = bottom_end_x。他も同じ要領) `_NET_WM_STRUT`は`NET_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自体には「デスクトップ」という概念はない。なので、デスクトップ機能を提供するクライアントが必ずどこかに居る(ここでいう「デスクトップ機能」は壁紙を設定<>したりアイコン等を表示する機能のことで、WMが提供する仮想デスクトップとは別。Windowsだとエクスプローラが担当している)。たいていはファイルマネージャがこの役割を担っている(NautilusとかKonquererとかthunarとか)。 デスクトップ機能を提供する上で最大の問題となるのはルートウィンドウが好き勝手にいじれるものではないということで、例えばルートウィンドウ上に何かを描画しても他のウィンドウによってすぐに消されるし、マウスイベントを拾おうにも他のクライアントに取られているかもしれない<>。アイコンを表示する方法として、直接描画する代わりに「アイコンウィンドウ」を作って配置する方法もあるが、増えてくると恐ろしく非効率になる<>。 以上の問題を解決する方法として、画面と同サイズの「デスクトップウィンドウ」を作り、その中にアイコンを描画する方法が用いられている。自前のウィンドウなのでマウスイベントも確実に拾える。 デスクトップは以上のようにして実現されているが、「デスクトップウィンドウ」はWMから見れば他のウィンドウと変わらない。そのため下手をすればタイトルバーがついて移動/リサイズができたり、下手をすると最小化されてしまう可能性すらある。加えて、デスクトップが他のウィンドウより前面に出てきたりすると最早訳が分からない<>。 このような問題が発生しないように、WMに対して「デスクトップウィンドウである」ことを明示するのが`_NET_WM_WINDOW_TYPE_DESKTOP`である。このタイプのウィンドウについては次のように扱う必要がある。 * 一切の装飾を施さない * 常に他の全てのウィンドウより下層に置く * ユーザによる移動やリサイズを認めない これを実現する一番簡単な方法は「完全に無視する(WMの管理下に置かない)」ことである。この手のウィンドウは自分で最下層に行くようなので、放っておけば常に最下層に置かれる<>。また、無視すれば装飾をすることもないし、移動やリサイズの対象にも成り得ない。 == _NET_WM_WINDOW_TYPE_DOCK == dockあるいはパネル機能を提供することを表す。STRUTプロパティを持っているのは99%このタイプと言ってよさそう(だが、このプロパティを持っていることとSTRUT自体は全く別の要素)。この手のウィンドウは次のように扱うのが自然。 * 一切の装飾を施さない * ユーザによる移動やリサイズを認めない(大抵の場合dock/パネル自身が配置オプションを持っていて、自分で勝手に移動する) さらに、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_MODEL`と`WM_TRANSINT_FOR`の組み合わせで表す。 == その他 == その他いろいろあるが、override_redirectなウィンドウ<>に対するものが多いのでとりあえずは考えなくて大丈夫。