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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 Y1O&WCppa|2=|EU}_x@1@1MMO3e{(I7Y5)KL literal 0 HcmV?d00001