kitty graphics: add support for shared memory transfer medium

Adds support for using shared memory to transfer images between
the CLI and Ghostty using the Kitty image protocol. This should be
the fastest way to transfer images if the CLI program and Ghostty are
running on the same system.

Works for single image transfer using `kitten icat`:

```
kitten icat --transfer-mode=memory images/icons/icon_256x256.png
```

However trying to play a movie with `mpv` fails in Ghostty (although it
works in Kitty):

```
mpv --vo=kitty --vo-kitty-use-shm=yes --profile=sw-fast --really-quiet video.mp4
```

`mpv` appears to be sending frames using the normal image transfer
commands but always setting `more_chunks` to `true` which results in an
image never being shown by Ghostty.

Shared memory transfer on Windows remains to be implemented.
This commit is contained in:
Jeffrey C. Ollie
2024-08-08 15:28:31 -05:00
parent df7409f4f3
commit e2fe6bf74b

View File

@ -75,9 +75,13 @@ pub const LoadingImage = struct {
}
var abs_buf: [std.fs.max_path_bytes]u8 = undefined;
const path = posix.realpath(cmd.data, &abs_buf) catch |err| {
log.warn("failed to get absolute path: {}", .{err});
return error.InvalidData;
const path = switch (t.medium) {
.direct => unreachable, // handled above
.file, .temporary_file => posix.realpath(cmd.data, &abs_buf) catch |err| {
log.warn("failed to get absolute path: {}", .{err});
return error.InvalidData;
},
.shared_memory => cmd.data,
};
// Depending on the medium, load the data from the path.
@ -98,15 +102,53 @@ pub const LoadingImage = struct {
t: command.Transmission,
path: []const u8,
) !void {
// We require libc for this for shm_open
if (comptime !builtin.link_libc) return error.UnsupportedMedium;
switch (builtin.target.os.tag) {
.windows => {
// TODO: support shared memory on windows
return error.UnsupportedMedium;
},
else => {
// libc is required for shm_open
if (comptime !builtin.link_libc) return error.UnsupportedMedium;
// Todo: support shared memory
_ = self;
_ = alloc;
_ = t;
_ = path;
return error.UnsupportedMedium;
const pathz = try alloc.dupeZ(u8, path);
defer alloc.free(pathz);
const fd = std.c.shm_open(pathz, @as(c_int, @bitCast(std.c.O{ .ACCMODE = .RDONLY })), 0);
switch (std.posix.errno(fd)) {
.SUCCESS => {
defer _ = std.c.close(fd);
defer _ = std.c.shm_unlink(pathz);
const stat = std.posix.fstat(fd) catch |err| {
log.warn("unable to fstat shared memory {s}: {}", .{ path, err });
return error.InvalidData;
};
if (stat.size <= 0) return error.InvalidData;
const size: usize = @intCast(stat.size);
const map = std.posix.mmap(null, size, std.c.PROT.READ, std.c.MAP{ .TYPE = .SHARED }, fd, 0) catch |err| {
log.warn("unable to mmap shared memory {s}: {}", .{ path, err });
return error.InvalidData;
};
defer std.posix.munmap(map);
const start: usize = @intCast(t.offset);
const end: usize = if (t.size > 0) @min(@as(usize, @intCast(t.offset)) + @as(usize, @intCast(t.size)), size) else size;
assert(self.data.items.len == 0);
try self.data.appendSlice(alloc, map[start..end]);
},
else => |err| {
log.warn("unable to open shared memory {s}: {}", .{ path, err });
return error.InvalidData;
},
}
},
}
}
/// Reads the data from a temporary file and returns it. This allocates