From 74bb56ca2d1440fefb08d8ee2847d49157632fe3 Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Thu, 28 May 2026 20:32:01 -0400 Subject: [PATCH 01/12] sdl 2 --- CMakeLists.txt | 6 + Makefile | 4 + src/desktop/backends/sdl2.c | 294 ++++++++++++++++++++++++++++++++++++ 3 files changed, 304 insertions(+) create mode 100644 src/desktop/backends/sdl2.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 35164e03..17fbe126 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -259,6 +259,12 @@ if(PLATFORM STREQUAL "desktop") find_package(SDL REQUIRED) target_include_directories(butterscotch PUBLIC ${SDL_INCLUDE_DIR}) set(BACKEND_LIBRARIES ${SDL_LIBRARY}) + elseif(DESKTOP_BACKEND STREQUAL "sdl2") + find_package(PkgConfig REQUIRED) + pkg_check_modules(SDL2 REQUIRED sdl2) + target_include_directories(butterscotch PRIVATE ${SDL2_INCLUDE_DIRS}) + target_link_directories(butterscotch PRIVATE ${SDL2_LIBRARY_DIRS}) + set(BACKEND_LIBRARIES ${SDL2_LIBRARIES}) endif() target_link_libraries(butterscotch ${BACKEND_LIBRARIES} ${AUDIO_LIBRARIES} ${PLATFORM_LIBRARIES}) target_sources(butterscotch PRIVATE src/desktop/backends/${DESKTOP_BACKEND}.c) diff --git a/Makefile b/Makefile index b9b1e795..d57d0213 100644 --- a/Makefile +++ b/Makefile @@ -153,6 +153,10 @@ SRCS += vendor/glad/src/glad.c INCLUDES += -Ivendor/glad/include endif endif +ifeq ($(DESKTOP_BACKEND),sdl2) +SDL1_LIBS += $(shell pkg-config --libs sdl2) +LIBS += $(SDL1_LIBS) +endif ifeq ($(OS),Windows) LIBS += -static -lwinmm diff --git a/src/desktop/backends/sdl2.c b/src/desktop/backends/sdl2.c new file mode 100644 index 00000000..6021c60e --- /dev/null +++ b/src/desktop/backends/sdl2.c @@ -0,0 +1,294 @@ +#include + +#include +#include +#include + +#include "common.h" +#include "input_recording.h" +#include "desktop/platformdefs.h" + +static Runner *g_runner; +static int32_t fbWidth, fbHeight; +static SDL_Surface* scr; +static SDL_Window *window; + +static void platformSetWindowTitle(const char* title) { + char windowTitle[256]; + snprintf(windowTitle, sizeof(windowTitle), "Butterscotch - %s", title); + SDL_SetWindowTitle(window, windowTitle); +} + +bool platformGetWindowSize(int32_t* outW, int32_t* outH) { + if (!outW || !outH) return false; + *outW = fbWidth; + *outH = fbHeight; + return true; +} + +static void platformSetWindowSize(int32_t width, int32_t height) { + if (width <= 0 || height <= 0) return; + fbWidth = width; + fbHeight = height; + SDL_SetWindowSize(window, width, height); + scr = SDL_GetWindowSurface(window); +} + +static bool platformGetWindowFocus(void) { + return SDL_GetWindowFlags(window) & SDL_WINDOW_INPUT_FOCUS; +} + +bool platformInit(int reqW, int reqH, const char *title, bool headless) { + if (headless && !SWRender) { + fprintf(stderr, "Headless mode on SDL requires the software renderer!\n"); + return false; + } + + // Init SDL + if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER)) { + fprintf(stderr, "Failed to initialize SDL\n"); + return false; + } + + if (legacyGL) { + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); + } else if (modernGL) { +#ifdef ENABLE_GLES +#ifdef SDL_GL_CONTEXT_PROFILE_MASK + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); +#endif + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); +#else + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); +#ifdef SDL_GL_CONTEXT_PROFILE_MASK + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG | SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); +#endif +#endif + } + + fbWidth = reqW; + fbHeight = reqH; + if(!headless) { + window = SDL_CreateWindow( + "Butterscotch", + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + fbWidth, fbHeight, + (SWRender ? 0 : SDL_WINDOW_OPENGL) | SDL_WINDOW_RESIZABLE + ); + if (!window && SWRender) { + SDL_DisplayMode mode; + if (SDL_GetDisplayMode(0, 0, &mode) == 0) { + fprintf(stderr, "Warning: %dx%d unavailable, falling back to %dx%d: %s\n", + reqW, reqH, mode.w, mode.h, SDL_GetError()); + fbWidth = mode.w; + fbHeight = mode.h; + window = SDL_CreateWindow( + "Butterscotch", + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + fbWidth, fbHeight, + (SWRender ? 0 : SDL_WINDOW_OPENGL) | SDL_WINDOW_RESIZABLE + ); + } + } + if (!window) { + fprintf(stderr, "Fatal: Could not set any video mode: %s\n", SDL_GetError()); + return false; + } + scr = SDL_GetWindowSurface(window); + if (!SWRender) { + SDL_GL_CreateContext(window); + SDL_GL_SetSwapInterval(0); // disable vsync + } + } + + return true; +} + +void platformExit(void) { + SDL_Quit(); +} + +void platformInitFunctions(Runner *runner) { + g_runner = runner; + runner->setWindowTitle = platformSetWindowTitle; + runner->getWindowSize = platformGetWindowSize; + runner->setWindowSize = platformSetWindowSize; + runner->windowHasFocus = platformGetWindowFocus; +} + +#ifdef ENABLE_SW_RENDERER + +static SDL_Surface* nextFb = NULL; + +void Runner_setNextFrame(uint32_t* framebuffer, int width, int height) { + if (nextFb) { + SDL_FreeSurface(nextFb); + nextFb = NULL; + } + + nextFb = SDL_CreateRGBSurfaceFrom( + framebuffer, + width, + height, + 32, + width * 4, + 0x00ff0000, // Rmask + 0x0000ff00, // Gmask + 0x000000ff, // Bmask + 0x00000000 // Amask + ); +} + +#endif + +void platformSwapBuffers(void) { +#ifdef ENABLE_SW_RENDERER + if(SWRender) { + SDL_BlitSurface(nextFb, NULL, scr, NULL); + SDL_UpdateWindowSurface(window); + } +#endif +#if defined(ENABLE_LEGACY_GL) || defined(ENABLE_MODERN_GL) + if (legacyGL || modernGL) + SDL_GL_SwapWindow(window); +#endif +} + +#if defined(ENABLE_MODERN_GL) || defined(ENABLE_LEGACY_GL) + +void *platformGetProcAddress(const char *name) { + return SDL_GL_GetProcAddress(name); +} + +#endif + +double platformGetTime(void) { + return (double)SDL_GetTicks() / 1000.0; +} + +static int32_t SDLKeyToGml(int sdlkey) { + // Letters and numbers are the same as GML + if (sdlkey >= 'a' && sdlkey <= 'z') return toupper(sdlkey); + if (sdlkey >= '0' && sdlkey <= '9') return sdlkey; + // Special keys need mapping + switch (sdlkey) { + case SDLK_ESCAPE: return VK_ESCAPE; + case SDLK_RETURN: return VK_ENTER; + case SDLK_TAB: return VK_TAB; + case SDLK_BACKSPACE: return VK_BACKSPACE; + case SDLK_SPACE: return VK_SPACE; + case SDLK_LSHIFT: + case SDLK_RSHIFT: return VK_SHIFT; + case SDLK_LCTRL: + case SDLK_RCTRL: return VK_CONTROL; + case SDLK_LALT: + case SDLK_RALT: return VK_ALT; + case SDLK_UP: return VK_UP; + case SDLK_DOWN: return VK_DOWN; + case SDLK_LEFT: return VK_LEFT; + case SDLK_RIGHT: return VK_RIGHT; + case SDLK_F1: return VK_F1; + case SDLK_F2: return VK_F2; + case SDLK_F3: return VK_F3; + case SDLK_F4: return VK_F4; + case SDLK_F5: return VK_F5; + case SDLK_F6: return VK_F6; + case SDLK_F7: return VK_F7; + case SDLK_F8: return VK_F8; + case SDLK_F9: return VK_F9; + case SDLK_F10: return VK_F10; + case SDLK_F11: return VK_F11; + case SDLK_F12: return VK_F12; + case SDLK_INSERT: return VK_INSERT; + case SDLK_DELETE: return VK_DELETE; + case SDLK_HOME: return VK_HOME; + case SDLK_END: return VK_END; + case SDLK_PAGEUP: return VK_PAGEUP; + case SDLK_PAGEDOWN: return VK_PAGEDOWN; + default: return -1; // Unknown + } +} + +static uint32_t utf8_to_codepoint(const char *s) { + const unsigned char *p = (const unsigned char *)s; + + if (p[0] < 0x80) + return p[0]; + + if ((p[0] & 0xE0) == 0xC0) + return ((p[0] & 0x1F) << 6) | + (p[1] & 0x3F); + + if ((p[0] & 0xF0) == 0xE0) + return ((p[0] & 0x0F) << 12) | + ((p[1] & 0x3F) << 6) | + (p[2] & 0x3F); + + if ((p[0] & 0xF8) == 0xF0) + return ((p[0] & 0x07) << 18) | + ((p[1] & 0x3F) << 12) | + ((p[2] & 0x3F) << 6) | + (p[3] & 0x3F); + + return 0xFFFD; // replacement character +} + +bool platformHandleEvents(void) { + bool should_exit = false; + SDL_Event e; + while (SDL_PollEvent(&e)) { + switch(e.type) { + case SDL_KEYDOWN: + // During playback, suppress real keyboard input + if (InputRecording_isPlaybackActive(globalInputRecording)) break; + if (e.key.repeat != 0) + break; + RunnerKeyboard_onKeyDown(g_runner->keyboard, SDLKeyToGml(e.key.keysym.sym)); + break; + case SDL_KEYUP: + // During playback, suppress real keyboard input + if (InputRecording_isPlaybackActive(globalInputRecording)) break; + RunnerKeyboard_onKeyUp(g_runner->keyboard, SDLKeyToGml(e.key.keysym.sym)); + break; + case SDL_TEXTINPUT: + // During playback, suppress real keyboard input + if (InputRecording_isPlaybackActive(globalInputRecording)) break; + RunnerKeyboard_onCharacter(g_runner->keyboard, utf8_to_codepoint(e.text.text)); + break; + case SDL_WINDOWEVENT: + if (e.window.event != SDL_WINDOWEVENT_SIZE_CHANGED) + break; + fbWidth = e.window.data1; + fbHeight = e.window.data2; + scr = SDL_GetWindowSurface(window); + break; + case SDL_QUIT: + should_exit = true; + break; + default: + break; + } + } + + return should_exit; +} + +void platformSleepUntil(double time) { + double remaining = time - platformGetTime(); + if (remaining > 0.002) + SDL_Delay((Uint32)((remaining - 0.001) * 1000)); + + while (platformGetTime() < time) { + // Spin-wait for the remaining sub-millisecond + } +} + +void platformGamepad_poll(RunnerGamepadState* gp) { + (void)gp; +} From 2b53e4d8f5b25069695cc20fe1fc60a466697593 Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Wed, 3 Jun 2026 20:33:09 -0400 Subject: [PATCH 02/12] fix --- src/desktop/backends/sdl2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/desktop/backends/sdl2.c b/src/desktop/backends/sdl2.c index 6021c60e..c8be173f 100644 --- a/src/desktop/backends/sdl2.c +++ b/src/desktop/backends/sdl2.c @@ -74,7 +74,7 @@ bool platformInit(int reqW, int reqH, const char *title, bool headless) { fbHeight = reqH; if(!headless) { window = SDL_CreateWindow( - "Butterscotch", + title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, fbWidth, fbHeight, From 71434999891a87c67d0a3c96fe0e5ffb6ab52d3b Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Thu, 4 Jun 2026 00:00:35 -0400 Subject: [PATCH 03/12] fix again --- src/desktop/backends/sdl2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/desktop/backends/sdl2.c b/src/desktop/backends/sdl2.c index c8be173f..a8a8986f 100644 --- a/src/desktop/backends/sdl2.c +++ b/src/desktop/backends/sdl2.c @@ -88,7 +88,7 @@ bool platformInit(int reqW, int reqH, const char *title, bool headless) { fbWidth = mode.w; fbHeight = mode.h; window = SDL_CreateWindow( - "Butterscotch", + title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, fbWidth, fbHeight, From 2dfc84be39b4888d6a079e2478d309f21ec62caa Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Thu, 4 Jun 2026 00:26:32 -0400 Subject: [PATCH 04/12] better --- src/desktop/backends/sdl2.c | 83 +++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 41 deletions(-) diff --git a/src/desktop/backends/sdl2.c b/src/desktop/backends/sdl2.c index a8a8986f..1d4cebdc 100644 --- a/src/desktop/backends/sdl2.c +++ b/src/desktop/backends/sdl2.c @@ -39,21 +39,16 @@ static bool platformGetWindowFocus(void) { } bool platformInit(int reqW, int reqH, const char *title, bool headless) { - if (headless && !SWRender) { - fprintf(stderr, "Headless mode on SDL requires the software renderer!\n"); - return false; - } - // Init SDL if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER)) { fprintf(stderr, "Failed to initialize SDL\n"); return false; } - if (legacyGL) { + if (gfx == LEGACY_GL) { SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); - } else if (modernGL) { + } else if (gfx == MODERN_GL) { #ifdef ENABLE_GLES #ifdef SDL_GL_CONTEXT_PROFILE_MASK SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); @@ -70,42 +65,48 @@ bool platformInit(int reqW, int reqH, const char *title, bool headless) { #endif } - fbWidth = reqW; - fbHeight = reqH; - if(!headless) { - window = SDL_CreateWindow( - title, - SDL_WINDOWPOS_UNDEFINED, - SDL_WINDOWPOS_UNDEFINED, - fbWidth, fbHeight, - (SWRender ? 0 : SDL_WINDOW_OPENGL) | SDL_WINDOW_RESIZABLE - ); - if (!window && SWRender) { - SDL_DisplayMode mode; - if (SDL_GetDisplayMode(0, 0, &mode) == 0) { - fprintf(stderr, "Warning: %dx%d unavailable, falling back to %dx%d: %s\n", - reqW, reqH, mode.w, mode.h, SDL_GetError()); - fbWidth = mode.w; - fbHeight = mode.h; - window = SDL_CreateWindow( - title, - SDL_WINDOWPOS_UNDEFINED, - SDL_WINDOWPOS_UNDEFINED, - fbWidth, fbHeight, - (SWRender ? 0 : SDL_WINDOW_OPENGL) | SDL_WINDOW_RESIZABLE - ); - } - } - if (!window) { - fprintf(stderr, "Fatal: Could not set any video mode: %s\n", SDL_GetError()); - return false; - } - scr = SDL_GetWindowSurface(window); - if (!SWRender) { - SDL_GL_CreateContext(window); - SDL_GL_SetSwapInterval(0); // disable vsync + Uint32 flags; + if (headless) { + fbWidth = 1; + fbHeight = 1; + flags = (gfx == SOFTWARE ? 0 : SDL_WINDOW_OPENGL) | SDL_WINDOW_HIDDEN; + } else { + fbWidth = reqW; + fbHeight = reqH; + flags = (gfx == SOFTWARE ? 0 : SDL_WINDOW_OPENGL) | SDL_WINDOW_RESIZABLE; + } + window = SDL_CreateWindow( + title, + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + fbWidth, fbHeight, + flags + ); + if (!window && gfx == SOFTWARE) { + SDL_DisplayMode mode; + if (SDL_GetDisplayMode(0, 0, &mode) == 0) { + fprintf(stderr, "Warning: %dx%d unavailable, falling back to %dx%d: %s\n", + reqW, reqH, mode.w, mode.h, SDL_GetError()); + fbWidth = mode.w; + fbHeight = mode.h; + window = SDL_CreateWindow( + title, + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + fbWidth, fbHeight, + flags + ); } } + if (!window) { + fprintf(stderr, "Fatal: Could not set any video mode: %s\n", SDL_GetError()); + return false; + } + scr = SDL_GetWindowSurface(window); + if (gfx != SOFTWARE) { + SDL_GL_CreateContext(window); + SDL_GL_SetSwapInterval(0); // disable vsync + } return true; } From d62b94b0fef5243d7cb2df3347a51613fbed115f Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Thu, 4 Jun 2026 01:49:41 -0400 Subject: [PATCH 05/12] bring it up to snuf --- src/desktop/backends/sdl2.c | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/src/desktop/backends/sdl2.c b/src/desktop/backends/sdl2.c index 1d4cebdc..3cdf3bda 100644 --- a/src/desktop/backends/sdl2.c +++ b/src/desktop/backends/sdl2.c @@ -13,7 +13,7 @@ static int32_t fbWidth, fbHeight; static SDL_Surface* scr; static SDL_Window *window; -static void platformSetWindowTitle(const char* title) { +void platformSetWindowTitle(const char* title) { char windowTitle[256]; snprintf(windowTitle, sizeof(windowTitle), "Butterscotch - %s", title); SDL_SetWindowTitle(window, windowTitle); @@ -26,7 +26,11 @@ bool platformGetWindowSize(int32_t* outW, int32_t* outH) { return true; } -static void platformSetWindowSize(int32_t width, int32_t height) { +bool platformGetScaledWindowSize(int32_t* outW, int32_t* outH) { + return platformGetWindowSize(outW, outH); +} + +void platformSetWindowSize(int32_t width, int32_t height) { if (width <= 0 || height <= 0) return; fbWidth = width; fbHeight = height; @@ -34,6 +38,14 @@ static void platformSetWindowSize(int32_t width, int32_t height) { scr = SDL_GetWindowSurface(window); } +void platformGetMousePos(double *xPos, double *yPos) { + if (!xPos || !yPos) return; + int mx = 0, my = 0; + SDL_GetMouseState(&mx, &my); + *xPos = (double)mx; + *yPos = (double)my; +} + static bool platformGetWindowFocus(void) { return SDL_GetWindowFlags(window) & SDL_WINDOW_INPUT_FOCUS; } @@ -66,15 +78,13 @@ bool platformInit(int reqW, int reqH, const char *title, bool headless) { } Uint32 flags; - if (headless) { - fbWidth = 1; - fbHeight = 1; + if (headless) flags = (gfx == SOFTWARE ? 0 : SDL_WINDOW_OPENGL) | SDL_WINDOW_HIDDEN; - } else { - fbWidth = reqW; - fbHeight = reqH; + else flags = (gfx == SOFTWARE ? 0 : SDL_WINDOW_OPENGL) | SDL_WINDOW_RESIZABLE; - } + + fbWidth = reqW; + fbHeight = reqH; window = SDL_CreateWindow( title, SDL_WINDOWPOS_UNDEFINED, @@ -150,13 +160,13 @@ void Runner_setNextFrame(uint32_t* framebuffer, int width, int height) { void platformSwapBuffers(void) { #ifdef ENABLE_SW_RENDERER - if(SWRender) { + if(gfx == SOFTWARE) { SDL_BlitSurface(nextFb, NULL, scr, NULL); SDL_UpdateWindowSurface(window); } #endif #if defined(ENABLE_LEGACY_GL) || defined(ENABLE_MODERN_GL) - if (legacyGL || modernGL) + if (gfx == LEGACY_GL || gfx == MODERN_GL) SDL_GL_SwapWindow(window); #endif } From 387a83e9b3c86afa7212545d2a79d78934c9d5e5 Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Thu, 4 Jun 2026 01:55:49 -0400 Subject: [PATCH 06/12] this is pointless now --- src/desktop/backends/sdl2.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/desktop/backends/sdl2.c b/src/desktop/backends/sdl2.c index 3cdf3bda..0d5ed142 100644 --- a/src/desktop/backends/sdl2.c +++ b/src/desktop/backends/sdl2.c @@ -127,9 +127,6 @@ void platformExit(void) { void platformInitFunctions(Runner *runner) { g_runner = runner; - runner->setWindowTitle = platformSetWindowTitle; - runner->getWindowSize = platformGetWindowSize; - runner->setWindowSize = platformSetWindowSize; runner->windowHasFocus = platformGetWindowFocus; } From f7a314b2848332ed2de5ddf7d83261bc6fc5ef3e Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Thu, 4 Jun 2026 02:00:29 -0400 Subject: [PATCH 07/12] error check? --- src/desktop/backends/sdl2.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/desktop/backends/sdl2.c b/src/desktop/backends/sdl2.c index 0d5ed142..d89b0535 100644 --- a/src/desktop/backends/sdl2.c +++ b/src/desktop/backends/sdl2.c @@ -114,7 +114,10 @@ bool platformInit(int reqW, int reqH, const char *title, bool headless) { } scr = SDL_GetWindowSurface(window); if (gfx != SOFTWARE) { - SDL_GL_CreateContext(window); + if (!SDL_GL_CreateContext(window)) { + fprintf(stderr, "Fatal: Could not create GL context: %s\n", SDL_GetError()); + return false; + } SDL_GL_SetSwapInterval(0); // disable vsync } From bc1dccfa12149ef990da042625f5a00440425e21 Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Thu, 4 Jun 2026 02:05:23 -0400 Subject: [PATCH 08/12] fixes --- CMakeLists.txt | 2 ++ Makefile | 9 +++++---- src/desktop/main.c | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 17fbe126..7af57aae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -260,6 +260,8 @@ if(PLATFORM STREQUAL "desktop") target_include_directories(butterscotch PUBLIC ${SDL_INCLUDE_DIR}) set(BACKEND_LIBRARIES ${SDL_LIBRARY}) elseif(DESKTOP_BACKEND STREQUAL "sdl2") + add_compile_definitions(USE_SDL2) + find_package(PkgConfig REQUIRED) pkg_check_modules(SDL2 REQUIRED sdl2) target_include_directories(butterscotch PRIVATE ${SDL2_INCLUDE_DIRS}) diff --git a/Makefile b/Makefile index d57d0213..d729b27d 100644 --- a/Makefile +++ b/Makefile @@ -74,6 +74,11 @@ SDL1_LIBS += $(shell pkg-config $(PKG_CONFIG_FLAGS) --libs sdl) LIBS += $(SDL1_LIBS) DEFINES += -DUSE_SDL1 endif +ifeq ($(DESKTOP_BACKEND),sdl2) +SDL2_LIBS += $(shell pkg-config --libs sdl2) +LIBS += $(SDL2_LIBS) +DEFINES += -DUSE_SDL2 +endif # GNU make doesn't have a way to do OR in conditionals, stupid language for clowns ifndef DISABLE_LEGACY_GL @@ -153,10 +158,6 @@ SRCS += vendor/glad/src/glad.c INCLUDES += -Ivendor/glad/include endif endif -ifeq ($(DESKTOP_BACKEND),sdl2) -SDL1_LIBS += $(shell pkg-config --libs sdl2) -LIBS += $(SDL1_LIBS) -endif ifeq ($(OS),Windows) LIBS += -static -lwinmm diff --git a/src/desktop/main.c b/src/desktop/main.c index bdd0fe69..c016ec69 100644 --- a/src/desktop/main.c +++ b/src/desktop/main.c @@ -341,7 +341,7 @@ static void parseCommandLineArgs(CommandLineArgs* args, int argc, char* argv[]) args->profilerFramesBetween = 0; // TODO: detect available driver features // at runtime to improve defaults. -#if defined(ENABLE_MODERN_GL) && defined(USE_GLFW3) +#if defined(ENABLE_MODERN_GL) && (defined(USE_GLFW3) || defined(USE_SDL2)) args->renderer = "modern-gl"; #elif defined(ENABLE_LEGACY_GL) args->renderer = "legacy-gl"; From 385a0f4aa5d08d0dccc4b0a8beb54160688ee636 Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Thu, 4 Jun 2026 02:33:53 -0400 Subject: [PATCH 09/12] fix --- src/desktop/main.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/desktop/main.c b/src/desktop/main.c index c016ec69..4610d8dd 100644 --- a/src/desktop/main.c +++ b/src/desktop/main.c @@ -50,6 +50,13 @@ #include "utils.h" #include "profiler.h" +/* For SDL_main */ +#if defined(USE_SDL1) +#include +#elif defined(USE_SDL2) +#include +#endif + enum GraphicsAPI gfx; #if !defined(ENABLE_GLES) && (defined(ENABLE_MODERN_GL) || defined(ENABLE_LEGACY_GL)) From 39a44f66b745d797f125a5af19b758890ab4d3e4 Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Thu, 4 Jun 2026 02:53:27 -0400 Subject: [PATCH 10/12] fix --- src/desktop/backends/sdl2.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/desktop/backends/sdl2.c b/src/desktop/backends/sdl2.c index d89b0535..3ca22af6 100644 --- a/src/desktop/backends/sdl2.c +++ b/src/desktop/backends/sdl2.c @@ -35,7 +35,8 @@ void platformSetWindowSize(int32_t width, int32_t height) { fbWidth = width; fbHeight = height; SDL_SetWindowSize(window, width, height); - scr = SDL_GetWindowSurface(window); + if (gfx == SOFTWARE) + scr = SDL_GetWindowSurface(window); } void platformGetMousePos(double *xPos, double *yPos) { @@ -112,14 +113,14 @@ bool platformInit(int reqW, int reqH, const char *title, bool headless) { fprintf(stderr, "Fatal: Could not set any video mode: %s\n", SDL_GetError()); return false; } - scr = SDL_GetWindowSurface(window); if (gfx != SOFTWARE) { if (!SDL_GL_CreateContext(window)) { fprintf(stderr, "Fatal: Could not create GL context: %s\n", SDL_GetError()); return false; } SDL_GL_SetSwapInterval(0); // disable vsync - } + } else + scr = SDL_GetWindowSurface(window); return true; } @@ -277,7 +278,8 @@ bool platformHandleEvents(void) { break; fbWidth = e.window.data1; fbHeight = e.window.data2; - scr = SDL_GetWindowSurface(window); + if (gfx == SOFTWARE) + scr = SDL_GetWindowSurface(window); break; case SDL_QUIT: should_exit = true; From 7a7c75f8a9eb47cbf2361b42288fc7bd07e6a783 Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Thu, 4 Jun 2026 02:57:03 -0400 Subject: [PATCH 11/12] dont --- src/desktop/backends/sdl2.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/desktop/backends/sdl2.c b/src/desktop/backends/sdl2.c index 3ca22af6..bc46dbfb 100644 --- a/src/desktop/backends/sdl2.c +++ b/src/desktop/backends/sdl2.c @@ -71,10 +71,8 @@ bool platformInit(int reqW, int reqH, const char *title, bool headless) { #else SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); -#ifdef SDL_GL_CONTEXT_PROFILE_MASK SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG | SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); -#endif #endif } From cf440602a254204baaa0fb390fefb93884df104d Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Thu, 4 Jun 2026 16:09:55 -0400 Subject: [PATCH 12/12] mouse input --- src/desktop/backends/sdl2.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/desktop/backends/sdl2.c b/src/desktop/backends/sdl2.c index bc46dbfb..c8f48fc5 100644 --- a/src/desktop/backends/sdl2.c +++ b/src/desktop/backends/sdl2.c @@ -249,6 +249,15 @@ static uint32_t utf8_to_codepoint(const char *s) { return 0xFFFD; // replacement character } +static int32_t SDLMouseButtonToGml(int sdlButton) { + switch (sdlButton) { + case SDL_BUTTON_LEFT: return GML_MB_LEFT; + case SDL_BUTTON_RIGHT: return GML_MB_RIGHT; + case SDL_BUTTON_MIDDLE: return GML_MB_MIDDLE; + default: return -1; + } +} + bool platformHandleEvents(void) { bool should_exit = false; SDL_Event e; @@ -271,6 +280,21 @@ bool platformHandleEvents(void) { if (InputRecording_isPlaybackActive(globalInputRecording)) break; RunnerKeyboard_onCharacter(g_runner->keyboard, utf8_to_codepoint(e.text.text)); break; + case SDL_MOUSEBUTTONDOWN: { + if (InputRecording_isPlaybackActive(globalInputRecording)) break; + int32_t gmlBtn = SDLMouseButtonToGml(e.button.button); + if (gmlBtn >= 0) RunnerMouse_onButtonDown(g_runner->mouse, gmlBtn); + } break; + case SDL_MOUSEBUTTONUP: { + if (InputRecording_isPlaybackActive(globalInputRecording)) break; + int32_t gmlBtn = SDLMouseButtonToGml(e.button.button); + if (gmlBtn >= 0) RunnerMouse_onButtonUp(g_runner->mouse, gmlBtn); + } break; + case SDL_MOUSEWHEEL: + if (InputRecording_isPlaybackActive(globalInputRecording)) break; + if (e.wheel.y != 0) + RunnerMouse_onWheel(g_runner->mouse, (float)e.wheel.y); + break; case SDL_WINDOWEVENT: if (e.window.event != SDL_WINDOWEVENT_SIZE_CHANGED) break;