From 4582aae6d02b05a0ab5c6f2bd4580740c3361fda Mon Sep 17 00:00:00 2001 From: foxnne Date: Sun, 8 Dec 2024 12:26:51 -0600 Subject: [PATCH 01/11] build: update `mach-objc` build.zig.zon, and small corrections to `core-transparent-window` example --- build.zig.zon | 4 ++-- examples/core-transparent-window/App.zig | 9 ++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index 51bc783384..e36c945237 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -22,8 +22,8 @@ .lazy = true, }, .mach_objc = .{ - .url = "https://pkg.machengine.org/mach-objc/0edc9de456ec90e06006211c8bf0fd72fc8ac0ad.tar.gz", - .hash = "122033028b2bac8c51706c7dcd8133e93215daac758fd7b22d6a020f898358da7cbe", + .url = "https://pkg.machengine.org/mach-objc/eb1e1eee9c02039d582f5fd9814d32e48b736ba6.tar.gz", + .hash = "12209742f139402c34a8901bfb012a748c7101bef971f0a541338d659baa345b237d", .lazy = true, }, .xcode_frameworks = .{ diff --git a/examples/core-transparent-window/App.zig b/examples/core-transparent-window/App.zig index e4fd754939..fcb20b0787 100644 --- a/examples/core-transparent-window/App.zig +++ b/examples/core-transparent-window/App.zig @@ -19,7 +19,7 @@ title_timer: mach.time.Timer, color_timer: mach.time.Timer, color_time: f32 = 0.0, flip: bool = false, -pipeline: *gpu.RenderPipeline, +pipeline: *gpu.RenderPipeline = undefined, pub fn init( core: *mach.Core, @@ -31,7 +31,7 @@ pub fn init( const window = try core.windows.new(.{ .title = "core-transparent-window", - .vsync_mode = .triple, + .vsync_mode = .double, }); // Store our render pipeline in our module's state, so we can access it later on. @@ -39,7 +39,6 @@ pub fn init( .window = window, .title_timer = try mach.time.Timer.start(), .color_timer = try mach.time.Timer.start(), - .pipeline = undefined, }; } @@ -89,7 +88,7 @@ pub fn tick(app: *App, core: *mach.Core) void { .window_open => |ev| { try setupPipeline(core, app, ev.window_id); }, - .key_press => |ev| { + .key_repeat, .key_press => |ev| { switch (ev.key) { .right => { core.windows.set(app.window, .width, core.windows.get(app.window, .width) + 10); @@ -154,7 +153,7 @@ pub fn tick(app: *App, core: *mach.Core) void { app.title_timer.reset(); // TODO(object): window-title - core.windows.set(app.window, .title, std.fmt.allocPrintZ(core.allocator, "core-custom-entrypoint [ {d}fps ] [ Input {d}hz ]", .{ core.frame.rate, core.input.rate }) catch unreachable); + core.windows.set(app.window, .title, std.fmt.allocPrintZ(core.allocator, "core-transparent-window [ {d}fps ] [ Input {d}hz ]", .{ core.frame.rate, core.input.rate }) catch unreachable); } if (app.color_time >= 4.0 or app.color_time <= 0.0) { From 27d89febb8850a0de4de4a20fc67b6bb87f9b4a4 Mon Sep 17 00:00:00 2001 From: foxnne Date: Wed, 11 Dec 2024 23:02:05 -0600 Subject: [PATCH 02/11] core: darwin: More input callbacks, correct framebuffer/window sizes, core has responsibility of swapchain --- src/Core.zig | 239 ++++++-------------------------------------- src/core/Darwin.zig | 148 ++++++++++++++++++++++++++- 2 files changed, 178 insertions(+), 209 deletions(-) diff --git a/src/Core.zig b/src/Core.zig index 28a0c19d26..214f4ce35b 100644 --- a/src/Core.zig +++ b/src/Core.zig @@ -209,8 +209,8 @@ pub fn initWindow(core: *Core, window_id: mach.ObjectID) !void { .label = "main swap chain", .usage = core_window.swap_chain_usage, .format = .bgra8_unorm, - .width = core_window.width, - .height = core_window.height, + .width = core_window.framebuffer_width, + .height = core_window.framebuffer_height, .present_mode = switch (core_window.vsync_mode) { .none => .immediate, .double => .fifo, @@ -218,10 +218,6 @@ pub fn initWindow(core: *Core, window_id: mach.ObjectID) !void { }, }; core_window.swap_chain = core_window.device.createSwapChain(core_window.surface, &core_window.swap_chain_descriptor); - core_window.framebuffer_format = core_window.swap_chain_descriptor.format; - core_window.framebuffer_width = core_window.swap_chain_descriptor.width; - core_window.framebuffer_height = core_window.swap_chain_descriptor.height; - core.pushEvent(.{ .window_open = .{ .window_id = window_id } }); } @@ -236,6 +232,32 @@ pub fn tick(core: *Core, core_mod: mach.Mod(Core)) !void { core_mod.call(.presentFrame); } +pub fn presentFrame(core: *Core, core_mod: mach.Mod(Core)) !void { + var windows = core.windows.slice(); + while (windows.next()) |window_id| { + var core_window = core.windows.getValue(window_id); + defer core.windows.setValueRaw(window_id, core_window); + + mach.sysgpu.Impl.deviceTick(core_window.device); + + core_window.swap_chain.present(); + } + + // Record to frame rate frequency monitor that a frame was finished. + core.frame.tick(); + + switch (core.state) { + .running => {}, + .exiting => { + core.state = .deinitializing; + core_mod.run(core.on_exit.?); + core_mod.call(.deinit); + }, + .deinitializing => {}, + .exited => @panic("application not running"), + } +} + pub fn main(core: *Core, core_mod: mach.Mod(Core)) !void { if (core.on_tick == null) @panic("core.on_tick callback must be set"); if (core.on_exit == null) @panic("core.on_exit callback must be set"); @@ -286,11 +308,14 @@ fn platform_update_callback(core: *Core, core_mod: mach.Mod(Core)) !bool { core_mod.run(core.on_tick.?); core_mod.call(.presentFrame); - //core_mod.call(.processWindowUpdates); return core.state != .exited; } +pub fn exit(core: *Core) void { + core.state = .exiting; +} + pub fn deinit(core: *Core) !void { core.state = .exited; @@ -369,206 +394,6 @@ pub fn mousePosition(core: *@This()) Position { return core.input_state.mouse_position; } -// TODO(object) -// /// Set refresh rate synchronization mode. Default `.triple` -// /// -// /// Calling this function also implicitly calls setFrameRateLimit for you: -// /// ``` -// /// .none => setFrameRateLimit(0) // unlimited -// /// .double => setFrameRateLimit(0) // unlimited -// /// .triple => setFrameRateLimit(2 * max_monitor_refresh_rate) -// /// ``` -// pub inline fn setVSync(core: *@This(), mode: VSyncMode) void { -// return core.platform.setVSync(mode); -// } - -// TODO(object) -// /// Returns refresh rate synchronization mode. -// pub inline fn vsync(core: *@This()) VSyncMode { -// return core.platform.vsync_mode; -// } - -// TODO(object) -// /// Sets the frame rate limit. Default 0 (unlimited) -// /// -// /// This is applied *in addition* to the vsync mode. -// pub inline fn setFrameRateLimit(core: *@This(), limit: u32) void { -// core.frame.target = limit; -// } - -// TODO(object) -// /// Returns the frame rate limit, or zero if unlimited. -// pub inline fn frameRateLimit(core: *@This()) u32 { -// return core.frame.target; -// } - -// TODO(object) -// /// Set the window size, in subpixel units. -// pub inline fn setSize(core: *@This(), value: Size) void { -// return core.platform.setSize(value); -// } - -// TODO(object) -// /// Returns the window size, in subpixel units. -// pub inline fn size(core: *@This()) Size { -// return core.platform.size; -// } - -// TODO(object) -// pub inline fn setCursorMode(core: *@This(), mode: CursorMode) void { -// return core.platform.setCursorMode(mode); -// } - -// TODO(object) -// pub inline fn cursorMode(core: *@This()) CursorMode { -// return core.platform.cursorMode(); -// } - -// TODO(object) -// pub inline fn setCursorShape(core: *@This(), cursor: CursorShape) void { -// return core.platform.setCursorShape(cursor); -// } - -// TODO(object) -// pub inline fn cursorShape(core: *@This()) CursorShape { -// return core.platform.cursorShape(); -// } - -// TODO(object) -// /// Sets the minimum target frequency of the input handling thread. -// /// -// /// Input handling (the main thread) runs at a variable frequency. The thread blocks until there are -// /// input events available, or until it needs to unblock in order to achieve the minimum target -// /// frequency which is your collaboration point of opportunity with the main thread. -// /// -// /// For example, by default (`setInputFrequency(1)`) mach-core will aim to invoke `updateMainThread` -// /// at least once per second (but potentially much more, e.g. once per every mouse movement or -// /// keyboard button press.) If you were to increase the input frequency to say 60hz e.g. -// /// `setInputFrequency(60)` then mach-core will aim to invoke your `updateMainThread` 60 times per -// /// second. -// /// -// /// An input frequency of zero implies unlimited, in which case the main thread will busy-wait. -// /// -// /// # Multithreaded mach-core behavior -// /// -// /// On some platforms, mach-core is able to handle input and rendering independently for -// /// improved performance and responsiveness. -// /// -// /// | Platform | Threading | -// /// |----------|-----------------| -// /// | Desktop | Multi threaded | -// /// | Browser | Single threaded | -// /// | Mobile | TBD | -// /// -// /// On single-threaded platforms, `update` and the (optional) `updateMainThread` callback are -// /// invoked in sequence, one after the other, on the same thread. -// /// -// /// On multi-threaded platforms, `init` and `deinit` are called on the main thread, while `update` -// /// is called on a separate rendering thread. The (optional) `updateMainThread` callback can be -// /// used in cases where you must run a function on the main OS thread (such as to open a native -// /// file dialog on macOS, since many system GUI APIs must be run on the main OS thread.) It is -// /// advised you do not use this callback to run any code except when absolutely neccessary, as -// /// it is in direct contention with input handling. -// /// -// /// APIs which are not accessible from a specific thread are declared as such, otherwise can be -// /// called from any thread as they are internally synchronized. -// pub inline fn setInputFrequency(core: *@This(), input_frequency: u32) void { -// core.input.target = input_frequency; -// } - -// TODO(object) -// /// Returns the input frequency, or zero if unlimited (busy-waiting mode) -// pub inline fn inputFrequency(core: *@This()) u32 { -// return core.input.target; -// } - -// TODO(object) -// /// Returns the actual number of frames rendered (`update` calls that returned) in the last second. -// /// -// /// This is updated once per second. -// pub inline fn frameRate(core: *@This()) u32 { -// return core.frame.rate; -// } - -// TODO(object) -// /// Returns the actual number of input thread iterations in the last second. See setInputFrequency -// /// for what this means. -// /// -// /// This is updated once per second. -// pub inline fn inputRate(core: *@This()) u32 { -// return core.input.rate; -// } - -// TODO(object) -// /// Returns the underlying native NSWindow pointer -// /// -// /// May only be called on macOS. -// pub fn nativeWindowCocoa(core: *@This()) *anyopaque { -// return core.platform.nativeWindowCocoa(); -// } - -// TODO(object) -// /// Returns the underlying native Windows' HWND pointer -// /// -// /// May only be called on Windows. -// pub fn nativeWindowWin32(core: *@This()) std.os.windows.HWND { -// return core.platform.nativeWindowWin32(); -// } - -pub fn presentFrame(core: *Core, core_mod: mach.Mod(Core)) !void { - var windows = core.windows.slice(); - while (windows.next()) |window_id| { - var core_window = core.windows.getValue(window_id); - defer core.windows.setValueRaw(window_id, core_window); - - mach.sysgpu.Impl.deviceTick(core_window.device); - - core_window.swap_chain.present(); - - // Update swapchain for the next frame - if (core_window.swap_chain_update.isSet()) blk: { - core_window.swap_chain_update.reset(); - - switch (core_window.vsync_mode) { - .triple => core.frame.target = 2 * core_window.refresh_rate, - else => core.frame.target = 0, - } - - if (core_window.width == 0 or core_window.height == 0) break :blk; - - core_window.swap_chain_descriptor.present_mode = switch (core_window.vsync_mode) { - .none => .immediate, - .double => .fifo, - .triple => .mailbox, - }; - - core_window.swap_chain_descriptor.width = core_window.width; - core_window.swap_chain_descriptor.height = core_window.height; - core_window.swap_chain.release(); - - core_window.swap_chain = core_window.device.createSwapChain(core_window.surface, &core_window.swap_chain_descriptor); - } - } - - // Record to frame rate frequency monitor that a frame was finished. - core.frame.tick(); - - switch (core.state) { - .running => {}, - .exiting => { - core.state = .deinitializing; - core_mod.run(core.on_exit.?); - core_mod.call(.deinit); - }, - .deinitializing => {}, - .exited => @panic("application not running"), - } -} - -pub fn exit(core: *Core) void { - core.state = .exiting; -} - inline fn requestAdapterCallback( context: *RequestAdapterResponse, status: gpu.RequestAdapterStatus, diff --git a/src/core/Darwin.zig b/src/core/Darwin.zig index f489e5ae6d..8a6114f4be 100644 --- a/src/core/Darwin.zig +++ b/src/core/Darwin.zig @@ -165,9 +165,19 @@ fn initWindow( screen, ); if (native_window_opt) |native_window| { + const framebuffer_scale: f32 = @floatCast(native_window.backingScaleFactor()); + const window_width: f32 = @floatFromInt(core_window.width); + const window_height: f32 = @floatFromInt(core_window.height); + + core_window.framebuffer_width = @intFromFloat(window_width * framebuffer_scale); + core_window.framebuffer_height = @intFromFloat(window_height * framebuffer_scale); + native_window.setReleasedWhenClosed(false); var view = objc.mach.View.allocInit(); + + // initWithFrame is overridden in our MACHView, which creates a tracking area for mouse tracking + view = view.initWithFrame(rect); view.setLayer(@ptrCast(layer)); const context = try core.allocator.create(Context); @@ -190,6 +200,46 @@ fn initWindow( null, ); view.setBlock_keyUp(keyUp.asBlock().copy()); + + var flagsChanged = objc.foundation.stackBlockLiteral( + ViewCallbacks.flagsChanged, + context, + null, + null, + ); + view.setBlock_flagsChanged(flagsChanged.asBlock().copy()); + + var mouseMoved = objc.foundation.stackBlockLiteral( + ViewCallbacks.mouseMoved, + context, + null, + null, + ); + view.setBlock_mouseMoved(mouseMoved.asBlock().copy()); + + var mouseDown = objc.foundation.stackBlockLiteral( + ViewCallbacks.mouseDown, + context, + null, + null, + ); + view.setBlock_mouseDown(mouseDown.asBlock().copy()); + + var mouseUp = objc.foundation.stackBlockLiteral( + ViewCallbacks.mouseUp, + context, + null, + null, + ); + view.setBlock_mouseUp(mouseUp.asBlock().copy()); + + var scrollWheel = objc.foundation.stackBlockLiteral( + ViewCallbacks.scrollWheel, + context, + null, + null, + ); + view.setBlock_scrollWheel(scrollWheel.asBlock().copy()); } native_window.setContentView(@ptrCast(view)); native_window.center(); @@ -261,12 +311,23 @@ const WindowDelegateCallbacks = struct { const native_window: *objc.app_kit.Window = native.window; const frame = native_window.frame(); - const content_rect = native_window.contentRectForFrameRect(frame); core_window.width = @intFromFloat(content_rect.size.width); core_window.height = @intFromFloat(content_rect.size.height); - core_window.swap_chain_update.set(); + + const framebuffer_scale: f32 = @floatCast(native_window.backingScaleFactor()); + const window_width: f32 = @floatFromInt(core_window.width); + const window_height: f32 = @floatFromInt(core_window.height); + + core_window.framebuffer_width = @intFromFloat(window_width * framebuffer_scale); + core_window.framebuffer_height = @intFromFloat(window_height * framebuffer_scale); + + core_window.swap_chain_descriptor.width = core_window.framebuffer_width; + core_window.swap_chain_descriptor.height = core_window.framebuffer_height; + core_window.swap_chain.release(); + + core_window.swap_chain = core_window.device.createSwapChain(core_window.surface, &core_window.swap_chain_descriptor); } core.windows.setValueRaw(block.context.window_id, core_window); @@ -288,6 +349,62 @@ const WindowDelegateCallbacks = struct { }; const ViewCallbacks = struct { + pub fn mouseMoved(block: *objc.foundation.BlockLiteral(*Context), event: *objc.app_kit.Event) callconv(.C) void { + const core: *Core = block.context.core; + const window_id = block.context.window_id; + + const mouse_location = event.locationInWindow(); + + const window_height: f32 = @floatFromInt(core.windows.get(window_id, .height)); + + core.pushEvent(.{ .mouse_motion = .{ + .window_id = window_id, + .pos = .{ .x = mouse_location.x, .y = window_height - mouse_location.y }, + } }); + } + + pub fn mouseDown(block: *objc.foundation.BlockLiteral(*Context), event: *objc.app_kit.Event) callconv(.C) void { + const core: *Core = block.context.core; + const window_id = block.context.window_id; + + core.pushEvent(.{ .mouse_press = .{ + .window_id = window_id, + .button = @enumFromInt(event.buttonNumber()), + .pos = .{ .x = event.locationInWindow().x, .y = event.locationInWindow().y }, + .mods = machModifierFromModifierFlag(event.modifierFlags()), + } }); + } + pub fn mouseUp(block: *objc.foundation.BlockLiteral(*Context), event: *objc.app_kit.Event) callconv(.C) void { + const core: *Core = block.context.core; + const window_id = block.context.window_id; + + core.pushEvent(.{ .mouse_release = .{ + .window_id = window_id, + .button = @enumFromInt(event.buttonNumber()), + .pos = .{ .x = event.locationInWindow().x, .y = event.locationInWindow().y }, + .mods = machModifierFromModifierFlag(event.modifierFlags()), + } }); + } + + pub fn scrollWheel(block: *objc.foundation.BlockLiteral(*Context), event: *objc.app_kit.Event) callconv(.C) void { + const core: *Core = block.context.core; + const window_id = block.context.window_id; + + var scroll_delta_x = event.scrollingDeltaX(); + var scroll_delta_y = event.scrollingDeltaY(); + + if (event.hasPreciseScrollingDeltas()) { + scroll_delta_x *= 0.1; + scroll_delta_y *= 0.1; + } + + core.pushEvent(.{ .mouse_scroll = .{ + .window_id = window_id, + .xoffset = @floatCast(scroll_delta_x), + .yoffset = @floatCast(scroll_delta_y), + } }); + } + pub fn keyDown(block: *objc.foundation.BlockLiteral(*Context), event: *objc.app_kit.Event) callconv(.C) void { const core: *Core = block.context.core; const window_id = block.context.window_id; @@ -316,6 +433,33 @@ const ViewCallbacks = struct { .mods = machModifierFromModifierFlag(event.modifierFlags()), } }); } + + pub fn flagsChanged(block: *objc.foundation.BlockLiteral(*Context), event: *objc.app_kit.Event) callconv(.C) void { + const core: *Core = block.context.core; + const window_id = block.context.window_id; + + const key = machKeyFromKeycode(event.keyCode()); + const mods = machModifierFromModifierFlag(event.modifierFlags()); + + const key_flag = switch (key) { + .left_shift, .right_shift => objc.app_kit.EventModifierFlagShift, + .left_control, .right_control => objc.app_kit.EventModifierFlagControl, + .left_alt, .right_alt => objc.app_kit.EventModifierFlagOption, + .left_super, .right_super => objc.app_kit.EventModifierFlagCommand, + .caps_lock => objc.app_kit.EventModifierFlagCapsLock, + else => 0, + }; + + if (event.modifierFlags() & key_flag != 0) { + if (core.input_state.isKeyPressed(key)) { + core.pushEvent(.{ .key_release = .{ .window_id = window_id, .key = key, .mods = mods } }); + } else { + core.pushEvent(.{ .key_press = .{ .window_id = window_id, .key = key, .mods = mods } }); + } + } else { + core.pushEvent(.{ .key_release = .{ .window_id = window_id, .key = key, .mods = mods } }); + } + } }; fn machModifierFromModifierFlag(modifier_flag: usize) Core.KeyMods { From 147a5e23263fe13969f3e4849762aec4065c553a Mon Sep 17 00:00:00 2001 From: foxnne Date: Thu, 12 Dec 2024 09:43:12 -0600 Subject: [PATCH 03/11] core: darwin: Add ability to detect and fire `magnify` event which is triggered by pinch to zoom on a trackpad --- src/Core.zig | 4 ++++ src/core/Darwin.zig | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/Core.zig b/src/Core.zig index 214f4ce35b..9f61db964a 100644 --- a/src/Core.zig +++ b/src/Core.zig @@ -522,6 +522,10 @@ pub const Event = union(enum) { window_open: struct { window_id: mach.ObjectID, }, + magnify: struct { + window_id: mach.ObjectID, + magnification: f32, + }, focus_gained: struct { window_id: mach.ObjectID, }, diff --git a/src/core/Darwin.zig b/src/core/Darwin.zig index 8a6114f4be..05e2bd2283 100644 --- a/src/core/Darwin.zig +++ b/src/core/Darwin.zig @@ -209,6 +209,14 @@ fn initWindow( ); view.setBlock_flagsChanged(flagsChanged.asBlock().copy()); + var magnify = objc.foundation.stackBlockLiteral( + ViewCallbacks.magnify, + context, + null, + null, + ); + view.setBlock_magnify(magnify.asBlock().copy()); + var mouseMoved = objc.foundation.stackBlockLiteral( ViewCallbacks.mouseMoved, context, @@ -405,6 +413,17 @@ const ViewCallbacks = struct { } }); } + // This is currently only supported on macOS using a trackpad + pub fn magnify(block: *objc.foundation.BlockLiteral(*Context), event: *objc.app_kit.Event) callconv(.C) void { + const core: *Core = block.context.core; + const window_id = block.context.window_id; + + core.pushEvent(.{ .magnify = .{ + .window_id = window_id, + .magnification = @floatCast(event.magnification()), + } }); + } + pub fn keyDown(block: *objc.foundation.BlockLiteral(*Context), event: *objc.app_kit.Event) callconv(.C) void { const core: *Core = block.context.core; const window_id = block.context.window_id; From e849334849f6751b9d1d376b41d8bb8e020e85ff Mon Sep 17 00:00:00 2001 From: foxnne Date: Thu, 12 Dec 2024 11:10:42 -0600 Subject: [PATCH 04/11] core: `window.color` -> `window.transparent`, `window.decorated`, `window.decoration_color` --- examples/core-transparent-window/App.zig | 11 ++- src/Core.zig | 13 +++- src/core/Darwin.zig | 85 ++++++++---------------- 3 files changed, 47 insertions(+), 62 deletions(-) diff --git a/examples/core-transparent-window/App.zig b/examples/core-transparent-window/App.zig index fcb20b0787..9160d526ad 100644 --- a/examples/core-transparent-window/App.zig +++ b/examples/core-transparent-window/App.zig @@ -32,6 +32,7 @@ pub fn init( const window = try core.windows.new(.{ .title = "core-transparent-window", .vsync_mode = .double, + .transparent = true, }); // Store our render pipeline in our module's state, so we can access it later on. @@ -124,10 +125,10 @@ pub fn tick(app: *App, core: *mach.Core) void { defer encoder.release(); // Begin render pass - const sky_blue_background = gpu.Color{ .r = 0.0, .g = 0.0, .b = 0.0, .a = 0.0 }; + const transparent_background = gpu.Color{ .r = 0.0, .g = 0.0, .b = 0.0, .a = 0.0 }; const color_attachments = [_]gpu.RenderPassColorAttachment{.{ .view = back_buffer_view, - .clear_value = sky_blue_background, + .clear_value = transparent_background, .load_op = .clear, .store_op = .store, }}; @@ -172,7 +173,11 @@ pub fn tick(app: *App, core: *mach.Core) void { const green = mach.math.lerp(0.2, 0.6, mach.math.clamp(app.color_time - 2.0, 0.0, 1.0)); const alpha = mach.math.lerp(0.3, 1.0, app.color_time / 4.0); - core.windows.set(app.window, .color, .{ .transparent = .{ .color = .{ .r = red, .g = green, .b = blue, .a = alpha }, .titlebar = true } }); + core.windows.set( + app.window, + .decoration_color, + .{ .r = red, .g = green, .b = blue, .a = alpha }, + ); } pub fn deinit(app: *App) void { diff --git a/src/Core.zig b/src/Core.zig index 9f61db964a..2803c62490 100644 --- a/src/Core.zig +++ b/src/Core.zig @@ -74,8 +74,17 @@ windows: mach.Objects( /// Target frames per second refresh_rate: u32 = 0, - /// Color of the window background/titlebar - color: WindowColor = .system, + /// Titlebar/window decorations + decorated: bool = true, + + /// Color of the window decorations, i.e. titlebar + /// if null, decoration is the system-determined color + decoration_color: ?gpu.Color = null, + + /// Whether the window should be completely transparent + /// or not. On macOS, to achieve a fully transparent window + /// decoration_color must also be set fully transparent. + transparent: bool = false, // GPU // When `native` is not null, the rest of the fields have been diff --git a/src/core/Darwin.zig b/src/core/Darwin.zig index 05e2bd2283..d99c9f4af6 100644 --- a/src/core/Darwin.zig +++ b/src/core/Darwin.zig @@ -70,36 +70,19 @@ pub fn tick(core: *Core) !void { if (core_window.native) |native| { const native_window: *objc.app_kit.Window = native.window; - const native_view: *objc.mach.View = native.view; - - if (core.windows.updated(window_id, .color)) { - switch (core_window.color) { - .transparent => |wc| { - const color = objc.app_kit.Color.colorWithRed_green_blue_alpha( - wc.color.r, - wc.color.g, - wc.color.b, - wc.color.a, - ); - native_window.setBackgroundColor(color); - native_window.setTitlebarAppearsTransparent(true); - native_view.layer().setOpaque(false); - }, - .solid => |wc| { - const color = objc.app_kit.Color.colorWithRed_green_blue_alpha( - wc.color.r, - wc.color.g, - wc.color.b, - wc.color.a, - ); - native_window.setBackgroundColor(color); - native_window.setTitlebarAppearsTransparent(false); - native_view.layer().setOpaque(true); - }, - .system => { - native_window.setTitlebarAppearsTransparent(false); - native_view.layer().setOpaque(true); - }, + + if (core.windows.updated(window_id, .decoration_color)) { + if (core_window.decoration_color) |decoration_color| { + const color = objc.app_kit.Color.colorWithRed_green_blue_alpha( + decoration_color.r, + decoration_color.g, + decoration_color.b, + decoration_color.a, + ); + native_window.setBackgroundColor(color); + native_window.setTitlebarAppearsTransparent(true); + } else { + native_window.setTitlebarAppearsTransparent(false); } } @@ -135,7 +118,7 @@ fn initWindow( const layer = objc.quartz_core.MetalLayer.new(); defer layer.release(); - if (core_window.color == .transparent) layer.setOpaque(false); + if (core_window.transparent) layer.setOpaque(false); metal_descriptor.* = .{ .layer = layer, @@ -151,11 +134,11 @@ fn initWindow( const window_style = (if (core_window.display_mode == .fullscreen) objc.app_kit.WindowStyleMaskFullScreen else 0) | - (if (core_window.display_mode == .windowed) objc.app_kit.WindowStyleMaskTitled else 0) | - (if (core_window.display_mode == .windowed) objc.app_kit.WindowStyleMaskClosable else 0) | - (if (core_window.display_mode == .windowed) objc.app_kit.WindowStyleMaskMiniaturizable else 0) | - (if (core_window.display_mode == .windowed) objc.app_kit.WindowStyleMaskResizable else 0); - // (if (core_window.display_mode == .windowed) objc.app_kit.WindowStyleMaskFullSizeContentView else 0); + (if (core_window.decorated) objc.app_kit.WindowStyleMaskTitled else 0) | + (if (core_window.decorated) objc.app_kit.WindowStyleMaskClosable else 0) | + (if (core_window.decorated) objc.app_kit.WindowStyleMaskMiniaturizable else 0) | + (if (core_window.decorated) objc.app_kit.WindowStyleMaskResizable else 0) | + (if (!core_window.decorated) objc.app_kit.WindowStyleMaskFullSizeContentView else 0); const native_window_opt: ?*objc.app_kit.Window = objc.app_kit.Window.alloc().initWithContentRect_styleMask_backing_defer_screen( rect, @@ -254,27 +237,15 @@ fn initWindow( native_window.setIsVisible(true); native_window.makeKeyAndOrderFront(null); - switch (core_window.color) { - .transparent => |wc| { - const color = objc.app_kit.Color.colorWithRed_green_blue_alpha( - wc.color.r, - wc.color.g, - wc.color.b, - wc.color.a, - ); - native_window.setBackgroundColor(color); - native_window.setTitlebarAppearsTransparent(true); - }, - .solid => |wc| { - const color = objc.app_kit.Color.colorWithRed_green_blue_alpha( - wc.color.r, - wc.color.g, - wc.color.b, - wc.color.a, - ); - native_window.setBackgroundColor(color); - }, - .system => {}, + if (core_window.decoration_color) |decoration_color| { + const color = objc.app_kit.Color.colorWithRed_green_blue_alpha( + decoration_color.r, + decoration_color.g, + decoration_color.b, + decoration_color.a, + ); + native_window.setBackgroundColor(color); + native_window.setTitlebarAppearsTransparent(true); } const string = objc.foundation.String.allocInit(); From 4f21e65067038a13ab999c9819c6e2f463cdc14e Mon Sep 17 00:00:00 2001 From: foxnne Date: Thu, 12 Dec 2024 11:54:15 -0600 Subject: [PATCH 05/11] darwin: Add `insertText` callback which is responsible for firing `char_input` events --- src/core/Darwin.zig | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/core/Darwin.zig b/src/core/Darwin.zig index d99c9f4af6..058c54a675 100644 --- a/src/core/Darwin.zig +++ b/src/core/Darwin.zig @@ -176,6 +176,14 @@ fn initWindow( ); view.setBlock_keyDown(keyDown.asBlock().copy()); + var insertText = objc.foundation.stackBlockLiteral( + ViewCallbacks.insertText, + context, + null, + null, + ); + view.setBlock_insertText(insertText.asBlock().copy()); + var keyUp = objc.foundation.stackBlockLiteral( ViewCallbacks.keyUp, context, @@ -413,6 +421,16 @@ const ViewCallbacks = struct { } } + pub fn insertText(block: *objc.foundation.BlockLiteral(*Context), event: *objc.app_kit.Event, codepoint: u32) callconv(.C) void { + _ = event; // autofix + const core: *Core = block.context.core; + const window_id = block.context.window_id; + core.pushEvent(.{ .char_input = .{ + .codepoint = @intCast(codepoint), + .window_id = window_id, + } }); + } + pub fn keyUp(block: *objc.foundation.BlockLiteral(*Context), event: *objc.app_kit.Event) callconv(.C) void { const core: *Core = block.context.core; const window_id = block.context.window_id; From 7ff5da8a316a1581c706e9106329c5a012c2f256 Mon Sep 17 00:00:00 2001 From: foxnne Date: Thu, 12 Dec 2024 15:06:18 -0600 Subject: [PATCH 06/11] core: darwin: implement `cursor_mode` and `cursor_shape` updates --- src/Core.zig | 2 ++ src/core/Darwin.zig | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/Core.zig b/src/Core.zig index 2803c62490..d9d045364d 100644 --- a/src/Core.zig +++ b/src/Core.zig @@ -56,6 +56,7 @@ windows: mach.Objects( /// Vertical sync mode, prevents screen tearing. vsync_mode: VSyncMode = .none, + /// Window display mode: fullscreen, windowed or borderless fullscreen display_mode: DisplayMode = .windowed, /// Cursor @@ -108,6 +109,7 @@ windows: mach.Objects( .render_attachment = true, }, + /// Container for native platform-specific information native: ?Platform.Native = null, }, ), diff --git a/src/core/Darwin.zig b/src/core/Darwin.zig index 058c54a675..7cf9926cd4 100644 --- a/src/core/Darwin.zig +++ b/src/core/Darwin.zig @@ -98,6 +98,31 @@ pub fn tick(core: *Core) !void { frame.size.height = @floatFromInt(core.windows.get(window_id, .height)); native_window.setFrame_display_animate(native_window.frameRectForContentRect(frame), true, true); } + + if (core.windows.updated(window_id, .cursor_mode)) { + switch (core_window.cursor_mode) { + .normal => objc.app_kit.Cursor.unhide(), + .disabled, .hidden => objc.app_kit.Cursor.hide(), + } + } + + if (core.windows.updated(window_id, .cursor_shape)) { + const Cursor = objc.app_kit.Cursor; + + Cursor.pop(); + + switch (core_window.cursor_shape) { + .arrow => Cursor.arrowCursor().push(), + .ibeam => Cursor.IBeamCursor().push(), + .crosshair => Cursor.crosshairCursor().push(), + .pointing_hand => Cursor.pointingHandCursor().push(), + .not_allowed => Cursor.operationNotAllowedCursor().push(), + .resize_ns => Cursor.resizeUpDownCursor().push(), + .resize_ew => Cursor.resizeLeftRightCursor().push(), + .resize_all => Cursor.closedHandCursor().push(), + else => std.log.warn("Unsupported cursor", .{}), + } + } } else { try initWindow(core, window_id); } From 7bf680dd48b632eba8c85cb774d90c808e06bbea Mon Sep 17 00:00:00 2001 From: foxnne Date: Fri, 13 Dec 2024 09:12:55 -0600 Subject: [PATCH 07/11] core: darwin: `magnify` -> `zoom_gesture`, add workaround from GLFW for command key blocking `keyUp` on other keys --- src/Core.zig | 16 ++++++++++++---- src/core/Darwin.zig | 42 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 50 insertions(+), 8 deletions(-) diff --git a/src/Core.zig b/src/Core.zig index d9d045364d..0e6d4e015e 100644 --- a/src/Core.zig +++ b/src/Core.zig @@ -533,10 +533,7 @@ pub const Event = union(enum) { window_open: struct { window_id: mach.ObjectID, }, - magnify: struct { - window_id: mach.ObjectID, - magnification: f32, - }, + zoom_gesture: ZoomGestureEvent, focus_gained: struct { window_id: mach.ObjectID, }, @@ -566,6 +563,17 @@ pub const ResizeEvent = struct { size: Size, }; +pub const ZoomGestureEvent = struct { + window_id: mach.ObjectID, + phase: GesturePhase, + zoom: f32, +}; + +pub const GesturePhase = enum { + began, + ended, +}; + pub const MouseButton = enum { left, right, diff --git a/src/core/Darwin.zig b/src/core/Darwin.zig index 7cf9926cd4..5de98e399d 100644 --- a/src/core/Darwin.zig +++ b/src/core/Darwin.zig @@ -139,6 +139,33 @@ fn initWindow( // TODO: Only call this on the first window creation _ = objc.app_kit.Application.sharedApplication().setActivationPolicy(objc.app_kit.ApplicationActivationPolicyRegular); + const commandFn = struct { + pub fn commandFn(block: *objc.foundation.BlockLiteral(*Context), event: *objc.app_kit.Event) callconv(.C) ?*objc.app_kit.Event { + const core_: *Core = block.context.core; + const window_id_ = block.context.window_id; + + if (core_.windows.get(window_id_, .native)) |native| { + const native_window: *objc.app_kit.Window = native.window; + + if (event.modifierFlags() & objc.app_kit.EventModifierFlagCommand != 0) + native_window.sendEvent(event); + } + return event; + } + }.commandFn; + + const context = try core.allocator.create(Context); + context.* = .{ .core = core, .window_id = window_id }; + + var commandBlock = objc.foundation.stackBlockLiteral( + commandFn, + context, + null, + null, + ); + + _ = objc.app_kit.Event.addLocalMonitorForEventsMatchingMask_handler(objc.app_kit.EventMaskKeyUp, commandBlock.asBlock().copy()); + const metal_descriptor = try core.allocator.create(gpu.Surface.DescriptorFromMetalLayer); const layer = objc.quartz_core.MetalLayer.new(); defer layer.release(); @@ -188,8 +215,6 @@ fn initWindow( view = view.initWithFrame(rect); view.setLayer(@ptrCast(layer)); - const context = try core.allocator.create(Context); - context.* = .{ .core = core, .window_id = window_id }; // TODO(core): free this allocation { @@ -422,9 +447,10 @@ const ViewCallbacks = struct { const core: *Core = block.context.core; const window_id = block.context.window_id; - core.pushEvent(.{ .magnify = .{ + core.pushEvent(.{ .zoom_gesture = .{ .window_id = window_id, - .magnification = @floatCast(event.magnification()), + .zoom = @floatCast(event.magnification()), + .phase = machPhaseFromPhase(event.phase()), } }); } @@ -495,6 +521,14 @@ const ViewCallbacks = struct { } }; +fn machPhaseFromPhase(phase: objc.app_kit.EventPhase) Core.GesturePhase { + return switch (phase) { + objc.app_kit.EventPhaseBegan => .began, + objc.app_kit.EventPhaseEnded => .ended, + else => .began, + }; +} + fn machModifierFromModifierFlag(modifier_flag: usize) Core.KeyMods { var modifier: Core.KeyMods = .{ .alt = false, From a208065deefc9651b0b59f24975c27149794d6a9 Mon Sep 17 00:00:00 2001 From: foxnne Date: Fri, 13 Dec 2024 09:43:19 -0600 Subject: [PATCH 08/11] core: darwin: Document the command key fix --- src/core/Darwin.zig | 46 ++++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/src/core/Darwin.zig b/src/core/Darwin.zig index 5de98e399d..e7bd24e469 100644 --- a/src/core/Darwin.zig +++ b/src/core/Darwin.zig @@ -134,37 +134,41 @@ fn initWindow( window_id: mach.ObjectID, ) !void { var core_window = core.windows.getValue(window_id); + + const context = try core.allocator.create(Context); + context.* = .{ .core = core, .window_id = window_id }; // If the application is not headless, we need to make the application a genuine UI application // by setting the activation policy, this moves the process to foreground // TODO: Only call this on the first window creation _ = objc.app_kit.Application.sharedApplication().setActivationPolicy(objc.app_kit.ApplicationActivationPolicyRegular); - const commandFn = struct { - pub fn commandFn(block: *objc.foundation.BlockLiteral(*Context), event: *objc.app_kit.Event) callconv(.C) ?*objc.app_kit.Event { - const core_: *Core = block.context.core; - const window_id_ = block.context.window_id; + { + // On macos, the command key in particular seems to be handled a bit differently and tends to block the `keyUp` event + // from firing. To remedy this, we borrow the same fix GLFW uses and add a monitor. + const commandFn = struct { + pub fn commandFn(block: *objc.foundation.BlockLiteral(*Context), event: *objc.app_kit.Event) callconv(.C) ?*objc.app_kit.Event { + const core_: *Core = block.context.core; + const window_id_ = block.context.window_id; - if (core_.windows.get(window_id_, .native)) |native| { - const native_window: *objc.app_kit.Window = native.window; + if (core_.windows.get(window_id_, .native)) |native| { + const native_window: *objc.app_kit.Window = native.window; - if (event.modifierFlags() & objc.app_kit.EventModifierFlagCommand != 0) - native_window.sendEvent(event); + if (event.modifierFlags() & objc.app_kit.EventModifierFlagCommand != 0) + native_window.sendEvent(event); + } + return event; } - return event; - } - }.commandFn; + }.commandFn; - const context = try core.allocator.create(Context); - context.* = .{ .core = core, .window_id = window_id }; + var commandBlock = objc.foundation.stackBlockLiteral( + commandFn, + context, + null, + null, + ); - var commandBlock = objc.foundation.stackBlockLiteral( - commandFn, - context, - null, - null, - ); - - _ = objc.app_kit.Event.addLocalMonitorForEventsMatchingMask_handler(objc.app_kit.EventMaskKeyUp, commandBlock.asBlock().copy()); + _ = objc.app_kit.Event.addLocalMonitorForEventsMatchingMask_handler(objc.app_kit.EventMaskKeyUp, commandBlock.asBlock().copy()); + } const metal_descriptor = try core.allocator.create(gpu.Surface.DescriptorFromMetalLayer); const layer = objc.quartz_core.MetalLayer.new(); From 3599437aa8a252eebb847cb857bb52b29d4176b8 Mon Sep 17 00:00:00 2001 From: foxnne Date: Sat, 14 Dec 2024 11:18:44 -0600 Subject: [PATCH 09/11] core: windows: Update to match darwin and handle updating the swapchain and window/framebuffer sizes --- src/Core.zig | 1 - src/core/Windows.zig | 29 +++++++++++++++++++---------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/Core.zig b/src/Core.zig index 0e6d4e015e..8fed0b4864 100644 --- a/src/Core.zig +++ b/src/Core.zig @@ -96,7 +96,6 @@ windows: mach.Objects( queue: *gpu.Queue = undefined, swap_chain: *gpu.SwapChain = undefined, swap_chain_descriptor: gpu.SwapChain.Descriptor = undefined, - swap_chain_update: std.Thread.ResetEvent = .{}, surface: *gpu.Surface = undefined, surface_descriptor: gpu.Surface.Descriptor = undefined, diff --git a/src/core/Windows.zig b/src/core/Windows.zig index 3c375c1921..b600d57e27 100644 --- a/src/core/Windows.zig +++ b/src/core/Windows.zig @@ -40,7 +40,6 @@ pub fn tick(core: *Core) !void { if (native_opt) |native| { _ = native; // autofix - var msg: w.MSG = undefined; while (w.PeekMessageW(&msg, null, 0, 0, w.PM_REMOVE) != 0) { _ = w.TranslateMessage(&msg); @@ -193,8 +192,7 @@ fn wndProc(wnd: w.HWND, msg: u32, wParam: w.WPARAM, lParam: w.LPARAM) callconv(w const core = context.core; const window_id = context.window_id; - var window = core.windows.getValue(window_id); - defer core.windows.setValueRaw(window_id, window); + var core_window = core.windows.getValue(window_id); switch (msg) { w.WM_CLOSE => { @@ -205,18 +203,28 @@ fn wndProc(wnd: w.HWND, msg: u32, wParam: w.WPARAM, lParam: w.LPARAM) callconv(w const width: u32 = @as(u32, @intCast(lParam & 0xFFFF)); const height: u32 = @as(u32, @intCast((lParam >> 16) & 0xFFFF)); - window.width = width; - window.height = height; + if (core_window.width != width or core_window.height != height) { + // Recreate the swap_chain + core_window.swap_chain.release(); + core_window.swap_chain_descriptor.width = width; + core_window.swap_chain_descriptor.height = height; + core_window.swap_chain = core_window.device.createSwapChain(core_window.surface, &core_window.swap_chain_descriptor); - core.pushEvent(.{ .window_resize = .{ .window_id = window_id, .size = .{ .width = width, .height = height } } }); + core_window.width = width; + core_window.height = height; + core_window.framebuffer_width = width; + core_window.framebuffer_height = height; + + core.pushEvent(.{ .window_resize = .{ .window_id = window_id, .size = .{ .width = width, .height = height } } }); + + core.windows.setValueRaw(window_id, core_window); + } // TODO (win32): only send resize event when sizing is done. // the main mach loops does not run while resizing. // Which means if events are pushed here they will // queue up until resize is done. - window.swap_chain_update.set(); - return 0; }, w.WM_KEYDOWN, w.WM_KEYUP, w.WM_SYSKEYDOWN, w.WM_SYSKEYUP => { @@ -225,6 +233,7 @@ fn wndProc(wnd: w.HWND, msg: u32, wParam: w.WPARAM, lParam: w.LPARAM) callconv(w if (msg == w.WM_SYSKEYDOWN and vkey == w.VK_F4) { core.pushEvent(.{ .close = .{ .window_id = window_id } }); + return 0; } @@ -235,7 +244,7 @@ fn wndProc(wnd: w.HWND, msg: u32, wParam: w.WPARAM, lParam: w.LPARAM) callconv(w // right alt sends left control first var next: w.MSG = undefined; const time = w.GetMessageTime(); - if (window.native) |native| { + if (core_window.native) |native| { if (w.PeekMessageW(&next, native.window, 0, 0, w.PM_NOREMOVE) != 0 and next.time == time and (next.message == msg or (msg == w.WM_SYSKEYDOWN and next.message == w.WM_KEYUP)) and @@ -276,7 +285,7 @@ fn wndProc(wnd: w.HWND, msg: u32, wParam: w.WPARAM, lParam: w.LPARAM) callconv(w return 0; }, w.WM_CHAR => { - if (window.native) |*native| { + if (core_window.native) |*native| { const char: u16 = @truncate(wParam); var chars: []const u16 = undefined; if (native.surrogate != 0) { From 37009f89b9dc96f90e037afac114f6b78553d027 Mon Sep 17 00:00:00 2001 From: foxnne Date: Fri, 20 Dec 2024 14:09:59 -0600 Subject: [PATCH 10/11] core: darwin: Only recreate swapchain and send resize event if the window size actually changed --- src/core/Darwin.zig | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/core/Darwin.zig b/src/core/Darwin.zig index e7bd24e469..71a3a89e87 100644 --- a/src/core/Darwin.zig +++ b/src/core/Darwin.zig @@ -354,29 +354,31 @@ const WindowDelegateCallbacks = struct { const frame = native_window.frame(); const content_rect = native_window.contentRectForFrameRect(frame); - core_window.width = @intFromFloat(content_rect.size.width); - core_window.height = @intFromFloat(content_rect.size.height); + if (core_window.width != @as(u32, @intFromFloat(content_rect.size.width)) or core_window.height != @as(u32, @intFromFloat(content_rect.size.height))) { + core_window.width = @intFromFloat(content_rect.size.width); + core_window.height = @intFromFloat(content_rect.size.height); - const framebuffer_scale: f32 = @floatCast(native_window.backingScaleFactor()); - const window_width: f32 = @floatFromInt(core_window.width); - const window_height: f32 = @floatFromInt(core_window.height); + const framebuffer_scale: f32 = @floatCast(native_window.backingScaleFactor()); + const window_width: f32 = @floatFromInt(core_window.width); + const window_height: f32 = @floatFromInt(core_window.height); - core_window.framebuffer_width = @intFromFloat(window_width * framebuffer_scale); - core_window.framebuffer_height = @intFromFloat(window_height * framebuffer_scale); + core_window.framebuffer_width = @intFromFloat(window_width * framebuffer_scale); + core_window.framebuffer_height = @intFromFloat(window_height * framebuffer_scale); - core_window.swap_chain_descriptor.width = core_window.framebuffer_width; - core_window.swap_chain_descriptor.height = core_window.framebuffer_height; - core_window.swap_chain.release(); + core_window.swap_chain_descriptor.width = core_window.framebuffer_width; + core_window.swap_chain_descriptor.height = core_window.framebuffer_height; + core_window.swap_chain.release(); - core_window.swap_chain = core_window.device.createSwapChain(core_window.surface, &core_window.swap_chain_descriptor); - } + core_window.swap_chain = core_window.device.createSwapChain(core_window.surface, &core_window.swap_chain_descriptor); - core.windows.setValueRaw(block.context.window_id, core_window); + core.windows.setValueRaw(block.context.window_id, core_window); - core.pushEvent(.{ .window_resize = .{ - .window_id = block.context.window_id, - .size = .{ .width = core_window.width, .height = core_window.height }, - } }); + core.pushEvent(.{ .window_resize = .{ + .window_id = block.context.window_id, + .size = .{ .width = core_window.width, .height = core_window.height }, + } }); + } + } } pub fn windowShouldClose(block: *objc.foundation.BlockLiteral(*Context)) callconv(.C) bool { From 26d63db6b0b1b47bde647030dff0f34f58ffc7a7 Mon Sep 17 00:00:00 2001 From: Stephen Gutekanst Date: Tue, 24 Dec 2024 15:28:19 -0700 Subject: [PATCH 11/11] build: update mach-objc dependency Signed-off-by: Stephen Gutekanst --- build.zig.zon | 4 ++-- src/core/Darwin.zig | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index e36c945237..0b8f5d296e 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -22,8 +22,8 @@ .lazy = true, }, .mach_objc = .{ - .url = "https://pkg.machengine.org/mach-objc/eb1e1eee9c02039d582f5fd9814d32e48b736ba6.tar.gz", - .hash = "12209742f139402c34a8901bfb012a748c7101bef971f0a541338d659baa345b237d", + .url = "https://pkg.machengine.org/mach-objc/61206f68d907111ce3c1f068ef7d0b926ead5d62.tar.gz", + .hash = "12205e56037f3c3112c073bc833eea5f95b6ec3fb03a53cc7264595d2266304a5f7f", .lazy = true, }, .xcode_frameworks = .{ diff --git a/src/core/Darwin.zig b/src/core/Darwin.zig index 71a3a89e87..e46a9a30fe 100644 --- a/src/core/Darwin.zig +++ b/src/core/Darwin.zig @@ -109,7 +109,7 @@ pub fn tick(core: *Core) !void { if (core.windows.updated(window_id, .cursor_shape)) { const Cursor = objc.app_kit.Cursor; - Cursor.pop(); + Cursor.T_pop(); switch (core_window.cursor_shape) { .arrow => Cursor.arrowCursor().push(),