287 lines
9.0 KiB
Zig
287 lines
9.0 KiB
Zig
const builtin = @import("builtin");
|
|
const std = @import("std");
|
|
const Io = std.Io;
|
|
const Allocator = std.mem.Allocator;
|
|
const log = std.log.scoped(.platform);
|
|
const assert = std.debug.assert;
|
|
|
|
const sokol = @import("sokol");
|
|
const sapp = sokol.app;
|
|
const sg = sokol.gfx;
|
|
const sglue = sokol.glue;
|
|
const sgl = sokol.gl;
|
|
const simgui = sokol.imgui;
|
|
|
|
const Math = @import("./math.zig");
|
|
const Vec2 = Math.Vec2;
|
|
|
|
const tracy = @import("tracy");
|
|
|
|
const ig = @import("cimgui");
|
|
|
|
const Color = @import("./color.zig");
|
|
|
|
fn PlatformType(App: type) type {
|
|
_ = App; // autofix
|
|
return struct {
|
|
const Self = @This();
|
|
|
|
pass_action: sg.PassAction = .{},
|
|
cli_args: []const [:0]const u8,
|
|
|
|
show_first_window: bool = true,
|
|
|
|
fn sokolInit(user_data: ?*anyopaque) callconv(.c) void {
|
|
const self: *Self = @ptrCast(@alignCast(user_data));
|
|
sg.setup(.{
|
|
.environment = sglue.environment(),
|
|
.logger = .{ .func = sokolLogCallback },
|
|
});
|
|
|
|
simgui.setup(.{
|
|
.logger = .{ .func = sokolLogCallback },
|
|
});
|
|
|
|
if (@hasDecl(ig, "ImGuiConfigFlags_DockingEnable")) {
|
|
ig.igGetIO().*.ConfigFlags |= ig.ImGuiConfigFlags_DockingEnable;
|
|
}
|
|
ig.igGetIO().*.ConfigFlags |= ig.ImGuiConfigFlags_ViewportsEnable;
|
|
|
|
self.pass_action.colors[0] = .{
|
|
.load_action = .CLEAR,
|
|
.clear_value = toSokolColor(.black)
|
|
};
|
|
}
|
|
|
|
fn sokolFrame(user_data: ?*anyopaque) callconv(.c) void {
|
|
const self: *Self = @ptrCast(@alignCast(user_data));
|
|
|
|
tracy.frameMark();
|
|
|
|
simgui.newFrame(.{
|
|
.width = sapp.width(),
|
|
.height = sapp.height(),
|
|
.delta_time = sapp.frameDuration(),
|
|
.dpi_scale = sapp.dpiScale(),
|
|
});
|
|
|
|
//=== UI CODE STARTS HERE
|
|
{
|
|
const viewport = ig.igGetMainViewport();
|
|
ig.igSetNextWindowPos(viewport[0].WorkPos, ig.ImGuiCond_Always);
|
|
ig.igSetNextWindowSize(viewport[0].WorkSize, ig.ImGuiCond_Always);
|
|
ig.igSetNextWindowViewport(viewport[0].ID);
|
|
|
|
const window_flags =
|
|
ig.ImGuiWindowFlags_NoDocking |
|
|
ig.ImGuiWindowFlags_NoTitleBar |
|
|
ig.ImGuiWindowFlags_NoCollapse |
|
|
ig.ImGuiWindowFlags_NoResize |
|
|
ig.ImGuiWindowFlags_NoMove |
|
|
ig.ImGuiWindowFlags_NoBringToFrontOnFocus |
|
|
ig.ImGuiWindowFlags_NoNavFocus;
|
|
|
|
ig.igPushStyleVar(ig.ImGuiStyleVar_WindowRounding, 0.0);
|
|
ig.igPushStyleVar(ig.ImGuiStyleVar_WindowBorderSize, 0.0);
|
|
ig.igPushStyleVarImVec2(ig.ImGuiStyleVar_WindowPadding, .{ .x = 0, .y = 0 });
|
|
_ = ig.igBegin("DockSpace", null, window_flags);
|
|
ig.igPopStyleVarEx(3);
|
|
|
|
const dockspace_id = ig.igGetID("MyDockSpace");
|
|
_ = ig.igDockSpaceEx(dockspace_id, ig.ImVec2{ .x = 0, .y = 0 }, ig.ImGuiDockNodeFlags_PassthruCentralNode, null);
|
|
|
|
ig.igSetNextWindowPos(.{ .x = 10, .y = 30 }, ig.ImGuiCond_Once);
|
|
ig.igSetNextWindowSize(.{ .x = 400, .y = 100 }, ig.ImGuiCond_Once);
|
|
if (ig.igBegin("Hello Dear ImGui!", &self.show_first_window, ig.ImGuiWindowFlags_None)) {
|
|
_ = ig.igColorEdit3("Background", &self.pass_action.colors[0].clear_value.r, ig.ImGuiColorEditFlags_None);
|
|
_ = ig.igText("Dear ImGui Version: %s", ig.IMGUI_VERSION);
|
|
}
|
|
ig.igEnd();
|
|
|
|
ig.igEnd();
|
|
}
|
|
|
|
// TODO:
|
|
// if (ig.igBeginMainMenuBar()) {
|
|
// ig.igEndMainMenuBar();
|
|
// }
|
|
//=== UI CODE ENDS HERE
|
|
|
|
// var frame = Platform.Frame{
|
|
// .platform = &self.exposed,
|
|
// .input = .{},
|
|
// .output = .{},
|
|
// };
|
|
// if (self.frame(&frame)) {
|
|
// // ?
|
|
// } else |e| {
|
|
// log.err("frame() failed: {}", .{e});
|
|
// if (@errorReturnTrace()) |trace| {
|
|
// std.debug.dumpErrorReturnTrace(trace);
|
|
// }
|
|
// self.quit();
|
|
// }
|
|
|
|
// var pass_action: sg.PassAction = .{};
|
|
// pass_action.colors[0] = .{
|
|
// .load_action = .CLEAR,
|
|
// .clear_value = toSokolColor(.black)
|
|
// };
|
|
|
|
sg.beginPass(.{ .action = self.pass_action, .swapchain = sglue.swapchain() });
|
|
simgui.render();
|
|
sg.endPass();
|
|
sg.commit();
|
|
}
|
|
|
|
fn sokolEvent(ev: [*c]const sapp.Event, user_data: ?*anyopaque) callconv(.c) void {
|
|
const self: *Self = @ptrCast(@alignCast(user_data));
|
|
_ = self; // autofix
|
|
|
|
// forward input events to sokol-imgui
|
|
_ = simgui.handleEvent(ev.*);
|
|
}
|
|
|
|
fn sokolCleanup(user_data: ?*anyopaque) callconv(.c) void {
|
|
const self: *Self = @ptrCast(@alignCast(user_data));
|
|
_ = self; // autofix
|
|
// self.deinit(&self.exposed);
|
|
simgui.shutdown();
|
|
sg.shutdown();
|
|
}
|
|
};
|
|
}
|
|
|
|
pub const RunOptions = struct {
|
|
init: std.process.Init,
|
|
|
|
window_title: [*c]const u8 = null,
|
|
width: i32 = 800,
|
|
height: i32 = 600,
|
|
};
|
|
|
|
pub fn run(App: type, opts: RunOptions) void {
|
|
const arena = opts.init.arena.allocator();
|
|
const gpa = opts.init.gpa;
|
|
_ = gpa; // autofix
|
|
|
|
const Platform = PlatformType(App);
|
|
const plt = arena.create(Platform) catch @panic("OOM");
|
|
plt.* = Platform{
|
|
.cli_args = opts.init.minimal.args.toSlice(arena) catch @panic("OOM")
|
|
};
|
|
|
|
// var init = Platform.Init{
|
|
// .cli_args = self.cli_args
|
|
// };
|
|
// if (self.init(&self.exposed, &init)) |maybe_error_code| {
|
|
// if (maybe_error_code) |error_code| {
|
|
// std.process.exit(error_code);
|
|
// }
|
|
// } else |e| {
|
|
// log.err("init() failed: {}", .{e});
|
|
// if (@errorReturnTrace()) |trace| {
|
|
// std.debug.dumpErrorReturnTrace(trace);
|
|
// }
|
|
// std.process.exit(1);
|
|
// }
|
|
|
|
if (builtin.os.tag == .linux) {
|
|
var sa: std.posix.Sigaction = .{
|
|
.handler = .{ .handler = posixSignalHandler },
|
|
.mask = std.posix.sigemptyset(),
|
|
.flags = std.posix.SA.RESTART,
|
|
};
|
|
std.posix.sigaction(std.posix.SIG.INT, &sa, null);
|
|
}
|
|
|
|
var icon: sapp.IconDesc = .{};
|
|
icon.sokol_default = true;
|
|
// TODO: Don't hard code icon path, allow changing through options
|
|
// var icon_data = try STBImage.load(@embedFile("../assets/icon.png"));
|
|
// defer icon_data.deinit();
|
|
// TODO:
|
|
// icon.images[0] = .{
|
|
// .width = @intCast(icon_data.width),
|
|
// .height = @intCast(icon_data.height),
|
|
// .pixels = .{
|
|
// .ptr = icon_data.rgba8_pixels,
|
|
// .size = icon_data.width * icon_data.height * 4
|
|
// }
|
|
// };
|
|
|
|
tracy.setThreadName("Main");
|
|
|
|
sapp.run(.{
|
|
.init_userdata_cb = Platform.sokolInit,
|
|
.frame_userdata_cb = Platform.sokolFrame,
|
|
.cleanup_userdata_cb = Platform.sokolCleanup,
|
|
.event_userdata_cb = Platform.sokolEvent,
|
|
.user_data = plt,
|
|
.window_title = opts.window_title,
|
|
.width = opts.width,
|
|
.height = opts.height,
|
|
.icon = icon,
|
|
.logger = .{ .func = sokolLogCallback },
|
|
});
|
|
}
|
|
|
|
fn posixSignalHandler(sig: std.posix.SIG) callconv(.c) void {
|
|
_ = sig;
|
|
sapp.requestQuit();
|
|
}
|
|
|
|
fn toSokolColor(color: Color) sokol.gfx.Color {
|
|
// TODO: Assert and do cast
|
|
return sokol.gfx.Color{
|
|
.r = color.r,
|
|
.g = color.g,
|
|
.b = color.b,
|
|
.a = color.a,
|
|
};
|
|
}
|
|
|
|
fn sokolLogFmt(log_level: u32, comptime format: []const u8, args: anytype) void {
|
|
if (log_level == 0) {
|
|
log.err(format, args);
|
|
} else if (log_level == 1) {
|
|
log.err(format, args);
|
|
} else if (log_level == 2) {
|
|
log.warn(format, args);
|
|
} else {
|
|
log.info(format, args);
|
|
}
|
|
}
|
|
|
|
fn cStrToZig(c_str: [*c]const u8) [:0]const u8 {
|
|
return std.mem.span(c_str);
|
|
}
|
|
|
|
fn sokolLogCallback(tag: [*c]const u8, log_level: u32, log_item: u32, message: [*c]const u8, line_nr: u32, filename: [*c]const u8, user_data: ?*anyopaque) callconv(.c) void {
|
|
_ = user_data;
|
|
|
|
if (filename != null) {
|
|
sokolLogFmt(
|
|
log_level,
|
|
"[{s}][id:{}] {s}:{}: {s}",
|
|
.{
|
|
cStrToZig(tag orelse "-"),
|
|
log_item,
|
|
std.fs.path.basename(cStrToZig(filename orelse "-")),
|
|
line_nr,
|
|
cStrToZig(message orelse "")
|
|
}
|
|
);
|
|
} else {
|
|
sokolLogFmt(
|
|
log_level,
|
|
"[{s}][id:{}] {s}",
|
|
.{
|
|
cStrToZig(tag orelse "-"),
|
|
log_item,
|
|
cStrToZig(message orelse "")
|
|
}
|
|
);
|
|
}
|
|
}
|