Files
ghostty/pkg/macos/video/display_link.zig
Mitchell Hashimoto a177ba90af macos: tolerate display link creation failures
Fixes #13293

Treat Core Video display link creation as optional when macOS has no
active displays. The previous error path reported every creation
failure as out of memory and aborted renderer initialization.

This also resyncs the display link on any display change so when
a display becomes available it re-adds itself.

Tabs created while the session is locked now initialize normally and
fall back to event-driven rendering without vsync.
2026-08-05 13:54:47 -07:00

85 lines
2.6 KiB
Zig

const c = @import("c.zig").c;
pub const DisplayLink = opaque {
pub const Error = error{
CreationFailed,
InvalidOperation,
};
pub fn createWithActiveCGDisplays() Error!*DisplayLink {
var result: ?*DisplayLink = null;
if (c.CVDisplayLinkCreateWithActiveCGDisplays(
@ptrCast(&result),
) != c.kCVReturnSuccess)
return error.CreationFailed;
return result orelse error.CreationFailed;
}
pub fn release(self: *DisplayLink) void {
c.CVDisplayLinkRelease(@ptrCast(self));
}
pub fn start(self: *DisplayLink) Error!void {
if (c.CVDisplayLinkStart(@ptrCast(self)) != c.kCVReturnSuccess)
return error.InvalidOperation;
}
pub fn stop(self: *DisplayLink) Error!void {
if (c.CVDisplayLinkStop(@ptrCast(self)) != c.kCVReturnSuccess)
return error.InvalidOperation;
}
pub fn isRunning(self: *DisplayLink) bool {
return c.CVDisplayLinkIsRunning(@ptrCast(self)) != 0;
}
pub fn setCurrentCGDisplay(
self: *DisplayLink,
display_id: c.CGDirectDisplayID,
) Error!void {
if (c.CVDisplayLinkSetCurrentCGDisplay(
@ptrCast(self),
display_id,
) != c.kCVReturnSuccess)
return error.InvalidOperation;
}
// Note: this purposely throws away a ton of arguments I didn't need.
// It would be trivial to refactor this into Zig types and properly
// pass this through.
pub fn setOutputCallback(
self: *DisplayLink,
comptime Userdata: type,
comptime callbackFn: *const fn (*DisplayLink, ?*Userdata) void,
userinfo: ?*Userdata,
) Error!void {
if (c.CVDisplayLinkSetOutputCallback(
@ptrCast(self),
@ptrCast(&(struct {
fn callback(
displayLink: *DisplayLink,
inNow: *const c.CVTimeStamp,
inOutputTime: *const c.CVTimeStamp,
flagsIn: c.CVOptionFlags,
flagsOut: *c.CVOptionFlags,
inner_userinfo: ?*anyopaque,
) callconv(.c) c.CVReturn {
_ = inNow;
_ = inOutputTime;
_ = flagsIn;
_ = flagsOut;
callbackFn(
displayLink,
@ptrCast(@alignCast(inner_userinfo)),
);
return c.kCVReturnSuccess;
}
}).callback),
userinfo,
) != c.kCVReturnSuccess)
return error.InvalidOperation;
}
};