247 lines
7.6 KiB
Zig
247 lines
7.6 KiB
Zig
const std = @import("std");
|
|
const mach = @import("../main.zig");
|
|
const Core = @import("../Core.zig");
|
|
const InputState = @import("InputState.zig");
|
|
const unicode = @import("unicode.zig");
|
|
const detectBackendType = @import("common.zig").detectBackendType;
|
|
const gpu = mach.gpu;
|
|
const InitOptions = Core.InitOptions;
|
|
const Event = Core.Event;
|
|
const KeyEvent = Core.KeyEvent;
|
|
const MouseButtonEvent = Core.MouseButtonEvent;
|
|
const MouseButton = Core.MouseButton;
|
|
const Size = Core.Size;
|
|
const DisplayMode = Core.DisplayMode;
|
|
const CursorShape = Core.CursorShape;
|
|
const VSyncMode = Core.VSyncMode;
|
|
const CursorMode = Core.CursorMode;
|
|
const Position = Core.Position;
|
|
const Key = Core.Key;
|
|
const KeyMods = Core.KeyMods;
|
|
const Joystick = Core.Joystick;
|
|
const objc = @import("objc");
|
|
|
|
const log = std.log.scoped(.mach);
|
|
|
|
const EventQueue = std.fifo.LinearFifo(Event, .Dynamic);
|
|
pub const EventIterator = struct {
|
|
queue: *EventQueue,
|
|
|
|
pub inline fn next(self: *EventIterator) ?Event {
|
|
return self.queue.readItem();
|
|
}
|
|
};
|
|
|
|
pub const Darwin = @This();
|
|
|
|
allocator: std.mem.Allocator,
|
|
core: *Core,
|
|
|
|
events: EventQueue,
|
|
input_state: InputState,
|
|
// modifiers: KeyMods,
|
|
|
|
title: [:0]const u8,
|
|
display_mode: DisplayMode,
|
|
vsync_mode: VSyncMode,
|
|
cursor_mode: CursorMode,
|
|
cursor_shape: CursorShape,
|
|
border: bool,
|
|
headless: bool,
|
|
refresh_rate: u32,
|
|
size: Size,
|
|
surface_descriptor: gpu.Surface.Descriptor,
|
|
window: ?*objc.app_kit.Window,
|
|
|
|
pub fn run(comptime on_each_update_fn: anytype, args_tuple: std.meta.ArgsTuple(@TypeOf(on_each_update_fn))) noreturn {
|
|
const Args = @TypeOf(args_tuple);
|
|
const args_bytes = std.mem.asBytes(&args_tuple);
|
|
const ArgsBytes = @TypeOf(args_bytes.*);
|
|
const Helper = struct {
|
|
// TODO: port libdispatch and use it instead of doing this directly.
|
|
extern "System" fn dispatch_async(queue: *anyopaque, block: *objc.foundation.Block(fn () void)) void;
|
|
extern "System" var _dispatch_main_q: anyopaque;
|
|
pub fn cCallback(block: *objc.foundation.BlockLiteral(ArgsBytes)) callconv(.C) void {
|
|
const args: *Args = @ptrCast(&block.context);
|
|
if (@call(.auto, on_each_update_fn, args.*) catch false) {
|
|
dispatch_async(&_dispatch_main_q, block.asBlockWithSignature(fn () void));
|
|
} else {
|
|
// We copied the block when we called `setRunBlock()`, so we release it here when the looping will end.
|
|
block.release();
|
|
}
|
|
}
|
|
};
|
|
var block_literal = objc.foundation.stackBlockLiteral(Helper.cCallback, args_bytes.*, null, null);
|
|
|
|
// `NSApplicationMain()` and `UIApplicationMain()` never return, so there's no point in trying to add any kind of cleanup work here.
|
|
const ns_app = objc.app_kit.Application.sharedApplication();
|
|
const delegate = objc.mach.AppDelegate.allocInit();
|
|
delegate.setRunBlock(block_literal.asBlock().copy());
|
|
ns_app.setDelegate(@ptrCast(delegate));
|
|
_ = objc.app_kit.applicationMain(0, undefined);
|
|
|
|
unreachable;
|
|
// TODO: support UIKit.
|
|
}
|
|
|
|
// Called on the main thread
|
|
pub fn init(darwin: *Darwin, options: InitOptions) !void {
|
|
var surface_descriptor = gpu.Surface.Descriptor{};
|
|
|
|
// TODO: support UIKit.
|
|
var window: ?*objc.app_kit.Window = null;
|
|
if (!options.headless) {
|
|
const metal_descriptor = try options.allocator.create(gpu.Surface.DescriptorFromMetalLayer);
|
|
const layer = objc.quartz_core.MetalLayer.new();
|
|
defer layer.release();
|
|
metal_descriptor.* = .{
|
|
.layer = layer,
|
|
};
|
|
surface_descriptor.next_in_chain = .{ .from_metal_layer = metal_descriptor };
|
|
|
|
const screen = objc.app_kit.Screen.mainScreen();
|
|
const rect = objc.core_graphics.Rect{ // TODO: use a meaningful rect
|
|
.origin = .{ .x = 100, .y = 100 },
|
|
.size = .{ .width = 480, .height = 270 },
|
|
};
|
|
const window_style =
|
|
(if (options.display_mode == .fullscreen) objc.app_kit.WindowStyleMaskFullScreen else 0) |
|
|
(if (options.display_mode == .windowed) objc.app_kit.WindowStyleMaskTitled else 0) |
|
|
(if (options.display_mode == .windowed) objc.app_kit.WindowStyleMaskClosable else 0) |
|
|
(if (options.display_mode == .windowed) objc.app_kit.WindowStyleMaskMiniaturizable else 0) |
|
|
(if (options.display_mode == .windowed) objc.app_kit.WindowStyleMaskResizable else 0);
|
|
window = objc.app_kit.Window.alloc().initWithContentRect_styleMask_backing_defer_screen(rect, window_style, objc.app_kit.BackingStoreBuffered, true, screen);
|
|
window.?.setReleasedWhenClosed(false);
|
|
if (window.?.contentView()) |view| {
|
|
view.setLayer(@ptrCast(layer));
|
|
}
|
|
window.?.setIsVisible(true);
|
|
window.?.makeKeyAndOrderFront(null);
|
|
}
|
|
|
|
var events = EventQueue.init(options.allocator);
|
|
try events.ensureTotalCapacity(2048);
|
|
|
|
darwin.* = .{
|
|
.allocator = options.allocator,
|
|
.core = @fieldParentPtr("platform", darwin),
|
|
.events = events,
|
|
.input_state = .{},
|
|
.title = options.title,
|
|
.display_mode = options.display_mode,
|
|
.vsync_mode = .none,
|
|
.cursor_mode = .normal,
|
|
.cursor_shape = .arrow,
|
|
.border = options.border,
|
|
.headless = options.headless,
|
|
.refresh_rate = 60, // TODO: set to something meaningful
|
|
.size = options.size,
|
|
.surface_descriptor = surface_descriptor,
|
|
.window = window,
|
|
};
|
|
}
|
|
|
|
pub fn deinit(darwin: *Darwin) void {
|
|
if (darwin.window) |w| @as(*objc.foundation.ObjectProtocol, @ptrCast(w)).release();
|
|
return;
|
|
}
|
|
|
|
// Called on the main thread
|
|
pub fn update(_: *Darwin) !void {
|
|
return;
|
|
}
|
|
|
|
// May be called from any thread.
|
|
pub inline fn pollEvents(n: *Darwin) EventIterator {
|
|
return EventIterator{ .queue = &n.events };
|
|
}
|
|
|
|
// May be called from any thread.
|
|
pub fn setTitle(_: *Darwin, _: [:0]const u8) void {
|
|
return;
|
|
}
|
|
|
|
// May be called from any thread.
|
|
pub fn setDisplayMode(_: *Darwin, _: DisplayMode) void {
|
|
return;
|
|
}
|
|
|
|
// May be called from any thread.
|
|
pub fn setBorder(_: *Darwin, _: bool) void {
|
|
return;
|
|
}
|
|
|
|
// May be called from any thread.
|
|
pub fn setHeadless(_: *Darwin, _: bool) void {
|
|
return;
|
|
}
|
|
|
|
// May be called from any thread.
|
|
pub fn setVSync(_: *Darwin, _: VSyncMode) void {
|
|
return;
|
|
}
|
|
|
|
// May be called from any thread.
|
|
pub fn setSize(_: *Darwin, _: Size) void {
|
|
return;
|
|
}
|
|
|
|
// May be called from any thread.
|
|
pub fn size(_: *Darwin) Size {
|
|
return Size{ .width = 100, .height = 100 };
|
|
}
|
|
|
|
// May be called from any thread.
|
|
pub fn setCursorMode(_: *Darwin, _: CursorMode) void {
|
|
return;
|
|
}
|
|
|
|
// May be called from any thread.
|
|
pub fn setCursorShape(_: *Darwin, _: CursorShape) void {
|
|
return;
|
|
}
|
|
|
|
// May be called from any thread.
|
|
pub fn joystickPresent(_: *Darwin, _: Joystick) bool {
|
|
return false;
|
|
}
|
|
|
|
// May be called from any thread.
|
|
pub fn joystickName(_: *Darwin, _: Joystick) ?[:0]const u8 {
|
|
return null;
|
|
}
|
|
|
|
// May be called from any thread.
|
|
pub fn joystickButtons(_: *Darwin, _: Joystick) ?[]const bool {
|
|
return null;
|
|
}
|
|
|
|
// May be called from any thread.
|
|
pub fn joystickAxes(_: *Darwin, _: Joystick) ?[]const f32 {
|
|
return null;
|
|
}
|
|
|
|
// May be called from any thread.
|
|
pub fn keyPressed(_: *Darwin, _: Key) bool {
|
|
return false;
|
|
}
|
|
|
|
// May be called from any thread.
|
|
pub fn keyReleased(_: *Darwin, _: Key) bool {
|
|
return true;
|
|
}
|
|
|
|
// May be called from any thread.
|
|
pub fn mousePressed(_: *Darwin, _: MouseButton) bool {
|
|
return false;
|
|
}
|
|
|
|
// May be called from any thread.
|
|
pub fn mouseReleased(_: *Darwin, _: MouseButton) bool {
|
|
return true;
|
|
}
|
|
|
|
// May be called from any thread.
|
|
pub fn mousePosition(_: *Darwin) Position {
|
|
return Position{ .x = 0, .y = 0 };
|
|
}
|