rename grid to a renderer, extract to subfolder

"Grid" was a really antiquated name when it had both the screen state
AND the renderering functionality tied together. This hasn't been true
for a LONG time and it is long overdue that this is renamed to its
proper name.

This also begins setting up a folder structure to anticipate future
renderers and rendering functionality. I'm not working on any alternate
renderers right now so the interface isn't expected to be good, just
laying out the files in this way.
This commit is contained in:
Mitchell Hashimoto
2022-10-23 16:47:34 -07:00
parent c3d9ee1d85
commit de9731da1f
5 changed files with 74 additions and 61 deletions

View File

@ -45,13 +45,13 @@ pub fn update(self: *DevMode) !void {
if (imgui.treeNode("Atlas: Greyscale", .{ .default_open = true })) {
defer imgui.treePop();
const atlas = &window.font_group.atlas_greyscale;
try self.atlasInfo(atlas, @intCast(usize, window.grid.texture.id));
try self.atlasInfo(atlas, @intCast(usize, window.renderer.texture.id));
}
if (imgui.treeNode("Atlas: Color (Emoji)", .{ .default_open = true })) {
defer imgui.treePop();
const atlas = &window.font_group.atlas_color;
try self.atlasInfo(atlas, @intCast(usize, window.grid.texture_color.id));
try self.atlasInfo(atlas, @intCast(usize, window.renderer.texture_color.id));
}
}
}

View File

@ -9,7 +9,7 @@ const std = @import("std");
const builtin = @import("builtin");
const assert = std.debug.assert;
const Allocator = std.mem.Allocator;
const Grid = @import("Grid.zig");
const renderer = @import("renderer.zig");
const glfw = @import("glfw");
const gl = @import("opengl.zig");
const imgui = @import("imgui");
@ -53,8 +53,8 @@ imgui_ctx: if (DevMode.enabled) *imgui.Context else void,
/// Whether the window is currently focused
focused: bool,
/// The terminal grid attached to this window.
grid: Grid,
/// The renderer for this window.
renderer: renderer.OpenGL,
/// The underlying pty for this window.
pty: Pty,
@ -368,14 +368,14 @@ pub fn create(alloc: Allocator, loop: libuv.Loop, config: *const Config) !*Windo
// Create our terminal grid with the initial window size
const window_size = try window.getSize();
var grid = try Grid.init(alloc, font_group);
try grid.setScreenSize(.{ .width = window_size.width, .height = window_size.height });
grid.background = .{
var renderer_impl = try renderer.OpenGL.init(alloc, font_group);
try renderer_impl.setScreenSize(.{ .width = window_size.width, .height = window_size.height });
renderer_impl.background = .{
.r = config.background.r,
.g = config.background.g,
.b = config.background.b,
};
grid.foreground = .{
renderer_impl.foreground = .{
.r = config.foreground.r,
.g = config.foreground.g,
.b = config.foreground.b,
@ -384,14 +384,14 @@ pub fn create(alloc: Allocator, loop: libuv.Loop, config: *const Config) !*Windo
// Set a minimum size that is cols=10 h=4. This matches Mac's Terminal.app
// but is otherwise somewhat arbitrary.
try window.setSizeLimits(.{
.width = @floatToInt(u32, grid.cell_size.width * 10),
.height = @floatToInt(u32, grid.cell_size.height * 4),
.width = @floatToInt(u32, renderer_impl.cell_size.width * 10),
.height = @floatToInt(u32, renderer_impl.cell_size.height * 4),
}, .{ .width = null, .height = null });
// Create our pty
var pty = try Pty.open(.{
.ws_row = @intCast(u16, grid.size.rows),
.ws_col = @intCast(u16, grid.size.columns),
.ws_row = @intCast(u16, renderer_impl.size.rows),
.ws_col = @intCast(u16, renderer_impl.size.columns),
.ws_xpixel = @intCast(u16, window_size.width),
.ws_ypixel = @intCast(u16, window_size.height),
});
@ -434,7 +434,7 @@ pub fn create(alloc: Allocator, loop: libuv.Loop, config: *const Config) !*Windo
try stream.readStart(ttyReadAlloc, ttyRead);
// Create our terminal
var term = try terminal.Terminal.init(alloc, grid.size.columns, grid.size.rows);
var term = try terminal.Terminal.init(alloc, renderer_impl.size.columns, renderer_impl.size.rows);
errdefer term.deinit(alloc);
// Setup a timer for blinking the cursor
@ -463,7 +463,7 @@ pub fn create(alloc: Allocator, loop: libuv.Loop, config: *const Config) !*Windo
.window = window,
.cursor = cursor,
.focused = false,
.grid = grid,
.renderer = renderer_impl,
.pty = pty,
.command = cmd,
.mouse = .{},
@ -556,7 +556,7 @@ pub fn destroy(self: *Window) void {
log.err("error waiting for command to exit: {}", .{err});
self.terminal.deinit(self.alloc);
self.grid.deinit();
self.renderer.deinit();
self.window.destroy();
self.terminal_cursor.timer.close((struct {
@ -662,19 +662,19 @@ fn sizeCallback(window: glfw.Window, width: i32, height: i32) void {
// Update our grid so that the projections on render are correct.
const win = window.getUserPointer(Window) orelse return;
win.grid.setScreenSize(.{
win.renderer.setScreenSize(.{
.width = px_size.width,
.height = px_size.height,
}) catch |err| log.err("error updating grid screen size err={}", .{err});
// Update the size of our terminal state
win.terminal.resize(win.alloc, win.grid.size.columns, win.grid.size.rows) catch |err|
win.terminal.resize(win.alloc, win.renderer.size.columns, win.renderer.size.rows) catch |err|
log.err("error updating terminal size: {}", .{err});
// Update the size of our pty
win.pty.setSize(.{
.ws_row = @intCast(u16, win.grid.size.rows),
.ws_col = @intCast(u16, win.grid.size.columns),
.ws_row = @intCast(u16, win.renderer.size.rows),
.ws_col = @intCast(u16, win.renderer.size.columns),
.ws_xpixel = @intCast(u16, width),
.ws_ypixel = @intCast(u16, height),
}) catch |err| log.err("error updating pty screen size err={}", .{err});
@ -1007,7 +1007,7 @@ fn scrollCallback(window: glfw.Window, xoff: f64, yoff: f64) void {
// Positive is up
const sign: isize = if (yoff > 0) -1 else 1;
const delta: isize = sign * @max(@divFloor(win.grid.size.rows, 15), 1);
const delta: isize = sign * @max(@divFloor(win.renderer.size.rows, 15), 1);
log.info("scroll: delta={}", .{delta});
win.terminal.scrollViewport(.{ .delta = delta }) catch |err|
log.err("error scrolling viewport err={}", .{err});
@ -1367,10 +1367,10 @@ fn cursorPosCallback(
//
// the boundary point at which we consider selection or non-selection
const cell_xboundary = win.grid.cell_size.width * 0.6;
const cell_xboundary = win.renderer.cell_size.width * 0.6;
// first xpos of the clicked cell
const cell_xstart = @intToFloat(f32, win.mouse.left_click_point.x) * win.grid.cell_size.width;
const cell_xstart = @intToFloat(f32, win.mouse.left_click_point.x) * win.renderer.cell_size.width;
const cell_start_xpos = win.mouse.left_click_xpos - cell_xstart;
// If this is the same cell, then we only start the selection if weve
@ -1445,7 +1445,7 @@ fn posToViewport(self: Window, xpos: f64, ypos: f64) terminal.point.Viewport {
return .{
.x = if (xpos < 0) 0 else x: {
// Our cell is the mouse divided by cell width
const cell_width = @floatCast(f64, self.grid.cell_size.width);
const cell_width = @floatCast(f64, self.renderer.cell_size.width);
const x = @floatToInt(usize, xpos / cell_width);
// Can be off the screen if the user drags it out, so max
@ -1454,7 +1454,7 @@ fn posToViewport(self: Window, xpos: f64, ypos: f64) terminal.point.Viewport {
},
.y = if (ypos < 0) 0 else y: {
const cell_height = @floatCast(f64, self.grid.cell_size.height);
const cell_height = @floatCast(f64, self.renderer.cell_size.height);
const y = @floatToInt(usize, ypos / cell_height);
break :y @min(y, self.terminal.rows - 1);
},
@ -1584,23 +1584,23 @@ fn renderTimerCallback(t: *libuv.Timer) void {
// Setup our cursor settings
if (win.focused) {
win.grid.cursor_visible = win.terminal_cursor.visible and !win.terminal_cursor.blink;
win.grid.cursor_style = Grid.CursorStyle.fromTerminal(win.terminal_cursor.style) orelse .box;
win.renderer.cursor_visible = win.terminal_cursor.visible and !win.terminal_cursor.blink;
win.renderer.cursor_style = renderer.OpenGL.CursorStyle.fromTerminal(win.terminal_cursor.style) orelse .box;
} else {
win.grid.cursor_visible = true;
win.grid.cursor_style = .box_hollow;
win.renderer.cursor_visible = true;
win.renderer.cursor_style = .box_hollow;
}
// Calculate foreground and background colors
const bg = win.grid.background;
const fg = win.grid.foreground;
const bg = win.renderer.background;
const fg = win.renderer.foreground;
defer {
win.grid.background = bg;
win.grid.foreground = fg;
win.renderer.background = bg;
win.renderer.foreground = fg;
}
if (win.terminal.modes.reverse_colors) {
win.grid.background = fg;
win.grid.foreground = bg;
win.renderer.background = fg;
win.renderer.foreground = bg;
}
// Set our background
@ -1624,15 +1624,15 @@ fn renderTimerCallback(t: *libuv.Timer) void {
gl.clear(gl.c.GL_COLOR_BUFFER_BIT);
// For now, rebuild all cells
win.grid.rebuildCells(&win.terminal) catch |err|
win.renderer.rebuildCells(&win.terminal) catch |err|
log.err("error calling rebuildCells in render timer err={}", .{err});
// Finalize the cells prior to render
win.grid.finalizeCells(&win.terminal) catch |err|
win.renderer.finalizeCells(&win.terminal) catch |err|
log.err("error calling updateCells in render timer err={}", .{err});
// Render the grid
win.grid.render() catch |err| {
win.renderer.render() catch |err| {
log.err("error rendering grid: {}", .{err});
return;
};
@ -1812,7 +1812,7 @@ pub fn setMode(self: *Window, mode: terminal.Mode, enabled: bool) !void {
self.terminal.setDeccolmSupported(enabled);
// Force resize back to the window size
self.terminal.resize(self.alloc, self.grid.size.columns, self.grid.size.rows) catch |err|
self.terminal.resize(self.alloc, self.renderer.size.columns, self.renderer.size.rows) catch |err|
log.err("error updating terminal size: {}", .{err});
},

View File

@ -105,16 +105,15 @@ fn glfwErrorCallback(code: glfw.Error, desc: [:0]const u8) void {
test {
_ = @import("Atlas.zig");
_ = @import("Grid.zig");
_ = @import("Pty.zig");
_ = @import("Command.zig");
_ = @import("TempDir.zig");
_ = @import("font/main.zig");
_ = @import("renderer.zig");
_ = @import("terminal/Terminal.zig");
_ = @import("input.zig");
// Libraries
_ = @import("libuv");
_ = @import("segmented_pool.zig");
_ = @import("terminal/main.zig");

14
src/renderer.zig Normal file
View File

@ -0,0 +1,14 @@
//! Renderer implementation and utilities. The renderer is responsible for
//! taking the internal screen state and turning into some output format,
//! usually for a screen.
//!
//! The renderer is closely tied to the windowing system which usually
//! has to prepare the window for the given renderer using system-specific
//! APIs. The renderers in this package assume that the renderer is already
//! setup (OpenGL has a context, Vulkan has a surface, etc.)
pub const OpenGL = @import("renderer/OpenGL.zig");
test {
@import("std").testing.refAllDecls(@This());
}

View File

@ -1,18 +1,18 @@
//! Represents a single terminal grid.
const Grid = @This();
//! Rendering implementation for OpenGL.
pub const OpenGL = @This();
const std = @import("std");
const assert = std.debug.assert;
const testing = std.testing;
const Allocator = std.mem.Allocator;
const Atlas = @import("Atlas.zig");
const font = @import("font/main.zig");
const terminal = @import("terminal/main.zig");
const Atlas = @import("../Atlas.zig");
const font = @import("../font/main.zig");
const terminal = @import("../terminal/main.zig");
const Terminal = terminal.Terminal;
const gl = @import("opengl.zig");
const gl = @import("../opengl.zig");
const trace = @import("tracy").trace;
const math = @import("math.zig");
const lru = @import("lru.zig");
const math = @import("../math.zig");
const lru = @import("../lru.zig");
const log = std.log.scoped(.grid);
@ -152,7 +152,7 @@ const GPUCellMode = enum(u8) {
}
};
pub fn init(alloc: Allocator, font_group: *font.GroupCache) !Grid {
pub fn init(alloc: Allocator, font_group: *font.GroupCache) !OpenGL {
// Create the initial font shaper
var shape_buf = try alloc.alloc(font.Shaper.Cell, 1);
errdefer alloc.free(shape_buf);
@ -171,8 +171,8 @@ pub fn init(alloc: Allocator, font_group: *font.GroupCache) !Grid {
// Create our shader
const program = try gl.Program.createVF(
@embedFile("./shaders/cell.v.glsl"),
@embedFile("./shaders/cell.f.glsl"),
@embedFile("../shaders/cell.v.glsl"),
@embedFile("../shaders/cell.f.glsl"),
);
// Set our cell dimensions
@ -284,7 +284,7 @@ pub fn init(alloc: Allocator, font_group: *font.GroupCache) !Grid {
);
}
return Grid{
return OpenGL{
.alloc = alloc,
.cells = .{},
.cells_lru = CellsLRU.init(0),
@ -305,7 +305,7 @@ pub fn init(alloc: Allocator, font_group: *font.GroupCache) !Grid {
};
}
pub fn deinit(self: *Grid) void {
pub fn deinit(self: *OpenGL) void {
self.font_shaper.deinit();
self.alloc.free(self.font_shaper.cell_buf);
@ -327,7 +327,7 @@ pub fn deinit(self: *Grid) void {
///
/// Note this doesn't have to typically be manually called. Internally,
/// the renderer will do this when it needs more memory space.
pub fn rebuildCells(self: *Grid, term: *Terminal) !void {
pub fn rebuildCells(self: *OpenGL, term: *Terminal) !void {
const t = trace(@src());
defer t.end();
@ -456,7 +456,7 @@ pub fn rebuildCells(self: *Grid, term: *Terminal) !void {
/// This should be called prior to render to finalize the cells and prepare
/// for render. This performs tasks such as preparing the cursor, refreshing
/// the cells if necessary, etc.
pub fn finalizeCells(self: *Grid, term: *Terminal) !void {
pub fn finalizeCells(self: *OpenGL, term: *Terminal) !void {
// If we're out of space or we have no more Z-space, rebuild.
if (self.cells.items.len == self.cells.capacity) {
log.info("cell cache full, rebuilding from scratch", .{});
@ -468,7 +468,7 @@ pub fn finalizeCells(self: *Grid, term: *Terminal) !void {
try self.flushAtlas();
}
fn addCursor(self: *Grid, term: *Terminal) void {
fn addCursor(self: *OpenGL, term: *Terminal) void {
// Add the cursor
if (self.cursor_visible and term.screen.viewportIsBottom()) {
const cell = term.screen.getCell(
@ -503,7 +503,7 @@ fn addCursor(self: *Grid, term: *Terminal) void {
/// or not. If the cell wasn't updated, a full refreshCells call is
/// needed.
pub fn updateCell(
self: *Grid,
self: *OpenGL,
term: *Terminal,
cell: terminal.Screen.Cell,
shaper_cell: font.Shaper.Cell,
@ -690,7 +690,7 @@ pub fn updateCell(
/// Set the screen size for rendering. This will update the projection
/// used for the shader so that the scaling of the grid is correct.
pub fn setScreenSize(self: *Grid, dim: ScreenSize) !void {
pub fn setScreenSize(self: *OpenGL, dim: ScreenSize) !void {
// Update the projection uniform within our shader
const bind = try self.program.use();
defer bind.unbind();
@ -725,7 +725,7 @@ pub fn setScreenSize(self: *Grid, dim: ScreenSize) !void {
}
/// Updates the font texture atlas if it is dirty.
fn flushAtlas(self: *Grid) !void {
fn flushAtlas(self: *OpenGL) !void {
{
const atlas = &self.font_group.atlas_greyscale;
if (atlas.modified) {
@ -797,7 +797,7 @@ fn flushAtlas(self: *Grid) !void {
/// Render renders the current cell state. This will not modify any of
/// the cells.
pub fn render(self: *Grid) !void {
pub fn render(self: *OpenGL) !void {
const t = trace(@src());
defer t.end();