OSのメッセージ処理とゲーム実装部分分離についての参考のためにSDLを解析したときのメモを公開します。
注意点
昔Blogに書いた記事の転載です。そのため、SDLのバージョンはリビジョン7724(2013/8/31更新とかなり古い)を参考にしているので現状の実装と異なる場合があります。
ウィンドウプロシージャ周りの挙動はWindowsOSを前提としています。
対象
基本的なC/C++の知識があること。
OSが介在するプログラミングの知識があること。
基本的なSDL/SDL2のプログラミングの知識があること。
SDLのポーリング
SDLではOSが送ってくるウィンドウメッセージをいったんSDL側で全て吸収してSDL独自形式に変換した上で、SDL_PollEvent関数を使用してユーザー側に渡して処理を記述させている。
そのウィンドウメッセージをどう処理しているのかコードを辿っていきたいと思う。
ウィンドウプロシージャの実装
まず、SDLにおけるウィンドウプロシージャの実装はSDL_sysevents.cに定義されているWinMessage内で行われている。
ここから様々なサブシステムにメッセージを投げて処理をしていると思われるがウィンドウプロシージャ側から辿るのが難しそう(分岐が多くて面倒くさそう)なので逆方向からアプローチを行う。
SDL_PollEventを呼び出しているのは?
ユーザーがイベントを取得するSDL_PollEvent側から辿ってみる。SDL_PollEvent(正確にはSDL_PeepEvent)で使用するSDL_EventQ(イベントキュー)はグローバルメンバ変数で、SDL_events.cに次のように定義されている
/* Private data -- event queue */
#define MAXEVENTS 128
static struct {
SDL_mutex *lock;
int active;
int head;
int tail;
SDL_Event event[MAXEVENTS];
int wmmsg_next;
struct SDL_SysWMmsg wmmsg[MAXEVENTS];
} SDL_EventQ;
構造体の仕様から、SDLの内部処理でeventメンバに対して随時ウィンドウメッセージを処理したイベントを追加されることが予想される。では、実際にどこでイベントが追加されるかというとSDL_AddEvent内で処理される。
ここまでの流れを簡単に整理するとWinMessage(ウィンドウプロシージャ)-> [SDL入力処理?] -> SDL_AddEvent -> SDL_PollEventの順に処理されることでメッセージをやり取りするのではないかと推測できる。
イベントキューの実装
次はSDL_AddEventをだれが呼び出しているかについて調べていきたい。
ここから多少処理の分岐が複雑になって理解が難しくなるので注意されたし。
SDL_AddEventを呼び出している処理はSDL_PollEvent内でも呼び出しているSDL_PeepEventsによって追加処理も行われる。
これはSDL_PeepEventsと引数であるSDL_eventactionが次のような定義になっている。
int SDL_PeepEvents(SDL_Event *events, int numevents, SDL_eventaction action,
Uint32 mask)
// SDL_eventactionの定義
typedef enum {
SDL_ADDEVENT,
SDL_PEEKEVENT,
SDL_GETEVENT
} SDL_eventaction;
定義を見るとSDL_ADDEVNETを渡せばイベントキューにイベントを登録するし、それ以外であればSDL_Event*型メンバ引数にイベントを取得する処理になるであろう仕様が推測できる。
では、さらに辿ってSDL_PeepEventに対してSDL_ADDEVENTを渡している処理を追ってみる。するとSDL_PeepEventをさらにラップしているSDL_PushEventを経由して多岐にわたるソースコードからイベントが登録されている。
SDL_PushEventを使用してイベントを登録しているサブシステムは以下の通り。
- sdl_mouse.c
- sdl_keyboard.c
- sdl_joystick.c
- sdl_events.c
- sdl_resize.c
- sdl_quit.c
- sdl_expose.c
- sdl_active.c
かなり分岐が多いので説明は省くがsdl_mouse、sdl_keyboardやsdl_joystick等入力処理からの登録がほとんどである。
これを集約してさらに元をたどるとWinMessageに行き当たる。
処理の流れをまとめるとWinMessage(ウィンドウプロシージャ)-> 上記サブシステム群 -> SDL_AddEvent -> SDL_PollEventとなる。
以上でSDLがウィンドウプロシージャからのメッセージをどの様にユーザーに提供しているのか大まかに把握できた。