mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-15 00:06:09 +03:00
Hook up new window, modify renderers
This commit is contained in:
48
src/App.zig
48
src/App.zig
@ -10,6 +10,7 @@ const Window = @import("Window.zig");
|
||||
const tracy = @import("tracy");
|
||||
const Config = @import("config.zig").Config;
|
||||
const BlockingQueue = @import("./blocking_queue.zig").BlockingQueue;
|
||||
const renderer = @import("renderer.zig");
|
||||
|
||||
const log = std.log.scoped(.app);
|
||||
|
||||
@ -34,34 +35,33 @@ mailbox: *Mailbox,
|
||||
/// 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.
|
||||
pub fn init(alloc: Allocator, config: *const Config) !App {
|
||||
pub fn create(alloc: Allocator, config: *const Config) !*App {
|
||||
// The mailbox for messaging this thread
|
||||
var mailbox = try Mailbox.create(alloc);
|
||||
errdefer mailbox.destroy(alloc);
|
||||
|
||||
// Create the first window
|
||||
var window = try Window.create(alloc, config);
|
||||
errdefer window.destroy();
|
||||
|
||||
// Create our windows list and add our initial window.
|
||||
var windows: WindowList = .{};
|
||||
errdefer windows.deinit(alloc);
|
||||
try windows.append(alloc, window);
|
||||
|
||||
return App{
|
||||
var app = try alloc.create(App);
|
||||
errdefer alloc.destroy(app);
|
||||
app.* = .{
|
||||
.alloc = alloc,
|
||||
.windows = windows,
|
||||
.windows = .{},
|
||||
.config = config,
|
||||
.mailbox = mailbox,
|
||||
};
|
||||
errdefer app.windows.deinit(alloc);
|
||||
|
||||
// Create the first window
|
||||
try app.newWindow();
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *App) void {
|
||||
pub fn destroy(self: *App) void {
|
||||
// Clean up all our windows
|
||||
for (self.windows.items) |window| window.destroy();
|
||||
self.windows.deinit(self.alloc);
|
||||
self.mailbox.destroy(self.alloc);
|
||||
self.* = undefined;
|
||||
self.alloc.destroy(self);
|
||||
}
|
||||
|
||||
/// Wake up the app event loop. This should be called after any messages
|
||||
@ -86,6 +86,11 @@ pub fn run(self: *App) !void {
|
||||
while (i < self.windows.items.len) {
|
||||
const window = self.windows.items[i];
|
||||
if (window.shouldClose()) {
|
||||
// If this was our final window, deinitialize the renderer
|
||||
if (self.windows.items.len == 1) {
|
||||
renderer.Renderer.lastWindowDeinit();
|
||||
}
|
||||
|
||||
window.destroy();
|
||||
_ = self.windows.swapRemove(i);
|
||||
continue;
|
||||
@ -107,11 +112,24 @@ fn drainMailbox(self: *App) !void {
|
||||
while (drain.next()) |message| {
|
||||
log.debug("mailbox message={}", .{message});
|
||||
switch (message) {
|
||||
.new_window => unreachable,
|
||||
.new_window => try self.newWindow(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new window
|
||||
fn newWindow(self: *App) !void {
|
||||
var window = try Window.create(self.alloc, self, self.config);
|
||||
errdefer window.destroy();
|
||||
try self.windows.append(self.alloc, window);
|
||||
errdefer _ = self.windows.pop();
|
||||
|
||||
// This was the first window, so we need to initialize our renderer.
|
||||
if (self.windows.items.len == 1) {
|
||||
try window.renderer.firstWindowInit(window.window);
|
||||
}
|
||||
}
|
||||
|
||||
/// The message types that can be sent to the app thread.
|
||||
pub const Message = union(enum) {
|
||||
/// Create a new terminal window.
|
||||
|
@ -22,6 +22,7 @@ const terminal = @import("terminal/main.zig");
|
||||
const Config = @import("config.zig").Config;
|
||||
const input = @import("input.zig");
|
||||
const DevMode = @import("DevMode.zig");
|
||||
const App = @import("App.zig");
|
||||
|
||||
// Get native API access on certain platforms so we can do more customization.
|
||||
const glfwNative = glfw.Native(.{
|
||||
@ -36,6 +37,9 @@ const Renderer = renderer.Renderer;
|
||||
/// Allocator
|
||||
alloc: Allocator,
|
||||
|
||||
/// The app that this window is a part of.
|
||||
app: *App,
|
||||
|
||||
/// The font structures
|
||||
font_lib: font.Library,
|
||||
font_group: *font.GroupCache,
|
||||
@ -107,7 +111,7 @@ const Mouse = struct {
|
||||
/// Create a new window. This allocates and returns a pointer because we
|
||||
/// need a stable pointer for user data callbacks. Therefore, a stack-only
|
||||
/// initialization is not currently possible.
|
||||
pub fn create(alloc: Allocator, config: *const Config) !*Window {
|
||||
pub fn create(alloc: Allocator, app: *App, config: *const Config) !*Window {
|
||||
var self = try alloc.create(Window);
|
||||
errdefer alloc.destroy(self);
|
||||
|
||||
@ -313,6 +317,7 @@ pub fn create(alloc: Allocator, config: *const Config) !*Window {
|
||||
|
||||
self.* = .{
|
||||
.alloc = alloc,
|
||||
.app = app,
|
||||
.font_lib = font_lib,
|
||||
.font_group = font_group,
|
||||
.window = window,
|
||||
@ -386,7 +391,7 @@ pub fn create(alloc: Allocator, config: *const Config) !*Window {
|
||||
|
||||
// Give the renderer one more opportunity to finalize any window
|
||||
// setup on the main thread prior to spinning up the rendering thread.
|
||||
try renderer_impl.finalizeInit(window);
|
||||
try renderer_impl.finalizeWindowInit(window);
|
||||
|
||||
// Start our renderer thread
|
||||
self.renderer_thr = try std.Thread.spawn(
|
||||
@ -752,6 +757,13 @@ fn keyCallback(
|
||||
DevMode.instance.visible = !DevMode.instance.visible;
|
||||
win.queueRender() catch unreachable;
|
||||
} else log.warn("dev mode was not compiled into this binary", .{}),
|
||||
|
||||
.new_window => {
|
||||
_ = win.app.mailbox.push(.{
|
||||
.new_window = {},
|
||||
}, .{ .forever = {} });
|
||||
win.app.wakeup();
|
||||
},
|
||||
}
|
||||
|
||||
// Bindings always result in us ignoring the char if printable
|
||||
|
@ -157,6 +157,12 @@ pub const Config = struct {
|
||||
.{ .toggle_dev_mode = {} },
|
||||
);
|
||||
|
||||
try result.keybind.set.put(
|
||||
alloc,
|
||||
.{ .key = .up, .mods = .{ .super = true } },
|
||||
.{ .new_window = {} },
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -133,6 +133,9 @@ pub const Action = union(enum) {
|
||||
|
||||
/// Dev mode
|
||||
toggle_dev_mode: void,
|
||||
|
||||
/// Open a new terminal window.
|
||||
new_window: void,
|
||||
};
|
||||
|
||||
/// Trigger is the associated key state that can trigger an action.
|
||||
|
@ -128,8 +128,8 @@ pub fn main() !void {
|
||||
defer glfw.terminate();
|
||||
|
||||
// Run our app
|
||||
var app = try App.init(alloc, &config);
|
||||
defer app.deinit();
|
||||
var app = try App.create(alloc, &config);
|
||||
defer app.destroy();
|
||||
try app.run();
|
||||
}
|
||||
|
||||
|
@ -240,11 +240,6 @@ pub fn init(alloc: Allocator, font_group: *font.GroupCache) !Metal {
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Metal) void {
|
||||
if (DevMode.enabled) {
|
||||
imgui.ImplMetal.shutdown();
|
||||
imgui.ImplGlfw.shutdown();
|
||||
}
|
||||
|
||||
self.cells.deinit(self.alloc);
|
||||
|
||||
self.font_shaper.deinit();
|
||||
@ -255,7 +250,7 @@ pub fn deinit(self: *Metal) void {
|
||||
|
||||
/// This is called just prior to spinning up the renderer thread for
|
||||
/// final main thread setup requirements.
|
||||
pub fn finalizeInit(self: *const Metal, window: glfw.Window) !void {
|
||||
pub fn finalizeWindowInit(self: *const Metal, window: glfw.Window) !void {
|
||||
// Set our window backing layer to be our swapchain
|
||||
const nswindow = objc.Object.fromId(glfwNative.getCocoaWindow(window).?);
|
||||
const contentView = objc.Object.fromId(nswindow.getProperty(?*anyopaque, "contentView").?);
|
||||
@ -268,7 +263,12 @@ pub fn finalizeInit(self: *const Metal, window: glfw.Window) !void {
|
||||
const layer = contentView.getProperty(objc.Object, "layer");
|
||||
const scaleFactor = nswindow.getProperty(macos.graphics.c.CGFloat, "backingScaleFactor");
|
||||
layer.setProperty("contentsScale", scaleFactor);
|
||||
}
|
||||
|
||||
/// This is called only after the first window is opened. This may be
|
||||
/// called multiple times if all windows are closed and a new one is
|
||||
/// reopened.
|
||||
pub fn firstWindowInit(self: *const Metal, window: glfw.Window) !void {
|
||||
if (DevMode.enabled) {
|
||||
// Initialize for our window
|
||||
assert(imgui.ImplGlfw.initForOther(
|
||||
@ -279,6 +279,14 @@ pub fn finalizeInit(self: *const Metal, window: glfw.Window) !void {
|
||||
}
|
||||
}
|
||||
|
||||
/// This is called only when the last window is destroyed.
|
||||
pub fn lastWindowDeinit() void {
|
||||
if (DevMode.enabled) {
|
||||
imgui.ImplMetal.shutdown();
|
||||
imgui.ImplGlfw.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
/// Callback called by renderer.Thread when it begins.
|
||||
pub fn threadEnter(self: *const Metal, window: glfw.Window) !void {
|
||||
_ = self;
|
||||
|
@ -300,11 +300,6 @@ pub fn init(alloc: Allocator, font_group: *font.GroupCache) !OpenGL {
|
||||
}
|
||||
|
||||
pub fn deinit(self: *OpenGL) void {
|
||||
if (DevMode.enabled) {
|
||||
imgui.ImplOpenGL3.shutdown();
|
||||
imgui.ImplGlfw.shutdown();
|
||||
}
|
||||
|
||||
self.font_shaper.deinit();
|
||||
self.alloc.free(self.font_shaper.cell_buf);
|
||||
|
||||
@ -387,7 +382,17 @@ pub fn windowInit(window: glfw.Window) !void {
|
||||
|
||||
/// This is called just prior to spinning up the renderer thread for
|
||||
/// final main thread setup requirements.
|
||||
pub fn finalizeInit(self: *const OpenGL, window: glfw.Window) !void {
|
||||
pub fn finalizeWindowInit(self: *const OpenGL, window: glfw.Window) !void {
|
||||
_ = self;
|
||||
_ = window;
|
||||
}
|
||||
|
||||
/// This is called only after the first window is opened. This may be
|
||||
/// called multiple times if all windows are closed and a new one is
|
||||
/// reopened.
|
||||
pub fn firstWindowInit(self: *const OpenGL, window: glfw.Window) !void {
|
||||
_ = self;
|
||||
|
||||
if (DevMode.enabled) {
|
||||
// Initialize for our window
|
||||
assert(imgui.ImplGlfw.initForOpenGL(
|
||||
@ -396,9 +401,14 @@ pub fn finalizeInit(self: *const OpenGL, window: glfw.Window) !void {
|
||||
));
|
||||
assert(imgui.ImplOpenGL3.init("#version 330 core"));
|
||||
}
|
||||
}
|
||||
|
||||
// Call thread exit to clean up our context
|
||||
self.threadExit();
|
||||
/// This is called only when the last window is destroyed.
|
||||
pub fn lastWindowDeinit() void {
|
||||
if (DevMode.enabled) {
|
||||
imgui.ImplOpenGL3.shutdown();
|
||||
imgui.ImplGlfw.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
/// Callback called by renderer.Thread when it begins.
|
||||
|
Reference in New Issue
Block a user