完全に放置状態だったがぼちぼち再開?
全画面型WMを考える
ここでいう全画面型とはあらゆるウィンドウが画面全体を占めるように配置される(= 常に1つのウィンドウのみが表示される)タイプを指す。非常にマイナーだがこのようなものも存在するらしい。使えそうな環境と利点、欠点を考えてみる。
利点
- Netbookに代表される、ディスプレイ解像度が小さい環境で活躍しそう
- 1024x600だとタイル型WMを用いていても表示できるウィンドウは1枚か2枚がせいぜいなので実は大して操作感は変わらない説
- screenやtmuxとの親和性が高い
- そもそもウィンドウが1枚しか見えないので、端末を複数表示して分けるか、tmuxで分けるか、といった心配をする必要がなくなる
- 実装が楽
- ウィンドウの移動とかリサイズとか面倒なことを一切考えないで良い
欠点
- リッチな環境では性能を持てあます
- 1920x1080だったりすると明らかに無駄が多い
- ウィンドウを複数表示するタイプのアプリケーションとの相性は最悪
- ダイナミック型と違って、Gimpとかは諦める以外の選択肢がない
- 何でもかんでも最大化されることによる弊害
- 例えばスキンを用いるメディアプレーヤーなどで、override_redirect(ウィンドウマネージャに無視させるためのフラグ)が立っていない場合、無理矢理最大化されて目も当てられないことになることが予想できる
- 小さな設定ウィンドウでも引き伸ばされてフルスクリーンになる可能性がある。クライアント側が適切に実装されていないとUIが崩壊する
- これに関してはWM_TRANSIENT_FORを適切に扱えば何とかなるかもしれない
- WM_TRANSIENT_FORはダイアログなど、一時的にトップレベルに出てくるウィンドウに付加されるプロパティ
- 例えばこれが付加されているウィンドウは元のウィンドウサイズを尊重した上で、親ウィンドウに重ねる形で画面中央に配置する、などが考えられる
- これに関してはWM_TRANSIENT_FORを適切に扱えば何とかなるかもしれない
いくつか適当に利点・欠点となりそうな事柄を挙げたが、おそらく他種のWMを完全に置き換えることはできないだろう。画面解像度が控えめの環境で使う分には良いかもしれない(根本的に相容れないものもあるので他のWMを何か用意しておくことが必須ではあるが)。実装面ではかなり楽をできそうなので、とりあえず何かWMを作ってみたいという場合には一番とっつきやすいのかもしれない(ウィンドウの移動とか配置がWMの要である気もするが)。
イベント処理を少しスマートにする
やってきたイベントをswitchで切り分けるのはコードも長くなるしあんまり良くないので、関数ポインタ配列を使うことを考える。ポイントとなるのはXEventの定義。
typedef union _XEvent {
int type; /* must not be changed */
XAnyEvent xany;
XKeyEvent xkey;
XButtonEvent xbutton;
XMotionEvent xmotion;
XCrossingEvent xcrossing;
XFocusChangeEvent xfocus;
XExposeEvent xexpose;
XGraphicsExposeEvent xgraphicsexpose;
XNoExposeEvent xnoexpose;
XVisibilityEvent xvisibility;
XCreateWindowEvent xcreatewindow;
XDestroyWindowEvent xdestroywindow;
XUnmapEvent xunmap;
XMapEvent xmap;
XMapRequestEvent xmaprequest;
XReparentEvent xreparent;
XConfigureEvent xconfigure;
XGravityEvent xgravity;
XResizeRequestEvent xresizerequest;
XConfigureRequestEvent xconfigurerequest;
XCirculateEvent xcirculate;
XCirculateRequestEvent xcirculaterequest;
XPropertyEvent xproperty;
XSelectionClearEvent xselectionclear;
XSelectionRequestEvent xselectionrequest;
XSelectionEvent xselection;
XColormapEvent xcolormap;
XClientMessageEvent xclient;
XMappingEvent xmapping;
XErrorEvent xerror;
XKeymapEvent xkeymap;
long pad[24];
} XEvent;
と、化け物のようなunionになっていて、XNextEventで取得したイベントの中にはこれらのうちどれかが入っている。全てのXなんとかEvent型は先頭にtypeメンバを持っているので、これによって中身が何であるか判別できることが保証されている。これを見て分かるとおり、イベントの数は数えられるほどしかなく、これならば全てのイベントに対するイベントハンドラを保持する関数ポインタ配列を用意しても問題ない。実際、X.hにおいて
#define LASTEvent 36 /* must be bigger than any event # */
というものが定義されており、
void (*handler[LASTEvent])(XEvent *e);
みたいな感じに書くことができる。この場合、各イベントハンドラの基本形は以下のようになる。
void なんとか(XEvent *e)
{
XなんとかEvent *ev = &e->xなんとか;
/* イベントに対する処理 */
}
イベントループは非常に簡潔になる。
int running = 1; /* 実行継続フラグ。イベント処理の結果によっては0になって終了 */
XEvent ev;
while (running) {
XNextEvent(display.d, &ev)
if (handler[ev.type])
handler[ev.type](&ev);
}
実はこれはdwmで使われている方法で、dwmではこれらに加えてC99の記法(指示付き初期化子)を使ってかなりスマートに書かれている。