サイズ: 4715
コメント:
|
サイズ: 6571
コメント:
|
削除された箇所はこのように表示されます。 | 追加された箇所はこのように表示されます。 |
行 37: | 行 37: |
MotionNotifyイベントはマウスポインタがウィンドウ上で移動したときに届く。MotionNotifyは1ピクセルでもマウスポインタが動けば発行されるので、実際には大量のMotionNotifyが連続して届くことになる。これら一つ一つについてウィンドウの移動などを行うとCPUにかなりの負荷を掛けることになる(特にリサイズ時は毎回再描画が発生するので描画が追いつかなくなりがち)。 | MotionNotifyイベントはマウスポインタがウィンドウ上で移動したときに届く。MotionNotifyは1ピクセルでもマウスポインタが動けば発行されるので、実際には大量のMotionNotifyが連続して届くことになる。<<FootNote(MotionNotifyがどの程度来るか、ということは決まっているわけではないが、最低1つ届くことは保証されている)>>これら一つ一つについてウィンドウの移動などを行うとCPUにかなりの負荷を掛けることになる(特にリサイズ時は毎回再描画が発生するので描画が追いつかなくなりがち)。 |
行 39: | 行 39: |
そこで、連続したMotionNotifyの大部分を捨てるなどして圧縮する方法がよく用いられている<<FootNote(tinywmですら実装している)>>。Xlibだと割と簡単に書ける(`XCheckTypedEvent()`でイベントキュー内のMotionNotifyだけを拾って片っ端から捨てれば良い)が、XCBだとイベントはキューの先頭から取り出すことしかできない<<FootNote(加えて、XCBはイベント構造体を毎回`malloc`して返してくるのでちゃんと`free`してやらないといけない)>>ので少し面倒になる。awesomeはこの辺りをちゃんと実装しているので参考になる。 | そこで、連続したMotionNotifyの大部分を捨てるなどして圧縮する方法がよく用いられている<<FootNote(tinywmですら実装している)>>。Xlibだと割と簡単に書ける(`XCheckTypedEvent()`でイベントキュー内のMotionNotifyだけを拾って片っ端から捨てれば良い)が、XCBだとイベントはキューの先頭から取り出すことしかできない<<FootNote(加えて、XCBはイベント構造体を毎回`malloc`して返してくるのでちゃんと`free`してやらないといけない)>>ので少し面倒になる。 awesomeはイベントループにlibevを使ってあれこれしている<<FootNote(libevはselectとかpollとかepollとかkqueueみたいなののフロントエンドになるライブラリらしい)>>。 == MotionNotify Hint == 大量のMotionNotifyが要らない、という場合は監視対象のイベントマスクにPointerMotionMaskと共にPointerMotionHintMaskをORしておく<<FootNote(Xlibでの話。XCBならXCB_EVENT_MASK_POINTER_MOTIONとXCB_EVENT_MASK_POINTER_MOTION_HINT)>>とMotionNotifyの届き方が変わり、以下の条件を満たすまでの間に一つだけのMotionNotifyが届くようになる。 * キーやマウスボタンの状態が変わる * マウスポインタが対象ウィンドウから出る * クライアントがQueryPointerかGetMotionEventのいずれかのリクエストを送信する このとき届くMotionNotifyはあくまで「動いた」ことを表すだけなので、実際のマウスポインタ位置はクライアント側でリクエストを送って取得する必要がある。 * QueryPointer: 現在位置の取得 * GetMotionEvents: 指定したタイムスタンプ間の移動履歴を取得する ただし、ヒントでない真のMotionNotifyが届く可能性もある<<FootNote(ヒントかどうかはイベン トの中身で判別可能。ただ、手元で試した限りではヒントしか届かない)>>。どのようにするかはXサーバの自由、ということになっている。 ヒントが届いたタイミングでQueryPointerするとそれによって次のヒントが届く条件が満たされるので、結局ヒントを使わない場合とあまり変わらない気がする…のだが違いはあるのだろうか。 |
マウスイベント
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が連続して届くことになる。3これら一つ一つについてウィンドウの移動などを行うとCPUにかなりの負荷を掛けることになる(特にリサイズ時は毎回再描画が発生するので描画が追いつかなくなりがち)。
そこで、連続したMotionNotifyの大部分を捨てるなどして圧縮する方法がよく用いられている4。Xlibだと割と簡単に書ける(XCheckTypedEvent()でイベントキュー内のMotionNotifyだけを拾って片っ端から捨てれば良い)が、XCBだとイベントはキューの先頭から取り出すことしかできない5ので少し面倒になる。
awesomeはイベントループにlibevを使ってあれこれしている6。
MotionNotify Hint
大量のMotionNotifyが要らない、という場合は監視対象のイベントマスクにPointerMotionMaskと共にPointerMotionHintMaskをORしておく7とMotionNotifyの届き方が変わり、以下の条件を満たすまでの間に一つだけのMotionNotifyが届くようになる。
- キーやマウスボタンの状態が変わる
- マウスポインタが対象ウィンドウから出る
クライアントがQueryPointerかGetMotionEventのいずれかのリクエストを送信する
このとき届くMotionNotifyはあくまで「動いた」ことを表すだけなので、実際のマウスポインタ位置はクライアント側でリクエストを送って取得する必要がある。
QueryPointer: 現在位置の取得
GetMotionEvents: 指定したタイムスタンプ間の移動履歴を取得する
ただし、ヒントでない真のMotionNotifyが届く可能性もある8。どのようにするかはXサーバの自由、ということになっている。
ヒントが届いたタイミングでQueryPointerするとそれによって次のヒントが届く条件が満たされるので、結局ヒントを使わない場合とあまり変わらない気がする…のだが違いはあるのだろうか。
この制限自体は必要だろう (1)
タイトルバーをドラッグする場合、タイトルバーはWMが作るので問題ない (2)
MotionNotifyがどの程度来るか、ということは決まっているわけではないが、最低1つ届くことは保証されている (3)
tinywmですら実装している (4)
加えて、XCBはイベント構造体を毎回mallocして返してくるのでちゃんとfreeしてやらないといけない (5)
libevはselectとかpollとかepollとかkqueueみたいなののフロントエンドになるライブラリらしい (6)
Xlibでの話。XCBならXCB_EVENT_MASK_POINTER_MOTIONとXCB_EVENT_MASK_POINTER_MOTION_HINT (7)
ヒントかどうかはイベン トの中身で判別可能。ただ、手元で試した限りではヒントしか届かない (8)