Compare commits

...

3 Commits

Author SHA1 Message Date
099c3c0607 add wasm support 2026-09-23 02:57:50 +03:00
e4000646ad update to work with zig 0.17 2026-09-23 00:45:23 +03:00
4764f153e6 rewrite build.zig to not use makeFn 2026-09-22 23:16:25 +03:00
20 changed files with 474 additions and 391 deletions

BIN
assets/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 454 B

325
build.zig
View File

@ -6,9 +6,57 @@ pub fn build(b: *std.Build) !void {
const target = b.standardTargetOptions(.{}); const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{}); const optimize = b.standardOptimizeOption(.{});
const dep_stb = b.dependency("stb", .{});
const ctx = Context.init(b, dep_stb.module("stb_image"));
var run_cmd_step: *std.Build.Step = undefined;
switch (try buildProject(ctx, target, optimize)) {
.native => |exe| {
b.installArtifact(exe);
const run_cmd = b.addRunArtifact(exe);
run_cmd.addPassthruArgs();
run_cmd_step = &run_cmd.step;
{
const exe_tests = b.addTest(.{ .root_module = exe.root_module, });
const run_exe_tests = b.addRunArtifact(exe_tests);
const test_step = b.step("test", "Run tests");
test_step.dependOn(&run_exe_tests.step);
}
},
.wasm => |build_step| {
b.getInstallStep().dependOn(build_step);
const dep_sokol = b.dependency("sokol", .{});
const dep_emsdk = dep_sokol.builder.dependency("emsdk", .{});
const emrun_step = sokol.emRunStep(b, .{ .name = "index", .emsdk = dep_emsdk });
emrun_step.step.dependOn(build_step);
run_cmd_step = &emrun_step.step;
// add an emsdk install step
const emsdk_install_step = sokol.emSdkInstallStep(b, dep_sokol.builder.dependency("emsdk", .{}), .{});
b.step("install-emsdk", "Install Emscripten SDK in zig-pkg").dependOn(emsdk_install_step);
}
}
const run_step = b.step("run", "Run game");
run_step.dependOn(run_cmd_step);
}
const ProjectResult = union(enum) {
native: *std.Build.Step.Compile,
wasm: *std.Build.Step,
};
fn buildProject(ctx: Context, target: Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) !ProjectResult {
const b = ctx.b;
const name = "sokol_template_v2"; const name = "sokol_template_v2";
const assets_dir = "assets"; const assets_dir = b.path("assets");
const asset_files = [_][]const u8{ const asset_files = [_][]const u8{
"sokoban/sokoban_tilesheet.png", "sokoban/sokoban_tilesheet.png",
"sokoban/sokoban_tilesheet@2.png", "sokoban/sokoban_tilesheet@2.png",
@ -18,19 +66,23 @@ pub fn build(b: *std.Build) !void {
"impact-sounds/footstep_concrete_000.ogg" "impact-sounds/footstep_concrete_000.ogg"
}; };
const asset_dir_realpath = try b.build_root.join(b.graph.arena, &.{ assets_dir });
const is_wasm = target.result.cpu.arch.isWasm(); const is_wasm = target.result.cpu.arch.isWasm();
const is_debug = (optimize == .Debug); const is_debug = optimize == .debug;
const has_imgui = b.option(bool, "imgui", "ImGui integration") orelse is_debug; const has_imgui = b.option(bool, "imgui", "ImGui integration") orelse is_debug;
const assets_from_filesystem = b.option(bool, "assets-filesystem", "Allow reading assets from filesystem") orelse is_debug; var assets_from_filesystem = false;
if (!is_wasm) {
assets_from_filesystem = b.option(bool, "assets-filesystem", "Allow reading assets from filesystem") orelse is_debug;
}
var has_tracy = false; var has_tracy = false;
if (!is_wasm) { if (!is_wasm) {
has_tracy = b.option(bool, "tracy", "Tracy integration") orelse is_debug; has_tracy = b.option(bool, "tracy", "Tracy integration") orelse is_debug;
} }
const win32_has_console = null;
const win32_icon_png: ?Build.LazyPath = b.path("assets/icon.png");
const exe_mod = b.createModule(.{ const exe_mod = b.createModule(.{
.root_source_file = b.path("src/main.zig"), .root_source_file = b.path("src/main.zig"),
.target = target, .target = target,
@ -52,10 +104,12 @@ pub fn build(b: *std.Build) !void {
{ {
const build_options = b.addOptions(); const build_options = b.addOptions();
build_options.addOption(bool, "has_imgui", has_imgui); build_options.addOption(bool, "has_imgui", has_imgui);
build_options.addOption(?[]const u8, "assets_dir", if (assets_from_filesystem) asset_dir_realpath else null); if (assets_from_filesystem) {
build_options.addOptionPathDirectory("assets_dir", assets_dir);
}
platform_mod.addOptions("build_options", build_options); platform_mod.addOptions("build_options", build_options);
platform_mod.addImport("embedded_assets", try createEmbeddedAssets(b, assets_dir, &asset_files)); platform_mod.addImport("embedded_assets", try createAssetsModule(b, assets_dir, &asset_files));
const math_mod = b.createModule(.{ const math_mod = b.createModule(.{
.root_source_file = b.path("src/platform/math.zig") .root_source_file = b.path("src/platform/math.zig")
@ -76,6 +130,7 @@ pub fn build(b: *std.Build) !void {
var slang = sokol.shdc.Slang{}; var slang = sokol.shdc.Slang{};
if (is_wasm) { if (is_wasm) {
slang.wgsl = true; slang.wgsl = true;
slang.glsl300es = true;
} else { } else {
slang.glsl410 = true; slang.glsl410 = true;
slang.hlsl4 = true; slang.hlsl4 = true;
@ -118,206 +173,121 @@ pub fn build(b: *std.Build) !void {
platform_mod.addImport("tracy", dep_tracy.module("tracy")); platform_mod.addImport("tracy", dep_tracy.module("tracy"));
} }
var run_cmd_step: *std.Build.Step = undefined;
if (is_wasm) { if (is_wasm) {
@panic("TODO: Test wasm support on 0.17"); return ProjectResult{
.wasm = buildWasm(b, .{
// const build_step = buildWasm(b, .{ .name = "index",
// .name = "index", .root_module = exe_mod,
// .root_module = exe_mod, .dep_sokol = dep_sokol,
// .dep_sokol = dep_sokol, })
// }); };
// b.getInstallStep().dependOn(build_step);
//
// const dep_emsdk = dep_sokol.builder.dependency("emsdk", .{});
// const emrun_step = sokol.emRunStep(b, .{ .name = "index", .emsdk = dep_emsdk });
// emrun_step.step.dependOn(build_step);
// run_cmd_step = &emrun_step.step;
// TODO: Create a zip archive of all of the files. Would be useful for easier itch.io upload
} else { } else {
const exe = buildNative(b, .{ const exe = b.addExecutable(.{
.name = name, .name = name,
.root_module = exe_mod .root_module = exe_mod,
}); });
b.installArtifact(exe);
{ if (target.result.os.tag == .windows) {
const run_cmd = b.addRunArtifact(exe); const has_console = win32_has_console orelse is_debug;
if (b.args) |args| { exe.subsystem = if (has_console) .console else .windows;
run_cmd.addArgs(args);
if (win32_icon_png) |icon_png| {
buildWin32ResourceFile(ctx, exe.root_module, .{
.icon_png = icon_png
});
} }
run_cmd_step = &run_cmd.step;
} }
{ return ProjectResult{
const exe_tests = b.addTest(.{ .root_module = exe_mod, }); .native = exe
const run_exe_tests = b.addRunArtifact(exe_tests); };
const test_step = b.step("test", "Run tests");
test_step.dependOn(&run_exe_tests.step);
}
} }
const run_step = b.step("run", "Run game");
run_step.dependOn(run_cmd_step);
} }
fn createEmbeddedAssets( fn createAssetsModule(
b: *std.Build, b: *std.Build,
assets_dir: []const u8, assets_dir: Build.LazyPath,
asset_files: []const []const u8 asset_files: []const []const u8
) !*std.Build.Module { ) !*std.Build.Module {
const write_files = b.addWriteFiles(); const write_files = b.addWriteFiles();
var embedded_assets: std.ArrayList(u8) = .empty; var mod_contents: std.ArrayList(u8) = .empty;
defer embedded_assets.deinit(b.allocator); defer mod_contents.deinit(b.allocator);
try embedded_assets.appendSlice(b.allocator, "pub const files = .{\n"); try mod_contents.appendSlice(b.allocator, "pub const files = .{\n");
const mod_embedded_assets = b.createModule(.{}); const mod = b.createModule(.{});
for (asset_files) |asset_file| { for (asset_files) |asset_file| {
mod_embedded_assets.addAnonymousImport(asset_file, .{ mod.addAnonymousImport(asset_file, .{
.root_source_file = b.path(b.pathJoin(&.{ assets_dir, asset_file })) .root_source_file = assets_dir.path(b, asset_file)
}); });
try embedded_assets.appendSlice(b.allocator, " .{ "); try mod_contents.appendSlice(b.allocator, " .{ ");
try embedded_assets.appendSlice(b.allocator, ".name = \""); try mod_contents.appendSlice(b.allocator, ".name = \"");
try embedded_assets.appendSlice(b.allocator, asset_file); try mod_contents.appendSlice(b.allocator, asset_file);
try embedded_assets.appendSlice(b.allocator, "\""); try mod_contents.appendSlice(b.allocator, "\"");
try embedded_assets.appendSlice(b.allocator, ", "); try mod_contents.appendSlice(b.allocator, ", ");
try embedded_assets.appendSlice(b.allocator, ".contents = @embedFile(\""); try mod_contents.appendSlice(b.allocator, ".contents = @embedFile(\"");
try embedded_assets.appendSlice(b.allocator, asset_file); try mod_contents.appendSlice(b.allocator, asset_file);
try embedded_assets.appendSlice(b.allocator, "\")"); try mod_contents.appendSlice(b.allocator, "\")");
try embedded_assets.appendSlice(b.allocator, " },\n"); try mod_contents.appendSlice(b.allocator, " },\n");
} }
try embedded_assets.appendSlice(b.allocator, "};\n"); try mod_contents.appendSlice(b.allocator, "};\n");
mod_embedded_assets.root_source_file = write_files.add("embedded_assets.zig", embedded_assets.items); mod.root_source_file = write_files.add("assets.zig", mod_contents.items);
return mod_embedded_assets; return mod;
} }
const BuildNativeOptions = struct { const Context = struct {
name: []const u8, b: *std.Build,
root_module: *std.Build.Module, png_to_icon: *Build.Step.Compile,
win32_has_console: ?bool = null, create_win32_resource: *Build.Step.Compile,
win32_png_icon: ?std.Build.LazyPath = null
pub fn init(b: *std.Build, stb_image: *Build.Module) Context {
return Context{
.b = b,
.png_to_icon = b.addExecutable(.{
.name = "png-to-icon",
.root_module = b.createModule(.{
.target = b.graph.host,
.root_source_file = b.path("scripts/png-to-icon.zig"),
.imports = &.{
.{ .name = "stb_image", .module = stb_image }
}
})
}),
.create_win32_resource = b.addExecutable(.{
.name = "create-win32-resource",
.root_module = b.createModule(.{
.target = b.graph.host,
.root_source_file = b.path("scripts/create-win32-resource.zig"),
})
})
};
}
}; };
fn buildNative( const Win32ResourceFileOptions = struct {
b: *std.Build, icon_png: Build.LazyPath
opts: BuildNativeOptions,
) *std.Build.Step.Compile {
const exe = b.addExecutable(.{
.name = opts.name,
.root_module = opts.root_module,
});
const target = opts.root_module.resolved_target.?;
if (target.result.os.tag == .windows) {
const optimize = opts.root_module.optimize.?;
const has_console = opts.win32_has_console orelse (optimize == .Debug);
exe.subsystem = if (has_console) .Console else .Windows;
if (opts.win32_png_icon) |win32_png_icon| {
const png_to_icon_tool = buildPngToIconTool(b, b.graph.host, .ReleaseSafe);
const png_to_icon_step = b.addRunArtifact(png_to_icon_tool);
png_to_icon_step.addFileArg(win32_png_icon);
const icon_file = png_to_icon_step.addOutputFileArg("icon.ico");
const add_icon_step = AddExecutableIcon.init(exe, icon_file);
exe.step.dependOn(&add_icon_step.step);
}
}
return exe;
}
fn buildPngToIconTool(
b: *std.Build,
target: std.Build.ResolvedTarget,
optimize: std.builtin.OptimizeMode,
) *std.Build.Step.Compile {
const mod = b.createModule(.{
.target = target,
.optimize = optimize,
.root_source_file = b.path("tools/png-to-icon.zig"),
});
const dep_stb = b.dependency("stb", .{});
mod.addImport("stb_image", dep_stb.module("stb_image"));
return b.addExecutable(.{
.name = "png-to-icon",
.root_module = mod,
});
}
const AddExecutableIcon = struct {
obj: *std.Build.Step.Compile,
step: std.Build.Step,
icon_file: std.Build.LazyPath,
resource_file: std.Build.LazyPath,
fn init(obj: *std.Build.Step.Compile, icon_file: std.Build.LazyPath) *AddExecutableIcon {
const b = obj.step.owner;
const self = b.allocator.create(AddExecutableIcon) catch @panic("OOM");
self.obj = obj;
self.step = std.Build.Step.init(.{
.id = .custom,
.name = "add executable icon",
.owner = b,
.makeFn = make
});
self.icon_file = icon_file;
icon_file.addStepDependencies(&self.step);
const write_files = b.addWriteFiles();
self.resource_file = write_files.add("resource-file.rc", "");
self.step.dependOn(&write_files.step);
self.obj.root_module.addWin32ResourceFile(.{
.file = self.resource_file,
});
return self;
}
fn make(step: *std.Build.Step, _: std.Build.Step.MakeOptions) !void {
const b = step.owner;
const self: *AddExecutableIcon = @fieldParentPtr("step", step);
const relative_icon_path = try std.fs.path.relative(
b.allocator,
"",
null,
self.resource_file.dirname().getPath(b),
self.icon_file.getPath(b)
);
std.mem.replaceScalar(u8, relative_icon_path, '\\', '/');
{
const cwd = std.Io.Dir.cwd();
const resource_file = try cwd.createFile(b.graph.io, self.resource_file.getPath(b), .{ });
defer resource_file.close(b.graph.io);
var buff: [4*4096]u8 = undefined;
var file_writer = resource_file.writer(b.graph.io, &buff);
const w = &file_writer.interface;
try w.writeAll("IDI_ICON ICON \"");
try w.writeAll(relative_icon_path);
try w.writeAll("\"");
}
}
}; };
fn buildWin32ResourceFile(ctx: Context, mod: *Build.Module, opts: Win32ResourceFileOptions) void {
const b = ctx.b;
const png_to_icon_step = b.addRunArtifact(ctx.png_to_icon);
png_to_icon_step.addFileArg(opts.icon_png);
const icon_file = png_to_icon_step.addOutputFileArg("icon.ico");
const resource_file_step = b.addRunArtifact(ctx.create_win32_resource);
resource_file_step.addFileArg(icon_file);
const resource_file = resource_file_step.addOutputFileArg("resource-file.rc");
mod.addWin32ResourceFile(.{ .file = resource_file });
}
const BuildWasmOptions = struct { const BuildWasmOptions = struct {
name: []const u8, name: []const u8,
root_module: *std.Build.Module, root_module: *std.Build.Module,
@ -354,8 +324,9 @@ fn buildWasm(b: *std.Build, opts: BuildWasmOptions) *std.Build.Step {
.emsdk = dep_emsdk, .emsdk = dep_emsdk,
.use_webgl2 = true, .use_webgl2 = true,
.use_emmalloc = true, .use_emmalloc = true,
.use_filesystem = false, .use_filesystem = true,
.shell_file_path = b.path("src/engine/shell.html"), .shell_file_path = b.path("src/platform/shell.html"),
.extra_args = &.{ "-sALLOW_MEMORY_GROWTH", "-sENVIRONMENT=web" }
}) catch unreachable; }) catch unreachable;
return &link_step.step; return &link_step.step;

View File

@ -4,12 +4,12 @@
.fingerprint = 0xf440b4e6e51f5206, // Changing this has security and trust implications. .fingerprint = 0xf440b4e6e51f5206, // Changing this has security and trust implications.
.dependencies = .{ .dependencies = .{
.sokol = .{ .sokol = .{
.url = "git+https://github.com/floooh/sokol-zig.git#ff40f04d4f51c73b2bf460d87fd2d7aa0dedc22f", .url = "git+https://github.com/floooh/sokol-zig.git#e371234960812882406a296c4f3154321a16ee4d",
.hash = "sokol-0.1.0-pb1HKygbQQCINV-qSe4SFm92QWaT_95VpWTJkwYqzOrX", .hash = "sokol-0.1.0-pb1HK0kUQABqwuelY7BmmQmAm-jLBZKJ7uyoxkdS49Ib",
}, },
.cimgui = .{ .cimgui = .{
.url = "git+https://github.com/floooh/dcimgui.git#e2f0e0d93adec02743c55940be23ffe286e857f7", .url = "git+https://github.com/floooh/dcimgui.git#ef68bcf1ea0a8218f4a2f24fb9414515ecbf5af4",
.hash = "cimgui-0.1.0-44ClkR8dmgDqqnfloyLE1bUAtl4zSKTT1wxaHSEC0ikV", .hash = "cimgui-0.1.0-44ClkVBEnABUEg0-NuUgCcvqhRLr9BCUWHFbuVYt9Nuv",
.lazy = true .lazy = true
}, },
.stb = .{ .stb = .{

View File

@ -2,36 +2,97 @@ const std = @import("std");
const Build = std.Build; const Build = std.Build;
const LazyPath = Build.LazyPath; const LazyPath = Build.LazyPath;
pub fn build(b: *std.Build) void { pub fn build(b: *std.Build) !void {
const target = b.standardTargetOptions(.{}); const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{}); const optimize = b.standardOptimizeOption(.{});
buildSTBModule(b, target, optimize, "stb_image", b.path("src/stb_image.zig"), b.path("src/stb_image_impl.c")); try buildSTBModule(b, target, optimize, .{
buildSTBModule(b, target, optimize, "stb_vorbis", b.path("src/stb_vorbis.zig"), b.path("src/stb_vorbis_impl.c")); .name = "stb_image",
buildSTBModule(b, target, optimize, "stb_rect_pack", b.path("src/stb_rect_pack.zig"), b.path("src/stb_rect_pack_impl.c")); .zig_file = b.path("src/stb_image.zig"),
buildSTBModule(b, target, optimize, "stb_truetype", b.path("src/stb_truetype.zig"), b.path("src/stb_truetype_impl.c")); .header_name = "stb_image.h",
.defines = &.{ "STBI_NO_STDIO" },
.impl_define = "STB_IMAGE_IMPLEMENTATION"
});
try buildSTBModule(b, target, optimize, .{
.name = "stb_vorbis",
.zig_file = b.path("src/stb_vorbis.zig"),
.header_name = "stb_vorbis.c",
.defines = &.{ "STB_VORBIS_NO_INTEGER_CONVERSION", "STB_VORBIS_NO_STDIO" },
.header_only_define = "STB_VORBIS_HEADER_ONLY"
});
try buildSTBModule(b, target, optimize, .{
.name = "stb_rect_pack",
.zig_file = b.path("src/stb_rect_pack.zig"),
.header_name = "stb_rect_pack.h",
.impl_define = "STB_RECT_PACK_IMPLEMENTATION"
});
try buildSTBModule(b, target, optimize, .{
.name = "stb_truetype",
.zig_file = b.path("src/stb_truetype.zig"),
.header_name = "stb_truetype.h",
// TODO: .defines = &.{ "STBTT_STATIC" },
.impl_define = "STB_TRUETYPE_IMPLEMENTATION"
});
} }
const STBModuleOptions = struct {
name: []const u8,
zig_file: LazyPath,
header_name: []const u8,
defines: []const []const u8 = &.{},
impl_define: ?[]const u8 = null,
header_only_define: ?[]const u8 = null,
};
fn buildSTBModule( fn buildSTBModule(
b: *std.Build, b: *std.Build,
target: Build.ResolvedTarget, target: Build.ResolvedTarget,
optimize: std.builtin.OptimizeMode, optimize: std.builtin.OptimizeMode,
name: []const u8, opts: STBModuleOptions
zig_file: LazyPath, ) !void {
impl_file: LazyPath
) void {
const stb_dependency = b.dependency("stb", .{}); const stb_dependency = b.dependency("stb", .{});
const mod = b.addModule(name, .{ const mod = b.addModule(opts.name, .{
.target = target, .target = target,
.optimize = optimize, .optimize = optimize,
.root_source_file = zig_file, .root_source_file = opts.zig_file,
.link_libc = true, .link_libc = true,
}); });
mod.addIncludePath(stb_dependency.path(".")); const write_file_step = b.addWriteFiles();
mod.addCSourceFile(.{
.file = impl_file, {
.flags = &.{} var translate_c_contents: std.ArrayList(u8) = .empty;
}); try translate_c_contents.appendSlice(b.graph.arena, "#include \"");
try translate_c_contents.appendSlice(b.graph.arena, opts.header_name);
try translate_c_contents.appendSlice(b.graph.arena, "\"");
const translate_c = b.addTranslateC(.{
.root_source_file = write_file_step.add("c.h", translate_c_contents.items),
.target = target,
.optimize = optimize,
});
translate_c.addIncludePath(stb_dependency.path("."));
for (opts.defines) |define| {
translate_c.defineCMacro(define, null);
}
if (opts.header_only_define) |header_only_define| {
translate_c.defineCMacro(header_only_define, null);
}
mod.addImport("c", translate_c.createModule());
}
{
mod.addCSourceFile(.{
.file = write_file_step.add("impl.c", b.fmt("#include \"{s}\"", .{opts.header_name})),
.flags = &.{}
});
mod.addIncludePath(stb_dependency.path("."));
for (opts.defines) |define| {
mod.addCMacro(define, "");
}
if (opts.impl_define) |impl_define| {
mod.addCMacro(impl_define, "");
}
}
} }

View File

@ -1,9 +1,6 @@
const std = @import("std"); const std = @import("std");
const c = @cImport({ const c = @import("c");
@cDefine("STBI_NO_STDIO", {});
@cInclude("stb_image.h");
});
const STBImage = @This(); const STBImage = @This();

View File

@ -1,3 +0,0 @@
#define STB_IMAGE_IMPLEMENTATION
#define STBI_NO_STDIO
#include "stb_image.h"

View File

@ -1,12 +1,10 @@
const std = @import("std"); const std = @import("std");
const c = @cImport({ const c = @import("c");
@cInclude("stb_rect_pack.h");
});
const STBRectPack = @This(); const STBRectPack = @This();
ctx: c.stbrp_context, ctx: c.stbrp_context = .{},
pub fn init(self: *STBRectPack, width: u32, height: u32, nodes: []Node) void { pub fn init(self: *STBRectPack, width: u32, height: u32, nodes: []Node) void {
c.stbrp_init_target(&self.ctx, @intCast(width), @intCast(height), nodes.ptr, @intCast(nodes.len)); c.stbrp_init_target(&self.ctx, @intCast(width), @intCast(height), nodes.ptr, @intCast(nodes.len));

View File

@ -1,2 +0,0 @@
#define STB_RECT_PACK_IMPLEMENTATION
#include "stb_rect_pack.h"

View File

@ -1,8 +1,6 @@
const std = @import("std"); const std = @import("std");
const c = @cImport({ const c = @import("c");
@cInclude("stb_truetype.h");
});
pub const Box = struct { pub const Box = struct {
x0: i32, x0: i32,

View File

@ -1,4 +0,0 @@
#define STB_TRUETYPE_IMPLEMENTATION
// TODO: #define STBTT_STATIC
#include "stb_truetype.h"

View File

@ -2,12 +2,7 @@ const std = @import("std");
const assert = std.debug.assert; const assert = std.debug.assert;
const log = std.log.scoped(.stb_vorbis); const log = std.log.scoped(.stb_vorbis);
const c = @cImport({ const c = @import("c");
@cDefine("STB_VORBIS_NO_INTEGER_CONVERSION", {});
@cDefine("STB_VORBIS_NO_STDIO", {});
@cDefine("STB_VORBIS_HEADER_ONLY", {});
@cInclude("stb_vorbis.c");
});
const STBVorbis = @This(); const STBVorbis = @This();

View File

@ -1,3 +0,0 @@
#define STB_VORBIS_NO_INTEGER_CONVERSION
#define STB_VORBIS_NO_STDIO
#include "stb_vorbis.c"

View File

@ -1,7 +1,7 @@
const std = @import("std"); const std = @import("std");
const digits2 = std.fmt.digits2; const digits2 = std.fmt.digits2;
pub fn build(b: *std.Build) void { pub fn build(b: *std.Build) !void {
const target = b.standardTargetOptions(.{}); const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{}); const optimize = b.standardOptimizeOption(.{});
@ -29,110 +29,113 @@ pub fn build(b: *std.Build) void {
const tracy_timer_fallback = b.option(bool, "tracy_timer_fallback", "Use lower resolution timers") orelse false; const tracy_timer_fallback = b.option(bool, "tracy_timer_fallback", "Use lower resolution timers") orelse false;
const shared = b.option(bool, "shared", "Build the tracy client as a shared libary") orelse false; const shared = b.option(bool, "shared", "Build the tracy client as a shared libary") orelse false;
const options = b.addOptions();
options.addOption(bool, "tracy_enable", tracy_enable);
options.addOption(bool, "tracy_on_demand", tracy_on_demand);
options.addOption(?u8, "tracy_callstack", tracy_callstack);
options.addOption(bool, "tracy_no_callstack", tracy_no_callstack);
options.addOption(bool, "tracy_no_callstack_inlines", tracy_no_callstack_inlines);
options.addOption(bool, "tracy_only_localhost", tracy_only_localhost);
options.addOption(bool, "tracy_no_broadcast", tracy_no_broadcast);
options.addOption(bool, "tracy_only_ipv4", tracy_only_ipv4);
options.addOption(bool, "tracy_no_code_transfer", tracy_no_code_transfer);
options.addOption(bool, "tracy_no_context_switch", tracy_no_context_switch);
options.addOption(bool, "tracy_no_exit", tracy_no_exit);
options.addOption(bool, "tracy_no_sampling", tracy_no_sampling);
options.addOption(bool, "tracy_no_verify", tracy_no_verify);
options.addOption(bool, "tracy_no_vsync_capture", tracy_no_vsync_capture);
options.addOption(bool, "tracy_no_frame_image", tracy_no_frame_image);
options.addOption(bool, "tracy_no_system_tracing", tracy_no_system_tracing);
options.addOption(bool, "tracy_delayed_init", tracy_delayed_init);
options.addOption(bool, "tracy_manual_lifetime", tracy_manual_lifetime);
options.addOption(bool, "tracy_fibers", tracy_fibers);
options.addOption(bool, "tracy_no_crash_handler", tracy_no_crash_handler);
options.addOption(bool, "tracy_timer_fallback", tracy_timer_fallback);
options.addOption(bool, "shared", shared);
const dep_src = b.dependency("src", .{}); const dep_src = b.dependency("src", .{});
const is_windows = target.result.os.tag == .windows;
const tracy_module = b.addModule("tracy", .{ const arena = b.graph.arena;
.root_source_file = b.path("src/root.zig"), var defines: std.ArrayList(CDefine) = .empty;
.target = target, inline for (.{
.optimize = optimize, .{ tracy_enable , "TRACY_ENABLE" , "1" },
}); .{ tracy_on_demand , "TRACY_ON_DEMAND" , "1" },
.{ tracy_no_callstack , "TRACY_NO_CALLSTACK" , "1" },
.{ tracy_no_callstack_inlines, "TRACY_NO_CALLSTACK_INLINES", "1" },
.{ tracy_only_localhost , "TRACY_ONLY_LOCALHOST" , "1" },
.{ tracy_no_broadcast , "TRACY_NO_BROADCAST" , "1" },
.{ tracy_only_ipv4 , "TRACY_ONLY_IPV4" , "1" },
.{ tracy_no_code_transfer , "TRACY_NO_CODE_TRANSFER" , "1" },
.{ tracy_no_context_switch , "TRACY_NO_CONTEXT_SWITCH" , "1" },
.{ tracy_no_exit , "TRACY_NO_EXIT" , "1" },
.{ tracy_no_sampling , "TRACY_NO_SAMPLING" , "1" },
.{ tracy_no_verify , "TRACY_NO_VERIFY" , "1" },
.{ tracy_no_vsync_capture , "TRACY_NO_VSYNC_CAPTURE" , "1" },
.{ tracy_no_frame_image , "TRACY_NO_FRAME_IMAGE" , "1" },
.{ tracy_no_system_tracing , "TRACY_NO_SYSTEM_TRACING" , "1" },
.{ tracy_delayed_init , "TRACY_DELAYED_INIT" , "1" },
.{ tracy_manual_lifetime , "TRACY_MANUAL_LIFETIME" , "1" },
.{ tracy_fibers , "TRACY_FIBERS" , "1" },
.{ tracy_no_crash_handler , "TRACY_NO_CRASH_HANDLER" , "1" },
.{ tracy_timer_fallback , "TRACY_TIMER_FALLBACK" , "1" },
}) |entry| {
const condition = entry[0];
const name = entry[1];
const value = entry[2];
if (condition) {
try defines.append(arena, .{ .name = name, .value = value });
}
}
if (tracy_callstack) |depth| {
try defines.append(arena, .{ .name = "TRACY_CALLSTACK", .value = "\"" ++ digits2(depth) ++ "\"" });
}
tracy_module.addImport("tracy-options", options.createModule()); {
tracy_module.addIncludePath(dep_src.path("./public")); const tracy_module = b.addModule("tracy", .{
.root_source_file = b.path("src/root.zig"),
const tracy_client = b.addLibrary(.{
.linkage = if (shared) .dynamic else .static,
.name = "tracy",
.root_module = b.createModule(.{
.target = target, .target = target,
.optimize = optimize, .optimize = optimize,
}), });
});
if (target.result.os.tag == .windows) { const options = b.addOptions();
tracy_client.root_module.linkSystemLibrary("dbghelp", .{}); options.addOption(bool, "tracy_enable", tracy_enable);
tracy_client.root_module.linkSystemLibrary("ws2_32", .{}); options.addOption(?u8, "tracy_callstack", tracy_callstack);
options.addOption(bool, "tracy_no_callstack", tracy_no_callstack);
options.addOption(bool, "tracy_fibers", tracy_fibers);
tracy_module.addImport("options", options.createModule());
const write_file_step = b.addWriteFiles();
const translate_c = b.addTranslateC(.{
.root_source_file = write_file_step.add("c.h", "#include <tracy/TracyC.h>"),
.target = target,
.optimize = optimize,
});
translate_c.addIncludePath(dep_src.path("./public"));
if (shared and is_windows) {
translate_c.defineCMacro("TRACY_IMPORTS", "1");
}
for (defines.items) |define| {
translate_c.defineCMacro(define.name, define.value);
}
tracy_module.addImport("c", translate_c.createModule());
} }
if (target.result.abi != .msvc) {
tracy_client.root_module.link_libcpp = true; {
} else { const mod = b.createModule(.{
tracy_client.root_module.link_libc = true; .target = target,
.optimize = optimize,
});
if (is_windows) {
mod.linkSystemLibrary("dbghelp", .{});
mod.linkSystemLibrary("ws2_32", .{});
}
if (target.result.abi != .msvc) {
mod.link_libcpp = true;
} else {
mod.link_libc = true;
}
mod.addCSourceFile(.{
.file = dep_src.path("./public/TracyClient.cpp"),
.flags = if (is_windows) &.{"-fms-extensions"} else &.{},
});
if (shared and is_windows) {
mod.addCMacro("TRACY_EXPORTS", "1");
}
for (defines.items) |define| {
mod.addCMacro(define.name, define.value);
}
const tracy_client = b.addLibrary(.{
.linkage = if (shared) .dynamic else .static,
.name = "tracy",
.root_module = mod
});
tracy_client.installHeadersDirectory(dep_src.path("public"), "", .{
.include_extensions = &.{".h", ".hpp"},
});
b.installArtifact(tracy_client);
} }
tracy_client.root_module.addCSourceFile(.{
.file = dep_src.path("./public/TracyClient.cpp"),
.flags = if (target.result.os.tag == .windows) &.{"-fms-extensions"} else &.{},
});
tracy_client.installHeadersDirectory(dep_src.path("public"), "", .{
.include_extensions = &.{".h", ".hpp"},
});
if (tracy_enable)
tracy_client.root_module.addCMacro("TRACY_ENABLE", "1");
if (tracy_on_demand)
tracy_client.root_module.addCMacro("TRACY_ON_DEMAND", "1");
if (tracy_callstack) |depth| {
tracy_client.root_module.addCMacro("TRACY_CALLSTACK", "\"" ++ digits2(depth) ++ "\"");
}
if (tracy_no_callstack)
tracy_client.root_module.addCMacro("TRACY_NO_CALLSTACK", "1");
if (tracy_no_callstack_inlines)
tracy_client.root_module.addCMacro("TRACY_NO_CALLSTACK_INLINES", "1");
if (tracy_only_localhost)
tracy_client.root_module.addCMacro("TRACY_ONLY_LOCALHOST", "1");
if (tracy_no_broadcast)
tracy_client.root_module.addCMacro("TRACY_NO_BROADCAST", "1");
if (tracy_only_ipv4)
tracy_client.root_module.addCMacro("TRACY_ONLY_IPV4", "1");
if (tracy_no_code_transfer)
tracy_client.root_module.addCMacro("TRACY_NO_CODE_TRANSFER", "1");
if (tracy_no_context_switch)
tracy_client.root_module.addCMacro("TRACY_NO_CONTEXT_SWITCH", "1");
if (tracy_no_exit)
tracy_client.root_module.addCMacro("TRACY_NO_EXIT", "1");
if (tracy_no_sampling)
tracy_client.root_module.addCMacro("TRACY_NO_SAMPLING", "1");
if (tracy_no_verify)
tracy_client.root_module.addCMacro("TRACY_NO_VERIFY", "1");
if (tracy_no_vsync_capture)
tracy_client.root_module.addCMacro("TRACY_NO_VSYNC_CAPTURE", "1");
if (tracy_no_frame_image)
tracy_client.root_module.addCMacro("TRACY_NO_FRAME_IMAGE", "1");
if (tracy_no_system_tracing)
tracy_client.root_module.addCMacro("TRACY_NO_SYSTEM_TRACING", "1");
if (tracy_delayed_init)
tracy_client.root_module.addCMacro("TRACY_DELAYED_INIT", "1");
if (tracy_manual_lifetime)
tracy_client.root_module.addCMacro("TRACY_MANUAL_LIFETIME", "1");
if (tracy_fibers)
tracy_client.root_module.addCMacro("TRACY_FIBERS", "1");
if (tracy_no_crash_handler)
tracy_client.root_module.addCMacro("TRACY_NO_CRASH_HANDLER", "1");
if (tracy_timer_fallback)
tracy_client.root_module.addCMacro("TRACY_TIMER_FALLBACK", "1");
if (shared and target.result.os.tag == .windows)
tracy_client.root_module.addCMacro("TRACY_EXPORTS", "1");
b.installArtifact(tracy_client);
} }
const CDefine = struct {
name: []const u8,
value: []const u8,
};

View File

@ -2,34 +2,9 @@ const std = @import("std");
const digits2 = std.fmt.digits2; const digits2 = std.fmt.digits2;
const builtin = @import("builtin"); const builtin = @import("builtin");
const options = @import("tracy-options"); const options = @import("options");
const c = @cImport({ const c = @import("c");
if (options.tracy_enable) @cDefine("TRACY_ENABLE", {});
if (options.tracy_on_demand) @cDefine("TRACY_ON_DEMAND", {});
if (options.tracy_callstack) |depth| @cDefine("TRACY_CALLSTACK", "\"" ++ digits2(depth) ++ "\"");
if (options.tracy_no_callstack) @cDefine("TRACY_NO_CALLSTACK", {});
if (options.tracy_no_callstack_inlines) @cDefine("TRACY_NO_CALLSTACK_INLINES", {});
if (options.tracy_only_localhost) @cDefine("TRACY_ONLY_LOCALHOST", {});
if (options.tracy_no_broadcast) @cDefine("TRACY_NO_BROADCAST", {});
if (options.tracy_only_ipv4) @cDefine("TRACY_ONLY_IPV4", {});
if (options.tracy_no_code_transfer) @cDefine("TRACY_NO_CODE_TRANSFER", {});
if (options.tracy_no_context_switch) @cDefine("TRACY_NO_CONTEXT_SWITCH", {});
if (options.tracy_no_exit) @cDefine("TRACY_NO_EXIT", {});
if (options.tracy_no_sampling) @cDefine("TRACY_NO_SAMPLING", {});
if (options.tracy_no_verify) @cDefine("TRACY_NO_VERIFY", {});
if (options.tracy_no_vsync_capture) @cDefine("TRACY_NO_VSYNC_CAPTURE", {});
if (options.tracy_no_frame_image) @cDefine("TRACY_NO_FRAME_IMAGE", {});
if (options.tracy_no_system_tracing) @cDefine("TRACY_NO_SYSTEM_TRACING", {});
if (options.tracy_delayed_init) @cDefine("TRACY_DELAYED_INIT", {});
if (options.tracy_manual_lifetime) @cDefine("TRACY_MANUAL_LIFETIME", {});
if (options.tracy_fibers) @cDefine("TRACY_FIBERS", {});
if (options.tracy_no_crash_handler) @cDefine("TRACY_NO_CRASH_HANDLER", {});
if (options.tracy_timer_fallback) @cDefine("TRACY_TIMER_FALLBACK", {});
if (options.shared and builtin.os.tag == .windows) @cDefine("TRACY_IMPORTS", {});
@cInclude("tracy/TracyC.h");
});
pub inline fn setThreadName(comptime name: [:0]const u8) void { pub inline fn setThreadName(comptime name: [:0]const u8) void {
if (!options.tracy_enable) return; if (!options.tracy_enable) return;

View File

@ -0,0 +1,31 @@
const std = @import("std");
const Io = std.Io;
pub fn main(init: std.process.Init) !void {
const io = init.io;
const arena = init.arena.allocator();
const args = try init.minimal.args.toSlice(arena);
if (args.len != 3) {
std.debug.print("Usage: ./create-win32-resource-file <icon> <output>", .{});
std.process.exit(1);
}
const icon_file_path = args[1];
const output_file_path = args[2];
const cwd = Io.Dir.cwd();
const output_file = try cwd.createFile(io, output_file_path, .{});
defer output_file.close(io);
var buff: [4*4096]u8 = undefined;
var file_writer = output_file.writer(io, &buff);
const writer = &file_writer.interface;
try writer.writeAll("IDI_ICON ICON \"");
try writer.writeAll(icon_file_path);
try writer.writeAll("\"");
try writer.flush();
}

View File

@ -1,27 +1,26 @@
const std = @import("std"); const std = @import("std");
const Io = std.Io;
const STBImage = @import("stb_image"); const STBImage = @import("stb_image");
// https://en.wikipedia.org/wiki/ICO_(file_format)#Icon_file_structure // https://en.wikipedia.org/wiki/ICO_(file_format)#Icon_file_structure
pub fn main() !void { pub fn main(init: std.process.Init) !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; const io = init.io;
const allocator = gpa.allocator(); const gpa = init.gpa;
defer _ = gpa.deinit(); const arena = init.arena.allocator();
const args = try init.minimal.args.toSlice(arena);
const args = try std.process.argsAlloc(allocator);
defer std.process.argsFree(allocator, args);
if (args.len != 3) { if (args.len != 3) {
std.debug.print("Usage: ./png-to-icon <png-file> <output-ico>", .{}); std.debug.print("Usage: ./png-to-icon <png-file> <output-ico>", .{});
std.process.exit(1); std.process.exit(1);
} }
const cwd = std.fs.cwd(); const cwd = Io.Dir.cwd();
const input_png_path = args[1]; const input_png_path = args[1];
const output_ico_path = args[2]; const output_ico_path = args[2];
const input_png_data = try cwd.readFileAlloc(allocator, input_png_path, 1024 * 1024 * 5); const input_png_data = try cwd.readFileAlloc(io, input_png_path, gpa, .unlimited);
defer allocator.free(input_png_data); defer gpa.free(input_png_data);
const png_image = try STBImage.load(input_png_data); const png_image = try STBImage.load(input_png_data);
defer png_image.deinit(); defer png_image.deinit();
@ -29,29 +28,30 @@ pub fn main() !void {
std.debug.assert(png_image.width > 0 and png_image.width <= 256); std.debug.assert(png_image.width > 0 and png_image.width <= 256);
std.debug.assert(png_image.height > 0 and png_image.height <= 256); std.debug.assert(png_image.height > 0 and png_image.height <= 256);
const output_ico_file = try std.fs.cwd().createFile(output_ico_path, .{ }); const output_ico_file = try cwd.createFile(io, output_ico_path, .{});
defer output_ico_file.close(); defer output_ico_file.close(io);
var buffer: [4096 * 4]u8 = undefined; var buffer: [4096 * 4]u8 = undefined;
var writer = output_ico_file.writer(&buffer); var file_writer = output_ico_file.writer(io, &buffer);
const writer = &file_writer.interface;
// ICONDIR structure // ICONDIR structure
try writer.interface.writeInt(u16, 0, .little); // Must always be zero try writer.writeInt(u16, 0, .little); // Must always be zero
try writer.interface.writeInt(u16, 1, .little); // Image type. 1 for .ICO try writer.writeInt(u16, 1, .little); // Image type. 1 for .ICO
try writer.interface.writeInt(u16, 1, .little); // Number of images try writer.writeInt(u16, 1, .little); // Number of images
// ICONDIRENTRY structure // ICONDIRENTRY structure
try writer.interface.writeInt(u8, @truncate(png_image.width), .little); // Image width try writer.writeInt(u8, @truncate(png_image.width), .little); // Image width
try writer.interface.writeInt(u8, @truncate(png_image.height), .little); // Image height try writer.writeInt(u8, @truncate(png_image.height), .little); // Image height
try writer.interface.writeInt(u8, 0, .little); // Number of colors in color pallete. 0 means that color pallete is not used try writer.writeInt(u8, 0, .little); // Number of colors in color pallete. 0 means that color pallete is not used
try writer.interface.writeInt(u8, 0, .little); // Must always be zero try writer.writeInt(u8, 0, .little); // Must always be zero
try writer.interface.writeInt(u16, 0, .little); // Color plane try writer.writeInt(u16, 0, .little); // Color plane
try writer.interface.writeInt(u16, 32, .little); // Bits per pixel try writer.writeInt(u16, 32, .little); // Bits per pixel
try writer.interface.writeInt(u32, @intCast(input_png_data.len), .little); // Image size in bytes try writer.writeInt(u32, @intCast(input_png_data.len), .little); // Image size in bytes
try writer.interface.writeInt(u32, 22, .little); // Offset to image data from the start try writer.writeInt(u32, 22, .little); // Offset to image data from the start
// PNG image data // PNG image data
try writer.interface.writeAll(input_png_data); try writer.writeAll(input_png_data);
try writer.interface.flush(); try writer.flush();
} }

View File

@ -164,6 +164,7 @@ pub fn init(io: Io, gpa: std.mem.Allocator, opts: InitOptions) !void {
.buffer_frames = @intCast(opts.buffer_frames), .buffer_frames = @intCast(opts.buffer_frames),
.sample_rate = g_sample_rate .sample_rate = g_sample_rate
}); });
assert(saudio.isvalid());
if (opts.track_elapsed_time) { if (opts.track_elapsed_time) {
const measurements_per_second: usize = @intCast(@divFloor(std.time.ns_per_s, getMaxAudioThreadDuration().nanoseconds)); const measurements_per_second: usize = @intCast(@divFloor(std.time.ns_per_s, getMaxAudioThreadDuration().nanoseconds));

View File

@ -91,7 +91,7 @@ pub fn init(gpa: std.mem.Allocator, logger: sg.Logger) !void {
.primitive_type = .TRIANGLES, .primitive_type = .TRIANGLES,
.index_type = .UINT16, .index_type = .UINT16,
.colors = init: { .colors = init: {
var s: [8]sg.ColorTargetState = [_]sg.ColorTargetState{.{}} ** 8; var s: [8]sg.ColorTargetState = @splat(.{});
s[0] = .{ s[0] = .{
.blend = .{ .blend = .{
.enabled = true, .enabled = true,
@ -130,7 +130,8 @@ pub fn init(gpa: std.mem.Allocator, logger: sg.Logger) !void {
}); });
const index_count = max_quads * 6; const index_count = max_quads * 6;
var indices: [index_count]u16 = undefined; var indices: []u16 = try gpa.alloc(u16, index_count);
defer gpa.free(indices);
for (0..max_quads) |i| { for (0..max_quads) |i| {
indices[6*i + 0] = @intCast(4*i + 0); indices[6*i + 0] = @intCast(4*i + 0);
indices[6*i + 1] = @intCast(4*i + 1); indices[6*i + 1] = @intCast(4*i + 1);
@ -142,7 +143,7 @@ pub fn init(gpa: std.mem.Allocator, logger: sg.Logger) !void {
} }
bindings.index_buffer = sg.makeBuffer(.{ bindings.index_buffer = sg.makeBuffer(.{
.data = sg.asRange(&indices), .data = sg.asRange(indices),
.usage = .{ .index_buffer = true, }, .usage = .{ .index_buffer = true, },
.label = "indices" .label = "indices"
}); });
@ -997,7 +998,7 @@ fn repackSpritesheet(id: Spritesheet.Id) void {
}; };
} }
spritesheet.packer = undefined; spritesheet.packer = .{};
const packer = &spritesheet.packer.?; const packer = &spritesheet.packer.?;
for (&texture_sizes) |texture_size| { for (&texture_sizes) |texture_size| {
@ -1270,8 +1271,8 @@ pub const Image = struct {
pub fn maxPixelSize() usize { pub fn maxPixelSize() usize {
var result: usize = 0; var result: usize = 0;
for (@typeInfo(Image.Format).@"enum".fields) |field| { for (@typeInfo(Image.Format).@"enum".field_values) |field_value| {
const pixel_size = Image.Format.pixelSize(@enumFromInt(field.value)); const pixel_size = Image.Format.pixelSize(@enumFromInt(field_value));
result = @max(result, pixel_size); result = @max(result, pixel_size);
} }
return result; return result;

View File

@ -36,7 +36,7 @@ fn PlatformType(App: type) type {
const Self = @This(); const Self = @This();
gpa: Allocator, gpa: Allocator,
arena: *std.heap.ArenaAllocator, arena: std.heap.ArenaAllocator,
io: Io, io: Io,
cli_args: []const [:0]const u8, cli_args: []const [:0]const u8,
@ -406,6 +406,13 @@ fn PlatformType(App: type) type {
}); });
}, },
.KEY_DOWN => { .KEY_DOWN => {
if (builtin.cpu.arch.isWasm()) {
// Pass through developer tools keybind
if (e.key_code == .I and e.modifiers & sapp.modifier_shift != 0 and e.modifiers & sapp.modifier_ctrl != 0) {
return false;
}
}
self.last_key_press_is_repeat = e.key_repeat; self.last_key_press_is_repeat = e.key_repeat;
if (!e.key_repeat) { if (!e.key_repeat) {
self.pushEvent(.{ self.pushEvent(.{
@ -478,6 +485,8 @@ fn PlatformType(App: type) type {
ImGUI.deinit(); ImGUI.deinit();
Gfx.deinit(); Gfx.deinit();
Audio.deinit(self.gpa); Audio.deinit(self.gpa);
self.arena.deinit();
} }
}; };
} }
@ -609,26 +618,27 @@ pub const Frame = struct {
}; };
pub fn run(App: type, opts: RunOptions) void { pub fn run(App: type, opts: RunOptions) void {
const arena = opts.init.arena.allocator();
const gpa = opts.init.gpa; const gpa = opts.init.gpa;
const io = opts.init.io; const io = opts.init.io;
var assets_dir: ?Io.Dir = null; var assets_dir: ?Io.Dir = null;
if (build_options.assets_dir) |assets_dir_path| { if (@hasDecl(build_options, "assets_dir")) {
const cwd = Io.Dir.cwd(); const cwd = Io.Dir.cwd();
assets_dir = cwd.openDir(io, assets_dir_path, .{}) catch |e| blk: { assets_dir = cwd.openDir(io, build_options.assets_dir, .{}) catch |e| blk: {
log.err("Failed to open assets directory: {}", .{e}); log.err("Failed to open assets directory: {}", .{e});
break :blk null; break :blk null;
}; };
} }
const PlatformApp = PlatformType(App); const PlatformApp = PlatformType(App);
const plt = arena.create(PlatformApp) catch @panic("OOM");
var arena = std.heap.ArenaAllocator.init(gpa);
const plt = arena.allocator().create(PlatformApp) catch @panic("OOM");
plt.* = PlatformApp{ plt.* = PlatformApp{
.gpa = gpa, .gpa = gpa,
.arena = opts.init.arena, .arena = undefined,
.io = io, .io = io,
.cli_args = opts.init.minimal.args.toSlice(arena) catch @panic("OOM"), .cli_args = opts.init.minimal.args.toSlice(arena.allocator()) catch @panic("OOM"),
.app = undefined, .app = undefined,
.input = undefined, .input = undefined,
.events_buffer = undefined, .events_buffer = undefined,
@ -643,6 +653,7 @@ pub fn run(App: type, opts: RunOptions) void {
.assets = .init(gpa, io, assets_dir), .assets = .init(gpa, io, assets_dir),
.last_mouse_position = null .last_mouse_position = null
}; };
plt.arena = arena;
inline for (EmbeddedAssets.files) |file| { inline for (EmbeddedAssets.files) |file| {
plt.assets.insertStaticFile(file.name, file.contents) catch @panic("OOM"); plt.assets.insertStaticFile(file.name, file.contents) catch @panic("OOM");

53
src/platform/shell.html Normal file
View File

@ -0,0 +1,53 @@
<!doctype html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"/>
<title>Sokol</title>
<style>
body { margin: 0; background-color: black }
.game {
position: absolute;
top: 0px;
left: 0px;
margin: 0px;
border: 0;
width: 100%;
height: 100%;
overflow: hidden;
display: block;
image-rendering: optimizeSpeed;
image-rendering: -moz-crisp-edges;
image-rendering: -o-crisp-edges;
image-rendering: -webkit-optimize-contrast;
image-rendering: optimize-contrast;
image-rendering: crisp-edges;
image-rendering: pixelated;
-ms-interpolation-mode: nearest-neighbor;
}
</style>
</head>
<body>
<canvas class="game" id="canvas" oncontextmenu="event.preventDefault()"></canvas>
<script type='text/javascript'>
var Module = {
preRun: [],
print: (function() {
return function(text) {
text = Array.prototype.slice.call(arguments).join(' ');
console.log(text);
};
})(),
printErr: function(text) {
text = Array.prototype.slice.call(arguments).join(' ');
console.error(text);
},
};
window.onerror = function() {
console.log("onerror: " + event.message);
};
window.addEventListener("click", () => window.focus())
</script>
{{{ SCRIPT }}}
</body>
</html>