Search code examples
c++callbacksdl

Pass C++ object method as SDL_EventFilter


I am trying to create a game in C++ using SDL2, and of course I want to check for window resize events... since I have everything SDL-related handled by a Game class:

bool Game::SetupSDL()
{
    if (SDL_Init(SDL_INIT_VIDEO) < 0)
    {
        std::cout << "SDL initialization failed: " << SDL_GetError() << std::endl;
        return false;
    }

    window = SDL_CreateWindow(
        "SDL",
        SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
        640, 480,
        SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);

    if (window == NULL)
    {
        std::cout << "SDL window creation failed: " << SDL_GetError() << std::endl;
        return false;
    }

    if (TTF_Init() < 0)
    {
        return false;
    }

    renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);

    SDL_AddEventWatch(OnResize, window);

    return true;
}

Here is how OnResize is initialized:

int Game::OnResize(void *data, SDL_Event *event)
{
    if (event->type == SDL_WINDOWEVENT && event->window.event == SDL_WINDOWEVENT_RESIZED)
    {
        SetScale();
        states[state]->OnResize(*this);
    }
    return 0;
}

However, when I attempt to compile it I get the following:

error: cannot convert 'Game::OnResize' from type 'int (Game::)(void*, SDL_Event*)' to type 'SDL_EventFilter {aka int (*)(void*, SDL_Event*)}'
  SDL_AddEventWatch(this->OnResize, window);

So it appears I´m having complications passing an instance method as an SDL_FilterEvent ... attempts to recast it have resulted in compile or runtime erros, how can I resolve this?


Solution

  • The problem:

    In order to register an event callback with SDL_AddEventWatch,
    you need to supply a callback in the form of a function with the type SDL_EventFilter:

    int (*SDL_EventFilter)(void *userdata, SDL_Event *event);
    

    This should be either a free function, or a static method of a class. It cannot be a non-static method because such method requires a Game instance (the this pointer) in order to be invoked.

    A solution:

    In order to use a non-static method as a callback, you can utilize the userdata parameter to serve as your context.

    When you register the event handler, pass this (i.e. a Game*) as userdata together with a static method:

    static int Game::OnResizeEventCallback(void *userdata, SDL_Event *event);  // see implementation below
    
    // ...
    
    //---------------------------------------vvvvv-
    SDL_AddEventWatch(OnResizeEventCallback, this);
    

    Then when you get the callback convert userdata back to Game*, and call the non-static method:

    static int Game::OnResizeEventCallback(void *userdata, SDL_Event *event)
    {
        // Retrieve your instance:
        Game* pThis = reinterpret_cast<Game*>(userdata);
        // Call your non-static method with it:
        pThis->OnResize(event);
    }