Last active
October 3, 2021 13:15
-
-
Save 0x1F9F1/19c422de0ee6d817a7fc94fe82bf5fb6 to your computer and use it in GitHub Desktop.
An example of using WM_PAINT and WM_TIMER with SDL2 to allow updates when moving/resizing
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 <SDL.h> | |
#include <SDL_opengl.h> | |
#include <SDL_syswm.h> | |
#include <Windows.h> | |
enum class UpdateReason | |
{ | |
Main, | |
Paint, | |
Timer, | |
}; | |
struct Application | |
{ | |
bool Running {}; | |
SDL_Window* Window {}; | |
SDL_GLContext GLContext {}; | |
Uint64 LastUpdate {}; | |
float Rotation {}; | |
void Init(); | |
void Shutdown(); | |
void PumpEvents(); | |
void Update(UpdateReason reason); | |
private: | |
void ProcessEvents(); | |
void ProcessEvent(SDL_Event& event); | |
void DrawScene(); | |
static int EventWatcher(void* data, SDL_Event* event); | |
static void CALLBACK TimerCallback(HWND hWnd, UINT uMsg, UINT_PTR nIDEvent, DWORD time); | |
} g_App; | |
void Application::Init() | |
{ | |
Window = SDL_CreateWindow("Test Window", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 640, 480, | |
SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL); | |
GLContext = SDL_GL_CreateContext(Window); | |
SDL_GL_MakeCurrent(Window, GLContext); | |
SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE); | |
SDL_AddEventWatch(EventWatcher, nullptr); | |
LastUpdate = SDL_GetPerformanceCounter(); | |
Running = true; | |
} | |
void Application::Shutdown() | |
{ | |
Running = false; | |
SDL_DelEventWatch(EventWatcher, nullptr); | |
SDL_GL_DeleteContext(GLContext); | |
GLContext = nullptr; | |
SDL_DestroyWindow(Window); | |
Window = nullptr; | |
} | |
void Application::PumpEvents() | |
{ | |
// Wait for events if the window isn't visbile | |
for (SDL_Event event; | |
Running && (SDL_GetWindowFlags(Window) & (SDL_WINDOW_HIDDEN | SDL_WINDOW_MINIMIZED)) && SDL_WaitEvent(&event);) | |
{ | |
ProcessEvent(event); | |
} | |
UINT_PTR timer_id = SetTimer(NULL, 0, USER_TIMER_MINIMUM, TimerCallback); | |
SDL_PumpEvents(); | |
KillTimer(NULL, timer_id); | |
} | |
void Application::Update(UpdateReason reason) | |
{ | |
ProcessEvents(); | |
Uint64 now = SDL_GetPerformanceCounter(); | |
float elapsed = (now - LastUpdate) / static_cast<float>(SDL_GetPerformanceFrequency()); | |
LastUpdate = now; | |
// SDL_Log("Update %f", elapsed * 1000.0); | |
Rotation = SDL_fmodf(Rotation + elapsed * 30.0f, 360.0f); | |
DrawScene(); | |
// For maximum responsiveness, avoid waiting on vsync if not called from the main loop | |
SDL_GL_SetSwapInterval((reason == UpdateReason::Main) ? 1 : 0); | |
SDL_GL_SwapWindow(Window); | |
} | |
void Application::ProcessEvents() | |
{ | |
// Use SDL_PeepEvents to avoid implicitly calling SDL_PumpEvents | |
SDL_Event events[32]; | |
while (int count = SDL_PeepEvents(events, SDL_TABLESIZE(events), SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT)) | |
{ | |
for (int i = 0; i < count; ++i) | |
ProcessEvent(events[i]); | |
} | |
} | |
void Application::ProcessEvent(SDL_Event& event) | |
{ | |
switch (event.type) | |
{ | |
case SDL_QUIT: Running = false; break; | |
case SDL_KEYDOWN: { | |
switch (event.key.keysym.sym) | |
{ | |
case SDLK_ESCAPE: Running = false; break; | |
case SDLK_RETURN: | |
if (event.key.keysym.mod & KMOD_ALT) | |
SDL_SetWindowFullscreen(Window, ~SDL_GetWindowFlags(Window) & SDL_WINDOW_FULLSCREEN_DESKTOP); | |
break; | |
} | |
break; | |
} | |
} | |
} | |
void Application::DrawScene() | |
{ | |
// Draw a simple moving scene | |
int draw_w = 0; | |
int draw_h = 0; | |
SDL_GL_GetDrawableSize(Window, &draw_w, &draw_h); | |
glViewport(0, 0, draw_w, draw_h); | |
glEnable(GL_CULL_FACE); | |
glCullFace(GL_BACK); | |
glMatrixMode(GL_PROJECTION); | |
glLoadIdentity(); | |
float right = (float) (draw_w) / (float) (draw_h); | |
glFrustum(-right / 2.0, right / 2.0, -1.0, 1.0, 1.0, 40.0); | |
glMatrixMode(GL_MODELVIEW); | |
glLoadIdentity(); | |
glTranslatef(0, 0, -4); | |
glRotatef(50, 1, 0, 0); | |
glRotatef(Rotation, 0, 1, 0); | |
glClearColor(0, 0, 0, 1); | |
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |
glColor3f(1, 1, 1); | |
glBegin(GL_LINES); | |
for (GLfloat i = -2.5; i <= 2.5; i += 0.25) | |
{ | |
glVertex3f(i, 0, 2.5); | |
glVertex3f(i, 0, -2.5); | |
glVertex3f(2.5, 0, i); | |
glVertex3f(-2.5, 0, i); | |
} | |
glEnd(); | |
glBegin(GL_TRIANGLE_FAN); | |
glColor3f(1, 0, 0); | |
glVertex3f(0, 2, 0); | |
glColor3f(0, 1, 0); | |
glVertex3f(-1, 0, 1); | |
glColor3f(0, 0, 1); | |
glVertex3f(1, 0, 1); | |
glColor3f(0, 1, 1); | |
glVertex3f(1, 0, -1); | |
glColor3f(1, 1, 0); | |
glVertex3f(-1, 0, -1); | |
glColor3f(0, 1, 0); | |
glVertex3f(-1, 0, 1); | |
glEnd(); | |
} | |
int Application::EventWatcher(void* data, SDL_Event* event) | |
{ | |
if (event->type == SDL_SYSWMEVENT) // WndProc | |
{ | |
auto& msg = event->syswm.msg->msg.win; | |
// Avoid a ~500ms delay when clicking but not dragging the title bar | |
// https://www.gamedev.net/forums/topic/520860-sizemove-loop-and-delay-in-defwindowproc/ | |
// if (msg.msg == WM_NCLBUTTONDOWN && msg.wParam == HTCAPTION) | |
if (msg.msg == WM_SYSCOMMAND && msg.wParam == SC_MOVE + HTCAPTION) | |
{ | |
POINT pt; | |
POINTSTOPOINT(pt, msg.lParam); | |
ScreenToClient(msg.hwnd, &pt); | |
PostMessage(msg.hwnd, WM_MOUSEMOVE, 0, POINTTOPOINTS(pt)); | |
} | |
// SDL_Log("WndProc: %p 0x%04x 0x%x 0x%x", msg.hwnd, msg.msg, msg.wParam, msg.lParam); | |
} | |
if (event->type == SDL_WINDOWEVENT && event->window.event == SDL_WINDOWEVENT_EXPOSED) | |
{ | |
SDL_Log("Paint"); | |
g_App.Update(UpdateReason::Paint); | |
} | |
return 0; | |
} | |
void Application::TimerCallback(HWND hWnd, UINT uMsg, UINT_PTR nIDEvent, DWORD time) | |
{ | |
// https://docs.microsoft.com/en-us/windows/win32/gdi/drawing-at-timed-intervals | |
SDL_Log("Timer"); | |
g_App.Update(UpdateReason::Timer); | |
} | |
int main(int argc, char** argv) | |
{ | |
SDL_Init(SDL_INIT_VIDEO); | |
g_App.Init(); | |
while (g_App.Running) | |
{ | |
g_App.PumpEvents(); | |
g_App.Update(UpdateReason::Main); | |
} | |
g_App.Shutdown(); | |
SDL_Quit(); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment