Last active
February 18, 2023 09:24
-
-
Save RT2Code/804bda0bb1ed305e6351dc3a9a07869b to your computer and use it in GitHub Desktop.
SDL2 Modal Loop Callback
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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 = ▭ | |
draw_userdata.color = &color; | |
SDL_SetModalLoopCallback(Draw, &draw_userdata); | |
Resize_UserData resize_userdata; | |
resize_userdata.window = window; | |
resize_userdata.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