Hook up new window, modify renderers

This commit is contained in:
Mitchell Hashimoto
2022-11-06 10:34:43 -08:00
parent a2edbb4698
commit ecbd119654
7 changed files with 90 additions and 33 deletions

View File

@ -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.

View File

@ -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

View File

@ -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;
} }

View File

@ -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.

View File

@ -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();
} }

View File

@ -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;

View File

@ -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.