Skip to content

Commit

Permalink
work on wasm without async
Browse files Browse the repository at this point in the history
It might be a dead end as it just maxes out the CPU at 100% (so a big no-no for mobile) and is hard to work around
  • Loading branch information
zenith391 committed Oct 15, 2023
1 parent 679eeeb commit fe92930
Show file tree
Hide file tree
Showing 5 changed files with 466 additions and 63 deletions.
30 changes: 25 additions & 5 deletions build_capy.zig
Original file line number Diff line number Diff line change
Expand Up @@ -87,24 +87,42 @@ const WebServerStep = struct {
} 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" });
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" });
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";
}

var file = try std.fs.openFileAbsolute(file_path, .{ .mode = .read_only });
defer file.close();
const content = try file.readToEndAlloc(req_allocator, std.math.maxInt(usize));
std.log.info("{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,
else => return err,
}
};
const content = blk: {
if (file) |f| {
defer f.close();
break :blk try f.readToEndAlloc(req_allocator, std.math.maxInt(usize));
} else {
break :blk "404 Not Found";
}
};

res.transfer_encoding = .{ .content_length = content.len };
try res.headers.append("Connection", res.request.headers.getFirstValue("Connection") orelse "close");
// try res.headers.append("Connection", res.request.headers.getFirstValue("Connection") orelse "close");
try res.headers.append("Connection", "close");
try res.headers.append("Content-Type", content_type);
try res.headers.append("Cross-Origin-Opener-Policy", "same-origin");
try res.headers.append("Cross-Origin-Embedder-Policy", "require-corp");

try res.do();
try res.writer().writeAll(content);
try res.finish();

if (res.connection.closing) break;
if (res.connection.closing or true) break;
}
}
};
Expand Down Expand Up @@ -280,6 +298,8 @@ pub fn install(step: *std.Build.CompileStep, options: CapyBuildOptions) !*std.Bu
if (step.optimize == .ReleaseSmall) {
step.strip = true;
}
step.export_symbol_names = &.{"_start"};
step.import_memory = true;

const serve = WebServerStep.create(b, step);
const install_step = b.addInstallArtifact(step, .{});
Expand Down
142 changes: 99 additions & 43 deletions src/backends/wasm/backend.zig
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,16 @@ pub fn init() !void {
var globalWindow: ?*Window = null;

pub const Window = struct {
peer: *GuiWidget,
child: ?PeerType = null,
scale: f32 = 1.0,

pub usingnamespace Events(Window);

pub fn create() !Window {
return Window{};
return Window{
.peer = try GuiWidget.init(Window, lasting_allocator, "div", "window"),
};
}

pub fn show(self: *Window) void {
Expand Down Expand Up @@ -119,6 +124,7 @@ pub fn Events(comptime T: type) type {
},
.KeyType => self.peer.user.keyTypeHandler = cb,
.KeyPress => self.peer.user.keyPressHandler = cb,
.PropertyChange => self.peer.user.propertyChangeHandler = cb,
}
}

Expand Down Expand Up @@ -188,11 +194,11 @@ pub fn Events(comptime T: type) type {
}

pub fn getWidth(self: *const T) c_int {
return std.math.max(10, js.getWidth(self.peer.element));
return @max(10, js.getWidth(self.peer.element));
}

pub fn getHeight(self: *const T) c_int {
return std.math.max(10, js.getHeight(self.peer.element));
return @max(10, js.getHeight(self.peer.element));
}

pub fn getPreferredSize(self: *const T) lib.Size {
Expand Down Expand Up @@ -241,7 +247,7 @@ pub const TextField = struct {
pub const Label = struct {
peer: *GuiWidget,
/// The text returned by getText(), it's invalidated everytime setText is called
temp_text: ?[:0]const u8 = null,
temp_text: ?[]const u8 = null,

pub usingnamespace Events(Label);

Expand All @@ -251,15 +257,15 @@ pub const Label = struct {

pub fn setAlignment(_: *Label, _: f32) void {}

pub fn setText(self: *Label, text: [:0]const u8) void {
pub fn setText(self: *Label, text: []const u8) void {
js.setText(self.peer.element, text.ptr, text.len);
if (self.temp_text) |slice| {
lasting_allocator.free(slice);
self.temp_text = null;
}
}

pub fn getText(self: *Label) [:0]const u8 {
pub fn getText(self: *Label) []const u8 {
if (self.temp_text) |text| {
return text;
} else {
Expand Down Expand Up @@ -463,6 +469,16 @@ pub const Container = struct {
self.children.append(peer) catch unreachable;
}

pub fn remove(self: *const Container, peer: PeerType) void {
_ = peer;
_ = self;
}

pub fn setTabOrder(self: *const Container, peers: []const PeerType) void {
_ = peers;
_ = self;
}

pub fn move(self: *const Container, peer: PeerType, x: u32, y: u32) void {
_ = self;
js.setPos(peer.element, x, y);
Expand Down Expand Up @@ -496,9 +512,27 @@ pub const HttpResponse = struct {
}
};

// Execution
var stopExecution = false;

fn executeMain() callconv(.Async) void {
// Temporary execution until async is added back in Zig
pub fn runStep(step: shared.EventLoopStep) bool {
_ = step;
while (js.hasEvent()) {
const eventId = js.popEvent();
switch (js.getEventType(eventId)) {
else => {
if (globalWindow) |window| {
if (window.child) |child| {
child.processEventFn(child.object, eventId);
}
}
},
}
}
return !stopExecution;
}

fn executeMain() void {
const mainFn = @import("root").main;
const ReturnType = @typeInfo(@TypeOf(mainFn)).Fn.return_type.?;
if (ReturnType == void) {
Expand All @@ -507,13 +541,28 @@ fn executeMain() callconv(.Async) void {
mainFn() catch |err| @panic(@errorName(err));
}
js.stopExecution();
stopExecution = true;
}

var frame: @Frame(executeMain) = undefined;
var result: void = {};
var suspending: bool = false;
// Execution
// TODO: reuse the old system when async is finally reimplemented in the zig compiler

// fn executeMain() callconv(.Async) void {
// const mainFn = @import("root").main;
// const ReturnType = @typeInfo(@TypeOf(mainFn)).Fn.return_type.?;
// if (ReturnType == void) {
// mainFn();
// } else {
// mainFn() catch |err| @panic(@errorName(err));
// }
// js.stopExecution();
// }

var resumePtr: anyframe = undefined;
// var frame: @Frame(executeMain) = undefined;
// var result: void = {};
// var suspending: bool = false;

// var resumePtr: anyframe = undefined;

fn milliTimestamp() i64 {
return @as(i64, @intFromFloat(js.now()));
Expand Down Expand Up @@ -556,10 +605,11 @@ pub const backendExport = struct {

const start = milliTimestamp();
while (milliTimestamp() < start + @as(i64, @intCast(duration))) {
suspending = true;
suspend {
resumePtr = @frame();
}
// suspending = true;
// suspend {
// resumePtr = @frame();
// }
// TODO: better way to sleep like calling a jS function for sleep
}
return 0;
}
Expand Down Expand Up @@ -600,37 +650,43 @@ pub const backendExport = struct {

//@breakpoint();
js.stopExecution();
stopExecution = true;
while (true) {}
}

pub export fn _start() callconv(.C) void {
_ = @asyncCall(&frame, &result, executeMain, .{});
executeMain();
}

pub export fn _zgtContinue() callconv(.C) void {
if (suspending) {
suspending = false;
resume resumePtr;
}
}
// pub export fn _start() callconv(.C) void {
// _ = @asyncCall(&frame, &result, executeMain, .{});
// }

// pub export fn _zgtContinue() callconv(.C) void {
// if (suspending) {
// suspending = false;
// resume resumePtr;
// }
// }
};

pub fn runStep(step: shared.EventLoopStep) callconv(.Async) bool {
_ = step;
while (js.hasEvent()) {
const eventId = js.popEvent();
switch (js.getEventType(eventId)) {
else => {
if (globalWindow) |window| {
if (window.child) |child| {
child.processEventFn(child.object, eventId);
}
}
},
}
}
suspending = true;
suspend {
resumePtr = @frame();
}
return true;
}
// pub fn runStep(step: shared.EventLoopStep) callconv(.Async) bool {
// _ = step;
// while (js.hasEvent()) {
// const eventId = js.popEvent();
// switch (js.getEventType(eventId)) {
// else => {
// if (globalWindow) |window| {
// if (window.child) |child| {
// child.processEventFn(child.object, eventId);
// }
// }
// },
// }
// }
// suspending = true;
// suspend {
// resumePtr = @frame();
// }
// return true;
// }
Loading

0 comments on commit fe92930

Please sign in to comment.