ログイン
編集不可のページディスカッション情報添付ファイル

2013-03-01 13:38:13時点のリビジョン3

メッセージを消す
clear/wm_devel/2013-02-19

MMA

マウスイベント

Xにおけるマウスイベントは3種類ある。

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する可能性が非常に高い)。

大抵の操作はボタンを押す所から始まるので、これが検知できないとほとんど何もできない。

このままではウィンドウを直接ドラッグして移動やリサイズを行う操作は不可能になってしまう2が、幸いXにはグラブ(Grab)という機能が用意されている。これは要するにイベントの横取りで、マウスをグラブすると、グラブしている間マウスイベントが自分の所にやってくるようになる。グラブし続けている限り他所にはイベントが届かなくなるので、必要でなくなったら直ちにグラブを終了しなければならない。

グラブすることができるのはキーボードかマウスで、さらに2種類に分かれる。

ウィンドウを直接ドラッグして移動する操作は次のようにして実装できる。

  1. 対象ウィンドウにパッシブグラブを仕掛けておく
  2. 操作が行われるとButtonPressが届く

  3. アクティブグラブを開始し、届いてくるMotionNotifyを用いて移動処理を行う

  4. 移動が終了したら(ボタンを離す=ButtonReleaseイベント)、グラブを終了する

MotionNotifyの圧縮

MotionNotifyイベントはマウスポインタがウィンドウ上で移動したときに届く。MotionNotifyは1ピクセルでもマウスポインタが動けば発行されるので、実際には大量のMotionNotifyが連続して届くことになる。これら一つ一つについてウィンドウの移動などを行うとCPUにかなりの負荷を掛けることになる(特にリサイズ時は毎回再描画が発生するので描画が追いつかなくなりがち)。

そこで、連続したMotionNotifyの大部分を捨てるなどして圧縮する方法がよく用いられている3。Xlibだと割と簡単に書ける(XCheckTypedEvent()でイベントキュー内のMotionNotifyだけを拾って片っ端から捨てれば良い)が、XCBだとイベントはキューの先頭から取り出すことしかできない4ので少し面倒になる。

awesomeはイベントループにlibevを使ってあれこれしている5

  1. この制限自体は必要だろう (1)

  2. タイトルバーをドラッグする場合、タイトルバーはWMが作るので問題ない (2)

  3. tinywmですら実装している (3)

  4. 加えて、XCBはイベント構造体を毎回mallocして返してくるのでちゃんとfreeしてやらないといけない (4)

  5. libevはselectとかpollとかepollとかkqueueみたいなののフロントエンドになるライブラリらしい (5)