From 834dad44ee12fa49fbe897eaa17e7bcb57ec47a5 Mon Sep 17 00:00:00 2001 From: Nathan Anderson <n8r@tuta.io> Date: Mon, 30 Dec 2024 09:26:12 -0700 Subject: [PATCH] First window rendered, sdl here I come --- .envrc | 1 + .gitignore | 2 + build.zig | 145 +++++++++++++++++++++++++++++++++++++++++++++ build.zig.zon | 16 +++++ flake.lock | 92 ++++++++++++++++++++++++++++ flake.nix | 27 +++++++++ nix/zls/flake.lock | 26 ++++++++ nix/zls/flake.nix | 33 +++++++++++ src/main.zig | 72 ++++++++++++++++++++++ src/root.zig | 10 ++++ src/zig.bmp | Bin 0 -> 168138 bytes 11 files changed, 424 insertions(+) create mode 100644 .envrc create mode 100644 .gitignore create mode 100644 build.zig create mode 100644 build.zig.zon create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 nix/zls/flake.lock create mode 100644 nix/zls/flake.nix create mode 100644 src/main.zig create mode 100644 src/root.zig create mode 100644 src/zig.bmp diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..914e310 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.direnv/** +.zig-cache/** diff --git a/build.zig b/build.zig new file mode 100644 index 0000000..de7f030 --- /dev/null +++ b/build.zig @@ -0,0 +1,145 @@ +const std = @import("std"); + +const Build = @import("std").Build; + +pub fn build(b: *Build) void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + const exe = b.addExecutable(.{ + .name = "zsdl", + .root_source_file = .{ .src_path = .{ .owner = b, .sub_path = "src/main.zig" } }, + .target = target, + .optimize = optimize, + }); + if (target.query.isNativeOs() and target.result.os.tag == .linux) { + // The SDL package doesn't work for Linux yet, so we rely on system + // packages for now. + exe.linkSystemLibrary("SDL2"); + exe.linkLibC(); + } else { + const sdl_dep = b.dependency("sdl", .{ + .optimize = .ReleaseFast, + .target = target, + }); + exe.linkLibrary(sdl_dep.artifact("SDL2")); + } + + b.installArtifact(exe); + + // Create Check step for zls + const exe_check = b.addExecutable(.{ + .name = "zsdl", + .root_source_file = .{ .src_path = .{ .owner = b, .sub_path = "src/main.zig" } }, + .target = target, + .optimize = optimize, + }); + if (target.query.isNativeOs() and target.result.os.tag == .linux) { + // The SDL package doesn't work for Linux yet, so we rely on system + // packages for now. + exe_check.linkSystemLibrary("SDL2"); + exe_check.linkLibC(); + } else { + const sdl_dep = b.dependency("sdl", .{ + .optimize = .ReleaseFast, + .target = target, + }); + exe_check.linkLibrary(sdl_dep.artifact("SDL2")); + } + const check = b.step("check", "Check if project compiles, used by Zig Language Server"); + check.dependOn(&exe_check.step); + + const run = b.step("run", "Run the demo"); + const run_cmd = b.addRunArtifact(exe); + run.dependOn(&run_cmd.step); +} + +// // Although this function looks imperative, note that its job is to +// // declaratively construct a build graph that will be executed by an external +// // runner. +// pub fn build(b: *std.Build) void { +// // Standard target options allows the person running `zig build` to choose +// // what target to build for. Here we do not override the defaults, which +// // means any target is allowed, and the default is native. Other options +// // for restricting supported target set are available. +// const target = b.standardTargetOptions(.{}); + +// // Standard optimization options allow the person running `zig build` to select +// // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not +// // set a preferred release mode, allowing the user to decide how to optimize. +// const optimize = b.standardOptimizeOption(.{}); + +// const lib = b.addStaticLibrary(.{ +// .name = "zsdl", +// // In this case the main source file is merely a path, however, in more +// // complicated build scripts, this could be a generated file. +// .root_source_file = b.path("src/root.zig"), +// .target = target, +// .optimize = optimize, +// }); + +// // This declares intent for the library to be installed into the standard +// // location when the user invokes the "install" step (the default step when +// // running `zig build`). +// b.installArtifact(lib); + +// const exe = b.addExecutable(.{ +// .name = "zsdl", +// .root_source_file = b.path("src/main.zig"), +// .target = target, +// .optimize = optimize, +// }); + +// // This declares intent for the executable to be installed into the +// // standard location when the user invokes the "install" step (the default +// // step when running `zig build`). +// b.installArtifact(exe); + +// // This *creates* a Run step in the build graph, to be executed when another +// // step is evaluated that depends on it. The next line below will establish +// // such a dependency. +// const run_cmd = b.addRunArtifact(exe); + +// // By making the run step depend on the install step, it will be run from the +// // installation directory rather than directly from within the cache directory. +// // This is not necessary, however, if the application depends on other installed +// // files, this ensures they will be present and in the expected location. +// run_cmd.step.dependOn(b.getInstallStep()); + +// // This allows the user to pass arguments to the application in the build +// // command itself, like this: `zig build run -- arg1 arg2 etc` +// if (b.args) |args| { +// run_cmd.addArgs(args); +// } + +// // This creates a build step. It will be visible in the `zig build --help` menu, +// // and can be selected like this: `zig build run` +// // This will evaluate the `run` step rather than the default, which is "install". +// const run_step = b.step("run", "Run the app"); +// run_step.dependOn(&run_cmd.step); + +// // Creates a step for unit testing. This only builds the test executable +// // but does not run it. +// const lib_unit_tests = b.addTest(.{ +// .root_source_file = b.path("src/root.zig"), +// .target = target, +// .optimize = optimize, +// }); + +// const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests); + +// const exe_unit_tests = b.addTest(.{ +// .root_source_file = b.path("src/main.zig"), +// .target = target, +// .optimize = optimize, +// }); + +// const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests); + +// // Similar to creating the run step earlier, this exposes a `test` step to +// // the `zig build --help` menu, providing a way for the user to request +// // running the unit tests. +// const test_step = b.step("test", "Run unit tests"); +// test_step.dependOn(&run_lib_unit_tests.step); +// test_step.dependOn(&run_exe_unit_tests.step); +// } diff --git a/build.zig.zon b/build.zig.zon new file mode 100644 index 0000000..176bfa3 --- /dev/null +++ b/build.zig.zon @@ -0,0 +1,16 @@ +.{ + .name = "zsdl", + .version = "0.0.1", + .minimum_zig_version = "0.13.0", + .dependencies = .{ + .SDL = .{ + .url = "https://github.com/allyourcodebase/SDL/archive/7e4fc30b201d266f197fef4153f7e046bd189a7c.tar.gz", + .hash = "1220c57b0bec66a2378e90cd7a79e36f9e7f60c02acca769ab3e6f974d4ef6766418", + }, + }, + .paths = .{ + "build.zig", + "build.zig.zon", + "src", + }, +} diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..762544c --- /dev/null +++ b/flake.lock @@ -0,0 +1,92 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1735471104, + "narHash": "sha256-0q9NGQySwDQc7RhAV2ukfnu7Gxa5/ybJ2ANT8DQrQrs=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "88195a94f390381c6afcdaa933c2f6ff93959cb4", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1719942202, + "narHash": "sha256-7sP4PzxRsUfRwN8rmFtRvU/nYqTI5YYeIG9P3KJV41g=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "4b015946c99a5bbe9c7a685e66f165aa44644b7d", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "zlsPkg": "zlsPkg" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "zlsPkg": { + "inputs": { + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1, + "narHash": "sha256-40mzryp9AaDiufsbEISP5VJCEpg0DYrdMjpP16oSJj0=", + "path": "./nix/zls", + "type": "path" + }, + "original": { + "path": "./nix/zls", + "type": "path" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..77e9df2 --- /dev/null +++ b/flake.nix @@ -0,0 +1,27 @@ +{ + description = "Zig flake with ZLS"; + inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + inputs.flake-utils.url = "github:numtide/flake-utils"; + inputs.zlsPkg.url = "path:./nix/zls"; + outputs = { + flake-utils, + nixpkgs, + zlsPkg, + ... + }: + flake-utils.lib.eachDefaultSystem (system: let + pkgs = import nixpkgs { + inherit system; + }; + zls = zlsPkg.defaultPackage.${system}; + in { + devShell = pkgs.mkShell { + buildInputs = with pkgs; [ + lldb + SDL2 + zig + zls + ]; + }; + }); +} diff --git a/nix/zls/flake.lock b/nix/zls/flake.lock new file mode 100644 index 0000000..188887d --- /dev/null +++ b/nix/zls/flake.lock @@ -0,0 +1,26 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1719942202, + "narHash": "sha256-7sP4PzxRsUfRwN8rmFtRvU/nYqTI5YYeIG9P3KJV41g=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "4b015946c99a5bbe9c7a685e66f165aa44644b7d", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/nix/zls/flake.nix b/nix/zls/flake.nix new file mode 100644 index 0000000..921bf0b --- /dev/null +++ b/nix/zls/flake.nix @@ -0,0 +1,33 @@ +{ + description = "Zig language server"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs"; + }; + + outputs = {self, nixpkgs}: { + defaultPackage.x86_64-linux = + with import nixpkgs { system = "x86_64-linux"; }; + + stdenv.mkDerivation rec { + name = "zls-${version}"; + version = "0.13.0"; + src = pkgs.fetchurl { + url = "https://github.com/zigtools/zls/releases/download/${version}/zls-x86_64-linux.tar.xz"; + sha256 = "sha256-7EwbRcr4jivLnrsWxnBgPMWW5PYhuWGE376DeznNhBA="; + }; + + sourceRoot = "."; + + installPhase = '' + install -m755 -D zls $out/bin/zls + ''; + + meta = with lib; { + homepage = "https://github.com/zigtools/zls/releases"; + description = "Zig language server"; + platforms = platforms.linux; + }; + }; + }; +} diff --git a/src/main.zig b/src/main.zig new file mode 100644 index 0000000..05d45b8 --- /dev/null +++ b/src/main.zig @@ -0,0 +1,72 @@ +const c = @cImport({ + @cInclude("SDL2/SDL.h"); +}); + +const std = @import("std"); +const assert = std.debug.assert; + +pub fn main() !void { + if (c.SDL_Init(c.SDL_INIT_VIDEO) != 0) { + c.SDL_Log("Unable to initialize SDL: %s", c.SDL_GetError()); + return error.SDLInitializationFailed; + } + defer c.SDL_Quit(); + + const screen = c.SDL_CreateWindow("My Game Window", c.SDL_WINDOWPOS_UNDEFINED, c.SDL_WINDOWPOS_UNDEFINED, 400, 140, c.SDL_WINDOW_OPENGL) orelse + { + c.SDL_Log("Unable to create window: %s", c.SDL_GetError()); + return error.SDLInitializationFailed; + }; + defer c.SDL_DestroyWindow(screen); + + const renderer = c.SDL_CreateRenderer(screen, -1, 0) orelse { + c.SDL_Log("Unable to create renderer: %s", c.SDL_GetError()); + return error.SDLInitializationFailed; + }; + defer c.SDL_DestroyRenderer(renderer); + + const zig_bmp = @embedFile("zig.bmp"); + const rw = c.SDL_RWFromConstMem(zig_bmp, zig_bmp.len) orelse { + c.SDL_Log("Unable to get RWFromConstMem: %s", c.SDL_GetError()); + return error.SDLInitializationFailed; + }; + defer assert(c.SDL_RWclose(rw) == 0); + + const zig_surface = c.SDL_LoadBMP_RW(rw, 0) orelse { + c.SDL_Log("Unable to load bmp: %s", c.SDL_GetError()); + return error.SDLInitializationFailed; + }; + defer c.SDL_FreeSurface(zig_surface); + + const zig_texture = c.SDL_CreateTextureFromSurface(renderer, zig_surface) orelse { + c.SDL_Log("Unable to create texture from surface: %s", c.SDL_GetError()); + return error.SDLInitializationFailed; + }; + defer c.SDL_DestroyTexture(zig_texture); + + var quit = false; + while (!quit) { + var event: c.SDL_Event = undefined; + while (c.SDL_PollEvent(&event) != 0) { + switch (event.type) { + c.SDL_QUIT => { + quit = true; + }, + else => {}, + } + } + + _ = c.SDL_RenderClear(renderer); + _ = c.SDL_RenderCopy(renderer, zig_texture, null, null); + c.SDL_RenderPresent(renderer); + + c.SDL_Delay(17); + } +} + +test "simple test" { + var list = std.ArrayList(i32).init(std.testing.allocator); + defer list.deinit(); // try commenting this out and see if zig detects the memory leak! + try list.append(42); + try std.testing.expectEqual(@as(i32, 42), list.pop()); +} diff --git a/src/root.zig b/src/root.zig new file mode 100644 index 0000000..ecfeade --- /dev/null +++ b/src/root.zig @@ -0,0 +1,10 @@ +const std = @import("std"); +const testing = std.testing; + +export fn add(a: i32, b: i32) i32 { + return a + b; +} + +test "basic add functionality" { + try testing.expect(add(3, 7) == 10); +} diff --git a/src/zig.bmp b/src/zig.bmp new file mode 100644 index 0000000000000000000000000000000000000000..bfe71082824ed2ca016fb5822cabcc8443683120 GIT binary patch literal 168138 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 Y1O&WCppa|2=|EU}_x@1@1MMO3e{(I7Y5)KL literal 0 HcmV?d00001