Login
Immutable PageDiscussionInfoAttachments
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が連続して届くことになる。3これら一つ一つについてウィンドウの移動などを行うとCPUにかなりの負荷を掛けることになる(特にリサイズ時は毎回再描画が発生するので描画が追いつかなくなりがち)。

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

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

MotionNotify Hint

大量のMotionNotifyが要らない、という場合は監視対象のイベントマスクにPointerMotionMaskと共にPointerMotionHintMaskをORしておく7MotionNotifyの届き方が変わり、以下の条件を満たすまでの間に一つだけのMotionNotifyが届くようになる。

このとき届くMotionNotifyはあくまで「動いた」ことを表すだけなので、実際のマウスポインタ位置はクライアント側でリクエストを送って取得する必要がある。

ただし、ヒントでない真のMotionNotifyが届く可能性もある8。どのようにするかはXサーバの自由、ということになっている。

ヒントが届いたタイミングでQueryPointerするとそれによって次のヒントが届く条件が満たされるので、結局ヒントを使わない場合とあまり変わらない気がする…のだが違いはあるのだろうか。むしろ、QueryPointerリクエストが行って帰ってくる分負荷が増える気もする。

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

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

  3. MotionNotifyがどの程度来るか、ということは決まっているわけではないが、最低1つ届くことは保証されている。手元の環境で試した所、ゆっくり動かすと1ピクセル単位でイベントが来るが、高速に動かすと飛び飛びに来る (3)

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

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

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

  7. Xlibでの話。XCBならXCB_EVENT_MASK_POINTER_MOTIONとXCB_EVENT_MASK_POINTER_MOTION_HINT (7)

  8. ヒントかどうかはイベン トの中身で判別可能。ただ、手元で試した限りではヒントしか届かない (8)

clear/wm_devel/2013-02-19 (last edited 2013-03-01 15:48:26 by clear)