mach/src/platform/wasm.zig
iddev5 40c0659cc9 mach: App.update will now return !void instead of !bool
In order to close the application, there is already
Engine.setShouldClose() which would roughly do the same thing.
2022-06-08 07:31:53 -07:00

239 lines
7.2 KiB
Zig

const std = @import("std");
const App = @import("app");
const Engine = @import("../Engine.zig");
const structs = @import("../structs.zig");
const enums = @import("../enums.zig");
const js = struct {
extern fn machCanvasInit(width: u32, height: u32, selector_id: *u8) CanvasId;
extern fn machCanvasDeinit(canvas: CanvasId) void;
extern fn machCanvasSetTitle(canvas: CanvasId, title: [*]const u8, len: u32) void;
extern fn machCanvasSetSize(canvas: CanvasId, width: u32, height: u32) void;
extern fn machCanvasGetWindowWidth(canvas: CanvasId) u32;
extern fn machCanvasGetWindowHeight(canvas: CanvasId) u32;
extern fn machCanvasGetFramebufferWidth(canvas: CanvasId) u32;
extern fn machCanvasGetFramebufferHeight(canvas: CanvasId) u32;
extern fn machEventShift() i32;
extern fn machEventShiftFloat() f64;
extern fn machChangeShift() u32;
extern fn machPerfNow() f64;
extern fn machLog(str: [*]const u8, len: u32) void;
extern fn machLogWrite(str: [*]const u8, len: u32) void;
extern fn machLogFlush() void;
extern fn machPanic(str: [*]const u8, len: u32) void;
};
pub const CanvasId = u32;
pub const Platform = struct {
id: CanvasId,
selector_id: []const u8,
last_window_size: structs.Size,
last_framebuffer_size: structs.Size,
pub fn init(allocator: std.mem.Allocator, eng: *Engine) !Platform {
const options = eng.options;
var selector = [1]u8{0} ** 15;
const id = js.machCanvasInit(options.width, options.height, &selector[0]);
const title = std.mem.span(options.title);
js.machCanvasSetTitle(id, title.ptr, title.len);
return Platform{
.id = id,
.selector_id = try allocator.dupe(u8, selector[0 .. selector.len - @as(u32, if (selector[selector.len - 1] == 0) 1 else 0)]),
.last_window_size = .{
.width = js.machCanvasGetWindowWidth(id),
.height = js.machCanvasGetWindowHeight(id),
},
.last_framebuffer_size = .{
.width = js.machCanvasGetFramebufferWidth(id),
.height = js.machCanvasGetFramebufferHeight(id),
},
};
}
pub fn setOptions(platform: *Platform, options: structs.Options) !void {
// NOTE: size limits do not exists on wasm
js.machCanvasSetSize(platform.id, options.width, options.height);
const title = std.mem.span(options.title);
js.machCanvasSetTitle(platform.id, title.ptr, title.len);
}
pub fn setShouldClose(_: *Platform, _: bool) void {}
pub fn getFramebufferSize(platform: *Platform) structs.Size {
return platform.last_framebuffer_size;
}
pub fn getWindowSize(platform: *Platform) structs.Size {
return platform.last_window_size;
}
fn pollChanges(platform: *Platform) void {
const change_type = js.machChangeShift();
switch (change_type) {
1 => {
const width = js.machChangeShift();
const height = js.machChangeShift();
const device_pixel_ratio = js.machChangeShift();
platform.last_window_size = .{
.width = @divFloor(width, device_pixel_ratio),
.height = @divFloor(height, device_pixel_ratio),
};
platform.last_framebuffer_size = .{
.width = width,
.height = height,
};
},
else => {},
}
}
pub fn pollEvent(_: *Platform) ?structs.Event {
const event_type = js.machEventShift();
return switch (event_type) {
1 => structs.Event{
.key_press = .{ .key = @intToEnum(enums.Key, js.machEventShift()) },
},
2 => structs.Event{
.key_release = .{ .key = @intToEnum(enums.Key, js.machEventShift()) },
},
3 => structs.Event{
.mouse_motion = .{
.x = @intToFloat(f64, js.machEventShift()),
.y = @intToFloat(f64, js.machEventShift()),
},
},
4 => structs.Event{
.mouse_press = .{
.button = toMachButton(js.machEventShift()),
},
},
5 => structs.Event{
.mouse_release = .{
.button = toMachButton(js.machEventShift()),
},
},
6 => structs.Event{
.mouse_scroll = .{
.xoffset = @floatCast(f32, sign(js.machEventShiftFloat())),
.yoffset = @floatCast(f32, sign(js.machEventShiftFloat())),
},
},
else => null,
};
}
inline fn sign(val: f64) f64 {
return switch (val) {
0.0 => 0.0,
else => -val,
};
}
fn toMachButton(button: i32) enums.MouseButton {
return switch (button) {
0 => .left,
1 => .middle,
2 => .right,
3 => .four,
4 => .five,
else => unreachable,
};
}
};
pub const BackingTimer = struct {
initial: f64 = undefined,
const WasmTimer = @This();
pub fn start() !WasmTimer {
return WasmTimer{ .initial = js.machPerfNow() };
}
pub fn read(timer: *WasmTimer) u64 {
return timeToNs(js.machPerfNow() - timer.initial);
}
pub fn reset(timer: *WasmTimer) void {
timer.initial = js.machPerfNow();
}
pub fn lap(timer: *WasmTimer) u64 {
const now = js.machPerfNow();
const initial = timer.initial;
timer.initial = now;
return timeToNs(now - initial);
}
fn timeToNs(t: f64) u64 {
return @floatToInt(u64, t) * 1000000;
}
};
const common = @import("common.zig");
comptime {
common.checkApplication(App);
}
var app: App = undefined;
var engine: Engine = undefined;
export fn wasmInit() void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
engine = Engine.init(allocator) catch unreachable;
app.init(&engine) catch {};
}
export fn wasmUpdate() void {
// Poll internal events, like resize
engine.internal.pollChanges();
engine.delta_time_ns = engine.timer.lapPrecise();
engine.delta_time = @intToFloat(f32, engine.delta_time_ns) / @intToFloat(f32, std.time.ns_per_s);
app.update(&engine) catch engine.setShouldClose(true);
}
export fn wasmDeinit() void {
app.deinit(&engine);
}
pub const log_level = .info;
const LogError = error{};
const LogWriter = std.io.Writer(void, LogError, writeLog);
fn writeLog(_: void, msg: []const u8) LogError!usize {
js.machLogWrite(msg.ptr, msg.len);
return msg.len;
}
pub fn log(
comptime message_level: std.log.Level,
comptime scope: @Type(.EnumLiteral),
comptime format: []const u8,
args: anytype,
) void {
const prefix = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): ";
const writer = LogWriter{ .context = {} };
writer.print(message_level.asText() ++ prefix ++ format ++ "\n", args) catch return;
js.machLogFlush();
}
pub fn panic(msg: []const u8, _: ?*std.builtin.StackTrace) noreturn {
js.machPanic(msg.ptr, msg.len);
unreachable;
}