From bb902cf4e315a8ccfb6103c73e4d8f440bef1d6d Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 14 Apr 2022 21:07:16 -0700 Subject: [PATCH] new Window abstraction --- src/App.zig | 76 +++------------------------------ src/Window.zig | 109 ++++++++++++++++++++++++++++++++++++++++++++++++ src/main.zig | 2 +- src/old/App.zig | 52 +++++++++++++++++++++++ 4 files changed, 168 insertions(+), 71 deletions(-) create mode 100644 src/Window.zig create mode 100644 src/old/App.zig diff --git a/src/App.zig b/src/App.zig index f46ad09b0..fd3df4804 100644 --- a/src/App.zig +++ b/src/App.zig @@ -5,96 +5,32 @@ const App = @This(); const std = @import("std"); const Allocator = std.mem.Allocator; -const glfw = @import("glfw"); -const gl = @import("opengl.zig"); -const TextRenderer = @import("TextRenderer.zig"); -const Grid = @import("Grid.zig"); +const Window = @import("Window.zig"); const log = std.log; alloc: Allocator, -window: glfw.Window, - -text: TextRenderer, -grid: Grid, +window: *Window, /// 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) !App { - // Create our window - const window = try glfw.Window.create(640, 480, "ghostty", null, null, .{ - .context_version_major = 3, - .context_version_minor = 3, - .opengl_profile = .opengl_core_profile, - .opengl_forward_compat = true, - }); - errdefer window.destroy(); - - // Setup OpenGL - // NOTE(mitchellh): we probably want to extract this to a dedicated - // renderer at some point. - try glfw.makeContextCurrent(window); - try glfw.swapInterval(1); - - // Load OpenGL bindings - const version = try gl.glad.load(glfw.getProcAddress); - log.info("loaded OpenGL {}.{}", .{ - gl.glad.versionMajor(version), - gl.glad.versionMinor(version), - }); - - // Culling, probably not necessary. We have to change the winding - // order since our 0,0 is top-left. - gl.c.glEnable(gl.c.GL_CULL_FACE); - gl.c.glFrontFace(gl.c.GL_CW); - - // Blending for text - gl.c.glEnable(gl.c.GL_BLEND); - gl.c.glBlendFunc(gl.c.GL_SRC_ALPHA, gl.c.GL_ONE_MINUS_SRC_ALPHA); - - // Setup our text renderer - var texter = try TextRenderer.init(alloc); - errdefer texter.deinit(alloc); - - var grid = try Grid.init(alloc); - try grid.setScreenSize(.{ .width = 3000, .height = 1666 }); - - window.setSizeCallback((struct { - fn callback(_: glfw.Window, width: i32, height: i32) void { - log.info("set viewport {} {}", .{ width, height }); - try gl.viewport(0, 0, width, height); - } - }).callback); + // Create the window + const window = try Window.create(alloc); return App{ .alloc = alloc, .window = window, - .text = texter, - .grid = grid, }; } pub fn deinit(self: *App) void { - self.text.deinit(self.alloc); - self.window.destroy(); + self.window.destroy(self.alloc); self.* = undefined; } pub fn run(self: App) !void { - while (!self.window.shouldClose()) { - // Setup basic OpenGL settings - gl.clearColor(0.2, 0.3, 0.3, 1.0); - gl.clear(gl.c.GL_COLOR_BUFFER_BIT); - - try self.grid.render(); - //try self.text.render("sh $ /bin/bash -c \"echo hello\"", 25.0, 25.0, .{ 0.5, 0.8, 0.2 }); - - try self.window.swapBuffers(); - try glfw.waitEvents(); - } + try self.window.run(); } - -const vs_source = @embedFile("../shaders/shape.v.glsl"); -const fs_source = @embedFile("../shaders/shape.f.glsl"); diff --git a/src/Window.zig b/src/Window.zig new file mode 100644 index 000000000..d39643f7c --- /dev/null +++ b/src/Window.zig @@ -0,0 +1,109 @@ +//! Window represents a single OS window. +//! +//! NOTE(multi-window): This may be premature, but this abstraction is here +//! to pave the way One Day(tm) for multi-window support. At the time of +//! writing, we support exactly one window. +const Window = @This(); + +const std = @import("std"); +const Allocator = std.mem.Allocator; +const Grid = @import("Grid.zig"); +const glfw = @import("glfw"); +const gl = @import("opengl.zig"); + +const log = std.log.scoped(.window); + +/// The glfw window handle. +window: glfw.Window, + +/// The terminal grid attached to this window. +grid: Grid, + +/// 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) !*Window { + var self = try alloc.create(Window); + errdefer alloc.destroy(self); + + // Create our window + const window = try glfw.Window.create(640, 480, "ghostty", null, null, .{ + .context_version_major = 3, + .context_version_minor = 3, + .opengl_profile = .opengl_core_profile, + .opengl_forward_compat = true, + }); + errdefer window.destroy(); + + // NOTE(multi-window): We'll need to extract all the below into a + // dedicated renderer and consider the multi-threading (or at the very + // least: multi-OpenGL-context) implications. Since we don't support + // multiple windows right now, we just do it all here. + + // Setup OpenGL + try glfw.makeContextCurrent(window); + try glfw.swapInterval(1); + + // Load OpenGL bindings + const version = try gl.glad.load(glfw.getProcAddress); + log.info("loaded OpenGL {}.{}", .{ + gl.glad.versionMajor(version), + gl.glad.versionMinor(version), + }); + + // Culling, probably not necessary. We have to change the winding + // order since our 0,0 is top-left. + gl.c.glEnable(gl.c.GL_CULL_FACE); + gl.c.glFrontFace(gl.c.GL_CW); + + // Blending for text + gl.c.glEnable(gl.c.GL_BLEND); + gl.c.glBlendFunc(gl.c.GL_SRC_ALPHA, gl.c.GL_ONE_MINUS_SRC_ALPHA); + + // Create our terminal grid with a bogus initial size. + var grid = try Grid.init(alloc); + try grid.setScreenSize(.{ .width = 640, .height = 480 }); + + self.* = .{ + .window = window, + .grid = grid, + }; + + // Setup our callbacks and user data + window.setUserPointer(self); + window.setSizeCallback(sizeCallback); + + return self; +} + +pub fn destroy(self: *Window, alloc: Allocator) void { + alloc.destroy(self); +} + +pub fn run(self: Window) !void { + while (!self.window.shouldClose()) { + // Set our background + gl.clearColor(0.2, 0.3, 0.3, 1.0); + gl.clear(gl.c.GL_COLOR_BUFFER_BIT); + + // Render the grid + try self.grid.render(); + + // Swap + try self.window.swapBuffers(); + try glfw.waitEvents(); + } +} + +fn sizeCallback(window: glfw.Window, width: i32, height: i32) void { + const win = window.getUserPointer(Window) orelse return; + + // Update our grid so that the projections on render are correct. + win.grid.setScreenSize(.{ + .width = width, + .height = height, + }) catch unreachable; + + // Update our viewport for this context to be the entire window + try gl.viewport(0, 0, width, height); +} diff --git a/src/main.zig b/src/main.zig index e799c821b..3599615ae 100644 --- a/src/main.zig +++ b/src/main.zig @@ -8,7 +8,7 @@ pub fn main() !void { const gpa = general_purpose_allocator.allocator(); defer _ = general_purpose_allocator.deinit(); - // List our fonts + // Initialize glfw try glfw.init(.{}); defer glfw.terminate(); diff --git a/src/old/App.zig b/src/old/App.zig new file mode 100644 index 000000000..ea2d6d8d8 --- /dev/null +++ b/src/old/App.zig @@ -0,0 +1,52 @@ +//! App is the primary GUI application for ghostty. This builds the window, +//! sets up the renderer, etc. The primary run loop is started by calling +//! the "run" function. +const App = @This(); + +const std = @import("std"); +const Allocator = std.mem.Allocator; +const glfw = @import("glfw"); +const gl = @import("opengl.zig"); +const TextRenderer = @import("TextRenderer.zig"); +const Grid = @import("Grid.zig"); +const Window = @import("Window.zig"); + +const log = std.log; + +alloc: Allocator, + +window: glfw.Window, + +text: TextRenderer, +grid: Grid, + +/// 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) !App { + // Create the window + const window = try Window.create(alloc); + + return App{ + .window = window, + }; +} + +pub fn deinit(self: *App) void { + self.window.destroy(); + self.* = undefined; +} + +pub fn run(self: App) !void { + while (!self.window.shouldClose()) { + // Setup basic OpenGL settings + gl.clearColor(0.2, 0.3, 0.3, 1.0); + gl.clear(gl.c.GL_COLOR_BUFFER_BIT); + + try self.grid.render(); + //try self.text.render("sh $ /bin/bash -c \"echo hello\"", 25.0, 25.0, .{ 0.5, 0.8, 0.2 }); + + try self.window.swapBuffers(); + try glfw.waitEvents(); + } +}