renderer/metal: API to handle macOS display ID changing

This commit is contained in:
Mitchell Hashimoto
2024-05-04 19:40:58 -07:00
parent 6b3cb69101
commit ca9689be46
6 changed files with 58 additions and 4 deletions

View File

@ -542,6 +542,10 @@ uintptr_t ghostty_surface_pwd(ghostty_surface_t, char*, uintptr_t);
bool ghostty_surface_has_selection(ghostty_surface_t);
uintptr_t ghostty_surface_selection(ghostty_surface_t, char*, uintptr_t);
#ifdef __APPLE__
void ghostty_surface_set_display_id(ghostty_surface_t, uint32_t);
#endif
ghostty_inspector_t ghostty_surface_inspector(ghostty_surface_t);
void ghostty_inspector_free(ghostty_surface_t);
void ghostty_inspector_set_focus(ghostty_inspector_t, bool);

View File

@ -112,6 +112,11 @@ extension Ghostty {
selector: #selector(onUpdateRendererHealth),
name: Ghostty.Notification.didUpdateRendererHealth,
object: self)
center.addObserver(
self,
selector: #selector(windowDidChangeScreen),
name: NSWindow.didChangeScreenNotification,
object: nil)
// Setup our surface. This will also initialize all the terminal IO.
let surface_cfg = baseConfig ?? SurfaceConfiguration()
@ -322,6 +327,19 @@ extension Ghostty {
healthy = health == GHOSTTY_RENDERER_HEALTH_OK
}
@objc private func windowDidChangeScreen(notification: SwiftUI.Notification) {
guard let window = self.window else { return }
guard let object = notification.object as? NSWindow, window == object else { return }
guard let screen = window.screen else { return }
guard let surface = self.surface else { return }
// When the window changes screens, we need to update libghostty with the screen
// ID. If vsync is enabled, this will be used with the CVDisplayLink to ensure
// the proper refresh rate is going.
let id = (screen.deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as! NSNumber).uint32Value
ghostty_surface_set_display_id(surface, id)
}
// MARK: - NSView
override func viewDidMoveToWindow() {

View File

@ -1737,6 +1737,15 @@ pub const CAPI = struct {
// Inspector Metal APIs are only available on Apple systems
usingnamespace if (builtin.target.isDarwin()) struct {
export fn ghostty_surface_set_display_id(ptr: *Surface, display_id: u32) void {
const surface = &ptr.core_surface;
_ = surface.renderer_thread.mailbox.push(
.{ .macos_display_id = display_id },
.{ .forever = {} },
);
surface.renderer_thread.wakeup.notify() catch {};
}
export fn ghostty_inspector_metal_init(ptr: *Inspector, device: objc.c.id) bool {
return ptr.initMetal(objc.Object.fromId(device));
}

View File

@ -561,10 +561,14 @@ pub fn init(alloc: Allocator, options: renderer.Options) !Metal {
var cells = try mtl_cell.Contents.init(alloc);
errdefer cells.deinit(alloc);
const display_link: ?DisplayLink = switch (builtin.os.tag) {
.macos => try macos.video.DisplayLink.createWithActiveCGDisplays(),
else => null,
};
const display_link: ?DisplayLink = null;
// Note(mitchellh): if/when we ever want to add vsync, we can use this
// display link to trigger rendering. We don't need this if vsync is off
// because any change will trigger a redraw immediately.
// const display_link: ?DisplayLink = switch (builtin.os.tag) {
// .macos => try macos.video.DisplayLink.createWithActiveCGDisplays(),
// else => null,
// };
errdefer if (display_link) |v| v.release();
return Metal{
@ -701,6 +705,16 @@ fn displayLinkCallback(
};
}
/// Called when we get an updated display ID for our display link.
pub fn setMacOSDisplayID(self: *Metal, id: u32) !void {
if (comptime DisplayLink == void) return;
const display_link = self.display_link orelse return;
log.info("updating display link display id={}", .{id});
display_link.setCurrentCGDisplay(id) catch |err| {
log.warn("error setting display link display id err={}", .{err});
};
}
/// True if our renderer has animations so that a higher frequency
/// timer is used.
pub fn hasAnimations(self: *const Metal) bool {

View File

@ -375,6 +375,12 @@ fn drainMailbox(self: *Thread) !void {
},
.inspector => |v| self.flags.has_inspector = v,
.macos_display_id => |v| {
if (@hasDecl(renderer.Renderer, "setMacOSDisplayID")) {
try self.renderer.setMacOSDisplayID(v);
}
},
}
}
}

View File

@ -69,6 +69,9 @@ pub const Message = union(enum) {
/// Activate or deactivate the inspector.
inspector: bool,
/// The macOS display ID has changed for the window.
macos_display_id: u32,
/// Initialize a change_config message.
pub fn initChangeConfig(alloc: Allocator, config: *const configpkg.Config) !Message {
const thread_ptr = try alloc.create(renderer.Thread.DerivedConfig);