From bfface4dbd8fe91069a5aadbf3f108d0c4abd608 Mon Sep 17 00:00:00 2001 From: Matt Knight Date: Sat, 25 Oct 2025 11:15:10 -0700 Subject: [PATCH 1/4] "Regz Wizard" -> Sorcerer --- build.zig | 47 ++- tools/regz-wizard/build.zig | 59 --- tools/regz-wizard/build.zig.zon | 19 - tools/regz-wizard/src/VirtualFilesystem.zig | 43 -- tools/regz-wizard/src/main.zig | 143 ------- tools/sorcerer/README.md | 1 + tools/sorcerer/build.zig | 291 +++++++++++++ tools/sorcerer/build.zig.zon | 25 ++ .../src/Inventory.zig} | 0 tools/sorcerer/src/RegisterSchemaUsage.zig | 35 ++ tools/sorcerer/src/RegzWindow.zig | 233 +++++++++++ tools/sorcerer/src/VirtualFilesystem.zig | 173 ++++++++ tools/sorcerer/src/main.zig | 393 ++++++++++++++++++ .../src/zig-favicon.png | Bin 14 files changed, 1180 insertions(+), 282 deletions(-) delete mode 100644 tools/regz-wizard/build.zig delete mode 100644 tools/regz-wizard/build.zig.zon delete mode 100644 tools/regz-wizard/src/VirtualFilesystem.zig delete mode 100644 tools/regz-wizard/src/main.zig create mode 100644 tools/sorcerer/README.md create mode 100644 tools/sorcerer/build.zig create mode 100644 tools/sorcerer/build.zig.zon rename tools/{regz-wizard/src/Virtual_FS.zig => sorcerer/src/Inventory.zig} (100%) create mode 100644 tools/sorcerer/src/RegisterSchemaUsage.zig create mode 100644 tools/sorcerer/src/RegzWindow.zig create mode 100644 tools/sorcerer/src/VirtualFilesystem.zig create mode 100644 tools/sorcerer/src/main.zig rename tools/{regz-wizard => sorcerer}/src/zig-favicon.png (100%) diff --git a/build.zig b/build.zig index 9f5580cf5..39292a303 100644 --- a/build.zig +++ b/build.zig @@ -1,6 +1,7 @@ const std = @import("std"); const Build = std.Build; const LazyPath = Build.LazyPath; +const assert = std.debug.assert; const internals = @import("build-internals"); pub const Target = internals.Target; @@ -138,25 +139,35 @@ fn generate_release_steps(b: *Build) void { } } -pub const PortSelect = blk: { - var fields: []const std.builtin.Type.StructField = &.{}; - for (port_list) |port| { - fields = fields ++ [_]std.builtin.Type.StructField{.{ - .name = port.name, - .type = bool, - .default_value_ptr = @as(*const anyopaque, @ptrCast(&false)), - .is_comptime = false, - .alignment = @alignOf(bool), - }}; +pub const PortSelect = struct { + esp: bool = false, + gd32: bool = false, + atsam: bool = false, + avr: bool = false, + nrf5x: bool = false, + lpc: bool = false, + mcx: bool = false, + rp2xxx: bool = false, + stm32: bool = false, + ch32v: bool = false, + + pub const all: PortSelect = blk: { + var ret: PortSelect = undefined; + for (@typeInfo(PortSelect).@"struct".fields) |field| { + @field(ret, field.name) = true; + } + + break :blk ret; + }; + + comptime { + // assumes fields are in the same order as the port list + for (port_list, @typeInfo(PortSelect).@"struct".fields) |port_entry, field| { + assert(std.mem.eql(u8, port_entry.name, field.name)); + const default_value_ptr: *const bool = @ptrCast(field.default_value_ptr); + assert(false == default_value_ptr.*); + } } - break :blk @Type(.{ - .@"struct" = .{ - .layout = .auto, - .fields = fields, - .decls = &.{}, - .is_tuple = false, - }, - }); }; // Don't know if this is required but it doesn't hurt either. diff --git a/tools/regz-wizard/build.zig b/tools/regz-wizard/build.zig deleted file mode 100644 index f803e615b..000000000 --- a/tools/regz-wizard/build.zig +++ /dev/null @@ -1,59 +0,0 @@ -const std = @import("std"); - -pub fn build(b: *std.Build) void { - const target = b.standardTargetOptions(.{}); - const optimize = b.standardOptimizeOption(.{}); - - const dvui_dep = b.dependency("dvui", .{ - .target = target, - .optimize = optimize, - }); - - const regz_dep = b.dependency("regz", .{ - .target = target, - .optimize = optimize, - }); - - const regz_mod = regz_dep.module("regz"); - const dvui_mod = dvui_dep.module("dvui_sdl3"); - - const exe_mod = b.createModule(.{ - .root_source_file = b.path("src/main.zig"), - .target = target, - .optimize = optimize, - .imports = &.{ - .{ - .name = "regz", - .module = regz_mod, - }, - .{ - .name = "dvui", - .module = dvui_mod, - }, - }, - }); - - const exe = b.addExecutable(.{ - .name = "regz_wizard", - .root_module = exe_mod, - }); - b.installArtifact(exe); - - const run_cmd = b.addRunArtifact(exe); - run_cmd.step.dependOn(b.getInstallStep()); - - if (b.args) |args| { - run_cmd.addArgs(args); - } - - const run_step = b.step("run", "Run the app"); - run_step.dependOn(&run_cmd.step); - - const exe_unit_tests = b.addTest(.{ - .root_module = exe_mod, - }); - - const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests); - const test_step = b.step("test", "Run unit tests"); - test_step.dependOn(&run_exe_unit_tests.step); -} diff --git a/tools/regz-wizard/build.zig.zon b/tools/regz-wizard/build.zig.zon deleted file mode 100644 index 6614c4f0f..000000000 --- a/tools/regz-wizard/build.zig.zon +++ /dev/null @@ -1,19 +0,0 @@ -.{ - .name = .tools_regzwizard, - .version = "0.0.0", - .fingerprint = 0xe9f2d03f97400569, - .minimum_zig_version = "0.15.1", - .dependencies = .{ - .regz = .{ .path = "../regz" }, - .dvui = .{ - .url = "git+https://github.com/david-vanderson/dvui#b6282bab5ec582dd78b9affda87365ad2185f18a", - .hash = "dvui-0.4.0-dev-AQFJmajB0wA8Is3S1UpcCeZzhvLhn8IpJ3p23RU0FoXu", - }, - }, - .paths = .{ - "README.md", - "build.zig", - "build.zig.zon", - "src", - }, -} diff --git a/tools/regz-wizard/src/VirtualFilesystem.zig b/tools/regz-wizard/src/VirtualFilesystem.zig deleted file mode 100644 index 311a97e58..000000000 --- a/tools/regz-wizard/src/VirtualFilesystem.zig +++ /dev/null @@ -1,43 +0,0 @@ -//! Filesystem to store generated files -gpa: Allocator, -map: std.StringArrayHashMapUnmanaged([]const u8) = .{}, - -const std = @import("std"); -const Allocator = std.mem.Allocator; - -const regz = @import("regz"); -const Directory = regz.Database.Directory; - -const VirtualFilesystem = @This(); - -pub fn init(gpa: Allocator) VirtualFilesystem { - return VirtualFilesystem{ - .gpa = gpa, - }; -} - -pub fn deinit(fs: *VirtualFilesystem) void { - for (fs.map.keys(), fs.map.values()) |path, content| { - fs.gpa.free(path); - fs.gpa.free(content); - } - - fs.map.deinit(fs.gpa); -} - -pub fn dir(fs: *VirtualFilesystem) Directory { - return Directory{ - .ptr = @ptrCast(fs), - .vtable = &.{ - .create_file = create_file, - }, - }; -} - -fn create_file(ctx: *anyopaque, path: []const u8, content: []const u8) Directory.CreateFileError!void { - const fs: *VirtualFilesystem = @ptrCast(@alignCast(ctx)); - const path_copy = try fs.gpa.dupe(u8, path); - const content_copy = try fs.gpa.dupe(u8, content); - // TODO: clean up collisions - try fs.map.put(fs.gpa, path_copy, content_copy); -} diff --git a/tools/regz-wizard/src/main.zig b/tools/regz-wizard/src/main.zig deleted file mode 100644 index dcf18615b..000000000 --- a/tools/regz-wizard/src/main.zig +++ /dev/null @@ -1,143 +0,0 @@ -const std = @import("std"); -const regz = @import("regz"); -const dvui = @import("dvui"); -const VirtualFilesystem = @import("VirtualFilesystem.zig"); - -const window_icon_png = @embedFile("zig-favicon.png"); - -pub const dvui_app: dvui.App = .{ - .config = .{ - .options = .{ - .size = .{ .w = 800.0, .h = 600.0 }, - .min_size = .{ .w = 250.0, .h = 350.0 }, - .title = "Regz Wizard", - .icon = window_icon_png, - }, - }, - .frameFn = frame, - .initFn = init, - .deinitFn = deinit, -}; - -pub const main = dvui.App.main; -pub const panic = dvui.App.panic; -pub const std_options: std.Options = .{ - .logFn = dvui.App.logFn, -}; - -var db: ?*regz.Database = null; -var debug_allocator: std.heap.DebugAllocator(.{}) = .init; -const gpa = debug_allocator.allocator(); -var vfs: VirtualFilesystem = undefined; -var current_gen_file: usize = 0; - -pub fn init(win: *dvui.Window) anyerror!void { - _ = win; - vfs = .init(gpa); -} - -// Run as app is shutting down before dvui.Window.deinit() -pub fn deinit() void { - if (db) |d| d.destroy(); - vfs.deinit(); - _ = debug_allocator.deinit(); -} - -// TODO: add mechanism to open a file, this creates a brand new db instance. -// - Some sort of menu bar -// - File -> Drop down - -// Run each frame to do normal UI -pub fn frame() !dvui.App.Result { - var scaler = dvui.scale(@src(), .{ .scale = &dvui.currentWindow().content_scale, .pinch_zoom = .global }, .{ .rect = .cast(dvui.windowRect()) }); - defer scaler.deinit(); - - var scroll = dvui.scrollArea(@src(), .{}, .{ - .expand = .both, - }); - defer scroll.deinit(); - - var vbox = try dvui.box(@src(), .{ .vertical = true }, .{ .expand = .both, .margin = .{ .x = 4 } }); - defer vbox.deinit(); - - { - var m = try dvui.menu(@src(), .horizontal, .{}); - defer m.deinit(); - - if (try dvui.menuItemLabel(@src(), "File", .{ .submenu = true }, .{ .expand = .horizontal })) |r| { - var fw = try dvui.floatingMenu(@src(), .{ .from = r }, .{}); - defer fw.deinit(); - - if (try dvui.menuItemLabel(@src(), "Open", .{}, .{}) != null) blk: { - defer fw.close(); - const filename = try dvui.dialogNativeFileOpen(dvui.currentWindow().arena(), .{ .title = "dvui native file open", .filters = &.{ "*.svd", "*.atdf" }, .filter_description = "images" }); - if (filename) |f| { - const ext = std.fs.path.extension(f); - const format: regz.Database.Format = if (std.mem.eql(u8, ext, ".svd")) - .svd - else if (std.mem.eql(u8, ext, ".atdf")) - .atdf - else - break :blk; - - if (db) |d| d.destroy(); - - vfs.deinit(); - vfs = .init(gpa); - - db = try regz.Database.create_from_path(gpa, format, f); - try db.?.to_zig(vfs.dir(), .{ .for_microzig = true }); - } - } - - if (try dvui.menuItemLabel(@src(), "Exit", .{}, .{})) |_| { - return .close; - } - } - - if (db != null) if (try dvui.menuItemLabel(@src(), "Edit", .{ .submenu = true }, .{ .expand = .horizontal })) |r| { - var fw = try dvui.floatingMenu(@src(), .{ .from = r }, .{}); - defer fw.deinit(); - - if (try dvui.menuItemLabel(@src(), "Apply Patches", .{}, .{})) |_| { - defer fw.close(); - - const filename = try dvui.dialogNativeFileOpen(dvui.currentWindow().arena(), .{ .title = "dvui native file open", .filters = &.{"*.json"}, .filter_description = "images" }); - _ = filename; - } - }; - - if (try dvui.menuItemLabel(@src(), "View", .{ .submenu = true }, .{ .expand = .horizontal })) |r| { - var fw = try dvui.floatingMenu(@src(), .{ .from = r }, .{}); - defer fw.deinit(); - - if (try dvui.menuItemLabel(@src(), "Demo Window", .{}, .{})) |_| { - dvui.Examples.show_demo_window = true; - fw.close(); - } - } - } - - // look at demo() for examples of dvui widgets, shows in a floating window - try dvui.Examples.demo(); - - if (db != null) { - const entries = vfs.map.keys(); - _ = try dvui.dropdown(@src(), entries, ¤t_gen_file, .{}); - - const current = entries[current_gen_file]; - - var font = dvui.themeGet().font_body; - font.name = "VeraMono"; - - var tl = dvui.TextLayoutWidget.init(@src(), .{}, .{ .expand = .horizontal, .font = font }); - try tl.install(.{}); - defer tl.deinit(); - - try tl.addText(vfs.map.get(current).?, .{ - .font = font, - }); - } - - return .ok; -} diff --git a/tools/sorcerer/README.md b/tools/sorcerer/README.md new file mode 100644 index 000000000..19cc35f33 --- /dev/null +++ b/tools/sorcerer/README.md @@ -0,0 +1 @@ +# Sorcerer diff --git a/tools/sorcerer/build.zig b/tools/sorcerer/build.zig new file mode 100644 index 000000000..12e284f49 --- /dev/null +++ b/tools/sorcerer/build.zig @@ -0,0 +1,291 @@ +const std = @import("std"); +const LazyPath = std.Build.LazyPath; +const microzig = @import("microzig"); +const RegisterSchemaUsage = @import("src/RegisterSchemaUsage.zig"); + +const MicroBuild = microzig.MicroBuild(.all); + +pub fn build(b: *std.Build) void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + const mz_dep = b.dependency("microzig", .{}); + + const mb = MicroBuild.init(b, mz_dep) orelse return; + const register_schemas = get_register_schemas(b, mb) catch @panic("OOM"); + const write_files = b.addWriteFiles(); + const register_schema = write_files.add("register_schemas.json", std.json.Stringify.valueAlloc(b.allocator, register_schemas, .{}) catch @panic("OOM")); + const register_schema_install = b.addInstallFile(register_schema, "data/register_schemas.json"); + b.getInstallStep().dependOn(®ister_schema_install.step); + + const dvui_dep = b.dependency("dvui", .{ + .target = target, + .optimize = optimize, + }); + + const regz_dep = mz_dep.builder.dependency("tools/regz", .{ + .target = target, + .optimize = optimize, + }); + + const serial_dep = b.dependency("serial", .{ + .target = target, + .optimize = optimize, + }); + + const dvui_mod = dvui_dep.module("dvui_sdl3"); + const regz_mod = regz_dep.module("regz"); + const serial_mod = serial_dep.module("serial"); + + const exe_mod = b.createModule(.{ + .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + .imports = &.{ + .{ + .name = "dvui", + .module = dvui_mod, + }, + .{ + .name = "regz", + .module = regz_mod, + }, + .{ + .name = "serial", + .module = serial_mod, + }, + }, + }); + + const exe = b.addExecutable(.{ + .name = "sorcerer", + .root_module = exe_mod, + }); + b.installArtifact(exe); + + const run_cmd = b.addRunArtifact(exe); + //run_cmd.addArg("--register-schemas"); + + // I only want the path to the register schema file, not the lazy path, + // because I want to be able to refresh it with `zig build` while sorcerer + // is running. Sorcerer will watch the file for changes and update itself + // automatically. + //run_cmd.addArg(b.getInstallPath(.prefix, register_schema_install.dest_rel_path)); + run_cmd.step.dependOn(b.getInstallStep()); + + if (b.args) |args| { + run_cmd.addArgs(args); + } + + const run_step = b.step("run", "Run the app"); + run_step.dependOn(&run_cmd.step); + + const exe_unit_tests = b.addTest(.{ + .root_module = exe_mod, + }); + + const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests); + const test_step = b.step("test", "Run unit tests"); + test_step.dependOn(&run_exe_unit_tests.step); +} + +fn get_targets(mb: *MicroBuild) []const *const microzig.Target { + @setEvalBranchQuota(50000); + var ret: std.array_list.Managed(*const microzig.Target) = .init(mb.builder.allocator); + inline for (@typeInfo(@FieldType(MicroBuild, "ports")).@"struct".fields) |field| { + recursively_collect_targets(@field(mb.ports, field.name), &ret) catch @panic("OOM"); + } + + return ret.toOwnedSlice() catch unreachable; +} + +fn recursively_collect_targets(field: anytype, targets: *std.array_list.Managed(*const microzig.Target)) !void { + const Type = @TypeOf(field); + + switch (Type) { + *const microzig.Target, *microzig.Target => { + try targets.append(field); + return; + }, + else => {}, + } + + const type_info = @typeInfo(Type); + if (type_info != .@"struct") { + std.log.info("missed type: {s}", .{@typeName(Type)}); + return; + } + + inline for (type_info.@"struct".fields) |child_field| { + try recursively_collect_targets(@field(field, child_field.name), targets); + } +} + +fn find_target_location(b: *std.Build, lazy_path: LazyPath) RegisterSchemaUsage.Location { + return switch (lazy_path) { + .src_path => |src_path| blk: { + const build_root = get_build_root(b, src_path.owner); + break :blk .{ + .src_path = .{ + .build_root = build_root, + //.owner = src_path.owner, + .sub_path = src_path.sub_path, + .port_name = get_port_name(build_root), + }, + }; + }, + .dependency => |val| { + const dependency = val.dependency; + const sub_path = val.sub_path; + const build_root = get_build_root(b, dependency.builder); + const root = @import("root"); + const packages = root.dependencies.packages; + const package_hash = inline for (@typeInfo(packages).@"struct".decls) |decl| { + const package = @field(packages, decl.name); + if (!@hasDecl(package, "build_root")) + continue; + + if (std.mem.eql(u8, package.build_root, build_root)) { + break decl.name; + } + } else unreachable; + + const Pair = struct { port: []const u8, dep: []const u8 }; + const result: Pair = outer: inline for (@typeInfo(packages).@"struct".decls) |decl| { + const package = @field(packages, decl.name); + if (!@hasDecl(package, "deps")) + continue; + + for (package.deps) |dep| { + const name = dep[0]; + const dep_package_hash = dep[1]; + if (std.mem.eql(u8, package_hash, dep_package_hash)) { + break :outer .{ .port = decl.name, .dep = name }; + } + } + } else unreachable; + + return .{ + .dependency = .{ + //.dependency = dependency, + .sub_path = sub_path, + .build_root = build_root, + .port_name = get_port_name(result.port), + .dep_name = result.dep, + }, + }; + }, + else => unreachable, + }; +} + +fn get_build_root(b: *std.Build, owner: *std.Build) []const u8 { + var it = b.graph.dependency_cache.iterator(); + return while (it.next()) |entry| { + if (entry.value_ptr.*.builder == owner) { + break entry.key_ptr.build_root_string; + } + } else unreachable; +} + +const LazyPathContext = struct { + pub const hash = std.array_hash_map.getAutoHashStratFn(LazyPath, @This(), .Shallow); + pub fn eql(_: @This(), a: LazyPath, b: LazyPath, _: usize) bool { + const a_tag: std.meta.Tag(LazyPath) = a; + const b_tag: std.meta.Tag(LazyPath) = b; + if (a_tag != b_tag) + return false; + + return switch (a) { + .src_path => |val| val.owner == b.src_path.owner and std.mem.eql(u8, val.sub_path, b.src_path.sub_path), + .generated => @panic("Generated paths unsupported"), + .cwd_relative => @panic("Cwd relative paths unsupported, you probably shouldn't be vendoring that in MicroZig anyways"), + .dependency => |val| val.dependency == b.dependency.dependency and std.mem.eql(u8, val.sub_path, b.dependency.sub_path), + }; + } +}; + +fn LazyPathHashMap(comptime Value: type) type { + return std.ArrayHashMap(std.Build.LazyPath, Value, LazyPathContext, true); +} + +fn get_register_schemas(b: *std.Build, mb: *MicroBuild) ![]const RegisterSchemaUsage { + const targets = get_targets(mb); + var deduped_targets: LazyPathHashMap(RegisterSchemaUsage.Format) = .init(b.allocator); + var chips: LazyPathHashMap(std.ArrayList(RegisterSchemaUsage.Chip)) = .init(b.allocator); + var boards: LazyPathHashMap(std.ArrayList(RegisterSchemaUsage.Board)) = .init(b.allocator); + var locations: LazyPathHashMap(RegisterSchemaUsage.Location) = .init(b.allocator); + + for (targets) |t| switch (t.chip.register_definition) { + inline else => |lazy_path| { + try deduped_targets.put(lazy_path, switch (t.chip.register_definition) { + .svd => .svd, + .embassy => .embassy, + .atdf => .atdf, + .zig => continue, + }); + + if (chips.getEntry(lazy_path)) |entry| { + try entry.value_ptr.append(b.allocator, .{ + .name = t.chip.name, + }); + } else { + var chip_list: std.ArrayList(RegisterSchemaUsage.Chip) = .{}; + try chip_list.append(b.allocator, .{ + .name = t.chip.name, + }); + try chips.put(lazy_path, chip_list); + } + + if (t.board) |board| if (boards.getEntry(lazy_path)) |entry| { + try entry.value_ptr.append(b.allocator, .{ + .name = board.name, + }); + } else { + var board_list: std.ArrayList(RegisterSchemaUsage.Board) = .{}; + try board_list.append(b.allocator, .{ + .name = board.name, + }); + try boards.put(lazy_path, board_list); + }; + }, + }; + + for (deduped_targets.keys()) |lazy_path| { + const location = find_target_location(b, lazy_path); + try locations.put(lazy_path, location); + } + + var ret: std.ArrayList(RegisterSchemaUsage) = .{}; + for (deduped_targets.keys(), deduped_targets.values()) |lazy_path, format| { + var chip_list = chips.get(lazy_path).?; + var board_list = boards.get(lazy_path); + try ret.append(b.allocator, .{ + .format = format, + .location = locations.get(lazy_path).?, + .chips = try chip_list.toOwnedSlice(b.allocator), + .boards = if (board_list) |*bl| try bl.toOwnedSlice(b.allocator) else &.{}, + }); + } + + return ret.toOwnedSlice(b.allocator); +} + +fn get_port_name(path: []const u8) []const u8 { + var i: u32 = 0; + var slash_count: u32 = 0; + while (i < path.len) : (i += 1) { + if (path[path.len - i - 1] == '/') { + switch (slash_count) { + 0 => slash_count += 1, + 1 => { + const start = path.len - i; + return path[start..]; + }, + else => unreachable, + } + } + } + + unreachable; +} diff --git a/tools/sorcerer/build.zig.zon b/tools/sorcerer/build.zig.zon new file mode 100644 index 000000000..bf876c50c --- /dev/null +++ b/tools/sorcerer/build.zig.zon @@ -0,0 +1,25 @@ +.{ + .name = .sorcerer, + .version = "0.0.1", + .dependencies = .{ + .microzig = .{ + .path = "../..", + }, + .dvui = .{ + .url = "git+https://github.com/david-vanderson/dvui#7cdcf937f41490b70e96f4abe80d8e61a858ec96", + .hash = "dvui-0.4.0-dev-AQFJmUCT1QAYkkYKaevIYepio5OH9VNUa4_8Cpp1qPkV", + }, + .serial = .{ + .url = "git+https://github.com/ZigEmbeddedGroup/serial#fbd7389ff8bbc9fa362aa74081588755b5d028a0", + .hash = "serial-0.0.1-PoeRzF60AAAN8Iu0yXTIX-t3DVzsnmN7vWHKM2HA2Zbq", + }, + }, + .minimum_zig_version = "0.15.2", + .fingerprint = 0x5f77e0bc82473a35, + .paths = .{ + "README.md", + "build.zig", + "build.zig.zon", + "src", + }, +} diff --git a/tools/regz-wizard/src/Virtual_FS.zig b/tools/sorcerer/src/Inventory.zig similarity index 100% rename from tools/regz-wizard/src/Virtual_FS.zig rename to tools/sorcerer/src/Inventory.zig diff --git a/tools/sorcerer/src/RegisterSchemaUsage.zig b/tools/sorcerer/src/RegisterSchemaUsage.zig new file mode 100644 index 000000000..68537079c --- /dev/null +++ b/tools/sorcerer/src/RegisterSchemaUsage.zig @@ -0,0 +1,35 @@ +format: Format, +chips: []const Chip, +boards: []const Board, +location: Location, + +const std = @import("std"); +pub const Format = enum { + svd, + atdf, + embassy, +}; + +pub const Chip = struct { + name: []const u8, +}; + +pub const Board = struct { + name: []const u8, +}; + +pub const Location = union(enum) { + src_path: struct { + //owner: *std.Build, + port_name: []const u8, + sub_path: []const u8, + build_root: []const u8, + }, + dependency: struct { + //dependency: *std.Build.Dependency, + sub_path: []const u8, + build_root: []const u8, + dep_name: []const u8, + port_name: []const u8, + }, +}; diff --git a/tools/sorcerer/src/RegzWindow.zig b/tools/sorcerer/src/RegzWindow.zig new file mode 100644 index 000000000..ce495c47f --- /dev/null +++ b/tools/sorcerer/src/RegzWindow.zig @@ -0,0 +1,233 @@ +gpa: Allocator, +arena: std.heap.ArenaAllocator, +db: *regz.Database, +id_extra: usize, +title: []const u8, +show_window: bool = true, +path: []const u8, +vfs: VirtualFilesystem, +selected_file: ?VirtualFilesystem.ID = null, + +const RegzWindow = @This(); +const std = @import("std"); +const Allocator = std.mem.Allocator; + +const regz = @import("regz"); +const dvui = @import("dvui"); +const VirtualFilesystem = @import("VirtualFilesystem.zig"); + +var count: usize = 0; + +pub fn create(gpa: Allocator, format: regz.Database.Format, path: []const u8) !*RegzWindow { + const window = try gpa.create(RegzWindow); + errdefer gpa.destroy(window); + + var db = try regz.Database.create_from_path(gpa, format, path); + errdefer db.destroy(); + + var arena: std.heap.ArenaAllocator = .init(gpa); + errdefer arena.deinit(); + + const devices = try db.get_devices(arena.allocator()); + const title = switch (format) { + .embassy => "Embassy STM32", + else => switch (devices.len) { + 0 => "", + 1 => devices[0].name, + else => "", + }, + }; + + window.* = .{ + .gpa = gpa, + .db = db, + .id_extra = count, + .title = title, + .arena = arena, + .path = path, + .vfs = .init(gpa), + }; + + try db.to_zig(window.vfs.dir(), .{ .for_microzig = true }); + + count += 1; + + return window; +} + +pub fn destroy(w: *RegzWindow) void { + w.vfs.deinit(); + w.arena.deinit(); + w.db.destroy(); +} + +pub fn show(w: *RegzWindow) !void { + if (!w.show_window) + return; + + var arena: std.heap.ArenaAllocator = .init(w.gpa); + defer arena.deinit(); + + var float = dvui.floatingWindow(@src(), .{}, .{ + .min_size_content = .{ .w = 400, .h = 400 }, + .max_size_content = .width(400), + .id_extra = w.id_extra, + }); + defer float.deinit(); + + float.dragAreaSet(dvui.windowHeader("Regz", w.title, &w.show_window)); + + var hbox = dvui.box(@src(), .{ .dir = .horizontal }, .{ .expand = .both }); + defer hbox.deinit(); + + { + const scroll_arena = dvui.scrollArea(@src(), .{}, .{}); + defer scroll_arena.deinit(); + + try w.show_file_tree( + arena.allocator(), + @src(), + .{}, + .{ + .background = true, + .border = dvui.Rect.all(1), + .padding = dvui.Rect.all(4), + }, + .{ + .padding = dvui.Rect.all(1), + }, + .{ + .border = .{ .x = 1 }, + .corner_radius = dvui.Rect.all(4), + .box_shadow = .{ + .color = .black, + .offset = .{ .x = -5, .y = 5 }, + .shrink = 5, + .fade = 10, + .alpha = 0.15, + }, + }, + ); + } + + const scroll_arena = dvui.scrollArea(@src(), .{}, .{}); + defer scroll_arena.deinit(); + + var tl = dvui.textLayout(@src(), .{}, .{ .expand = .horizontal, .font_style = .body }); + defer tl.deinit(); + + if (w.selected_file) |id| + tl.addText(w.vfs.get_content(id), .{}); +} + +fn show_file_tree( + w: *RegzWindow, + arena: Allocator, + src: std.builtin.SourceLocation, + tree_init_options: dvui.TreeWidget.InitOptions, + tree_options: dvui.Options, + branch_options: dvui.Options, + expander_options: dvui.Options, +) !void { + const unique_id = dvui.parentGet().extendId(@src(), 0); + + var tree = dvui.TreeWidget.tree(src, tree_init_options, tree_options); + defer tree.deinit(); + + const children = try w.vfs.get_children(arena, .root); + try w.show_file_tree_recursive(arena, .root, children, tree, unique_id, branch_options, expander_options); +} + +fn show_file_tree_recursive( + w: *RegzWindow, + arena: Allocator, + directory: VirtualFilesystem.ID, + children: []const VirtualFilesystem.Entry, + tree: *dvui.TreeWidget, + unique_id: dvui.Id, + branch_options: dvui.Options, + expander_options: dvui.Options, +) !void { + var id_extra: usize = 0; + for (children, 0..) |child_entry, i| { + if (directory == .root and w.selected_file == null and i == 0) { + w.selected_file = child_entry.id; + } + id_extra += 1; + var branch_opts_override = dvui.Options{ + .id_extra = id_extra, + .expand = .horizontal, + }; + const expanded = true; + //oconst branch_id = tree.data().id.update(w.vfs.get_name(directory)); + + const branch = tree.branch(@src(), .{ .expanded = expanded }, branch_opts_override.override(branch_options)); + defer branch.deinit(); + switch (child_entry.kind) { + .directory => { + dvui.icon( + @src(), + "FolderIcon", + dvui.entypo.folder, + .{}, + .{ + .gravity_y = 0.5, + }, + ); + + const name = w.vfs.get_name(child_entry.id); + _ = dvui.label(@src(), "{s}", .{name}, .{}); + + dvui.icon( + @src(), + "DropIcon", + if (branch.expanded) dvui.entypo.triangle_down else dvui.entypo.triangle_right, + .{ + //.fill_color = color + }, + .{ + .gravity_y = 0.5, + .gravity_x = 1.0, + }, + ); + + var expander_opts_override = dvui.Options{ + .margin = .{ .x = 14 }, + //.color_border = color, + .background = if (expander_options.border != null) true else false, + .expand = .horizontal, + }; + + if (branch.expander(@src(), .{ .indent = 14 }, expander_opts_override.override(expander_options))) { + // The expander is open, so we need to add the branch to our tracking map + //reorder_tree_open_branches.put(branch_id, {}) catch { + // dvui.log.debug("Failed to track branch state!", .{}); + //}; + + var box = dvui.box(@src(), .{ .dir = .vertical }, .{ + .expand = .horizontal, + .background = false, + .gravity_y = 1.0, + }); + defer box.deinit(); + } + + const grandchildren = try w.vfs.get_children(arena, child_entry.id); + try w.show_file_tree_recursive(arena, child_entry.id, grandchildren, tree, unique_id, branch_options, expander_options); + }, + .file => { + dvui.icon(@src(), "FileIcon", dvui.entypo.text_document, .{}, .{ + .gravity_y = 0.5, + }); + + const name = w.vfs.get_name(child_entry.id); + _ = dvui.label(@src(), "{s}", .{name}, .{}); + + if (branch.button.clicked()) { + w.selected_file = child_entry.id; + std.log.info("Clicked: {s}", .{name}); + } + }, + } + } +} diff --git a/tools/sorcerer/src/VirtualFilesystem.zig b/tools/sorcerer/src/VirtualFilesystem.zig new file mode 100644 index 000000000..0c3a105da --- /dev/null +++ b/tools/sorcerer/src/VirtualFilesystem.zig @@ -0,0 +1,173 @@ +//! Filesystem to store generated files +gpa: Allocator, +directories: Map(ID, Dir) = .{}, +files: Map(ID, File) = .{}, +// child -> parent +hierarchy: Map(ID, ID) = .{}, +next_id: u16 = 1, + +const VirtualFilesystem = @This(); + +const std = @import("std"); +const Allocator = std.mem.Allocator; +const Map = std.AutoArrayHashMapUnmanaged; +const assert = std.debug.assert; + +const regz = @import("regz"); +const Directory = regz.Database.Directory; + +pub const Kind = enum { + file, + directory, +}; + +pub const Dir = struct { + name: []const u8, + + pub fn deinit(d: *Dir, gpa: Allocator) void { + gpa.free(d.name); + } +}; + +pub const File = struct { + name: []const u8, + content: []const u8, + + pub fn deinit(f: *File, gpa: Allocator) void { + gpa.free(f.name); + gpa.free(f.content); + } +}; + +pub const ID = enum(u16) { + root = 0, + _, +}; + +pub fn init(gpa: Allocator) VirtualFilesystem { + return VirtualFilesystem{ + .gpa = gpa, + }; +} + +pub fn deinit(fs: *VirtualFilesystem) void { + for (fs.directories.values()) |*directory| directory.deinit(fs.gpa); + for (fs.files.values()) |*file| file.deinit(fs.gpa); + + fs.directories.deinit(fs.gpa); + fs.files.deinit(fs.gpa); + fs.hierarchy.deinit(fs.gpa); +} + +pub fn dir(fs: *VirtualFilesystem) Directory { + return Directory{ + .ptr = @ptrCast(fs), + .vtable = &.{ + .create_file = create_file_fn, + }, + }; +} + +pub const Entry = struct { + id: ID, + kind: Kind, +}; + +pub fn get_children(fs: *VirtualFilesystem, allocator: Allocator, id: ID) ![]const Entry { + var ret: std.ArrayList(Entry) = .{}; + for (fs.hierarchy.keys(), fs.hierarchy.values()) |child_id, parent_id| { + if (parent_id == id) + try ret.append(allocator, .{ + .id = child_id, + .kind = fs.get_kind(child_id), + }); + } + return ret.toOwnedSlice(allocator); +} + +fn new_id(fs: *VirtualFilesystem) ID { + defer fs.next_id += 1; + return @enumFromInt(fs.next_id); +} + +fn create_dir(fs: *VirtualFilesystem, parent: ID, name: []const u8) !ID { + const id = fs.new_id(); + + const name_copy = try fs.gpa.dupe(u8, name); + try fs.directories.put(fs.gpa, id, .{ + .name = name_copy, + }); + + try fs.hierarchy.put(fs.gpa, id, parent); + + return id; +} + +fn create_file(fs: *VirtualFilesystem, parent: ID, name: []const u8, content: []const u8) !ID { + const id = fs.new_id(); + + const name_copy = try fs.gpa.dupe(u8, name); + const content_copy = try fs.gpa.dupe(u8, content); + try fs.files.put(fs.gpa, id, .{ + .name = name_copy, + .content = content_copy, + }); + + try fs.hierarchy.put(fs.gpa, id, parent); + + return id; +} + +pub fn get_kind(fs: *VirtualFilesystem, id: ID) Kind { + return if (fs.files.contains(id)) + .file + else if (fs.directories.contains(id)) + .directory + else + unreachable; +} + +pub fn get_name(fs: *VirtualFilesystem, id: ID) []const u8 { + return if (fs.files.get(id)) |f| + f.name + else if (fs.directories.get(id)) |d| + d.name + else + unreachable; +} + +pub fn get_content(fs: *VirtualFilesystem, id: ID) []const u8 { + assert(fs.get_kind(id) == .file); + return fs.files.get(id).?.content; +} + +fn get_child(fs: *VirtualFilesystem, parent: ID, component: []const u8) ?ID { + return for (fs.hierarchy.keys(), fs.hierarchy.values()) |child_id, entry_parent_id| { + if (entry_parent_id == parent and std.mem.eql(u8, component, fs.get_name(child_id))) + break child_id; + } else null; +} + +fn create_file_fn(ctx: *anyopaque, path: []const u8, content: []const u8) Directory.CreateFileError!void { + const fs: *VirtualFilesystem = @ptrCast(@alignCast(ctx)); + + var components: std.ArrayList([]const u8) = .{}; + defer components.deinit(fs.gpa); + + var it = try std.fs.path.componentIterator(path); + while (it.next()) |component| + try components.append(fs.gpa, component.name); + + var parent: ID = .root; + if (components.items.len > 1) for (components.items[0 .. components.items.len - 2]) |component| { + const dir_id: ID = fs.get_child(parent, component) orelse blk: { + const id = try fs.create_dir(parent, component); + break :blk id; + }; + + parent = dir_id; + }; + + const file_name = components.items[components.items.len - 1]; + _ = try fs.create_file(parent, file_name, content); +} diff --git a/tools/sorcerer/src/main.zig b/tools/sorcerer/src/main.zig new file mode 100644 index 000000000..dcb7c7d04 --- /dev/null +++ b/tools/sorcerer/src/main.zig @@ -0,0 +1,393 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const dvui = @import("dvui"); +const serial = @import("serial"); +const regz = @import("regz"); +const RegisterSchemaUsage = @import("RegisterSchemaUsage.zig"); +const RegzWindow = @import("RegzWindow.zig"); + +const window_icon_png = @embedFile("zig-favicon.png"); + +// To be a dvui App: +// * declare "dvui_app" +// * expose the backend's main function +// * use the backend's log function +pub const dvui_app: dvui.App = .{ + .config = .{ + .options = .{ + .size = .{ .w = 800.0, .h = 600.0 }, + .min_size = .{ .w = 250.0, .h = 350.0 }, + .title = "Sorcerer", + .icon = window_icon_png, + .window_init_options = .{ + // Could set a default theme here + // .theme = dvui.Theme.builtin.dracula, + }, + }, + }, + .frameFn = AppFrame, + .initFn = AppInit, + .deinitFn = AppDeinit, +}; +pub const main = dvui.App.main; +pub const panic = dvui.App.panic; +pub const std_options: std.Options = .{ + .logFn = dvui.App.logFn, + .log_level = .info, +}; + +var gpa_instance = std.heap.GeneralPurposeAllocator(.{}){}; +const gpa = gpa_instance.allocator(); +var arena: std.heap.ArenaAllocator = .init(gpa); + +var state: State = .{}; + +const State = struct { + orig_content_scale: f32 = 1.0, + register_schema_path: []const u8 = "./zig-out/data/register_schemas.json", + register_schema_usages: ?std.json.Parsed([]const RegisterSchemaUsage) = null, + regz_windows: std.StringArrayHashMapUnmanaged(*RegzWindow) = .{}, + show_from_microzig_window: bool = false, +}; + +// Runs before the first frame, after backend and dvui.Window.init() +// - runs between win.begin()/win.end() +pub fn AppInit(win: *dvui.Window) !void { + state.orig_content_scale = win.content_scale; + //try dvui.addFont("NOTO", @embedFile("../src/fonts/NotoSansKR-Regular.ttf"), null); + + if (false) { + // If you need to set a theme based on the users preferred color scheme, do it here + win.theme = switch (win.backend.preferredColorScheme() orelse .light) { + .light => dvui.Theme.builtin.adwaita_light, + .dark => dvui.Theme.builtin.adwaita_dark, + }; + } + std.log.info("starting...", .{}); + const register_schema_file = try std.fs.cwd().openFile(state.register_schema_path, .{}); + defer register_schema_file.close(); + + const text = try register_schema_file.readToEndAlloc(gpa, 1024 * 1024); + defer gpa.free(text); + + state.register_schema_usages = try std.json.parseFromSlice([]const RegisterSchemaUsage, gpa, text, .{ + .allocate = .alloc_always, + }); + + //var it = try serial.list(); + //defer it.deinit(); + //while (try it.next()) |info| { + // tl2.addText(try arena.allocator().dupe(u8, info.display_name), .{}); + // tl2.addText("\n\n", .{}); + //} +} + +// Run as app is shutting down before dvui.Window.deinit() +pub fn AppDeinit() void {} + +// Run each frame to do normal UI +pub fn AppFrame() !dvui.App.Result { + return frame(); +} + +pub fn frame() !dvui.App.Result { + arena.deinit(); + arena = .init(gpa); + + var scaler = dvui.scale(@src(), .{ .scale = &dvui.currentWindow().content_scale, .pinch_zoom = .global }, .{ .rect = .cast(dvui.windowRect()) }); + scaler.deinit(); + + { + var hbox = dvui.box(@src(), .{ .dir = .horizontal }, .{ .style = .window, .background = true, .expand = .horizontal }); + defer hbox.deinit(); + + var m = dvui.menu(@src(), .horizontal, .{}); + defer m.deinit(); + + if (dvui.menuItemLabel(@src(), "File", .{ .submenu = true }, .{ .tag = "first-focusable" })) |r| { + var fw = dvui.floatingMenu(@src(), .{ .from = r }, .{}); + defer fw.deinit(); + + try open_register_schema_submenu(m); + + if (dvui.menuItemLabel(@src(), "Close Menu", .{}, .{ .expand = .horizontal }) != null) { + m.close(); + } + + if (dvui.backend.kind != .web) { + if (dvui.menuItemLabel(@src(), "Exit", .{}, .{ .expand = .horizontal }) != null) { + return .close; + } + } + } + } + + var scroll = dvui.scrollArea(@src(), .{}, .{ .expand = .both, .style = .window }); + defer scroll.deinit(); + + var tl = dvui.textLayout(@src(), .{}, .{ .expand = .horizontal, .font_style = .title_4 }); + const lorem = "This is a dvui.App example that can compile on multiple backends."; + tl.addText(lorem, .{}); + tl.addText("\n\n", .{}); + tl.format("Current backend: {s}", .{@tagName(dvui.backend.kind)}, .{}); + if (dvui.backend.kind == .web) { + tl.format(" : {s}", .{if (dvui.backend.wasm.wasm_about_webgl2() == 1) "webgl2" else "webgl (no mipmaps)"}, .{}); + } + + tl.deinit(); + + var tl2 = dvui.textLayout(@src(), .{}, .{ .expand = .horizontal }); + + tl2.addText( + \\DVUI + \\- paints the entire window + \\- can show floating windows and dialogs + \\- rest of the window is a scroll area + , .{}); + tl2.addText("\n\n", .{}); + tl2.addText("Framerate is variable and adjusts as needed for input events and animations.", .{}); + tl2.addText("\n\n", .{}); + tl2.addText("Framerate is capped by vsync.", .{}); + tl2.addText("\n\n", .{}); + tl2.addText("Cursor is always being set by dvui.", .{}); + tl2.addText("\n\n", .{}); + + if (dvui.useFreeType) { + tl2.addText("Fonts are being rendered by FreeType 2.", .{}); + } else { + tl2.addText("Fonts are being rendered by stb_truetype.", .{}); + } + tl2.deinit(); + + const label = if (dvui.Examples.show_demo_window) "Hide Demo Window" else "Show Demo Window"; + if (dvui.button(@src(), label, .{}, .{ .tag = "show-demo-btn" })) { + dvui.Examples.show_demo_window = !dvui.Examples.show_demo_window; + } + + if (dvui.button(@src(), "Debug Window", .{}, .{})) { + dvui.toggleDebugWindow(); + } + + { + var hbox = dvui.box(@src(), .{ .dir = .horizontal }, .{}); + defer hbox.deinit(); + dvui.label(@src(), "Pinch Zoom or Scale", .{}, .{}); + if (dvui.buttonIcon(@src(), "plus", dvui.entypo.plus, .{}, .{}, .{})) { + dvui.currentWindow().content_scale *= 1.1; + } + + if (dvui.buttonIcon(@src(), "minus", dvui.entypo.minus, .{}, .{}, .{})) { + dvui.currentWindow().content_scale /= 1.1; + } + + if (dvui.currentWindow().content_scale != state.orig_content_scale) { + if (dvui.button(@src(), "Reset Scale", .{}, .{})) { + dvui.currentWindow().content_scale = state.orig_content_scale; + } + } + } + + dvui.Examples.demo(); + + from_microzig_menu(); + + for (state.regz_windows.values()) |regz_window| + try regz_window.show(); + + return .ok; +} + +fn open_register_schema_submenu(m: *dvui.MenuWidget) !void { + if (dvui.menuItemLabel(@src(), "Open Register Schema...", .{ .submenu = true }, .{ .expand = .horizontal })) |r| { + var fw = dvui.floatingMenu(@src(), .{ .from = r }, .{}); + defer fw.deinit(); + + if (dvui.menuItemLabel(@src(), "From File", .{}, .{ .expand = .horizontal }) != null) { + m.close(); + const filename = dvui.dialogNativeFileOpen(dvui.currentWindow().arena(), .{ + .title = "dvui native file open", + .filters = &.{ "*.svd", "*.atdf" }, + .filter_description = "images", + }) catch |err| blk: { + dvui.log.debug("Could not open file dialog, got {any}", .{err}); + break :blk null; + }; + if (filename) |f| blk: { + if (state.regz_windows.contains(f)) { + dvui.dialog(@src(), .{}, .{ .modal = false, .title = "Error", .ok_label = "Done", .message = "File is already currently open in a Regz window" }); + } else { + const extension = std.fs.path.extension(f); + const format: regz.Database.Format = if (std.mem.eql(u8, ".svd", extension)) + .svd + else if (std.mem.eql(u8, ".atdf", extension)) + .atdf + else { + const message = try std.fmt.allocPrint(arena.allocator(), "Extension not recognized: {s}", .{extension}); + dvui.dialog(@src(), .{}, .{ .modal = false, .title = "Error", .ok_label = "Done", .message = message }); + break :blk; + }; + + const path = try gpa.dupe(u8, f); + const new_window = try RegzWindow.create(gpa, format, path); + errdefer new_window.destroy(); + + try state.regz_windows.put(gpa, f, new_window); + } + } + } + + if (dvui.menuItemLabel(@src(), "From MicroZig", .{}, .{ .expand = .horizontal }) != null) { + m.close(); + + state.show_from_microzig_window = true; + } + + if (dvui.menuItemLabel(@src(), "Search Chips", .{}, .{ .expand = .horizontal }) != null) {} + + if (dvui.menuItemLabel(@src(), "Search Boards", .{}, .{ .expand = .horizontal }) != null) {} + + if (dvui.menuItemLabel(@src(), "Close Menu", .{}, .{ .expand = .horizontal }) != null) { + fw.close(); + } + } +} + +var highlight_style: dvui.GridWidget.CellStyle.HoveredRow = .{ + .cell_opts = .{ + .color_fill_hover = .gray, + .background = true, + .border = .{ + .y = 0, + .h = 0, + .x = 0, + .w = 1, + }, + }, +}; + +const header_style: dvui.GridWidget.CellStyle = .{ + .cell_opts = .{ + .border = .{ + .y = 0, + .h = 1, + .x = 0, + .w = 0, + }, + }, +}; + +fn from_microzig_menu() void { + if (!state.show_from_microzig_window) + return; + + // TODO: handle no schema usages + + var float = dvui.floatingWindow(@src(), .{ .open_flag = &state.show_from_microzig_window }, .{ + .min_size_content = .{ .w = 400, .h = 400 }, + .tag = "from_microzig_window", + }); + defer float.deinit(); + + float.dragAreaSet(dvui.windowHeader("Open From MicroZig", "", &state.show_from_microzig_window)); + + var grid = dvui.grid(@src(), .numCols(3), .{ .scroll_opts = .{ .horizontal_bar = .auto } }, .{ .expand = .both, .background = true }); + defer grid.deinit(); + + const row_clicked: ?usize = blk: { + for (dvui.events()) |*e| { + if (!dvui.eventMatchSimple(e, grid.data())) continue; + if (e.evt != .mouse) continue; + const me = e.evt.mouse; + if (me.action != .press) continue; + if (grid.pointToCell(me.p)) |cell| { + if (cell.col_num > 0) break :blk cell.row_num; + } + } + break :blk null; + }; + + if (row_clicked) |row_num| { + std.log.info("clicked row: {}", .{row_num}); + const register_schema = state.register_schema_usages.?.value[row_num]; + const build_root = switch (register_schema.location) { + inline else => |location| location.build_root, + }; + const sub_path = switch (register_schema.location) { + inline else => |location| location.sub_path, + }; + const path = std.fs.path.join(gpa, &.{ build_root, sub_path }) catch unreachable; + + const format: regz.Database.Format = switch (register_schema.format) { + .svd => .svd, + .atdf => .atdf, + .embassy => .embassy, + }; + + const new_window = RegzWindow.create(gpa, format, path) catch unreachable; + + state.regz_windows.put(gpa, path, new_window) catch { + new_window.destroy(); + unreachable; + }; + } + + dvui.gridHeading(@src(), grid, 0, "Port", .fixed, header_style); + dvui.gridHeading(@src(), grid, 1, "Package", .fixed, header_style); + dvui.gridHeading(@src(), grid, 2, "Location", .fixed, header_style); + + highlight_style.processEvents(grid); + + if (state.register_schema_usages) |rsus| { + for (rsus.value, 0..) |rsu, row_num| { + var cell_num: dvui.GridWidget.Cell = .colRow(0, row_num); + switch (rsu.location) { + .src_path => |src| { + { + defer cell_num.col_num += 1; + var cell = grid.bodyCell(@src(), cell_num, highlight_style.cellOptions(cell_num)); + defer cell.deinit(); + + dvui.labelNoFmt(@src(), src.port_name, .{}, .{}); + } + { + defer cell_num.col_num += 1; + var cell = grid.bodyCell(@src(), cell_num, highlight_style.cellOptions(cell_num)); + defer cell.deinit(); + + dvui.labelNoFmt(@src(), "", .{}, .{}); + } + { + defer cell_num.col_num += 1; + var cell = grid.bodyCell(@src(), cell_num, highlight_style.cellOptions(cell_num)); + defer cell.deinit(); + + dvui.labelNoFmt(@src(), src.sub_path, .{}, .{}); + } + }, + .dependency => |dep| { + { + defer cell_num.col_num += 1; + var cell = grid.bodyCell(@src(), cell_num, highlight_style.cellOptions(cell_num)); + defer cell.deinit(); + + dvui.labelNoFmt(@src(), dep.port_name, .{}, .{}); + } + { + defer cell_num.col_num += 1; + var cell = grid.bodyCell(@src(), cell_num, highlight_style.cellOptions(cell_num)); + defer cell.deinit(); + + dvui.labelNoFmt(@src(), dep.dep_name, .{}, .{}); + } + { + defer cell_num.col_num += 1; + var cell = grid.bodyCell(@src(), cell_num, highlight_style.cellOptions(cell_num)); + defer cell.deinit(); + + dvui.labelNoFmt(@src(), dep.sub_path, .{}, .{}); + } + }, + } + } + } +} diff --git a/tools/regz-wizard/src/zig-favicon.png b/tools/sorcerer/src/zig-favicon.png similarity index 100% rename from tools/regz-wizard/src/zig-favicon.png rename to tools/sorcerer/src/zig-favicon.png From 673e7bcd73ec2238884e90192a40d08add81b710 Mon Sep 17 00:00:00 2001 From: Matt Knight Date: Sat, 25 Oct 2025 11:17:39 -0700 Subject: [PATCH 2/4] Update CI --- .../{tools-regz-wizard.yml => tools-sorcerer.yaml} | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) rename .github/workflows/{tools-regz-wizard.yml => tools-sorcerer.yaml} (58%) diff --git a/.github/workflows/tools-regz-wizard.yml b/.github/workflows/tools-sorcerer.yaml similarity index 58% rename from .github/workflows/tools-regz-wizard.yml rename to .github/workflows/tools-sorcerer.yaml index 6597dab9b..7287f96c9 100644 --- a/.github/workflows/tools-regz-wizard.yml +++ b/.github/workflows/tools-sorcerer.yaml @@ -1,4 +1,4 @@ -name: tools/regz-wizard +name: tools/sorcerer on: push: branches: [main] @@ -7,16 +7,11 @@ on: jobs: build: - if: false runs-on: ${{ matrix.os }} strategy: matrix: - os: [ - ubuntu-latest, - #windows-latest, # Windows is having a hard time fetching raygui package reliably - macos-latest - ] + os: [ubuntu-latest, windows-latest, macos-latest] steps: - name: Checkout uses: actions/checkout@v4 @@ -27,9 +22,9 @@ jobs: version: 0.15.1 - name: Build - working-directory: tools/regz-wizard + working-directory: tools/sorcerer run: zig build - name: Run Test Suite - working-directory: tools/regz-wizard + working-directory: tools/sorcerer run: zig build test From d808909fc9cd3e83748bb98ef2f52dac64c2cbb9 Mon Sep 17 00:00:00 2001 From: Matt Knight Date: Sat, 25 Oct 2025 11:39:39 -0700 Subject: [PATCH 3/4] Install SDL --- .github/workflows/tools-sorcerer.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/tools-sorcerer.yaml b/.github/workflows/tools-sorcerer.yaml index 7287f96c9..7ae0ca720 100644 --- a/.github/workflows/tools-sorcerer.yaml +++ b/.github/workflows/tools-sorcerer.yaml @@ -21,6 +21,11 @@ jobs: with: version: 0.15.1 + - name: Setup SDL3 + uses: libsdl-org/setup-sdl@main + with: + version: sdl3-latest + - name: Build working-directory: tools/sorcerer run: zig build From a7691d56c012f32e7cb5ebf27b55fc6093eaa827 Mon Sep 17 00:00:00 2001 From: Matt Knight Date: Sun, 26 Oct 2025 16:20:39 -0700 Subject: [PATCH 4/4] Drop CI, depend on local Sorcerer testing for now. --- .github/workflows/tools-sorcerer.yaml | 35 --------------------------- 1 file changed, 35 deletions(-) delete mode 100644 .github/workflows/tools-sorcerer.yaml diff --git a/.github/workflows/tools-sorcerer.yaml b/.github/workflows/tools-sorcerer.yaml deleted file mode 100644 index 7ae0ca720..000000000 --- a/.github/workflows/tools-sorcerer.yaml +++ /dev/null @@ -1,35 +0,0 @@ -name: tools/sorcerer -on: - push: - branches: [main] - pull_request: - branches: [main] - -jobs: - build: - runs-on: ${{ matrix.os }} - - strategy: - matrix: - os: [ubuntu-latest, windows-latest, macos-latest] - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup Zig - uses: mlugg/setup-zig@v2 - with: - version: 0.15.1 - - - name: Setup SDL3 - uses: libsdl-org/setup-sdl@main - with: - version: sdl3-latest - - - name: Build - working-directory: tools/sorcerer - run: zig build - - - name: Run Test Suite - working-directory: tools/sorcerer - run: zig build test