# SDL.zig A Zig package that provides you with the means to link SDL2 to your project, as well as a Zig-infused header implementation (allows you to not have the SDL2 headers on your system and still compile for SDL2) and a shallow wrapper around the SDL apis that allow a more Zig-style coding with Zig error handling and tagged unions. ## Getting started ### Linking SDL2 to your project This is an example `build.zig` that will link the SDL2 library to your project. ```zig const std = @import("std"); const sdl = @import("sdl"); // Replace with the actual name in your build.zig.zon pub fn build(b: *std.Build) !void { // Determine compilation target const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); // Create a new instance of the SDL2 Sdk // Specify dependency name explicitly if necessary (use sdl by default) const sdk = sdl.init(b, .{}); // Create executable for our example const root_module = b.createModule(.{ .root_source_file = b.path("my-game.zig"), .target = target, .optimize = optimize, }); const demo_basic = b.addExecutable(.{ .name = "demo-basic", .root_module = root_module, }); sdk.link(demo_basic, .dynamic, sdl.Library.SDL2); // link SDL2 as a shared library // Add "sdl2" package that exposes the SDL2 api (like SDL_Init or SDL_CreateWindow) root_module.addImport("sdl2", sdk.getNativeModule()); // Install the executable into the prefix when invoking "zig build" b.installArtifact(demo_basic); } ``` ### Using the native API This package exposes the SDL2 API as defined in the SDL headers. Use this to create a normal SDL2 program: ```zig const std = @import("std"); const SDL = @import("sdl2"); // Add this package by using sdk.getNativeModule pub fn main() !void { if (SDL.SDL_Init(SDL.SDL_INIT_VIDEO | SDL.SDL_INIT_EVENTS | SDL.SDL_INIT_AUDIO) < 0) sdlPanic(); defer SDL.SDL_Quit(); var window = SDL.SDL_CreateWindow( "SDL2 Native Demo", SDL.SDL_WINDOWPOS_CENTERED, SDL.SDL_WINDOWPOS_CENTERED, 640, 480, SDL.SDL_WINDOW_SHOWN, ) orelse sdlPanic(); defer _ = SDL.SDL_DestroyWindow(window); var renderer = SDL.SDL_CreateRenderer(window, -1, SDL.SDL_RENDERER_ACCELERATED) orelse sdlPanic(); defer _ = SDL.SDL_DestroyRenderer(renderer); mainLoop: while (true) { var ev: SDL.SDL_Event = undefined; while (SDL.SDL_PollEvent(&ev) != 0) { if(ev.type == SDL.SDL_QUIT) break :mainLoop; } _ = SDL.SDL_SetRenderDrawColor(renderer, 0xF7, 0xA4, 0x1D, 0xFF); _ = SDL.SDL_RenderClear(renderer); SDL.SDL_RenderPresent(renderer); } } fn sdlPanic() noreturn { const str = @as(?[*:0]const u8, SDL.SDL_GetError()) orelse "unknown error"; @panic(std.mem.sliceTo(str, 0)); } ``` ### Using the wrapper API This package also exposes the SDL2 API with a more Zig-style API. Use this if you want a more convenient Zig experience. **Note:** This API is experimental and might change in the future ```zig const std = @import("std"); const SDL = @import("sdl2"); // Created in build.zig by using exe.root_module.addImport("sdl2", sdk.getWrapperModule()); pub fn main() !void { try SDL.init(.{ .video = true, .events = true, .audio = true, }); defer SDL.quit(); var window = try SDL.createWindow( "SDL2 Wrapper Demo", .{ .centered = {} }, .{ .centered = {} }, 640, 480, .{ .vis = .shown }, ); defer window.destroy(); var renderer = try SDL.createRenderer(window, null, .{ .accelerated = true }); defer renderer.destroy(); mainLoop: while (true) { while (SDL.pollEvent()) |ev| { switch (ev) { .quit => break :mainLoop, else => {}, } } try renderer.setColorRGB(0xF7, 0xA4, 0x1D); try renderer.clear(); renderer.present(); } } ``` ## `build.zig` API ```zig /// Just call `Sdk.init(b, .{})` to obtain a handle to the Sdk! /// Use `sdl` as dependency name by default. const Sdk = @This(); /// Creates a instance of the Sdk and initializes internal steps. /// Initialize once, use everywhere (in your `build` function). /// /// const SdkOption = struct { /// dep_name: ?[]const u8 = "sdl", /// maybe_config_path: ?[]const u8 = null, /// maybe_sdl_ttf_config_path: ?[]const u8 = null, /// }; pub fn init(b: *Build, opt: SdkOption) *Sdk /// Returns a module with the raw SDL api with proper argument types, but no functional/logical changes /// for a more *ziggy* feeling. /// This is similar to the *C import* result. pub fn getNativeModule(sdk: *Sdk) *Build.Module; /// Returns a module with the raw SDL api with proper argument types, but no functional/logical changes /// for a more *ziggy* feeling, with Vulkan support! The Vulkan module provided by `vulkan-zig` must be /// provided as an argument. /// This is similar to the *C import* result. pub fn getNativeModuleVulkan(sdk: *Sdk, vulkan: *Build.Module) *Build.Module; /// Returns the smart wrapper for the SDL api. Contains convenient zig types, tagged unions and so on. pub fn getWrapperModule(sdk: *Sdk) *Build.Module; /// Returns the smart wrapper with Vulkan support. The Vulkan module provided by `vulkan-zig` must be /// provided as an argument. pub fn getWrapperModuleVulkan(sdk: *Sdk, vulkan: *Build.Module) *Build.Module; /// Links SDL2 or SDL2_ttf to the given exe and adds required installs if necessary. /// **Important:** The target of the `exe` must already be set, otherwise the Sdk will do the wrong thing! pub fn link(sdk: *Sdk, exe: *Build.Step.Compile, linkage: std.builtin.LinkMode, comptime library: Library) void; ``` ## Dependencies All of those are dependencies for the *target* platform, not for your host. Zig will run/build the same on all source platforms. ### Windows For Windows, you need to fetch the correct dev libraries from the [SDL download page](https://www.libsdl.org/download-2.0.php). It is recommended to use the MinGW versions if you don't require MSVC compatibility. ### MacOS Right now, cross-compiling for MacOS isn't possible. On a Mac, install SDL2 via `brew`. ### Linux If you are cross-compiling, no dependencies exist. The build Sdk compiles a `libSDL2.so` stub which is used for linking. If you compile to your target platform, you require SDL2 to be installed via your OS package manager. ## Support Matrix This project tries to provide you the best possible development experience for SDL2. Thus, this project supports the maximum amount of cross-compilation targets for SDL2. The following table documents this. The rows document the *target* whereas the columns are the *build host*: | | Windows (x86_64) | Windows (i386) | Linux (x86_64) | MacOS (x86_64) | MacOS (aarch64) | |-----------------------|------------------|----------------|----------------|----------------|-----------------| | `i386-windows-gnu` | ✅ | ✅ | ✅ | ✅ | ⚠️ | | `i386-windows-msvc` | ✅ | ✅ | ✅ | ✅ | ⚠️ | | `x86_64-windows-gnu` | ✅ | ✅ | ✅ | ✅ | ⚠️ | | `x86_64-windows-msvc` | ✅ | ✅ | ✅ | ✅ | ⚠️ | | `x86_64-macos` | ❌ | ❌ | ❌ | ✅ | ❌ | | `aarch64-macos` | ❌ | ❌ | ❌ | ❌ | ⚠️ | | `x86_64-linux-gnu` | 🧪 | 🧪 | ✅ | 🧪 | ⚠️ | | `aarch64-linux-gnu` | 🧪 | 🧪 | 🧪 | 🧪 | ⚠️ | Legend: - ✅ Cross-compilation is known to work and tested via CI - 🧪 Experimental cross-compilation support, covered via CI - ⚠️ Cross-compilation *might* work, but is not tested via CI - ❌ Cross-compilation is not possible right now ## Contributing You can contribute to this project in several ways: - Use it! This helps me to track bugs (which i know that there are some), and usability defects (which we can resolve then). I want this library to have the best development experience possible. - Implement/improve the linking experience: Right now, it's not possible to cross-compile for MacOS, which is very sad. We might find a way to do so, though! Also VCPKG is not well supported on windows platforms. - Improve the wrapper. Just add the functions you need and make a PR. Or improve existing ones. I won't do it for you, so you have to get your own hands dirty!