From 71f1f35cfcd90ff5c48c9751895fcd632fd63398 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 22 Aug 2023 12:15:51 -0700 Subject: [PATCH] terminal/kitty-gfx: move cursor after image placement --- src/terminal/Terminal.zig | 7 ++++ src/terminal/kitty/graphics_exec.zig | 53 +++++++++++++++++++++++++++- src/termio/Exec.zig | 16 ++++++--- 3 files changed, 70 insertions(+), 6 deletions(-) diff --git a/src/terminal/Terminal.zig b/src/terminal/Terminal.zig index 93a380ca9..badb81a96 100644 --- a/src/terminal/Terminal.zig +++ b/src/terminal/Terminal.zig @@ -63,6 +63,10 @@ tabstops: Tabstops, rows: usize, cols: usize, +/// The size of the screen in pixels. This is used for pty events and images +width_px: u32 = 0, +height_px: u32 = 0, + /// The current scrolling region. scrolling_region: ScrollingRegion, @@ -305,6 +309,9 @@ pub fn resize(self: *Terminal, alloc: Allocator, cols_req: usize, rows: usize) ! else cols_req; + // If our cols/rows didn't change then we're done + if (self.cols == cols and self.rows == rows) return; + // Resize our tabstops // TODO: use resize, but it doesn't set new tabstops if (self.cols != cols) { diff --git a/src/terminal/kitty/graphics_exec.zig b/src/terminal/kitty/graphics_exec.zig index 277d808b3..380205b81 100644 --- a/src/terminal/kitty/graphics_exec.zig +++ b/src/terminal/kitty/graphics_exec.zig @@ -19,7 +19,6 @@ const log = std.log.scoped(.kitty_gfx); // - terminal state around deleting images (i.e. CSI J) // - terminal state around deleting placements (i.e. scrolling) // (not exhaustive, almost every op is ignoring additional config) - /// Execute a Kitty graphics command against the given terminal. This /// will never fail, but the response may indicate an error and the /// terminal state may not be updated to reflect the command. This will @@ -175,6 +174,58 @@ fn display( return result; }; + // Cursor needs to move after placement + switch (d.cursor_movement) { + .none => {}, + .after => { + // Determine the rectangle of the image in grid cells + const image_grid_size: struct { + columns: u32, + rows: u32, + } = grid_size: { + // Calculate our cell size. + const terminal_width_f64: f64 = @floatFromInt(terminal.width_px); + const terminal_height_f64: f64 = @floatFromInt(terminal.height_px); + const grid_columns_f64: f64 = @floatFromInt(terminal.cols); + const grid_rows_f64: f64 = @floatFromInt(terminal.rows); + const cell_width_f64 = terminal_width_f64 / grid_columns_f64; + const cell_height_f64 = terminal_height_f64 / grid_rows_f64; + + // Calculate our image size in grid cells + const width_f64: f64 = @floatFromInt(img.width); + const height_f64: f64 = @floatFromInt(img.height); + const width_cells: u32 = @intFromFloat(@ceil(width_f64 / cell_width_f64)); + const height_cells: u32 = @intFromFloat(@ceil(height_f64 / cell_height_f64)); + + break :grid_size .{ .columns = width_cells, .rows = height_cells }; + }; + log.warn("terminal width={} height={} image_grid={}", .{ + terminal.width_px, + terminal.height_px, + image_grid_size, + }); + + // If we are moving beneath the screen we need to scroll. + // TODO: handle scroll regions + var new_y = terminal.screen.cursor.y + image_grid_size.rows; + if (new_y >= terminal.rows) { + const scroll_amount = (new_y + 1) - terminal.rows; + terminal.screen.scroll(.{ .screen = @intCast(scroll_amount) }) catch |err| { + // If this failed we just warn, the screen will just be in a + // weird state but nothing fatal. + log.warn("scroll for image failed: {}", .{err}); + }; + new_y = terminal.rows - 1; + } + + // Move the cursor + terminal.setCursorPos( + terminal.screen.cursor.x + image_grid_size.columns, + new_y, + ); + }, + } + return result; } diff --git a/src/termio/Exec.zig b/src/termio/Exec.zig index 94f53d3ba..562961237 100644 --- a/src/termio/Exec.zig +++ b/src/termio/Exec.zig @@ -103,6 +103,8 @@ pub fn init(alloc: Allocator, opts: termio.Options) !Exec { ); errdefer term.deinit(alloc); term.color_palette = opts.config.palette; + term.width_px = opts.screen_size.width; + term.height_px = opts.screen_size.height; var subprocess = try Subprocess.init(alloc, opts); errdefer subprocess.deinit(); @@ -255,10 +257,6 @@ pub fn resize( const padded_size = screen_size.subPadding(padding); try self.subprocess.resize(grid_size, padded_size); - // If our grid size didn't change, then we don't need to change - // the underlying terminal. - if (grid_size.equals(self.grid_size)) return; - // Update our cached grid size self.grid_size = grid_size; @@ -268,7 +266,15 @@ pub fn resize( defer self.renderer_state.mutex.unlock(); // Update the size of our terminal state - try self.terminal.resize(self.alloc, grid_size.columns, grid_size.rows); + try self.terminal.resize( + self.alloc, + grid_size.columns, + grid_size.rows, + ); + + // Update our pixel sizes + self.terminal.width_px = screen_size.width; + self.terminal.height_px = screen_size.height; } }