mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-22 19:56:08 +03:00
terminal/kitty-gfx: chunked transmit and display
This commit is contained in:
@ -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;
|
||||||
|
@ -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
|
||||||
|
Reference in New Issue
Block a user