先日の整理してみたおかげで少しすっきりすることができたので、プログラムを作り直してみました。また今回は真っ黒のウィンドウではなくカラフルにしてみたいと思います。そしてキーボードやマウスの入力についても紹介します。
画面の更新
Wayland でウィンドウを作って黒い四角を出している状態が今までのものでした。これの中身を塗ってみます。
・・・と言ってもやることはこれだけならば簡単です。以下の手順で内容を描画して画面に反映できます
- mmap で取得したアドレスから画面サイズ分のピクセルデータを書き込む
- wl_surface_damage 関数で、指定したウィンドウサイズ領域を指定
- wl_surface_commit 関数で反映処理
しかしアニメーションさせる際には wl_surface_frame を使うと良い、というような話を見たのでこちらで実装を行ってみました。
wl_surface_frame 関数では wl_callback のオブジェクトが返ってくるので、このコールバックで処理することでアニメーション処理をやっています。中身としては描画して更新、次の描画更新のためにコールバックを設定、という感じです。
コードとしては以下のようになっています。ここでは抜粋して流れがおおよそわかる程度にしています。
static void frame_redraw( void* data, wl_callback* callback, uint32_t time ) { WaylandCore* core = static_cast(data); wl_callback_destroy( callback ); core->redrawWindow(); } static wl_callback_listener frame_listeners = { frame_redraw, }; wl_callback* callback = wl_surface_frame( surface ); wl_callback_add_listener( callback, &frame_listeners, this );
そして呼び出される redrawWindow が以下のようになっています。再びコールバックを設定しています
void WaylandCore::redrawWindow() { wl_surface* surface; wl_surface_damage( surface, 0, 0, width, HEIGHT ); // 中身の塗りつぶし. wl_callback* callback = wl_surface_frame( surface ); wl_surface_attach( surface, mSurface.buffer, 0, 0 ); wl_callback_add_listener( callback, &frame_listeners, this ); wl_surface_commit( surface ); }
damage の時の範囲指定を行うと、毎回全画面塗りつぶしているだけなのに、冒頭で紹介しているようなカラフルな感じにできたりします。
マウス入力の取得
ウィンドウに対しての入力処理についてです。
Wayland では入力デバイスを取り扱うオブジェクトの上層が seat というものになっているようです。ここから各デバイスに応じてオブジェクトを取得して使う、という感じでコードを作成します。
レジストリを処理している部分で、”wl_seat” が来たときに、別処理を振り分けることからスタートします。
if( strcmp( interface, "wl_seat") == 0 ) { obj = wl_registry_bind( reg, name, &wl_seat_interface, 1 ); mSeat = static_cast(obj); wl_seat_add_listener( mSeat, &seat_listeners, this );
ここでさらにリスナーを登録しています。このリスナーの中でマウスやらキーボードやらの振り分けを行うことになります。マウスの場合だと以下のような感じで wl_pointer というマウスのオブジェクトを取得しています。またリスナー関数も登録して、マウスに関するイベント情報を受け取れるように設定します。
void seat_listener_capabilities( wl_seat* seat, uint32_t caps ) { if( caps & WL_SEAT_CAPABILITY_POINTER ) { wl_pointer* pointer = wl_seat_get_pointer( seat ); wl_pointer_add_listener( pointer, &pointer_listeners, this ); mPointer = pointer; }
リスナー関数は以下の種類があり、全て関数実装しておくことが必要です。量が多くなるためここでは割愛します。
- pointer_handler_enter
- pointer_handler_leave
- pointer_handler_motion
- pointer_handler_button
- pointer_handler_axis
マウスのウィンドウへのインアウト、座標情報、ボタン情報、ホイール情報というイベントごとに処理する関数となっています。
キーボード入力の取得
キーボードもほぼマウスと同じ流れで処理します。 listener_capabilities 関数では以下の条件でキーボードのオブジェクトを取得しています。
void seat_listener_capabilities( wl_seat* seat, uint32_t caps ) { if( caps & WL_SEAT_CAPABILITY_KEYBOARD ) { wl_keyboard* kbd = wl_seat_get_keyboard( seat ); wl_keyboard_add_listener( kbd, &keyboard_listeners, this ); mKeyboard = kbd; }
そしてキーボードのリスナー関数は以下の種類があり、全て関数実装しておくことが必要です。こちらも量が多くなるためここでは割愛します。
- keyboard_handler_keymap
- keyboard_handler_enter
- keyboard_handler_leave
- keyboard_handler_key
- keyboard_handler_modifiers
まとめ
マウスキーボードの情報を得るための道筋を紹介しました。 Windows や X11 のように1つのイベントハンドラで全て処理できる機構になっておらず、複数のハンドラ(リスナー)によって構成されるため、具体的な実装の紹介はここでは割愛しました。しかし全ソースコードについては GitHub のほうで公開します。気になる方はそちらを見てもらえればと思います。
画面の塗りつぶしからの画面反映についても試してみました。部分的に更新することで縞模様にできたりします。 gif アニメにすると余計なゴミが出ていますが、実際に動いているときにはそのようなものにはならないので大丈夫です。
ソースコード
全ソースコードを GitHub のほうにあげてあります。興味のある方はそちらから clone してご確認ください。
今回の内容分については、タグ base-1.0 で参照できるようにしています。
その他
次はウィンドウの移動をと思っていましたが、実は Super キーとの組み合わせでできることがわかりました。Super キーは Windows キーボードだと、Windowsキーが該当します。
- Super + マウス左ボタンドラッグ : ウィンドウの移動
- Super + マウス右ボタンドラッグ : ウィンドウの回転
- Super + ホール回転 : 拡大縮小
- Super + K : アプリケーション終了
これらの点を別途実装が必要かと思っていましたが標準でもできるようだったので、アプリ実装としてはちょっと後回しにしようと思います。それよりはウィンドウ枠などの完成を目指したいと思います。