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