terminal/kitty-gfx: chunked transmit and display

This commit is contained in:
Mitchell Hashimoto
2023-08-23 17:55:41 -07:00
parent 23c7d95ee1
commit 53452bab78
2 changed files with 41 additions and 33 deletions

View File

@ -30,17 +30,22 @@ pub fn execute(
// If storage is disabled then we disable the full protocol. This means // If storage is disabled then we disable the full protocol. This means
// we don't even respond to queries so the terminal completely acts as // we don't even respond to queries so the terminal completely acts as
// if this feature is not supported. // if this feature is not supported.
if (!terminal.screen.kitty_images.enabled()) return null; if (!terminal.screen.kitty_images.enabled()) {
log.debug("kitty graphics requested but disabled", .{});
return null;
}
// Only Metal supports rendering the images, right now. // Only Metal supports rendering the images, right now.
if (comptime renderer.Renderer != renderer.Metal) return null; if (comptime renderer.Renderer != renderer.Metal) {
log.warn("kitty graphics not supported on this renderer", .{});
return null;
}
log.debug("executing kitty graphics command: {}", .{cmd.control}); log.debug("executing kitty graphics command: {}", .{cmd.control});
const resp_: ?Response = switch (cmd.control) { const resp_: ?Response = switch (cmd.control) {
.query => query(alloc, cmd), .query => query(alloc, cmd),
.transmit => transmit(alloc, terminal, cmd), .transmit, .transmit_and_display => transmit(alloc, terminal, cmd),
.transmit_and_display => transmitAndDisplay(alloc, terminal, cmd),
.display => display(alloc, terminal, cmd), .display => display(alloc, terminal, cmd),
.delete => delete(alloc, terminal, cmd), .delete => delete(alloc, terminal, cmd),
@ -113,19 +118,26 @@ fn transmit(
.placement_id = t.placement_id, .placement_id = t.placement_id,
}; };
var img = loadAndAddImage(alloc, terminal, cmd) catch |err| { const load = loadAndAddImage(alloc, terminal, cmd) catch |err| {
encodeError(&result, err); encodeError(&result, err);
return result; return result;
}; };
errdefer img.deinit(alloc); errdefer load.image.deinit(alloc);
// If we're also displaying, then do that now. This function does
// both transmit and transmit and display. The display might also be
// deferred if it is multi-chunk.
if (load.display) |d| {
var d_copy = d;
d_copy.image_id = load.image.id;
return display(alloc, terminal, &.{
.control = .{ .display = d_copy },
.quiet = cmd.quiet,
});
}
// After the image is added, set the ID in case it changed // After the image is added, set the ID in case it changed
result.id = img.id; result.id = load.image.id;
// If this is a transmit_and_display then the display part needs the image ID
if (cmd.control == .transmit_and_display) {
cmd.control.transmit_and_display.display.image_id = img.id;
}
return result; return result;
} }
@ -134,7 +146,7 @@ fn transmit(
fn display( fn display(
alloc: Allocator, alloc: Allocator,
terminal: *Terminal, terminal: *Terminal,
cmd: *Command, cmd: *const Command,
) Response { ) Response {
const d = cmd.display().?; const d = cmd.display().?;
@ -212,22 +224,6 @@ fn display(
return result; return result;
} }
/// A combination of transmit and display. Nothing special.
fn transmitAndDisplay(
alloc: Allocator,
terminal: *Terminal,
cmd: *Command,
) Response {
const resp = transmit(alloc, terminal, cmd);
if (!resp.ok()) return resp;
// If the transmission is chunked, we defer the display
const t = cmd.transmission().?;
if (t.more_chunks) return resp;
return display(alloc, terminal, cmd);
}
/// Display a previously transmitted image. /// Display a previously transmitted image.
fn delete( fn delete(
alloc: Allocator, alloc: Allocator,
@ -243,7 +239,10 @@ fn loadAndAddImage(
alloc: Allocator, alloc: Allocator,
terminal: *Terminal, terminal: *Terminal,
cmd: *Command, cmd: *Command,
) !Image { ) !struct {
image: Image,
display: ?command.Display = null,
} {
const t = cmd.transmission().?; const t = cmd.transmission().?;
const storage = &terminal.screen.kitty_images; const storage = &terminal.screen.kitty_images;
@ -254,7 +253,7 @@ fn loadAndAddImage(
try loading.addData(alloc, cmd.data); try loading.addData(alloc, cmd.data);
// If we have more then we're done // If we have more then we're done
if (t.more_chunks) return loading.image; if (t.more_chunks) return .{ .image = loading.image };
// We have no more chunks. We're going to be completing the // We have no more chunks. We're going to be completing the
// image so we want to destroy the pointer to the loading // image so we want to destroy the pointer to the loading
@ -287,7 +286,7 @@ fn loadAndAddImage(
errdefer alloc.destroy(loading_ptr); errdefer alloc.destroy(loading_ptr);
loading_ptr.* = loading; loading_ptr.* = loading;
storage.loading = loading_ptr; storage.loading = loading_ptr;
return loading.image; return .{ .image = loading.image };
} }
// Dump the image data before it is decompressed // Dump the image data before it is decompressed
@ -298,11 +297,14 @@ fn loadAndAddImage(
errdefer img.deinit(alloc); errdefer img.deinit(alloc);
try storage.addImage(alloc, img); try storage.addImage(alloc, img);
// Get our display settings
const display_ = loading.display;
// Ensure we deinit the loading state because we're done. The image // Ensure we deinit the loading state because we're done. The image
// won't be deinit because of "complete" above. // won't be deinit because of "complete" above.
loading.deinit(alloc); loading.deinit(alloc);
return img; return .{ .image = img, .display = display_ };
} }
const EncodeableError = Image.Error || Allocator.Error; const EncodeableError = Image.Error || Allocator.Error;

View File

@ -29,6 +29,10 @@ pub const LoadingImage = struct {
/// The data that is being built up. /// The data that is being built up.
data: std.ArrayListUnmanaged(u8) = .{}, data: std.ArrayListUnmanaged(u8) = .{},
/// This is non-null when a transmit and display command is given
/// so that we display the image after it is fully loaded.
display: ?command.Display = null,
/// Initialize a chunked immage from the first image transmission. /// Initialize a chunked immage from the first image transmission.
/// If this is a multi-chunk image, this should only be the FIRST /// If this is a multi-chunk image, this should only be the FIRST
/// chunk. /// chunk.
@ -49,6 +53,8 @@ pub const LoadingImage = struct {
.compression = t.compression, .compression = t.compression,
.format = t.format, .format = t.format,
}, },
.display = cmd.display(),
}; };
// Special case for the direct medium, we just add it directly // Special case for the direct medium, we just add it directly