From 660faf3ac353dc254cd8ee9d31294147c7aabf12 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 21 Aug 2023 15:34:46 -0700 Subject: [PATCH] terminal: clear screen, alt screen, etc. clear all kitty graphics --- src/terminal/Terminal.zig | 37 ++++++++++++++++++------- src/terminal/kitty/graphics_exec.zig | 2 ++ src/terminal/kitty/graphics_storage.zig | 25 +++++++++++++++++ src/termio/Exec.zig | 8 +++--- 4 files changed, 58 insertions(+), 14 deletions(-) diff --git a/src/terminal/Terminal.zig b/src/terminal/Terminal.zig index 5726e2833..93a380ca9 100644 --- a/src/terminal/Terminal.zig +++ b/src/terminal/Terminal.zig @@ -189,7 +189,11 @@ pub const AlternateScreenOptions = struct { /// * has its own cursor state (included saved cursor) /// * does not support scrollback /// -pub fn alternateScreen(self: *Terminal, options: AlternateScreenOptions) void { +pub fn alternateScreen( + self: *Terminal, + alloc: Allocator, + options: AlternateScreenOptions, +) void { const tracy = trace(@src()); defer tracy.end(); @@ -216,12 +220,16 @@ pub fn alternateScreen(self: *Terminal, options: AlternateScreenOptions) void { self.screen.selection = null; if (options.clear_on_enter) { - self.eraseDisplay(.complete); + self.eraseDisplay(alloc, .complete); } } /// Switch back to the primary screen (reset alternate screen mode). -pub fn primaryScreen(self: *Terminal, options: AlternateScreenOptions) void { +pub fn primaryScreen( + self: *Terminal, + alloc: Allocator, + options: AlternateScreenOptions, +) void { const tracy = trace(@src()); defer tracy.end(); @@ -231,7 +239,7 @@ pub fn primaryScreen(self: *Terminal, options: AlternateScreenOptions) void { // TODO(mitchellh): what happens if we enter alternate screen multiple times? if (self.active_screen == .primary) return; - if (options.clear_on_exit) self.eraseDisplay(.complete); + if (options.clear_on_exit) self.eraseDisplay(alloc, .complete); // Switch the screens const old = self.screen; @@ -278,7 +286,7 @@ pub fn deccolm(self: *Terminal, alloc: Allocator, mode: DeccolmMode) !void { try self.resize(alloc, 0, self.rows); // TODO: do not clear screen flag mode - self.eraseDisplay(.complete); + self.eraseDisplay(alloc, .complete); self.setCursorPos(1, 1); // TODO: left/right margins @@ -987,6 +995,7 @@ pub fn setCursorColAbsolute(self: *Terminal, col_req: usize) void { /// TODO: test pub fn eraseDisplay( self: *Terminal, + alloc: Allocator, mode: csi.EraseDisplay, ) void { const tracy = trace(@src()); @@ -1003,6 +1012,13 @@ pub fn eraseDisplay( // Unsets pending wrap state self.screen.cursor.pending_wrap = false; + + // Clear all Kitty graphics state for this screen + self.screen.kitty_images.delete( + alloc, + &self.screen, + .{ .all = true }, + ); }, .below => { @@ -1572,17 +1588,18 @@ pub fn kittyGraphics( } /// Full reset -pub fn fullReset(self: *Terminal) void { - self.primaryScreen(.{ .clear_on_exit = true, .cursor_save = true }); +pub fn fullReset(self: *Terminal, alloc: Allocator) void { + self.primaryScreen(alloc, .{ .clear_on_exit = true, .cursor_save = true }); self.charset = .{}; - self.eraseDisplay(.scrollback); - self.eraseDisplay(.complete); + self.eraseDisplay(alloc, .scrollback); + self.eraseDisplay(alloc, .complete); self.modes = .{}; self.flags = .{}; self.tabstops.reset(0); self.screen.cursor = .{}; self.screen.saved_cursor = .{}; self.screen.selection = null; + self.screen.kitty_keyboard = .{}; self.scrolling_region = .{ .top = 0, .bottom = self.rows - 1 }; self.previous_char = null; self.pwd.clearRetainingCapacity(); @@ -2541,7 +2558,7 @@ test "Terminal: cursorIsAtPrompt alternate screen" { try testing.expect(t.cursorIsAtPrompt()); // Secondary screen is never a prompt - t.alternateScreen(.{}); + t.alternateScreen(alloc, .{}); try testing.expect(!t.cursorIsAtPrompt()); t.markSemanticPrompt(.prompt); try testing.expect(!t.cursorIsAtPrompt()); diff --git a/src/terminal/kitty/graphics_exec.zig b/src/terminal/kitty/graphics_exec.zig index 8af16a8a2..562765a46 100644 --- a/src/terminal/kitty/graphics_exec.zig +++ b/src/terminal/kitty/graphics_exec.zig @@ -16,6 +16,8 @@ const log = std.log.scoped(.kitty_gfx); // TODO: // - delete // - shared memory transmit +// - 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 diff --git a/src/terminal/kitty/graphics_storage.zig b/src/terminal/kitty/graphics_storage.zig index 009121ab1..c9aaa12d7 100644 --- a/src/terminal/kitty/graphics_storage.zig +++ b/src/terminal/kitty/graphics_storage.zig @@ -5,6 +5,7 @@ const ArenaAllocator = std.heap.ArenaAllocator; const point = @import("../point.zig"); const command = @import("graphics_command.zig"); +const Screen = @import("../Screen.zig"); const LoadingImage = @import("graphics_image.zig").LoadingImage; const Image = @import("graphics_image.zig").Image; const Command = command.Command; @@ -107,6 +108,30 @@ pub const ImageStorage = struct { return null; } + /// Delete placements, images. + pub fn delete( + self: *ImageStorage, + alloc: Allocator, + screen: *const Screen, + cmd: command.Delete, + ) void { + _ = screen; + + switch (cmd) { + .all => |delete_images| if (delete_images) { + // We just reset our entire state. + self.deinit(alloc); + self.* = .{}; + } else { + // Delete all our placements + self.placements.deinit(alloc); + self.placements = .{}; + }, + + else => log.warn("unimplemented delete command: {}", .{cmd}), + } + } + /// Every placement is uniquely identified by the image ID and the /// placement ID. If an image ID isn't specified it is assumed to be 0. /// Likewise, if a placement ID isn't specified it is assumed to be 0. diff --git a/src/termio/Exec.zig b/src/termio/Exec.zig index dd94e6e5c..94f53d3ba 100644 --- a/src/termio/Exec.zig +++ b/src/termio/Exec.zig @@ -1185,7 +1185,7 @@ const StreamHandler = struct { try self.queueRender(); } - self.terminal.eraseDisplay(mode); + self.terminal.eraseDisplay(self.alloc, mode); } pub fn eraseLine(self: *StreamHandler, mode: terminal.EraseLine) !void { @@ -1280,9 +1280,9 @@ const StreamHandler = struct { }; if (enabled) - self.terminal.alternateScreen(opts) + self.terminal.alternateScreen(self.alloc, opts) else - self.terminal.primaryScreen(opts); + self.terminal.primaryScreen(self.alloc, opts); // Schedule a render since we changed screens try self.queueRender(); @@ -1450,7 +1450,7 @@ const StreamHandler = struct { pub fn fullReset( self: *StreamHandler, ) !void { - self.terminal.fullReset(); + self.terminal.fullReset(self.alloc); } pub fn queryKittyKeyboard(self: *StreamHandler) !void {