mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-15 00:06:09 +03:00
Merge pull request #2676 from ghostty-org/coordinate-space
Refactor how we pass around sizes
This commit is contained in:
301
src/Surface.zig
301
src/Surface.zig
@ -113,13 +113,8 @@ io_thr: std.Thread,
|
|||||||
/// Terminal inspector
|
/// Terminal inspector
|
||||||
inspector: ?*inspector.Inspector = null,
|
inspector: ?*inspector.Inspector = null,
|
||||||
|
|
||||||
/// All the cached sizes since we need them at various times.
|
/// All our sizing information.
|
||||||
screen_size: renderer.ScreenSize,
|
size: renderer.Size,
|
||||||
grid_size: renderer.GridSize,
|
|
||||||
cell_size: renderer.CellSize,
|
|
||||||
|
|
||||||
/// Explicit padding due to configuration
|
|
||||||
padding: renderer.Padding,
|
|
||||||
|
|
||||||
/// The configuration derived from the main config. We "derive" it so that
|
/// The configuration derived from the main config. We "derive" it so that
|
||||||
/// we don't have a shared pointer hanging around that we need to worry about
|
/// we don't have a shared pointer hanging around that we need to worry about
|
||||||
@ -329,6 +324,32 @@ const DerivedConfig = struct {
|
|||||||
for (self.links) |*link| link.regex.deinit();
|
for (self.links) |*link| link.regex.deinit();
|
||||||
self.arena.deinit();
|
self.arena.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn scaledPadding(self: *const DerivedConfig, x_dpi: f32, y_dpi: f32) renderer.Padding {
|
||||||
|
const padding_top: u32 = padding_top: {
|
||||||
|
const padding_top: f32 = @floatFromInt(self.window_padding_top);
|
||||||
|
break :padding_top @intFromFloat(@floor(padding_top * y_dpi / 72));
|
||||||
|
};
|
||||||
|
const padding_bottom: u32 = padding_bottom: {
|
||||||
|
const padding_bottom: f32 = @floatFromInt(self.window_padding_bottom);
|
||||||
|
break :padding_bottom @intFromFloat(@floor(padding_bottom * y_dpi / 72));
|
||||||
|
};
|
||||||
|
const padding_left: u32 = padding_left: {
|
||||||
|
const padding_left: f32 = @floatFromInt(self.window_padding_left);
|
||||||
|
break :padding_left @intFromFloat(@floor(padding_left * x_dpi / 72));
|
||||||
|
};
|
||||||
|
const padding_right: u32 = padding_right: {
|
||||||
|
const padding_right: f32 = @floatFromInt(self.window_padding_right);
|
||||||
|
break :padding_right @intFromFloat(@floor(padding_right * x_dpi / 72));
|
||||||
|
};
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.top = padding_top,
|
||||||
|
.bottom = padding_bottom,
|
||||||
|
.left = padding_left,
|
||||||
|
.right = padding_right,
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Create a new surface. This must be called from the main thread. The
|
/// Create a new surface. This must be called from the main thread. The
|
||||||
@ -378,28 +399,26 @@ pub fn init(
|
|||||||
// Pre-calculate our initial cell size ourselves.
|
// Pre-calculate our initial cell size ourselves.
|
||||||
const cell_size = font_grid.cellSize();
|
const cell_size = font_grid.cellSize();
|
||||||
|
|
||||||
// Convert our padding from points to pixels
|
// Build our size struct which has all the sizes we need.
|
||||||
const padding_top: u32 = padding_top: {
|
const size: renderer.Size = size: {
|
||||||
const padding_top: f32 = @floatFromInt(derived_config.window_padding_top);
|
var size: renderer.Size = .{
|
||||||
break :padding_top @intFromFloat(@floor(padding_top * y_dpi / 72));
|
.screen = screen: {
|
||||||
};
|
const surface_size = try rt_surface.getSize();
|
||||||
const padding_bottom: u32 = padding_bottom: {
|
break :screen .{
|
||||||
const padding_bottom: f32 = @floatFromInt(derived_config.window_padding_bottom);
|
.width = surface_size.width,
|
||||||
break :padding_bottom @intFromFloat(@floor(padding_bottom * y_dpi / 72));
|
.height = surface_size.height,
|
||||||
};
|
};
|
||||||
const padding_left: u32 = padding_left: {
|
},
|
||||||
const padding_left: f32 = @floatFromInt(derived_config.window_padding_left);
|
|
||||||
break :padding_left @intFromFloat(@floor(padding_left * x_dpi / 72));
|
.cell = font_grid.cellSize(),
|
||||||
};
|
.padding = derived_config.scaledPadding(x_dpi, y_dpi),
|
||||||
const padding_right: u32 = padding_right: {
|
};
|
||||||
const padding_right: f32 = @floatFromInt(derived_config.window_padding_right);
|
|
||||||
break :padding_right @intFromFloat(@floor(padding_right * x_dpi / 72));
|
if (derived_config.window_padding_balance) {
|
||||||
};
|
size.balancePadding();
|
||||||
const padding: renderer.Padding = .{
|
}
|
||||||
.top = padding_top,
|
|
||||||
.bottom = padding_bottom,
|
break :size size;
|
||||||
.left = padding_left,
|
|
||||||
.right = padding_right,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create our terminal grid with the initial size
|
// Create our terminal grid with the initial size
|
||||||
@ -407,26 +426,12 @@ pub fn init(
|
|||||||
var renderer_impl = try Renderer.init(alloc, .{
|
var renderer_impl = try Renderer.init(alloc, .{
|
||||||
.config = try Renderer.DerivedConfig.init(alloc, config),
|
.config = try Renderer.DerivedConfig.init(alloc, config),
|
||||||
.font_grid = font_grid,
|
.font_grid = font_grid,
|
||||||
.padding = .{
|
.size = size,
|
||||||
.explicit = padding,
|
|
||||||
.balance = config.@"window-padding-balance",
|
|
||||||
},
|
|
||||||
.surface_mailbox = .{ .surface = self, .app = app_mailbox },
|
.surface_mailbox = .{ .surface = self, .app = app_mailbox },
|
||||||
.rt_surface = rt_surface,
|
.rt_surface = rt_surface,
|
||||||
});
|
});
|
||||||
errdefer renderer_impl.deinit();
|
errdefer renderer_impl.deinit();
|
||||||
|
|
||||||
// Calculate our grid size based on known dimensions.
|
|
||||||
const surface_size = try rt_surface.getSize();
|
|
||||||
const screen_size: renderer.ScreenSize = .{
|
|
||||||
.width = surface_size.width,
|
|
||||||
.height = surface_size.height,
|
|
||||||
};
|
|
||||||
const grid_size = renderer.GridSize.init(
|
|
||||||
screen_size.subPadding(padding),
|
|
||||||
cell_size,
|
|
||||||
);
|
|
||||||
|
|
||||||
// The mutex used to protect our renderer state.
|
// The mutex used to protect our renderer state.
|
||||||
const mutex = try alloc.create(std.Thread.Mutex);
|
const mutex = try alloc.create(std.Thread.Mutex);
|
||||||
mutex.* = .{};
|
mutex.* = .{};
|
||||||
@ -467,10 +472,7 @@ pub fn init(
|
|||||||
.io = undefined,
|
.io = undefined,
|
||||||
.io_thread = io_thread,
|
.io_thread = io_thread,
|
||||||
.io_thr = undefined,
|
.io_thr = undefined,
|
||||||
.screen_size = .{ .width = 0, .height = 0 },
|
.size = size,
|
||||||
.grid_size = .{},
|
|
||||||
.cell_size = cell_size,
|
|
||||||
.padding = padding,
|
|
||||||
.config = derived_config,
|
.config = derived_config,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -510,10 +512,7 @@ pub fn init(
|
|||||||
errdefer io_mailbox.deinit(alloc);
|
errdefer io_mailbox.deinit(alloc);
|
||||||
|
|
||||||
try termio.Termio.init(&self.io, alloc, .{
|
try termio.Termio.init(&self.io, alloc, .{
|
||||||
.grid_size = grid_size,
|
.size = size,
|
||||||
.cell_size = cell_size,
|
|
||||||
.screen_size = screen_size,
|
|
||||||
.padding = padding,
|
|
||||||
.full_config = config,
|
.full_config = config,
|
||||||
.config = try termio.Termio.DerivedConfig.init(alloc, config),
|
.config = try termio.Termio.DerivedConfig.init(alloc, config),
|
||||||
.backend = .{ .exec = io_exec },
|
.backend = .{ .exec = io_exec },
|
||||||
@ -532,7 +531,7 @@ pub fn init(
|
|||||||
try rt_app.performAction(
|
try rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.cell_size,
|
.cell_size,
|
||||||
.{ .width = cell_size.width, .height = cell_size.height },
|
.{ .width = size.cell.width, .height = size.cell.height },
|
||||||
);
|
);
|
||||||
|
|
||||||
// Set a minimum size that is cols=10 h=4. This matches Mac's Terminal.app
|
// Set a minimum size that is cols=10 h=4. This matches Mac's Terminal.app
|
||||||
@ -541,8 +540,8 @@ pub fn init(
|
|||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.size_limit,
|
.size_limit,
|
||||||
.{
|
.{
|
||||||
.min_width = cell_size.width * 10,
|
.min_width = size.cell.width * 10,
|
||||||
.min_height = cell_size.height * 4,
|
.min_height = size.cell.height * 4,
|
||||||
// No max:
|
// No max:
|
||||||
.max_width = 0,
|
.max_width = 0,
|
||||||
.max_height = 0,
|
.max_height = 0,
|
||||||
@ -554,7 +553,7 @@ pub fn init(
|
|||||||
// init stuff we should get rid of this. But this is required because
|
// init stuff we should get rid of this. But this is required because
|
||||||
// sizeCallback does retina-aware stuff we don't do here and don't want
|
// sizeCallback does retina-aware stuff we don't do here and don't want
|
||||||
// to duplicate.
|
// to duplicate.
|
||||||
try self.sizeCallback(surface_size);
|
try self.resize(self.size.screen);
|
||||||
|
|
||||||
// Give the renderer one more opportunity to finalize any surface
|
// Give the renderer one more opportunity to finalize any surface
|
||||||
// setup on the main thread prior to spinning up the rendering thread.
|
// setup on the main thread prior to spinning up the rendering thread.
|
||||||
@ -594,12 +593,12 @@ pub fn init(
|
|||||||
// account for the padding so we get the exact correct grid size.
|
// account for the padding so we get the exact correct grid size.
|
||||||
const final_width: u32 =
|
const final_width: u32 =
|
||||||
@as(u32, @intFromFloat(@ceil(width_f32 / scale.x))) +
|
@as(u32, @intFromFloat(@ceil(width_f32 / scale.x))) +
|
||||||
padding.left +
|
size.padding.left +
|
||||||
padding.right;
|
size.padding.right;
|
||||||
const final_height: u32 =
|
const final_height: u32 =
|
||||||
@as(u32, @intFromFloat(@ceil(height_f32 / scale.y))) +
|
@as(u32, @intFromFloat(@ceil(height_f32 / scale.y))) +
|
||||||
padding.top +
|
size.padding.top +
|
||||||
padding.bottom;
|
size.padding.bottom;
|
||||||
|
|
||||||
rt_app.performAction(
|
rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
@ -1152,18 +1151,12 @@ pub fn selectionInfo(self: *const Surface) ?apprt.Selection {
|
|||||||
// Our sizes are all scaled so we need to send the unscaled values back.
|
// Our sizes are all scaled so we need to send the unscaled values back.
|
||||||
const content_scale = self.rt_surface.getContentScale() catch .{ .x = 1, .y = 1 };
|
const content_scale = self.rt_surface.getContentScale() catch .{ .x = 1, .y = 1 };
|
||||||
|
|
||||||
// We need to account for padding as well.
|
|
||||||
const pad = if (self.config.window_padding_balance)
|
|
||||||
renderer.Padding.balanced(self.screen_size, self.grid_size, self.cell_size)
|
|
||||||
else
|
|
||||||
self.padding;
|
|
||||||
|
|
||||||
const x: f64 = x: {
|
const x: f64 = x: {
|
||||||
// Simple x * cell width gives the left
|
// Simple x * cell width gives the left
|
||||||
var x: f64 = @floatFromInt(tl_coord.x * self.cell_size.width);
|
var x: f64 = @floatFromInt(tl_coord.x * self.size.cell.width);
|
||||||
|
|
||||||
// Add padding
|
// Add padding
|
||||||
x += @floatFromInt(pad.left);
|
x += @floatFromInt(self.size.padding.left);
|
||||||
|
|
||||||
// Scale
|
// Scale
|
||||||
x /= content_scale.x;
|
x /= content_scale.x;
|
||||||
@ -1173,14 +1166,14 @@ pub fn selectionInfo(self: *const Surface) ?apprt.Selection {
|
|||||||
|
|
||||||
const y: f64 = y: {
|
const y: f64 = y: {
|
||||||
// Simple y * cell height gives the top
|
// Simple y * cell height gives the top
|
||||||
var y: f64 = @floatFromInt(tl_coord.y * self.cell_size.height);
|
var y: f64 = @floatFromInt(tl_coord.y * self.size.cell.height);
|
||||||
|
|
||||||
// We want the text baseline
|
// We want the text baseline
|
||||||
y += @floatFromInt(self.cell_size.height);
|
y += @floatFromInt(self.size.cell.height);
|
||||||
y -= @floatFromInt(self.font_metrics.cell_baseline);
|
y -= @floatFromInt(self.font_metrics.cell_baseline);
|
||||||
|
|
||||||
// Add padding
|
// Add padding
|
||||||
y += @floatFromInt(pad.top);
|
y += @floatFromInt(self.size.padding.top);
|
||||||
|
|
||||||
// Scale
|
// Scale
|
||||||
y /= content_scale.y;
|
y /= content_scale.y;
|
||||||
@ -1221,10 +1214,10 @@ pub fn imePoint(self: *const Surface) apprt.IMEPos {
|
|||||||
|
|
||||||
const x: f64 = x: {
|
const x: f64 = x: {
|
||||||
// Simple x * cell width gives the top-left corner
|
// Simple x * cell width gives the top-left corner
|
||||||
var x: f64 = @floatFromInt(cursor.x * self.cell_size.width);
|
var x: f64 = @floatFromInt(cursor.x * self.size.cell.width);
|
||||||
|
|
||||||
// We want the midpoint
|
// We want the midpoint
|
||||||
x += @as(f64, @floatFromInt(self.cell_size.width)) / 2;
|
x += @as(f64, @floatFromInt(self.size.cell.width)) / 2;
|
||||||
|
|
||||||
// And scale it
|
// And scale it
|
||||||
x /= content_scale.x;
|
x /= content_scale.x;
|
||||||
@ -1234,10 +1227,10 @@ pub fn imePoint(self: *const Surface) apprt.IMEPos {
|
|||||||
|
|
||||||
const y: f64 = y: {
|
const y: f64 = y: {
|
||||||
// Simple x * cell width gives the top-left corner
|
// Simple x * cell width gives the top-left corner
|
||||||
var y: f64 = @floatFromInt(cursor.y * self.cell_size.height);
|
var y: f64 = @floatFromInt(cursor.y * self.size.cell.height);
|
||||||
|
|
||||||
// We want the bottom
|
// We want the bottom
|
||||||
y += @floatFromInt(self.cell_size.height);
|
y += @floatFromInt(self.size.cell.height);
|
||||||
|
|
||||||
// And scale it
|
// And scale it
|
||||||
y /= content_scale.y;
|
y /= content_scale.y;
|
||||||
@ -1363,24 +1356,12 @@ fn setSelection(self: *Surface, sel_: ?terminal.Selection) !void {
|
|||||||
/// Change the cell size for the terminal grid. This can happen as
|
/// Change the cell size for the terminal grid. This can happen as
|
||||||
/// a result of changing the font size at runtime.
|
/// a result of changing the font size at runtime.
|
||||||
fn setCellSize(self: *Surface, size: renderer.CellSize) !void {
|
fn setCellSize(self: *Surface, size: renderer.CellSize) !void {
|
||||||
// Update our new cell size for future calcs
|
// Update our cell size within our size struct
|
||||||
self.cell_size = size;
|
self.size.cell = size;
|
||||||
|
if (self.config.window_padding_balance) self.size.balancePadding();
|
||||||
// Update our grid_size
|
|
||||||
self.grid_size = renderer.GridSize.init(
|
|
||||||
self.screen_size.subPadding(self.padding),
|
|
||||||
self.cell_size,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Notify the terminal
|
// Notify the terminal
|
||||||
self.io.queueMessage(.{
|
self.io.queueMessage(.{ .resize = self.size }, .unlocked);
|
||||||
.resize = .{
|
|
||||||
.grid_size = self.grid_size,
|
|
||||||
.cell_size = self.cell_size,
|
|
||||||
.screen_size = self.screen_size,
|
|
||||||
.padding = self.padding,
|
|
||||||
},
|
|
||||||
}, .unlocked);
|
|
||||||
|
|
||||||
// Notify the window
|
// Notify the window
|
||||||
try self.rt_app.performAction(
|
try self.rt_app.performAction(
|
||||||
@ -1451,41 +1432,32 @@ pub fn sizeCallback(self: *Surface, size: apprt.SurfaceSize) !void {
|
|||||||
// Update our screen size, but only if it actually changed. And if
|
// Update our screen size, but only if it actually changed. And if
|
||||||
// the screen size didn't change, then our grid size could not have
|
// the screen size didn't change, then our grid size could not have
|
||||||
// changed, so we just return.
|
// changed, so we just return.
|
||||||
if (self.screen_size.equals(new_screen_size)) return;
|
if (self.size.screen.equals(new_screen_size)) return;
|
||||||
|
|
||||||
try self.resize(new_screen_size);
|
try self.resize(new_screen_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resize(self: *Surface, size: renderer.ScreenSize) !void {
|
fn resize(self: *Surface, size: renderer.ScreenSize) !void {
|
||||||
// Save our screen size
|
// Save our screen size
|
||||||
self.screen_size = size;
|
self.size.screen = size;
|
||||||
|
if (self.config.window_padding_balance) self.size.balancePadding();
|
||||||
|
|
||||||
// Recalculate our grid size. Because Ghostty supports fluid resizing,
|
// Recalculate our grid size. Because Ghostty supports fluid resizing,
|
||||||
// its possible the grid doesn't change at all even if the screen size changes.
|
// its possible the grid doesn't change at all even if the screen size changes.
|
||||||
// We have to update the IO thread no matter what because we send
|
// We have to update the IO thread no matter what because we send
|
||||||
// pixel-level sizing to the subprocess.
|
// pixel-level sizing to the subprocess.
|
||||||
self.grid_size = renderer.GridSize.init(
|
const grid_size = self.size.grid();
|
||||||
self.screen_size.subPadding(self.padding),
|
if (grid_size.columns < 5 and (self.size.padding.left > 0 or self.size.padding.right > 0)) {
|
||||||
self.cell_size,
|
|
||||||
);
|
|
||||||
if (self.grid_size.columns < 5 and (self.padding.left > 0 or self.padding.right > 0)) {
|
|
||||||
log.warn("WARNING: very small terminal grid detected with padding " ++
|
log.warn("WARNING: very small terminal grid detected with padding " ++
|
||||||
"set. Is your padding reasonable?", .{});
|
"set. Is your padding reasonable?", .{});
|
||||||
}
|
}
|
||||||
if (self.grid_size.rows < 2 and (self.padding.top > 0 or self.padding.bottom > 0)) {
|
if (grid_size.rows < 2 and (self.size.padding.top > 0 or self.size.padding.bottom > 0)) {
|
||||||
log.warn("WARNING: very small terminal grid detected with padding " ++
|
log.warn("WARNING: very small terminal grid detected with padding " ++
|
||||||
"set. Is your padding reasonable?", .{});
|
"set. Is your padding reasonable?", .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mail the IO thread
|
// Mail the IO thread
|
||||||
self.io.queueMessage(.{
|
self.io.queueMessage(.{ .resize = self.size }, .unlocked);
|
||||||
.resize = .{
|
|
||||||
.grid_size = self.grid_size,
|
|
||||||
.cell_size = self.cell_size,
|
|
||||||
.screen_size = self.screen_size,
|
|
||||||
.padding = self.padding,
|
|
||||||
},
|
|
||||||
}, .unlocked);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called to set the preedit state for character input. Preedit is used
|
/// Called to set the preedit state for character input. Preedit is used
|
||||||
@ -2144,7 +2116,8 @@ pub fn scrollCallback(
|
|||||||
if (!scroll_mods.precision) {
|
if (!scroll_mods.precision) {
|
||||||
// Calculate our magnitude of scroll. This is constant (not
|
// Calculate our magnitude of scroll. This is constant (not
|
||||||
// dependent on yoff).
|
// dependent on yoff).
|
||||||
const grid_rows_f64: f64 = @floatFromInt(self.grid_size.rows);
|
const grid_size = self.size.grid();
|
||||||
|
const grid_rows_f64: f64 = @floatFromInt(grid_size.rows);
|
||||||
const y_delta_f64: f64 = @round((grid_rows_f64 * self.config.mouse_scroll_multiplier) / 15.0);
|
const y_delta_f64: f64 = @round((grid_rows_f64 * self.config.mouse_scroll_multiplier) / 15.0);
|
||||||
const y_delta_usize: usize = @max(1, @as(usize, @intFromFloat(y_delta_f64)));
|
const y_delta_usize: usize = @max(1, @as(usize, @intFromFloat(y_delta_f64)));
|
||||||
|
|
||||||
@ -2171,7 +2144,7 @@ pub fn scrollCallback(
|
|||||||
|
|
||||||
// If the new offset is less than a single unit of scroll, we save
|
// If the new offset is less than a single unit of scroll, we save
|
||||||
// the new pending value and do not scroll yet.
|
// the new pending value and do not scroll yet.
|
||||||
const cell_size: f64 = @floatFromInt(self.cell_size.height);
|
const cell_size: f64 = @floatFromInt(self.size.cell.height);
|
||||||
if (@abs(poff) < cell_size) {
|
if (@abs(poff) < cell_size) {
|
||||||
self.mouse.pending_scroll_y = poff;
|
self.mouse.pending_scroll_y = poff;
|
||||||
break :y .{};
|
break :y .{};
|
||||||
@ -2201,7 +2174,7 @@ pub fn scrollCallback(
|
|||||||
|
|
||||||
const xoff_adjusted: f64 = xoff * self.config.mouse_scroll_multiplier;
|
const xoff_adjusted: f64 = xoff * self.config.mouse_scroll_multiplier;
|
||||||
const poff: f64 = self.mouse.pending_scroll_x + xoff_adjusted;
|
const poff: f64 = self.mouse.pending_scroll_x + xoff_adjusted;
|
||||||
const cell_size: f64 = @floatFromInt(self.cell_size.width);
|
const cell_size: f64 = @floatFromInt(self.size.cell.width);
|
||||||
if (@abs(poff) < cell_size) {
|
if (@abs(poff) < cell_size) {
|
||||||
self.mouse.pending_scroll_x = poff;
|
self.mouse.pending_scroll_x = poff;
|
||||||
break :x .{};
|
break :x .{};
|
||||||
@ -2326,36 +2299,15 @@ pub fn contentScaleCallback(self: *Surface, content_scale: apprt.ContentScale) !
|
|||||||
|
|
||||||
try self.setFontSize(size);
|
try self.setFontSize(size);
|
||||||
|
|
||||||
// Update our padding which is dependent on DPI.
|
// Update our padding which is dependent on DPI. We only do this for
|
||||||
self.padding = padding: {
|
// unbalanced padding since balanced padding is not dependent on DPI.
|
||||||
const padding_top: u32 = padding_top: {
|
if (!self.config.window_padding_balance) {
|
||||||
const padding_top: f32 = @floatFromInt(self.config.window_padding_top);
|
self.size.padding = self.config.scaledPadding(x_dpi, y_dpi);
|
||||||
break :padding_top @intFromFloat(@floor(padding_top * y_dpi / 72));
|
}
|
||||||
};
|
|
||||||
const padding_bottom: u32 = padding_bottom: {
|
|
||||||
const padding_bottom: f32 = @floatFromInt(self.config.window_padding_bottom);
|
|
||||||
break :padding_bottom @intFromFloat(@floor(padding_bottom * y_dpi / 72));
|
|
||||||
};
|
|
||||||
const padding_left: u32 = padding_left: {
|
|
||||||
const padding_left: f32 = @floatFromInt(self.config.window_padding_left);
|
|
||||||
break :padding_left @intFromFloat(@floor(padding_left * x_dpi / 72));
|
|
||||||
};
|
|
||||||
const padding_right: u32 = padding_right: {
|
|
||||||
const padding_right: f32 = @floatFromInt(self.config.window_padding_right);
|
|
||||||
break :padding_right @intFromFloat(@floor(padding_right * x_dpi / 72));
|
|
||||||
};
|
|
||||||
|
|
||||||
break :padding .{
|
|
||||||
.top = padding_top,
|
|
||||||
.bottom = padding_bottom,
|
|
||||||
.left = padding_left,
|
|
||||||
.right = padding_right,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// Force a resize event because the change in padding will affect
|
// Force a resize event because the change in padding will affect
|
||||||
// pixel-level changes to the renderer and viewport.
|
// pixel-level changes to the renderer and viewport.
|
||||||
try self.resize(self.screen_size);
|
try self.resize(self.size.screen);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The type of action to report for a mouse event.
|
/// The type of action to report for a mouse event.
|
||||||
@ -2394,8 +2346,8 @@ fn mouseReport(
|
|||||||
// We always report release events no matter where they happen.
|
// We always report release events no matter where they happen.
|
||||||
if (action != .release) {
|
if (action != .release) {
|
||||||
const pos_out_viewport = pos_out_viewport: {
|
const pos_out_viewport = pos_out_viewport: {
|
||||||
const max_x: f32 = @floatFromInt(self.screen_size.width);
|
const max_x: f32 = @floatFromInt(self.size.screen.width);
|
||||||
const max_y: f32 = @floatFromInt(self.screen_size.height);
|
const max_y: f32 = @floatFromInt(self.size.screen.height);
|
||||||
break :pos_out_viewport pos.x < 0 or pos.y < 0 or
|
break :pos_out_viewport pos.x < 0 or pos.y < 0 or
|
||||||
pos.x > max_x or pos.y > max_y;
|
pos.x > max_x or pos.y > max_y;
|
||||||
};
|
};
|
||||||
@ -2554,15 +2506,22 @@ fn mouseReport(
|
|||||||
.sgr_pixels => {
|
.sgr_pixels => {
|
||||||
// Final character to send in the CSI
|
// Final character to send in the CSI
|
||||||
const final: u8 = if (action == .release) 'm' else 'M';
|
const final: u8 = if (action == .release) 'm' else 'M';
|
||||||
const adjusted = self.posAdjusted(pos.x, pos.y);
|
|
||||||
|
// The position has to be adjusted to the terminal space.
|
||||||
|
const coord: renderer.Coordinate.Terminal = (renderer.Coordinate{
|
||||||
|
.surface = .{
|
||||||
|
.x = pos.x,
|
||||||
|
.y = pos.y,
|
||||||
|
},
|
||||||
|
}).convert(.terminal, self.size).terminal;
|
||||||
|
|
||||||
// Response always is at least 4 chars, so this leaves the
|
// Response always is at least 4 chars, so this leaves the
|
||||||
// remainder for numbers which are very large...
|
// remainder for numbers which are very large...
|
||||||
var data: termio.Message.WriteReq.Small.Array = undefined;
|
var data: termio.Message.WriteReq.Small.Array = undefined;
|
||||||
const resp = try std.fmt.bufPrint(&data, "\x1B[<{d};{d};{d}{c}", .{
|
const resp = try std.fmt.bufPrint(&data, "\x1B[<{d};{d};{d}{c}", .{
|
||||||
button_code,
|
button_code,
|
||||||
@as(i32, @intFromFloat(@round(adjusted.x))),
|
@as(i32, @intFromFloat(@round(coord.x))),
|
||||||
@as(i32, @intFromFloat(@round(adjusted.y))),
|
@as(i32, @intFromFloat(@round(coord.y))),
|
||||||
final,
|
final,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -2817,7 +2776,7 @@ pub fn mouseButtonCallback(
|
|||||||
// If we move our cursor too much between clicks then we reset
|
// If we move our cursor too much between clicks then we reset
|
||||||
// the multi-click state.
|
// the multi-click state.
|
||||||
if (self.mouse.left_click_count > 0) {
|
if (self.mouse.left_click_count > 0) {
|
||||||
const max_distance: f64 = @floatFromInt(self.cell_size.width);
|
const max_distance: f64 = @floatFromInt(self.size.cell.width);
|
||||||
const distance = @sqrt(
|
const distance = @sqrt(
|
||||||
std.math.pow(f64, pos.x - self.mouse.left_click_xpos, 2) +
|
std.math.pow(f64, pos.x - self.mouse.left_click_xpos, 2) +
|
||||||
std.math.pow(f64, pos.y - self.mouse.left_click_ypos, 2),
|
std.math.pow(f64, pos.y - self.mouse.left_click_ypos, 2),
|
||||||
@ -3317,8 +3276,8 @@ pub fn cursorPosCallback(
|
|||||||
// We allow for a 1 pixel buffer at the top and bottom to detect
|
// We allow for a 1 pixel buffer at the top and bottom to detect
|
||||||
// scroll even in full screen windows.
|
// scroll even in full screen windows.
|
||||||
// Note: one day, we can change this from distance to time based if we want.
|
// Note: one day, we can change this from distance to time based if we want.
|
||||||
//log.warn("CURSOR POS: {} {}", .{ pos, self.screen_size });
|
//log.warn("CURSOR POS: {} {}", .{ pos, self.size.screen });
|
||||||
const max_y: f32 = @floatFromInt(self.screen_size.height);
|
const max_y: f32 = @floatFromInt(self.size.screen.height);
|
||||||
if (pos.y <= 1 or pos.y > max_y - 1) {
|
if (pos.y <= 1 or pos.y > max_y - 1) {
|
||||||
const delta: isize = if (pos.y < 0) -1 else 1;
|
const delta: isize = if (pos.y < 0) -1 else 1;
|
||||||
try self.io.terminal.scrollViewport(.{ .delta = delta });
|
try self.io.terminal.scrollViewport(.{ .delta = delta });
|
||||||
@ -3477,11 +3436,11 @@ fn dragLeftClickSingle(
|
|||||||
const click_pin = self.mouse.left_click_pin.?.*;
|
const click_pin = self.mouse.left_click_pin.?.*;
|
||||||
|
|
||||||
// the boundary point at which we consider selection or non-selection
|
// the boundary point at which we consider selection or non-selection
|
||||||
const cell_width_f64: f64 = @floatFromInt(self.cell_size.width);
|
const cell_width_f64: f64 = @floatFromInt(self.size.cell.width);
|
||||||
const cell_xboundary = cell_width_f64 * 0.6;
|
const cell_xboundary = cell_width_f64 * 0.6;
|
||||||
|
|
||||||
// first xpos of the clicked cell adjusted for padding
|
// first xpos of the clicked cell adjusted for padding
|
||||||
const left_padding_f64: f64 = @as(f64, @floatFromInt(self.padding.left));
|
const left_padding_f64: f64 = @as(f64, @floatFromInt(self.size.padding.left));
|
||||||
const cell_xstart = @as(f64, @floatFromInt(click_pin.x)) * cell_width_f64;
|
const cell_xstart = @as(f64, @floatFromInt(click_pin.x)) * cell_width_f64;
|
||||||
const cell_start_xpos = self.mouse.left_click_xpos - cell_xstart - left_padding_f64;
|
const cell_start_xpos = self.mouse.left_click_xpos - cell_xstart - left_padding_f64;
|
||||||
|
|
||||||
@ -3631,43 +3590,11 @@ pub fn colorSchemeCallback(self: *Surface, scheme: apprt.ColorScheme) !void {
|
|||||||
if (report) try self.reportColorScheme();
|
if (report) try self.reportColorScheme();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn posAdjusted(self: Surface, xpos: f64, ypos: f64) struct { x: f64, y: f64 } {
|
|
||||||
const pad = if (self.config.window_padding_balance)
|
|
||||||
renderer.Padding.balanced(self.screen_size, self.grid_size, self.cell_size)
|
|
||||||
else
|
|
||||||
self.padding;
|
|
||||||
|
|
||||||
return .{
|
|
||||||
.x = xpos - @as(f64, @floatFromInt(pad.left)),
|
|
||||||
.y = ypos - @as(f64, @floatFromInt(pad.top)),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn posToViewport(self: Surface, xpos: f64, ypos: f64) terminal.point.Coordinate {
|
pub fn posToViewport(self: Surface, xpos: f64, ypos: f64) terminal.point.Coordinate {
|
||||||
// xpos/ypos need to be adjusted for window padding
|
// Get our grid cell
|
||||||
// (i.e. "window-padding-*" settings.
|
const coord: renderer.Coordinate = .{ .surface = .{ .x = xpos, .y = ypos } };
|
||||||
const adjusted = self.posAdjusted(xpos, ypos);
|
const grid = coord.convert(.grid, self.size).grid;
|
||||||
|
return .{ .x = grid.x, .y = grid.y };
|
||||||
// adjusted.x and adjusted.y can be negative if while dragging, the user moves the
|
|
||||||
// mouse off the surface. Likewise, they can be larger than our surface
|
|
||||||
// width if the user drags out of the surface positively.
|
|
||||||
return .{
|
|
||||||
.x = if (adjusted.x < 0) 0 else x: {
|
|
||||||
// Our cell is the mouse divided by cell width
|
|
||||||
const cell_width: f64 = @floatFromInt(self.cell_size.width);
|
|
||||||
const x: usize = @intFromFloat(adjusted.x / cell_width);
|
|
||||||
|
|
||||||
// Can be off the screen if the user drags it out, so max
|
|
||||||
// it out on our available columns
|
|
||||||
break :x @min(x, self.grid_size.columns - 1);
|
|
||||||
},
|
|
||||||
|
|
||||||
.y = if (adjusted.y < 0) 0 else y: {
|
|
||||||
const cell_height: f64 = @floatFromInt(self.cell_size.height);
|
|
||||||
const y: usize = @intFromFloat(adjusted.y / cell_height);
|
|
||||||
break :y @min(y, self.grid_size.rows - 1);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Scroll to the bottom of the viewport.
|
/// Scroll to the bottom of the viewport.
|
||||||
@ -3905,21 +3832,21 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool
|
|||||||
},
|
},
|
||||||
|
|
||||||
.scroll_page_up => {
|
.scroll_page_up => {
|
||||||
const rows: isize = @intCast(self.grid_size.rows);
|
const rows: isize = @intCast(self.size.grid().rows);
|
||||||
self.io.queueMessage(.{
|
self.io.queueMessage(.{
|
||||||
.scroll_viewport = .{ .delta = -1 * rows },
|
.scroll_viewport = .{ .delta = -1 * rows },
|
||||||
}, .unlocked);
|
}, .unlocked);
|
||||||
},
|
},
|
||||||
|
|
||||||
.scroll_page_down => {
|
.scroll_page_down => {
|
||||||
const rows: isize = @intCast(self.grid_size.rows);
|
const rows: isize = @intCast(self.size.grid().rows);
|
||||||
self.io.queueMessage(.{
|
self.io.queueMessage(.{
|
||||||
.scroll_viewport = .{ .delta = rows },
|
.scroll_viewport = .{ .delta = rows },
|
||||||
}, .unlocked);
|
}, .unlocked);
|
||||||
},
|
},
|
||||||
|
|
||||||
.scroll_page_fractional => |fraction| {
|
.scroll_page_fractional => |fraction| {
|
||||||
const rows: f32 = @floatFromInt(self.grid_size.rows);
|
const rows: f32 = @floatFromInt(self.size.grid().rows);
|
||||||
const delta: isize = @intFromFloat(@trunc(fraction * rows));
|
const delta: isize = @intFromFloat(@trunc(fraction * rows));
|
||||||
self.io.queueMessage(.{
|
self.io.queueMessage(.{
|
||||||
.scroll_viewport = .{ .delta = delta },
|
.scroll_viewport = .{ .delta = delta },
|
||||||
@ -3989,7 +3916,7 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool
|
|||||||
.left => .left,
|
.left => .left,
|
||||||
.down => .down,
|
.down => .down,
|
||||||
.up => .up,
|
.up => .up,
|
||||||
.auto => if (self.screen_size.width > self.screen_size.height)
|
.auto => if (self.size.screen.width > self.size.screen.height)
|
||||||
.right
|
.right
|
||||||
else
|
else
|
||||||
.down,
|
.down,
|
||||||
|
@ -1447,13 +1447,14 @@ pub const CAPI = struct {
|
|||||||
|
|
||||||
/// Return the size information a surface has.
|
/// Return the size information a surface has.
|
||||||
export fn ghostty_surface_size(surface: *Surface) SurfaceSize {
|
export fn ghostty_surface_size(surface: *Surface) SurfaceSize {
|
||||||
|
const grid_size = surface.core_surface.size.grid();
|
||||||
return .{
|
return .{
|
||||||
.columns = surface.core_surface.grid_size.columns,
|
.columns = grid_size.columns,
|
||||||
.rows = surface.core_surface.grid_size.rows,
|
.rows = grid_size.rows,
|
||||||
.width_px = surface.core_surface.screen_size.width,
|
.width_px = surface.core_surface.size.screen.width,
|
||||||
.height_px = surface.core_surface.screen_size.height,
|
.height_px = surface.core_surface.size.screen.height,
|
||||||
.cell_width_px = surface.core_surface.cell_size.width,
|
.cell_width_px = surface.core_surface.size.cell.width,
|
||||||
.cell_height_px = surface.core_surface.cell_size.height,
|
.cell_height_px = surface.core_surface.size.cell.height,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,13 +94,14 @@ fn gtkUpdate(ud: ?*anyopaque) callconv(.C) c.gboolean {
|
|||||||
return c.FALSE;
|
return c.FALSE;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const grid_size = surface.core_surface.size.grid();
|
||||||
var buf: [32]u8 = undefined;
|
var buf: [32]u8 = undefined;
|
||||||
const text = std.fmt.bufPrintZ(
|
const text = std.fmt.bufPrintZ(
|
||||||
&buf,
|
&buf,
|
||||||
"{d}c ⨯ {d}r",
|
"{d}c ⨯ {d}r",
|
||||||
.{
|
.{
|
||||||
surface.core_surface.grid_size.columns,
|
grid_size.columns,
|
||||||
surface.core_surface.grid_size.rows,
|
grid_size.rows,
|
||||||
},
|
},
|
||||||
) catch |err| {
|
) catch |err| {
|
||||||
log.err("unable to format text: {}", .{err});
|
log.err("unable to format text: {}", .{err});
|
||||||
|
@ -177,29 +177,30 @@ fn beforeSend(
|
|||||||
const obj = sentry.Value.initObject();
|
const obj = sentry.Value.initObject();
|
||||||
errdefer obj.decref();
|
errdefer obj.decref();
|
||||||
const surface = thr_state.surface;
|
const surface = thr_state.surface;
|
||||||
|
const grid_size = surface.size.grid();
|
||||||
obj.set(
|
obj.set(
|
||||||
"screen-width",
|
"screen-width",
|
||||||
sentry.Value.initInt32(std.math.cast(i32, surface.screen_size.width) orelse -1),
|
sentry.Value.initInt32(std.math.cast(i32, surface.size.screen.width) orelse -1),
|
||||||
);
|
);
|
||||||
obj.set(
|
obj.set(
|
||||||
"screen-height",
|
"screen-height",
|
||||||
sentry.Value.initInt32(std.math.cast(i32, surface.screen_size.height) orelse -1),
|
sentry.Value.initInt32(std.math.cast(i32, surface.size.screen.height) orelse -1),
|
||||||
);
|
);
|
||||||
obj.set(
|
obj.set(
|
||||||
"grid-columns",
|
"grid-columns",
|
||||||
sentry.Value.initInt32(std.math.cast(i32, surface.grid_size.columns) orelse -1),
|
sentry.Value.initInt32(std.math.cast(i32, grid_size.columns) orelse -1),
|
||||||
);
|
);
|
||||||
obj.set(
|
obj.set(
|
||||||
"grid-rows",
|
"grid-rows",
|
||||||
sentry.Value.initInt32(std.math.cast(i32, surface.grid_size.rows) orelse -1),
|
sentry.Value.initInt32(std.math.cast(i32, grid_size.rows) orelse -1),
|
||||||
);
|
);
|
||||||
obj.set(
|
obj.set(
|
||||||
"cell-width",
|
"cell-width",
|
||||||
sentry.Value.initInt32(std.math.cast(i32, surface.cell_size.width) orelse -1),
|
sentry.Value.initInt32(std.math.cast(i32, surface.size.cell.width) orelse -1),
|
||||||
);
|
);
|
||||||
obj.set(
|
obj.set(
|
||||||
"cell-height",
|
"cell-height",
|
||||||
sentry.Value.initInt32(std.math.cast(i32, surface.cell_size.height) orelse -1),
|
sentry.Value.initInt32(std.math.cast(i32, surface.size.cell.height) orelse -1),
|
||||||
);
|
);
|
||||||
|
|
||||||
contexts.set("Dimensions", obj);
|
contexts.set("Dimensions", obj);
|
||||||
|
@ -11,6 +11,7 @@ const cimgui = @import("cimgui");
|
|||||||
const Surface = @import("../Surface.zig");
|
const Surface = @import("../Surface.zig");
|
||||||
const font = @import("../font/main.zig");
|
const font = @import("../font/main.zig");
|
||||||
const input = @import("../input.zig");
|
const input = @import("../input.zig");
|
||||||
|
const renderer = @import("../renderer.zig");
|
||||||
const terminal = @import("../terminal/main.zig");
|
const terminal = @import("../terminal/main.zig");
|
||||||
const inspector = @import("main.zig");
|
const inspector = @import("main.zig");
|
||||||
|
|
||||||
@ -641,8 +642,8 @@ fn renderSizeWindow(self: *Inspector) void {
|
|||||||
_ = cimgui.c.igTableSetColumnIndex(1);
|
_ = cimgui.c.igTableSetColumnIndex(1);
|
||||||
cimgui.c.igText(
|
cimgui.c.igText(
|
||||||
"%dpx x %dpx",
|
"%dpx x %dpx",
|
||||||
self.surface.screen_size.width,
|
self.surface.size.screen.width,
|
||||||
self.surface.screen_size.height,
|
self.surface.size.screen.height,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -656,10 +657,11 @@ fn renderSizeWindow(self: *Inspector) void {
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
_ = cimgui.c.igTableSetColumnIndex(1);
|
_ = cimgui.c.igTableSetColumnIndex(1);
|
||||||
|
const grid_size = self.surface.size.grid();
|
||||||
cimgui.c.igText(
|
cimgui.c.igText(
|
||||||
"%dc x %dr",
|
"%dc x %dr",
|
||||||
self.surface.grid_size.columns,
|
grid_size.columns,
|
||||||
self.surface.grid_size.rows,
|
grid_size.rows,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -675,8 +677,8 @@ fn renderSizeWindow(self: *Inspector) void {
|
|||||||
_ = cimgui.c.igTableSetColumnIndex(1);
|
_ = cimgui.c.igTableSetColumnIndex(1);
|
||||||
cimgui.c.igText(
|
cimgui.c.igText(
|
||||||
"%dpx x %dpx",
|
"%dpx x %dpx",
|
||||||
self.surface.cell_size.width,
|
self.surface.size.cell.width,
|
||||||
self.surface.cell_size.height,
|
self.surface.size.cell.height,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -692,10 +694,10 @@ fn renderSizeWindow(self: *Inspector) void {
|
|||||||
_ = cimgui.c.igTableSetColumnIndex(1);
|
_ = cimgui.c.igTableSetColumnIndex(1);
|
||||||
cimgui.c.igText(
|
cimgui.c.igText(
|
||||||
"T=%d B=%d L=%d R=%d px",
|
"T=%d B=%d L=%d R=%d px",
|
||||||
self.surface.padding.top,
|
self.surface.size.padding.top,
|
||||||
self.surface.padding.bottom,
|
self.surface.size.padding.bottom,
|
||||||
self.surface.padding.left,
|
self.surface.size.padding.left,
|
||||||
self.surface.padding.right,
|
self.surface.size.padding.right,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -785,7 +787,13 @@ fn renderSizeWindow(self: *Inspector) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const adjusted = self.surface.posAdjusted(self.mouse.last_xpos, self.mouse.last_ypos);
|
const coord: renderer.Coordinate.Terminal = (renderer.Coordinate{
|
||||||
|
.surface = .{
|
||||||
|
.x = self.mouse.last_xpos,
|
||||||
|
.y = self.mouse.last_ypos,
|
||||||
|
},
|
||||||
|
}).convert(.terminal, self.surface.size).terminal;
|
||||||
|
|
||||||
cimgui.c.igTableNextRow(cimgui.c.ImGuiTableRowFlags_None, 0);
|
cimgui.c.igTableNextRow(cimgui.c.ImGuiTableRowFlags_None, 0);
|
||||||
{
|
{
|
||||||
_ = cimgui.c.igTableSetColumnIndex(0);
|
_ = cimgui.c.igTableSetColumnIndex(0);
|
||||||
@ -795,8 +803,8 @@ fn renderSizeWindow(self: *Inspector) void {
|
|||||||
_ = cimgui.c.igTableSetColumnIndex(1);
|
_ = cimgui.c.igTableSetColumnIndex(1);
|
||||||
cimgui.c.igText(
|
cimgui.c.igText(
|
||||||
"(%dpx, %dpx)",
|
"(%dpx, %dpx)",
|
||||||
@as(i64, @intFromFloat(adjusted.x)),
|
@as(i64, @intFromFloat(coord.x)),
|
||||||
@as(i64, @intFromFloat(adjusted.y)),
|
@as(i64, @intFromFloat(coord.y)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,8 @@ pub const Thread = @import("renderer/Thread.zig");
|
|||||||
pub const State = @import("renderer/State.zig");
|
pub const State = @import("renderer/State.zig");
|
||||||
pub const CursorStyle = cursor.Style;
|
pub const CursorStyle = cursor.Style;
|
||||||
pub const Message = message.Message;
|
pub const Message = message.Message;
|
||||||
|
pub const Size = size.Size;
|
||||||
|
pub const Coordinate = size.Coordinate;
|
||||||
pub const CellSize = size.CellSize;
|
pub const CellSize = size.CellSize;
|
||||||
pub const ScreenSize = size.ScreenSize;
|
pub const ScreenSize = size.ScreenSize;
|
||||||
pub const GridSize = size.GridSize;
|
pub const GridSize = size.GridSize;
|
||||||
|
@ -70,12 +70,8 @@ surface_mailbox: apprt.surface.Mailbox,
|
|||||||
/// Current font metrics defining our grid.
|
/// Current font metrics defining our grid.
|
||||||
grid_metrics: font.face.Metrics,
|
grid_metrics: font.face.Metrics,
|
||||||
|
|
||||||
/// Current screen size dimensions for this grid. This is set on the first
|
/// The size of everything.
|
||||||
/// resize event, and is not immediately available.
|
size: renderer.Size,
|
||||||
screen_size: ?renderer.ScreenSize,
|
|
||||||
|
|
||||||
/// Explicit padding.
|
|
||||||
padding: renderer.Options.Padding,
|
|
||||||
|
|
||||||
/// True if the window is focused
|
/// True if the window is focused
|
||||||
focused: bool,
|
focused: bool,
|
||||||
@ -626,13 +622,12 @@ pub fn init(alloc: Allocator, options: renderer.Options) !Metal {
|
|||||||
};
|
};
|
||||||
errdefer if (display_link) |v| v.release();
|
errdefer if (display_link) |v| v.release();
|
||||||
|
|
||||||
return Metal{
|
var result: Metal = .{
|
||||||
.alloc = alloc,
|
.alloc = alloc,
|
||||||
.config = options.config,
|
.config = options.config,
|
||||||
.surface_mailbox = options.surface_mailbox,
|
.surface_mailbox = options.surface_mailbox,
|
||||||
.grid_metrics = font_critical.metrics,
|
.grid_metrics = font_critical.metrics,
|
||||||
.screen_size = null,
|
.size = options.size,
|
||||||
.padding = options.padding,
|
|
||||||
.focused = true,
|
.focused = true,
|
||||||
.foreground_color = options.config.foreground,
|
.foreground_color = options.config.foreground,
|
||||||
.background_color = options.config.background,
|
.background_color = options.config.background,
|
||||||
@ -668,6 +663,12 @@ pub fn init(alloc: Allocator, options: renderer.Options) !Metal {
|
|||||||
.custom_shader_state = custom_shader_state,
|
.custom_shader_state = custom_shader_state,
|
||||||
.gpu_state = gpu_state,
|
.gpu_state = gpu_state,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Do an initialize screen size setup to ensure our undefined values
|
||||||
|
// above are initialized.
|
||||||
|
try result.setScreenSize(result.size);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *Metal) void {
|
pub fn deinit(self: *Metal) void {
|
||||||
@ -796,19 +797,6 @@ pub fn hasVsync(self: *const Metal) bool {
|
|||||||
return display_link.isRunning();
|
return display_link.isRunning();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the grid size for a given screen size. This is safe to call
|
|
||||||
/// on any thread.
|
|
||||||
fn gridSize(self: *Metal) ?renderer.GridSize {
|
|
||||||
const screen_size = self.screen_size orelse return null;
|
|
||||||
return renderer.GridSize.init(
|
|
||||||
screen_size.subPadding(self.padding.explicit),
|
|
||||||
.{
|
|
||||||
.width = self.grid_metrics.cell_width,
|
|
||||||
.height = self.grid_metrics.cell_height,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Callback when the focus changes for the terminal this is rendering.
|
/// Callback when the focus changes for the terminal this is rendering.
|
||||||
///
|
///
|
||||||
/// Must be called on the render thread.
|
/// Must be called on the render thread.
|
||||||
@ -878,15 +866,13 @@ pub fn setFontGrid(self: *Metal, grid: *font.SharedGrid) void {
|
|||||||
//
|
//
|
||||||
// If the screen size isn't set, it will be eventually so that'll call
|
// If the screen size isn't set, it will be eventually so that'll call
|
||||||
// the setScreenSize automatically.
|
// the setScreenSize automatically.
|
||||||
if (self.screen_size) |size| {
|
self.setScreenSize(self.size) catch |err| {
|
||||||
self.setScreenSize(size, self.padding.explicit) catch |err| {
|
// The setFontGrid function can't fail but resizing our cell
|
||||||
// The setFontGrid function can't fail but resizing our cell
|
// buffer definitely can fail. If it does, our renderer is probably
|
||||||
// buffer definitely can fail. If it does, our renderer is probably
|
// screwed but let's just log it and continue until we can figure
|
||||||
// screwed but let's just log it and continue until we can figure
|
// out a better way to handle this.
|
||||||
// out a better way to handle this.
|
log.err("error resizing cells buffer err={}", .{err});
|
||||||
log.err("error resizing cells buffer err={}", .{err});
|
};
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the frame data.
|
/// Update the frame data.
|
||||||
@ -898,9 +884,6 @@ pub fn updateFrame(
|
|||||||
) !void {
|
) !void {
|
||||||
_ = surface;
|
_ = surface;
|
||||||
|
|
||||||
// If we don't have a screen size yet then we can't render anything.
|
|
||||||
if (self.screen_size == null) return;
|
|
||||||
|
|
||||||
// Data we extract out of the critical area.
|
// Data we extract out of the critical area.
|
||||||
const Critical = struct {
|
const Critical = struct {
|
||||||
bg: terminal.color.RGB,
|
bg: terminal.color.RGB,
|
||||||
@ -1116,9 +1099,6 @@ pub fn updateFrame(
|
|||||||
pub fn drawFrame(self: *Metal, surface: *apprt.Surface) !void {
|
pub fn drawFrame(self: *Metal, surface: *apprt.Surface) !void {
|
||||||
_ = surface;
|
_ = surface;
|
||||||
|
|
||||||
// If we don't have a screen size yet then we can't render anything.
|
|
||||||
if (self.screen_size == null) return;
|
|
||||||
|
|
||||||
// If we have no cells rebuilt we can usually skip drawing since there
|
// If we have no cells rebuilt we can usually skip drawing since there
|
||||||
// is no changed data. However, if we have active animations we still
|
// is no changed data. However, if we have active animations we still
|
||||||
// need to draw so that we can update the time uniform and render the
|
// need to draw so that we can update the time uniform and render the
|
||||||
@ -1972,38 +1952,19 @@ pub fn changeConfig(self: *Metal, config: *DerivedConfig) !void {
|
|||||||
/// Resize the screen.
|
/// Resize the screen.
|
||||||
pub fn setScreenSize(
|
pub fn setScreenSize(
|
||||||
self: *Metal,
|
self: *Metal,
|
||||||
dim: renderer.ScreenSize,
|
size: renderer.Size,
|
||||||
pad: renderer.Padding,
|
|
||||||
) !void {
|
) !void {
|
||||||
// Store our sizes
|
// Store our sizes
|
||||||
self.screen_size = dim;
|
self.size = size;
|
||||||
self.padding.explicit = pad;
|
const grid_size = size.grid();
|
||||||
|
const terminal_size = size.terminal();
|
||||||
// Recalculate the rows/columns. This can't fail since we just set
|
|
||||||
// the screen size above.
|
|
||||||
const grid_size = self.gridSize().?;
|
|
||||||
|
|
||||||
// Determine if we need to pad the window. For "auto" padding, we take
|
|
||||||
// the leftover amounts on the right/bottom that don't fit a full grid cell
|
|
||||||
// and we split them equal across all boundaries.
|
|
||||||
const padding = if (self.padding.balance)
|
|
||||||
renderer.Padding.balanced(
|
|
||||||
dim,
|
|
||||||
grid_size,
|
|
||||||
.{
|
|
||||||
.width = self.grid_metrics.cell_width,
|
|
||||||
.height = self.grid_metrics.cell_height,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
else
|
|
||||||
self.padding.explicit;
|
|
||||||
const padded_dim = dim.subPadding(padding);
|
|
||||||
|
|
||||||
// Blank space around the grid.
|
// Blank space around the grid.
|
||||||
const blank: renderer.Padding = dim.blankPadding(padding, grid_size, .{
|
const blank: renderer.Padding = size.screen.blankPadding(
|
||||||
.width = self.grid_metrics.cell_width,
|
size.padding,
|
||||||
.height = self.grid_metrics.cell_height,
|
grid_size,
|
||||||
}).add(padding);
|
size.cell,
|
||||||
|
).add(size.padding);
|
||||||
|
|
||||||
var padding_extend = self.uniforms.padding_extend;
|
var padding_extend = self.uniforms.padding_extend;
|
||||||
switch (self.config.padding_color) {
|
switch (self.config.padding_color) {
|
||||||
@ -2030,18 +1991,18 @@ pub fn setScreenSize(
|
|||||||
|
|
||||||
// Set the size of the drawable surface to the bounds
|
// Set the size of the drawable surface to the bounds
|
||||||
self.layer.setProperty("drawableSize", macos.graphics.Size{
|
self.layer.setProperty("drawableSize", macos.graphics.Size{
|
||||||
.width = @floatFromInt(dim.width),
|
.width = @floatFromInt(size.screen.width),
|
||||||
.height = @floatFromInt(dim.height),
|
.height = @floatFromInt(size.screen.height),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Setup our uniforms
|
// Setup our uniforms
|
||||||
const old = self.uniforms;
|
const old = self.uniforms;
|
||||||
self.uniforms = .{
|
self.uniforms = .{
|
||||||
.projection_matrix = math.ortho2d(
|
.projection_matrix = math.ortho2d(
|
||||||
-1 * @as(f32, @floatFromInt(padding.left)),
|
-1 * @as(f32, @floatFromInt(size.padding.left)),
|
||||||
@floatFromInt(padded_dim.width + padding.right),
|
@floatFromInt(terminal_size.width + size.padding.right),
|
||||||
@floatFromInt(padded_dim.height + padding.bottom),
|
@floatFromInt(terminal_size.height + size.padding.bottom),
|
||||||
-1 * @as(f32, @floatFromInt(padding.top)),
|
-1 * @as(f32, @floatFromInt(size.padding.top)),
|
||||||
),
|
),
|
||||||
.cell_size = .{
|
.cell_size = .{
|
||||||
@floatFromInt(self.grid_metrics.cell_width),
|
@floatFromInt(self.grid_metrics.cell_width),
|
||||||
@ -2082,8 +2043,8 @@ pub fn setScreenSize(
|
|||||||
}
|
}
|
||||||
|
|
||||||
state.uniforms.resolution = .{
|
state.uniforms.resolution = .{
|
||||||
@floatFromInt(dim.width),
|
@floatFromInt(size.screen.width),
|
||||||
@floatFromInt(dim.height),
|
@floatFromInt(size.screen.height),
|
||||||
1,
|
1,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2097,8 +2058,8 @@ pub fn setScreenSize(
|
|||||||
break :init id_init;
|
break :init id_init;
|
||||||
};
|
};
|
||||||
desc.setProperty("pixelFormat", @intFromEnum(mtl.MTLPixelFormat.bgra8unorm));
|
desc.setProperty("pixelFormat", @intFromEnum(mtl.MTLPixelFormat.bgra8unorm));
|
||||||
desc.setProperty("width", @as(c_ulong, @intCast(dim.width)));
|
desc.setProperty("width", @as(c_ulong, @intCast(size.screen.width)));
|
||||||
desc.setProperty("height", @as(c_ulong, @intCast(dim.height)));
|
desc.setProperty("height", @as(c_ulong, @intCast(size.screen.height)));
|
||||||
desc.setProperty(
|
desc.setProperty(
|
||||||
"usage",
|
"usage",
|
||||||
@intFromEnum(mtl.MTLTextureUsage.render_target) |
|
@intFromEnum(mtl.MTLTextureUsage.render_target) |
|
||||||
@ -2127,8 +2088,8 @@ pub fn setScreenSize(
|
|||||||
break :init id_init;
|
break :init id_init;
|
||||||
};
|
};
|
||||||
desc.setProperty("pixelFormat", @intFromEnum(mtl.MTLPixelFormat.bgra8unorm));
|
desc.setProperty("pixelFormat", @intFromEnum(mtl.MTLPixelFormat.bgra8unorm));
|
||||||
desc.setProperty("width", @as(c_ulong, @intCast(dim.width)));
|
desc.setProperty("width", @as(c_ulong, @intCast(size.screen.width)));
|
||||||
desc.setProperty("height", @as(c_ulong, @intCast(dim.height)));
|
desc.setProperty("height", @as(c_ulong, @intCast(size.screen.height)));
|
||||||
desc.setProperty(
|
desc.setProperty(
|
||||||
"usage",
|
"usage",
|
||||||
@intFromEnum(mtl.MTLTextureUsage.render_target) |
|
@intFromEnum(mtl.MTLTextureUsage.render_target) |
|
||||||
@ -2148,7 +2109,7 @@ pub fn setScreenSize(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug("screen size screen={} grid={}, cell_width={} cell_height={}", .{ dim, grid_size, self.grid_metrics.cell_width, self.grid_metrics.cell_height });
|
log.debug("screen size size={}", .{size});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert the terminal state to GPU cells stored in CPU memory. These
|
/// Convert the terminal state to GPU cells stored in CPU memory. These
|
||||||
|
@ -51,10 +51,8 @@ config: DerivedConfig,
|
|||||||
/// Current font metrics defining our grid.
|
/// Current font metrics defining our grid.
|
||||||
grid_metrics: font.face.Metrics,
|
grid_metrics: font.face.Metrics,
|
||||||
|
|
||||||
/// Current screen size dimensions for this grid. This is set on the first
|
/// The size of everything.
|
||||||
/// resize event, and is not immediately available.
|
size: renderer.Size,
|
||||||
screen_size: ?renderer.ScreenSize,
|
|
||||||
grid_size: renderer.GridSize,
|
|
||||||
|
|
||||||
/// The current set of cells to render. Each set of cells goes into
|
/// The current set of cells to render. Each set of cells goes into
|
||||||
/// a separate shader call.
|
/// a separate shader call.
|
||||||
@ -108,9 +106,6 @@ cursor_color: ?terminal.color.RGB,
|
|||||||
/// foreground color as the cursor color.
|
/// foreground color as the cursor color.
|
||||||
cursor_invert: bool,
|
cursor_invert: bool,
|
||||||
|
|
||||||
/// Padding options
|
|
||||||
padding: renderer.Options.Padding,
|
|
||||||
|
|
||||||
/// The mailbox for communicating with the window.
|
/// The mailbox for communicating with the window.
|
||||||
surface_mailbox: apprt.surface.Mailbox,
|
surface_mailbox: apprt.surface.Mailbox,
|
||||||
|
|
||||||
@ -141,7 +136,7 @@ image_virtual: bool = false,
|
|||||||
|
|
||||||
/// Defererred OpenGL operation to update the screen size.
|
/// Defererred OpenGL operation to update the screen size.
|
||||||
const SetScreenSize = struct {
|
const SetScreenSize = struct {
|
||||||
size: renderer.ScreenSize,
|
size: renderer.Size,
|
||||||
|
|
||||||
fn apply(self: SetScreenSize, r: *OpenGL) !void {
|
fn apply(self: SetScreenSize, r: *OpenGL) !void {
|
||||||
const gl_state: *GLState = if (r.gl_state) |*v|
|
const gl_state: *GLState = if (r.gl_state) |*v|
|
||||||
@ -150,19 +145,8 @@ const SetScreenSize = struct {
|
|||||||
return error.OpenGLUninitialized;
|
return error.OpenGLUninitialized;
|
||||||
|
|
||||||
// Apply our padding
|
// Apply our padding
|
||||||
const grid_size = r.gridSize(self.size);
|
const grid_size = self.size.grid();
|
||||||
const padding = if (r.padding.balance)
|
const terminal_size = self.size.terminal();
|
||||||
renderer.Padding.balanced(
|
|
||||||
self.size,
|
|
||||||
grid_size,
|
|
||||||
.{
|
|
||||||
.width = r.grid_metrics.cell_width,
|
|
||||||
.height = r.grid_metrics.cell_height,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
else
|
|
||||||
r.padding.explicit;
|
|
||||||
const padded_size = self.size.subPadding(padding);
|
|
||||||
|
|
||||||
// Blank space around the grid.
|
// Blank space around the grid.
|
||||||
const blank: renderer.Padding = switch (r.config.padding_color) {
|
const blank: renderer.Padding = switch (r.config.padding_color) {
|
||||||
@ -170,30 +154,20 @@ const SetScreenSize = struct {
|
|||||||
// clear color.
|
// clear color.
|
||||||
.background => .{},
|
.background => .{},
|
||||||
|
|
||||||
.extend, .@"extend-always" => self.size.blankPadding(padding, grid_size, .{
|
.extend, .@"extend-always" => self.size.screen.blankPadding(
|
||||||
.width = r.grid_metrics.cell_width,
|
self.size.padding,
|
||||||
.height = r.grid_metrics.cell_height,
|
grid_size,
|
||||||
}).add(padding),
|
self.size.cell,
|
||||||
|
).add(self.size.padding),
|
||||||
};
|
};
|
||||||
|
|
||||||
log.debug("GL api: screen size padded={} screen={} grid={} cell={} padding={}", .{
|
|
||||||
padded_size,
|
|
||||||
self.size,
|
|
||||||
r.gridSize(self.size),
|
|
||||||
renderer.CellSize{
|
|
||||||
.width = r.grid_metrics.cell_width,
|
|
||||||
.height = r.grid_metrics.cell_height,
|
|
||||||
},
|
|
||||||
r.padding.explicit,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Update our viewport for this context to be the entire window.
|
// Update our viewport for this context to be the entire window.
|
||||||
// OpenGL works in pixels, so we have to use the pixel size.
|
// OpenGL works in pixels, so we have to use the pixel size.
|
||||||
try gl.viewport(
|
try gl.viewport(
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
@intCast(self.size.width),
|
@intCast(self.size.screen.width),
|
||||||
@intCast(self.size.height),
|
@intCast(self.size.screen.height),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Update the projection uniform within our shader
|
// Update the projection uniform within our shader
|
||||||
@ -206,10 +180,10 @@ const SetScreenSize = struct {
|
|||||||
|
|
||||||
// 2D orthographic projection with the full w/h
|
// 2D orthographic projection with the full w/h
|
||||||
math.ortho2d(
|
math.ortho2d(
|
||||||
-1 * @as(f32, @floatFromInt(padding.left)),
|
-1 * @as(f32, @floatFromInt(self.size.padding.left)),
|
||||||
@floatFromInt(padded_size.width + padding.right),
|
@floatFromInt(terminal_size.width + self.size.padding.right),
|
||||||
@floatFromInt(padded_size.height + padding.bottom),
|
@floatFromInt(terminal_size.height + self.size.padding.bottom),
|
||||||
-1 * @as(f32, @floatFromInt(padding.top)),
|
-1 * @as(f32, @floatFromInt(self.size.padding.top)),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -405,8 +379,7 @@ pub fn init(alloc: Allocator, options: renderer.Options) !OpenGL {
|
|||||||
.cells_bg = .{},
|
.cells_bg = .{},
|
||||||
.cells = .{},
|
.cells = .{},
|
||||||
.grid_metrics = grid.metrics,
|
.grid_metrics = grid.metrics,
|
||||||
.screen_size = null,
|
.size = options.size,
|
||||||
.grid_size = .{},
|
|
||||||
.gl_state = gl_state,
|
.gl_state = gl_state,
|
||||||
.font_grid = grid,
|
.font_grid = grid,
|
||||||
.font_shaper = shaper,
|
.font_shaper = shaper,
|
||||||
@ -417,7 +390,6 @@ pub fn init(alloc: Allocator, options: renderer.Options) !OpenGL {
|
|||||||
.background_color = options.config.background,
|
.background_color = options.config.background,
|
||||||
.cursor_color = options.config.cursor_color,
|
.cursor_color = options.config.cursor_color,
|
||||||
.cursor_invert = options.config.cursor_invert,
|
.cursor_invert = options.config.cursor_invert,
|
||||||
.padding = options.padding,
|
|
||||||
.surface_mailbox = options.surface_mailbox,
|
.surface_mailbox = options.surface_mailbox,
|
||||||
.deferred_font_size = .{ .metrics = grid.metrics },
|
.deferred_font_size = .{ .metrics = grid.metrics },
|
||||||
.deferred_config = .{},
|
.deferred_config = .{},
|
||||||
@ -558,9 +530,7 @@ pub fn displayRealize(self: *OpenGL) !void {
|
|||||||
self.texture_color_resized = 0;
|
self.texture_color_resized = 0;
|
||||||
|
|
||||||
// We need to reset our uniforms
|
// We need to reset our uniforms
|
||||||
if (self.screen_size) |size| {
|
self.deferred_screen_size = .{ .size = self.size };
|
||||||
self.deferred_screen_size = .{ .size = size };
|
|
||||||
}
|
|
||||||
self.deferred_font_size = .{ .metrics = self.grid_metrics };
|
self.deferred_font_size = .{ .metrics = self.grid_metrics };
|
||||||
self.deferred_config = .{};
|
self.deferred_config = .{};
|
||||||
}
|
}
|
||||||
@ -688,15 +658,9 @@ pub fn setFontGrid(self: *OpenGL, grid: *font.SharedGrid) void {
|
|||||||
self.font_shaper_cache.deinit(self.alloc);
|
self.font_shaper_cache.deinit(self.alloc);
|
||||||
self.font_shaper_cache = font_shaper_cache;
|
self.font_shaper_cache = font_shaper_cache;
|
||||||
|
|
||||||
if (self.screen_size) |size| {
|
// Update our screen size because the font grid can affect grid
|
||||||
// Update our grid size if we have a screen size. If we don't, its okay
|
// metrics which update uniforms.
|
||||||
// because this will get set when we get the screen size set.
|
self.deferred_screen_size = .{ .size = self.size };
|
||||||
self.grid_size = self.gridSize(size);
|
|
||||||
|
|
||||||
// Update our screen size because the font grid can affect grid
|
|
||||||
// metrics which update uniforms.
|
|
||||||
self.deferred_screen_size = .{ .size = size };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Defer our GPU updates
|
// Defer our GPU updates
|
||||||
self.deferred_font_size = .{ .metrics = grid.metrics };
|
self.deferred_font_size = .{ .metrics = grid.metrics };
|
||||||
@ -725,6 +689,8 @@ pub fn updateFrame(
|
|||||||
|
|
||||||
// Update all our data as tightly as possible within the mutex.
|
// Update all our data as tightly as possible within the mutex.
|
||||||
var critical: Critical = critical: {
|
var critical: Critical = critical: {
|
||||||
|
const grid_size = self.size.grid();
|
||||||
|
|
||||||
state.mutex.lock();
|
state.mutex.lock();
|
||||||
defer state.mutex.unlock();
|
defer state.mutex.unlock();
|
||||||
|
|
||||||
@ -753,8 +719,8 @@ pub fn updateFrame(
|
|||||||
//
|
//
|
||||||
// For some reason this doesn't seem to cause any significant issues
|
// For some reason this doesn't seem to cause any significant issues
|
||||||
// with flickering while resizing. '\_('-')_/'
|
// with flickering while resizing. '\_('-')_/'
|
||||||
if (self.grid_size.rows != state.terminal.rows or
|
if (grid_size.rows != state.terminal.rows or
|
||||||
self.grid_size.columns != state.terminal.cols)
|
grid_size.columns != state.terminal.cols)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1314,7 +1280,7 @@ pub fn rebuildCells(
|
|||||||
color_palette,
|
color_palette,
|
||||||
self.background_color,
|
self.background_color,
|
||||||
);
|
);
|
||||||
} else if (y == self.grid_size.rows - 1) {
|
} else if (y == self.size.grid().rows - 1) {
|
||||||
self.padding_extend_bottom = !row.neverExtendBg(
|
self.padding_extend_bottom = !row.neverExtendBg(
|
||||||
color_palette,
|
color_palette,
|
||||||
self.background_color,
|
self.background_color,
|
||||||
@ -2115,18 +2081,6 @@ fn addGlyph(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the grid size for a given screen size. This is safe to call
|
|
||||||
/// on any thread.
|
|
||||||
fn gridSize(self: *const OpenGL, screen_size: renderer.ScreenSize) renderer.GridSize {
|
|
||||||
return renderer.GridSize.init(
|
|
||||||
screen_size.subPadding(self.padding.explicit),
|
|
||||||
.{
|
|
||||||
.width = self.grid_metrics.cell_width,
|
|
||||||
.height = self.grid_metrics.cell_height,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Update the configuration.
|
/// Update the configuration.
|
||||||
pub fn changeConfig(self: *OpenGL, config: *DerivedConfig) !void {
|
pub fn changeConfig(self: *OpenGL, config: *DerivedConfig) !void {
|
||||||
// We always redo the font shaper in case font features changed. We
|
// We always redo the font shaper in case font features changed. We
|
||||||
@ -2164,8 +2118,7 @@ pub fn changeConfig(self: *OpenGL, config: *DerivedConfig) !void {
|
|||||||
/// used for the shader so that the scaling of the grid is correct.
|
/// used for the shader so that the scaling of the grid is correct.
|
||||||
pub fn setScreenSize(
|
pub fn setScreenSize(
|
||||||
self: *OpenGL,
|
self: *OpenGL,
|
||||||
dim: renderer.ScreenSize,
|
size: renderer.Size,
|
||||||
pad: renderer.Padding,
|
|
||||||
) !void {
|
) !void {
|
||||||
if (single_threaded_draw) self.draw_mutex.lock();
|
if (single_threaded_draw) self.draw_mutex.lock();
|
||||||
defer if (single_threaded_draw) self.draw_mutex.unlock();
|
defer if (single_threaded_draw) self.draw_mutex.unlock();
|
||||||
@ -2177,22 +2130,12 @@ pub fn setScreenSize(
|
|||||||
self.cells_bg.clearAndFree(self.alloc);
|
self.cells_bg.clearAndFree(self.alloc);
|
||||||
|
|
||||||
// Store our screen size
|
// Store our screen size
|
||||||
self.screen_size = dim;
|
self.size = size;
|
||||||
self.padding.explicit = pad;
|
|
||||||
self.grid_size = self.gridSize(dim);
|
|
||||||
|
|
||||||
log.debug("screen size screen={} grid={} cell={} padding={}", .{
|
|
||||||
dim,
|
|
||||||
self.grid_size,
|
|
||||||
renderer.CellSize{
|
|
||||||
.width = self.grid_metrics.cell_width,
|
|
||||||
.height = self.grid_metrics.cell_height,
|
|
||||||
},
|
|
||||||
self.padding.explicit,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Defer our OpenGL updates
|
// Defer our OpenGL updates
|
||||||
self.deferred_screen_size = .{ .size = dim };
|
self.deferred_screen_size = .{ .size = size };
|
||||||
|
|
||||||
|
log.debug("screen size size={}", .{size});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates the font texture atlas if it is dirty.
|
/// Updates the font texture atlas if it is dirty.
|
||||||
|
@ -11,8 +11,8 @@ config: renderer.Renderer.DerivedConfig,
|
|||||||
/// The font grid that should be used along with the key for deref-ing.
|
/// The font grid that should be used along with the key for deref-ing.
|
||||||
font_grid: *font.SharedGrid,
|
font_grid: *font.SharedGrid,
|
||||||
|
|
||||||
/// Padding options for the viewport.
|
/// The size of everything.
|
||||||
padding: Padding,
|
size: renderer.Size,
|
||||||
|
|
||||||
/// The mailbox for sending the surface messages. This is only valid
|
/// The mailbox for sending the surface messages. This is only valid
|
||||||
/// once the thread has started and should not be used outside of the thread.
|
/// once the thread has started and should not be used outside of the thread.
|
||||||
@ -20,12 +20,3 @@ surface_mailbox: apprt.surface.Mailbox,
|
|||||||
|
|
||||||
/// The apprt surface.
|
/// The apprt surface.
|
||||||
rt_surface: *apprt.Surface,
|
rt_surface: *apprt.Surface,
|
||||||
|
|
||||||
pub const Padding = struct {
|
|
||||||
// Explicit padding options, in pixels. The surface thread is
|
|
||||||
// expected to convert points to pixels for a given DPI.
|
|
||||||
explicit: renderer.Padding,
|
|
||||||
|
|
||||||
// Balance options
|
|
||||||
balance: bool = false,
|
|
||||||
};
|
|
||||||
|
@ -371,9 +371,7 @@ fn drainMailbox(self: *Thread) !void {
|
|||||||
self.renderer.markDirty();
|
self.renderer.markDirty();
|
||||||
},
|
},
|
||||||
|
|
||||||
.resize => |v| {
|
.resize => |v| try self.renderer.setScreenSize(v),
|
||||||
try self.renderer.setScreenSize(v.screen_size, v.padding);
|
|
||||||
},
|
|
||||||
|
|
||||||
.change_config => |config| {
|
.change_config => |config| {
|
||||||
defer config.alloc.destroy(config.thread);
|
defer config.alloc.destroy(config.thread);
|
||||||
|
@ -54,14 +54,8 @@ pub const Message = union(enum) {
|
|||||||
/// config file in response to an OSC 12 command.
|
/// config file in response to an OSC 12 command.
|
||||||
cursor_color: ?terminal.color.RGB,
|
cursor_color: ?terminal.color.RGB,
|
||||||
|
|
||||||
/// Changes the screen size.
|
/// Changes the size. The screen size might change, padding, grid, etc.
|
||||||
resize: struct {
|
resize: renderer.Size,
|
||||||
/// The full screen (drawable) size. This does NOT include padding.
|
|
||||||
screen_size: renderer.ScreenSize,
|
|
||||||
|
|
||||||
/// The explicit padding values.
|
|
||||||
padding: renderer.Padding,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// The derived configuration to update the renderer with.
|
/// The derived configuration to update the renderer with.
|
||||||
change_config: struct {
|
change_config: struct {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const gl = @import("opengl");
|
const gl = @import("opengl");
|
||||||
const ScreenSize = @import("../size.zig").ScreenSize;
|
const Size = @import("../size.zig").Size;
|
||||||
|
|
||||||
const log = std.log.scoped(.opengl_custom);
|
const log = std.log.scoped(.opengl_custom);
|
||||||
|
|
||||||
@ -154,11 +154,11 @@ pub const State = struct {
|
|||||||
self.fbo.destroy();
|
self.fbo.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setScreenSize(self: *State, size: ScreenSize) !void {
|
pub fn setScreenSize(self: *State, size: Size) !void {
|
||||||
// Update our uniforms
|
// Update our uniforms
|
||||||
self.uniforms.resolution = .{
|
self.uniforms.resolution = .{
|
||||||
@floatFromInt(size.width),
|
@floatFromInt(size.screen.width),
|
||||||
@floatFromInt(size.height),
|
@floatFromInt(size.screen.height),
|
||||||
1,
|
1,
|
||||||
};
|
};
|
||||||
try self.syncUniforms();
|
try self.syncUniforms();
|
||||||
@ -168,8 +168,8 @@ pub const State = struct {
|
|||||||
try texbind.image2D(
|
try texbind.image2D(
|
||||||
0,
|
0,
|
||||||
.rgb,
|
.rgb,
|
||||||
@intCast(size.width),
|
@intCast(size.screen.width),
|
||||||
@intCast(size.height),
|
@intCast(size.screen.height),
|
||||||
0,
|
0,
|
||||||
.rgb,
|
.rgb,
|
||||||
.UnsignedByte,
|
.UnsignedByte,
|
||||||
|
@ -5,6 +5,139 @@ const terminal = @import("../terminal/main.zig");
|
|||||||
|
|
||||||
const log = std.log.scoped(.renderer_size);
|
const log = std.log.scoped(.renderer_size);
|
||||||
|
|
||||||
|
/// All relevant sizes for a rendered terminal. These are all the sizes that
|
||||||
|
/// any functionality should need to know about the terminal in order to
|
||||||
|
/// convert between any coordinate systems.
|
||||||
|
///
|
||||||
|
/// See the individual field type documentation for more information on each
|
||||||
|
/// field. One important note is that any pixel values should already be scaled
|
||||||
|
/// to the current DPI of the screen. If the DPI changes, the sizes should be
|
||||||
|
/// recalculated and we expect this to be done by the caller.
|
||||||
|
pub const Size = struct {
|
||||||
|
screen: ScreenSize,
|
||||||
|
cell: CellSize,
|
||||||
|
padding: Padding,
|
||||||
|
|
||||||
|
/// Return the grid size for this size. The grid size is calculated by
|
||||||
|
/// taking the screen size, removing padding, and dividing by the cell
|
||||||
|
/// dimensions.
|
||||||
|
pub fn grid(self: Size) GridSize {
|
||||||
|
return GridSize.init(self.screen.subPadding(self.padding), self.cell);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The size of the terminal. This is the same as the screen without
|
||||||
|
/// padding.
|
||||||
|
pub fn terminal(self: Size) ScreenSize {
|
||||||
|
return self.screen.subPadding(self.padding);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the padding to be balanced around the grid. Overwrites the current
|
||||||
|
/// padding.
|
||||||
|
pub fn balancePadding(self: *Size) void {
|
||||||
|
self.padding = Padding.balanced(
|
||||||
|
self.screen,
|
||||||
|
self.grid(),
|
||||||
|
self.cell,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A coordinate. This is defined as a tagged union to allow for different
|
||||||
|
/// coordinate systems to be represented.
|
||||||
|
///
|
||||||
|
/// A coordinate is only valid within the context of a stable Size value.
|
||||||
|
/// If any of the sizes in the Size struct change, the coordinate is no
|
||||||
|
/// longer valid and must be recalculated. A conversion function is provided
|
||||||
|
/// to migrate to a new Size (which may result in failure).
|
||||||
|
///
|
||||||
|
/// The coordinate systems are:
|
||||||
|
///
|
||||||
|
/// * surface: (0, 0) is the top-left of the surface (with padding). Negative
|
||||||
|
/// values are allowed and are off the surface. Likewise, values greater
|
||||||
|
/// than the surface size are off the surface. Units are pixels.
|
||||||
|
///
|
||||||
|
/// * terminal: (0, 0) is the top-left of the terminal grid. This is the
|
||||||
|
/// same as the surface but with the padding removed. Negative values and
|
||||||
|
/// values greater than the grid size are allowed and are off the terminal.
|
||||||
|
/// Units are pixels.
|
||||||
|
///
|
||||||
|
/// * grid: (0, 0) is the top-left of the grid. Units are in cells. Negative
|
||||||
|
/// values are not allowed but values greater than the grid size are
|
||||||
|
/// possible and are off the grid.
|
||||||
|
///
|
||||||
|
pub const Coordinate = union(enum) {
|
||||||
|
surface: Surface,
|
||||||
|
terminal: Terminal,
|
||||||
|
grid: Grid,
|
||||||
|
|
||||||
|
pub const Tag = @typeInfo(Coordinate).Union.tag_type.?;
|
||||||
|
pub const Surface = struct { x: f64, y: f64 };
|
||||||
|
pub const Terminal = struct { x: f64, y: f64 };
|
||||||
|
pub const Grid = struct { x: GridSize.Unit, y: GridSize.Unit };
|
||||||
|
|
||||||
|
/// Convert a coordinate to a different space within the same Size.
|
||||||
|
pub fn convert(self: Coordinate, to: Tag, size: Size) Coordinate {
|
||||||
|
// Unlikely fast-path but avoid work.
|
||||||
|
if (@as(Tag, self) == to) return self;
|
||||||
|
|
||||||
|
// To avoid the combinatorial explosion of conversion functions, we
|
||||||
|
// convert to the surface system first and then reconvert from there.
|
||||||
|
const surface = self.convertToSurface(size);
|
||||||
|
|
||||||
|
return switch (to) {
|
||||||
|
.surface => .{ .surface = surface },
|
||||||
|
.terminal => .{ .terminal = .{
|
||||||
|
.x = surface.x - @as(f64, @floatFromInt(size.padding.left)),
|
||||||
|
.y = surface.y - @as(f64, @floatFromInt(size.padding.top)),
|
||||||
|
} },
|
||||||
|
.grid => grid: {
|
||||||
|
// Get rid of the padding.
|
||||||
|
const term = (Coordinate{ .surface = surface }).convert(
|
||||||
|
.terminal,
|
||||||
|
size,
|
||||||
|
).terminal;
|
||||||
|
|
||||||
|
// We need our grid to clamp
|
||||||
|
const grid = size.grid();
|
||||||
|
|
||||||
|
// Calculate the grid position.
|
||||||
|
const cell_width: f64 = @as(f64, @floatFromInt(size.cell.width));
|
||||||
|
const cell_height: f64 = @as(f64, @floatFromInt(size.cell.height));
|
||||||
|
const clamped_x: f64 = @max(0, term.x);
|
||||||
|
const clamped_y: f64 = @max(0, term.y);
|
||||||
|
const col: GridSize.Unit = @intFromFloat(clamped_x / cell_width);
|
||||||
|
const row: GridSize.Unit = @intFromFloat(clamped_y / cell_height);
|
||||||
|
const clamped_col: GridSize.Unit = @min(col, grid.columns - 1);
|
||||||
|
const clamped_row: GridSize.Unit = @min(row, grid.rows - 1);
|
||||||
|
break :grid .{ .grid = .{ .x = clamped_col, .y = clamped_row } };
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert a coordinate to the surface coordinate system.
|
||||||
|
fn convertToSurface(self: Coordinate, size: Size) Surface {
|
||||||
|
return switch (self) {
|
||||||
|
.surface => |v| v,
|
||||||
|
.terminal => |v| .{
|
||||||
|
.x = v.x + @as(f64, @floatFromInt(size.padding.left)),
|
||||||
|
.y = v.y + @as(f64, @floatFromInt(size.padding.top)),
|
||||||
|
},
|
||||||
|
.grid => |v| grid: {
|
||||||
|
const col: f64 = @floatFromInt(v.x);
|
||||||
|
const row: f64 = @floatFromInt(v.y);
|
||||||
|
const cell_width: f64 = @floatFromInt(size.cell.width);
|
||||||
|
const cell_height: f64 = @floatFromInt(size.cell.height);
|
||||||
|
const padding_left: f64 = @floatFromInt(size.padding.left);
|
||||||
|
const padding_top: f64 = @floatFromInt(size.padding.top);
|
||||||
|
break :grid .{
|
||||||
|
.x = col * cell_width + padding_left,
|
||||||
|
.y = row * cell_height + padding_top,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/// The dimensions of a single "cell" in the terminal grid.
|
/// The dimensions of a single "cell" in the terminal grid.
|
||||||
///
|
///
|
||||||
/// The dimensions are dependent on the current loaded set of font glyphs.
|
/// The dimensions are dependent on the current loaded set of font glyphs.
|
||||||
@ -67,7 +200,7 @@ pub const ScreenSize = struct {
|
|||||||
|
|
||||||
/// The dimensions of the grid itself, in rows/columns units.
|
/// The dimensions of the grid itself, in rows/columns units.
|
||||||
pub const GridSize = struct {
|
pub const GridSize = struct {
|
||||||
const Unit = terminal.size.CellCountInt;
|
pub const Unit = terminal.size.CellCountInt;
|
||||||
|
|
||||||
columns: Unit = 0,
|
columns: Unit = 0,
|
||||||
rows: Unit = 0,
|
rows: Unit = 0,
|
||||||
@ -201,3 +334,54 @@ test "GridSize update rounding" {
|
|||||||
try testing.expectEqual(@as(GridSize.Unit, 3), grid.columns);
|
try testing.expectEqual(@as(GridSize.Unit, 3), grid.columns);
|
||||||
try testing.expectEqual(@as(GridSize.Unit, 2), grid.rows);
|
try testing.expectEqual(@as(GridSize.Unit, 2), grid.rows);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "coordinate conversion" {
|
||||||
|
const testing = std.testing;
|
||||||
|
|
||||||
|
// A size for testing purposes. Purposely easy to calculate numbers.
|
||||||
|
const test_size: Size = .{
|
||||||
|
.screen = .{
|
||||||
|
.width = 100,
|
||||||
|
.height = 100,
|
||||||
|
},
|
||||||
|
|
||||||
|
.cell = .{
|
||||||
|
.width = 5,
|
||||||
|
.height = 10,
|
||||||
|
},
|
||||||
|
|
||||||
|
.padding = .{},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Each pair is a test case of [expected, actual]. We only test
|
||||||
|
// one-way conversion because conversion can be lossy due to clamping
|
||||||
|
// and so on.
|
||||||
|
const table: []const [2]Coordinate = &.{
|
||||||
|
.{
|
||||||
|
.{ .grid = .{ .x = 0, .y = 0 } },
|
||||||
|
.{ .surface = .{ .x = 0, .y = 0 } },
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.{ .grid = .{ .x = 1, .y = 0 } },
|
||||||
|
.{ .surface = .{ .x = 6, .y = 0 } },
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.{ .grid = .{ .x = 1, .y = 1 } },
|
||||||
|
.{ .surface = .{ .x = 6, .y = 10 } },
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.{ .grid = .{ .x = 0, .y = 0 } },
|
||||||
|
.{ .surface = .{ .x = -10, .y = -10 } },
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.{ .grid = .{ .x = test_size.grid().columns - 1, .y = test_size.grid().rows - 1 } },
|
||||||
|
.{ .surface = .{ .x = 100_000, .y = 100_000 } },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (table) |pair| {
|
||||||
|
const expected = pair[0];
|
||||||
|
const actual = pair[1].convert(@as(Coordinate.Tag, expected), test_size);
|
||||||
|
try testing.expectEqual(expected, actual);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -8,17 +8,8 @@ const Command = @import("../Command.zig");
|
|||||||
const Config = @import("../config.zig").Config;
|
const Config = @import("../config.zig").Config;
|
||||||
const termio = @import("../termio.zig");
|
const termio = @import("../termio.zig");
|
||||||
|
|
||||||
/// The size of the terminal grid.
|
/// All size metrics for the terminal.
|
||||||
grid_size: renderer.GridSize,
|
size: renderer.Size,
|
||||||
|
|
||||||
/// The size of a single cell, in pixels.
|
|
||||||
cell_size: renderer.CellSize,
|
|
||||||
|
|
||||||
/// The size of the viewport in pixels.
|
|
||||||
screen_size: renderer.ScreenSize,
|
|
||||||
|
|
||||||
/// The padding of the viewport.
|
|
||||||
padding: renderer.Padding,
|
|
||||||
|
|
||||||
/// The full app configuration. This is only available during initialization.
|
/// The full app configuration. This is only available during initialization.
|
||||||
/// The memory it points to is NOT stable after the init call so any values
|
/// The memory it points to is NOT stable after the init call so any values
|
||||||
|
@ -56,11 +56,8 @@ renderer_mailbox: *renderer.Thread.Mailbox,
|
|||||||
/// The mailbox for communicating with the surface.
|
/// The mailbox for communicating with the surface.
|
||||||
surface_mailbox: apprt.surface.Mailbox,
|
surface_mailbox: apprt.surface.Mailbox,
|
||||||
|
|
||||||
/// The cached grid size whenever a resize is called.
|
/// The cached size info
|
||||||
grid_size: renderer.GridSize,
|
size: renderer.Size,
|
||||||
|
|
||||||
/// The size of a single cell. Used for size reports.
|
|
||||||
cell_size: renderer.CellSize,
|
|
||||||
|
|
||||||
/// The mailbox implementation to use.
|
/// The mailbox implementation to use.
|
||||||
mailbox: termio.Mailbox,
|
mailbox: termio.Mailbox,
|
||||||
@ -131,10 +128,13 @@ pub const DerivedConfig = struct {
|
|||||||
/// to run a child process.
|
/// to run a child process.
|
||||||
pub fn init(self: *Termio, alloc: Allocator, opts: termio.Options) !void {
|
pub fn init(self: *Termio, alloc: Allocator, opts: termio.Options) !void {
|
||||||
// Create our terminal
|
// Create our terminal
|
||||||
var term = try terminal.Terminal.init(alloc, .{
|
var term = try terminal.Terminal.init(alloc, opts: {
|
||||||
.cols = opts.grid_size.columns,
|
const grid_size = opts.size.grid();
|
||||||
.rows = opts.grid_size.rows,
|
break :opts .{
|
||||||
.max_scrollback = opts.full_config.@"scrollback-limit",
|
.cols = grid_size.columns,
|
||||||
|
.rows = grid_size.rows,
|
||||||
|
.max_scrollback = opts.full_config.@"scrollback-limit",
|
||||||
|
};
|
||||||
});
|
});
|
||||||
errdefer term.deinit(alloc);
|
errdefer term.deinit(alloc);
|
||||||
term.default_palette = opts.config.palette;
|
term.default_palette = opts.config.palette;
|
||||||
@ -168,14 +168,14 @@ pub fn init(self: *Termio, alloc: Allocator, opts: termio.Options) !void {
|
|||||||
// Set our default cursor style
|
// Set our default cursor style
|
||||||
term.screen.cursor.cursor_style = opts.config.cursor_style;
|
term.screen.cursor.cursor_style = opts.config.cursor_style;
|
||||||
|
|
||||||
|
// Setup our terminal size in pixels for certain requests.
|
||||||
|
term.width_px = term.cols * opts.size.cell.width;
|
||||||
|
term.height_px = term.rows * opts.size.cell.height;
|
||||||
|
|
||||||
// Setup our backend.
|
// Setup our backend.
|
||||||
var backend = opts.backend;
|
var backend = opts.backend;
|
||||||
backend.initTerminal(&term);
|
backend.initTerminal(&term);
|
||||||
|
|
||||||
// Setup our terminal size in pixels for certain requests.
|
|
||||||
term.width_px = opts.grid_size.columns * opts.cell_size.width;
|
|
||||||
term.height_px = opts.grid_size.rows * opts.cell_size.height;
|
|
||||||
|
|
||||||
// Create our stream handler. This points to memory in self so it
|
// Create our stream handler. This points to memory in self so it
|
||||||
// isn't safe to use until self.* is set.
|
// isn't safe to use until self.* is set.
|
||||||
const handler: StreamHandler = handler: {
|
const handler: StreamHandler = handler: {
|
||||||
@ -191,7 +191,7 @@ pub fn init(self: *Termio, alloc: Allocator, opts: termio.Options) !void {
|
|||||||
.renderer_state = opts.renderer_state,
|
.renderer_state = opts.renderer_state,
|
||||||
.renderer_wakeup = opts.renderer_wakeup,
|
.renderer_wakeup = opts.renderer_wakeup,
|
||||||
.renderer_mailbox = opts.renderer_mailbox,
|
.renderer_mailbox = opts.renderer_mailbox,
|
||||||
.grid_size = &self.grid_size,
|
.size = &self.size,
|
||||||
.terminal = &self.terminal,
|
.terminal = &self.terminal,
|
||||||
.osc_color_report_format = opts.config.osc_color_report_format,
|
.osc_color_report_format = opts.config.osc_color_report_format,
|
||||||
.enquiry_response = opts.config.enquiry_response,
|
.enquiry_response = opts.config.enquiry_response,
|
||||||
@ -214,8 +214,7 @@ pub fn init(self: *Termio, alloc: Allocator, opts: termio.Options) !void {
|
|||||||
.renderer_wakeup = opts.renderer_wakeup,
|
.renderer_wakeup = opts.renderer_wakeup,
|
||||||
.renderer_mailbox = opts.renderer_mailbox,
|
.renderer_mailbox = opts.renderer_mailbox,
|
||||||
.surface_mailbox = opts.surface_mailbox,
|
.surface_mailbox = opts.surface_mailbox,
|
||||||
.grid_size = opts.grid_size,
|
.size = opts.size,
|
||||||
.cell_size = opts.cell_size,
|
|
||||||
.backend = opts.backend,
|
.backend = opts.backend,
|
||||||
.mailbox = opts.mailbox,
|
.mailbox = opts.mailbox,
|
||||||
.terminal_stream = .{
|
.terminal_stream = .{
|
||||||
@ -349,18 +348,12 @@ pub fn changeConfig(self: *Termio, td: *ThreadData, config: *DerivedConfig) !voi
|
|||||||
pub fn resize(
|
pub fn resize(
|
||||||
self: *Termio,
|
self: *Termio,
|
||||||
td: *ThreadData,
|
td: *ThreadData,
|
||||||
grid_size: renderer.GridSize,
|
size: renderer.Size,
|
||||||
cell_size: renderer.CellSize,
|
|
||||||
screen_size: renderer.ScreenSize,
|
|
||||||
padding: renderer.Padding,
|
|
||||||
) !void {
|
) !void {
|
||||||
// Update the size of our pty.
|
const grid_size = size.grid();
|
||||||
const padded_size = screen_size.subPadding(padding);
|
|
||||||
try self.backend.resize(grid_size, padded_size);
|
|
||||||
|
|
||||||
// Update our cached grid size
|
// Update the size of our pty.
|
||||||
self.grid_size = grid_size;
|
try self.backend.resize(grid_size, size.terminal());
|
||||||
self.cell_size = cell_size;
|
|
||||||
|
|
||||||
// Enter the critical area that we want to keep small
|
// Enter the critical area that we want to keep small
|
||||||
{
|
{
|
||||||
@ -375,8 +368,8 @@ pub fn resize(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Update our pixel sizes
|
// Update our pixel sizes
|
||||||
self.terminal.width_px = self.grid_size.columns * self.cell_size.width;
|
self.terminal.width_px = grid_size.columns * self.size.cell.width;
|
||||||
self.terminal.height_px = self.grid_size.rows * self.cell_size.height;
|
self.terminal.height_px = grid_size.rows * self.size.cell.height;
|
||||||
|
|
||||||
// Disable synchronized output mode so that we show changes
|
// Disable synchronized output mode so that we show changes
|
||||||
// immediately for a resize. This is allowed by the spec.
|
// immediately for a resize. This is allowed by the spec.
|
||||||
@ -389,12 +382,7 @@ pub fn resize(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Mail the renderer so that it can update the GPU and re-render
|
// Mail the renderer so that it can update the GPU and re-render
|
||||||
_ = self.renderer_mailbox.push(.{
|
_ = self.renderer_mailbox.push(.{ .resize = size }, .{ .forever = {} });
|
||||||
.resize = .{
|
|
||||||
.screen_size = screen_size,
|
|
||||||
.padding = padding,
|
|
||||||
},
|
|
||||||
}, .{ .forever = {} });
|
|
||||||
self.renderer_wakeup.notify() catch {};
|
self.renderer_wakeup.notify() catch {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -406,6 +394,8 @@ pub fn sizeReport(self: *Termio, td: *ThreadData, style: termio.Message.SizeRepo
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn sizeReportLocked(self: *Termio, td: *ThreadData, style: termio.Message.SizeReport) !void {
|
fn sizeReportLocked(self: *Termio, td: *ThreadData, style: termio.Message.SizeReport) !void {
|
||||||
|
const grid_size = self.size.grid();
|
||||||
|
|
||||||
// 1024 bytes should be enough for size report since report
|
// 1024 bytes should be enough for size report since report
|
||||||
// in columns and pixels.
|
// in columns and pixels.
|
||||||
var buf: [1024]u8 = undefined;
|
var buf: [1024]u8 = undefined;
|
||||||
@ -414,34 +404,34 @@ fn sizeReportLocked(self: *Termio, td: *ThreadData, style: termio.Message.SizeRe
|
|||||||
&buf,
|
&buf,
|
||||||
"\x1B[48;{};{};{};{}t",
|
"\x1B[48;{};{};{};{}t",
|
||||||
.{
|
.{
|
||||||
self.grid_size.rows,
|
grid_size.rows,
|
||||||
self.grid_size.columns,
|
grid_size.columns,
|
||||||
self.grid_size.rows * self.cell_size.height,
|
grid_size.rows * self.size.cell.height,
|
||||||
self.grid_size.columns * self.cell_size.width,
|
grid_size.columns * self.size.cell.width,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
.csi_14_t => try std.fmt.bufPrint(
|
.csi_14_t => try std.fmt.bufPrint(
|
||||||
&buf,
|
&buf,
|
||||||
"\x1b[4;{};{}t",
|
"\x1b[4;{};{}t",
|
||||||
.{
|
.{
|
||||||
self.grid_size.rows * self.cell_size.height,
|
grid_size.rows * self.size.cell.height,
|
||||||
self.grid_size.columns * self.cell_size.width,
|
grid_size.columns * self.size.cell.width,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
.csi_16_t => try std.fmt.bufPrint(
|
.csi_16_t => try std.fmt.bufPrint(
|
||||||
&buf,
|
&buf,
|
||||||
"\x1b[6;{};{}t",
|
"\x1b[6;{};{}t",
|
||||||
.{
|
.{
|
||||||
self.cell_size.height,
|
self.size.cell.height,
|
||||||
self.cell_size.width,
|
self.size.cell.width,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
.csi_18_t => try std.fmt.bufPrint(
|
.csi_18_t => try std.fmt.bufPrint(
|
||||||
&buf,
|
&buf,
|
||||||
"\x1b[8;{};{}t",
|
"\x1b[8;{};{}t",
|
||||||
.{
|
.{
|
||||||
self.grid_size.rows,
|
grid_size.rows,
|
||||||
self.grid_size.columns,
|
grid_size.columns,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
@ -17,6 +17,7 @@ const builtin = @import("builtin");
|
|||||||
const xev = @import("xev");
|
const xev = @import("xev");
|
||||||
const crash = @import("../crash/main.zig");
|
const crash = @import("../crash/main.zig");
|
||||||
const termio = @import("../termio.zig");
|
const termio = @import("../termio.zig");
|
||||||
|
const renderer = @import("../renderer.zig");
|
||||||
const BlockingQueue = @import("../datastruct/main.zig").BlockingQueue;
|
const BlockingQueue = @import("../datastruct/main.zig").BlockingQueue;
|
||||||
|
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
@ -28,7 +29,7 @@ const Coalesce = struct {
|
|||||||
/// Not all message types are coalesced.
|
/// Not all message types are coalesced.
|
||||||
const min_ms = 25;
|
const min_ms = 25;
|
||||||
|
|
||||||
resize: ?termio.Message.Resize = null,
|
resize: ?renderer.Size = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The number of milliseconds before we reset the synchronized output flag
|
/// The number of milliseconds before we reset the synchronized output flag
|
||||||
@ -324,7 +325,7 @@ fn startSynchronizedOutput(self: *Thread, cb: *CallbackData) void {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handleResize(self: *Thread, cb: *CallbackData, resize: termio.Message.Resize) void {
|
fn handleResize(self: *Thread, cb: *CallbackData, resize: renderer.Size) void {
|
||||||
self.coalesce_data.resize = resize;
|
self.coalesce_data.resize = resize;
|
||||||
|
|
||||||
// If the timer is already active we just return. In the future we want
|
// If the timer is already active we just return. In the future we want
|
||||||
@ -380,13 +381,7 @@ fn coalesceCallback(
|
|||||||
|
|
||||||
if (cb.self.coalesce_data.resize) |v| {
|
if (cb.self.coalesce_data.resize) |v| {
|
||||||
cb.self.coalesce_data.resize = null;
|
cb.self.coalesce_data.resize = null;
|
||||||
cb.io.resize(
|
cb.io.resize(&cb.data, v) catch |err| {
|
||||||
&cb.data,
|
|
||||||
v.grid_size,
|
|
||||||
v.cell_size,
|
|
||||||
v.screen_size,
|
|
||||||
v.padding,
|
|
||||||
) catch |err| {
|
|
||||||
log.warn("error during resize err={}", .{err});
|
log.warn("error during resize err={}", .{err});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -16,22 +16,6 @@ pub const Message = union(enum) {
|
|||||||
/// in the future.
|
/// in the future.
|
||||||
pub const WriteReq = MessageData(u8, 38);
|
pub const WriteReq = MessageData(u8, 38);
|
||||||
|
|
||||||
pub const Resize = struct {
|
|
||||||
/// The grid size for the given screen size with padding applied.
|
|
||||||
grid_size: renderer.GridSize,
|
|
||||||
|
|
||||||
/// The updated cell size.
|
|
||||||
cell_size: renderer.CellSize,
|
|
||||||
|
|
||||||
/// The full screen (drawable) size. This does NOT include padding.
|
|
||||||
/// This should be sent on to the renderer.
|
|
||||||
screen_size: renderer.ScreenSize,
|
|
||||||
|
|
||||||
/// The padding, so that the terminal implementation can subtract
|
|
||||||
/// this to send to the pty.
|
|
||||||
padding: renderer.Padding,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Purposely crash the renderer. This is used for testing and debugging.
|
/// Purposely crash the renderer. This is used for testing and debugging.
|
||||||
/// See the "crash" binding action.
|
/// See the "crash" binding action.
|
||||||
crash: void,
|
crash: void,
|
||||||
@ -47,7 +31,7 @@ pub const Message = union(enum) {
|
|||||||
inspector: bool,
|
inspector: bool,
|
||||||
|
|
||||||
/// Resize the window.
|
/// Resize the window.
|
||||||
resize: Resize,
|
resize: renderer.Size,
|
||||||
|
|
||||||
/// Request a size report is sent to the pty ([in-band
|
/// Request a size report is sent to the pty ([in-band
|
||||||
/// size report, mode 2048](https://gist.github.com/rockorager/e695fb2924d36b2bcf1fff4a3704bd83) and
|
/// size report, mode 2048](https://gist.github.com/rockorager/e695fb2924d36b2bcf1fff4a3704bd83) and
|
||||||
|
@ -26,7 +26,7 @@ const disable_kitty_keyboard_protocol = apprt.runtime == apprt.glfw;
|
|||||||
/// unless all of the member fields are copied.
|
/// unless all of the member fields are copied.
|
||||||
pub const StreamHandler = struct {
|
pub const StreamHandler = struct {
|
||||||
alloc: Allocator,
|
alloc: Allocator,
|
||||||
grid_size: *renderer.GridSize,
|
size: *renderer.Size,
|
||||||
terminal: *terminal.Terminal,
|
terminal: *terminal.Terminal,
|
||||||
|
|
||||||
/// Mailbox for data to the termio thread.
|
/// Mailbox for data to the termio thread.
|
||||||
@ -611,12 +611,15 @@ pub const StreamHandler = struct {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// Force resize back to the window size
|
// Force resize back to the window size
|
||||||
.enable_mode_3 => self.terminal.resize(
|
.enable_mode_3 => {
|
||||||
self.alloc,
|
const grid_size = self.size.grid();
|
||||||
self.grid_size.columns,
|
self.terminal.resize(
|
||||||
self.grid_size.rows,
|
self.alloc,
|
||||||
) catch |err| {
|
grid_size.columns,
|
||||||
log.err("error updating terminal size: {}", .{err});
|
grid_size.rows,
|
||||||
|
) catch |err| {
|
||||||
|
log.err("error updating terminal size: {}", .{err});
|
||||||
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
.@"132_column" => try self.terminal.deccolm(
|
.@"132_column" => try self.terminal.deccolm(
|
||||||
|
Reference in New Issue
Block a user