summaryrefslogtreecommitdiff
path: root/ui_sdl.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-07-03 23:37:10 -0300
committerGabriel Schneider <[email protected]>2026-08-01 15:02:07 -0300
commitf3f0e27372d8cf111269dde674d4581cf4cb0c41 (patch)
tree4cc53bb5893ece129ec4c3908380c46d0e691378 /ui_sdl.zig
parenta354d34329de597b80943c78e2afd84f235ee505 (diff)
downloadpardes-f3f0e27372d8cf111269dde674d4581cf4cb0c41.tar.gz
pardes-f3f0e27372d8cf111269dde674d4581cf4cb0c41.zip
sdl: main + ui_sdl rework, build.zig updates
Diffstat (limited to 'ui_sdl.zig')
-rw-r--r--ui_sdl.zig201
1 files changed, 132 insertions, 69 deletions
diff --git a/ui_sdl.zig b/ui_sdl.zig
index 0b2a2fd2..ee7f118c 100644
--- a/ui_sdl.zig
+++ b/ui_sdl.zig
@@ -25,6 +25,7 @@ const vaxis = @import("vaxis");
const ui_sdl_config = @import("ui_sdl_config");
const posix = std.posix;
const linux = std.os.linux;
+const os_tag = builtin.os.tag;
const is_emscripten = builtin.os.tag == .emscripten;
pub const c = @cImport({
@@ -150,7 +151,7 @@ capture_xfer_size: u32 = 0,
capture_frame: u64 = 0,
trace_enabled: bool = false,
-trace_fd: c_int = -1,
+trace_fd: posix.fd_t = -1,
trace_every: u32 = 1,
trace_frame: u64 = 0,
@@ -233,9 +234,93 @@ fn envFlag(env_map: *std.process.Environ.Map, name: []const u8) bool {
fn nowNs() i128 {
if (is_emscripten) return @intCast(c.SDL_GetTicksNS());
- var ts: linux.timespec = undefined;
- _ = linux.clock_gettime(linux.CLOCK.MONOTONIC, &ts);
- return @as(i128, ts.sec) * 1_000_000_000 + @as(i128, ts.nsec);
+ switch (os_tag) {
+ .linux => {
+ var ts: linux.timespec = undefined;
+ _ = linux.clock_gettime(linux.CLOCK.MONOTONIC, &ts);
+ return @as(i128, ts.sec) * 1_000_000_000 + @as(i128, ts.nsec);
+ },
+ else => {
+ var ts: posix.timespec = undefined;
+ switch (posix.errno(posix.system.clock_gettime(posix.CLOCK.MONOTONIC, &ts))) {
+ .SUCCESS => return @as(i128, ts.sec) * 1_000_000_000 + @as(i128, ts.nsec),
+ else => return 0,
+ }
+ },
+ }
+}
+
+fn openCreateTrunc(path: [*:0]const u8) ?posix.fd_t {
+ switch (os_tag) {
+ .linux => {
+ const fd_rc = linux.open(path, .{ .ACCMODE = .WRONLY, .CREAT = true, .TRUNC = true }, 0o644);
+ const fd_s: isize = @bitCast(fd_rc);
+ return if (fd_s < 0) null else @intCast(fd_s);
+ },
+ else => return posix.openatZ(posix.AT.FDCWD, path, .{
+ .ACCMODE = .WRONLY,
+ .CREAT = true,
+ .TRUNC = true,
+ }, 0o644) catch null,
+ }
+}
+
+fn closeFdBestEffort(fd: posix.fd_t) void {
+ switch (os_tag) {
+ .linux => _ = linux.close(fd),
+ else => switch (posix.errno(posix.system.close(fd))) {
+ .SUCCESS, .INTR => {},
+ else => {},
+ },
+ }
+}
+
+fn writeAllFd(fd: posix.fd_t, bytes: []const u8) !void {
+ var off: usize = 0;
+ while (off < bytes.len) {
+ switch (os_tag) {
+ .linux => {
+ const rc = linux.write(fd, bytes[off..].ptr, bytes.len - off);
+ const n: isize = @bitCast(rc);
+ if (n < 0) {
+ if (n == -@as(isize, @intFromEnum(linux.E.INTR))) continue;
+ return error.WriteFailed;
+ }
+ if (n == 0) return error.WriteFailed;
+ off += @intCast(n);
+ },
+ else => {
+ const rc = posix.system.write(fd, bytes[off..].ptr, bytes.len - off);
+ switch (posix.errno(rc)) {
+ .SUCCESS => {
+ const n: usize = @intCast(rc);
+ if (n == 0) return error.WriteFailed;
+ off += n;
+ },
+ .INTR => continue,
+ else => return error.WriteFailed,
+ }
+ },
+ }
+ }
+}
+
+fn writeAllFdIgnore(fd: posix.fd_t, bytes: []const u8) void {
+ writeAllFd(fd, bytes) catch {};
+}
+
+fn renameZ(old_path: [*:0]const u8, new_path: [*:0]const u8) !void {
+ switch (os_tag) {
+ .linux => {
+ const rr = linux.rename(old_path, new_path);
+ const rs: isize = @bitCast(rr);
+ if (rs < 0) return error.RenameFailed;
+ },
+ else => switch (posix.errno(posix.system.rename(old_path, new_path))) {
+ .SUCCESS => {},
+ else => return error.RenameFailed,
+ },
+ }
}
fn nsToUs(delta: i128) u64 {
@@ -302,7 +387,7 @@ pub fn init(
}
const trace_enabled = envFlag(env_map, "PARDES_SDL_TRACE");
const trace_every = envU32(env_map, "PARDES_SDL_TRACE_EVERY") orelse 1;
- var trace_fd: c_int = -1;
+ var trace_fd: posix.fd_t = -1;
if (!is_emscripten and trace_enabled) {
if (env_map.get("PARDES_SDL_TRACE_FILE")) |path| {
trace_fd = openTraceFile(path);
@@ -463,7 +548,7 @@ fn initGl(
test_mode: bool,
capture_dir: ?[]const u8,
trace_enabled: bool,
- trace_fd: c_int,
+ trace_fd: posix.fd_t,
trace_every: u32,
) !SdlBackend {
_ = c.SDL_GL_SetAttribute(c.SDL_GL_CONTEXT_PROFILE_MASK, c.SDL_GL_CONTEXT_PROFILE_ES);
@@ -606,7 +691,7 @@ pub fn deinit(self: *SdlBackend, alloc: ?std.mem.Allocator, writer: *std.Io.Writ
}
if (self.capture_tex) |t| c.SDL_ReleaseGPUTexture(self.device, t);
if (self.capture_xfer) |x| c.SDL_ReleaseGPUTransferBuffer(self.device, x);
- if (self.trace_fd >= 0 and self.trace_fd != 2) _ = linux.close(self.trace_fd);
+ if (self.trace_fd >= 0 and self.trace_fd != 2) closeFdBestEffort(self.trace_fd);
c.SDL_ReleaseGPUBuffer(self.device, self.vbuf);
c.SDL_ReleaseGPUTransferBuffer(self.device, self.vxfer);
self.alloc.free(self.cell_keys);
@@ -994,17 +1079,7 @@ fn dumpGrid(self: *SdlBackend) !void {
}
buf.appendSlice(self.alloc, "---ENDFRAME---\n") catch return;
- // write to stdout (fd 1)
- var off: usize = 0;
- while (off < buf.items.len) {
- const rc = linux.write(1, buf.items[off..].ptr, buf.items.len - off);
- const n: isize = @bitCast(rc);
- if (n < 0) {
- if (n == -@as(isize, @intFromEnum(linux.E.INTR))) continue;
- return;
- }
- off += @intCast(n);
- }
+ try writeAllFd(1, buf.items);
}
fn captureSwapchain(self: *SdlBackend, cmd: *c.SDL_GPUCommandBuffer, target: *c.SDL_GPUTexture, sw: u32, sh: u32) !void {
@@ -1094,11 +1169,8 @@ fn writeCapturePpm(self: *SdlBackend, pixels: []const u8, width: u32, height: u3
const tmp_path = std.fmt.bufPrintZ(&tmp_buf, "{s}/latest.ppm.tmp", .{dir}) catch return error.CapturePathTooLong;
const final_path = std.fmt.bufPrintZ(&final_buf, "{s}/latest.ppm", .{dir}) catch return error.CapturePathTooLong;
- const fd_rc = linux.open(tmp_path.ptr, .{ .ACCMODE = .WRONLY, .CREAT = true, .TRUNC = true }, 0o644);
- const fd_s: isize = @bitCast(fd_rc);
- if (fd_s < 0) return error.CaptureWriteFailed;
- const fd: c_int = @intCast(fd_s);
- defer _ = linux.close(fd);
+ const fd = openCreateTrunc(tmp_path.ptr) orelse return error.CaptureWriteFailed;
+ defer closeFdBestEffort(fd);
var header: [64]u8 = undefined;
const hdr = std.fmt.bufPrint(&header, "P6\n{d} {d}\n255\n", .{ width, height }) catch return error.CaptureWriteFailed;
@@ -1134,47 +1206,14 @@ fn writeCapturePpm(self: *SdlBackend, pixels: []const u8, width: u32, height: u3
try writeAllFd(fd, row_rgb);
}
- const rr = linux.rename(tmp_path.ptr, final_path.ptr);
- const rs: isize = @bitCast(rr);
- if (rs < 0) return error.CaptureWriteFailed;
+ renameZ(tmp_path.ptr, final_path.ptr) catch return error.CaptureWriteFailed;
self.capture_frame += 1;
}
-fn writeAllFd(fd: c_int, bytes: []const u8) !void {
- var off: usize = 0;
- while (off < bytes.len) {
- const rc = linux.write(fd, bytes[off..].ptr, bytes.len - off);
- const n: isize = @bitCast(rc);
- if (n < 0) {
- if (n == -@as(isize, @intFromEnum(linux.E.INTR))) continue;
- return error.CaptureWriteFailed;
- }
- if (n == 0) return error.CaptureWriteFailed;
- off += @intCast(n);
- }
-}
-
-fn openTraceFile(path: []const u8) c_int {
+fn openTraceFile(path: []const u8) posix.fd_t {
var buf: [4096]u8 = undefined;
const z = std.fmt.bufPrintZ(&buf, "{s}", .{path}) catch return 2;
- const fd_rc = linux.open(z.ptr, .{ .ACCMODE = .WRONLY, .CREAT = true, .TRUNC = true }, 0o644);
- const fd_s: isize = @bitCast(fd_rc);
- if (fd_s < 0) return 2;
- return @intCast(fd_s);
-}
-
-fn writeAllFdIgnore(fd: c_int, bytes: []const u8) void {
- var off: usize = 0;
- while (off < bytes.len) {
- const rc = linux.write(fd, bytes[off..].ptr, bytes.len - off);
- const n: isize = @bitCast(rc);
- if (n < 0) {
- if (n == -@as(isize, @intFromEnum(linux.E.INTR))) continue;
- return;
- }
- if (n == 0) return;
- off += @intCast(n);
- }
+ return openCreateTrunc(z.ptr) orelse 2;
}
fn tracef(self: *SdlBackend, comptime fmt: []const u8, args: anytype) void {
@@ -1759,7 +1798,7 @@ fn SdlLoop(comptime EventType: type) type {
if (!is_emscripten and self.vx.test_mode) {
// read winsize from stdin (fd 0 = the pty slave)
var ws: posix.winsize = std.mem.zeroes(posix.winsize);
- const iorc = posix.system.ioctl(0, posix.T.IOCGWINSZ, @intFromPtr(&ws));
+ const iorc = posix.system.ioctl(0, tiocgwinszReq(), @intFromPtr(&ws));
if (iorc < 0) return; // fd 0 is not a pty
const cols: u16 = ws.col;
const rows: u16 = ws.row;
@@ -1968,14 +2007,9 @@ fn SdlLoop(comptime EventType: type) type {
_ = posix.poll(&fds, 50) catch continue;
if ((fds[0].revents & posix.POLL.IN) == 0) continue;
- const rc = linux.read(0, buf[read_start..].ptr, buf.len - read_start);
- const n: isize = @bitCast(rc);
- if (n < 0) {
- if (n == -@as(isize, @intFromEnum(linux.E.INTR))) continue;
- break;
- }
+ const n = readStdin(buf[read_start..]) catch break;
if (n == 0) break;
- const len: usize = read_start + @as(usize, @intCast(n));
+ const len: usize = read_start + n;
var seq_start: usize = 0;
while (seq_start < len) {
const result = parser.parse(buf[seq_start..len], null) catch break;
@@ -2023,11 +2057,40 @@ fn setStdinRaw() !void {
term.lflag.ICANON = false;
term.lflag.IEXTEN = false;
term.lflag.ISIG = false;
- term.cc[@intFromEnum(linux.V.MIN)] = 1;
- term.cc[@intFromEnum(linux.V.TIME)] = 0;
+ term.cc[@intFromEnum(posix.V.MIN)] = 1;
+ term.cc[@intFromEnum(posix.V.TIME)] = 0;
try posix.tcsetattr(0, .NOW, term);
}
+fn ioctlReq(bits: u32) c_int {
+ return @bitCast(bits);
+}
+
+fn tiocgwinszReq() c_int {
+ return switch (os_tag) {
+ .linux => @intCast(posix.T.IOCGWINSZ),
+ .macos, .freebsd, .openbsd, .netbsd, .dragonfly => ioctlReq(0x40087468),
+ else => @intCast(posix.T.IOCGWINSZ),
+ };
+}
+
+fn readStdin(buf: []u8) !usize {
+ switch (os_tag) {
+ .linux => {
+ while (true) {
+ const rc = linux.read(0, buf.ptr, buf.len);
+ const n: isize = @bitCast(rc);
+ if (n < 0) {
+ if (n == -@as(isize, @intFromEnum(linux.E.INTR))) continue;
+ return error.ReadFailed;
+ }
+ return @intCast(n);
+ }
+ },
+ else => return posix.read(0, buf),
+ }
+}
+
// ---- test-mode SIGWINCH handler ----
// Signal handlers can't take closures, so we just set a flag; the stdin reader
// (SdlLoop.stdinRunTyped) checks it and reads + posts the new winsize.