From d1dd4961a5143abeacb82d6c99de43b568e24605 Mon Sep 17 00:00:00 2001 From: peterino2 Date: Mon, 30 Jun 2025 18:28:55 -0700 Subject: [PATCH] fixes gpa usage, we now have a lot of memory leaks --- build/generateApi.zig | 1 + engine/platform/src/windowing.zig | 4 +- engine/sys/src/systemProc.zig | 4 +- lib/cimgui/src/cimgui.zig | 41 ++++++- projects/build.zig | 2 +- projects/tools/newProjectMaker.zig | 166 +++++++++++++++++++++++------ 6 files changed, 175 insertions(+), 43 deletions(-) diff --git a/build/generateApi.zig b/build/generateApi.zig index 2ac4263..ef54118 100644 --- a/build/generateApi.zig +++ b/build/generateApi.zig @@ -151,6 +151,7 @@ pub fn main() !void { \\ \\ pub fn getSpec(comptime name:[]const u8) !core.SpecVariantMap { \\ return try core.createSpecVariant(.{ + \\ .useGPA = true, \\ .name = name, ); diff --git a/engine/platform/src/windowing.zig b/engine/platform/src/windowing.zig index cc48d6b..5d7cde9 100644 --- a/engine/platform/src/windowing.zig +++ b/engine/platform/src/windowing.zig @@ -46,6 +46,7 @@ pub const RawInputObjectRef = struct { // We are having some serious issues with this events pump. pub const PlatformParams = struct { extent: core.Vector2c = .{ .x = 1600, .y = 900 }, + resizeable: bool = true, windowName: []const u8, icon: []const u8 = "textures/icon.png", hasVideo: bool = true, @@ -97,6 +98,7 @@ pub const PlatformInstance = struct { self.allocator.free(self.windowName); self.allocator.free(self.iconPath); self.processFuncs.deinit(self.allocator); + self.allocator.destroy(self); // shutdown } @@ -149,7 +151,7 @@ pub const PlatformInstance = struct { pub fn setupWindow(self: *@This()) core.EngineDataEventError!void { sdl3.init(.{ .video = true, .gamepad = true }) catch return error.BadInit; - self.window = sdl3.c.SDL_CreateWindow(self.windowName, self.windowExtent.x, self.windowExtent.y, 0).?; // resizeable + self.window = sdl3.c.SDL_CreateWindow(self.windowName, self.windowExtent.x, self.windowExtent.y, sdl3.c.SDL_WINDOW_RESIZABLE).?; // resizeable self.extent = .{ .x = @floatFromInt(self.windowExtent.x), .y = @floatFromInt(self.windowExtent.y), diff --git a/engine/sys/src/systemProc.zig b/engine/sys/src/systemProc.zig index 04aa8ee..069d796 100644 --- a/engine/sys/src/systemProc.zig +++ b/engine/sys/src/systemProc.zig @@ -34,8 +34,8 @@ pub const SubprocessTask = struct { self.mutex.lock(); debugPrint("running task", .{}); self.child = std.process.Child.init(self.args, self.allocator); - self.child.?.stdout_behavior = .Inherit; - self.child.?.stderr_behavior = .Inherit; + self.child.?.stdout_behavior = .Pipe; + self.child.?.stderr_behavior = .Pipe; self.child.?.cwd = self.workingDir; _ = self.child.?.spawn() catch {}; diff --git a/lib/cimgui/src/cimgui.zig b/lib/cimgui/src/cimgui.zig index 616764b..cb86f30 100644 --- a/lib/cimgui/src/cimgui.zig +++ b/lib/cimgui/src/cimgui.zig @@ -122,18 +122,50 @@ pub const WindowFlags = packed struct(c_int) { // ImGuiWindowFlags no_docking: bool = false, reserved: u11 = 0, // reserved, don't use + // Obsolete names + //ImGuiInputTextFlags_AlwaysInsertMode = ImGuiInputTextFlags_AlwaysOverwrite // [renamed in 1.82] name was not matching behavior + pub const no_nav: @This() = .{ .no_nav_inputs = true, .no_nav_focus = true }; pub const no_decoration: @This() = .{ .no_title_bar = true, .no_resize = true, .no_scrollbar = true, .no_collapse = true }; pub const no_inputs: @This() = .{ .no_mouse_inputs = true, .no_nav_inputs = true, .no_nav_focus = true }; }; +// Inputs +// ImGuiInputTextFlags_AllowTabInput = 1 << 5, // Pressing TAB input a '\t' character into the text field +// ImGuiInputTextFlags_EnterReturnsTrue = 1 << 6, // Return 'true' when Enter is pressed (as opposed to every time the value was modified). Consider using IsItemDeactivatedAfterEdit() instead! +// ImGuiInputTextFlags_EscapeClearsAll = 1 << 7, // Escape key clears content if not empty, and deactivate otherwise (contrast to default behavior of Escape to revert) +// ImGuiInputTextFlags_CtrlEnterForNewLine = 1 << 8, // In multi-line mode, validate with Enter, add new line with Ctrl+Enter (default is opposite: validate with Ctrl+Enter, add line with Enter). + +// // Other options +// ImGuiInputTextFlags_ReadOnly = 1 << 9, // Read-only mode +// ImGuiInputTextFlags_Password = 1 << 10, // Password mode, display all characters as '*', disable copy +// ImGuiInputTextFlags_AlwaysOverwrite = 1 << 11, // Overwrite mode +// ImGuiInputTextFlags_AutoSelectAll = 1 << 12, // Select entire text when first taking mouse focus +// ImGuiInputTextFlags_ParseEmptyRefVal = 1 << 13, // InputFloat(), InputInt(), InputScalar() etc. only: parse empty string as zero value. +// ImGuiInputTextFlags_DisplayEmptyRefVal = 1 << 14, // InputFloat(), InputInt(), InputScalar() etc. only: when value is zero, do not display it. Generally used with ImGuiInputTextFlags_ParseEmptyRefVal. +// ImGuiInputTextFlags_NoHorizontalScroll = 1 << 15, // Disable following the cursor horizontally +// ImGuiInputTextFlags_NoUndoRedo = 1 << 16, // Disable undo/redo. Note that input text owns the text data while active, if you want to provide your own undo/redo stack you need e.g. to call ClearActiveID(). + +// // Elide display / Alignment +// ImGuiInputTextFlags_ElideLeft = 1 << 17, // When text doesn't fit, elide left side to ensure right side stays visible. Useful for path/filenames. Single-line only! + +// // Callback features +// ImGuiInputTextFlags_CallbackCompletion = 1 << 18, // Callback on pressing TAB (for completion handling) +// ImGuiInputTextFlags_CallbackHistory = 1 << 19, // Callback on pressing Up/Down arrows (for history handling) +// ImGuiInputTextFlags_CallbackAlways = 1 << 20, // Callback on each iteration. User code may query cursor position, modify text buffer. +// ImGuiInputTextFlags_CallbackCharFilter = 1 << 21, // Callback on character inputs to replace or discard them. Modify 'EventChar' to replace or discard, or return 1 in callback to discard. +// ImGuiInputTextFlags_CallbackResize = 1 << 22, // Callback on buffer capacity changes request (beyond 'buf_size' parameter value), allowing the string to grow. Notify when the string wants to be resized (for string types which hold a cache of their Size). You will be provided a new BufSize in the callback and NEED to honor it. (see misc/cpp/imgui_stdlib.h for an example of using this) +// ImGuiInputTextFlags_CallbackEdit = 1 << 23, // Callback on any edit. Note that InputText() already returns true on edit + you can always use IsItemEdited(). The callback is useful to manipulate the underlying buffer while focus is active. + pub const InputTextFlags = packed struct(c_int) { // ImGuiInputTextFlags chars_decimal: bool = false, // ImGuiInputTextFlags_CharsDecimal = 1 << 0 chars_hexadecimal: bool = false, // ImGuiInputTextFlags_CharsHexadecimal = 1 << 1 - chars_uppercase: bool = false, // ImGuiInputTextFlags_CharsUppercase = 1 << 2 - no_blank: bool = false, // ImGuiInputTextFlags_CharsNoBlank = 1 << 3 - auto_select_all: bool = false, // ImGuiInputTextFlags_AutoSelectAll = 1 << 4 - enter_returns_true: bool = false, // ImGuiInputTextFlags_EnterReturnsTrue = 1 << 5 + chars_scientific: bool = false, // ImGuiInputTextFlags_CharsHexadecimal = 1 << 2 + chars_uppercase: bool = false, // ImGuiInputTextFlags_CharsUppercase = 1 << 3 + no_blank: bool = false, // ImGuiInputTextFlags_CharsNoBlank = 1 << 4 + auto_select_all: bool = false, // ImGuiInputTextFlags_AutoSelectAll = 1 << 5 + enter_returns_true: bool = false, // ImGuiInputTextFlags_EnterReturnsTrue = 1 << 6 + // // todo verify these callback_completion: bool = false, // ImGuiInputTextFlags_CallbackCompletion = 1 << 6, callback_history: bool = false, // ImGuiInputTextFlags_CallbackHistory = 1 << 7, callback_always: bool = false, // ImGuiInputTextFlags_CallbackAlways = 1 << 8, @@ -145,7 +177,6 @@ pub const InputTextFlags = packed struct(c_int) { // ImGuiInputTextFlags read_only: bool = false, // ImGuiInputTextFlags_ReadOnly = 1 << 14, password: bool = false, // ImGuiInputTextFlags_Password = 1 << 15, no_undo_redo: bool = false, // ImGuiInputTextFlags_NoUndoRedo = 1 << 16, - chars_scientific: bool = false, // ImGuiInputTextFlags_CharsScientific = 1 << 17, callback_resize: bool = false, // ImGuiInputTextFlags_CallbackResize = 1 << 18, callback_edit: bool = false, // ImGuiInputTextFlags_CallbackEdit = 1 << 19 reserved: u12 = 0, // reserved, don't use diff --git a/projects/build.zig b/projects/build.zig index ad51cd2..4122633 100644 --- a/projects/build.zig +++ b/projects/build.zig @@ -36,7 +36,7 @@ pub fn build(b: *std.Build) void { // tools const newProjectMaker = blbuild.program(.{ - .name = "newProjectMaker", + .name = "newProject", .desc = "new project maker", .root_source_file = b.path("tools/newProjectMaker.zig"), }); diff --git a/projects/tools/newProjectMaker.zig b/projects/tools/newProjectMaker.zig index 797b92c..e6d03d5 100644 --- a/projects/tools/newProjectMaker.zig +++ b/projects/tools/newProjectMaker.zig @@ -3,7 +3,8 @@ pub var NeonObjectTable = core.EngineObjectVTable.from(@This(), "main"); const GameContext = @This(); allocator: std.mem.Allocator, -textBuffer: [8192]u8, +pathBuffer: [8192]u8, +projectNameBuffer: [8192]u8, commandQueue: core.RingQueue(Task), @@ -25,11 +26,14 @@ pub fn init(allocator: std.mem.Allocator) !*@This() { const self = try allocator.create(@This()); self.* = .{ .allocator = allocator, - .textBuffer = std.mem.zeroes([8192]u8), + .pathBuffer = std.mem.zeroes([8192]u8), + .projectNameBuffer = std.mem.zeroes([8192]u8), .commandQueue = try core.RingQueue(Task).init(self.allocator, 4096), .targetName = try allocator.dupe(u8, "sample"), .engineRelative = try allocator.dupe(u8, "Backlog"), }; + + std.mem.copyForwards(u8, &self.projectNameBuffer, "newProject"); return self; } @@ -41,6 +45,35 @@ pub fn prepare(self: *@This()) !void { pub fn tick(self: *@This(), dt: f64) void { _ = dt; + self.tickTasks() catch {}; + + if (self.activeCommand != null or self.commandQueue.count() > 0) { + ig.setNextWindowPos(.{ .x = 20, .y = 20 }, .{}, .{}); + + if (ig.begin("tasks", null, .{ + .always_auto_resize = true, + .no_title_bar = true, + .no_move = true, + .no_resize = true, + .no_collapse = true, + })) { + ig.textf("outstanding tasks: {d}", .{self.commandQueue.count() + 1}); + + if (self.activeCommand) |cmd| { + ig.textf("current command: ", .{}); + + for (cmd.args) |arg| { + ig.textf("{s}", .{arg}); + ig.sameLine(0, 4); + } + } + } + + ig.end(); + + return; + } + ig.setNextWindowPos(.{ .x = 200, .y = 200 }, .{}, .{}); if (ig.begin("New Project...", null, .{ @@ -49,16 +82,27 @@ pub fn tick(self: *@This(), dt: f64) void { .no_resize = true, .no_collapse = true, })) { - ig.setNextItemWidth(600); if (ig.inputText( - "##Path", - &self.textBuffer, - self.textBuffer.len, - .{ .no_blank = true }, + "##ProjectName", + &self.projectNameBuffer, + self.projectNameBuffer.len, + .{}, null, null, )) { - core.engine_log("huh", .{}); + // core.engine_log("huh", .{}); + } + + ig.setNextItemWidth(600); + if (ig.inputText( + "##Path", + &self.pathBuffer, + self.pathBuffer.len, + .{}, + null, + null, + )) { + // core.engine_log("huh", .{}); } ig.sameLine(0, 3); @@ -72,31 +116,15 @@ pub fn tick(self: *@This(), dt: f64) void { } if (ig.button("Create", .{})) { - const asSlice = std.mem.span(@as([*c]const u8, @ptrCast(&self.textBuffer))); - core.engine_log("creating project: {s}", .{self.textBuffer}); + const asSlice = std.mem.span(@as([*c]const u8, @ptrCast(&self.pathBuffer))); + core.engine_log("creating project: {s}", .{self.pathBuffer}); self.createProject(asSlice) catch {}; } } ig.end(); - if (self.activeCommand != null or self.commandQueue.count() > 0) { - ig.setNextWindowPos(.{ .x = 20, .y = 20 }, .{}, .{}); - - if (ig.begin("tasks", null, .{ - .always_auto_resize = true, - .no_title_bar = true, - .no_move = true, - .no_resize = true, - .no_collapse = true, - })) { - ig.textf("outstanding tasks: {d}", .{self.commandQueue.count() + 1}); - } - - ig.end(); - } - if (self.showInstructions) { - ig.setNextWindowPos(.{ .x = 20, .y = 20 }, .{}, .{}); + ig.setNextWindowPos(.{ .x = 100, .y = 100 }, .{}, .{}); ig.setNextWindowSize(.{ .x = 800, .y = 500 }, .{}); if (ig.begin("Instructions", null, .{ .always_auto_resize = true, @@ -112,8 +140,6 @@ pub fn tick(self: *@This(), dt: f64) void { } ig.end(); } - - self.tickTasks() catch {}; } fn tickTasks(self: *@This()) !void { @@ -122,7 +148,7 @@ fn tickTasks(self: *@This()) !void { switch (task) { .subprocess => |t| { self.activeCommand = t; - core.engine_log("starting task {any}", .{t.args}); + // core.engine_log("starting task {any}", .{t.args}); try t.run(); }, .func => |f| { @@ -143,12 +169,24 @@ fn tickTasks(self: *@This()) !void { pub fn createProject(self: *@This(), targetDir: []const u8) !void { self.targetDir = try self.allocator.dupe(u8, targetDir); + + { + const asSlice = std.mem.span(@as([*c]const u8, @ptrCast(&self.projectNameBuffer))); + self.targetName = try self.allocator.dupe(u8, asSlice); + } + try self.commandQueue.pushLocked(.{ .subprocess = try sys.runCommand( self.allocator, &.{ "zig", "init" }, targetDir, ) }); + // const deployShaderDir = try std.fmt.allocPrint(self.allocator, "{s}/content/_shaders", .{self.targetDir.?}); + + if (builtin.os.tag == .windows) { + // os copy over the entire directory + } else {} + try self.commandQueue.pushLocked(.{ .func = updateBuildZig }); try self.commandQueue.pushLocked(.{ .subprocess = try sys.runCommand( @@ -157,9 +195,46 @@ pub fn createProject(self: *@This(), targetDir: []const u8) !void { targetDir, ) }); + try self.commandQueue.pushLocked(.{ .func = fingerprintGeneratePostbuild }); + try self.commandQueue.pushLocked(.{ .func = copyShaders }); try self.commandQueue.pushLocked(.{ .func = displayInstructions }); } +fn fingerprintGeneratePostbuild(self: *@This()) void { + const task = sys.runCommand( + self.allocator, + &.{ "zig", "build", "install" }, + self.targetDir.?, + ) catch return; + defer task.destroy(); + + task.wait(); + + task.mutex.lock(); + core.engine_log("task stdout: {s}", .{task.stdout.items}); + task.mutex.unlock(); +} + +fn copyShaders(self: *@This()) void { + self.copyShadersWrap() catch {}; +} + +fn copyShadersWrap(self: *@This()) !void { + var srcDir = try std.fs.cwd().openDir("content/_shaders", .{ .iterate = true }); + defer srcDir.close(); + + var targetDir = try std.fs.cwd().openDir(self.targetDir.?, .{}); + defer targetDir.close(); + + try targetDir.makeDir("content"); + try targetDir.makeDir("content/_shaders"); + + var dstDir = try targetDir.openDir("content/_shaders", .{}); + defer dstDir.close(); + + try copyRecursiveDir(self.allocator, &srcDir, &dstDir); +} + fn loadFileAllocFromCwd(allocator: std.mem.Allocator, dir: std.fs.Dir, path: []const u8) ![]u8 { var file = try dir.openFile(path, .{}); defer file.close(); @@ -217,10 +292,10 @@ fn writeBuildZigFiles(self: *@This()) !void { // 2. create src/main.zig // 3. build.zig // 4. build.zig.zon - var workingDir = try std.fs.cwd().openDir(self.targetDir.?, .{}); + var workingDir = try std.fs.cwd().openDir(self.targetDir.?, .{ .iterate = true }); defer workingDir.close(); - core.engine_logs("deliting files..."); + core.engine_logs("deleting files..."); try workingDir.deleteFile("src/root.zig"); try workingDir.deleteFile("src/main.zig"); try workingDir.deleteFile("build.zig"); @@ -413,8 +488,8 @@ pub fn onFolderSelected( ) void { const self: *@This() = @ptrCast(@alignCast(ctx)); if (path) |p| { - std.mem.copyForwards(u8, &self.textBuffer, p); - self.textBuffer[p.len] = 0; + std.mem.copyForwards(u8, &self.pathBuffer, p); + self.pathBuffer[p.len] = 0; } } @@ -425,11 +500,34 @@ pub fn deinit(self: *@This()) void { self.allocator.destroy(self); } +fn copyRecursiveDir( + allocator: std.mem.Allocator, + src_dir: *std.fs.Dir, + dest_dir: *std.fs.Dir, +) !void { + var walker = try src_dir.walk(allocator); + defer walker.deinit(); + + while (try walker.next()) |entry| { + switch (entry.kind) { + .file => { + try entry.dir.copyFile(entry.basename, dest_dir.*, entry.path, .{}); + }, + .directory => { + try dest_dir.makeDir(entry.path); + }, + else => {}, + } + } +} + pub fn main() anyerror!void { var spec = try api.getSpec("New Project Dialogue"); + // try spec.put("useGPA", .{ .boolean = false }); _ = api.startEngine(&NeonObjectTable, &spec); } +const builtin = @import("builtin"); const std = @import("std"); const api = @import("backlog"); const ig = api.imgui.api;