From bb90104de3f71596b38b9b4c4aadb5d13be7f126 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 16 Nov 2022 20:24:59 -0800 Subject: [PATCH] enable Mac native tabbing --- src/App.zig | 39 +++++++++++++++++++++++++++++++++++++++ src/Window.zig | 10 +++++++++- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/App.zig b/src/App.zig index a27e78ba0..a2a81b687 100644 --- a/src/App.zig +++ b/src/App.zig @@ -4,6 +4,7 @@ const App = @This(); const std = @import("std"); +const builtin = @import("builtin"); const Allocator = std.mem.Allocator; const glfw = @import("glfw"); const Window = @import("Window.zig"); @@ -12,6 +13,8 @@ const Config = @import("config.zig").Config; const BlockingQueue = @import("./blocking_queue.zig").BlockingQueue; const renderer = @import("renderer.zig"); const font = @import("font/main.zig"); +const macos = @import("macos"); +const objc = @import("objc"); const log = std.log.scoped(.app); @@ -36,6 +39,21 @@ mailbox: *Mailbox, /// Set to true once we're quitting. This never goes false again. quit: bool, +/// Mac settings +darwin: if (Darwin.enabled) Darwin else void, + +/// Mac-specific settings +pub const Darwin = struct { + pub const enabled = builtin.target.isDarwin(); + + tabbing_id: *macos.foundation.String, + + pub fn deinit(self: *Darwin) void { + self.tabbing_id.release(); + self.* = undefined; + } +}; + /// Initialize the main app instance. This creates the main window, sets /// up the renderer state, compiles the shaders, etc. This is the primary /// "startup" logic. @@ -52,9 +70,30 @@ pub fn create(alloc: Allocator, config: *const Config) !*App { .config = config, .mailbox = mailbox, .quit = false, + .darwin = if (Darwin.enabled) undefined else {}, }; errdefer app.windows.deinit(alloc); + // On Mac, we enable window tabbing + if (comptime builtin.target.isDarwin()) { + const NSWindow = objc.Class.getClass("NSWindow").?; + NSWindow.msgSend(void, objc.sel("setAllowsAutomaticWindowTabbing:"), .{true}); + + // Our tabbing ID allows all of our windows to group together + const tabbing_id = try macos.foundation.String.createWithBytes( + "dev.ghostty.window", + .utf8, + false, + ); + errdefer tabbing_id.release(); + + // Setup our Mac settings + app.darwin = .{ + .tabbing_id = tabbing_id, + }; + } + errdefer if (comptime builtin.target.isDarwin()) app.darwin.deinit(); + // Create the first window try app.newWindow(.{}); diff --git a/src/Window.zig b/src/Window.zig index affafad21..864453520 100644 --- a/src/Window.zig +++ b/src/Window.zig @@ -32,7 +32,7 @@ const App = @import("App.zig"); // Get native API access on certain platforms so we can do more customization. const glfwNative = glfw.Native(.{ - .cocoa = builtin.os.tag == .macos, + .cocoa = builtin.target.isDarwin(), }); const log = std.log.scoped(.window); @@ -132,6 +132,14 @@ pub fn create(alloc: Allocator, app: *App, config: *const Config) !*Window { errdefer window.destroy(); try Renderer.windowInit(window); + // On Mac, enable tabbing + if (comptime builtin.target.isDarwin()) { + const NSWindowTabbingMode = enum(usize) { automatic = 0, preferred = 1, disallowed = 2 }; + const nswindow = objc.Object.fromId(glfwNative.getCocoaWindow(window).?); + nswindow.setProperty("tabbingMode", NSWindowTabbingMode.automatic); + nswindow.setProperty("tabbingIdentifier", app.darwin.tabbing_id); + } + // Determine our DPI configurations so we can properly configure // font points to pixels and handle other high-DPI scaling factors. const content_scale = try window.getContentScale();