LoginSignup
1
1

More than 5 years have passed since last update.

SDL_PollEventの実装を解析

Posted at

SDL_logo.png

OSのメッセージ処理とゲーム実装部分分離についての参考のためにSDLを解析したときのメモを公開します。

注意点

昔Blogに書いた記事の転載です。そのため、SDLのバージョンはリビジョン7724(2013/8/31更新とかなり古い)を参考にしているので現状の実装と異なる場合があります。
ウィンドウプロシージャ周りの挙動はWindowsOSを前提としています。

対象

基本的なC/C++の知識があること。
OSが介在するプログラミングの知識があること。
基本的なSDL/SDL2のプログラミングの知識があること。

SDL_PollEventの正体

 結論から言ってしまうとSDL_PollEventの中身は通常のWin32アプリケーションで実装されるメッセージポンプと同様の処理を行うSDL_PumpEventsと、メッセージキューに貯まっているウィンドウズメッセージを横取りするSDL_PeepEventsによって実装されている。

 SDL_PumpEventsの実装を辿っていくと最終的に次のコードが見つかる。これは典型的なメッセージポンプ処理の実装。

SDL_dibevents.c
void DIB_PumpEvents(_THIS)
{
    MSG msg;

    while ( PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE) ) {
        if ( GetMessage(&msg, NULL, 0, 0) > 0 ) {
            DispatchMessage(&msg);
        }
    }

    if ( SDL_GetAppState() & SDL_APPMOUSEFOCUS ) {
        DIB_GenerateMouseMotionEvent( this );
    }
}

SDL_PeepEventの実装を辿っていくと「SDLが管理しているイベントキューの中にある全てのイベント種類の中から最初の1つを取得する」という動作を行う。取得したイベントはSDL_CutEventが呼び出され、メッセージキューから消える。

追記

リビジョン10179では構成が変わり、SDL_PollEvent()からすぐにSDL_WaitEventTimeout()が呼び出され、SDL_WaitEventTimeout()内ではSDL_PumpEvents()とSDL_PeepEvents()が実行されて処理を返すようになった。

SDL_PumpEventsは環境によって呼び出されるコードが変わり、Windows環境では下記に引用するWIN_PumpEventsが呼び出されるように仕様変更されたようだ。
内容は結局一緒で典型的なメッセージループを回している。

SDL_windowsevents.c

void
WIN_PumpEvents(_THIS)
{
    const Uint8 *keystate;
    MSG msg;
    DWORD start_ticks = GetTickCount();

    if (g_WindowsEnableMessageLoop) {
        while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
            if (g_WindowsMessageHook) {
                g_WindowsMessageHook(g_WindowsMessageHookData, msg.hwnd, msg.message, msg.wParam, msg.lParam);
            }

            /* Always translate the message in case it's a non-SDL window (e.g. with Qt integration) */
            TranslateMessage(&msg);
            DispatchMessage(&msg);

            /* Make sure we don't busy loop here forever if there are lots of events coming in */
            if (SDL_TICKS_PASSED(msg.time, start_ticks)) {
                break;
            }
        }
    }

    /* Windows loses a shift KEYUP event when you have both pressed at once and let go of one.
       You won't get a KEYUP until both are released, and that keyup will only be for the second
       key you released. Take heroic measures and check the keystate as of the last handled event,
       and if we think a key is pressed when Windows doesn't, unstick it in SDL's state. */
    keystate = SDL_GetKeyboardState(NULL);
    if ((keystate[SDL_SCANCODE_LSHIFT] == SDL_PRESSED) && !(GetKeyState(VK_LSHIFT) & 0x8000)) {
        SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_LSHIFT);
    }
    if ((keystate[SDL_SCANCODE_RSHIFT] == SDL_PRESSED) && !(GetKeyState(VK_RSHIFT) & 0x8000)) {
        SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_RSHIFT);
    }
}

SDL_PeepEventsの役割は変更なく、「SDLが管理しているイベントキューの中にある全てのイベント種類の中から最初の1つを取得する」という動作を行う。取得したイベントはSDL_CutEventが呼び出され、メッセージキューから消える。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1