mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-21 19:26:09 +03:00
window: abstract more, it starts
This commit is contained in:
130
src/Window.zig
130
src/Window.zig
@ -141,44 +141,19 @@ pub fn create(alloc: Allocator, app: *App, config: *const Config) !*Window {
|
|||||||
|
|
||||||
// Create the windowing system
|
// Create the windowing system
|
||||||
var winsys = try WindowingSystem.init(app);
|
var winsys = try WindowingSystem.init(app);
|
||||||
winsys.deinit();
|
errdefer winsys.deinit();
|
||||||
|
|
||||||
// Create our window
|
// Initialize our renderer with our initialized windowing system.
|
||||||
const window = try glfw.Window.create(640, 480, "ghostty", null, null, Renderer.windowHints());
|
try Renderer.windowInit(winsys);
|
||||||
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).?);
|
|
||||||
|
|
||||||
// Tabbing mode enables tabbing at all
|
|
||||||
nswindow.setProperty("tabbingMode", NSWindowTabbingMode.automatic);
|
|
||||||
|
|
||||||
// All windows within a tab bar must have a matching tabbing ID.
|
|
||||||
// The app sets this up for us.
|
|
||||||
nswindow.setProperty("tabbingIdentifier", app.darwin.tabbing_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the cursor
|
|
||||||
const cursor = try glfw.Cursor.createStandard(.ibeam);
|
|
||||||
errdefer cursor.destroy();
|
|
||||||
if ((comptime !builtin.target.isDarwin()) or internal_os.macosVersionAtLeast(13, 0, 0)) {
|
|
||||||
// We only set our cursor if we're NOT on Mac, or if we are then the
|
|
||||||
// macOS version is >= 13 (Ventura). On prior versions, glfw crashes
|
|
||||||
// since we use a tab group.
|
|
||||||
try window.setCursor(cursor);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine our DPI configurations so we can properly configure
|
// Determine our DPI configurations so we can properly configure
|
||||||
// font points to pixels and handle other high-DPI scaling factors.
|
// font points to pixels and handle other high-DPI scaling factors.
|
||||||
const content_scale = try window.getContentScale();
|
const content_scale = try winsys.getContentScale();
|
||||||
const x_dpi = content_scale.x_scale * font.face.default_dpi;
|
const x_dpi = content_scale.x * font.face.default_dpi;
|
||||||
const y_dpi = content_scale.y_scale * font.face.default_dpi;
|
const y_dpi = content_scale.y * font.face.default_dpi;
|
||||||
log.debug("xscale={} yscale={} xdpi={} ydpi={}", .{
|
log.debug("xscale={} yscale={} xdpi={} ydpi={}", .{
|
||||||
content_scale.x_scale,
|
content_scale.x,
|
||||||
content_scale.y_scale,
|
content_scale.y,
|
||||||
x_dpi,
|
x_dpi,
|
||||||
y_dpi,
|
y_dpi,
|
||||||
});
|
});
|
||||||
@ -330,7 +305,7 @@ pub fn create(alloc: Allocator, app: *App, config: *const Config) !*Window {
|
|||||||
errdefer renderer_impl.deinit();
|
errdefer renderer_impl.deinit();
|
||||||
|
|
||||||
// Calculate our grid size based on known dimensions.
|
// Calculate our grid size based on known dimensions.
|
||||||
const window_size = try window.getSize();
|
const window_size = try winsys.getSize();
|
||||||
const screen_size: renderer.ScreenSize = .{
|
const screen_size: renderer.ScreenSize = .{
|
||||||
.width = window_size.width,
|
.width = window_size.width,
|
||||||
.height = window_size.height,
|
.height = window_size.height,
|
||||||
@ -348,7 +323,7 @@ pub fn create(alloc: Allocator, app: *App, config: *const Config) !*Window {
|
|||||||
// Create the renderer thread
|
// Create the renderer thread
|
||||||
var render_thread = try renderer.Thread.init(
|
var render_thread = try renderer.Thread.init(
|
||||||
alloc,
|
alloc,
|
||||||
window,
|
winsys,
|
||||||
&self.renderer,
|
&self.renderer,
|
||||||
&self.renderer_state,
|
&self.renderer_state,
|
||||||
);
|
);
|
||||||
@ -382,8 +357,8 @@ pub fn create(alloc: Allocator, app: *App, config: *const Config) !*Window {
|
|||||||
.font_lib = font_lib,
|
.font_lib = font_lib,
|
||||||
.font_group = font_group,
|
.font_group = font_group,
|
||||||
.font_size = font_size,
|
.font_size = font_size,
|
||||||
.window = window,
|
.window = winsys.window,
|
||||||
.cursor = cursor,
|
.cursor = winsys.cursor,
|
||||||
.renderer = renderer_impl,
|
.renderer = renderer_impl,
|
||||||
.renderer_thread = render_thread,
|
.renderer_thread = render_thread,
|
||||||
.renderer_state = .{
|
.renderer_state = .{
|
||||||
@ -413,21 +388,22 @@ pub fn create(alloc: Allocator, app: *App, config: *const Config) !*Window {
|
|||||||
|
|
||||||
// Set a minimum size that is cols=10 h=4. This matches Mac's Terminal.app
|
// Set a minimum size that is cols=10 h=4. This matches Mac's Terminal.app
|
||||||
// but is otherwise somewhat arbitrary.
|
// but is otherwise somewhat arbitrary.
|
||||||
try window.setSizeLimits(.{
|
// TODO:
|
||||||
.width = @floatToInt(u32, cell_size.width * 10),
|
// try window.setSizeLimits(.{
|
||||||
.height = @floatToInt(u32, cell_size.height * 4),
|
// .width = @floatToInt(u32, cell_size.width * 10),
|
||||||
}, .{ .width = null, .height = null });
|
// .height = @floatToInt(u32, cell_size.height * 4),
|
||||||
|
// }, .{ .width = null, .height = null });
|
||||||
|
|
||||||
// Setup our callbacks and user data
|
// Setup our callbacks and user data
|
||||||
window.setUserPointer(self);
|
winsys.window.setUserPointer(self);
|
||||||
window.setSizeCallback(sizeCallback);
|
winsys.window.setSizeCallback(sizeCallback);
|
||||||
window.setCharCallback(charCallback);
|
winsys.window.setCharCallback(charCallback);
|
||||||
window.setKeyCallback(keyCallback);
|
winsys.window.setKeyCallback(keyCallback);
|
||||||
window.setFocusCallback(focusCallback);
|
winsys.window.setFocusCallback(focusCallback);
|
||||||
window.setRefreshCallback(refreshCallback);
|
winsys.window.setRefreshCallback(refreshCallback);
|
||||||
window.setScrollCallback(scrollCallback);
|
winsys.window.setScrollCallback(scrollCallback);
|
||||||
window.setCursorPosCallback(cursorPosCallback);
|
winsys.window.setCursorPosCallback(cursorPosCallback);
|
||||||
window.setMouseButtonCallback(mouseButtonCallback);
|
winsys.window.setMouseButtonCallback(mouseButtonCallback);
|
||||||
|
|
||||||
// Call our size callback which handles all our retina setup
|
// Call our size callback which handles all our retina setup
|
||||||
// Note: this shouldn't be necessary and when we clean up the window
|
// Note: this shouldn't be necessary and when we clean up the window
|
||||||
@ -435,7 +411,7 @@ pub fn create(alloc: Allocator, app: *App, config: *const Config) !*Window {
|
|||||||
// sizeCallback does retina-aware stuff we don't do here and don't want
|
// sizeCallback does retina-aware stuff we don't do here and don't want
|
||||||
// to duplicate.
|
// to duplicate.
|
||||||
sizeCallback(
|
sizeCallback(
|
||||||
window,
|
winsys.window,
|
||||||
@intCast(i32, window_size.width),
|
@intCast(i32, window_size.width),
|
||||||
@intCast(i32, window_size.height),
|
@intCast(i32, window_size.height),
|
||||||
);
|
);
|
||||||
@ -461,12 +437,12 @@ pub fn create(alloc: Allocator, app: *App, config: *const Config) !*Window {
|
|||||||
DevMode.instance.window = self;
|
DevMode.instance.window = self;
|
||||||
|
|
||||||
// Let our renderer setup
|
// Let our renderer setup
|
||||||
try renderer_impl.initDevMode(window);
|
try renderer_impl.initDevMode(winsys);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Give the renderer one more opportunity to finalize any window
|
// Give the renderer one more opportunity to finalize any window
|
||||||
// setup on the main thread prior to spinning up the rendering thread.
|
// setup on the main thread prior to spinning up the rendering thread.
|
||||||
try renderer_impl.finalizeWindowInit(window);
|
try renderer_impl.finalizeWindowInit(winsys);
|
||||||
|
|
||||||
// Start our renderer thread
|
// Start our renderer thread
|
||||||
self.renderer_thr = try std.Thread.spawn(
|
self.renderer_thr = try std.Thread.spawn(
|
||||||
@ -495,7 +471,7 @@ pub fn destroy(self: *Window) void {
|
|||||||
self.renderer_thr.join();
|
self.renderer_thr.join();
|
||||||
|
|
||||||
// We need to become the active rendering thread again
|
// We need to become the active rendering thread again
|
||||||
self.renderer.threadEnter(self.window) catch unreachable;
|
self.renderer.threadEnter(self.windowing_system) catch unreachable;
|
||||||
self.renderer_thread.deinit();
|
self.renderer_thread.deinit();
|
||||||
|
|
||||||
// If we are devmode-owning, clean that up.
|
// If we are devmode-owning, clean that up.
|
||||||
@ -525,51 +501,7 @@ pub fn destroy(self: *Window) void {
|
|||||||
self.io.deinit();
|
self.io.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
var tabgroup_opt: if (builtin.target.isDarwin()) ?objc.Object else void = undefined;
|
self.windowing_system.deinit();
|
||||||
if (comptime builtin.target.isDarwin()) {
|
|
||||||
const nswindow = objc.Object.fromId(glfwNative.getCocoaWindow(self.window).?);
|
|
||||||
const tabgroup = nswindow.getProperty(objc.Object, "tabGroup");
|
|
||||||
|
|
||||||
// On macOS versions prior to Ventura, we lose window focus on tab close
|
|
||||||
// for some reason. We manually fix this by keeping track of the tab
|
|
||||||
// group and just selecting the next window.
|
|
||||||
if (internal_os.macosVersionAtLeast(13, 0, 0))
|
|
||||||
tabgroup_opt = null
|
|
||||||
else
|
|
||||||
tabgroup_opt = tabgroup;
|
|
||||||
|
|
||||||
const windows = tabgroup.getProperty(objc.Object, "windows");
|
|
||||||
switch (windows.getProperty(usize, "count")) {
|
|
||||||
// If we're going down to one window our tab bar is going to be
|
|
||||||
// destroyed so unset it so that the later logic doesn't try to
|
|
||||||
// use it.
|
|
||||||
1 => tabgroup_opt = null,
|
|
||||||
|
|
||||||
// If our tab bar is visible and we are going down to 1 window,
|
|
||||||
// hide the tab bar. The check is "2" because our current window
|
|
||||||
// is still present.
|
|
||||||
2 => if (tabgroup.getProperty(bool, "tabBarVisible")) {
|
|
||||||
nswindow.msgSend(void, objc.sel("toggleTabBar:"), .{nswindow.value});
|
|
||||||
},
|
|
||||||
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.window.destroy();
|
|
||||||
|
|
||||||
// If we have a tabgroup set, we want to manually focus the next window.
|
|
||||||
// We should NOT have to do this usually, see the comments above.
|
|
||||||
if (comptime builtin.target.isDarwin()) {
|
|
||||||
if (tabgroup_opt) |tabgroup| {
|
|
||||||
const selected = tabgroup.getProperty(objc.Object, "selectedWindow");
|
|
||||||
selected.msgSend(void, objc.sel("makeKeyWindow"), .{});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We can destroy the cursor right away. glfw will just revert any
|
|
||||||
// windows using it to the default.
|
|
||||||
self.cursor.destroy();
|
|
||||||
|
|
||||||
self.font_group.deinit(self.alloc);
|
self.font_group.deinit(self.alloc);
|
||||||
self.font_lib.deinit();
|
self.font_lib.deinit();
|
||||||
|
@ -18,6 +18,7 @@ const math = @import("../math.zig");
|
|||||||
const lru = @import("../lru.zig");
|
const lru = @import("../lru.zig");
|
||||||
const DevMode = @import("../DevMode.zig");
|
const DevMode = @import("../DevMode.zig");
|
||||||
const Window = @import("../Window.zig");
|
const Window = @import("../Window.zig");
|
||||||
|
const window = @import("../window.zig");
|
||||||
|
|
||||||
const log = std.log.scoped(.grid);
|
const log = std.log.scoped(.grid);
|
||||||
|
|
||||||
@ -350,7 +351,7 @@ fn resetCellsLRU(self: *OpenGL) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the hints that we want for this
|
/// Returns the hints that we want for this
|
||||||
pub fn windowHints() glfw.Window.Hints {
|
pub fn glfwWindowHints() glfw.Window.Hints {
|
||||||
return .{
|
return .{
|
||||||
.context_version_major = 3,
|
.context_version_major = 3,
|
||||||
.context_version_minor = 3,
|
.context_version_minor = 3,
|
||||||
@ -363,10 +364,10 @@ pub fn windowHints() glfw.Window.Hints {
|
|||||||
|
|
||||||
/// This is called early right after window creation to setup our
|
/// This is called early right after window creation to setup our
|
||||||
/// window surface as necessary.
|
/// window surface as necessary.
|
||||||
pub fn windowInit(window: glfw.Window) !void {
|
pub fn windowInit(winsys: window.System) !void {
|
||||||
// Treat this like a thread entry
|
// Treat this like a thread entry
|
||||||
const self: OpenGL = undefined;
|
const self: OpenGL = undefined;
|
||||||
try self.threadEnter(window);
|
try self.threadEnter(winsys);
|
||||||
|
|
||||||
// Blending for text
|
// Blending for text
|
||||||
try gl.enable(gl.c.GL_BLEND);
|
try gl.enable(gl.c.GL_BLEND);
|
||||||
@ -380,40 +381,23 @@ pub fn windowInit(window: glfw.Window) !void {
|
|||||||
// log.debug("OpenGL extension available name={s}", .{ext});
|
// log.debug("OpenGL extension available name={s}", .{ext});
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
if (builtin.mode == .Debug) {
|
|
||||||
// Get our physical DPI - debug only because we don't have a use for
|
|
||||||
// this but the logging of it may be useful
|
|
||||||
const monitor = window.getMonitor() orelse monitor: {
|
|
||||||
log.warn("window had null monitor, getting primary monitor", .{});
|
|
||||||
break :monitor glfw.Monitor.getPrimary().?;
|
|
||||||
};
|
|
||||||
const physical_size = monitor.getPhysicalSize();
|
|
||||||
const video_mode = try monitor.getVideoMode();
|
|
||||||
const physical_x_dpi = @intToFloat(f32, video_mode.getWidth()) / (@intToFloat(f32, physical_size.width_mm) / 25.4);
|
|
||||||
const physical_y_dpi = @intToFloat(f32, video_mode.getHeight()) / (@intToFloat(f32, physical_size.height_mm) / 25.4);
|
|
||||||
log.debug("physical dpi x={} y={}", .{
|
|
||||||
physical_x_dpi,
|
|
||||||
physical_y_dpi,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is called just prior to spinning up the renderer thread for
|
/// This is called just prior to spinning up the renderer thread for
|
||||||
/// final main thread setup requirements.
|
/// final main thread setup requirements.
|
||||||
pub fn finalizeWindowInit(self: *const OpenGL, window: glfw.Window) !void {
|
pub fn finalizeWindowInit(self: *const OpenGL, winsys: window.System) !void {
|
||||||
_ = self;
|
_ = self;
|
||||||
_ = window;
|
_ = winsys;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is called if this renderer runs DevMode.
|
/// This is called if this renderer runs DevMode.
|
||||||
pub fn initDevMode(self: *const OpenGL, window: glfw.Window) !void {
|
pub fn initDevMode(self: *const OpenGL, winsys: window.System) !void {
|
||||||
_ = self;
|
_ = self;
|
||||||
|
|
||||||
if (DevMode.enabled) {
|
if (DevMode.enabled) {
|
||||||
// Initialize for our window
|
// Initialize for our window
|
||||||
assert(imgui.ImplGlfw.initForOpenGL(
|
assert(imgui.ImplGlfw.initForOpenGL(
|
||||||
@ptrCast(*imgui.ImplGlfw.GLFWWindow, window.handle),
|
@ptrCast(*imgui.ImplGlfw.GLFWWindow, winsys.window.handle),
|
||||||
true,
|
true,
|
||||||
));
|
));
|
||||||
assert(imgui.ImplOpenGL3.init("#version 330 core"));
|
assert(imgui.ImplOpenGL3.init("#version 330 core"));
|
||||||
@ -431,7 +415,7 @@ pub fn deinitDevMode(self: *const OpenGL) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Callback called by renderer.Thread when it begins.
|
/// Callback called by renderer.Thread when it begins.
|
||||||
pub fn threadEnter(self: *const OpenGL, window: glfw.Window) !void {
|
pub fn threadEnter(self: *const OpenGL, winsys: window.System) !void {
|
||||||
_ = self;
|
_ = self;
|
||||||
|
|
||||||
// We need to make the OpenGL context current. OpenGL requires
|
// We need to make the OpenGL context current. OpenGL requires
|
||||||
@ -439,7 +423,7 @@ pub fn threadEnter(self: *const OpenGL, window: glfw.Window) !void {
|
|||||||
// ensures that the context switches over to our thread. Important:
|
// ensures that the context switches over to our thread. Important:
|
||||||
// the prior thread MUST have detached the context prior to calling
|
// the prior thread MUST have detached the context prior to calling
|
||||||
// this entrypoint.
|
// this entrypoint.
|
||||||
try glfw.makeContextCurrent(window);
|
try glfw.makeContextCurrent(winsys.window);
|
||||||
errdefer glfw.makeContextCurrent(null) catch |err|
|
errdefer glfw.makeContextCurrent(null) catch |err|
|
||||||
log.warn("failed to cleanup OpenGL context err={}", .{err});
|
log.warn("failed to cleanup OpenGL context err={}", .{err});
|
||||||
try glfw.swapInterval(1);
|
try glfw.swapInterval(1);
|
||||||
@ -541,7 +525,7 @@ fn resetFontMetrics(
|
|||||||
/// The primary render callback that is completely thread-safe.
|
/// The primary render callback that is completely thread-safe.
|
||||||
pub fn render(
|
pub fn render(
|
||||||
self: *OpenGL,
|
self: *OpenGL,
|
||||||
window: glfw.Window,
|
winsys: window.System,
|
||||||
state: *renderer.State,
|
state: *renderer.State,
|
||||||
) !void {
|
) !void {
|
||||||
// Data we extract out of the critical area.
|
// Data we extract out of the critical area.
|
||||||
@ -657,7 +641,7 @@ pub fn render(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Swap our window buffers
|
// Swap our window buffers
|
||||||
try window.swapBuffers();
|
try winsys.window.swapBuffers();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// rebuildCells rebuilds all the GPU cells from our CPU state. This is a
|
/// rebuildCells rebuilds all the GPU cells from our CPU state. This is a
|
||||||
|
@ -4,9 +4,9 @@ pub const Thread = @This();
|
|||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
const glfw = @import("glfw");
|
|
||||||
const libuv = @import("libuv");
|
const libuv = @import("libuv");
|
||||||
const renderer = @import("../renderer.zig");
|
const renderer = @import("../renderer.zig");
|
||||||
|
const window = @import("../window.zig");
|
||||||
const BlockingQueue = @import("../blocking_queue.zig").BlockingQueue;
|
const BlockingQueue = @import("../blocking_queue.zig").BlockingQueue;
|
||||||
const tracy = @import("tracy");
|
const tracy = @import("tracy");
|
||||||
const trace = tracy.trace;
|
const trace = tracy.trace;
|
||||||
@ -37,8 +37,8 @@ render_h: libuv.Timer,
|
|||||||
/// The timer used for cursor blinking
|
/// The timer used for cursor blinking
|
||||||
cursor_h: libuv.Timer,
|
cursor_h: libuv.Timer,
|
||||||
|
|
||||||
/// The windo we're rendering to.
|
/// The window we're rendering to.
|
||||||
window: glfw.Window,
|
window: window.System,
|
||||||
|
|
||||||
/// The underlying renderer implementation.
|
/// The underlying renderer implementation.
|
||||||
renderer: *renderer.Renderer,
|
renderer: *renderer.Renderer,
|
||||||
@ -55,7 +55,7 @@ mailbox: *Mailbox,
|
|||||||
/// is up to the caller to start the thread with the threadMain entrypoint.
|
/// is up to the caller to start the thread with the threadMain entrypoint.
|
||||||
pub fn init(
|
pub fn init(
|
||||||
alloc: Allocator,
|
alloc: Allocator,
|
||||||
window: glfw.Window,
|
win: window.System,
|
||||||
renderer_impl: *renderer.Renderer,
|
renderer_impl: *renderer.Renderer,
|
||||||
state: *renderer.State,
|
state: *renderer.State,
|
||||||
) !Thread {
|
) !Thread {
|
||||||
@ -120,7 +120,7 @@ pub fn init(
|
|||||||
.stop = stop_h,
|
.stop = stop_h,
|
||||||
.render_h = render_h,
|
.render_h = render_h,
|
||||||
.cursor_h = cursor_timer,
|
.cursor_h = cursor_timer,
|
||||||
.window = window,
|
.window = win,
|
||||||
.renderer = renderer_impl,
|
.renderer = renderer_impl,
|
||||||
.state = state,
|
.state = state,
|
||||||
.mailbox = mailbox,
|
.mailbox = mailbox,
|
||||||
|
@ -18,6 +18,8 @@ const glfwNative = glfw.Native(.{
|
|||||||
.cocoa = builtin.target.isDarwin(),
|
.cocoa = builtin.target.isDarwin(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const log = std.log.scoped(.glfw_window);
|
||||||
|
|
||||||
/// The glfw window handle
|
/// The glfw window handle
|
||||||
window: glfw.Window,
|
window: glfw.Window,
|
||||||
|
|
||||||
@ -26,9 +28,25 @@ cursor: glfw.Cursor,
|
|||||||
|
|
||||||
pub fn init(app: *const App) !Glfw {
|
pub fn init(app: *const App) !Glfw {
|
||||||
// Create our window
|
// Create our window
|
||||||
const win = try glfw.Window.create(640, 480, "ghostty", null, null, Renderer.windowHints());
|
const win = try glfw.Window.create(640, 480, "ghostty", null, null, Renderer.glfwWindowHints());
|
||||||
errdefer win.destroy();
|
errdefer win.destroy();
|
||||||
try Renderer.windowInit(win);
|
|
||||||
|
if (builtin.mode == .Debug) {
|
||||||
|
// Get our physical DPI - debug only because we don't have a use for
|
||||||
|
// this but the logging of it may be useful
|
||||||
|
const monitor = win.getMonitor() orelse monitor: {
|
||||||
|
log.warn("window had null monitor, getting primary monitor", .{});
|
||||||
|
break :monitor glfw.Monitor.getPrimary().?;
|
||||||
|
};
|
||||||
|
const physical_size = monitor.getPhysicalSize();
|
||||||
|
const video_mode = try monitor.getVideoMode();
|
||||||
|
const physical_x_dpi = @intToFloat(f32, video_mode.getWidth()) / (@intToFloat(f32, physical_size.width_mm) / 25.4);
|
||||||
|
const physical_y_dpi = @intToFloat(f32, video_mode.getHeight()) / (@intToFloat(f32, physical_size.height_mm) / 25.4);
|
||||||
|
log.debug("physical dpi x={} y={}", .{
|
||||||
|
physical_x_dpi,
|
||||||
|
physical_y_dpi,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// On Mac, enable tabbing
|
// On Mac, enable tabbing
|
||||||
if (comptime builtin.target.isDarwin()) {
|
if (comptime builtin.target.isDarwin()) {
|
||||||
@ -53,6 +71,7 @@ pub fn init(app: *const App) !Glfw {
|
|||||||
try win.setCursor(cursor);
|
try win.setCursor(cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build our result
|
||||||
return Glfw{
|
return Glfw{
|
||||||
.window = win,
|
.window = win,
|
||||||
.cursor = cursor,
|
.cursor = cursor,
|
||||||
@ -60,8 +79,50 @@ pub fn init(app: *const App) !Glfw {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *Glfw) void {
|
pub fn deinit(self: *Glfw) void {
|
||||||
|
var tabgroup_opt: if (builtin.target.isDarwin()) ?objc.Object else void = undefined;
|
||||||
|
if (comptime builtin.target.isDarwin()) {
|
||||||
|
const nswindow = objc.Object.fromId(glfwNative.getCocoaWindow(self.window).?);
|
||||||
|
const tabgroup = nswindow.getProperty(objc.Object, "tabGroup");
|
||||||
|
|
||||||
|
// On macOS versions prior to Ventura, we lose window focus on tab close
|
||||||
|
// for some reason. We manually fix this by keeping track of the tab
|
||||||
|
// group and just selecting the next window.
|
||||||
|
if (internal_os.macosVersionAtLeast(13, 0, 0))
|
||||||
|
tabgroup_opt = null
|
||||||
|
else
|
||||||
|
tabgroup_opt = tabgroup;
|
||||||
|
|
||||||
|
const windows = tabgroup.getProperty(objc.Object, "windows");
|
||||||
|
switch (windows.getProperty(usize, "count")) {
|
||||||
|
// If we're going down to one window our tab bar is going to be
|
||||||
|
// destroyed so unset it so that the later logic doesn't try to
|
||||||
|
// use it.
|
||||||
|
1 => tabgroup_opt = null,
|
||||||
|
|
||||||
|
// If our tab bar is visible and we are going down to 1 window,
|
||||||
|
// hide the tab bar. The check is "2" because our current window
|
||||||
|
// is still present.
|
||||||
|
2 => if (tabgroup.getProperty(bool, "tabBarVisible")) {
|
||||||
|
nswindow.msgSend(void, objc.sel("toggleTabBar:"), .{nswindow.value});
|
||||||
|
},
|
||||||
|
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can now safely destroy our windows. We have to do this BEFORE
|
||||||
|
// setting up the new focused window below.
|
||||||
self.window.destroy();
|
self.window.destroy();
|
||||||
self.cursor.destroy();
|
self.cursor.destroy();
|
||||||
|
|
||||||
|
// If we have a tabgroup set, we want to manually focus the next window.
|
||||||
|
// We should NOT have to do this usually, see the comments above.
|
||||||
|
if (comptime builtin.target.isDarwin()) {
|
||||||
|
if (tabgroup_opt) |tabgroup| {
|
||||||
|
const selected = tabgroup.getProperty(objc.Object, "selectedWindow");
|
||||||
|
selected.msgSend(void, objc.sel("makeKeyWindow"), .{});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the content scale for the created window.
|
/// Returns the content scale for the created window.
|
||||||
|
Reference in New Issue
Block a user