kitty graphics support

This commit is contained in:
Gregor Lohaus
2026-05-27 14:33:41 +02:00
parent a03bc2ec48
commit 5285fb68c9
14 changed files with 651 additions and 0 deletions

View File

@@ -0,0 +1,63 @@
package dev.jlibghostty;
import dev.jlibghostty.internal.GhosttyLibrary;
import java.lang.foreign.MemorySegment;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public final class KittyGraphics {
private final GhosttyLibrary library;
private final MemorySegment terminal;
private final MemorySegment graphics;
KittyGraphics(GhosttyLibrary library, MemorySegment terminal, MemorySegment graphics) {
this.library = library;
this.terminal = terminal;
this.graphics = graphics;
}
public Optional<KittyImage> image(long imageId) {
MemorySegment image = library.kittyGraphicsImage(graphics, imageId);
if (image.address() == 0) {
return Optional.empty();
}
return Optional.of(new KittyImage(library, image));
}
public List<KittyPlacement> placements() {
return placements(KittyPlacementLayer.ALL);
}
public List<KittyPlacement> placements(KittyPlacementLayer layer) {
try (KittyPlacementIterator iterator = KittyPlacementIterator.open(library, graphics, layer)) {
List<KittyPlacement> placements = new ArrayList<>();
while (iterator.next()) {
long imageId = iterator.getU32(GhosttyLibrary.KITTY_GRAPHICS_PLACEMENT_DATA_IMAGE_ID);
Optional<KittyImage> image = image(imageId);
KittyRenderInfo renderInfo = image
.flatMap(value -> library.kittyPlacementRenderInfo(iterator.handle(), value.handle(), terminal))
.orElse(null);
placements.add(new KittyPlacement(
imageId,
iterator.getU32(GhosttyLibrary.KITTY_GRAPHICS_PLACEMENT_DATA_PLACEMENT_ID),
iterator.getBoolean(GhosttyLibrary.KITTY_GRAPHICS_PLACEMENT_DATA_IS_VIRTUAL),
iterator.getU32(GhosttyLibrary.KITTY_GRAPHICS_PLACEMENT_DATA_X_OFFSET),
iterator.getU32(GhosttyLibrary.KITTY_GRAPHICS_PLACEMENT_DATA_Y_OFFSET),
iterator.getU32(GhosttyLibrary.KITTY_GRAPHICS_PLACEMENT_DATA_SOURCE_X),
iterator.getU32(GhosttyLibrary.KITTY_GRAPHICS_PLACEMENT_DATA_SOURCE_Y),
iterator.getU32(GhosttyLibrary.KITTY_GRAPHICS_PLACEMENT_DATA_SOURCE_WIDTH),
iterator.getU32(GhosttyLibrary.KITTY_GRAPHICS_PLACEMENT_DATA_SOURCE_HEIGHT),
iterator.getU32(GhosttyLibrary.KITTY_GRAPHICS_PLACEMENT_DATA_COLUMNS),
iterator.getU32(GhosttyLibrary.KITTY_GRAPHICS_PLACEMENT_DATA_ROWS),
iterator.getI32(GhosttyLibrary.KITTY_GRAPHICS_PLACEMENT_DATA_Z),
image.map(KittyImage::snapshot),
Optional.ofNullable(renderInfo)
));
}
return List.copyOf(placements);
}
}
}

View File

@@ -0,0 +1,31 @@
package dev.jlibghostty;
import dev.jlibghostty.internal.GhosttyLibrary;
import java.lang.foreign.MemorySegment;
public final class KittyImage {
private final GhosttyLibrary library;
private final MemorySegment handle;
KittyImage(GhosttyLibrary library, MemorySegment handle) {
this.library = library;
this.handle = handle;
}
public KittyImageSnapshot snapshot() {
return new KittyImageSnapshot(
library.kittyImageGetU32(handle, GhosttyLibrary.KITTY_IMAGE_DATA_ID),
library.kittyImageGetU32(handle, GhosttyLibrary.KITTY_IMAGE_DATA_NUMBER),
library.kittyImageGetU32(handle, GhosttyLibrary.KITTY_IMAGE_DATA_WIDTH),
library.kittyImageGetU32(handle, GhosttyLibrary.KITTY_IMAGE_DATA_HEIGHT),
KittyImageFormat.fromNative(library.kittyImageGetI32(handle, GhosttyLibrary.KITTY_IMAGE_DATA_FORMAT)),
KittyImageCompression.fromNative(library.kittyImageGetI32(handle, GhosttyLibrary.KITTY_IMAGE_DATA_COMPRESSION)),
library.kittyImageData(handle)
);
}
MemorySegment handle() {
return handle;
}
}

View File

@@ -0,0 +1,25 @@
package dev.jlibghostty;
public enum KittyImageCompression {
NONE(0),
ZLIB_DEFLATE(1);
private final int nativeValue;
KittyImageCompression(int nativeValue) {
this.nativeValue = nativeValue;
}
public int nativeValue() {
return nativeValue;
}
static KittyImageCompression fromNative(int value) {
for (KittyImageCompression compression : values()) {
if (compression.nativeValue == value) {
return compression;
}
}
throw new IllegalArgumentException("Unknown Kitty image compression: " + value);
}
}

View File

@@ -0,0 +1,28 @@
package dev.jlibghostty;
public enum KittyImageFormat {
RGB(0),
RGBA(1),
PNG(2),
GRAY_ALPHA(3),
GRAY(4);
private final int nativeValue;
KittyImageFormat(int nativeValue) {
this.nativeValue = nativeValue;
}
public int nativeValue() {
return nativeValue;
}
static KittyImageFormat fromNative(int value) {
for (KittyImageFormat format : values()) {
if (format.nativeValue == value) {
return format;
}
}
throw new IllegalArgumentException("Unknown Kitty image format: " + value);
}
}

View File

@@ -0,0 +1,20 @@
package dev.jlibghostty;
public record KittyImageSnapshot(
long id,
long number,
long width,
long height,
KittyImageFormat format,
KittyImageCompression compression,
byte[] data
) {
public KittyImageSnapshot {
data = data.clone();
}
@Override
public byte[] data() {
return data.clone();
}
}

View File

@@ -0,0 +1,21 @@
package dev.jlibghostty;
import java.util.Optional;
public record KittyPlacement(
long imageId,
long placementId,
boolean virtual,
long xOffset,
long yOffset,
long sourceX,
long sourceY,
long sourceWidth,
long sourceHeight,
long columns,
long rows,
int z,
Optional<KittyImageSnapshot> image,
Optional<KittyRenderInfo> renderInfo
) {
}

View File

@@ -0,0 +1,56 @@
package dev.jlibghostty;
import dev.jlibghostty.internal.GhosttyLibrary;
import java.lang.foreign.MemorySegment;
final class KittyPlacementIterator implements AutoCloseable {
private final GhosttyLibrary library;
private final MemorySegment handle;
private boolean closed;
private KittyPlacementIterator(GhosttyLibrary library, MemorySegment handle) {
this.library = library;
this.handle = handle;
}
static KittyPlacementIterator open(GhosttyLibrary library, MemorySegment graphics, KittyPlacementLayer layer) {
MemorySegment handle = library.kittyPlacementIteratorNew();
try {
library.kittyGraphicsPopulatePlacementIterator(graphics, handle);
library.kittyPlacementIteratorSetLayer(handle, layer.nativeValue());
return new KittyPlacementIterator(library, handle);
} catch (RuntimeException | Error e) {
library.kittyPlacementIteratorFree(handle);
throw e;
}
}
boolean next() {
return library.kittyPlacementNext(handle);
}
long getU32(int key) {
return library.kittyPlacementGetU32(handle, key);
}
int getI32(int key) {
return library.kittyPlacementGetI32(handle, key);
}
boolean getBoolean(int key) {
return library.kittyPlacementGetBoolean(handle, key);
}
MemorySegment handle() {
return handle;
}
@Override
public void close() {
if (!closed) {
closed = true;
library.kittyPlacementIteratorFree(handle);
}
}
}

View File

@@ -0,0 +1,18 @@
package dev.jlibghostty;
public enum KittyPlacementLayer {
ALL(0),
BELOW_BACKGROUND(1),
BELOW_TEXT(2),
ABOVE_TEXT(3);
private final int nativeValue;
KittyPlacementLayer(int nativeValue) {
this.nativeValue = nativeValue;
}
int nativeValue() {
return nativeValue;
}
}

View File

@@ -0,0 +1,16 @@
package dev.jlibghostty;
public record KittyRenderInfo(
long pixelWidth,
long pixelHeight,
long gridColumns,
long gridRows,
int viewportColumn,
int viewportRow,
boolean viewportVisible,
long sourceX,
long sourceY,
long sourceWidth,
long sourceHeight
) {
}

View File

@@ -4,6 +4,7 @@ import dev.jlibghostty.internal.GhosttyLibrary;
import java.lang.foreign.MemorySegment;
import java.nio.charset.StandardCharsets;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
public final class Terminal implements AutoCloseable {
@@ -40,6 +41,38 @@ public final class Terminal implements AutoCloseable {
library.terminalResize(handle, columns, rows, cellWidthPx, cellHeightPx);
}
public void setKittyImageStorageLimit(long bytes) {
ensureOpen();
library.terminalSetU64(handle, GhosttyLibrary.TERMINAL_OPT_KITTY_IMAGE_STORAGE_LIMIT, bytes);
}
public void setKittyImageMediumFile(boolean enabled) {
ensureOpen();
library.terminalSetBoolean(handle, GhosttyLibrary.TERMINAL_OPT_KITTY_IMAGE_MEDIUM_FILE, enabled);
}
public void setKittyImageMediumTemporaryFile(boolean enabled) {
ensureOpen();
library.terminalSetBoolean(handle, GhosttyLibrary.TERMINAL_OPT_KITTY_IMAGE_MEDIUM_TEMP_FILE, enabled);
}
public void setKittyImageMediumSharedMemory(boolean enabled) {
ensureOpen();
library.terminalSetBoolean(handle, GhosttyLibrary.TERMINAL_OPT_KITTY_IMAGE_MEDIUM_SHARED_MEM, enabled);
}
public Optional<KittyGraphics> kittyGraphics() {
ensureOpen();
MemorySegment graphics = library.terminalGetPointerOrNull(
handle,
GhosttyLibrary.TERMINAL_DATA_KITTY_GRAPHICS
);
if (graphics.address() == 0) {
return Optional.empty();
}
return Optional.of(new KittyGraphics(library, handle, graphics));
}
public TerminalSnapshot snapshot() {
ensureOpen();
return new TerminalSnapshot(

View File

@@ -26,6 +26,36 @@ public final class GhosttyLibrary {
public static final int TERMINAL_DATA_CURSOR_VISIBLE = 7;
public static final int TERMINAL_DATA_TITLE = 12;
public static final int TERMINAL_DATA_PWD = 13;
public static final int TERMINAL_DATA_KITTY_GRAPHICS = 30;
public static final int TERMINAL_OPT_KITTY_IMAGE_STORAGE_LIMIT = 15;
public static final int TERMINAL_OPT_KITTY_IMAGE_MEDIUM_FILE = 16;
public static final int TERMINAL_OPT_KITTY_IMAGE_MEDIUM_TEMP_FILE = 17;
public static final int TERMINAL_OPT_KITTY_IMAGE_MEDIUM_SHARED_MEM = 18;
public static final int KITTY_GRAPHICS_DATA_PLACEMENT_ITERATOR = 1;
public static final int KITTY_GRAPHICS_PLACEMENT_DATA_IMAGE_ID = 1;
public static final int KITTY_GRAPHICS_PLACEMENT_DATA_PLACEMENT_ID = 2;
public static final int KITTY_GRAPHICS_PLACEMENT_DATA_IS_VIRTUAL = 3;
public static final int KITTY_GRAPHICS_PLACEMENT_DATA_X_OFFSET = 4;
public static final int KITTY_GRAPHICS_PLACEMENT_DATA_Y_OFFSET = 5;
public static final int KITTY_GRAPHICS_PLACEMENT_DATA_SOURCE_X = 6;
public static final int KITTY_GRAPHICS_PLACEMENT_DATA_SOURCE_Y = 7;
public static final int KITTY_GRAPHICS_PLACEMENT_DATA_SOURCE_WIDTH = 8;
public static final int KITTY_GRAPHICS_PLACEMENT_DATA_SOURCE_HEIGHT = 9;
public static final int KITTY_GRAPHICS_PLACEMENT_DATA_COLUMNS = 10;
public static final int KITTY_GRAPHICS_PLACEMENT_DATA_ROWS = 11;
public static final int KITTY_GRAPHICS_PLACEMENT_DATA_Z = 12;
public static final int KITTY_IMAGE_DATA_ID = 1;
public static final int KITTY_IMAGE_DATA_NUMBER = 2;
public static final int KITTY_IMAGE_DATA_WIDTH = 3;
public static final int KITTY_IMAGE_DATA_HEIGHT = 4;
public static final int KITTY_IMAGE_DATA_FORMAT = 5;
public static final int KITTY_IMAGE_DATA_COMPRESSION = 6;
public static final int KITTY_IMAGE_DATA_DATA_PTR = 7;
public static final int KITTY_IMAGE_DATA_DATA_LEN = 8;
private static final int GHOSTTY_SUCCESS = 0;
private static final int GHOSTTY_OUT_OF_SPACE = -3;
@@ -36,6 +66,7 @@ public final class GhosttyLibrary {
private static final ValueLayout.OfBoolean C_BOOL = (ValueLayout.OfBoolean) LINKER.canonicalLayouts().get("bool");
private static final ValueLayout.OfShort C_SHORT = (ValueLayout.OfShort) LINKER.canonicalLayouts().get("short");
private static final ValueLayout.OfInt C_INT = (ValueLayout.OfInt) LINKER.canonicalLayouts().get("int");
private static final ValueLayout.OfLong C_LONG_LONG = (ValueLayout.OfLong) LINKER.canonicalLayouts().get("long long");
private static final ValueLayout.OfLong C_SIZE_T = sizeTLayout();
private static final GroupLayout TERMINAL_OPTIONS = MemoryLayout.structLayout(
@@ -50,14 +81,41 @@ public final class GhosttyLibrary {
C_SIZE_T.withName("len")
);
private static final GroupLayout KITTY_RENDER_INFO = MemoryLayout.structLayout(
C_SIZE_T.withName("size"),
C_INT.withName("pixel_width"),
C_INT.withName("pixel_height"),
C_INT.withName("grid_cols"),
C_INT.withName("grid_rows"),
C_INT.withName("viewport_col"),
C_INT.withName("viewport_row"),
C_BOOL.withName("viewport_visible"),
MemoryLayout.paddingLayout(3),
C_INT.withName("source_x"),
C_INT.withName("source_y"),
C_INT.withName("source_width"),
C_INT.withName("source_height"),
MemoryLayout.paddingLayout(4)
);
private final MethodHandle terminalNew;
private final MethodHandle terminalFree;
private final MethodHandle terminalReset;
private final MethodHandle terminalResize;
private final MethodHandle terminalVtWrite;
private final MethodHandle terminalSet;
private final MethodHandle terminalGet;
private final MethodHandle pasteIsSafe;
private final MethodHandle pasteEncode;
private final MethodHandle kittyGraphicsGet;
private final MethodHandle kittyGraphicsImage;
private final MethodHandle kittyGraphicsImageGet;
private final MethodHandle kittyGraphicsPlacementIteratorNew;
private final MethodHandle kittyGraphicsPlacementIteratorFree;
private final MethodHandle kittyGraphicsPlacementIteratorSet;
private final MethodHandle kittyGraphicsPlacementNext;
private final MethodHandle kittyGraphicsPlacementGet;
private final MethodHandle kittyGraphicsPlacementRenderInfo;
private GhosttyLibrary(Path libraryPath) {
try {
@@ -73,12 +131,32 @@ public final class GhosttyLibrary {
FunctionDescriptor.of(C_INT, C_POINTER, C_SHORT, C_SHORT, C_INT, C_INT));
terminalVtWrite = downcall(symbols, "ghostty_terminal_vt_write",
FunctionDescriptor.ofVoid(C_POINTER, C_POINTER, C_SIZE_T));
terminalSet = downcall(symbols, "ghostty_terminal_set",
FunctionDescriptor.of(C_INT, C_POINTER, C_INT, C_POINTER));
terminalGet = downcall(symbols, "ghostty_terminal_get",
FunctionDescriptor.of(C_INT, C_POINTER, C_INT, C_POINTER));
pasteIsSafe = downcall(symbols, "ghostty_paste_is_safe",
FunctionDescriptor.of(C_BOOL, C_POINTER, C_SIZE_T));
pasteEncode = downcall(symbols, "ghostty_paste_encode",
FunctionDescriptor.of(C_INT, C_POINTER, C_SIZE_T, C_BOOL, C_POINTER, C_SIZE_T, C_POINTER));
kittyGraphicsGet = downcall(symbols, "ghostty_kitty_graphics_get",
FunctionDescriptor.of(C_INT, C_POINTER, C_INT, C_POINTER));
kittyGraphicsImage = downcall(symbols, "ghostty_kitty_graphics_image",
FunctionDescriptor.of(C_POINTER, C_POINTER, C_INT));
kittyGraphicsImageGet = downcall(symbols, "ghostty_kitty_graphics_image_get",
FunctionDescriptor.of(C_INT, C_POINTER, C_INT, C_POINTER));
kittyGraphicsPlacementIteratorNew = downcall(symbols, "ghostty_kitty_graphics_placement_iterator_new",
FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER));
kittyGraphicsPlacementIteratorFree = downcall(symbols, "ghostty_kitty_graphics_placement_iterator_free",
FunctionDescriptor.ofVoid(C_POINTER));
kittyGraphicsPlacementIteratorSet = downcall(symbols, "ghostty_kitty_graphics_placement_iterator_set",
FunctionDescriptor.of(C_INT, C_POINTER, C_INT, C_POINTER));
kittyGraphicsPlacementNext = downcall(symbols, "ghostty_kitty_graphics_placement_next",
FunctionDescriptor.of(C_BOOL, C_POINTER));
kittyGraphicsPlacementGet = downcall(symbols, "ghostty_kitty_graphics_placement_get",
FunctionDescriptor.of(C_INT, C_POINTER, C_INT, C_POINTER));
kittyGraphicsPlacementRenderInfo = downcall(symbols, "ghostty_kitty_graphics_placement_render_info",
FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER, C_POINTER, C_POINTER));
} catch (IllegalCallerException e) {
throw new IllegalStateException(
"FFM native access is disabled. Run with --enable-native-access=dev.jlibghostty "
@@ -159,6 +237,28 @@ public final class GhosttyLibrary {
}
}
public void terminalSetU64(MemorySegment terminal, int key, long value) {
try (Arena arena = Arena.ofConfined()) {
MemorySegment nativeValue = arena.allocate(C_LONG_LONG);
nativeValue.set(C_LONG_LONG, 0, value);
int result = (int) terminalSet.invoke(terminal, key, nativeValue);
checkResult("ghostty_terminal_set", result);
} catch (Throwable t) {
rethrow(t);
}
}
public void terminalSetBoolean(MemorySegment terminal, int key, boolean value) {
try (Arena arena = Arena.ofConfined()) {
MemorySegment nativeValue = arena.allocate(C_BOOL);
nativeValue.set(C_BOOL, 0, value);
int result = (int) terminalSet.invoke(terminal, key, nativeValue);
checkResult("ghostty_terminal_set", result);
} catch (Throwable t) {
rethrow(t);
}
}
public int terminalGetU16(MemorySegment terminal, int key) {
try (Arena arena = Arena.ofConfined()) {
MemorySegment out = arena.allocate(C_SHORT);
@@ -203,6 +303,20 @@ public final class GhosttyLibrary {
}
}
public MemorySegment terminalGetPointerOrNull(MemorySegment terminal, int key) {
try (Arena arena = Arena.ofConfined()) {
MemorySegment out = arena.allocate(C_POINTER);
int result = (int) terminalGet.invoke(terminal, key, out);
if (result == GHOSTTY_NO_VALUE) {
return MemorySegment.NULL;
}
checkResult("ghostty_terminal_get", result);
return out.get(C_POINTER, 0);
} catch (Throwable t) {
return rethrow(t);
}
}
public boolean pasteIsSafe(byte[] data) {
if (data.length == 0) {
return true;
@@ -261,6 +375,163 @@ public final class GhosttyLibrary {
}
}
public void kittyGraphicsPopulatePlacementIterator(MemorySegment graphics, MemorySegment iterator) {
try (Arena arena = Arena.ofConfined()) {
MemorySegment out = arena.allocate(C_POINTER);
out.set(C_POINTER, 0, iterator);
int result = (int) kittyGraphicsGet.invoke(graphics, KITTY_GRAPHICS_DATA_PLACEMENT_ITERATOR, out);
checkResult("ghostty_kitty_graphics_get", result);
} catch (Throwable t) {
rethrow(t);
}
}
public MemorySegment kittyGraphicsImage(MemorySegment graphics, long imageId) {
try {
return (MemorySegment) kittyGraphicsImage.invoke(graphics, (int) imageId);
} catch (Throwable t) {
return rethrow(t);
}
}
public long kittyImageGetU32(MemorySegment image, int key) {
return Integer.toUnsignedLong(kittyImageGetI32(image, key));
}
public int kittyImageGetI32(MemorySegment image, int key) {
try (Arena arena = Arena.ofConfined()) {
MemorySegment out = arena.allocate(C_INT);
int result = (int) kittyGraphicsImageGet.invoke(image, key, out);
checkResult("ghostty_kitty_graphics_image_get", result);
return out.get(C_INT, 0);
} catch (Throwable t) {
return rethrow(t);
}
}
public byte[] kittyImageData(MemorySegment image) {
try (Arena arena = Arena.ofConfined()) {
MemorySegment outPtr = arena.allocate(C_POINTER);
MemorySegment outLen = arena.allocate(C_SIZE_T);
int result = (int) kittyGraphicsImageGet.invoke(image, KITTY_IMAGE_DATA_DATA_PTR, outPtr);
checkResult("ghostty_kitty_graphics_image_get", result);
result = (int) kittyGraphicsImageGet.invoke(image, KITTY_IMAGE_DATA_DATA_LEN, outLen);
checkResult("ghostty_kitty_graphics_image_get", result);
MemorySegment ptr = outPtr.get(C_POINTER, 0);
long len = outLen.get(C_SIZE_T, 0);
if (ptr.address() == 0 || len == 0) {
return new byte[0];
}
return ptr.reinterpret(len).toArray(JAVA_BYTE);
} catch (Throwable t) {
return rethrow(t);
}
}
public MemorySegment kittyPlacementIteratorNew() {
try (Arena arena = Arena.ofConfined()) {
MemorySegment out = arena.allocate(C_POINTER);
int result = (int) kittyGraphicsPlacementIteratorNew.invoke(MemorySegment.NULL, out);
checkResult("ghostty_kitty_graphics_placement_iterator_new", result);
MemorySegment iterator = out.get(C_POINTER, 0);
if (iterator.address() == 0) {
throw new IllegalStateException("ghostty_kitty_graphics_placement_iterator_new returned null");
}
return iterator;
} catch (Throwable t) {
return rethrow(t);
}
}
public void kittyPlacementIteratorFree(MemorySegment iterator) {
try {
kittyGraphicsPlacementIteratorFree.invoke(iterator);
} catch (Throwable t) {
rethrow(t);
}
}
public void kittyPlacementIteratorSetLayer(MemorySegment iterator, int layer) {
try (Arena arena = Arena.ofConfined()) {
MemorySegment nativeLayer = arena.allocate(C_INT);
nativeLayer.set(C_INT, 0, layer);
int result = (int) kittyGraphicsPlacementIteratorSet.invoke(iterator, 0, nativeLayer);
checkResult("ghostty_kitty_graphics_placement_iterator_set", result);
} catch (Throwable t) {
rethrow(t);
}
}
public boolean kittyPlacementNext(MemorySegment iterator) {
try {
return (boolean) kittyGraphicsPlacementNext.invoke(iterator);
} catch (Throwable t) {
return rethrow(t);
}
}
public long kittyPlacementGetU32(MemorySegment iterator, int key) {
return Integer.toUnsignedLong(kittyPlacementGetI32(iterator, key));
}
public int kittyPlacementGetI32(MemorySegment iterator, int key) {
try (Arena arena = Arena.ofConfined()) {
MemorySegment out = arena.allocate(C_INT);
int result = (int) kittyGraphicsPlacementGet.invoke(iterator, key, out);
checkResult("ghostty_kitty_graphics_placement_get", result);
return out.get(C_INT, 0);
} catch (Throwable t) {
return rethrow(t);
}
}
public boolean kittyPlacementGetBoolean(MemorySegment iterator, int key) {
try (Arena arena = Arena.ofConfined()) {
MemorySegment out = arena.allocate(C_BOOL);
int result = (int) kittyGraphicsPlacementGet.invoke(iterator, key, out);
checkResult("ghostty_kitty_graphics_placement_get", result);
return out.get(C_BOOL, 0);
} catch (Throwable t) {
return rethrow(t);
}
}
public java.util.Optional<dev.jlibghostty.KittyRenderInfo> kittyPlacementRenderInfo(
MemorySegment iterator,
MemorySegment image,
MemorySegment terminal
) {
try (Arena arena = Arena.ofConfined()) {
MemorySegment out = arena.allocate(KITTY_RENDER_INFO);
out.set(C_SIZE_T, 0, KITTY_RENDER_INFO.byteSize());
int result = (int) kittyGraphicsPlacementRenderInfo.invoke(iterator, image, terminal, out);
if (result == GHOSTTY_NO_VALUE) {
return java.util.Optional.empty();
}
checkResult("ghostty_kitty_graphics_placement_render_info", result);
return java.util.Optional.of(new dev.jlibghostty.KittyRenderInfo(
Integer.toUnsignedLong(out.get(C_INT, 8)),
Integer.toUnsignedLong(out.get(C_INT, 12)),
Integer.toUnsignedLong(out.get(C_INT, 16)),
Integer.toUnsignedLong(out.get(C_INT, 20)),
out.get(C_INT, 24),
out.get(C_INT, 28),
out.get(C_BOOL, 32),
Integer.toUnsignedLong(out.get(C_INT, 36)),
Integer.toUnsignedLong(out.get(C_INT, 40)),
Integer.toUnsignedLong(out.get(C_INT, 44)),
Integer.toUnsignedLong(out.get(C_INT, 48))
));
} catch (Throwable t) {
return rethrow(t);
}
}
private static MethodHandle downcall(SymbolLookup symbols, String name, FunctionDescriptor descriptor) {
MemorySegment symbol = symbols.find(name)
.orElseThrow(() -> new UnsatisfiedLinkError("Missing libghostty-vt symbol: " + name));