From dedb811c290c19e5be39bee6468f0c43bb8c2edb Mon Sep 17 00:00:00 2001 From: peterino2 Date: Sun, 15 Jun 2025 20:43:17 -0700 Subject: [PATCH] adding sys module --- build.zig | 2 +- build/generateLoadDynamics.zig | 8 +- engine/sys/build.zig | 37 +++++ engine/sys/build.zig.zon | 16 ++ engine/sys/src/subprocess.zig | 137 ++++++++++++++++++ engine/sys/src/sys.zig | 30 ++++ engine/sys/tests/test.zig | 1 + engine/sys/tests/tests.zig | 22 +++ lib/cimgui/src/cimgui.zig | 8 +- projects/build.zig | 25 ++-- projects/tools/newProjectMaker.zig | 83 +++++++++++ .../projectGeneratorGUI.zig | 0 12 files changed, 347 insertions(+), 22 deletions(-) create mode 100644 engine/sys/build.zig create mode 100644 engine/sys/build.zig.zon create mode 100644 engine/sys/src/subprocess.zig create mode 100644 engine/sys/src/sys.zig create mode 100644 engine/sys/tests/test.zig create mode 100644 engine/sys/tests/tests.zig create mode 100644 projects/tools/newProjectMaker.zig create mode 100644 tools/projectGeneratorGui/projectGeneratorGUI.zig diff --git a/build.zig b/build.zig index 6261950..2acb749 100644 --- a/build.zig +++ b/build.zig @@ -545,7 +545,7 @@ pub fn build(b: *std.Build) void { } pub fn generateApi(self: *@This(), programName: []const u8, moduleList: []const []const u8, dynamicModules: ?[]const *DynamicModule) *std.Build.Module { - std.debug.print("GENERATE API:: {s}\n\n", .{programName}); + // std.debug.print("GENERATE API:: {s}\n\n", .{programName}); if (self.generatedApis.get(programName)) |m| { return m; } diff --git a/build/generateLoadDynamics.zig b/build/generateLoadDynamics.zig index 6a33d81..efc23c1 100644 --- a/build/generateLoadDynamics.zig +++ b/build/generateLoadDynamics.zig @@ -41,6 +41,12 @@ pub fn main() !void { \\ const args = core.externModule.getModuleLoaderArgs(true); ); + if (modlist.len == 0) { + _ = try writer.write( + \\ _ = args; + ); + } + for (modlist) |mod| { try writer.print("try {s}.start_module(args);\n", .{mod}); } @@ -65,7 +71,7 @@ pub fn main() !void { var ast = try std.zig.Ast.parse(allocator, @as([:0]const u8, @ptrCast(content.items[0 .. content.items.len - 1])), .zig); - std.debug.print("output=\n{s}", .{content.items[0 .. content.items.len - 1]}); + // std.debug.print("output=\n{s}", .{content.items[0 .. content.items.len - 1]}); defer ast.deinit(allocator); const out = try ast.render(allocator); diff --git a/engine/sys/build.zig b/engine/sys/build.zig new file mode 100644 index 0000000..d387972 --- /dev/null +++ b/engine/sys/build.zig @@ -0,0 +1,37 @@ +const std = @import("std"); +const sdl3 = @import("sdl3"); + +const dependencyList = [_][]const u8{ + "core", +}; + +pub fn build(b: *std.Build) void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + const static_build = b.option(bool, "static_build", "builds backlog dependencies for static linking") orelse false; + + const mod = b.addModule("sys", .{ + .target = target, + .optimize = optimize, + .root_source_file = b.path("src/sys.zig"), + }); + + for (dependencyList) |depName| { + const dep = b.dependency(depName, .{ .target = target, .optimize = optimize, .static_build = static_build }); + const dep_mod = dep.module(depName); + mod.addImport(depName, dep_mod); + } + + // ========== tests ========== + const tests = b.addTest(.{ + .target = target, + .optimize = optimize, + .root_source_file = b.path("tests/tests.zig"), + }); + const test_step = b.step("test", "run unit tests for ui"); + + tests.root_module.addImport("sys", mod); + const runArtifact = b.addRunArtifact(tests); + test_step.dependOn(&runArtifact.step); + b.installArtifact(tests); +} diff --git a/engine/sys/build.zig.zon b/engine/sys/build.zig.zon new file mode 100644 index 0000000..507859a --- /dev/null +++ b/engine/sys/build.zig.zon @@ -0,0 +1,16 @@ +.{ + .name = .ui, + .version = "0.0.0", + .dependencies = .{ + .core = .{ .path = "../core" }, + .platform = .{ .path = "../platform" }, + .papyrus = .{ .path = "../papyrus" }, + .rend = .{ .path = "../rend" }, + .sdl3 = .{ .path = "../../lib/sdl3" }, + .shaderTypes = .{ .path = "../../lib/sdl3/shaderTypes" }, + }, + .paths = .{ + "", + }, + .fingerprint = 0x27ff46b0db01e0ab, +} diff --git a/engine/sys/src/subprocess.zig b/engine/sys/src/subprocess.zig new file mode 100644 index 0000000..9d7ec8b --- /dev/null +++ b/engine/sys/src/subprocess.zig @@ -0,0 +1,137 @@ +pub const SystemTaskRunner = struct { + allocator: std.mem.Allocator, + + jobs: std.ArrayListUnmanaged(*SystemTask), + + pub fn create(allocator: std.mem.Allocator) *@This() { + const self = allocator.create(@This()); + + self.* = .{ + .allocator = allocator, + }; + + const Job = struct { + ctx: *SystemTaskRunner, + + pub fn func(ctx: @This(), job: *core.JobContext) void { + _ = job; + _ = ctx; + + while (true) { + // pump events + // if nothing pumped this frame, wait 100ms + } + } + }; + + try core.dispatchJob(Job{ .ctx = self }); + + return self; + } + + pub fn pushJob(self: *@This()) !*SystemTask { + const task = try self.allocator.create(SystemTask); + try self.jobs.append(self.allocator, task); + } + + // check completion from the task thread + pub fn checkCompletionTT(self: *@This()) bool { + if (builtin.os.tag == .windows) { + _ = self; + } + } + + pub fn destroy(self: *@This()) void { + self.allocator.destroy(self); + } +}; + +pub const SystemTask = struct { + status: ExecutionStatus, + taskRunner: *SystemTaskRunner, +}; + +pub const ExecutionStatus = enum { + ready, + failed, + success, +}; + +pub const Execution = struct { + mutex: std.Thread.Mutex = .{}, + status: ExecutionStatus = .ready, + payload: ?*anyopaque = null, + command: std.ArrayListUnmanaged([]const u8) = .{}, + outputStream: std.ArrayListUnmanaged(u8) = .{}, + backing: std.mem.Allocator, + arena: std.heap.ArenaAllocator, + child: ?std.process.Child, + + lastUpdateTime: i64 = 0, + timeout: i64 = 1000000000, + + pub fn startAlloc(allocator: std.mem.Allocator, command: []const []const u8) !*@This() { + const self = try allocator.create(@This()); + self.* = .{ + .command = undefined, + .backing = allocator, + .arena = std.heap.ArenaAllocator.init(allocator), + }; + + const alloc = self.arena.allocator(); + for (command) |arg| { + const a = try alloc.dupe(u8, arg); + try self.command.append(alloc, a); + } + + return self; + } + + pub fn run(self: *@This()) void { + self.child = std.process.Child.init(self.command.items, self.arena.allocator()); + self.child.?.spawn(); + } + + pub fn clearOutputStream(self: *@This()) void { + self.outputStream.clearRetainingCapacity(); + } + + // returns true if the status changed at all + // can access output via Execution.outputStream + pub fn tick(self: *@This()) bool { + const lastStatus = self.status; + _ = lastStatus; + const lastOutputStreamLen = self.outputStream.len; + var rv: bool = false; + + // const allocator = self.arena.allocator(); + // try self.child.?.collectOutput(allocator, &self.outputStream, &self.outputStream, 8192 * 2); + + if (lastOutputStreamLen != self.outputStream.len) { + self.lastUpdateTime = std.time.microTimestamp(); + rv = true; + } + + const ts = std.time.microTimestamp(); + + if (self.lastUpdateTime - self.timeout > ts) {} + + if (self.child.?) {} + + return rv; + } + + pub fn start(command: []const []const u8) !*@This() { + return try startAlloc(sys.getAllocator(), command); + } + + pub fn destroy(self: *@This()) void { + self.arena.deinit(); + self.backing.destroy(self); + } +}; + +const std = @import("std"); +pub const core = @import("core"); +const sys = @import("../sys.zig"); +const builtin = @import("builtin"); diff --git a/engine/sys/src/sys.zig b/engine/sys/src/sys.zig new file mode 100644 index 0000000..4fff72e --- /dev/null +++ b/engine/sys/src/sys.zig @@ -0,0 +1,30 @@ +const std = @import("std"); +pub const core = @import("core"); + +// TODO get rid of core.ModuleDescription +// it's really not needed anymore with the +// new backlog API +pub const Module: core.ModuleDescription = .{ + .name = "sys", + .enabledByDefault = true, +}; + +// Module: sys +// +// this module is not enabled by default +// not intended for shipping builds +// but provides utilities for file manipulation among other things. + +pub fn getAllocator() std.mem.Allocator { + return core.getEngine().allocator; +} + +pub fn start_module(spec: *core.SpecVariantMap, args: anytype, allocator: std.mem.Allocator) !void { + _ = args; + _ = spec; + _ = allocator; +} + +pub fn shutdown_module(allocator: std.mem.Allocator) void { + _ = allocator; +} diff --git a/engine/sys/tests/test.zig b/engine/sys/tests/test.zig new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/engine/sys/tests/test.zig @@ -0,0 +1 @@ + diff --git a/engine/sys/tests/tests.zig b/engine/sys/tests/tests.zig new file mode 100644 index 0000000..acea37a --- /dev/null +++ b/engine/sys/tests/tests.zig @@ -0,0 +1,22 @@ +const sys = @import("sys"); +const core = sys.core; +const std = @import("std"); + +test "testing threadrunner" { + var spec = core.SpecVariantMap.init(std.testing.allocator); + try spec.put("name", .{ .string = "test" }); + defer spec.deinit(); + + try core.start_module(&spec, .{}, std.testing.allocator); + defer core.shutdown_module(std.testing.allocator); + + try sys.start_module(&spec, .{}, std.testing.allocator); + defer sys.shutdown_module(std.testing.allocator); + + const command = sys.runSystemCommand(&.{ "timeout", "/t", "3" }); + defer command.destroy(); + + while (command.isComplete()) {} + + if (command.ensureCompleted()) {} +} diff --git a/lib/cimgui/src/cimgui.zig b/lib/cimgui/src/cimgui.zig index b7aedaa..616764b 100644 --- a/lib/cimgui/src/cimgui.zig +++ b/lib/cimgui/src/cimgui.zig @@ -81,7 +81,7 @@ pub const Wchar = c_ushort; // ImWchar; pub const Wchar16 = c_ushort; // ImWchar16; pub const Wchar32 = c_int; // ImWchar32; -pub const InputTextCallback = *const fn (data: *InputTextCallbackData) callconv(.C) c_int; // ImGuiInputTextCallback +pub const InputTextCallback = ?*const fn (data: *InputTextCallbackData) callconv(.C) c_int; // ImGuiInputTextCallback pub const SizeCallback = *const fn (data: *SizeCallbackData) callconv(.C) void; // ImGuiInputTextCallback pub const MemAllocFunc = *const fn (sz: usize, user_data: ?*anyopaque) callconv(.C) ?*anyopaque; // ImGuiMemAllocFunc pub const MemFreeFunc = *const fn (ptr: ?*anyopaque, user_data: ?*anyopaque) callconv(.C) void; // ImMemAllocFunction @@ -2759,13 +2759,13 @@ pub inline fn vSliderScalar(label: [*c]const u8, size: Vec2, data_type: DataType return c.igVSliderScalar(label, @bitCast(size), @intFromEnum(data_type), p_data, p_min, p_max, format, @bitCast(flags)); } pub inline fn inputText(label: [*c]const u8, buf: [*c]u8, buf_size: usize, flags: InputTextFlags, callback: InputTextCallback, user_data: ?*anyopaque) bool { //igInputText - return c.igInputText(label, buf, buf_size, @bitCast(flags), callback, user_data); + return c.igInputText(label, buf, buf_size, @bitCast(flags), @ptrCast(callback), user_data); } pub inline fn inputTextMultiline(label: [*c]const u8, buf: [*c]u8, buf_size: usize, size: Vec2, flags: InputTextFlags, callback: InputTextCallback, user_data: ?*anyopaque) bool { //igInputTextMultiline - return c.igInputTextMultiline(label, buf, buf_size, @bitCast(size), @bitCast(flags), callback, user_data); + return c.igInputTextMultiline(label, buf, buf_size, @bitCast(size), @bitCast(flags), @ptrCast(callback), user_data); } pub inline fn inputTextWithHint(label: [*c]const u8, hint: [*c]const u8, buf: [*c]u8, buf_size: usize, flags: InputTextFlags, callback: InputTextCallback, user_data: ?*anyopaque) bool { //igInputTextWithHint - return c.igInputTextWithHint(label, hint, buf, buf_size, @bitCast(flags), callback, user_data); + return c.igInputTextWithHint(label, hint, buf, buf_size, @bitCast(flags), @ptrCast(callback), user_data); } pub inline fn inputFloat(label: [*c]const u8, v: [*c]f32, step: f32, step_fast: f32, format: [*c]const u8, flags: InputTextFlags) bool { //igInputFloat return c.igInputFloat(label, v, step, step_fast, format, @bitCast(flags)); diff --git a/projects/build.zig b/projects/build.zig index b073500..b410f79 100644 --- a/projects/build.zig +++ b/projects/build.zig @@ -30,26 +30,19 @@ pub fn build(b: *std.Build) void { _ = externGame.compileInstall(); } - _ = sampleGame.compileInstall(); - // external reload modules - // const sampleGameExtern = b.addSharedLibrary(.{ - // .root_source_file = b.path("sampleGame/externGame/externGame.zig"), - // .link_libc = true, - // .optimize = optimize, - // .target = target, - // .name = "externGame", - // }); + // tools - // blbuild.addExtraModule(sampleGameExtern.root_module, "gameExtras"); - // blbuild.addExtraModule(sampleGameExtern.root_module, "bsp"); - // sampleGameExtern.root_module.addImport("backlog", blbuild.nw_mod); + const newProjectMaker = blbuild.program(.{ + .name = "newProjectMaker", + .desc = "new project maker", + .root_source_file = b.path("tools/newProjectMaker.zig"), + }); - // const installExtern = b.addInstallArtifact(sampleGameExtern, .{ - // .dest_dir = .{ .override = .{ .custom = "modules" } }, - // }); - // b.getInstallStep().dependOn(&installExtern.step); + newProjectMaker.setModuleEnabled("imgui", true); + newProjectMaker.setModuleEnabled("audio", false); + _ = newProjectMaker.compileInstall(); blbuild.addDependencyInstalls(b, .ReleaseFast); //.ReleaseFast); } diff --git a/projects/tools/newProjectMaker.zig b/projects/tools/newProjectMaker.zig new file mode 100644 index 0000000..7731a9e --- /dev/null +++ b/projects/tools/newProjectMaker.zig @@ -0,0 +1,83 @@ +pub var NeonObjectTable = core.EngineObjectVTable.from(@This(), "main"); + +allocator: std.mem.Allocator, +textBuffer: [8192]u8, + +pub fn init(allocator: std.mem.Allocator) !*@This() { + const self = try allocator.create(@This()); + self.* = .{ + .allocator = allocator, + .textBuffer = std.mem.zeroes([8192]u8), + }; + return self; +} + +pub fn prepare(self: *@This()) !void { + _ = self; + core.engine_log("program ready", .{}); +} + +pub fn tick(self: *@This(), dt: f64) void { + _ = dt; + + ig.setNextWindowPos(.{ .x = 200, .y = 200 }, .{}, .{}); + + if (ig.begin("New Project...", null, .{ + .always_auto_resize = true, + .no_move = true, + .no_resize = true, + .no_collapse = true, + })) { + ig.setNextItemWidth(600); + if (ig.inputText( + "##Path", + &self.textBuffer, + self.textBuffer.len, + .{ .no_blank = true }, + null, + null, + )) { + core.engine_log("huh", .{}); + } + + ig.sameLine(0, 3); + + if (ig.button("...", .{})) { + core.engine_log("opening NFD", .{}); + core.asyncOpenFolder(.{ + .callback = onFolderSelected, + .callbackContext = self, + }) catch @panic(" unable to open dialog"); + } + + if (ig.button("Create", .{})) { + core.engine_log("creating project: ", .{}); + } + } + ig.end(); +} + +pub fn onFolderSelected( + ctx: ?*anyopaque, + path: ?[]const u8, +) void { + const self: *@This() = @ptrCast(@alignCast(ctx)); + if (path) |p| { + std.mem.copyForwards(u8, &self.textBuffer, p); + self.textBuffer[p.len] = 0; + } +} + +pub fn deinit(self: *@This()) void { + self.allocator.destroy(self); +} + +pub fn main() anyerror!void { + var spec = try api.getSpec("New Project Dialogue"); + _ = api.startEngine(&NeonObjectTable, &spec); +} + +const std = @import("std"); +const api = @import("backlog"); +const ig = api.imgui.api; +const core = api.core; diff --git a/tools/projectGeneratorGui/projectGeneratorGUI.zig b/tools/projectGeneratorGui/projectGeneratorGUI.zig new file mode 100644 index 0000000..e69de29