It’s possible to use the main callbacks, but you can’t just #define SDL_MAIN_USE_CALLBACKS 1
like you would in C. Instead, you have to call SDL_EnterAppMainCallbacks()
manually by passing the callbacks as function pointers. Here’s a more or less 1:1 port of their callbacks template:
const c = @cImport({
@cDefine("SDL_DISABLE_OLD_NAMES", {});
@cInclude("SDL3/SDL.h");
// For programs that provide their own entry points instead of relying on SDL's main function
// macro magic, 'SDL_MAIN_HANDLED' should be defined before including 'SDL_main.h'.
@cDefine("SDL_MAIN_HANDLED", {});
@cInclude("SDL3/SDL_main.h");
});
pub fn main() u8 {
// For programs that provide their own entry points instead of relying on SDL's main function
// macro magic, 'SDL_SetMainReady()' should be called before calling 'SDL_Init()'.
c.SDL_SetMainReady();
// This is more or less what 'SDL_main.h' does behind the curtains.
const status = c.SDL_EnterAppMainCallbacks(0, null, appInit, appIterate, appEvent, appQuit);
return @bitCast(@as(i8, @truncate(status)));
}
// We will use this renderer to draw into this window every frame.
var window: ?*c.SDL_Window = null;
var renderer: ?*c.SDL_Renderer = null;
// This function runs once at startup.
fn appInit(appstate: ?*?*anyopaque, argc: c_int, argv: ?[*:null]?[*:0]u8) callconv(.c) c.SDL_AppResult {
_ = appstate;
_ = argc;
_ = argv;
_ = c.SDL_SetAppMetadata("Example HUMAN READABLE NAME", "1.0", "com.example.CATEGORY-NAME");
if (!c.SDL_Init(c.SDL_INIT_VIDEO)) {
c.SDL_Log("Couldn't initialize SDL: %s", c.SDL_GetError());
return c.SDL_APP_FAILURE;
}
if (!c.SDL_CreateWindowAndRenderer("examples/CATEGORY/NAME", 640, 480, 0, &window, &renderer)) {
c.SDL_Log("Couldn't create window/renderer: %s", c.SDL_GetError());
return c.SDL_APP_FAILURE;
}
return c.SDL_APP_CONTINUE; // carry on with the program!
}
// This function runs when a new event (mouse input, keypresses, etc) occurs.
fn appEvent(appstate: ?*anyopaque, event: ?*c.SDL_Event) callconv(.c) c.SDL_AppResult {
_ = appstate;
if (event.?.type == c.SDL_EVENT_QUIT) {
return c.SDL_APP_SUCCESS; // end the program, reporting success to the OS.
}
return c.SDL_APP_CONTINUE; // carry on with the program!
}
// This function runs once per frame, and is the heart of the program.
fn appIterate(appstate: ?*anyopaque) callconv(.c) c.SDL_AppResult {
_ = appstate;
return c.SDL_APP_CONTINUE; // carry on with the program!
}
// This function runs once at shutdown.
fn appQuit(appstate: ?*anyopaque, result: c.SDL_AppResult) callconv(.c) void {
_ = appstate;
_ = result;
// SDL will clean up the window/renderer for us.
}