From 834dad44ee12fa49fbe897eaa17e7bcb57ec47a5 Mon Sep 17 00:00:00 2001 From: Nathan Anderson 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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 AY5)KL literal 0 HcmV?d00001