From ce490e21ea49a30e3bcde1c7df48c3d3e6f17bad Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 31 Dec 2022 08:53:11 -0800 Subject: [PATCH] can specify a wasm target in build --- build.zig | 6 ++++++ example/app.ts | 1 + src/App.zig | 29 +++++++++++++++++++++++++++++ src/DevMode.zig | 3 ++- src/apprt.zig | 5 ++++- src/apprt/browser.zig | 2 ++ src/main_wasm.zig | 1 + src/os/wasm.zig | 11 +++++++++++ src/os/wasm/target.zig | 6 ++++++ src/renderer.zig | 6 +++++- src/renderer/WebGL.zig | 2 ++ 11 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 src/apprt/browser.zig create mode 100644 src/os/wasm/target.zig create mode 100644 src/renderer/WebGL.zig diff --git a/build.zig b/build.zig index a9794aa13..81f47f88d 100644 --- a/build.zig +++ b/build.zig @@ -19,6 +19,7 @@ const utf8proc = @import("pkg/utf8proc/build.zig"); const zlib = @import("pkg/zlib/build.zig"); const tracylib = @import("pkg/tracy/build.zig"); const system_sdk = @import("vendor/mach/libs/glfw/system_sdk.zig"); +const WasmTarget = @import("src/os/wasm/target.zig").Target; // Build options, see the build options help for more info. var tracy: bool = false; @@ -134,6 +135,11 @@ pub fn build(b: *std.build.Builder) !void { const wasm_shared: bool = true; exe_options.addOption(bool, "wasm_shared", wasm_shared); + // We want to support alternate wasm targets in the future (i.e. + // server side) so we have this now although its hardcoded. + const wasm_specific_target: WasmTarget = .browser; + exe_options.addOption(WasmTarget, "wasm_target", wasm_specific_target); + const wasm = b.addSharedLibrary( "ghostty-wasm", "src/main_wasm.zig", diff --git a/example/app.ts b/example/app.ts index 55ffb8279..7508e95a3 100644 --- a/example/app.ts +++ b/example/app.ts @@ -77,6 +77,7 @@ fetch(url.href).then(response => const config_str = makeStr("font-family = monospace"); config_load_string(config, config_str.ptr, config_str.len); config_finalize(config); + free(config_str.ptr); // Create our atlas // const atlas = atlas_new(512, 0 /* greyscale */); diff --git a/src/App.zig b/src/App.zig index b640d64c9..e1e01bd00 100644 --- a/src/App.zig +++ b/src/App.zig @@ -266,3 +266,32 @@ pub const Message = union(enum) { font_size: ?font.face.DesiredSize = null, }; }; + +// Wasm API. +pub const Wasm = if (!builtin.target.isWasm()) struct {} else struct { + const wasm = @import("os/wasm.zig"); + const alloc = wasm.alloc; + + // export fn app_new(config: *Config) ?*App { + // return app_new_(config) catch |err| { + // log.err("error initializing app err={}", .{err}); + // return null; + // }; + // } + // + // fn app_new_(config: *Config) !*App { + // const app = try App.create(alloc, config); + // errdefer app.destroy(); + // + // const result = try alloc.create(App); + // result.* = app; + // return result; + // } + // + // export fn app_free(ptr: ?*App) void { + // if (ptr) |v| { + // v.destroy(); + // alloc.destroy(v); + // } + // } +}; diff --git a/src/DevMode.zig b/src/DevMode.zig index 4e0fc0317..2df187b82 100644 --- a/src/DevMode.zig +++ b/src/DevMode.zig @@ -3,6 +3,7 @@ const DevMode = @This(); const std = @import("std"); +const builtin = @import("builtin"); const imgui = @import("imgui"); const Allocator = std.mem.Allocator; const assert = std.debug.assert; @@ -14,7 +15,7 @@ const Config = @import("config.zig").Config; /// If this is false, the rest of the terminal will be compiled without /// dev mode support at all. -pub const enabled = true; +pub const enabled = !builtin.target.isWasm(); /// The global DevMode instance that can be used app-wide. Assume all functions /// are NOT thread-safe unless otherwise noted. diff --git a/src/apprt.zig b/src/apprt.zig index 35efc0d85..4246d5a3c 100644 --- a/src/apprt.zig +++ b/src/apprt.zig @@ -12,13 +12,16 @@ const builtin = @import("builtin"); pub usingnamespace @import("apprt/structs.zig"); pub const glfw = @import("apprt/glfw.zig"); +pub const browser = @import("apprt/browser.zig"); pub const Window = @import("apprt/Window.zig"); /// The implementation to use for the app runtime. This is comptime chosen /// so that every build has exactly one application runtime implementation. /// Note: it is very rare to use Runtime directly; most usage will use /// Window or something. -pub const runtime = switch (builtin.os.tag) { +pub const runtime = if (builtin.target.isWasm()) + browser +else switch (builtin.os.tag) { else => glfw, }; diff --git a/src/apprt/browser.zig b/src/apprt/browser.zig new file mode 100644 index 000000000..d60776a6a --- /dev/null +++ b/src/apprt/browser.zig @@ -0,0 +1,2 @@ +pub const App = struct {}; +pub const Window = struct {}; diff --git a/src/main_wasm.zig b/src/main_wasm.zig index 46c77082b..cacd4f196 100644 --- a/src/main_wasm.zig +++ b/src/main_wasm.zig @@ -8,6 +8,7 @@ pub usingnamespace @import("os/wasm/log.zig"); pub usingnamespace @import("font/main.zig"); pub usingnamespace @import("terminal/main.zig"); pub usingnamespace @import("config.zig").Wasm; +pub usingnamespace @import("App.zig").Wasm; // Set our log level. We try to get as much logging as possible but in // ReleaseSmall mode where we're optimizing for space, we elevate the diff --git a/src/os/wasm.zig b/src/os/wasm.zig index 1be8bb011..0c933471d 100644 --- a/src/os/wasm.zig +++ b/src/os/wasm.zig @@ -2,6 +2,7 @@ const std = @import("std"); const builtin = @import("builtin"); const options = @import("build_options"); +const Target = @import("wasm/target.zig").Target; comptime { if (!builtin.target.isWasm()) { @@ -13,6 +14,16 @@ comptime { /// in JS will be backed by a SharedArrayBuffer and some behaviors change. pub const shared_mem = options.wasm_shared; +/// Our specific target platform. +pub const target: ?Target = if (!builtin.target.isWasm()) null else target: { + const result = @intToEnum(Target, @enumToInt(options.wasm_target)); + // This maybe isn't necessary but I don't know if enums without a specific + // tag type and value are guaranteed to be the same between build.zig + // compilation and our own source compilation so I have this just in case. + std.debug.assert(std.mem.eql(u8, @tagName(result), @tagName(options.wasm_target))); + break :target result; +}; + /// The allocator to use in wasm environments. /// /// The return values of this should NOT be sent to the host environment diff --git a/src/os/wasm/target.zig b/src/os/wasm/target.zig new file mode 100644 index 000000000..bd897cc21 --- /dev/null +++ b/src/os/wasm/target.zig @@ -0,0 +1,6 @@ +/// The wasm target platform. This is used to toggle certain features +/// on and off since the standard triple target is often not specific +/// enough (i.e. we can't tell wasm32-freestanding is for browser or not). +pub const Target = enum { + browser, +}; diff --git a/src/renderer.zig b/src/renderer.zig index f52cff302..721cdc224 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -14,13 +14,17 @@ pub usingnamespace @import("renderer/message.zig"); pub usingnamespace @import("renderer/size.zig"); pub const Metal = @import("renderer/Metal.zig"); pub const OpenGL = @import("renderer/OpenGL.zig"); +pub const WebGL = @import("renderer/WebGL.zig"); pub const Options = @import("renderer/Options.zig"); pub const Thread = @import("renderer/Thread.zig"); pub const State = @import("renderer/State.zig"); /// The implementation to use for the renderer. This is comptime chosen /// so that every build has exactly one renderer implementation. -pub const Renderer = switch (builtin.os.tag) { +const wasm = @import("os/wasm.zig"); +pub const Renderer = if (wasm.target) |target| switch (target) { + .browser => WebGL, +} else switch (builtin.os.tag) { .macos => Metal, else => OpenGL, }; diff --git a/src/renderer/WebGL.zig b/src/renderer/WebGL.zig new file mode 100644 index 000000000..655f7a503 --- /dev/null +++ b/src/renderer/WebGL.zig @@ -0,0 +1,2 @@ +//! Renderer implementation for WebGL in the browser. +pub const WebGL = @This();