Skip to content

Instantly share code, notes, and snippets.

@RT2Code
Last active February 18, 2023 09:24
Show Gist options
  • Save RT2Code/804bda0bb1ed305e6351dc3a9a07869b to your computer and use it in GitHub Desktop.
Save RT2Code/804bda0bb1ed305e6351dc3a9a07869b to your computer and use it in GitHub Desktop.
SDL2 Modal Loop Callback
#include <SDL2/SDL.h>
#include <SDL2/SDL_syswm.h>
#include <Windows.h>
typedef void (*ModalLoopCallback)(void*);
typedef void (*ModalLoopResizeCallback)(SDL_Window*, int, int, void*);
ModalLoopCallback g_modal_loop_callback = NULL;
void* g_modal_loop_userdata = NULL;
ModalLoopResizeCallback g_modal_loop_resize_callback = NULL;
void* g_modal_loop_resize_userdata = NULL;
SDL_Window* g_modal_loop_window = NULL;
// Use this function to set a callback which will be called repeatedly during a modal loop.
void SDL_SetModalLoopCallback(ModalLoopCallback callback, void* userdata)
{
g_modal_loop_callback = callback;
g_modal_loop_userdata = userdata;
}
// Use this function to set a callback callback which will be called when the window is resized
// during the modal loop. This can be used to resize a swap chain for example.
void SDL_SetModalLoopResizeCallback(ModalLoopResizeCallback resize_callback, void* userdata)
{
g_modal_loop_resize_callback = resize_callback;
g_modal_loop_resize_userdata = userdata;
}
int EventWatch(void*, SDL_Event* event)
{
if (event->type == SDL_SYSWMEVENT)
{
SDL_SysWMmsg* sys_event = event->syswm.msg;
#if defined (WIN32)
switch (sys_event->msg.win.msg)
{
// We enter a modal loop and set a timer
case WM_ENTERSIZEMOVE:
case WM_ENTERMENULOOP:
{
g_modal_loop_window = (SDL_Window*)GetWindowLongPtrW(sys_event->msg.win.hwnd, GWLP_USERDATA);
SetTimer(sys_event->msg.win.hwnd, (UINT_PTR)g_modal_loop_window, USER_TIMER_MINIMUM, NULL);
break;
}
// The timer will send this message repeatedly during the modal loop
case WM_TIMER:
if (g_modal_loop_callback && g_modal_loop_window && sys_event->msg.win.wParam == (UINT_PTR)g_modal_loop_window)
g_modal_loop_callback(g_modal_loop_userdata);
break;
// While we're stuck in the modal loop, we can't use SDL messages including SDL_WINDOWEVENT_RESIZED,
// so in order to handle the window resizing, we have to process the WM_SIZE message directly.
case WM_SIZE:
if (g_modal_loop_resize_callback && g_modal_loop_window)
g_modal_loop_resize_callback(g_modal_loop_window, LOWORD(sys_event->msg.win.lParam), HIWORD(sys_event->msg.win.lParam), g_modal_loop_resize_userdata);
break;
// We leave the modal loop and end the timer
case WM_EXITSIZEMOVE:
case WM_EXITMENULOOP:
KillTimer(sys_event->msg.win.hwnd, (UINT_PTR)g_modal_loop_window);
g_modal_loop_window = NULL;
break;
}
#endif
}
return 0;
}
struct Draw_UserData
{
SDL_Renderer* renderer;
SDL_Rect* rect;
SDL_Color* color;
};
void Draw(void* userdata)
{
Draw_UserData* draw_userdata = (Draw_UserData*)userdata;
SDL_Renderer* renderer = draw_userdata->renderer;
SDL_Rect* rect = draw_userdata->rect;
SDL_Color* color = draw_userdata->color;
SDL_SetRenderDrawColor(renderer, 255, 255, 255, SDL_ALPHA_OPAQUE);
SDL_RenderClear(renderer);
SDL_SetRenderDrawColor(renderer, color->r, color->g, color->b, color->a);
SDL_RenderFillRect(renderer, rect);
SDL_RenderPresent(renderer);
color->r += 5;
}
struct Resize_UserData
{
SDL_Window* window;
SDL_Rect* rect;
};
// The SDL window pointer allows to know which specific window is being resized
// in case you created more than one.
void Resize(SDL_Window* window, int w, int h, void* userdata)
{
Resize_UserData* resize_userdata = (Resize_UserData*)userdata;
if (window == resize_userdata->window)
{
SDL_Rect* rect = resize_userdata->rect;
rect->x = (int)(w * 0.1f);
rect->y = (int)(h * 0.1f);
rect->w = (int)(w * 0.8f);
rect->h = (int)(h * 0.8f);
}
}
int main(int, char**)
{
SDL_Init(SDL_INIT_VIDEO);
SDL_Window* window = SDL_CreateWindow("Window", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_RESIZABLE);
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
SDL_Rect rect = {.x = 80, .y = 60, .w = 640, .h = 480};
SDL_Color color = {.r = 0, .g = 0, .b = 0, .a = SDL_ALPHA_OPAQUE};
// We'll have to process the SDL_SYSWMEVENT event, but it only gives us the win32 handle,
// so we need a way to retrieve the SDL window from it.
#if defined (WIN32)
SDL_SysWMinfo wmInfo;
SDL_VERSION(&wmInfo.version);
SDL_GetWindowWMInfo(window, &wmInfo);
SetWindowLongPtrW((HWND)wmInfo.info.win.window, GWLP_USERDATA, (LONG_PTR)window);
#endif
SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
SDL_AddEventWatch(EventWatch, NULL);
Draw_UserData draw_userdata;
draw_userdata.renderer = renderer;
draw_userdata.rect = &rect;
draw_userdata.color = &color;
SDL_SetModalLoopCallback(Draw, &draw_userdata);
Resize_UserData resize_userdata;
resize_userdata.window = window;
resize_userdata.rect = &rect;
SDL_SetModalLoopResizeCallback(Resize, &resize_userdata);
bool run = true;
while (run)
{
SDL_Event event;
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_WINDOWEVENT:
if (event.window.event == SDL_WINDOWEVENT_RESIZED)
Resize(SDL_GetWindowFromID(event.window.windowID), event.window.data1, event.window.data2, &resize_userdata);
break;
case SDL_QUIT:
run = false;
break;
}
}
Draw(&draw_userdata);
}
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment