Upgrade to zig 0.16!

This commit is contained in:
2026-05-05 21:21:31 +02:00
parent 91b72e3bd6
commit 7d41c3b7b1
8 changed files with 136 additions and 99 deletions
+16 -3
View File
@@ -1,4 +1,5 @@
const std = @import("std"); const std = @import("std");
const Translator = @import("translate_c").Translator;
pub fn build(b: *std.Build) void { pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{}); const target = b.standardTargetOptions(.{});
@@ -10,13 +11,25 @@ pub fn build(b: *std.Build) void {
// in this directory. // in this directory.
const llvm = b.option(bool, "llvm", "Build with llvm"); const llvm = b.option(bool, "llvm", "Build with llvm");
const translate_c = b.dependency("translate_c", .{});
const t: Translator = .init(translate_c, .{
.c_source_file = b.path("src/c.h"),
.target = target,
.optimize = optimize,
.default_init = false,
.warnings = .@"error",
});
const mod = b.addModule("puzzle", .{ const mod = b.addModule("puzzle", .{
.root_source_file = b.path("src/root.zig"), .root_source_file = b.path("src/root.zig"),
.target = target, .target = target,
.optimize = optimize,
.imports = &.{
.{ .name = "c", .module = t.mod },
},
}); });
mod.addIncludePath(b.path("src"));
const exe = b.addExecutable(.{ const exe = b.addExecutable(.{
.name = "wl-main", .name = "wl-main",
.use_llvm = llvm, .use_llvm = llvm,
@@ -36,7 +49,7 @@ pub fn build(b: *std.Build) void {
exe.root_module.addIncludePath(b.path("src")); exe.root_module.addIncludePath(b.path("src"));
exe.root_module.addCSourceFiles(.{ exe.root_module.addCSourceFiles(.{
.language = .c, .language = .c,
.files = &.{ "src/xdg-shell.c", }, .files = &.{ "src/xdg-shell.c", "src/stbi.c", },
}); });
b.installArtifact(exe); b.installArtifact(exe);
+5 -38
View File
@@ -25,51 +25,18 @@
.fingerprint = 0x5fad1fab9564c392, // Changing this has security and trust implications. .fingerprint = 0x5fad1fab9564c392, // Changing this has security and trust implications.
// Tracks the earliest Zig version that the package considers to be a // Tracks the earliest Zig version that the package considers to be a
// supported use case. // supported use case.
.minimum_zig_version = "0.15.2", .minimum_zig_version = "0.16.0",
// This field is optional. // This field is optional.
// Each dependency must either provide a `url` and `hash`, or a `path`. // Each dependency must either provide a `url` and `hash`, or a `path`.
// `zig build --fetch` can be used to fetch all dependencies of a package, recursively. // `zig build --fetch` can be used to fetch all dependencies of a package, recursively.
// Once all dependencies are fetched, `zig build` no longer requires // Once all dependencies are fetched, `zig build` no longer requires
// internet connectivity. // internet connectivity.
.dependencies = .{ .dependencies = .{
// See `zig fetch --save <url>` for a command-line interface for adding dependencies. .translate_c = .{
//.example = .{ .url = "git+https://codeberg.org/ifreund/translate-c#fdf30e6298cd1184ae6da5602aff0cb5d4cde4cb",
// // When updating this field to a new URL, be sure to delete the corresponding .hash = "translate_c-0.0.0-Q_BUWrT0BgDOFSyiz0UVBIpBe3-CiEqx9ej2EecgKcBD",
// // `hash`, otherwise you are communicating that you expect to find the old hash at },
// // the new URL. If the contents of a URL change this will result in a hash mismatch
// // which will prevent zig from using it.
// .url = "https://example.com/foo.tar.gz",
//
// // This is computed from the file contents of the directory of files that is
// // obtained after fetching `url` and applying the inclusion rules given by
// // `paths`.
// //
// // This field is the source of truth; packages do not come from a `url`; they
// // come from a `hash`. `url` is just one of many possible mirrors for how to
// // obtain a package matching this `hash`.
// //
// // Uses the [multihash](https://multiformats.io/multihash/) format.
// .hash = "...",
//
// // When this is provided, the package is found in a directory relative to the
// // build root. In this case the package's hash is irrelevant and therefore not
// // computed. This field and `url` are mutually exclusive.
// .path = "foo",
//
// // When this is set to `true`, a package is declared to be lazily
// // fetched. This makes the dependency only get fetched if it is
// // actually used.
// .lazy = false,
//},
}, },
// Specifies the set of files and directories that are included in this package.
// Only files and directories listed here are included in the `hash` that
// is computed for this package. Only files listed here will remain on disk
// when using the zig package manager. As a rule of thumb, one should list
// files required for compilation plus any license(s).
// Paths are relative to the build root. Use the empty string (`""`) to refer to
// the build root itself.
// A directory listed here means that all files within, recursively, are included.
.paths = .{ .paths = .{
"build.zig", "build.zig",
"build.zig.zon", "build.zig.zon",
+8 -4
View File
@@ -1,14 +1,18 @@
with import <nixpkgs> {}; with import <nixpkgs> {};
mkShell { mkShell {
packages = [ packages = [
zig pkg-config
zls
wayland-scanner wayland-scanner
gdb
lldb
]; ];
buildInputs = [ buildInputs = [
libc
wayland
libxkbcommon
];
LD_LIBRARY_PATH = lib.makeLibraryPath [
libc
wayland wayland
libxkbcommon libxkbcommon
]; ];
+1
View File
@@ -0,0 +1 @@
#include "stb_image.h"
+90 -37
View File
@@ -87,7 +87,7 @@ fn createShmFile(pool_size: usize) !std.posix.fd_t {
return error.ShmOpenError; return error.ShmOpenError;
} }
try std.posix.ftruncate(result, pool_size); _ = linux.ftruncate(result, @intCast(pool_size));
std.debug.print("[Wayland] ftruncate shm to size {d}B\n", .{pool_size}); std.debug.print("[Wayland] ftruncate shm to size {d}B\n", .{pool_size});
return result; return result;
@@ -99,7 +99,7 @@ fn createSharedMemoryPool(wl_state: WaylandState) !MemoryPool {
const fd: c_int = try createShmFile(pool_size); const fd: c_int = try createShmFile(pool_size);
const prot = linux.PROT.READ | std.os.linux.PROT.WRITE; const prot: linux.PROT = .{ .READ = true, .WRITE = true };
const flags: linux.MAP = .{ .TYPE = .SHARED }; const flags: linux.MAP = .{ .TYPE = .SHARED };
const ret = try std.posix.mmap(null, pool_size, prot, flags, fd, 0); const ret = try std.posix.mmap(null, pool_size, prot, flags, fd, 0);
@@ -125,7 +125,6 @@ fn wlFrameHandleDone(data: ?*anyopaque, callback: ?*c.wl_callback, callback_data
std.debug.print("[Wayland] Frame ready\n", .{}); std.debug.print("[Wayland] Frame ready\n", .{});
wl_state.frame_commited = true; wl_state.frame_commited = true;
} }
const frame_callback_listener: c.wl_callback_listener = .{ const frame_callback_listener: c.wl_callback_listener = .{
@@ -559,7 +558,7 @@ fn wlPointerHandleFrame(data: ?*anyopaque, wl_pointer: ?*c.wl_pointer) callconv(
_ = wl_pointer; _ = wl_pointer;
const wl_state: *WaylandState = @ptrCast(@alignCast(data)); const wl_state: *WaylandState = @ptrCast(@alignCast(data));
// std.debug.print("[Wayland] Pointer frame\n", .{}); std.debug.print("[Wayland] Pointer frame\n", .{});
if (wl_state.pointer_event.mask.ENTER) { if (wl_state.pointer_event.mask.ENTER) {
wl_state.app_input.mouse_present = true; wl_state.app_input.mouse_present = true;
@@ -627,14 +626,16 @@ fn wlKeyboardHandleKeymap(
std.debug.assert(format == c.WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1); std.debug.assert(format == c.WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1);
const wl_state: *WaylandState = @ptrCast(@alignCast(data)); const wl_state: *WaylandState = @ptrCast(@alignCast(data));
const map_shm: []align(4096) const u8 = std.posix.mmap(null, size, linux.PROT.READ, .{ .TYPE = .SHARED }, fd, 0) catch { const prot: linux.PROT = .{ .READ = true, .WRITE = false };
const flags: linux.MAP = .{ .TYPE = .SHARED };
const map_shm: []align(4096) const u8 = std.posix.mmap(null, size, prot, flags, fd, 0) catch {
std.debug.panic("[Wayland] Keyboard keymap handler failed mmap", .{}); std.debug.panic("[Wayland] Keyboard keymap handler failed mmap", .{});
}; };
const keymap: ?*c.xkb_keymap = c.xkb_keymap_new_from_string(wl_state.xkb_context, @ptrCast(map_shm), c.XKB_KEYMAP_FORMAT_TEXT_V1, c.XKB_KEYMAP_COMPILE_NO_FLAGS); const keymap: ?*c.xkb_keymap = c.xkb_keymap_new_from_string(wl_state.xkb_context, @ptrCast(map_shm), c.XKB_KEYMAP_FORMAT_TEXT_V1, c.XKB_KEYMAP_COMPILE_NO_FLAGS);
std.posix.munmap(map_shm); std.posix.munmap(map_shm);
std.posix.close(fd); _ = linux.close(fd);
const xkb_state: ?*c.xkb_state = c.xkb_state_new(keymap); const xkb_state: ?*c.xkb_state = c.xkb_state_new(keymap);
@@ -781,6 +782,7 @@ fn registryHandleGlobal(
version: u32, version: u32,
) callconv(.c) void { ) callconv(.c) void {
var wl_state: *WaylandState = @ptrCast(@alignCast(data)); var wl_state: *WaylandState = @ptrCast(@alignCast(data));
std.debug.print("[Wayland] Got interface {s} version {d}.\n", .{ interface, version });
if (c.strcmp(interface, c.wl_compositor_interface.name) == 0) { if (c.strcmp(interface, c.wl_compositor_interface.name) == 0) {
std.debug.print("[Wayland] Binding to interface {s} version {d}.\n", .{ interface, version }); std.debug.print("[Wayland] Binding to interface {s} version {d}.\n", .{ interface, version });
wl_state.compositor = @ptrCast(@alignCast(c.wl_registry_bind(registry, name, &c.wl_compositor_interface, version))); wl_state.compositor = @ptrCast(@alignCast(c.wl_registry_bind(registry, name, &c.wl_compositor_interface, version)));
@@ -809,13 +811,13 @@ fn registryHandleGlobalRemove(
_ = name; _ = name;
} }
const registry_listener: c.wl_registry_listener = .{ const registry_listener: c.wl_registry_listener = .{
.global = registryHandleGlobal, .global = registryHandleGlobal,
.global_remove = registryHandleGlobalRemove, .global_remove = registryHandleGlobalRemove,
}; };
fn pollEvents(wl_state: *WaylandState, fds: *[1]linux.pollfd) void { fn pollEvents(wl_state: *WaylandState, fds: *[1]linux.pollfd) void {
var count: u32 = 0;
while (true) { while (true) {
const cont = std.posix.poll(fds, 0) catch |err| { const cont = std.posix.poll(fds, 0) catch |err| {
switch (err) { switch (err) {
@@ -838,6 +840,7 @@ fn pollEvents(wl_state: *WaylandState, fds: *[1]linux.pollfd) void {
for (fds) |fd| { for (fds) |fd| {
if (fd.revents & std.posix.POLL.IN > 0) { if (fd.revents & std.posix.POLL.IN > 0) {
const ret = c.wl_display_dispatch(wl_state.display); const ret = c.wl_display_dispatch(wl_state.display);
count += 1;
if (ret < 0) { if (ret < 0) {
std.debug.print("Dispatch error {d}.\n", .{ret}); std.debug.print("Dispatch error {d}.\n", .{ret});
wl_state.running = false; wl_state.running = false;
@@ -846,9 +849,13 @@ fn pollEvents(wl_state: *WaylandState, fds: *[1]linux.pollfd) void {
} }
} }
} }
std.debug.print("[Wayland] Polled and dispatched {d} events.\n", .{count});
} }
pub fn main() u8 { pub fn main(init: std.process.Init) u8 {
const io = init.io;
const display = c.wl_display_connect(null); const display = c.wl_display_connect(null);
if (display == null) { if (display == null) {
std.debug.print("Error connecting to wayland display.\n", .{}); std.debug.print("Error connecting to wayland display.\n", .{});
@@ -920,14 +927,29 @@ pub fn main() u8 {
wl_state.window_width = 1280; wl_state.window_width = 1280;
wl_state.window_height = 960; wl_state.window_height = 960;
const clock = std.Io.Clock.awake;
const clock_resolution = clock.resolution(io) catch {
std.debug.print("[Wayland] ERROR: awake clock resoultion not supported.\n", .{});
return 1;
};
{
var buf: [34]u8 = undefined;
var w: std.Io.Writer = .fixed(&buf);
w.print("{any}", .{clock_resolution}) catch {
std.debug.print("[Wayland] ERROR: awake clock resoultion not supported.\n", .{});
return 1;
};
std.debug.print("Clock resolution is: {s}.\n", .{w.buffered()});
}
var shared_memory_pool: ?*c.wl_shm_pool = undefined; var shared_memory_pool: ?*c.wl_shm_pool = undefined;
var shared_memory_pool_data: []u8 = undefined; var shared_memory_pool_data: []u8 = undefined;
const monitor_update_hz: f64 = 60; const monitor_update_hz: f64 = 30;
const game_update_hz: f64 = monitor_update_hz; const game_update_hz: f64 = monitor_update_hz;
const frame_target_time_s: f64 = 1.0 / game_update_hz; const frame_target_time_s: f64 = 1.0 / game_update_hz;
const frame_target_time_ms: f64 = std.time.ms_per_s * frame_target_time_s; const frame_target_time_ms: f64 = std.time.ms_per_s * frame_target_time_s;
const frame_target_time_ns: u64 = std.time.ns_per_ms * @as(u64, @intFromFloat(frame_target_time_ms)); const frame_target_time_ns = std.Io.Duration.fromNanoseconds(std.time.ns_per_ms * @as(u64, @intFromFloat(frame_target_time_ms)));
// Memory // Memory
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
@@ -951,10 +973,7 @@ pub fn main() u8 {
std.debug.print("[Wayland] Added frame callback with ret {d}.\n", .{ret}); std.debug.print("[Wayland] Added frame callback with ret {d}.\n", .{ret});
} }
var timer = std.time.Timer.start() catch { var start = std.Io.Clock.Timestamp.now(io, clock);
std.debug.print("[Wayland] ERROR: timer not supported.\n", .{});
return 1;
};
var frame_count: u32 = 0; var frame_count: u32 = 0;
@@ -966,7 +985,41 @@ pub fn main() u8 {
}, },
}; };
while (wl_state.running) { while (wl_state.running) {
pollEvents(&wl_state, &fds); // pollEvents(&wl_state, &fds);
var count: u32 = 0;
outer: while (true) {
const cont = std.posix.poll(&fds, 0) catch |err| {
switch (err) {
error.Unexpected => {
std.debug.print("Poll error: Unexpected.\n", .{});
},
error.SystemResources => {
std.debug.print("Poll error: SystemResources: Kernel has no space for table allocations.\n", .{});
},
else => {
std.debug.print("Poll error: network stuff idk.\n", .{});
},
}
return 0;
};
if (cont == 0) break;
for (fds) |fd| {
if (fd.revents & std.posix.POLL.IN > 0) {
const ret = c.wl_display_dispatch(wl_state.display);
count += 1;
if (ret < 0) {
std.debug.print("Dispatch error {d}.\n", .{ret});
wl_state.running = false;
break :outer;
}
}
}
}
std.debug.print("[Wayland] Polled and dispatched {d} events.\n", .{count});
if (wl_state.resize) { if (wl_state.resize) {
const new_pool = createSharedMemoryPool(wl_state) catch { const new_pool = createSharedMemoryPool(wl_state) catch {
@@ -1008,7 +1061,7 @@ pub fn main() u8 {
c.wl_surface_commit(wl_state.surface); c.wl_surface_commit(wl_state.surface);
} }
const render_start_time: u64 = timer.read(); const render_start_time = clock.now(io);
wl_state.app_input.delta_time = frame_target_time_s; wl_state.app_input.delta_time = frame_target_time_s;
@@ -1019,22 +1072,26 @@ pub fn main() u8 {
.data = shared_memory_pool_data, .data = shared_memory_pool_data,
}; };
// wl_state.app_input.controls = puzzle.platform.Controls{ .key = old_key }; // wl_state.app_input.controls = puzzle.platform.Controls{ .key = old_key };
puzzle.updateAndRender(&app_memory, app_offscreen_buffer, &wl_state.app_input); puzzle.updateAndRender(io, &app_memory, app_offscreen_buffer, &wl_state.app_input);
const render_end_time: u64 = timer.read(); const render_end_time = clock.now(io);
const render_time = render_end_time - render_start_time; const render_time = render_start_time.durationTo(render_end_time);
const render_time_ms: f64 = @as(f64, @floatFromInt(render_time)) / @as(f64, @floatFromInt(std.time.ns_per_ms));
std.debug.print("[Wayland] Render: {d:2.3}ms.\n", .{ render_time_ms }); std.debug.print("[Wayland] Render: {f}ms.\n", .{render_time});
var sleep_time: u64 = frame_target_time_ns; var sleep_time = std.Io.Timestamp.now(io, clock);
if (render_time < frame_target_time_ns) { sleep_time = sleep_time.addDuration(frame_target_time_ns);
sleep_time -= render_time; if (render_time.toNanoseconds() < frame_target_time_ns.toNanoseconds()) {
const sleep_time_ms: f64 = @as(f64, @floatFromInt(sleep_time)) / @as(f64, @floatFromInt(std.time.ns_per_ms)); sleep_time = sleep_time.subDuration(render_time);
std.debug.print("[Wayland] Sleeping: {d:2.3}ms.\n", .{ sleep_time_ms }); std.debug.print("[Wayland] Sleeping: {d}ms.\n", .{sleep_time});
std.posix.nanosleep(0, sleep_time); const with_clock = std.Io.Timestamp.withClock(sleep_time, clock);
const timeout = std.Io.Timeout{ .deadline = with_clock };
timeout.sleep(io) catch {
std.debug.print("[Wayland] Error sleeping\n", .{});
return 1;
};
} else { } else {
sleep_time = 0; sleep_time = std.Io.Timestamp.zero;
std.debug.print("[Wayland] MISSED FRAME\n", .{}); std.debug.print("[Wayland] MISSED FRAME\n", .{});
} }
@@ -1042,24 +1099,20 @@ pub fn main() u8 {
c.wl_surface_damage_buffer(wl_state.surface, 0, 0, wl_state.window_width, wl_state.window_height); c.wl_surface_damage_buffer(wl_state.surface, 0, 0, wl_state.window_width, wl_state.window_height);
c.wl_surface_commit(wl_state.surface); c.wl_surface_commit(wl_state.surface);
var wait_frame: u32 = 0; _ = c.wl_display_roundtrip(wl_state.display);
while (!wl_state.frame_commited) {
_ = c.wl_display_dispatch(display);
wait_frame += 1;
}
std.debug.print("[Wayland] Waited for {d} frame(s).\n", .{wait_frame});
wl_state.frame_commited = false; wl_state.frame_commited = false;
// std.posix.nanosleep(0, std.time.ns_per_ms * 32); // std.posix.nanosleep(0, std.time.ns_per_ms * 32);
const current = timer.lap(); const last = std.Io.Clock.Timestamp.now(io, clock);
const current_ms: f64 = @as(f64, @floatFromInt(current)) / std.time.ns_per_ms; const current = start.durationTo(last);
start = last;
// std.debug.print( // std.debug.print(
// "[Wayland] Render: {d:2.3}ms, Sleeping: {d:2.3}ms, Frame: {d:.3}ms.\n", // "[Wayland] Render: {d:2.3}ms, Sleeping: {d:2.3}ms, Frame: {d:.3}ms.\n",
// .{ render_time_ms, sleep_time_ms, current_ms }, // .{ render_time_ms, sleep_time_ms, current_ms },
// ); // );
std.debug.print("[Wayland] Frame done: {d:.3}ms.\n", .{ current_ms }); std.debug.print("[Wayland] Frame done: {f}ms.\n", .{current.raw});
frame_count += 1; frame_count += 1;
} }
-6
View File
@@ -1,11 +1,5 @@
const std = @import("std"); const std = @import("std");
pub const c = @cImport({
@cDefine("STB_IMAGE_IMPLEMENTATION", "");
@cDefine("STBI_ONLY_PNG", "");
@cInclude("stb_image.h");
});
pub fn kiloBytes(bytes: usize) usize { pub fn kiloBytes(bytes: usize) usize {
return bytes * 1024; return bytes * 1024;
} }
+12 -10
View File
@@ -6,6 +6,7 @@ const epoch = std.time.epoch;
const MonthAndDay = epoch.MonthAndDay; const MonthAndDay = epoch.MonthAndDay;
pub const platform = @import("platform.zig"); pub const platform = @import("platform.zig");
const c = @import("c");
const V2 = @import("V2.zig"); const V2 = @import("V2.zig");
const Image = @import("Image.zig"); const Image = @import("Image.zig");
@@ -193,10 +194,10 @@ fn tilesIntersects(r1: V2, r2: V2) bool {
const a = pointInsideRect(r2a, r1, tile_size); const a = pointInsideRect(r2a, r1, tile_size);
const b = pointInsideRect(r2b, r1, tile_size); const b = pointInsideRect(r2b, r1, tile_size);
const c = pointInsideRect(r2c, r1, tile_size); const @"c'" = pointInsideRect(r2c, r1, tile_size);
const d = pointInsideRect(r2d, r1, tile_size); const d = pointInsideRect(r2d, r1, tile_size);
result = a or b or c or d; result = a or b or @"c'" or d;
return result; return result;
} }
@@ -211,10 +212,10 @@ fn rectsIntersects(r1: V2, r1_size: V2, r2: V2, r2_size: V2) bool {
const a = pointInsideRect(r2a, r1, r1_size); const a = pointInsideRect(r2a, r1, r1_size);
const b = pointInsideRect(r2b, r1, r1_size); const b = pointInsideRect(r2b, r1, r1_size);
const c = pointInsideRect(r2c, r1, r1_size); const @"c'" = pointInsideRect(r2c, r1, r1_size);
const d = pointInsideRect(r2d, r1, r1_size); const d = pointInsideRect(r2d, r1, r1_size);
result = a or b or c or d; result = a or b or @"c'" or d;
return result; return result;
} }
@@ -447,8 +448,8 @@ fn drawString(buffer: platform.OffscreenBuffer, font: Font, pos: V2, scale: f32,
var current_pos = pos; var current_pos = pos;
const font_size = 32; const font_size = 32;
const font_width_scaled = scale * font_size; const font_width_scaled = scale * font_size;
for (str) |c| { for (str) |char| {
const char_image_pos = font.findCharImagePos(c); const char_image_pos = font.findCharImagePos(char);
const pos_x: i32 = @intFromFloat(char_image_pos.x); const pos_x: i32 = @intFromFloat(char_image_pos.x);
const pos_y: i32 = @intFromFloat(char_image_pos.y); const pos_y: i32 = @intFromFloat(char_image_pos.y);
@@ -487,7 +488,7 @@ fn loadPng(image_path: []const u8) ?Image {
var x: c_int = undefined; var x: c_int = undefined;
var y: c_int = undefined; var y: c_int = undefined;
var n: c_int = undefined; var n: c_int = undefined;
const image_data: [*c]platform.c.stbi_uc = platform.c.stbi_load(@ptrCast(image_path), &x, &y, &n, 0); const image_data: [*c]c.stbi_uc = c.stbi_load(@ptrCast(image_path), &x, &y, &n, 0);
if (image_data != 0) { if (image_data != 0) {
const stride = n * x; const stride = n * x;
@@ -506,7 +507,7 @@ fn loadPng(image_path: []const u8) ?Image {
} }
} }
pub fn updateAndRender(memory: *platform.AppMemory, buffer: platform.OffscreenBuffer, input: *platform.AppInput) void { pub fn updateAndRender(io: std.Io, memory: *platform.AppMemory, buffer: platform.OffscreenBuffer, input: *platform.AppInput) void {
var state: *State = @ptrCast(@alignCast(memory.permanent_storage)); var state: *State = @ptrCast(@alignCast(memory.permanent_storage));
const mid = V2{ const mid = V2{
@@ -516,8 +517,9 @@ pub fn updateAndRender(memory: *platform.AppMemory, buffer: platform.OffscreenBu
if (!memory.initialized) { if (!memory.initialized) {
state.has_won = false; state.has_won = false;
const timestamp = std.time.timestamp(); const clock = std.Io.Clock.real;
const epoch_seconds = epoch.EpochSeconds{ .secs = @intCast(timestamp) }; const timestamp = clock.now(io);
const epoch_seconds = epoch.EpochSeconds{ .secs = @intCast(timestamp.toSeconds()) };
const epoch_day = epoch_seconds.getEpochDay(); const epoch_day = epoch_seconds.getEpochDay();
const year_and_day = epoch_day.calculateYearDay(); const year_and_day = epoch_day.calculateYearDay();
const day: MonthAndDay = year_and_day.calculateMonthDay(); const day: MonthAndDay = year_and_day.calculateMonthDay();
+3
View File
@@ -0,0 +1,3 @@
#define STB_IMAGE_IMPLEMENTATION
#define STBI_ONLY_PNG
#include "stb_image.h"