{mach,core}: move core sources to libs/core
Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
This commit is contained in:
parent
562b908c84
commit
9bbada90b2
18 changed files with 13 additions and 79 deletions
401
src/Core.zig
401
src/Core.zig
|
|
@ -1,401 +0,0 @@
|
|||
const builtin = @import("builtin");
|
||||
const std = @import("std");
|
||||
const gpu = @import("gpu");
|
||||
const platform = @import("platform.zig");
|
||||
|
||||
pub const Core = @This();
|
||||
|
||||
internal: platform.Core,
|
||||
|
||||
pub const Options = struct {
|
||||
is_app: bool = false,
|
||||
title: [*:0]const u8 = "Mach Engine",
|
||||
size: Size = .{ .width = 640, .height = 640 },
|
||||
power_preference: gpu.PowerPreference = .undefined,
|
||||
required_features: ?[]const gpu.FeatureName = null,
|
||||
required_limits: ?gpu.Limits = null,
|
||||
};
|
||||
|
||||
pub fn init(core: *Core, allocator: std.mem.Allocator, options: Options) !void {
|
||||
try platform.Core.init(&core.internal, allocator, options);
|
||||
}
|
||||
|
||||
pub fn deinit(core: *Core) void {
|
||||
return core.internal.deinit();
|
||||
}
|
||||
|
||||
pub fn hasEvent(core: *Core) bool {
|
||||
return core.internal.hasEvent();
|
||||
}
|
||||
|
||||
pub fn pollEvents(core: *Core) ?Event {
|
||||
return core.internal.pollEvents();
|
||||
}
|
||||
|
||||
/// Returns the framebuffer size, in subpixel units.
|
||||
pub fn framebufferSize(core: *Core) Size {
|
||||
return core.internal.framebufferSize();
|
||||
}
|
||||
|
||||
/// Sets seconds to wait for an event with timeout when calling `Core.update()`
|
||||
/// again.
|
||||
///
|
||||
/// timeout is in seconds (<= `0.0` disables waiting)
|
||||
/// - pass `std.math.inf(f64)` to wait with no timeout
|
||||
///
|
||||
/// `Core.update()` will return earlier than timeout if an event happens (key press,
|
||||
/// mouse motion, etc.)
|
||||
///
|
||||
/// `Core.update()` can return a bit later than timeout due to timer precision and
|
||||
/// process scheduling.
|
||||
pub fn setWaitTimeout(core: *Core, timeout: f64) void {
|
||||
return core.internal.setWaitTimeout(timeout);
|
||||
}
|
||||
|
||||
/// Set the window title
|
||||
pub fn setTitle(core: *Core, title: [:0]const u8) void {
|
||||
return core.internal.setTitle(title);
|
||||
}
|
||||
|
||||
/// Set the window mode
|
||||
pub fn setDisplayMode(core: *Core, mode: DisplayMode, monitor: ?usize) void {
|
||||
return core.internal.setDisplayMode(mode, monitor);
|
||||
}
|
||||
|
||||
/// Returns the window mode
|
||||
pub fn displayMode(core: *Core) DisplayMode {
|
||||
return core.internal.displayMode();
|
||||
}
|
||||
|
||||
pub fn setBorder(core: *Core, value: bool) void {
|
||||
return core.internal.setBorder(value);
|
||||
}
|
||||
|
||||
pub fn border(core: *Core) bool {
|
||||
return core.internal.border();
|
||||
}
|
||||
|
||||
pub fn setHeadless(core: *Core, value: bool) void {
|
||||
return core.internal.setHeadless(value);
|
||||
}
|
||||
|
||||
pub fn headless(core: *Core) bool {
|
||||
return core.internal.headless();
|
||||
}
|
||||
|
||||
pub const VSyncMode = enum {
|
||||
/// Potential screen tearing.
|
||||
/// No synchronization with monitor, render frames as fast as possible.
|
||||
///
|
||||
/// Not available on WASM, fallback to double
|
||||
none,
|
||||
|
||||
/// No tearing, synchronizes rendering with monitor refresh rate, rendering frames when ready.
|
||||
///
|
||||
/// Tries to stay one frame ahead of the monitor, so when it's ready for the next frame it is
|
||||
/// already prepared.
|
||||
double,
|
||||
|
||||
/// No tearing, synchronizes rendering with monitor refresh rate, rendering frames when ready.
|
||||
///
|
||||
/// Tries to stay two frames ahead of the monitor, so when it's ready for the next frame it is
|
||||
/// already prepared.
|
||||
///
|
||||
/// Not available on WASM, fallback to double
|
||||
triple,
|
||||
};
|
||||
|
||||
/// Set monitor synchronization mode.
|
||||
pub fn setVSync(core: *Core, mode: VSyncMode) void {
|
||||
return core.internal.setVSync(mode);
|
||||
}
|
||||
|
||||
/// Returns monitor synchronization mode.
|
||||
pub fn vsync(core: *Core) VSyncMode {
|
||||
return core.internal.vsync();
|
||||
}
|
||||
|
||||
/// Set the window size, in subpixel units.
|
||||
pub fn setSize(core: *Core, value: Size) void {
|
||||
return core.internal.setSize(value);
|
||||
}
|
||||
|
||||
/// Returns the window size, in subpixel units.
|
||||
pub fn size(core: *Core) Size {
|
||||
return core.internal.size();
|
||||
}
|
||||
|
||||
/// Set the minimum and maximum allowed size for the window.
|
||||
pub fn setSizeLimit(core: *Core, size_limit: SizeLimit) void {
|
||||
return core.internal.setSizeLimit(size_limit);
|
||||
}
|
||||
|
||||
/// Returns the minimum and maximum allowed size for the window.
|
||||
pub fn sizeLimit(core: *Core) SizeLimit {
|
||||
return core.internal.sizeLimit();
|
||||
}
|
||||
|
||||
pub fn setCursorMode(core: *Core, mode: CursorMode) void {
|
||||
return core.internal.setCursorMode(mode);
|
||||
}
|
||||
|
||||
pub fn cursorMode(core: *Core) CursorMode {
|
||||
return core.internal.cursorMode();
|
||||
}
|
||||
|
||||
pub fn setCursorShape(core: *Core, cursor: CursorShape) void {
|
||||
return core.internal.setCursorShape(cursor);
|
||||
}
|
||||
|
||||
pub fn cursorShape(core: *Core) CursorShape {
|
||||
return core.internal.cursorShape();
|
||||
}
|
||||
|
||||
pub fn adapter(core: *Core) *gpu.Adapter {
|
||||
return core.internal.adapter();
|
||||
}
|
||||
|
||||
pub fn device(core: *Core) *gpu.Device {
|
||||
return core.internal.device();
|
||||
}
|
||||
|
||||
pub fn swapChain(core: *Core) *gpu.SwapChain {
|
||||
return core.internal.swapChain();
|
||||
}
|
||||
|
||||
pub fn descriptor(core: *Core) gpu.SwapChain.Descriptor {
|
||||
return core.internal.descriptor();
|
||||
}
|
||||
|
||||
pub const Size = struct {
|
||||
width: u32,
|
||||
height: u32,
|
||||
};
|
||||
|
||||
pub const SizeOptional = struct {
|
||||
width: ?u32,
|
||||
height: ?u32,
|
||||
};
|
||||
|
||||
pub const SizeLimit = struct {
|
||||
min: SizeOptional,
|
||||
max: SizeOptional,
|
||||
};
|
||||
|
||||
pub const Position = struct {
|
||||
x: f64,
|
||||
y: f64,
|
||||
};
|
||||
|
||||
pub const Event = union(enum) {
|
||||
key_press: KeyEvent,
|
||||
key_repeat: KeyEvent,
|
||||
key_release: KeyEvent,
|
||||
char_input: struct {
|
||||
codepoint: u21,
|
||||
},
|
||||
mouse_motion: struct {
|
||||
pos: Position,
|
||||
},
|
||||
mouse_press: MouseButtonEvent,
|
||||
mouse_release: MouseButtonEvent,
|
||||
mouse_scroll: struct {
|
||||
xoffset: f32,
|
||||
yoffset: f32,
|
||||
},
|
||||
framebuffer_resize: Size,
|
||||
focus_gained,
|
||||
focus_lost,
|
||||
close,
|
||||
};
|
||||
|
||||
pub const KeyEvent = struct {
|
||||
key: Key,
|
||||
mods: KeyMods,
|
||||
};
|
||||
|
||||
pub const MouseButtonEvent = struct {
|
||||
button: MouseButton,
|
||||
pos: Position,
|
||||
mods: KeyMods,
|
||||
};
|
||||
|
||||
pub const MouseButton = enum {
|
||||
left,
|
||||
right,
|
||||
middle,
|
||||
four,
|
||||
five,
|
||||
six,
|
||||
seven,
|
||||
eight,
|
||||
};
|
||||
|
||||
pub const Key = enum {
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
d,
|
||||
e,
|
||||
f,
|
||||
g,
|
||||
h,
|
||||
i,
|
||||
j,
|
||||
k,
|
||||
l,
|
||||
m,
|
||||
n,
|
||||
o,
|
||||
p,
|
||||
q,
|
||||
r,
|
||||
s,
|
||||
t,
|
||||
u,
|
||||
v,
|
||||
w,
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
|
||||
zero,
|
||||
one,
|
||||
two,
|
||||
three,
|
||||
four,
|
||||
five,
|
||||
six,
|
||||
seven,
|
||||
eight,
|
||||
nine,
|
||||
|
||||
f1,
|
||||
f2,
|
||||
f3,
|
||||
f4,
|
||||
f5,
|
||||
f6,
|
||||
f7,
|
||||
f8,
|
||||
f9,
|
||||
f10,
|
||||
f11,
|
||||
f12,
|
||||
f13,
|
||||
f14,
|
||||
f15,
|
||||
f16,
|
||||
f17,
|
||||
f18,
|
||||
f19,
|
||||
f20,
|
||||
f21,
|
||||
f22,
|
||||
f23,
|
||||
f24,
|
||||
f25,
|
||||
|
||||
kp_divide,
|
||||
kp_multiply,
|
||||
kp_subtract,
|
||||
kp_add,
|
||||
kp_0,
|
||||
kp_1,
|
||||
kp_2,
|
||||
kp_3,
|
||||
kp_4,
|
||||
kp_5,
|
||||
kp_6,
|
||||
kp_7,
|
||||
kp_8,
|
||||
kp_9,
|
||||
kp_decimal,
|
||||
kp_equal,
|
||||
kp_enter,
|
||||
|
||||
enter,
|
||||
escape,
|
||||
tab,
|
||||
left_shift,
|
||||
right_shift,
|
||||
left_control,
|
||||
right_control,
|
||||
left_alt,
|
||||
right_alt,
|
||||
left_super,
|
||||
right_super,
|
||||
menu,
|
||||
num_lock,
|
||||
caps_lock,
|
||||
print,
|
||||
scroll_lock,
|
||||
pause,
|
||||
delete,
|
||||
home,
|
||||
end,
|
||||
page_up,
|
||||
page_down,
|
||||
insert,
|
||||
left,
|
||||
right,
|
||||
up,
|
||||
down,
|
||||
backspace,
|
||||
space,
|
||||
minus,
|
||||
equal,
|
||||
left_bracket,
|
||||
right_bracket,
|
||||
backslash,
|
||||
semicolon,
|
||||
apostrophe,
|
||||
comma,
|
||||
period,
|
||||
slash,
|
||||
grave,
|
||||
|
||||
unknown,
|
||||
};
|
||||
|
||||
pub const KeyMods = packed struct {
|
||||
shift: bool,
|
||||
control: bool,
|
||||
alt: bool,
|
||||
super: bool,
|
||||
caps_lock: bool,
|
||||
num_lock: bool,
|
||||
_reserved: u2 = 0,
|
||||
};
|
||||
|
||||
pub const DisplayMode = enum {
|
||||
windowed,
|
||||
fullscreen,
|
||||
// TODO: fullscreen_windowed,
|
||||
};
|
||||
|
||||
pub const CursorMode = enum {
|
||||
/// Makes the cursor visible and behaving normally.
|
||||
normal,
|
||||
|
||||
/// Makes the cursor invisible when it is over the content area of the window but does not
|
||||
/// restrict it from leaving.
|
||||
hidden,
|
||||
|
||||
/// Hides and grabs the cursor, providing virtual and unlimited cursor movement. This is useful
|
||||
/// for implementing for example 3D camera controls.
|
||||
disabled,
|
||||
};
|
||||
|
||||
pub const CursorShape = enum {
|
||||
arrow,
|
||||
ibeam,
|
||||
crosshair,
|
||||
pointing_hand,
|
||||
resize_ew,
|
||||
resize_ns,
|
||||
resize_nwse,
|
||||
resize_nesw,
|
||||
resize_all,
|
||||
not_allowed,
|
||||
};
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
const std = @import("std");
|
||||
const platform = @import("platform.zig");
|
||||
|
||||
pub const Timer = @This();
|
||||
|
||||
internal: platform.Timer,
|
||||
|
||||
/// Initialize the timer.
|
||||
pub fn start() !Timer {
|
||||
return Timer{
|
||||
.internal = try platform.Timer.start(),
|
||||
};
|
||||
}
|
||||
|
||||
/// Reads the timer value since start or the last reset in nanoseconds.
|
||||
pub inline fn readPrecise(timer: *Timer) u64 {
|
||||
return timer.internal.read();
|
||||
}
|
||||
|
||||
/// Reads the timer value since start or the last reset in seconds.
|
||||
pub inline fn read(timer: *Timer) f32 {
|
||||
return @intToFloat(f32, timer.readPrecise()) / @intToFloat(f32, std.time.ns_per_s);
|
||||
}
|
||||
|
||||
/// Resets the timer value to 0/now.
|
||||
pub inline fn reset(timer: *Timer) void {
|
||||
timer.internal.reset();
|
||||
}
|
||||
|
||||
/// Returns the current value of the timer in nanoseconds, then resets it.
|
||||
pub inline fn lapPrecise(timer: *Timer) u64 {
|
||||
return timer.internal.lap();
|
||||
}
|
||||
|
||||
/// Returns the current value of the timer in seconds, then resets it.
|
||||
pub inline fn lap(timer: *Timer) f32 {
|
||||
return @intToFloat(f32, timer.lapPrecise()) / @intToFloat(f32, std.time.ns_per_s);
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
const std = @import("std");
|
||||
pub const Core = @import("Core.zig");
|
||||
pub const gpu = @import("gpu");
|
||||
pub const ecs = @import("ecs");
|
||||
|
||||
/// The Mach engine ECS module. This enables access to `engine.get(.mach, .core)` `*Core` APIs, as
|
||||
/// to for example `.setOptions(.{.title = "foobar"})`, or to access the GPU device via
|
||||
/// `engine.get(.mach, .device)`
|
||||
pub const module = ecs.Module(.{
|
||||
.globals = struct {
|
||||
core: *Core,
|
||||
device: *gpu.Device,
|
||||
},
|
||||
});
|
||||
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
pub fn App(
|
||||
comptime modules: anytype,
|
||||
comptime app_init: anytype, // fn (engine: *ecs.World(modules)) !void
|
||||
) type {
|
||||
// TODO: validate modules.mach is the expected type.
|
||||
// TODO: validate init has the right function signature
|
||||
|
||||
return struct {
|
||||
engine: ecs.World(modules),
|
||||
core: Core,
|
||||
|
||||
pub fn init(app: *@This()) !void {
|
||||
try app.core.init(allocator, .{});
|
||||
app.* = .{
|
||||
.core = app.core,
|
||||
.engine = try ecs.World(modules).init(allocator),
|
||||
};
|
||||
app.engine.set(.mach, .core, &app.core);
|
||||
app.engine.set(.mach, .device, app.core.device());
|
||||
try app_init(&app.engine);
|
||||
}
|
||||
|
||||
pub fn deinit(app: *@This()) void {
|
||||
const core = app.engine.get(.mach, .core);
|
||||
core.deinit();
|
||||
allocator.destroy(core);
|
||||
app.engine.deinit();
|
||||
_ = gpa.deinit();
|
||||
}
|
||||
|
||||
pub fn update(app: *@This()) !bool {
|
||||
app.engine.tick();
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
const builtin = @import("builtin");
|
||||
|
||||
pub usingnamespace @import("platform.zig").entry;
|
||||
|
||||
comptime {
|
||||
if (!builtin.is_test) {
|
||||
if (!@hasDecl(@import("app"), "App")) {
|
||||
@compileError("expected e.g. `pub const App = mach.App(modules, init)' (App definition missing in your main Zig file)");
|
||||
}
|
||||
|
||||
const App = @import("app").App;
|
||||
if (@typeInfo(App) != .Struct) {
|
||||
@compileError("App must be a struct type. Found:" ++ @typeName(App));
|
||||
}
|
||||
|
||||
if (@hasDecl(App, "init")) {
|
||||
const InitFn = @TypeOf(@field(App, "init"));
|
||||
if (InitFn != fn (*App) @typeInfo(@typeInfo(InitFn).Fn.return_type.?).ErrorUnion.error_set!void)
|
||||
@compileError("expected 'pub fn init(app: *App) !void' found '" ++ @typeName(InitFn) ++ "'");
|
||||
} else {
|
||||
@compileError("App must export 'pub fn init(app: *App) !void'");
|
||||
}
|
||||
|
||||
if (@hasDecl(App, "update")) {
|
||||
const UpdateFn = @TypeOf(@field(App, "update"));
|
||||
if (UpdateFn != fn (app: *App) @typeInfo(@typeInfo(UpdateFn).Fn.return_type.?).ErrorUnion.error_set!bool)
|
||||
@compileError("expected 'pub fn update(app: *App) !bool' found '" ++ @typeName(UpdateFn) ++ "'");
|
||||
} else {
|
||||
@compileError("App must export 'pub fn update(app: *App) !bool'");
|
||||
}
|
||||
|
||||
if (@hasDecl(App, "deinit")) {
|
||||
const DeinitFn = @TypeOf(@field(App, "deinit"));
|
||||
if (DeinitFn != fn (app: *App) void)
|
||||
@compileError("expected 'pub fn deinit(app: *App) void' found '" ++ @typeName(DeinitFn) ++ "'");
|
||||
} else {
|
||||
@compileError("App must export 'pub fn deinit(app: *App) void'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
const builtin = @import("builtin");
|
||||
|
||||
pub usingnamespace if (builtin.cpu.arch == .wasm32)
|
||||
@import("platform/wasm.zig")
|
||||
else
|
||||
@import("platform/native.zig");
|
||||
|
||||
// Verifies that a platform implementation exposes the expected function declarations.
|
||||
comptime {
|
||||
assertHasDecl(@This(), "entry");
|
||||
assertHasDecl(@This(), "Core");
|
||||
assertHasDecl(@This(), "Timer");
|
||||
|
||||
// Core
|
||||
assertHasDecl(@This().Core, "init");
|
||||
assertHasDecl(@This().Core, "deinit");
|
||||
assertHasDecl(@This().Core, "hasEvent");
|
||||
assertHasDecl(@This().Core, "pollEvents");
|
||||
assertHasDecl(@This().Core, "framebufferSize");
|
||||
|
||||
assertHasDecl(@This().Core, "setWaitTimeout");
|
||||
assertHasDecl(@This().Core, "setTitle");
|
||||
|
||||
assertHasDecl(@This().Core, "setDisplayMode");
|
||||
assertHasDecl(@This().Core, "displayMode");
|
||||
|
||||
assertHasDecl(@This().Core, "setBorder");
|
||||
assertHasDecl(@This().Core, "border");
|
||||
|
||||
assertHasDecl(@This().Core, "setHeadless");
|
||||
assertHasDecl(@This().Core, "headless");
|
||||
|
||||
assertHasDecl(@This().Core, "setVSync");
|
||||
assertHasDecl(@This().Core, "vsync");
|
||||
|
||||
assertHasDecl(@This().Core, "setSize");
|
||||
assertHasDecl(@This().Core, "size");
|
||||
|
||||
assertHasDecl(@This().Core, "setSizeLimit");
|
||||
assertHasDecl(@This().Core, "sizeLimit");
|
||||
|
||||
assertHasDecl(@This().Core, "setCursorMode");
|
||||
assertHasDecl(@This().Core, "cursorMode");
|
||||
|
||||
assertHasDecl(@This().Core, "setCursorShape");
|
||||
assertHasDecl(@This().Core, "cursorShape");
|
||||
|
||||
assertHasDecl(@This().Core, "adapter");
|
||||
assertHasDecl(@This().Core, "device");
|
||||
assertHasDecl(@This().Core, "swapChain");
|
||||
assertHasDecl(@This().Core, "descriptor");
|
||||
|
||||
// Timer
|
||||
assertHasDecl(@This().Timer, "start");
|
||||
assertHasDecl(@This().Timer, "read");
|
||||
assertHasDecl(@This().Timer, "reset");
|
||||
assertHasDecl(@This().Timer, "lap");
|
||||
}
|
||||
|
||||
fn assertHasDecl(comptime T: anytype, comptime name: []const u8) void {
|
||||
if (!@hasDecl(T, name)) @compileError("Core missing declaration: " ++ name);
|
||||
}
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
const std = @import("std");
|
||||
const gpu = @import("gpu");
|
||||
const ecs = @import("ecs");
|
||||
const glfw = @import("glfw");
|
||||
const Core = @import("../Core.zig");
|
||||
const native = @import("native.zig");
|
||||
|
||||
pub const App = @This();
|
||||
|
||||
pub const GPUInterface = gpu.dawn.Interface;
|
||||
|
||||
const _ = gpu.Export(GPUInterface);
|
||||
|
||||
// Current Limitations:
|
||||
// 1. Currently, ecs seems to be using some weird compile-time type trickery, so I'm not exactly sure how
|
||||
// `engine` should be integrated into the C API
|
||||
// 2. Core might need to expose more state so more API functions can be exposed (for example, the WebGPU API)
|
||||
// 3. Be very careful about arguments, types, memory, etc - any mismatch will result in undefined behavior
|
||||
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
// Returns a pointer to a newly allocated Core
|
||||
// Will return a null pointer if an error occurred while initializing Core
|
||||
pub export fn mach_core_init() ?*native.Core {
|
||||
gpu.Impl.init();
|
||||
// TODO(libmach): eliminate this allocation
|
||||
var core = allocator.create(native.Core) catch {
|
||||
return @intToPtr(?*native.Core, 0);
|
||||
};
|
||||
// TODO(libmach): allow passing init options
|
||||
core.init(allocator, .{}) catch {
|
||||
// TODO(libmach): better error handling
|
||||
return @intToPtr(?*native.Core, 0);
|
||||
};
|
||||
return core;
|
||||
}
|
||||
|
||||
pub export fn mach_core_deinit(core: *native.Core) void {
|
||||
native.Core.deinit(core);
|
||||
}
|
||||
|
||||
// pub export fn mach_core_poll_events(core: *native.Core) Core.Event {
|
||||
// return native.Core.pollEvents(core);
|
||||
// }
|
||||
|
||||
const MachStatus = enum(c_int) {
|
||||
Success = 0x00000000,
|
||||
Error = 0x00000001,
|
||||
};
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
const std = @import("std");
|
||||
|
||||
pub const entry = @import("native/entry.zig");
|
||||
pub const Core = @import("native/Core.zig");
|
||||
pub const Timer = std.time.Timer;
|
||||
|
|
@ -1,765 +0,0 @@
|
|||
const builtin = @import("builtin");
|
||||
const std = @import("std");
|
||||
const gpu = @import("gpu");
|
||||
const glfw = @import("glfw");
|
||||
const util = @import("util.zig");
|
||||
const Options = @import("../../Core.zig").Options;
|
||||
const Event = @import("../../Core.zig").Event;
|
||||
const KeyEvent = @import("../../Core.zig").KeyEvent;
|
||||
const MouseButtonEvent = @import("../../Core.zig").MouseButtonEvent;
|
||||
const MouseButton = @import("../../Core.zig").MouseButton;
|
||||
const Size = @import("../../Core.zig").Size;
|
||||
const DisplayMode = @import("../../Core.zig").DisplayMode;
|
||||
const SizeLimit = @import("../../Core.zig").SizeLimit;
|
||||
const CursorShape = @import("../../Core.zig").CursorShape;
|
||||
const VSyncMode = @import("../../Core.zig").VSyncMode;
|
||||
const CursorMode = @import("../../Core.zig").CursorMode;
|
||||
const Key = @import("../../Core.zig").Key;
|
||||
const KeyMods = @import("../../Core.zig").KeyMods;
|
||||
|
||||
pub const Core = @This();
|
||||
|
||||
allocator: std.mem.Allocator,
|
||||
window: glfw.Window,
|
||||
backend_type: gpu.BackendType,
|
||||
user_ptr: UserPtr,
|
||||
|
||||
instance: *gpu.Instance,
|
||||
surface: *gpu.Surface,
|
||||
gpu_adapter: *gpu.Adapter,
|
||||
gpu_device: *gpu.Device,
|
||||
swap_chain: *gpu.SwapChain,
|
||||
swap_chain_desc: gpu.SwapChain.Descriptor,
|
||||
|
||||
events: EventQueue,
|
||||
wait_timeout: f64,
|
||||
|
||||
last_size: glfw.Window.Size,
|
||||
last_pos: glfw.Window.Pos,
|
||||
size_limit: SizeLimit,
|
||||
frame_buffer_resized: bool,
|
||||
|
||||
current_cursor: CursorShape,
|
||||
cursors: [@typeInfo(CursorShape).Enum.fields.len]?glfw.Cursor,
|
||||
cursors_tried: [@typeInfo(CursorShape).Enum.fields.len]bool,
|
||||
|
||||
linux_gamemode: ?bool,
|
||||
|
||||
const EventQueue = std.TailQueue(Event);
|
||||
const EventNode = EventQueue.Node;
|
||||
|
||||
const UserPtr = struct {
|
||||
self: *Core,
|
||||
};
|
||||
|
||||
pub fn init(core: *Core, allocator: std.mem.Allocator, options: Options) !void {
|
||||
const backend_type = try util.detectBackendType(allocator);
|
||||
|
||||
glfw.setErrorCallback(errorCallback);
|
||||
if (!glfw.init(.{}))
|
||||
glfw.getErrorCode() catch |err| switch (err) {
|
||||
error.PlatformError,
|
||||
error.PlatformUnavailable,
|
||||
=> return err,
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
// Create the test window and discover adapters using it (esp. for OpenGL)
|
||||
var hints = util.glfwWindowHintsForBackend(backend_type);
|
||||
hints.cocoa_retina_framebuffer = true;
|
||||
const window = glfw.Window.create(
|
||||
options.size.width,
|
||||
options.size.height,
|
||||
options.title,
|
||||
null,
|
||||
null,
|
||||
hints,
|
||||
) orelse switch (glfw.mustGetErrorCode()) {
|
||||
error.InvalidEnum,
|
||||
error.InvalidValue,
|
||||
error.FormatUnavailable,
|
||||
=> unreachable,
|
||||
error.APIUnavailable,
|
||||
error.VersionUnavailable,
|
||||
error.PlatformError,
|
||||
=> |err| return err,
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
switch (backend_type) {
|
||||
.opengl, .opengles => {
|
||||
glfw.makeContextCurrent(window);
|
||||
glfw.getErrorCode() catch |err| switch (err) {
|
||||
error.PlatformError => return err,
|
||||
else => unreachable,
|
||||
};
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
|
||||
const instance = gpu.createInstance(null) orelse {
|
||||
std.log.err("mach: failed to create GPU instance", .{});
|
||||
std.process.exit(1);
|
||||
};
|
||||
const surface = util.createSurfaceForWindow(instance, window, comptime util.detectGLFWOptions());
|
||||
|
||||
var response: util.RequestAdapterResponse = undefined;
|
||||
instance.requestAdapter(&gpu.RequestAdapterOptions{
|
||||
.compatible_surface = surface,
|
||||
.power_preference = options.power_preference,
|
||||
.force_fallback_adapter = false,
|
||||
}, &response, util.requestAdapterCallback);
|
||||
if (response.status != .success) {
|
||||
std.log.err("mach: failed to create GPU adapter: {?s}", .{response.message});
|
||||
std.log.info("-> maybe try MACH_GPU_BACKEND=opengl ?", .{});
|
||||
std.process.exit(1);
|
||||
}
|
||||
|
||||
// Print which adapter we are going to use.
|
||||
var props = std.mem.zeroes(gpu.Adapter.Properties);
|
||||
response.adapter.getProperties(&props);
|
||||
if (props.backend_type == .null) {
|
||||
std.log.err("no backend found for {s} adapter", .{props.adapter_type.name()});
|
||||
std.process.exit(1);
|
||||
}
|
||||
std.log.info("mach: found {s} backend on {s} adapter: {s}, {s}\n", .{
|
||||
props.backend_type.name(),
|
||||
props.adapter_type.name(),
|
||||
props.name,
|
||||
props.driver_description,
|
||||
});
|
||||
|
||||
// Create a device with default limits/features.
|
||||
const gpu_device = response.adapter.createDevice(&.{
|
||||
.required_features_count = if (options.required_features) |v| @intCast(u32, v.len) else 0,
|
||||
.required_features = if (options.required_features) |v| @as(?[*]const gpu.FeatureName, v.ptr) else null,
|
||||
.required_limits = if (options.required_limits) |limits| @as(?*gpu.RequiredLimits, &gpu.RequiredLimits{
|
||||
.limits = limits,
|
||||
}) else null,
|
||||
}) orelse {
|
||||
std.log.err("mach: failed to create GPU device\n", .{});
|
||||
std.process.exit(1);
|
||||
};
|
||||
gpu_device.setUncapturedErrorCallback({}, util.printUnhandledErrorCallback);
|
||||
|
||||
const framebuffer_size = window.getFramebufferSize();
|
||||
const swap_chain_desc = gpu.SwapChain.Descriptor{
|
||||
.label = "main swap chain",
|
||||
.usage = .{ .render_attachment = true },
|
||||
.format = .bgra8_unorm,
|
||||
.width = framebuffer_size.width,
|
||||
.height = framebuffer_size.height,
|
||||
.present_mode = .fifo,
|
||||
};
|
||||
const swap_chain = gpu_device.createSwapChain(surface, &swap_chain_desc);
|
||||
|
||||
core.* = .{
|
||||
.allocator = allocator,
|
||||
.window = window,
|
||||
.backend_type = backend_type,
|
||||
.user_ptr = undefined,
|
||||
|
||||
.instance = instance,
|
||||
.surface = surface,
|
||||
.gpu_adapter = response.adapter,
|
||||
.gpu_device = gpu_device,
|
||||
.swap_chain = swap_chain,
|
||||
.swap_chain_desc = swap_chain_desc,
|
||||
|
||||
.events = .{},
|
||||
.wait_timeout = 0.0,
|
||||
|
||||
.last_size = window.getSize(),
|
||||
.last_pos = window.getPos(),
|
||||
.size_limit = .{
|
||||
.min = .{ .width = 350, .height = 350 },
|
||||
.max = .{ .width = null, .height = null },
|
||||
},
|
||||
.frame_buffer_resized = false,
|
||||
|
||||
.current_cursor = .arrow,
|
||||
.cursors = std.mem.zeroes([@typeInfo(CursorShape).Enum.fields.len]?glfw.Cursor),
|
||||
.cursors_tried = std.mem.zeroes([@typeInfo(CursorShape).Enum.fields.len]bool),
|
||||
|
||||
.linux_gamemode = null,
|
||||
};
|
||||
|
||||
core.setSizeLimit(core.size_limit);
|
||||
|
||||
core.initCallbacks();
|
||||
if (builtin.os.tag == .linux and !options.is_app and
|
||||
core.linux_gamemode == null and try activateGamemode(core.allocator))
|
||||
core.linux_gamemode = initLinuxGamemode();
|
||||
}
|
||||
|
||||
fn initCallbacks(self: *Core) void {
|
||||
self.user_ptr = UserPtr{ .self = self };
|
||||
|
||||
self.window.setUserPointer(&self.user_ptr);
|
||||
|
||||
const key_callback = struct {
|
||||
fn callback(window: glfw.Window, key: glfw.Key, scancode: i32, action: glfw.Action, mods: glfw.Mods) void {
|
||||
const pf = (window.getUserPointer(UserPtr) orelse unreachable).self;
|
||||
const key_event = KeyEvent{
|
||||
.key = toMachKey(key),
|
||||
.mods = toMachMods(mods),
|
||||
};
|
||||
switch (action) {
|
||||
.press => pf.pushEvent(.{ .key_press = key_event }),
|
||||
.repeat => pf.pushEvent(.{ .key_repeat = key_event }),
|
||||
.release => pf.pushEvent(.{ .key_release = key_event }),
|
||||
}
|
||||
_ = scancode;
|
||||
}
|
||||
}.callback;
|
||||
self.window.setKeyCallback(key_callback);
|
||||
|
||||
const char_callback = struct {
|
||||
fn callback(window: glfw.Window, codepoint: u21) void {
|
||||
const pf = (window.getUserPointer(UserPtr) orelse unreachable).self;
|
||||
pf.pushEvent(.{
|
||||
.char_input = .{
|
||||
.codepoint = codepoint,
|
||||
},
|
||||
});
|
||||
}
|
||||
}.callback;
|
||||
self.window.setCharCallback(char_callback);
|
||||
|
||||
const mouse_motion_callback = struct {
|
||||
fn callback(window: glfw.Window, xpos: f64, ypos: f64) void {
|
||||
const pf = (window.getUserPointer(UserPtr) orelse unreachable).self;
|
||||
pf.pushEvent(.{
|
||||
.mouse_motion = .{
|
||||
.pos = .{
|
||||
.x = xpos,
|
||||
.y = ypos,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}.callback;
|
||||
self.window.setCursorPosCallback(mouse_motion_callback);
|
||||
|
||||
const mouse_button_callback = struct {
|
||||
fn callback(window: glfw.Window, button: glfw.mouse_button.MouseButton, action: glfw.Action, mods: glfw.Mods) void {
|
||||
const pf = (window.getUserPointer(UserPtr) orelse unreachable).self;
|
||||
const cursor_pos = pf.window.getCursorPos();
|
||||
const mouse_button_event = MouseButtonEvent{
|
||||
.button = toMachButton(button),
|
||||
.pos = .{ .x = cursor_pos.xpos, .y = cursor_pos.ypos },
|
||||
.mods = toMachMods(mods),
|
||||
};
|
||||
switch (action) {
|
||||
.press => pf.pushEvent(.{ .mouse_press = mouse_button_event }),
|
||||
.release => pf.pushEvent(.{
|
||||
.mouse_release = mouse_button_event,
|
||||
}),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
}.callback;
|
||||
self.window.setMouseButtonCallback(mouse_button_callback);
|
||||
|
||||
const scroll_callback = struct {
|
||||
fn callback(window: glfw.Window, xoffset: f64, yoffset: f64) void {
|
||||
const pf = (window.getUserPointer(UserPtr) orelse unreachable).self;
|
||||
pf.pushEvent(.{
|
||||
.mouse_scroll = .{
|
||||
.xoffset = @floatCast(f32, xoffset),
|
||||
.yoffset = @floatCast(f32, yoffset),
|
||||
},
|
||||
});
|
||||
}
|
||||
}.callback;
|
||||
self.window.setScrollCallback(scroll_callback);
|
||||
|
||||
const focus_callback = struct {
|
||||
fn callback(window: glfw.Window, focused: bool) void {
|
||||
const pf = (window.getUserPointer(UserPtr) orelse unreachable).self;
|
||||
pf.pushEvent(if (focused) .focus_gained else .focus_lost);
|
||||
}
|
||||
}.callback;
|
||||
self.window.setFocusCallback(focus_callback);
|
||||
|
||||
const framebuffer_size_callback = struct {
|
||||
fn callback(window: glfw.Window, _: u32, _: u32) void {
|
||||
const pf = (window.getUserPointer(UserPtr) orelse unreachable).self;
|
||||
pf.frame_buffer_resized = true;
|
||||
}
|
||||
}.callback;
|
||||
self.window.setFramebufferSizeCallback(framebuffer_size_callback);
|
||||
}
|
||||
|
||||
fn pushEvent(self: *Core, event: Event) void {
|
||||
const node = self.allocator.create(EventNode) catch unreachable;
|
||||
node.* = .{ .data = event };
|
||||
self.events.append(node);
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Core) void {
|
||||
for (self.cursors) |glfw_cursor| {
|
||||
if (glfw_cursor) |cur| {
|
||||
cur.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
while (self.events.popFirst()) |ev| {
|
||||
self.allocator.destroy(ev);
|
||||
}
|
||||
|
||||
if (builtin.os.tag == .linux and
|
||||
self.linux_gamemode != null and
|
||||
self.linux_gamemode.?)
|
||||
deinitLinuxGamemode();
|
||||
}
|
||||
|
||||
pub fn hasEvent(self: *Core) bool {
|
||||
return self.events.first != null;
|
||||
}
|
||||
|
||||
pub fn pollEvents(self: *Core) ?Event {
|
||||
if (self.wait_timeout > 0.0) {
|
||||
if (self.wait_timeout == std.math.inf(f64)) {
|
||||
// Wait for an event
|
||||
glfw.waitEvents();
|
||||
} else {
|
||||
// Wait for an event with a timeout
|
||||
glfw.waitEventsTimeout(self.wait_timeout);
|
||||
}
|
||||
} else {
|
||||
// Don't wait for events
|
||||
glfw.pollEvents();
|
||||
}
|
||||
|
||||
glfw.getErrorCode() catch |err| switch (err) {
|
||||
error.PlatformError => std.log.err("glfw: failed to poll events", .{}),
|
||||
error.InvalidValue => unreachable,
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
if (self.frame_buffer_resized) blk: {
|
||||
self.frame_buffer_resized = false;
|
||||
|
||||
const framebuffer_size = self.window.getFramebufferSize();
|
||||
glfw.getErrorCode() catch break :blk;
|
||||
|
||||
if (framebuffer_size.width != 0 and framebuffer_size.height != 0) {
|
||||
self.swap_chain_desc.width = framebuffer_size.width;
|
||||
self.swap_chain_desc.height = framebuffer_size.height;
|
||||
self.swap_chain = self.gpu_device.createSwapChain(self.surface, &self.swap_chain_desc);
|
||||
self.pushEvent(.{
|
||||
.framebuffer_resize = .{
|
||||
.width = framebuffer_size.width,
|
||||
.height = framebuffer_size.height,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (self.window.shouldClose()) {
|
||||
self.pushEvent(.close);
|
||||
}
|
||||
|
||||
if (self.events.popFirst()) |n| {
|
||||
const data = n.data;
|
||||
self.allocator.destroy(n);
|
||||
return data;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn shouldClose(self: *Core) bool {
|
||||
return self.window.shouldClose();
|
||||
}
|
||||
|
||||
pub fn framebufferSize(self: *Core) Size {
|
||||
const framebuffer_size = self.window.getFramebufferSize();
|
||||
return .{
|
||||
.width = framebuffer_size.width,
|
||||
.height = framebuffer_size.height,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn setWaitTimeout(self: *Core, timeout: f64) void {
|
||||
self.wait_timeout = timeout;
|
||||
}
|
||||
|
||||
pub fn setTitle(self: *Core, title: [:0]const u8) void {
|
||||
self.window.setTitle(title);
|
||||
}
|
||||
|
||||
pub fn setDisplayMode(self: *Core, mode: DisplayMode, monitor_index: ?usize) !void {
|
||||
switch (mode) {
|
||||
.windowed => {
|
||||
try self.window.setMonitor(
|
||||
null,
|
||||
@intCast(i32, self.last_pos.x),
|
||||
@intCast(i32, self.last_pos.y),
|
||||
self.last_size.width,
|
||||
self.last_size.height,
|
||||
null,
|
||||
);
|
||||
},
|
||||
.fullscreen => {
|
||||
if (try self.displayMode() == .windowed) {
|
||||
self.last_size = try self.window.getSize();
|
||||
self.last_pos = try self.window.getPos();
|
||||
}
|
||||
|
||||
const monitor = blk: {
|
||||
if (monitor_index) |i| {
|
||||
const monitor_list = try glfw.Monitor.getAll(self.allocator);
|
||||
defer self.allocator.free(monitor_list);
|
||||
break :blk monitor_list[i];
|
||||
}
|
||||
break :blk glfw.Monitor.getPrimary();
|
||||
};
|
||||
|
||||
const video_mode = try monitor.?.getVideoMode();
|
||||
try self.window.setMonitor(monitor, 0, 0, video_mode.getWidth(), video_mode.getHeight(), null);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn displayMode(self: *Core) DisplayMode {
|
||||
if (self.window.getMonitor()) |_| {
|
||||
return .fullscreen;
|
||||
} else {
|
||||
return .windowed;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setBorder(self: *Core, value: bool) void {
|
||||
self.window.setAttrib(.decorated, value);
|
||||
}
|
||||
|
||||
pub fn border(self: *Core) !bool {
|
||||
const decorated = try self.window.getAttrib(.decorated);
|
||||
return decorated == 1;
|
||||
}
|
||||
|
||||
pub fn setHeadless(self: *Core, value: bool) void {
|
||||
if (value) {
|
||||
self.window.hide();
|
||||
} else {
|
||||
self.window.show();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn headless(self: *Core) bool {
|
||||
const visible = self.window.getAttrib(.visible);
|
||||
return visible == 0;
|
||||
}
|
||||
|
||||
pub fn setVSync(self: *Core, mode: VSyncMode) void {
|
||||
const framebuffer_size = self.framebufferSize();
|
||||
self.swap_chain_desc.present_mode = switch (mode) {
|
||||
.none => .immediate,
|
||||
.double => .fifo,
|
||||
.triple => .mailbox,
|
||||
};
|
||||
self.swap_chain_desc.width = framebuffer_size.width;
|
||||
self.swap_chain_desc.height = framebuffer_size.height;
|
||||
self.swap_chain = self.gpu_device.createSwapChain(self.surface, &self.swap_chain_desc);
|
||||
}
|
||||
|
||||
pub fn vsync(self: *Core) VSyncMode {
|
||||
return switch (self.swap_chain_desc.present_mode) {
|
||||
.immediate => .none,
|
||||
.fifo => .double,
|
||||
.mailbox => .triple,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn setSize(self: *Core, value: Size) void {
|
||||
self.window.setSize(.{
|
||||
.width = value.width,
|
||||
.height = value.height,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn size(self: *Core) Size {
|
||||
const window_size = self.window.getSize();
|
||||
return .{ .width = window_size.width, .height = window_size.height };
|
||||
}
|
||||
|
||||
pub fn setSizeLimit(self: *Core, limit: SizeLimit) void {
|
||||
self.window.setSizeLimits(
|
||||
.{ .width = limit.min.width, .height = limit.min.height },
|
||||
.{ .width = limit.max.width, .height = limit.max.height },
|
||||
);
|
||||
self.size_limit = limit;
|
||||
}
|
||||
|
||||
pub fn sizeLimit(self: *Core) SizeLimit {
|
||||
return self.size_limit;
|
||||
}
|
||||
|
||||
pub fn setCursorMode(self: *Core, mode: CursorMode) void {
|
||||
const glfw_mode: glfw.Window.InputModeCursor = switch (mode) {
|
||||
.normal => .normal,
|
||||
.hidden => .hidden,
|
||||
.disabled => .disabled,
|
||||
};
|
||||
self.window.setInputModeCursor(glfw_mode);
|
||||
}
|
||||
|
||||
pub fn cursorMode(self: *Core) CursorMode {
|
||||
const glfw_mode = self.window.getInputModeCursor();
|
||||
return switch (glfw_mode) {
|
||||
.normal => .normal,
|
||||
.hidden => .hidden,
|
||||
.disabled => .disabled,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn setCursorShape(self: *Core, cursor: CursorShape) void {
|
||||
// Try to create glfw standard cursor, but could fail. In the future
|
||||
// we hope to provide custom backup images for these.
|
||||
// See https://github.com/hexops/mach/pull/352 for more info
|
||||
|
||||
const enum_int = @enumToInt(cursor);
|
||||
const tried = self.cursors_tried[enum_int];
|
||||
if (!tried) {
|
||||
self.cursors_tried[enum_int] = true;
|
||||
self.cursors[enum_int] = switch (cursor) {
|
||||
.arrow => glfw.Cursor.createStandard(.arrow) catch null,
|
||||
.ibeam => glfw.Cursor.createStandard(.ibeam) catch null,
|
||||
.crosshair => glfw.Cursor.createStandard(.crosshair) catch null,
|
||||
.pointing_hand => glfw.Cursor.createStandard(.pointing_hand) catch null,
|
||||
.resize_ew => glfw.Cursor.createStandard(.resize_ew) catch null,
|
||||
.resize_ns => glfw.Cursor.createStandard(.resize_ns) catch null,
|
||||
.resize_nwse => glfw.Cursor.createStandard(.resize_nwse) catch null,
|
||||
.resize_nesw => glfw.Cursor.createStandard(.resize_nesw) catch null,
|
||||
.resize_all => glfw.Cursor.createStandard(.resize_all) catch null,
|
||||
.not_allowed => glfw.Cursor.createStandard(.not_allowed) catch null,
|
||||
};
|
||||
}
|
||||
|
||||
if (self.cursors[enum_int]) |cur| {
|
||||
self.window.setCursor(cur);
|
||||
} else {
|
||||
// TODO: In the future we shouldn't hit this because we'll provide backup
|
||||
// custom cursors.
|
||||
// See https://github.com/hexops/mach/pull/352 for more info
|
||||
std.log.warn("mach: setCursorShape: {s} not yet supported\n", .{@tagName(cursor)});
|
||||
}
|
||||
|
||||
self.current_cursor = cursor;
|
||||
}
|
||||
|
||||
pub fn cursorShape(self: *Core) CursorShape {
|
||||
return self.current_cursor;
|
||||
}
|
||||
|
||||
pub fn adapter(self: *Core) *gpu.Adapter {
|
||||
return self.gpu_adapter;
|
||||
}
|
||||
|
||||
pub fn device(self: *Core) *gpu.Device {
|
||||
return self.gpu_device;
|
||||
}
|
||||
|
||||
pub fn swapChain(self: *Core) *gpu.SwapChain {
|
||||
return self.swap_chain;
|
||||
}
|
||||
|
||||
pub fn descriptor(self: *Core) gpu.SwapChain.Descriptor {
|
||||
return self.swap_chain_desc;
|
||||
}
|
||||
|
||||
fn toMachButton(button: glfw.mouse_button.MouseButton) MouseButton {
|
||||
return switch (button) {
|
||||
.left => .left,
|
||||
.right => .right,
|
||||
.middle => .middle,
|
||||
.four => .four,
|
||||
.five => .five,
|
||||
.six => .six,
|
||||
.seven => .seven,
|
||||
.eight => .eight,
|
||||
};
|
||||
}
|
||||
|
||||
fn toMachKey(key: glfw.Key) Key {
|
||||
return switch (key) {
|
||||
.a => .a,
|
||||
.b => .b,
|
||||
.c => .c,
|
||||
.d => .d,
|
||||
.e => .e,
|
||||
.f => .f,
|
||||
.g => .g,
|
||||
.h => .h,
|
||||
.i => .i,
|
||||
.j => .j,
|
||||
.k => .k,
|
||||
.l => .l,
|
||||
.m => .m,
|
||||
.n => .n,
|
||||
.o => .o,
|
||||
.p => .p,
|
||||
.q => .q,
|
||||
.r => .r,
|
||||
.s => .s,
|
||||
.t => .t,
|
||||
.u => .u,
|
||||
.v => .v,
|
||||
.w => .w,
|
||||
.x => .x,
|
||||
.y => .y,
|
||||
.z => .z,
|
||||
|
||||
.zero => .zero,
|
||||
.one => .one,
|
||||
.two => .two,
|
||||
.three => .three,
|
||||
.four => .four,
|
||||
.five => .five,
|
||||
.six => .six,
|
||||
.seven => .seven,
|
||||
.eight => .eight,
|
||||
.nine => .nine,
|
||||
|
||||
.F1 => .f1,
|
||||
.F2 => .f2,
|
||||
.F3 => .f3,
|
||||
.F4 => .f4,
|
||||
.F5 => .f5,
|
||||
.F6 => .f6,
|
||||
.F7 => .f7,
|
||||
.F8 => .f8,
|
||||
.F9 => .f9,
|
||||
.F10 => .f10,
|
||||
.F11 => .f11,
|
||||
.F12 => .f12,
|
||||
.F13 => .f13,
|
||||
.F14 => .f14,
|
||||
.F15 => .f15,
|
||||
.F16 => .f16,
|
||||
.F17 => .f17,
|
||||
.F18 => .f18,
|
||||
.F19 => .f19,
|
||||
.F20 => .f20,
|
||||
.F21 => .f21,
|
||||
.F22 => .f22,
|
||||
.F23 => .f23,
|
||||
.F24 => .f24,
|
||||
.F25 => .f25,
|
||||
|
||||
.kp_divide => .kp_divide,
|
||||
.kp_multiply => .kp_multiply,
|
||||
.kp_subtract => .kp_subtract,
|
||||
.kp_add => .kp_add,
|
||||
.kp_0 => .kp_0,
|
||||
.kp_1 => .kp_1,
|
||||
.kp_2 => .kp_2,
|
||||
.kp_3 => .kp_3,
|
||||
.kp_4 => .kp_4,
|
||||
.kp_5 => .kp_5,
|
||||
.kp_6 => .kp_6,
|
||||
.kp_7 => .kp_7,
|
||||
.kp_8 => .kp_8,
|
||||
.kp_9 => .kp_9,
|
||||
.kp_decimal => .kp_decimal,
|
||||
.kp_equal => .kp_equal,
|
||||
.kp_enter => .kp_enter,
|
||||
|
||||
.enter => .enter,
|
||||
.escape => .escape,
|
||||
.tab => .tab,
|
||||
.left_shift => .left_shift,
|
||||
.right_shift => .right_shift,
|
||||
.left_control => .left_control,
|
||||
.right_control => .right_control,
|
||||
.left_alt => .left_alt,
|
||||
.right_alt => .right_alt,
|
||||
.left_super => .left_super,
|
||||
.right_super => .right_super,
|
||||
.menu => .menu,
|
||||
.num_lock => .num_lock,
|
||||
.caps_lock => .caps_lock,
|
||||
.print_screen => .print,
|
||||
.scroll_lock => .scroll_lock,
|
||||
.pause => .pause,
|
||||
.delete => .delete,
|
||||
.home => .home,
|
||||
.end => .end,
|
||||
.page_up => .page_up,
|
||||
.page_down => .page_down,
|
||||
.insert => .insert,
|
||||
.left => .left,
|
||||
.right => .right,
|
||||
.up => .up,
|
||||
.down => .down,
|
||||
.backspace => .backspace,
|
||||
.space => .space,
|
||||
.minus => .minus,
|
||||
.equal => .equal,
|
||||
.left_bracket => .left_bracket,
|
||||
.right_bracket => .right_bracket,
|
||||
.backslash => .backslash,
|
||||
.semicolon => .semicolon,
|
||||
.apostrophe => .apostrophe,
|
||||
.comma => .comma,
|
||||
.period => .period,
|
||||
.slash => .slash,
|
||||
.grave_accent => .grave,
|
||||
|
||||
.world_1 => .unknown,
|
||||
.world_2 => .unknown,
|
||||
.unknown => .unknown,
|
||||
};
|
||||
}
|
||||
|
||||
fn toMachMods(mods: glfw.Mods) KeyMods {
|
||||
return .{
|
||||
.shift = mods.shift,
|
||||
.control = mods.control,
|
||||
.alt = mods.alt,
|
||||
.super = mods.super,
|
||||
.caps_lock = mods.caps_lock,
|
||||
.num_lock = mods.num_lock,
|
||||
};
|
||||
}
|
||||
|
||||
/// Default GLFW error handling callback
|
||||
fn errorCallback(error_code: glfw.ErrorCode, description: [:0]const u8) void {
|
||||
std.log.err("glfw: {}: {s}\n", .{ error_code, description });
|
||||
}
|
||||
|
||||
fn getEnvVarOwned(allocator: std.mem.Allocator, key: []const u8) error{ OutOfMemory, InvalidUtf8 }!?[]u8 {
|
||||
return std.process.getEnvVarOwned(allocator, key) catch |err| switch (err) {
|
||||
error.EnvironmentVariableNotFound => @as(?[]u8, null),
|
||||
else => |e| e,
|
||||
};
|
||||
}
|
||||
|
||||
/// Check if gamemode should be activated
|
||||
fn activateGamemode(allocator: std.mem.Allocator) error{ OutOfMemory, InvalidUtf8 }!bool {
|
||||
if (try getEnvVarOwned(allocator, "MACH_USE_GAMEMODE")) |env| {
|
||||
defer allocator.free(env);
|
||||
return !(std.ascii.eqlIgnoreCase(env, "off") or std.ascii.eqlIgnoreCase(env, "false"));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
fn initLinuxGamemode() bool {
|
||||
const gamemode = @import("gamemode");
|
||||
gamemode.requestStart() catch |err| {
|
||||
if (!std.mem.containsAtLeast(u8, gamemode.errorString(), 1, "dlopen failed"))
|
||||
std.log.err("Gamemode error {} -> {s}", .{ err, gamemode.errorString() });
|
||||
return false;
|
||||
};
|
||||
std.log.info("Gamemode activated", .{});
|
||||
return true;
|
||||
}
|
||||
|
||||
fn deinitLinuxGamemode() void {
|
||||
const gamemode = @import("gamemode");
|
||||
gamemode.requestEnd() catch |err| {
|
||||
std.log.err("Gamemode error {} -> {s}", .{ err, gamemode.errorString() });
|
||||
};
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
const std = @import("std");
|
||||
const gpu = @import("gpu");
|
||||
const App = @import("app").App;
|
||||
const util = @import("util.zig");
|
||||
|
||||
pub const GPUInterface = gpu.dawn.Interface;
|
||||
pub const scope_levels = if (@hasDecl(App, "scope_levels")) App.scope_levels else [0]std.log.ScopeLevel{};
|
||||
pub const log_level = if (@hasDecl(App, "log_level")) App.log_level else std.log.default_level;
|
||||
pub fn main() !void {
|
||||
gpu.Impl.init();
|
||||
_ = gpu.Export(GPUInterface);
|
||||
|
||||
var app: App = undefined;
|
||||
try app.init();
|
||||
defer app.deinit();
|
||||
|
||||
while (true) {
|
||||
const pool = try util.AutoReleasePool.init();
|
||||
defer util.AutoReleasePool.release(pool);
|
||||
if (try app.update()) return;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
// Extracted from `zig translate-c tmp.c` with `#include <objc/message.h>` in the file.
|
||||
pub const SEL = opaque {};
|
||||
pub const Class = opaque {};
|
||||
|
||||
pub extern fn sel_getUid(str: [*c]const u8) ?*SEL;
|
||||
pub extern fn objc_getClass(name: [*c]const u8) ?*Class;
|
||||
pub extern fn objc_msgSend() void;
|
||||
|
|
@ -1,187 +0,0 @@
|
|||
const std = @import("std");
|
||||
|
||||
const glfw = @import("glfw");
|
||||
const gpu = @import("gpu");
|
||||
const objc = @import("objc_message.zig");
|
||||
|
||||
pub inline fn printUnhandledErrorCallback(_: void, typ: gpu.ErrorType, message: [*:0]const u8) void {
|
||||
switch (typ) {
|
||||
.validation => std.log.err("gpu: validation error: {s}\n", .{message}),
|
||||
.out_of_memory => std.log.err("gpu: out of memory: {s}\n", .{message}),
|
||||
.device_lost => std.log.err("gpu: device lost: {s}\n", .{message}),
|
||||
.unknown => std.log.err("gpu: unknown error: {s}\n", .{message}),
|
||||
else => unreachable,
|
||||
}
|
||||
std.os.exit(1);
|
||||
}
|
||||
|
||||
fn getEnvVarOwned(allocator: std.mem.Allocator, key: []const u8) error{ OutOfMemory, InvalidUtf8 }!?[]u8 {
|
||||
return std.process.getEnvVarOwned(allocator, key) catch |err| switch (err) {
|
||||
error.EnvironmentVariableNotFound => @as(?[]u8, null),
|
||||
else => |e| e,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn detectBackendType(allocator: std.mem.Allocator) !gpu.BackendType {
|
||||
const MACH_GPU_BACKEND = try getEnvVarOwned(allocator, "MACH_GPU_BACKEND");
|
||||
if (MACH_GPU_BACKEND) |backend| {
|
||||
defer allocator.free(backend);
|
||||
if (std.ascii.eqlIgnoreCase(backend, "null")) return .null;
|
||||
if (std.ascii.eqlIgnoreCase(backend, "d3d11")) return .d3d11;
|
||||
if (std.ascii.eqlIgnoreCase(backend, "d3d12")) return .d3d12;
|
||||
if (std.ascii.eqlIgnoreCase(backend, "metal")) return .metal;
|
||||
if (std.ascii.eqlIgnoreCase(backend, "vulkan")) return .vulkan;
|
||||
if (std.ascii.eqlIgnoreCase(backend, "opengl")) return .opengl;
|
||||
if (std.ascii.eqlIgnoreCase(backend, "opengles")) return .opengles;
|
||||
@panic("unknown MACH_GPU_BACKEND type");
|
||||
}
|
||||
|
||||
const target = @import("builtin").target;
|
||||
if (target.isDarwin()) return .metal;
|
||||
if (target.os.tag == .windows) return .d3d12;
|
||||
return .vulkan;
|
||||
}
|
||||
|
||||
pub const RequestAdapterResponse = struct {
|
||||
status: gpu.RequestAdapterStatus,
|
||||
adapter: *gpu.Adapter,
|
||||
message: ?[*:0]const u8,
|
||||
};
|
||||
|
||||
pub inline fn requestAdapterCallback(
|
||||
context: *RequestAdapterResponse,
|
||||
status: gpu.RequestAdapterStatus,
|
||||
adapter: *gpu.Adapter,
|
||||
message: ?[*:0]const u8,
|
||||
) void {
|
||||
context.* = RequestAdapterResponse{
|
||||
.status = status,
|
||||
.adapter = adapter,
|
||||
.message = message,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn glfwWindowHintsForBackend(backend: gpu.BackendType) glfw.Window.Hints {
|
||||
return switch (backend) {
|
||||
.opengl => .{
|
||||
// Ask for OpenGL 4.4 which is what the GL backend requires for compute shaders and
|
||||
// texture views.
|
||||
.context_version_major = 4,
|
||||
.context_version_minor = 4,
|
||||
.opengl_forward_compat = true,
|
||||
.opengl_profile = .opengl_core_profile,
|
||||
},
|
||||
.opengles => .{
|
||||
.context_version_major = 3,
|
||||
.context_version_minor = 1,
|
||||
.client_api = .opengl_es_api,
|
||||
.context_creation_api = .egl_context_api,
|
||||
},
|
||||
else => .{
|
||||
// Without this GLFW will initialize a GL context on the window, which prevents using
|
||||
// the window with other APIs (by crashing in weird ways).
|
||||
.client_api = .no_api,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn detectGLFWOptions() glfw.BackendOptions {
|
||||
const target = @import("builtin").target;
|
||||
if (target.isDarwin()) return .{ .cocoa = true };
|
||||
return switch (target.os.tag) {
|
||||
.windows => .{ .win32 = true },
|
||||
.linux => .{ .x11 = true, .wayland = true },
|
||||
else => .{},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn createSurfaceForWindow(
|
||||
instance: *gpu.Instance,
|
||||
window: glfw.Window,
|
||||
comptime glfw_options: glfw.BackendOptions,
|
||||
) *gpu.Surface {
|
||||
const glfw_native = glfw.Native(glfw_options);
|
||||
const extension = if (glfw_options.win32) gpu.Surface.Descriptor.NextInChain{
|
||||
.from_windows_hwnd = &.{
|
||||
.hinstance = std.os.windows.kernel32.GetModuleHandleW(null).?,
|
||||
.hwnd = glfw_native.getWin32Window(window),
|
||||
},
|
||||
} else if (glfw_options.x11) gpu.Surface.Descriptor.NextInChain{
|
||||
.from_xlib_window = &.{
|
||||
.display = glfw_native.getX11Display(),
|
||||
.window = glfw_native.getX11Window(window),
|
||||
},
|
||||
} else if (glfw_options.wayland) gpu.Surface.Descriptor.NextInChain{
|
||||
.from_wayland_surface = &.{
|
||||
.display = glfw_native.getWaylandDisplay(),
|
||||
.surface = glfw_native.getWaylandWindow(window),
|
||||
},
|
||||
} else if (glfw_options.cocoa) blk: {
|
||||
const ns_window = glfw_native.getCocoaWindow(window);
|
||||
const ns_view = msgSend(ns_window, "contentView", .{}, *anyopaque); // [nsWindow contentView]
|
||||
|
||||
// Create a CAMetalLayer that covers the whole window that will be passed to CreateSurface.
|
||||
msgSend(ns_view, "setWantsLayer:", .{true}, void); // [view setWantsLayer:YES]
|
||||
const layer = msgSend(objc.objc_getClass("CAMetalLayer"), "layer", .{}, ?*anyopaque); // [CAMetalLayer layer]
|
||||
if (layer == null) @panic("failed to create Metal layer");
|
||||
msgSend(ns_view, "setLayer:", .{layer.?}, void); // [view setLayer:layer]
|
||||
|
||||
// Use retina if the window was created with retina support.
|
||||
const scale_factor = msgSend(ns_window, "backingScaleFactor", .{}, f64); // [ns_window backingScaleFactor]
|
||||
msgSend(layer.?, "setContentsScale:", .{scale_factor}, void); // [layer setContentsScale:scale_factor]
|
||||
|
||||
break :blk gpu.Surface.Descriptor.NextInChain{ .from_metal_layer = &.{ .layer = layer.? } };
|
||||
} else unreachable;
|
||||
|
||||
return instance.createSurface(&gpu.Surface.Descriptor{
|
||||
.next_in_chain = extension,
|
||||
});
|
||||
}
|
||||
|
||||
pub const AutoReleasePool = if (!@import("builtin").target.isDarwin()) opaque {
|
||||
pub fn init() error{OutOfMemory}!?*AutoReleasePool {
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn release(pool: ?*AutoReleasePool) void {
|
||||
_ = pool;
|
||||
return;
|
||||
}
|
||||
} else opaque {
|
||||
pub fn init() error{OutOfMemory}!?*AutoReleasePool {
|
||||
// pool = [NSAutoreleasePool alloc];
|
||||
var pool = msgSend(objc.objc_getClass("NSAutoreleasePool"), "alloc", .{}, ?*AutoReleasePool);
|
||||
if (pool == null) return error.OutOfMemory;
|
||||
|
||||
// pool = [pool init];
|
||||
pool = msgSend(pool, "init", .{}, ?*AutoReleasePool);
|
||||
if (pool == null) unreachable;
|
||||
|
||||
return pool;
|
||||
}
|
||||
|
||||
pub fn release(pool: ?*AutoReleasePool) void {
|
||||
// [pool release];
|
||||
msgSend(pool, "release", .{}, void);
|
||||
}
|
||||
};
|
||||
|
||||
// Borrowed from https://github.com/hazeycode/zig-objcrt
|
||||
pub fn msgSend(obj: anytype, sel_name: [:0]const u8, args: anytype, comptime ReturnType: type) ReturnType {
|
||||
const args_meta = @typeInfo(@TypeOf(args)).Struct.fields;
|
||||
|
||||
const FnType = switch (args_meta.len) {
|
||||
0 => *const fn (@TypeOf(obj), ?*objc.SEL) callconv(.C) ReturnType,
|
||||
1 => *const fn (@TypeOf(obj), ?*objc.SEL, args_meta[0].type) callconv(.C) ReturnType,
|
||||
2 => *const fn (@TypeOf(obj), ?*objc.SEL, args_meta[0].type, args_meta[1].type) callconv(.C) ReturnType,
|
||||
3 => *const fn (@TypeOf(obj), ?*objc.SEL, args_meta[0].type, args_meta[1].type, args_meta[2].type) callconv(.C) ReturnType,
|
||||
4 => *const fn (@TypeOf(obj), ?*objc.SEL, args_meta[0].type, args_meta[1].type, args_meta[2].type, args_meta[3].type) callconv(.C) ReturnType,
|
||||
else => @compileError("Unsupported number of args"),
|
||||
};
|
||||
|
||||
// NOTE: func is a var because making it const causes a compile error which I believe is a compiler bug
|
||||
var func = @ptrCast(FnType, &objc.objc_msgSend);
|
||||
const sel = objc.sel_getUid(@ptrCast([*c]const u8, sel_name));
|
||||
|
||||
return @call(.auto, func, .{ obj, sel } ++ args);
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
pub const Core = @import("wasm/Core.zig");
|
||||
pub const Timer = @import("wasm/Timer.zig");
|
||||
pub const entry = @import("wasm/entry.zig");
|
||||
|
|
@ -1,295 +0,0 @@
|
|||
const std = @import("std");
|
||||
const gpu = @import("gpu");
|
||||
const js = @import("js.zig");
|
||||
const Timer = @import("Timer.zig");
|
||||
const Options = @import("../../Core.zig").Options;
|
||||
const Event = @import("../../Core.zig").Event;
|
||||
const KeyEvent = @import("../../Core.zig").KeyEvent;
|
||||
const MouseButtonEvent = @import("../../Core.zig").MouseButtonEvent;
|
||||
const MouseButton = @import("../../Core.zig").MouseButton;
|
||||
const Size = @import("../../Core.zig").Size;
|
||||
const Position = @import("../../Core.zig").Position;
|
||||
const DisplayMode = @import("../../Core.zig").DisplayMode;
|
||||
const SizeLimit = @import("../../Core.zig").SizeLimit;
|
||||
const CursorShape = @import("../../Core.zig").CursorShape;
|
||||
const VSyncMode = @import("../../Core.zig").VSyncMode;
|
||||
const CursorMode = @import("../../Core.zig").CursorMode;
|
||||
const Key = @import("../../Core.zig").Key;
|
||||
const KeyMods = @import("../../Core.zig").KeyMods;
|
||||
|
||||
pub const Core = @This();
|
||||
|
||||
allocator: std.mem.Allocator,
|
||||
id: js.CanvasId,
|
||||
|
||||
last_cursor_position: Position,
|
||||
last_key_mods: KeyMods,
|
||||
|
||||
pub fn init(core: *Core, allocator: std.mem.Allocator, options: Options) !void {
|
||||
_ = options;
|
||||
var selector = [1]u8{0} ** 15;
|
||||
const id = js.machCanvasInit(&selector[0]);
|
||||
|
||||
core.* = Core{
|
||||
.allocator = allocator,
|
||||
.id = id,
|
||||
|
||||
// TODO initialize these properly
|
||||
.last_cursor_position = .{
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
},
|
||||
.last_key_mods = .{
|
||||
.shift = false,
|
||||
.control = false,
|
||||
.alt = false,
|
||||
.super = false,
|
||||
.caps_lock = false,
|
||||
.num_lock = false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Core) void {
|
||||
js.machCanvasDeinit(self.id);
|
||||
}
|
||||
|
||||
pub fn hasEvent(_: *Core) bool {
|
||||
return js.machHasEvent();
|
||||
}
|
||||
|
||||
pub fn pollEvents(self: *Core) ?Event {
|
||||
const event_int = js.machEventShift();
|
||||
if (event_int == -1) return null;
|
||||
|
||||
const event_type = @intToEnum(std.meta.Tag(Event), event_int);
|
||||
return switch (event_type) {
|
||||
.key_press, .key_repeat => blk: {
|
||||
const key = @intToEnum(Key, js.machEventShift());
|
||||
switch (key) {
|
||||
.left_shift, .right_shift => self.last_key_mods.shift = true,
|
||||
.left_control, .right_control => self.last_key_mods.control = true,
|
||||
.left_alt, .right_alt => self.last_key_mods.alt = true,
|
||||
.left_super, .right_super => self.last_key_mods.super = true,
|
||||
.caps_lock => self.last_key_mods.caps_lock = true,
|
||||
.num_lock => self.last_key_mods.num_lock = true,
|
||||
else => {},
|
||||
}
|
||||
break :blk switch (event_type) {
|
||||
.key_press => Event{
|
||||
.key_press = .{
|
||||
.key = key,
|
||||
.mods = self.last_key_mods,
|
||||
},
|
||||
},
|
||||
.key_repeat => Event{
|
||||
.key_repeat = .{
|
||||
.key = key,
|
||||
.mods = self.last_key_mods,
|
||||
},
|
||||
},
|
||||
else => unreachable,
|
||||
};
|
||||
},
|
||||
.key_release => blk: {
|
||||
const key = @intToEnum(Key, js.machEventShift());
|
||||
switch (key) {
|
||||
.left_shift, .right_shift => self.last_key_mods.shift = false,
|
||||
.left_control, .right_control => self.last_key_mods.control = false,
|
||||
.left_alt, .right_alt => self.last_key_mods.alt = false,
|
||||
.left_super, .right_super => self.last_key_mods.super = false,
|
||||
.caps_lock => self.last_key_mods.caps_lock = false,
|
||||
.num_lock => self.last_key_mods.num_lock = false,
|
||||
else => {},
|
||||
}
|
||||
break :blk Event{
|
||||
.key_release = .{
|
||||
.key = key,
|
||||
.mods = self.last_key_mods,
|
||||
},
|
||||
};
|
||||
},
|
||||
.mouse_motion => blk: {
|
||||
const x = @intToFloat(f64, js.machEventShift());
|
||||
const y = @intToFloat(f64, js.machEventShift());
|
||||
self.last_cursor_position = .{
|
||||
.x = x,
|
||||
.y = y,
|
||||
};
|
||||
break :blk Event{
|
||||
.mouse_motion = .{
|
||||
.pos = .{
|
||||
.x = x,
|
||||
.y = y,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
.mouse_press => Event{
|
||||
.mouse_press = .{
|
||||
.button = toMachButton(js.machEventShift()),
|
||||
.pos = self.last_cursor_position,
|
||||
.mods = self.last_key_mods,
|
||||
},
|
||||
},
|
||||
.mouse_release => Event{
|
||||
.mouse_release = .{
|
||||
.button = toMachButton(js.machEventShift()),
|
||||
.pos = self.last_cursor_position,
|
||||
.mods = self.last_key_mods,
|
||||
},
|
||||
},
|
||||
.mouse_scroll => Event{
|
||||
.mouse_scroll = .{
|
||||
.xoffset = @floatCast(f32, std.math.sign(js.machEventShiftFloat())),
|
||||
.yoffset = @floatCast(f32, std.math.sign(js.machEventShiftFloat())),
|
||||
},
|
||||
},
|
||||
.framebuffer_resize => blk: {
|
||||
const width = @intCast(u32, js.machEventShift());
|
||||
const height = @intCast(u32, js.machEventShift());
|
||||
const pixel_ratio = @intCast(u32, js.machEventShift());
|
||||
break :blk Event{
|
||||
.framebuffer_resize = .{
|
||||
.width = width * pixel_ratio,
|
||||
.height = height * pixel_ratio,
|
||||
},
|
||||
};
|
||||
},
|
||||
.focus_gained => Event.focus_gained,
|
||||
.focus_lost => Event.focus_lost,
|
||||
else => null,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn framebufferSize(self: *Core) Size {
|
||||
return .{
|
||||
.width = js.machCanvasFramebufferWidth(self.id),
|
||||
.height = js.machCanvasFramebufferHeight(self.id),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn setWaitTimeout(_: *Core, timeout: f64) void {
|
||||
js.machSetWaitTimeout(timeout);
|
||||
}
|
||||
|
||||
pub fn setTitle(self: *Core, title: [:0]const u8) void {
|
||||
js.machCanvasSetTitle(self.id, title.ptr, title.len);
|
||||
}
|
||||
|
||||
pub fn setDisplayMode(self: *Core, mode: DisplayMode, monitor: ?usize) void {
|
||||
_ = monitor;
|
||||
js.machCanvasSetDisplayMode(self.id, @enumToInt(mode));
|
||||
}
|
||||
|
||||
pub fn displayMode(self: *Core) DisplayMode {
|
||||
return @intToEnum(DisplayMode, js.machDisplayMode(self.id));
|
||||
}
|
||||
|
||||
pub fn setBorder(self: *Core, value: bool) void {
|
||||
_ = self;
|
||||
_ = value;
|
||||
}
|
||||
|
||||
pub fn border(self: *Core) bool {
|
||||
_ = self;
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn setHeadless(self: *Core, value: bool) void {
|
||||
_ = self;
|
||||
_ = value;
|
||||
}
|
||||
|
||||
pub fn headless(self: *Core) bool {
|
||||
_ = self;
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn setVSync(self: *Core, mode: VSyncMode) void {
|
||||
_ = self;
|
||||
_ = mode;
|
||||
}
|
||||
|
||||
// TODO: https://github.com/gpuweb/gpuweb/issues/1224
|
||||
pub fn vsync(self: *Core) VSyncMode {
|
||||
_ = self;
|
||||
return .double;
|
||||
}
|
||||
|
||||
pub fn setSize(self: *Core, value: Size) void {
|
||||
js.machCanvasSetSize(self.id, value.width, value.height);
|
||||
}
|
||||
|
||||
pub fn size(self: *Core) Size {
|
||||
return .{
|
||||
.width = js.machCanvasWidth(self.id),
|
||||
.height = js.machCanvasHeight(self.id),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn setSizeLimit(self: *Core, limit: SizeLimit) void {
|
||||
js.machCanvasSetSizeLimit(
|
||||
self.id,
|
||||
if (limit.min.width) |val| @intCast(i32, val) else -1,
|
||||
if (limit.min.height) |val| @intCast(i32, val) else -1,
|
||||
if (limit.max.width) |val| @intCast(i32, val) else -1,
|
||||
if (limit.max.height) |val| @intCast(i32, val) else -1,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn sizeLimit(self: *Core) SizeLimit {
|
||||
return .{
|
||||
.min = .{
|
||||
.width = js.machCanvasMinWidth(self.id),
|
||||
.height = js.machCanvasMinHeight(self.id),
|
||||
},
|
||||
.max = .{
|
||||
.width = js.machCanvasMaxWidth(self.id),
|
||||
.height = js.machCanvasMaxHeight(self.id),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn setCursorMode(self: *Core, mode: CursorMode) void {
|
||||
js.machSetCursorMode(self.id, @enumToInt(mode));
|
||||
}
|
||||
|
||||
pub fn cursorMode(self: *Core) CursorMode {
|
||||
return @intToEnum(CursorMode, js.machCursorMode(self.id));
|
||||
}
|
||||
|
||||
pub fn setCursorShape(self: *Core, shape: CursorShape) void {
|
||||
js.machSetCursorShape(self.id, @enumToInt(shape));
|
||||
}
|
||||
|
||||
pub fn cursorShape(self: *Core) CursorShape {
|
||||
return @intToEnum(CursorShape, js.machCursorShape(self.id));
|
||||
}
|
||||
|
||||
pub fn adapter(_: *Core) *gpu.Adapter {
|
||||
unreachable;
|
||||
}
|
||||
|
||||
pub fn device(_: *Core) *gpu.Device {
|
||||
unreachable;
|
||||
}
|
||||
|
||||
pub fn swapChain(_: *Core) *gpu.SwapChain {
|
||||
unreachable;
|
||||
}
|
||||
|
||||
pub fn descriptor(_: *Core) gpu.SwapChain.Descriptor {
|
||||
unreachable;
|
||||
}
|
||||
|
||||
fn toMachButton(button: i32) MouseButton {
|
||||
return switch (button) {
|
||||
0 => .left,
|
||||
1 => .middle,
|
||||
2 => .right,
|
||||
3 => .four,
|
||||
4 => .five,
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
const std = @import("std");
|
||||
const js = @import("js.zig");
|
||||
|
||||
pub const Timer = @This();
|
||||
|
||||
initial: f64 = undefined,
|
||||
|
||||
pub fn start() !Timer {
|
||||
return Timer{ .initial = js.machPerfNow() };
|
||||
}
|
||||
|
||||
pub fn read(timer: *Timer) u64 {
|
||||
return (js.machPerfNow() - timer.initial) * std.time.ns_per_ms;
|
||||
}
|
||||
|
||||
pub fn reset(timer: *Timer) void {
|
||||
timer.initial = js.machPerfNow();
|
||||
}
|
||||
|
||||
pub fn lap(timer: *Timer) u64 {
|
||||
const now = js.machPerfNow();
|
||||
const initial = timer.initial;
|
||||
timer.initial = now;
|
||||
return @floatToInt(u64, now - initial) * std.time.ns_per_ms;
|
||||
}
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
const std = @import("std");
|
||||
const gpu = @import("gpu");
|
||||
const App = @import("app").App;
|
||||
const js = @import("js.zig");
|
||||
|
||||
pub const GPUInterface = gpu.StubInterface;
|
||||
pub const log_level = if (@hasDecl(App, "log_level")) App.log_level else std.log.default_level;
|
||||
pub const scope_levels = if (@hasDecl(App, "scope_levels")) App.scope_levels else [0]std.log.ScopeLevel{};
|
||||
|
||||
var app: App = undefined;
|
||||
export fn wasmInit() void {
|
||||
app.init() catch unreachable;
|
||||
}
|
||||
|
||||
export fn wasmUpdate() bool {
|
||||
return app.update() catch unreachable;
|
||||
}
|
||||
|
||||
export fn wasmDeinit() void {
|
||||
app.deinit();
|
||||
}
|
||||
|
||||
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 const std_options = struct {
|
||||
pub fn logFn(
|
||||
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, error_return_trace: ?*std.builtin.StackTrace, ret_addr: ?usize) noreturn {
|
||||
_ = error_return_trace;
|
||||
_ = ret_addr;
|
||||
js.machPanic(msg.ptr, msg.len);
|
||||
unreachable;
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
pub const CanvasId = u32;
|
||||
|
||||
pub extern "mach" fn machLogWrite(str: [*]const u8, len: u32) void;
|
||||
pub extern "mach" fn machLogFlush() void;
|
||||
pub extern "mach" fn machPanic(str: [*]const u8, len: u32) void;
|
||||
|
||||
pub extern "mach" fn machCanvasInit(selector_id: *u8) CanvasId;
|
||||
pub extern "mach" fn machCanvasDeinit(canvas: CanvasId) void;
|
||||
pub extern "mach" fn machCanvasFramebufferWidth(canvas: CanvasId) u32;
|
||||
pub extern "mach" fn machCanvasFramebufferHeight(canvas: CanvasId) u32;
|
||||
pub extern "mach" fn machCanvasSetTitle(canvas: CanvasId, title: [*]const u8, len: u32) void;
|
||||
pub extern "mach" fn machCanvasSetDisplayMode(canvas: CanvasId, mode: u32) void;
|
||||
pub extern "mach" fn machCanvasDisplayMode(canvas: CanvasId) u32;
|
||||
pub extern "mach" fn machCanvasSetBorder(canvas: CanvasId, value: bool) void;
|
||||
pub extern "mach" fn machCanvasBorder(canvas: CanvasId) bool;
|
||||
pub extern "mach" fn machCanvasSetHeadless(canvas: CanvasId, value: bool) void;
|
||||
pub extern "mach" fn machCanvasHeadless(canvas: CanvasId) bool;
|
||||
pub extern "mach" fn machCanvasSetVsync(canvas: CanvasId, mode: u32) void;
|
||||
pub extern "mach" fn machCanvasVsync(canvas: CanvasId) u32;
|
||||
pub extern "mach" fn machCanvasSetSize(canvas: CanvasId, width: u32, height: u32) void;
|
||||
pub extern "mach" fn machCanvasWidth(canvas: CanvasId) u32;
|
||||
pub extern "mach" fn machCanvasHeight(canvas: CanvasId) u32;
|
||||
pub extern "mach" fn machCanvasSetSizeLimit(canvas: CanvasId, min_width: i32, min_height: i32, max_width: i32, max_height: i32) void;
|
||||
pub extern "mach" fn machCanvasMinWidth(canvas: CanvasId) u32;
|
||||
pub extern "mach" fn machCanvasMinHeight(canvas: CanvasId) u32;
|
||||
pub extern "mach" fn machCanvasMaxWidth(canvas: CanvasId) u32;
|
||||
pub extern "mach" fn machCanvasMaxHeight(canvas: CanvasId) u32;
|
||||
pub extern "mach" fn machSetCursorMode(canvas: CanvasId, mode: u32) void;
|
||||
pub extern "mach" fn machCursorMode(canvas: CanvasId) u32;
|
||||
pub extern "mach" fn machSetCursorShape(canvas: CanvasId, shape: u32) void;
|
||||
pub extern "mach" fn machCursorShape(canvas: CanvasId) u32;
|
||||
|
||||
pub extern "mach" fn machShouldClose() bool;
|
||||
pub extern "mach" fn machHasEvent() bool;
|
||||
pub extern "mach" fn machSetWaitTimeout(timeout: f64) void;
|
||||
pub extern "mach" fn machEventShift() i32;
|
||||
pub extern "mach" fn machEventShiftFloat() f64;
|
||||
pub extern "mach" fn machChangeShift() u32;
|
||||
|
||||
pub extern "mach" fn machPerfNow() f64;
|
||||
|
|
@ -1,511 +0,0 @@
|
|||
const text_decoder = new TextDecoder();
|
||||
const text_encoder = new TextEncoder();
|
||||
|
||||
const mach = {
|
||||
canvases: [],
|
||||
wasm: undefined,
|
||||
observer: undefined,
|
||||
events: [],
|
||||
changes: [],
|
||||
wait_timeout: 0,
|
||||
log_buf: "",
|
||||
|
||||
init(wasm) {
|
||||
mach.wasm = wasm;
|
||||
mach.observer = new MutationObserver((mutables) => {
|
||||
mutables.forEach((mutable) => {
|
||||
mach.canvases.forEach((canvas) => {
|
||||
if (mutable.target == canvas) {
|
||||
if (mutable.attributeName === "width" ||
|
||||
mutable.attributeName === "height" ||
|
||||
mutable.attributeName === "style") {
|
||||
mutable.target.dispatchEvent(new Event("mach-canvas-resize"));
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
getString(str, len) {
|
||||
const memory = mach.wasm.exports.memory.buffer;
|
||||
return text_decoder.decode(new Uint8Array(memory, str, len));
|
||||
},
|
||||
|
||||
setString(str, buf) {
|
||||
const memory = mach.wasm.exports.memory.buffer;
|
||||
const strbuf = text_encoder.encode(str);
|
||||
const outbuf = new Uint8Array(memory, buf, strbuf.length);
|
||||
for (let i = 0; i < strbuf.length; i += 1) {
|
||||
outbuf[i] = strbuf[i];
|
||||
}
|
||||
},
|
||||
|
||||
machLogWrite(str, len) {
|
||||
mach.log_buf += mach.getString(str, len);
|
||||
},
|
||||
|
||||
machLogFlush() {
|
||||
console.log(log_buf);
|
||||
mach.log_buf = "";
|
||||
},
|
||||
|
||||
machPanic(str, len) {
|
||||
throw Error(mach.getString(str, len));
|
||||
},
|
||||
|
||||
machCanvasInit(id) {
|
||||
let canvas = document.createElement("canvas");
|
||||
canvas.id = "#mach-canvas-" + mach.canvases.length;
|
||||
canvas.style.border = "1px solid";
|
||||
canvas.style.position = "absolute";
|
||||
canvas.style.display = "block";
|
||||
canvas.tabIndex = 1;
|
||||
|
||||
mach.observer.observe(canvas, { attributes: true });
|
||||
|
||||
mach.setString(canvas.id, id);
|
||||
|
||||
canvas.addEventListener("contextmenu", (ev) => ev.preventDefault());
|
||||
|
||||
canvas.addEventListener("keydown", (ev) => {
|
||||
if (ev.repeat) {
|
||||
mach.events.push(...[EventCode.key_repeat, convertKeyCode(ev.code)]);
|
||||
} else {
|
||||
mach.events.push(...[EventCode.key_press, convertKeyCode(ev.code)]);
|
||||
}
|
||||
});
|
||||
|
||||
canvas.addEventListener("keyup", (ev) => {
|
||||
mach.events.push(...[EventCode.key_release, convertKeyCode(ev.code)]);
|
||||
});
|
||||
|
||||
canvas.addEventListener("mousemove", (ev) => {
|
||||
mach.events.push(...[EventCode.mouse_motion, ev.clientX, ev.clientY]);
|
||||
});
|
||||
|
||||
canvas.addEventListener("mousedown", (ev) => {
|
||||
mach.events.push(...[EventCode.mouse_press, ev.button]);
|
||||
});
|
||||
|
||||
canvas.addEventListener("mouseup", (ev) => {
|
||||
mach.events.push(...[EventCode.mouse_release, ev.button]);
|
||||
});
|
||||
|
||||
canvas.addEventListener("wheel", (ev) => {
|
||||
mach.events.push(...[EventCode.mouse_scroll, ev.deltaX, ev.deltaY]);
|
||||
});
|
||||
|
||||
canvas.addEventListener("mach-canvas-resize", (ev) => {
|
||||
const cv_index = mach.canvases.findIndex((el) => el === ev.currentTarget);
|
||||
const cv = mach.canvases[cv_index];
|
||||
mach.events.push(...[EventCode.framebuffer_resize, cv.width, cv.height, window.devicePixelRatio]);
|
||||
});
|
||||
|
||||
canvas.addEventListener("focus", (ev) => {
|
||||
mach.events.push(...[EventCode.focus_gained]);
|
||||
});
|
||||
|
||||
canvas.addEventListener("blur", (ev) => {
|
||||
mach.events.push(...[EventCode.focus_lost]);
|
||||
});
|
||||
|
||||
document.body.appendChild(canvas);
|
||||
return mach.canvases.push(canvas) - 1;
|
||||
},
|
||||
|
||||
machCanvasDeinit(canvas) {
|
||||
if (mach.canvases[canvas] != undefined) {
|
||||
mach.canvases.splice(canvas, 1);
|
||||
}
|
||||
},
|
||||
|
||||
machCanvasFramebufferWidth(canvas) {
|
||||
const cv = mach.canvases[canvas];
|
||||
return cv.width;
|
||||
},
|
||||
|
||||
machCanvasFramebufferHeight(canvas) {
|
||||
const cv = mach.canvases[canvas];
|
||||
return cv.height;
|
||||
},
|
||||
|
||||
machCanvasSetTitle(canvas, title, len) {
|
||||
// TODO
|
||||
},
|
||||
|
||||
machCanvasSetDisplayMode(canvas, mode) {
|
||||
const cv = mach.canvases[canvas];
|
||||
switch (mode) {
|
||||
case DisplayMode.windowed:
|
||||
document.exitFullscreen();
|
||||
break;
|
||||
case DisplayMode.fullscreen:
|
||||
cv.requestFullscreen();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
machCanvasDisplayMode(canvas) {
|
||||
if (mach.canvases[canvas].fullscreenElement == null) {
|
||||
return DisplayMode.windowed;
|
||||
} else {
|
||||
return DisplayMode.fullscreen;
|
||||
}
|
||||
},
|
||||
|
||||
machCanvasSetBorder(canvas, value) {
|
||||
// TODO
|
||||
},
|
||||
|
||||
machCanvasBorder(canvas) {
|
||||
// TODO
|
||||
},
|
||||
|
||||
machCanvasSetHeadless(canvas, value) {
|
||||
// TODO
|
||||
},
|
||||
|
||||
machCanvasHeadless(canvas) {
|
||||
// TODO
|
||||
},
|
||||
|
||||
machCanvasSetVSync(canvas, mode) {
|
||||
// TODO
|
||||
},
|
||||
|
||||
machCanvasVSync(canvas) {
|
||||
// TODO
|
||||
},
|
||||
|
||||
machCanvasSetSize(canvas, width, height) {
|
||||
const cv = mach.canvases[canvas];
|
||||
if (width > 0 && height > 0) {
|
||||
cv.style.width = width + "px";
|
||||
cv.style.height = height + "px";
|
||||
cv.width = Math.floor(width * window.devicePixelRatio);
|
||||
cv.height = Math.floor(height * window.devicePixelRatio);
|
||||
}
|
||||
},
|
||||
|
||||
machCanvasWidth(canvas) {
|
||||
const cv = mach.canvases[canvas];
|
||||
return cv.width / window.devicePixelRatio;
|
||||
},
|
||||
|
||||
machCanvasHeight(canvas) {
|
||||
const cv = mach.canvases[canvas];
|
||||
return cv.height / window.devicePixelRatio;
|
||||
},
|
||||
|
||||
machCanvasSetSizeLimit(canvas, min_width, min_height, max_width, max_height) {
|
||||
const cv = mach.canvases[canvas];
|
||||
if (min_width == -1) {
|
||||
cv.style.minWidth = "inherit"
|
||||
} else {
|
||||
cv.style.minWidth = min_width + "px";
|
||||
}
|
||||
if (min_width == -1) {
|
||||
cv.style.minHeight = "inherit"
|
||||
} else {
|
||||
cv.style.minHeight = min_height + "px";
|
||||
}
|
||||
if (min_width == -1) {
|
||||
cv.style.maxWidth = "inherit"
|
||||
} else {
|
||||
cv.style.maxWidth = max_width + "px";
|
||||
}
|
||||
if (min_width == -1) {
|
||||
cv.style.maxHeight = "inherit"
|
||||
} else {
|
||||
cv.style.maxHeight = max_height + "px";
|
||||
}
|
||||
},
|
||||
|
||||
machCanvasMinWidth(canvas) {
|
||||
const cv = mach.canvases[canvas];
|
||||
return cv.style.minWidth;
|
||||
},
|
||||
|
||||
machCanvasMinHeight(canvas) {
|
||||
const cv = mach.canvases[canvas];
|
||||
return cv.style.minHeight;
|
||||
},
|
||||
|
||||
machCanvasMaxWidth(canvas) {
|
||||
const cv = mach.canvases[canvas];
|
||||
return cv.style.maxWidth;
|
||||
},
|
||||
|
||||
machCanvasMaxHeight(canvas) {
|
||||
const cv = mach.canvases[canvas];
|
||||
return cv.style.maxHeight;
|
||||
},
|
||||
|
||||
machSetCursorMode(canvas, mode) {
|
||||
const cv = mach.canvases[canvas];
|
||||
switch (mode) {
|
||||
case CursorMode.normal:
|
||||
cv.style.cursor = 'default';
|
||||
break;
|
||||
case CursorMode.hidden:
|
||||
cv.style.cursor = 'none';
|
||||
break;
|
||||
case CursorMode.hidden:
|
||||
cv.style.cursor = 'none';
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
machCursorMode(canvas) {
|
||||
switch (mach.canvases[canvas].style.cursor) {
|
||||
case 'none': return CursorMode.hidden;
|
||||
default: return CursorMode.normal;
|
||||
}
|
||||
},
|
||||
|
||||
machSetCursorShape(canvas, shape) {
|
||||
const cv = mach.canvases[canvas];
|
||||
switch (shape) {
|
||||
case CursorShape.arrow:
|
||||
cv.style.cursor = 'default';
|
||||
break;
|
||||
case CursorShape.ibeam:
|
||||
cv.style.cursor = 'text';
|
||||
break;
|
||||
case CursorShape.crosshair:
|
||||
cv.style.cursor = 'crosshair';
|
||||
break;
|
||||
case CursorShape.pointing_hand:
|
||||
cv.style.cursor = 'pointer';
|
||||
break;
|
||||
case CursorShape.resize_ew:
|
||||
cv.style.cursor = 'ew-resize';
|
||||
break;
|
||||
case CursorShape.resize_ns:
|
||||
cv.style.cursor = 'ns-resize';
|
||||
break;
|
||||
case CursorShape.resize_nwse:
|
||||
cv.style.cursor = 'nwse-resize';
|
||||
break;
|
||||
case CursorShape.resize_nesw:
|
||||
cv.style.cursor = 'nesw-resize';
|
||||
break;
|
||||
case CursorShape.resize_all:
|
||||
cv.style.cursor = 'move';
|
||||
break;
|
||||
case CursorShape.not_allowed:
|
||||
cv.style.cursor = 'not-allowed';
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
machCursorShape(canvas) {
|
||||
switch (mach.canvases[canvas].style.cursor) {
|
||||
case 'default': return CursorShape.arrow;
|
||||
case 'text': return CursorShape.ibeam;
|
||||
case 'crosshair': return CursorShape.crosshair;
|
||||
case 'pointer': return CursorShape.pointing_hand;
|
||||
case 'ew-resize': return CursorShape.resize_ew;
|
||||
case 'ns-resize': return CursorShape.resize_ns;
|
||||
case 'nwse-resize': return CursorShape.resize_nwse;
|
||||
case 'nesw-resize': return CursorShape.resize_nesw;
|
||||
case 'move': return CursorShape.resize_all;
|
||||
case 'not-allowed': return CursorShape.not_allowed;
|
||||
}
|
||||
},
|
||||
|
||||
machSetWaitTimeout(timeout) {
|
||||
mach.wait_timeout = timeout;
|
||||
},
|
||||
|
||||
machHasEvent() {
|
||||
return mach.events.length > 0;
|
||||
},
|
||||
|
||||
machEventShift() {
|
||||
if (mach.machHasEvent())
|
||||
return mach.events.shift();
|
||||
|
||||
return -1;
|
||||
},
|
||||
|
||||
machEventShiftFloat() {
|
||||
return mach.machEventShift();
|
||||
},
|
||||
|
||||
machPerfNow() {
|
||||
return performance.now();
|
||||
},
|
||||
};
|
||||
|
||||
function convertKeyCode(code) {
|
||||
const k = Key[code];
|
||||
if (k != undefined)
|
||||
return k;
|
||||
return 118; // Unknown
|
||||
}
|
||||
|
||||
const Key = {
|
||||
KeyA: 0,
|
||||
KeyB: 1,
|
||||
KeyC: 2,
|
||||
KeyD: 3,
|
||||
KeyE: 4,
|
||||
KeyF: 5,
|
||||
KeyG: 6,
|
||||
KeyH: 7,
|
||||
KeyI: 8,
|
||||
KeyJ: 9,
|
||||
KeyK: 10,
|
||||
KeyL: 11,
|
||||
KeyM: 12,
|
||||
KeyN: 13,
|
||||
KeyO: 14,
|
||||
KeyP: 15,
|
||||
KeyQ: 16,
|
||||
KeyR: 17,
|
||||
KeyS: 18,
|
||||
KeyT: 19,
|
||||
KeyU: 20,
|
||||
KeyV: 21,
|
||||
KeyW: 22,
|
||||
KeyX: 23,
|
||||
KeyY: 24,
|
||||
KeyZ: 25,
|
||||
Digit0: 26,
|
||||
Digit1: 27,
|
||||
Digit2: 28,
|
||||
Digit3: 29,
|
||||
Digit4: 30,
|
||||
Digit5: 31,
|
||||
Digit6: 32,
|
||||
Digit7: 33,
|
||||
Digit8: 34,
|
||||
Digit9: 35,
|
||||
F1: 36,
|
||||
F2: 37,
|
||||
F3: 38,
|
||||
F4: 39,
|
||||
F5: 40,
|
||||
F6: 41,
|
||||
F7: 42,
|
||||
F8: 43,
|
||||
F9: 44,
|
||||
F10: 45,
|
||||
F11: 46,
|
||||
F12: 47,
|
||||
F13: 48,
|
||||
F14: 49,
|
||||
F15: 50,
|
||||
F16: 51,
|
||||
F17: 52,
|
||||
F18: 53,
|
||||
F19: 54,
|
||||
F20: 55,
|
||||
F21: 56,
|
||||
F22: 57,
|
||||
F23: 58,
|
||||
F24: 59,
|
||||
F25: 60,
|
||||
NumpadDivide: 61,
|
||||
NumpadMultiply: 62,
|
||||
NumpadSubtract: 63,
|
||||
NumpadAdd: 64,
|
||||
Numpad0: 65,
|
||||
Numpad1: 66,
|
||||
Numpad2: 67,
|
||||
Numpad3: 68,
|
||||
Numpad4: 69,
|
||||
Numpad5: 70,
|
||||
Numpad6: 71,
|
||||
Numpad7: 72,
|
||||
Numpad8: 73,
|
||||
Numpad9: 74,
|
||||
NumpadDecimal: 75,
|
||||
NumpadEqual: 76,
|
||||
NumpadEnter: 77,
|
||||
Enter: 78,
|
||||
Escape: 79,
|
||||
Tab: 80,
|
||||
ShiftLeft: 81,
|
||||
ShiftRight: 82,
|
||||
ControlLeft: 83,
|
||||
ControlRight: 84,
|
||||
AltLeft: 85,
|
||||
AltRight: 86,
|
||||
OSLeft: 87,
|
||||
MetaLeft: 87,
|
||||
OSRight: 88,
|
||||
MetaRight: 88,
|
||||
ContextMenu: 89,
|
||||
NumLock: 90,
|
||||
CapsLock: 91,
|
||||
PrintScreen: 92,
|
||||
ScrollLock: 93,
|
||||
Pause: 94,
|
||||
Delete: 95,
|
||||
Home: 96,
|
||||
End: 97,
|
||||
PageUp: 98,
|
||||
PageDown: 99,
|
||||
Insert: 100,
|
||||
ArrowLeft: 101,
|
||||
ArrowRight: 102,
|
||||
ArrowUp: 103,
|
||||
ArrowDown: 104,
|
||||
Backspace: 105,
|
||||
Space: 106,
|
||||
Minus: 107,
|
||||
Equal: 108,
|
||||
BracketLeft: 109,
|
||||
BracketRight: 110,
|
||||
Backslash: 111,
|
||||
Semicolon: 112,
|
||||
Quote: 113,
|
||||
Comma: 114,
|
||||
Period: 115,
|
||||
Slash: 116,
|
||||
Backquote: 117,
|
||||
};
|
||||
|
||||
const EventCode = {
|
||||
key_press: 0,
|
||||
key_repeat: 1,
|
||||
key_release: 2,
|
||||
char_input: 3,
|
||||
mouse_motion: 4,
|
||||
mouse_press: 5,
|
||||
mouse_release: 6,
|
||||
mouse_scroll: 7,
|
||||
framebuffer_resize: 8,
|
||||
focus_gained: 9,
|
||||
focus_lost: 10,
|
||||
close: 11,
|
||||
};
|
||||
|
||||
const DisplayMode = {
|
||||
windowed: 0,
|
||||
fullscreen: 1,
|
||||
};
|
||||
|
||||
const CursorMode = {
|
||||
normal: 0,
|
||||
hidden: 1,
|
||||
disabled: 2,
|
||||
};
|
||||
|
||||
const CursorShape = {
|
||||
arrow: 0,
|
||||
ibeam: 1,
|
||||
crosshair: 2,
|
||||
pointing_hand: 3,
|
||||
resize_ew: 4,
|
||||
resize_ns: 5,
|
||||
resize_nwse: 6,
|
||||
resize_nesw: 7,
|
||||
resize_all: 8,
|
||||
not_allowed: 9,
|
||||
};
|
||||
|
||||
export { mach };
|
||||
Loading…
Add table
Add a link
Reference in a new issue