mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-25 05:06:24 +03:00
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:
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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});
|
||||
},
|
||||
|
||||
|
@ -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
14
src/renderer.zig
Normal 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());
|
||||
}
|
@ -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();
|
||||
|
Reference in New Issue
Block a user