Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 66 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,22 @@ Supports cross compilation and custom platform configuration.

See `build.zig.zon` for the current Zig version, see releases for support for previous Zig versions.

The versions specified in `build.zig.zon` are split between the package version (before the plus) and the SDL version (after the plus). For example, version `1.0.0+3.4.4` is package version 1.0.0 with support for SDL 3.4.4.
The versions specified in `build.zig.zon` are split between the package version (before the plus)
and the SDL version (after the plus). For example, version `1.0.0+3.4.4` is package version 1.0.0
with support for SDL 3.4.4.

For SDL2, see the SDL2 branch.

# Setup

You can add SDL to your project like this by updating `build.zig.zon` from the command line:

```sh
zig fetch --save <url-of-this-repo>
```

And then you can add it to your `build.zig` like this:

```zig
const sdl = b.dependency("sdl", .{
.optimize = optimize,
Expand All @@ -31,6 +35,7 @@ exe.root_module.addImport("sdl3", sdl.module("sdl3"));
```

Finally, you can use SDL's C API from Zig like this:

```zig
const std = @import("std");
const sdl = @import("sdl3");
Expand All @@ -43,6 +48,7 @@ defer sdl.SDL_Quit();
## Example

You can run `src/example.zig` from the command line:

```sh
zig build run-example
```
Expand All @@ -55,11 +61,16 @@ This should produce a window with a pulsing gradient.
SDL_Init failed: No available video device
```

By default, [SDL loads most of its dependencies at runtime on Linux.](https://wiki.libsdl.org/SDL3/README-linux) This lets it decide at runtime which audio drivers to use, whether to use Wayland or X11, etc.
By default,
[SDL loads most of its dependencies at runtime on Linux.](https://wiki.libsdl.org/SDL3/README-linux)
This lets it decide at runtime which audio drivers to use, whether to use Wayland or X11, etc.

For this to work, the libraries SDL is looking for need to be on your `LD_LIBRARY_PATH`. This may
not be the case by default on distributions like NixOS.

For this to work, the libraries SDL is looking for need to be on your `LD_LIBRARY_PATH`. This may not be the case by default on distributions like NixOS.
Here's a `shell.nix` for a Vulkan app as an example of running an SDL application on NixOS with
either X11 or Wayland. You can enter it with `nix-shell shell.nix`.

Here's a `shell.nix` for a Vulkan app as an example of running an SDL application on NixOS with either X11 or Wayland.
```
{ pkgs ? import <nixpkgs> {}}:

Expand All @@ -74,29 +85,42 @@ pkgs.mkShell {
libxkbcommon
vulkan-loader
wayland
xorg.libX11
xorg.libXext
xorg.libXi
libX11
libXext
libXi
udev
]);
}
```

Not all of these dependencies in this example are required. Since both X11 and Wayland dependencies are listed, SDL will use its judgement to decide which to prefer unless `SDL_VIDEODRIVER` is set.
Not all of these dependencies in this example are required. Since both X11 and Wayland dependencies
are listed, SDL will use its judgement to decide which to prefer unless `SDL_VIDEODRIVER` is set.

# System Integration

All dependencies are provided via the build system where possible. If you prefer to get dependencies
from your system, you can override this behavior for individual dependencies with `-fsys` or for all
dependencies with `--system`. See `zig build --help` for more info, also note `--search-prefix`.

# Target Configuration

This library provides a default configuration for common targets:
* [x] Linux (including Steam Deck)
* [x] Steam Deck
* [x] Windows
* [x] macOS (no cross compilation due to Apple licensing)
* [x] iOS (the iOS SDK is not bundled with Zig; pass `-Dinclude_path=$SDK/usr/include -Dframework_path=$SDK/System/Library/Frameworks` with `SDK=$(xcrun --sdk iphoneos --show-sdk-path)`)
* [x] Android (pass `-Dinclude_path=<ndk>/toolchains/llvm/prebuilt/<host>/sysroot/usr/include`)
* [ ] [Emscripten (help wanted!)](https://github.com/allyourcodebase/SDL/issues/5)
* [ ] [Consoles (help wanted!)](https://github.com/allyourcodebase/SDL/issues/6)

You can override the default target configuration by setting `default_target_config` to `false`, and then providing your own configuration. This is typically only necessary when your platform doesn't yet have a default configuration:

- [x] Linux (including Steam Deck)
- [x] Steam Deck
- [x] Windows
- [x] macOS (no cross compilation due to Apple licensing)
- [x] iOS (the iOS SDK is not bundled with Zig; pass
`-Dinclude_path=$SDK/usr/include -Dframework_path=$SDK/System/Library/Frameworks` with
`SDK=$(xcrun --sdk iphoneos --show-sdk-path)`)
- [x] Android (pass `-Dinclude_path=<ndk>/toolchains/llvm/prebuilt/<host>/sysroot/usr/include`)
- [ ] [Emscripten (help wanted!)](https://github.com/allyourcodebase/SDL/issues/5)
- [ ] [Consoles (help wanted!)](https://github.com/allyourcodebase/SDL/issues/6)

You can override the default target configuration by setting `default_target_config` to `false`, and
then providing your own configuration. This is typically only necessary when your platform doesn't
yet have a default configuration:

```zig
const sdl = b.dependency("sdl", .{
.optimize = optimize,
Expand All @@ -107,25 +131,38 @@ const sdl_lib = sdl.artifact("SDL3");
sdl_lib.addIncludePath(...); // Path to your `SDL_build_config.h`, see `windows.zig` for an example of generating this
```

Any other necessary tweaks such as turning of linking with libc, linking with dependencies, or adding other headers can be done here as well.
Any other necessary tweaks such as turning of linking with libc, linking with dependencies, or
adding other headers can be done here as well.

If you're interested in adding default configuration for additional targets, listed or not, contributions are welcome! See [src/linux.zig](src/linux.zig) or [src/windows.zig](src/windows.zig) for examples of how this works.
If you're interested in adding default configuration for additional targets, listed or not,
contributions are welcome! See [src/linux.zig](src/linux.zig) or [src/windows.zig](src/windows.zig)
for examples of how this works.

When making a PR that adds support for a new target:
* Replicate the [default SDL configuration for the target](https://github.com/libsdl-org/SDL/tree/main/include/build_config) within reason
* Pull dependencies in via the build system rather than vendoring them when possible. If this isn't possible, vendor the needed files in `/deps` with a README explaining why they couldn't be pulled in via the build system and any relevant licensing information.
* Cross compilation to all targets should be possible within reason unless forbidden by licensing.
* Update [.github/workflows/ci.yaml](.github/workflows/ci.yaml) to test the new target.

- Replicate the
[default SDL configuration for the target](https://github.com/libsdl-org/SDL/tree/main/include/build_config)
within reason
- Pull dependencies in via the build system rather than vendoring them when possible. If this isn't
possible, vendor the needed files in `/deps` with a README explaining why they couldn't be pulled
in via the build system and any relevant licensing information.
- Cross compilation to all targets should be possible within reason unless forbidden by licensing.
- Update [.github/workflows/ci.yaml](.github/workflows/ci.yaml) to test the new target.

# Updating Dependencies

## SDL

* Modify `build.zig.zon` to point to the desired SDL version
* If you get linker errors or missing headers relating to Wayland protocols on Linux, new Wayland protocols were added upstream. You can fix this by running `zig build wayland-scanner` with `wayland-scanner`.
* If you get any other linker errors or missing files, sources were added or renamed upstream, and you need to update `src/sdl.zon`.
* Check if any headers need to be added to to `src/sdl.h`, or alternatively, if the intentional absence of any headers from this file needs to be documented.
- Modify `build.zig.zon` to point to the desired SDL version
- If you get linker errors or missing headers relating to Wayland protocols on Linux, new Wayland
protocols were added upstream. You can fix this by running `zig build wayland-scanner` with
`wayland-scanner`.
- If you get any other linker errors or missing files, sources were added or renamed upstream, and
you need to update `src/sdl.zon`.
- Check if any headers need to be added to to `src/sdl.h`, or alternatively, if the intentional
absence of any headers from this file needs to be documented.

## SDL's Dependencies

This should rarely be necessary. When it is, you can update their version in `build.zig.zon` if present, and any relevant files in `/deps` if present.
This should rarely be necessary. When it is, you can update their version in `build.zig.zon` if
present, and any relevant files in `/deps` if present.
37 changes: 37 additions & 0 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -172,3 +172,40 @@ pub fn build(b: *std.Build) !void {
const run_step = b.step("run-example", "Run the example app");
run_step.dependOn(&run_example.step);
}

/// Prefetches a list of dependencies, where each dependency has a name field and an args field. If
/// any dependencies are not yet fetched, returns `error.LazyDependencyNeeded`. Saves us from
/// repeatedly earlying out every time a new lazy dependency is needed.
pub fn prefetch(b: *std.Build, comptime deps: anytype) error{LazyDependencyNeeded}!void {
var lazy_deps_needed = false;
inline for (deps) |dep| {
if (!b.systemIntegrationOption(dep.name, .{})) {
if (b.lazyDependency(dep.name, dep.args) == null) {
lazy_deps_needed = true;
}
}
}
if (lazy_deps_needed) {
return error.LazyDependencyNeeded;
}
}

/// Similar to `std.Build.lazyDependency`, but returns `null` if provided by the system with
/// `-fsys`, and otherwise assserts that the dependency was already prefetched (e.g. with
/// `prefetch`.)
pub fn lazyDependency(b: *std.Build, name: []const u8) ?*std.Build.Dependency {
if (b.systemIntegrationOption(name, .{})) return null;
return b.lazyDependency(name, .{}) orelse std.debug.panic("{s} not prefetched", .{name});
}

/// Adds an include path via `lazyDependency`, unless provided via `-fsys`.
pub fn addDepIncludePath(
b: *std.Build,
lib: *std.Build.Step.Compile,
name: []const u8,
sub_path: []const u8,
) void {
if (lazyDependency(b, name)) |dep| {
lib.root_module.addIncludePath(dep.path(sub_path));
}
}
31 changes: 23 additions & 8 deletions build.zig.zon
Original file line number Diff line number Diff line change
Expand Up @@ -9,88 +9,98 @@
.hash = "N-V-__8AACpx-AKEJIUZ0-XrP9-96MpSaw17FJ7L6B7LmO42",
.version = "3.4.18",
},
// Linux dependencies
.dbus = .{
.version = "1.16.2",
.url = "https://gitlab.freedesktop.org/dbus/dbus/-/archive/dbus-1.16.2/dbus-dbus-1.16.2.tar.gz",
.hash = "N-V-__8AALxqaQCXLK7KmtlbRHzj5quTSajWtJZ86Qy7aBxj",
.lazy = true,
},
.x11 = .{
.url = "https://gitlab.freedesktop.org/xorg/lib/libx11/-/archive/libX11-1.8.13/libx11-libX11-1.8.13.tar.gz",
.hash = "N-V-__8AANL5xACqderCBKpnIFKe282dZAikvyc--kNSKWgd",
},
.egl = .{
.url = "https://github.com/KhronosGroup/EGL-Registry/archive/3d7796b3721d93976b6bfe536aa97bbc4bce8667.tar.gz",
.hash = "N-V-__8AAItrbQBen6_Wl2eZ2ax6B0G6qtKys7_uVz224o99",
.lazy = true,
},
.wayland = .{
.version = "1.26.0",
.url = "git+https://gitlab.freedesktop.org/wayland/wayland?ref=1.26.0#87cc8a8728a923fc57938faa81ba0e74f34ecdc7",
.hash = "N-V-__8AAA46JQABKU7sa-6xGUAN44uFsYSUS_w79UVuu7qo",
.lazy = true,
},
.xkbcommon = .{
.version = "1.13.2",
.url = "https://github.com/xkbcommon/libxkbcommon/archive/refs/tags/xkbcommon-1.13.2.tar.gz",
.hash = "N-V-__8AAGh7bAAD9LH6Hh6HB6br9SDZmRBbRu-_1NNpQ4GL",
.lazy = true,
},
.xorgproto = .{
.url = "https://gitlab.freedesktop.org/xorg/proto/xorgproto/-/archive/xorgproto-2025.1/xorgproto-xorgproto-2025.1.tar.gz",
.hash = "N-V-__8AAG86ZgA2-SHGTO4n81-Sj8LNvSASCKZ91wOWZ7Sg",
.lazy = true,
},
.xext = .{
.url = "https://gitlab.freedesktop.org/xorg/lib/libxext/-/archive/libXext-1.3.7/libxext-libXext-1.3.7.tar.gz",
.hash = "N-V-__8AAHTdBwAEWsgYzKEVP-WmGT0wYKIJjBtqH1No6y73",
.lazy = true,
},
.xcb = .{
.url = "https://gitlab.freedesktop.org/xorg/lib/libxcb/-/archive/libxcb-1.17.0/libxcb-libxcb-1.17.0.tar.gz",
.hash = "N-V-__8AAPZxCQD7JyQLx0_D9J9dnkSoP7iziBVxreTgUqlj",
.lazy = true,
},
.ibus = .{
.url = "https://github.com/ibus/ibus/releases/download/1.5.34/ibus-1.5.34.tar.gz",
.hash = "N-V-__8AANTkvAHAAXsApyhePM0ufoLcIQxEGXVB6Q7n7grW",
.lazy = true,
},
.usb = .{
.url = "https://github.com/libusb/libusb/archive/refs/tags/v1.0.30.zip",
.hash = "N-V-__8AAP6YHABRm4muBuwd_qlKgvsenaOLg962zfsjQr8B",
.lazy = true,
},
.xcursor = .{
.version = "1.2.3",
.url = "https://gitlab.freedesktop.org/xorg/lib/libxcursor/-/archive/libXcursor-1.2.3/libxcursor-libXcursor-1.2.3.tar.gz",
.hash = "N-V-__8AAKtlAgAAqwksheZkCSajEEPpi2XivNet-_3hZyz2",
.lazy = true,
},
.xi = .{
.url = "https://gitlab.freedesktop.org/xorg/lib/libxi/-/archive/libXi-1.8.3/libxi-libXi-1.8.3.tar.gz",
.hash = "N-V-__8AAPR0DQAuD3_ZopKbezyogf0SLNe6GiYF3RlaaNrh",
.lazy = true,
},
.xfixes = .{
.url = "https://gitlab.freedesktop.org/xorg/lib/libxfixes/-/archive/libXfixes-6.0.2/libxfixes-libXfixes-6.0.2.tar.gz",
.hash = "N-V-__8AAFIgAQCJSug37AGy4zypGtiDtUR9wtQa8U74Nm0a",
.lazy = true,
},
.xrandr = .{
.url = "https://gitlab.freedesktop.org/xorg/lib/libxrandr/-/archive/libXrandr-1.5.5/libxrandr-libXrandr-1.5.5.tar.gz",
.hash = "N-V-__8AAGlRAgBdkf1F-Y410qIz6cV4C62ZAUk_SGeUVriw",
.lazy = true,
},
.xrender = .{
.url = "https://gitlab.freedesktop.org/xorg/lib/libxrender/-/archive/libXrender-0.9.12/libxrender-libXrender-0.9.12.tar.gz",
.hash = "N-V-__8AANorBADbt-RlITZJofNvQWTiaQlv7r7lWYtxAqGx",
.lazy = true,
},
.decor = .{
.url = "https://gitlab.freedesktop.org/libdecor/libdecor/-/archive/0.2.5/libdecor-0.2.5.tar.gz",
.hash = "N-V-__8AADG6BADZ6vLbOD3OrWhDYa5jrubmgY-ljspuv-4R",
.lazy = true,
.version = "0.2.5",
},
.opengl = .{
.url = "https://github.com/KhronosGroup/OpenGL-Registry/archive/e8f7cd0e35ac8d6f5667a021ff83d04b1fec41ef.tar.gz",
.hash = "N-V-__8AADsI5wqNqQMgAmqSba8PUrQcWdd3Svxc8GoX8SgY",
},
.pipewire = .{
.url = "https://gitlab.freedesktop.org/pipewire/pipewire/-/archive/1.6.8/pipewire-1.6.8.tar.gz",
.hash = "N-V-__8AAEBm3gCliUlb1HgAJA-3XvTWKC2YfRa5hJMiaEld",
.lazy = true,
.version = "1.6.8",
.api_version = "0.3", // From `meson.build`, does not directly track `version`!
},
.jack = .{
.url = "https://github.com/jackaudio/jack2/archive/refs/tags/jack2-control-api-relicense-nedko.tar.gz",
.hash = "N-V-__8AANOERwAn9QBxYJf43h9P3jq2HYTPvXlZRCHPEmrj",
.lazy = true,
},
.pulseaudio = .{
// This dependency must be kept in sync with `pulseaudio_windows`.
Expand All @@ -117,25 +127,30 @@
// This dependency must be kept in sync with `xscrnsaver_windows` for similar reasons explained in the `pulseaudio` dependency.
.url = "https://gitlab.freedesktop.org/xorg/lib/libxscrnsaver/-/archive/libXScrnSaver-1.2.5/libxscrnsaver-libXScrnSaver-1.2.5.tar.gz",
.hash = "N-V-__8AAMXRAADYeHz48Y1IuZUzOOqRvOO7f0wrdeNkWcBF",
.lazy = true,
},
.alsa = .{
.url = "https://github.com/alsa-project/alsa-lib/archive/refs/tags/v1.2.16.1.tar.gz",
.hash = "N-V-__8AACQ2RADoahGXBHOr7fJYspVVN8sMHwQn2C-kzECe",
.lazy = true,
},
.sndio = .{
.url = "https://sndio.org/sndio-1.10.0.tar.gz",
.hash = "N-V-__8AAKA0CgB5SuKmufbOKBnZsjDFZuUTNO70MHQEf1p8",
.lazy = true,
},
.fribidi = .{
.version = "1.0.16",
.interface_version = 4,
.unicode_version = "16.0.0",
.url = "https://github.com/fribidi/fribidi/archive/refs/tags/v1.0.16.tar.gz",
.hash = "N-V-__8AABbxDAFJr4_dchs33gEqLRpBMP2oUBTuIueW9u_h",
.lazy = true,
},
.thai = .{
.url = "https://github.com/tlwg/libthai/archive/refs/tags/v0.1.30.tar.gz",
.hash = "N-V-__8AAAqMIgArahV5mT0LGKWmDhZC9QtlMIZlaT9yp_lP",
.lazy = true,
},
},
.paths = .{
Expand Down
6 changes: 4 additions & 2 deletions src/android.zig
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ pub fn build(
build_config_h: *std.Build.Step.ConfigHeader,
paths: root.SystemPaths,
) void {
const sdl = b.dependency("sdl", .{});

// NDK headers are not bundled with Zig; bionic's arch-specific headers (e.g. `jni.h`
// adjacent files, `<triple>/asm/...`) live in a per-triple subdirectory of usr/include.
const include_path = root.SystemPaths.print_missing_system_path_option(
Expand Down Expand Up @@ -37,13 +39,13 @@ pub fn build(
// The core/video/audio/sensor/camera Android sources live in the generic group that
// build.zig already compiles for every target; listing them here too duplicates them.
.files = &(sources.android ++ sources.pthread),
.root = b.dependency("sdl", .{}).path("src"),
.root = sdl.path("src"),
.flags = root.flags,
});
// SDL_android.c registers HIDDeviceManager_tab, which only hid.cpp defines; with
// SDL_HIDAPI_DISABLED it compiles to JNI stubs that use no C++ stdlib.
lib.root_module.addCSourceFile(.{
.file = b.dependency("sdl", .{}).path("src/hidapi/android/hid.cpp"),
.file = sdl.path("src/hidapi/android/hid.cpp"),
.flags = &(root.flags.* ++ [_][]const u8{ "-fno-exceptions", "-fno-rtti" }),
});

Expand Down
Loading
Loading