sokoban-v2/src/platform.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 "")
}
);
}
}