diff --git a/build.zig b/build.zig index 6f80a8961..7f0bf84c5 100644 --- a/build.zig +++ b/build.zig @@ -145,7 +145,6 @@ pub fn build(b: *std.Build) !void { if (std.mem.indexOf(u8, stdout.items, "wayland")) |_| wayland = true; } else { std.log.warn("pkg-config: {s} with code {d}", .{ @tagName(term), code }); - return error.Unexpected; } }, inline else => |code| { diff --git a/macos/Sources/App/macOS/AppDelegate.swift b/macos/Sources/App/macOS/AppDelegate.swift index 70873236a..e3518cd2b 100644 --- a/macos/Sources/App/macOS/AppDelegate.swift +++ b/macos/Sources/App/macOS/AppDelegate.swift @@ -424,35 +424,42 @@ class AppDelegate: NSObject, // If we have a main window then we don't process any of the keys // because we let it capture and propagate. guard NSApp.mainWindow == nil else { return event } - + // If this event as-is would result in a key binding then we send it. if let app = ghostty.app, ghostty_app_key_is_binding( - app, - event.ghosttyKeyEvent(GHOSTTY_ACTION_PRESS)) { - ghostty_app_key(app, event.ghosttyKeyEvent(GHOSTTY_ACTION_PRESS)) - return nil + app, + event.ghosttyKeyEvent(GHOSTTY_ACTION_PRESS)) { + // If the key was handled by Ghostty we stop the event chain. If + // the key wasn't handled then we let it fall through and continue + // processing. This is important because some bindings may have no + // affect at this scope. + if (ghostty_app_key( + app, + event.ghosttyKeyEvent(GHOSTTY_ACTION_PRESS))) { + return nil + } } - + // If this event would be handled by our menu then we do nothing. if let mainMenu = NSApp.mainMenu, mainMenu.performKeyEquivalent(with: event) { return nil } - + // If we reach this point then we try to process the key event // through the Ghostty key mechanism. - + // Ghostty must be loaded guard let ghostty = self.ghostty.app else { return event } - + // Build our event input and call ghostty if (ghostty_app_key(ghostty, event.ghosttyKeyEvent(GHOSTTY_ACTION_PRESS))) { // The key was used so we want to stop it from going to our Mac app Ghostty.logger.debug("local key event handled event=\(event)") return nil } - + return event } diff --git a/macos/Sources/Features/Terminal/TerminalWindow.swift b/macos/Sources/Features/Terminal/TerminalWindow.swift index 35f629bfd..0eb8daeeb 100644 --- a/macos/Sources/Features/Terminal/TerminalWindow.swift +++ b/macos/Sources/Features/Terminal/TerminalWindow.swift @@ -667,12 +667,16 @@ fileprivate class WindowDragView: NSView { // A view that matches the color of selected and unselected tabs in the adjacent tab bar. fileprivate class WindowButtonsBackdropView: NSView { - private let terminalWindow: TerminalWindow + // This must be weak because the window has this view. Otherwise + // a retain cycle occurs. + private weak var terminalWindow: TerminalWindow? private let isLightTheme: Bool private let overlayLayer = VibrantLayer() var isHighlighted: Bool = true { didSet { + guard let terminalWindow else { return } + if isLightTheme { overlayLayer.isHidden = isHighlighted layer?.backgroundColor = .clear diff --git a/src/renderer/Metal.zig b/src/renderer/Metal.zig index 75e61ebc0..5ad45da8e 100644 --- a/src/renderer/Metal.zig +++ b/src/renderer/Metal.zig @@ -209,20 +209,31 @@ pub const GPUState = struct { } fn chooseDevice() error{NoMetalDevice}!objc.Object { - const devices = objc.Object.fromId(mtl.MTLCopyAllDevices()); - defer devices.release(); var chosen_device: ?objc.Object = null; - var iter = devices.iterate(); - while (iter.next()) |device| { - // We want a GPU that’s connected to a display. - if (device.getProperty(bool, "isHeadless")) continue; - chosen_device = device; - // If the user has an eGPU plugged in, they probably want - // to use it. Otherwise, integrated GPUs are better for - // battery life and thermals. - if (device.getProperty(bool, "isRemovable") or - device.getProperty(bool, "isLowPower")) break; + + switch (comptime builtin.os.tag) { + .macos => { + const devices = objc.Object.fromId(mtl.MTLCopyAllDevices()); + defer devices.release(); + + var iter = devices.iterate(); + while (iter.next()) |device| { + // We want a GPU that’s connected to a display. + if (device.getProperty(bool, "isHeadless")) continue; + chosen_device = device; + // If the user has an eGPU plugged in, they probably want + // to use it. Otherwise, integrated GPUs are better for + // battery life and thermals. + if (device.getProperty(bool, "isRemovable") or + device.getProperty(bool, "isLowPower")) break; + } + }, + .ios => { + chosen_device = objc.Object.fromId(mtl.MTLCreateSystemDefaultDevice()); + }, + else => @compileError("unsupported target for Metal"), } + const device = chosen_device orelse return error.NoMetalDevice; return device.retain(); } diff --git a/src/renderer/metal/api.zig b/src/renderer/metal/api.zig index bd4f407cd..48056ae5e 100644 --- a/src/renderer/metal/api.zig +++ b/src/renderer/metal/api.zig @@ -175,4 +175,8 @@ pub const MTLSize = extern struct { depth: c_ulong, }; +/// https://developer.apple.com/documentation/metal/1433367-mtlcopyalldevices pub extern "c" fn MTLCopyAllDevices() ?*anyopaque; + +/// https://developer.apple.com/documentation/metal/1433401-mtlcreatesystemdefaultdevice +pub extern "c" fn MTLCreateSystemDefaultDevice() ?*anyopaque;