サイズ: 5250
コメント:
|
サイズ: 4851
コメント:
|
削除された箇所はこのように表示されます。 | 追加された箇所はこのように表示されます。 |
行 41: | 行 41: |
awesomeはイベントループにlibevを使ってあれこれしている<<FootNote(libevはselectとかpollとかepollとかkqueueみたいなののフロントエンドになるライブラリらしい)>>。libevが無くても、libxcb 1.8以降にある`xcb_poll_for_queued_event`が使えれば「キュー内で最後のMotionNotifyだけ処理する」みたいなのは比較的簡単に書ける。他に「座標の変化がしきい値未満だったら捨てる」というアプローチもあり、これは実装がかなり簡単だが、細かい操作が不可能になる問題がある。 | awesomeはイベントループにlibevを使ってあれこれしている<<FootNote(libevはselectとかpollとかepollとかkqueueみたいなののフロントエンドになるライブラリらしい)>>。 |
マウスイベント
Xにおけるマウスイベントは3種類ある。
ButtonPress: ウィンドウ内でボタンが押された
ButtonRelease: ウィンドウ内でボタンが離された
MotionNotify: ウィンドウ内でマウスポインタが移動した
Xには「ダブルクリック」イベントは存在しない。そのため、ダブルクリックを検知したければイベントのタイムスタンプを使って自前で判定する必要がある(GTKとかのツールキットを使うなら、ツールキットがこの辺をやってくれる)。
他所のウィンドウでのマウスイベントの処理
自分で作成したウィンドウについては上記の3イベントを監視すればよいが、そうでないウィンドウについては同じ方法を取ることはできない。というのも
Only one client at a time can select a ButtonPress event, which is associated with the event mask ButtonPressMask. (Xlib - C Language Interface 11.1 Selecting Events より)
となっていて1、基本的に自分が作成したのでないウィンドウについてButtonPressを検知することは不可能と考えられるからである(ウィンドウを作成したクライアント自身がselectする可能性が非常に高い)。
- 例外として、ルートウィンドウについてはWMが自由にできると考えて良さそう
大抵の操作はボタンを押す所から始まるので、これが検知できないとほとんど何もできない。
このままではウィンドウを直接ドラッグして移動やリサイズを行う操作は不可能になってしまう2が、幸いXにはグラブ(Grab)という機能が用意されている。これは要するにイベントの横取りで、マウスをグラブすると、グラブしている間マウスイベントが自分の所にやってくるようになる。グラブし続けている限り他所にはイベントが届かなくなるので、必要でなくなったら直ちにグラブを終了しなければならない。
グラブすることができるのはキーボードかマウスで、さらに2種類に分かれる。
- アクティブなグラブ: 自分で明示的にグラブを開始する
xcb_grab_keyboard()とかxcb_grab_pointer()とか
- 上記のリクエストが実行された時点からグラブが始まり、グラブを終了するまでイベントが届く
- パッシブなグラブ: 特定のキー/ボタンの組み合わせを指定しておき、それが実際に押されたときにグラブを開始する
xcb_grab_key()とかxcb_grab_button()とか
- あらかじめ上記の関数によってグラブするキー/ボタンを登録しておく
指定した組み合わせが押されるとKeyPressとかButtonPressが届く
ウィンドウを直接ドラッグして移動する操作は次のようにして実装できる。
- 対象ウィンドウにパッシブグラブを仕掛けておく
操作が行われるとButtonPressが届く
アクティブグラブを開始し、届いてくるMotionNotifyを用いて移動処理を行う
移動が終了したら(ボタンを離す=ButtonReleaseイベント)、グラブを終了する
MotionNotifyの圧縮
MotionNotifyイベントはマウスポインタがウィンドウ上で移動したときに届く。MotionNotifyは1ピクセルでもマウスポインタが動けば発行されるので、実際には大量のMotionNotifyが連続して届くことになる。これら一つ一つについてウィンドウの移動などを行うとCPUにかなりの負荷を掛けることになる(特にリサイズ時は毎回再描画が発生するので描画が追いつかなくなりがち)。
そこで、連続したMotionNotifyの大部分を捨てるなどして圧縮する方法がよく用いられている3。Xlibだと割と簡単に書ける(XCheckTypedEvent()でイベントキュー内のMotionNotifyだけを拾って片っ端から捨てれば良い)が、XCBだとイベントはキューの先頭から取り出すことしかできない4ので少し面倒になる。
awesomeはイベントループにlibevを使ってあれこれしている5。