zig-sdl3-bindings
0
总安装量
1
周安装量
安装命令
npx skills add https://github.com/rudedogg/zig-skills --skill zig-sdl3-bindings
Agent 安装分布
amp
1
cline
1
opencode
1
cursor
1
continue
1
kimi-cli
1
Skill 文档
Zig SDL3 Bindings Reference
Idiomatic Zig bindings for SDL3, wrapping the C API with Zig patterns: error unions, optionals, slices, and defer-based resource management.
Version: 0.1.7+ (SDL 3.4.0+)
Critical: Build Configuration
build.zig.zon Dependency
.dependencies = .{
.sdl3 = .{
.url = "git+https://codeberg.org/7Games/zig-sdl3#main",
.hash = "...", // Get from build error on first run
},
},
build.zig Setup
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
// Get the SDL3 dependency
const sdl3_dep = b.dependency("sdl3", .{
.target = target,
.optimize = optimize,
// Build options:
// .callbacks = true, // Enable SDL main callbacks pattern
// .main = true, // Enable SDL_main
// .ext_image = true, // Enable SDL_image extension
// .ext_ttf = true, // Enable SDL_ttf extension
// .ext_net = true, // Enable SDL_net extension
});
const exe = b.addExecutable(.{
.name = "my-game",
.root_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
}),
});
// Add SDL3 module and link
exe.root_module.addImport("sdl3", sdl3_dep.module("sdl3"));
b.installArtifact(exe);
}
Import in Code
const sdl3 = @import("sdl3");
Critical: Two Application Patterns
Pattern 1: Traditional Main Loop (Recommended for most apps)
const sdl3 = @import("sdl3");
pub fn main() !void {
// ALWAYS call shutdown at end, even on error
defer sdl3.shutdown();
// Initialize subsystems
const init_flags = sdl3.InitFlags{ .video = true, .audio = true };
try sdl3.init(init_flags);
defer sdl3.quit(init_flags);
// Create window
const window = try sdl3.video.Window.init("My Game", 800, 600, .{});
defer window.deinit();
// Optional: Create renderer for 2D graphics
const renderer = try sdl3.render.Renderer.init(window, null);
defer renderer.deinit();
// FPS limiter (provided by zig-sdl3)
var fps_capper = sdl3.extras.FramerateCapper(f32){ .mode = .{ .limited = 60 } };
var running = true;
while (running) {
const dt = fps_capper.delay(); // Delta time in seconds
// Event handling
while (sdl3.events.poll()) |event| {
switch (event) {
.quit, .terminating => running = false,
.key_down => |key| {
if (key.scancode == .escape) running = false;
},
else => {},
}
}
// Update game state using dt...
// Render
try renderer.setDrawColor(.{ .r = 0, .g = 0, .b = 0, .a = 255 });
try renderer.clear();
// Draw game objects...
try renderer.present();
}
}
Pattern 2: App Callbacks (Mobile-friendly, required for some platforms)
Enable with -Dcallbacks=true build option:
const sdl3 = @import("sdl3");
const std = @import("std");
const allocator = std.heap.smp_allocator;
const AppState = struct {
window: sdl3.video.Window,
renderer: sdl3.render.Renderer,
fps_capper: sdl3.extras.FramerateCapper(f32),
};
fn init(app_state: *?*AppState, args: [][*:0]u8) !sdl3.AppResult {
_ = args;
const window = try sdl3.video.Window.init("Callback App", 800, 600, .{});
errdefer window.deinit();
const renderer = try sdl3.render.Renderer.init(window, null);
errdefer renderer.deinit();
const state = try allocator.create(AppState);
state.* = .{
.window = window,
.renderer = renderer,
.fps_capper = .{ .mode = .{ .limited = 60 } },
};
app_state.* = state;
return .run; // Continue running
}
fn iterate(app_state: *AppState) !sdl3.AppResult {
const dt = app_state.fps_capper.delay();
_ = dt;
try app_state.renderer.setDrawColor(.{ .r = 100, .g = 50, .b = 200, .a = 255 });
try app_state.renderer.clear();
try app_state.renderer.present();
return .run;
}
fn event(app_state: *AppState, curr_event: sdl3.events.Event) !sdl3.AppResult {
_ = app_state;
return switch (curr_event) {
.quit, .terminating => .success, // Exit successfully
else => .run,
};
}
fn quit(app_state: ?*AppState, result: sdl3.AppResult) void {
_ = result;
if (app_state) |state| {
state.renderer.deinit();
state.window.deinit();
allocator.destroy(state);
}
}
pub fn main() u8 {
sdl3.main_funcs.setMainReady();
var args = [_:null]?[*:0]u8{ @constCast("My App") };
return sdl3.main_funcs.enterAppMainCallbacks(&args, AppState, init, iterate, event, quit);
}
Critical: Error Handling
All SDL functions that can fail return Zig error unions:
// Functions return !T (error union)
const window = try sdl3.video.Window.init("Title", 800, 600, .{});
// Get error message on failure
const window = sdl3.video.Window.init("Title", 800, 600, .{}) catch |err| {
std.debug.print("SDL Error: {?s}\n", .{sdl3.errors.get()});
return err;
};
// Optional: Set thread-local error callback for debugging
sdl3.errors.error_callback = struct {
fn callback(err: ?[:0]const u8) void {
std.debug.print("SDL Error: {?s}\n", .{err});
}
}.callback;
Common Error Type
pub const Error = error{
SdlError, // All SDL errors map to this
};
Critical: Resource Management with Defer
Always use defer for cleanup:
pub fn main() !void {
defer sdl3.shutdown(); // ALWAYS first
try sdl3.init(.{ .video = true });
defer sdl3.quit(.{ .video = true });
const window = try sdl3.video.Window.init("Title", 800, 600, .{});
defer window.deinit();
const renderer = try sdl3.render.Renderer.init(window, null);
defer renderer.deinit();
const texture = try renderer.createTexture(.rgba8888, .streaming, 256, 256);
defer texture.deinit();
}
Critical: Type Conversions (fromSdl/toSdl)
The bindings convert between C and Zig types using consistent patterns:
// Enum conversions
const zig_theme: sdl3.video.SystemTheme = .dark;
const c_theme = zig_theme.toSdl(); // Zig -> C
const back_to_zig = sdl3.video.SystemTheme.fromSdl(c_theme); // C -> Zig
// Struct conversions
const zig_rect = sdl3.rect.IRect{ .x = 10, .y = 20, .w = 100, .h = 50 };
const c_rect = zig_rect.toSdl(); // Zig -> C
const back = sdl3.rect.IRect.fromSdl(c_rect); // C -> Zig
// Flag struct conversions
const flags = sdl3.InitFlags{ .video = true, .audio = true };
const c_flags = flags.toSdl(); // Zig -> C bitmask
const back_flags = sdl3.InitFlags.fromSdl(c_flags); // C bitmask -> Zig
// Nullable conversions (C uses special values for "none")
const format: ?sdl3.pixels.Format = sdl3.pixels.Format.fromSdl(c_format);
const c_format = sdl3.pixels.Format.toSdl(format); // null -> SDL_PIXELFORMAT_UNKNOWN
Critical: Custom Allocator Integration
Use Zig allocators for SDL memory management:
const std = @import("std");
const sdl3 = @import("sdl3");
pub fn main() !void {
// Set up allocator BEFORE any SDL calls
var debug_allocator = std.heap.DebugAllocator(.{}).init;
_ = try sdl3.setMemoryFunctionsByAllocator(debug_allocator.allocator());
// Check for leaks at end
defer if (debug_allocator.detectLeaks()) @panic("Memory leaks detected!");
defer sdl3.shutdown();
// ... rest of app
}
Quick Reference: Initialization Flags
const sdl3 = @import("sdl3");
// Individual subsystems
try sdl3.init(.{ .video = true }); // Windowing
try sdl3.init(.{ .audio = true }); // Audio playback/recording
try sdl3.init(.{ .events = true }); // Event queue (auto-init by video/audio)
try sdl3.init(.{ .joystick = true }); // Raw joystick access
try sdl3.init(.{ .gamepad = true }); // Controller with standard layout
try sdl3.init(.{ .haptic = true }); // Force feedback
try sdl3.init(.{ .sensor = true }); // Accelerometer, gyro
try sdl3.init(.{ .camera = true }); // Webcam access
// Everything at once
try sdl3.init(sdl3.InitFlags.everything);
// Clean shutdown
defer sdl3.quit(.{ .video = true, .audio = true }); // Quit specific
defer sdl3.shutdown(); // Quit everything
Quick Reference: Window Creation
const sdl3 = @import("sdl3");
// Basic window
const window = try sdl3.video.Window.init("Title", 800, 600, .{});
defer window.deinit();
// Window with flags
const window = try sdl3.video.Window.init("Fullscreen", 1920, 1080, .{
.fullscreen = true,
.high_pixel_density = true,
});
// Common window flags
.{
.fullscreen = true, // Fullscreen exclusive
.borderless = true, // No window decorations
.resizable = true, // User can resize
.minimized = true, // Start minimized
.maximized = true, // Start maximized
.high_pixel_density = true, // HiDPI support
.always_on_top = true, // Stay above other windows
.hidden = true, // Create hidden
.open_gl = true, // OpenGL context
.vulkan = true, // Vulkan surface
.metal = true, // Metal layer
}
// Window operations
try window.setTitle("New Title");
try window.setSize(1024, 768);
try window.setPosition(.{ .absolute = 100 }, .{ .absolute = 100 });
try window.show();
try window.hide();
try window.setFullscreen(true);
Quick Reference: 2D Rendering
const sdl3 = @import("sdl3");
// Create renderer
const renderer = try sdl3.render.Renderer.init(window, null);
defer renderer.deinit();
// Clear screen
try renderer.setDrawColor(.{ .r = 0, .g = 0, .b = 0, .a = 255 });
try renderer.clear();
// Draw filled rectangle
try renderer.setDrawColor(.{ .r = 255, .g = 0, .b = 0, .a = 255 });
try renderer.renderFillRect(.{ .x = 100, .y = 100, .w = 200, .h = 150 });
// Draw outline
try renderer.renderRect(.{ .x = 50, .y = 50, .w = 100, .h = 100 });
// Draw line
try renderer.renderLine(.{ .x = 0, .y = 0 }, .{ .x = 800, .y = 600 });
// Draw point
try renderer.renderPoint(.{ .x = 400, .y = 300 });
// Load and draw texture
const surface = try sdl3.image.loadFile("sprite.png"); // Requires ext_image
defer surface.deinit();
const texture = try renderer.createTextureFromSurface(surface);
defer texture.deinit();
try renderer.renderTexture(texture, null, .{ .x = 100, .y = 100, .w = 64, .h = 64 });
// Present frame
try renderer.present();
Quick Reference: Event Handling
const sdl3 = @import("sdl3");
// Poll events
while (sdl3.events.poll()) |event| {
switch (event) {
// Application events
.quit => break, // Window close, Alt+F4
.terminating => break, // OS killing app
.low_memory => freeCache(), // Memory pressure
// Keyboard
.key_down => |k| {
if (k.scancode == .escape) break;
if (k.key == .space and !k.repeat) jump();
},
.key_up => |k| handleKeyUp(k.scancode),
.text_input => |t| appendText(t.text),
// Mouse
.mouse_motion => |m| updateCursor(m.x, m.y),
.mouse_button_down => |m| {
if (m.button == .left) onClick(m.x, m.y);
},
.mouse_wheel => |w| scroll(w.scroll_y),
// Gamepad
.gamepad_button_down => |g| {
if (g.button == .south) jump(); // A/Cross button
},
.gamepad_axis_motion => |g| {
if (g.axis == .left_x) moveHorizontal(g.value);
},
// Window events
.window_resized => |w| resize(w.width, w.height),
.window_focus_gained => resumeGame(),
.window_focus_lost => pauseGame(),
else => {},
}
}
// Wait for events (blocks thread)
if (sdl3.events.wait()) |event| {
// Handle event
}
// Wait with timeout
if (sdl3.events.waitTimeout(1000)) |event| {
// Handle event
}
Quick Reference: Audio Playback
const sdl3 = @import("sdl3");
// Load WAV file (returns tuple: Spec + data)
const spec, const audio_buf = try sdl3.audio.loadWav("sound.wav");
defer sdl3.free(audio_buf.ptr);
// Open audio device
const device = try sdl3.audio.Device.default_playback.open(spec);
defer device.close();
// Create stream and bind to device
const stream = try sdl3.audio.Stream.init(spec, spec);
defer stream.deinit();
try device.bindStream(stream);
// Queue audio data
try stream.putData(audio_buf);
// Resume playback (devices start paused)
try device.resumePlayback();
Module Reference
Core
- Getting Started – Build setup, project template, dependencies
- Init & Lifecycle – Initialization, main loop vs callbacks, shutdown
- Video & Windows – Window creation, displays, surfaces, pixels
- Render – 2D renderer, textures, drawing primitives
- GPU – Modern GPU API (3D, compute, shaders)
- Events – Event handling, polling, filtering
Input
- Keyboard – Keyboard input, keycodes, scancodes
- Mouse – Mouse input, cursors, relative mode
- Gamepad & Joystick – Controllers, haptics, sensors
- Touch & Pen – Touch screens, stylus input
Media
- Audio – Audio streams, playback, recording, WAV
- Image – Image loading (SDL_image extension)
- TTF – Font rendering (SDL_ttf extension)
- Camera – Video capture, webcams
System
- Filesystem & I/O – File I/O, paths, directories, async I/O
- Storage – Platform storage, cloud saves
- Clipboard – Clipboard operations
- Networking – TCP/UDP (SDL_net extension)
- System & Platform – Platform detection, CPU info, locale
- Threading – Threads, mutexes, atomics
Configuration
- Properties & Hints – Configuration hints, property bags
- Log & Errors – Logging, error handling patterns
- Dialogs & UI – Message boxes, file dialogs, tray icons
Patterns
- Binding Patterns – fromSdl/toSdl, callback wrappers, C interop
- Allocator Integration – Custom Zig allocators with SDL
Common Type Mappings
| SDL3 C Type | zig-sdl3 Type |
|---|---|
SDL_Window* |
sdl3.video.Window |
SDL_Renderer* |
sdl3.render.Renderer |
SDL_Texture* |
sdl3.render.Texture |
SDL_Surface* |
sdl3.surface.Surface |
SDL_Event |
sdl3.events.Event (tagged union) |
SDL_Rect |
sdl3.rect.IRect |
SDL_FRect |
sdl3.rect.FRect |
SDL_Point |
sdl3.rect.IPoint |
SDL_FPoint |
sdl3.rect.FPoint |
SDL_Color |
sdl3.pixels.Color |
SDL_Keycode |
sdl3.keycode.Keycode |
SDL_Scancode |
sdl3.Scancode |
SDL_AudioDeviceID |
sdl3.audio.Device |
SDL_AudioStream* |
sdl3.audio.Stream |
SDL_GPUDevice* |
sdl3.gpu.Device |
SDL_Gamepad* |
sdl3.gamepad.Gamepad |
SDL_Joystick* |
sdl3.joystick.Joystick |
SDL_PropertiesID |
sdl3.properties.Group |
Platform Support
zig-sdl3 supports all SDL3 platforms:
- Windows (x86_64, ARM64)
- macOS (x86_64, ARM64)
- Linux (x86_64, ARM64)
- iOS
- Android
- Emscripten (WebAssembly)
Cross-compilation works via Zig’s cross-compilation:
zig build -Dtarget=x86_64-windows
zig build -Dtarget=aarch64-macos
zig build -Dtarget=wasm32-emscripten
Version Compatibility
| zig-sdl3 | SDL3 | Zig |
|---|---|---|
| 0.1.7+ | 3.4.0+ | 0.15.1+ |
| 0.1.0-0.1.6 | 3.2.0+ | 0.14.0+ |
Raw C Access
For functions not yet wrapped:
const sdl3 = @import("sdl3");
const c = sdl3.c; // Raw SDL3 C API
// Use C functions directly
const result = c.SDL_SomeUnwrappedFunction();