Skip to content

Commit

Permalink
wasm: Add support for extra JS functions
Browse files Browse the repository at this point in the history
This allows to extend the capabilities of capy.js with access to other DOM APIs
which can be used by external libraries
  • Loading branch information
zenith391 committed Oct 26, 2023
1 parent 5f12ecb commit c9e5ff8
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 9 deletions.
31 changes: 24 additions & 7 deletions build_capy.zig
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub const CapyBuildOptions = struct {
// TODO: disable android build if password is not set
// TODO: use optional
android: AndroidOptions = .{ .password = "foo", .package_name = "org.capyui.example" },
wasm: WasmOptions = .{},
args: ?[]const []const u8 = &.{},

pub const AndroidOptions = struct {
Expand All @@ -20,15 +21,21 @@ pub const CapyBuildOptions = struct {
password: []const u8,
};

pub const WasmOptions = struct {
extras_js_file: ?[]const u8 = null,
debug_requests: bool = true,
};

pub const LinuxOptions = struct {};
};

/// Step used to run a web server for WebAssembly apps
const WebServerStep = struct {
step: std.build.Step,
exe: *std.build.CompileStep,
options: CapyBuildOptions.WasmOptions,

pub fn create(owner: *std.build.Builder, exe: *std.build.LibExeObjStep) *WebServerStep {
pub fn create(owner: *std.build.Builder, exe: *std.build.LibExeObjStep, options: CapyBuildOptions.WasmOptions) *WebServerStep {
const self = owner.allocator.create(WebServerStep) catch unreachable;
self.* = .{
.step = std.build.Step.init(.{
Expand All @@ -38,6 +45,7 @@ const WebServerStep = struct {
.makeFn = WebServerStep.make,
}),
.exe = exe,
.options = options,
};
return self;
}
Expand Down Expand Up @@ -69,7 +77,8 @@ const WebServerStep = struct {

fn handler(self: *WebServerStep, build: *std.Build, res: *Server.Response) !void {
const allocator = build.allocator;
const build_root = build.build_root.path orelse unreachable;
const prefix = comptime std.fs.path.dirname(@src().file).? ++ std.fs.path.sep_str;

while (true) {
defer _ = res.reset();
try res.wait();
Expand All @@ -82,20 +91,27 @@ const WebServerStep = struct {
var file_path: []const u8 = "";
var content_type: []const u8 = "text/html";
if (std.mem.eql(u8, path, "/")) {
file_path = try std.fs.path.join(req_allocator, &.{ build_root, "src/backends/wasm/index.html" });
file_path = try std.fs.path.join(req_allocator, &.{ prefix, "src/backends/wasm/index.html" });
content_type = "text/html";
} else if (std.mem.eql(u8, path, "/capy.js")) {
file_path = try std.fs.path.join(req_allocator, &.{ build_root, "src/backends/wasm/capy.js" });
file_path = try std.fs.path.join(req_allocator, &.{ prefix, "src/backends/wasm/capy.js" });
content_type = "application/javascript";
} else if (std.mem.eql(u8, path, "/capy-worker.js")) {
file_path = try std.fs.path.join(req_allocator, &.{ build_root, "src/backends/wasm/capy-worker.js" });
file_path = try std.fs.path.join(req_allocator, &.{ prefix, "src/backends/wasm/capy-worker.js" });
content_type = "application/javascript";
} else if (std.mem.eql(u8, path, "/zig-app.wasm")) {
file_path = self.exe.getOutputSource().getPath2(build, &self.step);
content_type = "application/wasm";
} else if (std.mem.eql(u8, path, "/extras.js")) {
if (self.options.extras_js_file) |extras_path| {
file_path = extras_path;
content_type = "application/javascript";
}
}

std.log.info("{s}", .{path});
if (self.options.debug_requests) {
std.log.debug("{s} -> {s}", .{ path, file_path });
}
const file: ?std.fs.File = std.fs.cwd().openFile(file_path, .{ .mode = .read_only }) catch |err| blk: {
switch (err) {
error.FileNotFound => break :blk null,
Expand All @@ -107,6 +123,7 @@ const WebServerStep = struct {
defer f.close();
break :blk try f.readToEndAlloc(req_allocator, std.math.maxInt(usize));
} else {
res.status = .not_found;
break :blk "404 Not Found";
}
};
Expand Down Expand Up @@ -301,7 +318,7 @@ pub fn install(step: *std.Build.CompileStep, options: CapyBuildOptions) !*std.Bu
step.export_symbol_names = &.{"_start"};
step.import_memory = true;

const serve = WebServerStep.create(b, step);
const serve = WebServerStep.create(b, step, options.wasm);
const install_step = b.addInstallArtifact(step, .{});
serve.step.dependOn(&install_step.step);
return &serve.step;
Expand Down
13 changes: 13 additions & 0 deletions src/backends/wasm/capy-worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,20 @@ const env = {
},
};

async function loadExtras() {
const obj = await import("./extras.js");
for (const key in obj.envWorker) {
env[key] = obj.envWorker[key];
}
}

(async function() {
try {
await loadExtras();
} catch (e) {
console.debug("No extras.js (worker)");
}

const importObject = {
env: env,
};
Expand Down
18 changes: 16 additions & 2 deletions src/backends/wasm/capy.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ function readString(addr, len) {

return utf8Decoder.decode(view.slice(addr, addr + len));
}
const env = {

let env = {
jsPrint: function(arg, len) {
console.log(readString(arg, len));
},
Expand Down Expand Up @@ -171,6 +172,7 @@ const env = {
domObjects[root].style.width = "100%";
domObjects[root].style.height = "100%";
rootElementId = root;
window.onresize(); // call resize handler atleast once, to setup layout
},
setText: function(element, text) {
const elem = domObjects[element];
Expand Down Expand Up @@ -370,11 +372,24 @@ const env = {
},
};

async function loadExtras() {
const obj = await import("./extras.js");
for (const key in obj.env) {
env[key] = obj.env[key];
}
}

(async function() {
if (!window.Worker) {
alert("Capy requires Web Workers until Zig supports async");
}

try {
await loadExtras();
} catch (e) {
console.debug("No extras.js");
}

const wasmWorker = new Worker("capy-worker.js");
wasmWorker.postMessage("test");
wasmWorker.onmessage = (e) => {
Expand Down Expand Up @@ -440,5 +455,4 @@ const env = {
window.onresize = function() {
pushEvent({ type: 0, target: rootElementId });
};
window.onresize(); // call resize handler atleast once, to setup layout
})();

0 comments on commit c9e5ff8

Please sign in to comment.