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 Translator = @import("translate_c").Translator;
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
@@ -10,13 +11,25 @@ pub fn build(b: *std.Build) void {
// in this directory.
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", .{
.root_source_file = b.path("src/root.zig"),
.target = target,
.optimize = optimize,
.imports = &.{
.{ .name = "c", .module = t.mod },
},
});
mod.addIncludePath(b.path("src"));
const exe = b.addExecutable(.{
.name = "wl-main",
.use_llvm = llvm,
@@ -36,7 +49,7 @@ pub fn build(b: *std.Build) void {
exe.root_module.addIncludePath(b.path("src"));
exe.root_module.addCSourceFiles(.{
.language = .c,
.files = &.{ "src/xdg-shell.c", },
.files = &.{ "src/xdg-shell.c", "src/stbi.c", },
});
b.installArtifact(exe);
+5 -38
View File
@@ -25,51 +25,18 @@
.fingerprint = 0x5fad1fab9564c392, // Changing this has security and trust implications.
// Tracks the earliest Zig version that the package considers to be a
// supported use case.
.minimum_zig_version = "0.15.2",
.minimum_zig_version = "0.16.0",
// This field is optional.
// 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.
// Once all dependencies are fetched, `zig build` no longer requires
// internet connectivity.
.dependencies = .{
// See `zig fetch --save <url>` for a command-line interface for adding dependencies.
//.example = .{
// // When updating this field to a new URL, be sure to delete the corresponding
// // `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,
//},
.translate_c = .{
.url = "git+https://codeberg.org/ifreund/translate-c#fdf30e6298cd1184ae6da5602aff0cb5d4cde4cb",
.hash = "translate_c-0.0.0-Q_BUWrT0BgDOFSyiz0UVBIpBe3-CiEqx9ej2EecgKcBD",
},
},
// 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 = .{
"build.zig",
"build.zig.zon",
+8 -4
View File
@@ -1,14 +1,18 @@
with import <nixpkgs> {};
mkShell {
packages = [
zig
zls
pkg-config
wayland-scanner
gdb
lldb
];
buildInputs = [
libc
wayland
libxkbcommon
];
LD_LIBRARY_PATH = lib.makeLibraryPath [
libc
wayland
libxkbcommon
];
+1
View File
@@ -0,0 +1 @@
#include "stb_image.h"
+91 -38
View File
@@ -87,7 +87,7 @@ fn createShmFile(pool_size: usize) !std.posix.fd_t {
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});
return result;
@@ -99,7 +99,7 @@ fn createSharedMemoryPool(wl_state: WaylandState) !MemoryPool {
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 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", .{});
wl_state.frame_commited = true;
}
const frame_callback_listener: c.wl_callback_listener = .{
@@ -559,7 +558,7 @@ fn wlPointerHandleFrame(data: ?*anyopaque, wl_pointer: ?*c.wl_pointer) callconv(
_ = wl_pointer;
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) {
wl_state.app_input.mouse_present = true;
@@ -627,14 +626,16 @@ fn wlKeyboardHandleKeymap(
std.debug.assert(format == c.WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1);
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", .{});
};
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.close(fd);
_ = linux.close(fd);
const xkb_state: ?*c.xkb_state = c.xkb_state_new(keymap);
@@ -688,7 +689,7 @@ fn wlKeyboardHandleKey(data: ?*anyopaque, keyboard: ?*c.wl_keyboard, serial: u32
}
break :blk i;
};
const str: []const u8 = buf[0 .. buf_end];
const str: []const u8 = buf[0..buf_end];
std.debug.print("{s} {any}\n", .{ str, pressed });
switch (sym) {
@@ -781,6 +782,7 @@ fn registryHandleGlobal(
version: u32,
) callconv(.c) void {
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) {
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)));
@@ -809,13 +811,13 @@ fn registryHandleGlobalRemove(
_ = name;
}
const registry_listener: c.wl_registry_listener = .{
.global = registryHandleGlobal,
.global_remove = registryHandleGlobalRemove,
};
fn pollEvents(wl_state: *WaylandState, fds: *[1]linux.pollfd) void {
var count: u32 = 0;
while (true) {
const cont = std.posix.poll(fds, 0) catch |err| {
switch (err) {
@@ -838,6 +840,7 @@ fn pollEvents(wl_state: *WaylandState, fds: *[1]linux.pollfd) void {
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;
@@ -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);
if (display == null) {
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_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_data: []u8 = undefined;
const monitor_update_hz: f64 = 60;
const monitor_update_hz: f64 = 30;
const game_update_hz: f64 = monitor_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_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
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});
}
var timer = std.time.Timer.start() catch {
std.debug.print("[Wayland] ERROR: timer not supported.\n", .{});
return 1;
};
var start = std.Io.Clock.Timestamp.now(io, clock);
var frame_count: u32 = 0;
@@ -966,7 +985,41 @@ pub fn main() u8 {
},
};
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) {
const new_pool = createSharedMemoryPool(wl_state) catch {
@@ -1008,7 +1061,7 @@ pub fn main() u8 {
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;
@@ -1019,22 +1072,26 @@ pub fn main() u8 {
.data = shared_memory_pool_data,
};
// 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_time = render_end_time - render_start_time;
const render_time_ms: f64 = @as(f64, @floatFromInt(render_time)) / @as(f64, @floatFromInt(std.time.ns_per_ms));
const render_end_time = clock.now(io);
const render_time = render_start_time.durationTo(render_end_time);
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;
if (render_time < frame_target_time_ns) {
sleep_time -= render_time;
const sleep_time_ms: f64 = @as(f64, @floatFromInt(sleep_time)) / @as(f64, @floatFromInt(std.time.ns_per_ms));
std.debug.print("[Wayland] Sleeping: {d:2.3}ms.\n", .{ sleep_time_ms });
std.posix.nanosleep(0, sleep_time);
var sleep_time = std.Io.Timestamp.now(io, clock);
sleep_time = sleep_time.addDuration(frame_target_time_ns);
if (render_time.toNanoseconds() < frame_target_time_ns.toNanoseconds()) {
sleep_time = sleep_time.subDuration(render_time);
std.debug.print("[Wayland] Sleeping: {d}ms.\n", .{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 {
sleep_time = 0;
sleep_time = std.Io.Timestamp.zero;
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_commit(wl_state.surface);
var wait_frame: u32 = 0;
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});
_ = c.wl_display_roundtrip(wl_state.display);
wl_state.frame_commited = false;
// std.posix.nanosleep(0, std.time.ns_per_ms * 32);
const current = timer.lap();
const current_ms: f64 = @as(f64, @floatFromInt(current)) / std.time.ns_per_ms;
const last = std.Io.Clock.Timestamp.now(io, clock);
const current = start.durationTo(last);
start = last;
// std.debug.print(
// "[Wayland] Render: {d:2.3}ms, Sleeping: {d:2.3}ms, Frame: {d:.3}ms.\n",
// .{ 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;
}
-6
View File
@@ -1,11 +1,5 @@
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 {
return bytes * 1024;
}
+12 -10
View File
@@ -6,6 +6,7 @@ const epoch = std.time.epoch;
const MonthAndDay = epoch.MonthAndDay;
pub const platform = @import("platform.zig");
const c = @import("c");
const V2 = @import("V2.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 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);
result = a or b or c or d;
result = a or b or @"c'" or d;
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 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);
result = a or b or c or d;
result = a or b or @"c'" or d;
return result;
}
@@ -447,8 +448,8 @@ fn drawString(buffer: platform.OffscreenBuffer, font: Font, pos: V2, scale: f32,
var current_pos = pos;
const font_size = 32;
const font_width_scaled = scale * font_size;
for (str) |c| {
const char_image_pos = font.findCharImagePos(c);
for (str) |char| {
const char_image_pos = font.findCharImagePos(char);
const pos_x: i32 = @intFromFloat(char_image_pos.x);
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 y: 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) {
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));
const mid = V2{
@@ -516,8 +517,9 @@ pub fn updateAndRender(memory: *platform.AppMemory, buffer: platform.OffscreenBu
if (!memory.initialized) {
state.has_won = false;
const timestamp = std.time.timestamp();
const epoch_seconds = epoch.EpochSeconds{ .secs = @intCast(timestamp) };
const clock = std.Io.Clock.real;
const timestamp = clock.now(io);
const epoch_seconds = epoch.EpochSeconds{ .secs = @intCast(timestamp.toSeconds()) };
const epoch_day = epoch_seconds.getEpochDay();
const year_and_day = epoch_day.calculateYearDay();
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"