From c077bcc6c03f3a63e9b324d81623be1a0f31dcf2 Mon Sep 17 00:00:00 2001 From: Gregor Lohaus Date: Wed, 27 May 2026 14:56:04 +0200 Subject: [PATCH] everything --- README.md | 20 +++ src/main/java/dev/jlibghostty/FocusEvent.java | 16 ++ src/main/java/dev/jlibghostty/Ghostty.java | 32 ++++ .../dev/jlibghostty/GhosttyBuildInfo.java | 15 ++ .../java/dev/jlibghostty/GhosttyNative.java | 165 +++++++++++++++++ .../java/dev/jlibghostty/ModeReportState.java | 19 ++ .../java/dev/jlibghostty/OptimizeMode.java | 23 +++ .../java/dev/jlibghostty/SizeReportSize.java | 18 ++ .../java/dev/jlibghostty/SizeReportStyle.java | 18 ++ .../jlibghostty/internal/GhosttyLibrary.java | 170 ++++++++++++++++++ .../jlibghostty/reachability-metadata.json | 40 +++++ .../dev/jlibghostty/GhosttySmokeTest.java | 15 ++ 12 files changed, 551 insertions(+) create mode 100644 src/main/java/dev/jlibghostty/FocusEvent.java create mode 100644 src/main/java/dev/jlibghostty/GhosttyBuildInfo.java create mode 100644 src/main/java/dev/jlibghostty/GhosttyNative.java create mode 100644 src/main/java/dev/jlibghostty/ModeReportState.java create mode 100644 src/main/java/dev/jlibghostty/OptimizeMode.java create mode 100644 src/main/java/dev/jlibghostty/SizeReportSize.java create mode 100644 src/main/java/dev/jlibghostty/SizeReportStyle.java diff --git a/README.md b/README.md index 4a21a8d..4f9ef6c 100644 --- a/README.md +++ b/README.md @@ -145,6 +145,26 @@ try (Terminal terminal = Ghostty.open(TerminalOptions.of(80, 24))) { } ``` +## Full Native Surface + +The public Java API covers the common terminal, paste, build-info, focus, mode-report, size-report, and Kitty graphics paths. For libghostty-vt APIs that do not yet have ergonomic Java wrappers, use `GhosttyNative`: + +```java +MethodHandle handle = GhosttyNative.downcall( + "ghostty_render_state_new", + FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.ADDRESS, ValueLayout.ADDRESS) +); +``` + +`GhosttyNative.symbolNames()` lists every exported symbol found in the current public headers. Some entries are target-specific, such as WASM helpers, so use `GhosttyNative.findSymbol(name)` when probing optional symbols. + +Build metadata and C struct layout JSON are exposed directly: + +```java +GhosttyBuildInfo info = Ghostty.buildInfo(); +String typeJson = Ghostty.typeJson(); +``` + ## Kitty Graphics Kitty graphics storage can be enabled and inspected: diff --git a/src/main/java/dev/jlibghostty/FocusEvent.java b/src/main/java/dev/jlibghostty/FocusEvent.java new file mode 100644 index 0000000..98911f5 --- /dev/null +++ b/src/main/java/dev/jlibghostty/FocusEvent.java @@ -0,0 +1,16 @@ +package dev.jlibghostty; + +public enum FocusEvent { + GAINED(0), + LOST(1); + + private final int nativeValue; + + FocusEvent(int nativeValue) { + this.nativeValue = nativeValue; + } + + int nativeValue() { + return nativeValue; + } +} diff --git a/src/main/java/dev/jlibghostty/Ghostty.java b/src/main/java/dev/jlibghostty/Ghostty.java index eee20b6..b0f1a54 100644 --- a/src/main/java/dev/jlibghostty/Ghostty.java +++ b/src/main/java/dev/jlibghostty/Ghostty.java @@ -12,6 +12,14 @@ public final class Ghostty { return Terminal.open(options); } + public static GhosttyBuildInfo buildInfo() { + return GhosttyLibrary.loadDefault().buildInfo(); + } + + public static String typeJson() { + return GhosttyLibrary.loadDefault().typeJson(); + } + public static boolean pasteIsSafe(String text) { return pasteIsSafe(text.getBytes(StandardCharsets.UTF_8)); } @@ -28,4 +36,28 @@ public final class Ghostty { public static byte[] encodePaste(byte[] data, boolean bracketed) { return GhosttyLibrary.loadDefault().pasteEncode(data, bracketed); } + + public static String encodeFocus(FocusEvent event) { + return new String(encodeFocusBytes(event), StandardCharsets.UTF_8); + } + + public static byte[] encodeFocusBytes(FocusEvent event) { + return GhosttyLibrary.loadDefault().focusEncode(event.nativeValue()); + } + + public static String encodeModeReport(int mode, ModeReportState state) { + return new String(encodeModeReportBytes(mode, state), StandardCharsets.UTF_8); + } + + public static byte[] encodeModeReportBytes(int mode, ModeReportState state) { + return GhosttyLibrary.loadDefault().modeReportEncode(mode, state.nativeValue()); + } + + public static String encodeSizeReport(SizeReportStyle style, SizeReportSize size) { + return new String(encodeSizeReportBytes(style, size), StandardCharsets.UTF_8); + } + + public static byte[] encodeSizeReportBytes(SizeReportStyle style, SizeReportSize size) { + return GhosttyLibrary.loadDefault().sizeReportEncode(style.nativeValue(), size); + } } diff --git a/src/main/java/dev/jlibghostty/GhosttyBuildInfo.java b/src/main/java/dev/jlibghostty/GhosttyBuildInfo.java new file mode 100644 index 0000000..d97aae5 --- /dev/null +++ b/src/main/java/dev/jlibghostty/GhosttyBuildInfo.java @@ -0,0 +1,15 @@ +package dev.jlibghostty; + +public record GhosttyBuildInfo( + boolean simd, + boolean kittyGraphics, + boolean tmuxControlMode, + OptimizeMode optimizeMode, + String version, + long versionMajor, + long versionMinor, + long versionPatch, + String versionPre, + String versionBuild +) { +} diff --git a/src/main/java/dev/jlibghostty/GhosttyNative.java b/src/main/java/dev/jlibghostty/GhosttyNative.java new file mode 100644 index 0000000..fea75d3 --- /dev/null +++ b/src/main/java/dev/jlibghostty/GhosttyNative.java @@ -0,0 +1,165 @@ +package dev.jlibghostty; + +import dev.jlibghostty.internal.NativeLibraryLoader; + +import java.lang.foreign.Arena; +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.Linker; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.SymbolLookup; +import java.lang.invoke.MethodHandle; +import java.util.List; +import java.util.Optional; + +public final class GhosttyNative { + private static final SymbolLookup SYMBOLS = SymbolLookup.libraryLookup( + NativeLibraryLoader.resolve(), + Arena.global() + ); + + private GhosttyNative() { + } + + public static List symbolNames() { + return Symbols.ALL; + } + + public static Optional findSymbol(String name) { + return SYMBOLS.find(name); + } + + public static MemorySegment requireSymbol(String name) { + return findSymbol(name) + .orElseThrow(() -> new UnsatisfiedLinkError("Missing libghostty-vt symbol: " + name)); + } + + public static MethodHandle downcall(String name, FunctionDescriptor descriptor) { + return Linker.nativeLinker().downcallHandle(requireSymbol(name), descriptor); + } + + private static final class Symbols { + private static final List ALL = List.of( + "ghostty_alloc", + "ghostty_build_info", + "ghostty_cell_get", + "ghostty_cell_get_multi", + "ghostty_color_rgb_get", + "ghostty_focus_encode", + "ghostty_formatter_format_alloc", + "ghostty_formatter_format_buf", + "ghostty_formatter_free", + "ghostty_formatter_terminal_new", + "ghostty_free", + "ghostty_grid_ref_cell", + "ghostty_grid_ref_graphemes", + "ghostty_grid_ref_hyperlink_uri", + "ghostty_grid_ref_row", + "ghostty_grid_ref_style", + "ghostty_kitty_graphics_get", + "ghostty_kitty_graphics_image", + "ghostty_kitty_graphics_image_get", + "ghostty_kitty_graphics_image_get_multi", + "ghostty_kitty_graphics_placement_get", + "ghostty_kitty_graphics_placement_get_multi", + "ghostty_kitty_graphics_placement_grid_size", + "ghostty_kitty_graphics_placement_iterator_free", + "ghostty_kitty_graphics_placement_iterator_new", + "ghostty_kitty_graphics_placement_iterator_set", + "ghostty_kitty_graphics_placement_next", + "ghostty_kitty_graphics_placement_pixel_size", + "ghostty_kitty_graphics_placement_rect", + "ghostty_kitty_graphics_placement_render_info", + "ghostty_kitty_graphics_placement_source_rect", + "ghostty_kitty_graphics_placement_viewport_pos", + "ghostty_mode_report_encode", + "ghostty_osc_command_data", + "ghostty_osc_command_type", + "ghostty_osc_end", + "ghostty_osc_free", + "ghostty_osc_new", + "ghostty_osc_next", + "ghostty_osc_reset", + "ghostty_paste_encode", + "ghostty_paste_is_safe", + "ghostty_render_state_colors_get", + "ghostty_render_state_free", + "ghostty_render_state_get", + "ghostty_render_state_get_multi", + "ghostty_render_state_new", + "ghostty_render_state_row_cells_free", + "ghostty_render_state_row_cells_get", + "ghostty_render_state_row_cells_get_multi", + "ghostty_render_state_row_cells_new", + "ghostty_render_state_row_cells_next", + "ghostty_render_state_row_cells_select", + "ghostty_render_state_row_get", + "ghostty_render_state_row_get_multi", + "ghostty_render_state_row_iterator_free", + "ghostty_render_state_row_iterator_new", + "ghostty_render_state_row_iterator_next", + "ghostty_render_state_row_set", + "ghostty_render_state_set", + "ghostty_render_state_update", + "ghostty_row_get", + "ghostty_row_get_multi", + "ghostty_sgr_attribute_tag", + "ghostty_sgr_attribute_value", + "ghostty_sgr_free", + "ghostty_sgr_new", + "ghostty_sgr_next", + "ghostty_sgr_reset", + "ghostty_sgr_set_params", + "ghostty_sgr_unknown_full", + "ghostty_sgr_unknown_partial", + "ghostty_size_report_encode", + "ghostty_style_default", + "ghostty_style_is_default", + "ghostty_sys_log_stderr", + "ghostty_sys_set", + "ghostty_terminal_free", + "ghostty_terminal_get", + "ghostty_terminal_get_multi", + "ghostty_terminal_grid_ref", + "ghostty_terminal_grid_ref_track", + "ghostty_terminal_mode_get", + "ghostty_terminal_mode_set", + "ghostty_terminal_new", + "ghostty_terminal_point_from_grid_ref", + "ghostty_terminal_reset", + "ghostty_terminal_resize", + "ghostty_terminal_scroll_viewport", + "ghostty_terminal_select_all", + "ghostty_terminal_select_line", + "ghostty_terminal_select_output", + "ghostty_terminal_select_word", + "ghostty_terminal_select_word_between", + "ghostty_terminal_selection_adjust", + "ghostty_terminal_selection_contains", + "ghostty_terminal_selection_equal", + "ghostty_terminal_selection_format_alloc", + "ghostty_terminal_selection_format_buf", + "ghostty_terminal_selection_order", + "ghostty_terminal_selection_ordered", + "ghostty_terminal_set", + "ghostty_terminal_vt_write", + "ghostty_tracked_grid_ref_free", + "ghostty_tracked_grid_ref_has_value", + "ghostty_tracked_grid_ref_point", + "ghostty_tracked_grid_ref_set", + "ghostty_tracked_grid_ref_snapshot", + "ghostty_type_json", + "ghostty_wasm_alloc_opaque", + "ghostty_wasm_alloc_sgr_attribute", + "ghostty_wasm_alloc_u16_array", + "ghostty_wasm_alloc_u8", + "ghostty_wasm_alloc_u8_array", + "ghostty_wasm_alloc_usize", + "ghostty_wasm_free_opaque", + "ghostty_wasm_free_sgr_attribute", + "ghostty_wasm_free_u16_array", + "ghostty_wasm_free_u8", + "ghostty_wasm_free_u8_array", + "ghostty_wasm_free_usize" + ); + } +} diff --git a/src/main/java/dev/jlibghostty/ModeReportState.java b/src/main/java/dev/jlibghostty/ModeReportState.java new file mode 100644 index 0000000..7682e41 --- /dev/null +++ b/src/main/java/dev/jlibghostty/ModeReportState.java @@ -0,0 +1,19 @@ +package dev.jlibghostty; + +public enum ModeReportState { + NOT_RECOGNIZED(0), + SET(1), + RESET(2), + PERMANENTLY_SET(3), + PERMANENTLY_RESET(4); + + private final int nativeValue; + + ModeReportState(int nativeValue) { + this.nativeValue = nativeValue; + } + + int nativeValue() { + return nativeValue; + } +} diff --git a/src/main/java/dev/jlibghostty/OptimizeMode.java b/src/main/java/dev/jlibghostty/OptimizeMode.java new file mode 100644 index 0000000..0fbe667 --- /dev/null +++ b/src/main/java/dev/jlibghostty/OptimizeMode.java @@ -0,0 +1,23 @@ +package dev.jlibghostty; + +public enum OptimizeMode { + DEBUG(0), + RELEASE_SAFE(1), + RELEASE_SMALL(2), + RELEASE_FAST(3); + + private final int nativeValue; + + OptimizeMode(int nativeValue) { + this.nativeValue = nativeValue; + } + + public static OptimizeMode fromNative(int value) { + for (OptimizeMode mode : values()) { + if (mode.nativeValue == value) { + return mode; + } + } + throw new IllegalArgumentException("Unknown Ghostty optimize mode: " + value); + } +} diff --git a/src/main/java/dev/jlibghostty/SizeReportSize.java b/src/main/java/dev/jlibghostty/SizeReportSize.java new file mode 100644 index 0000000..3dea05d --- /dev/null +++ b/src/main/java/dev/jlibghostty/SizeReportSize.java @@ -0,0 +1,18 @@ +package dev.jlibghostty; + +public record SizeReportSize(int rows, int columns, long cellWidth, long cellHeight) { + public SizeReportSize { + if (rows < 1 || rows > 65535) { + throw new IllegalArgumentException("rows must be between 1 and 65535"); + } + if (columns < 1 || columns > 65535) { + throw new IllegalArgumentException("columns must be between 1 and 65535"); + } + if (cellWidth < 0 || cellWidth > 0xffffffffL) { + throw new IllegalArgumentException("cellWidth must fit in uint32_t"); + } + if (cellHeight < 0 || cellHeight > 0xffffffffL) { + throw new IllegalArgumentException("cellHeight must fit in uint32_t"); + } + } +} diff --git a/src/main/java/dev/jlibghostty/SizeReportStyle.java b/src/main/java/dev/jlibghostty/SizeReportStyle.java new file mode 100644 index 0000000..1c6aae8 --- /dev/null +++ b/src/main/java/dev/jlibghostty/SizeReportStyle.java @@ -0,0 +1,18 @@ +package dev.jlibghostty; + +public enum SizeReportStyle { + MODE_2048(0), + CSI_14_T(1), + CSI_16_T(2), + CSI_18_T(3); + + private final int nativeValue; + + SizeReportStyle(int nativeValue) { + this.nativeValue = nativeValue; + } + + int nativeValue() { + return nativeValue; + } +} diff --git a/src/main/java/dev/jlibghostty/internal/GhosttyLibrary.java b/src/main/java/dev/jlibghostty/internal/GhosttyLibrary.java index fe4bd24..030761c 100644 --- a/src/main/java/dev/jlibghostty/internal/GhosttyLibrary.java +++ b/src/main/java/dev/jlibghostty/internal/GhosttyLibrary.java @@ -1,6 +1,9 @@ package dev.jlibghostty.internal; +import dev.jlibghostty.GhosttyBuildInfo; import dev.jlibghostty.GhosttyException; +import dev.jlibghostty.OptimizeMode; +import dev.jlibghostty.SizeReportSize; import dev.jlibghostty.TerminalOptions; import java.lang.foreign.Arena; @@ -61,6 +64,17 @@ public final class GhosttyLibrary { private static final int GHOSTTY_OUT_OF_SPACE = -3; private static final int GHOSTTY_NO_VALUE = -4; + private static final int BUILD_INFO_SIMD = 1; + private static final int BUILD_INFO_KITTY_GRAPHICS = 2; + private static final int BUILD_INFO_TMUX_CONTROL_MODE = 3; + private static final int BUILD_INFO_OPTIMIZE = 4; + private static final int BUILD_INFO_VERSION_STRING = 5; + private static final int BUILD_INFO_VERSION_MAJOR = 6; + private static final int BUILD_INFO_VERSION_MINOR = 7; + private static final int BUILD_INFO_VERSION_PATCH = 8; + private static final int BUILD_INFO_VERSION_PRE = 9; + private static final int BUILD_INFO_VERSION_BUILD = 10; + private static final Linker LINKER = Linker.nativeLinker(); private static final AddressLayout C_POINTER = (AddressLayout) LINKER.canonicalLayouts().get("void*"); private static final ValueLayout.OfBoolean C_BOOL = (ValueLayout.OfBoolean) LINKER.canonicalLayouts().get("bool"); @@ -81,6 +95,13 @@ public final class GhosttyLibrary { C_SIZE_T.withName("len") ); + private static final GroupLayout SIZE_REPORT_SIZE = MemoryLayout.structLayout( + C_SHORT.withName("rows"), + C_SHORT.withName("columns"), + C_INT.withName("cell_width"), + C_INT.withName("cell_height") + ); + private static final GroupLayout KITTY_RENDER_INFO = MemoryLayout.structLayout( C_SIZE_T.withName("size"), C_INT.withName("pixel_width"), @@ -107,6 +128,11 @@ public final class GhosttyLibrary { private final MethodHandle terminalGet; private final MethodHandle pasteIsSafe; private final MethodHandle pasteEncode; + private final MethodHandle buildInfo; + private final MethodHandle typeJson; + private final MethodHandle focusEncode; + private final MethodHandle modeReportEncode; + private final MethodHandle sizeReportEncode; private final MethodHandle kittyGraphicsGet; private final MethodHandle kittyGraphicsImage; private final MethodHandle kittyGraphicsImageGet; @@ -139,6 +165,16 @@ public final class GhosttyLibrary { 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)); + buildInfo = downcall(symbols, "ghostty_build_info", + FunctionDescriptor.of(C_INT, C_INT, C_POINTER)); + typeJson = downcall(symbols, "ghostty_type_json", + FunctionDescriptor.of(C_POINTER)); + focusEncode = downcall(symbols, "ghostty_focus_encode", + FunctionDescriptor.of(C_INT, C_INT, C_POINTER, C_SIZE_T, C_POINTER)); + modeReportEncode = downcall(symbols, "ghostty_mode_report_encode", + FunctionDescriptor.of(C_INT, C_INT, C_INT, C_POINTER, C_SIZE_T, C_POINTER)); + sizeReportEncode = downcall(symbols, "ghostty_size_report_encode", + FunctionDescriptor.of(C_INT, C_INT, SIZE_REPORT_SIZE, 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", @@ -375,6 +411,56 @@ public final class GhosttyLibrary { } } + public GhosttyBuildInfo buildInfo() { + return new GhosttyBuildInfo( + buildInfoBoolean(BUILD_INFO_SIMD), + buildInfoBoolean(BUILD_INFO_KITTY_GRAPHICS), + buildInfoBoolean(BUILD_INFO_TMUX_CONTROL_MODE), + OptimizeMode.fromNative(buildInfoInt(BUILD_INFO_OPTIMIZE)), + buildInfoString(BUILD_INFO_VERSION_STRING), + buildInfoSizeT(BUILD_INFO_VERSION_MAJOR), + buildInfoSizeT(BUILD_INFO_VERSION_MINOR), + buildInfoSizeT(BUILD_INFO_VERSION_PATCH), + buildInfoString(BUILD_INFO_VERSION_PRE), + buildInfoString(BUILD_INFO_VERSION_BUILD) + ); + } + + public String typeJson() { + try { + MemorySegment ptr = (MemorySegment) typeJson.invoke(); + if (ptr.address() == 0) { + return ""; + } + return ptr.reinterpret(16L * 1024L * 1024L).getString(0); + } catch (Throwable t) { + return rethrow(t); + } + } + + public byte[] focusEncode(int event) { + return encodeBuffer("ghostty_focus_encode", (arena, out, outLen, outWritten) -> + (int) focusEncode.invoke(event, out, outLen, outWritten) + ); + } + + public byte[] modeReportEncode(int mode, int state) { + return encodeBuffer("ghostty_mode_report_encode", (arena, out, outLen, outWritten) -> + (int) modeReportEncode.invoke(mode, state, out, outLen, outWritten) + ); + } + + public byte[] sizeReportEncode(int style, SizeReportSize size) { + return encodeBuffer("ghostty_size_report_encode", (arena, out, outLen, outWritten) -> { + MemorySegment nativeSize = arena.allocate(SIZE_REPORT_SIZE); + nativeSize.set(C_SHORT, 0, (short) size.rows()); + nativeSize.set(C_SHORT, 2, (short) size.columns()); + nativeSize.set(C_INT, 4, (int) size.cellWidth()); + nativeSize.set(C_INT, 8, (int) size.cellHeight()); + return (int) sizeReportEncode.invoke(style, nativeSize, out, outLen, outWritten); + }); + } + public void kittyGraphicsPopulatePlacementIterator(MemorySegment graphics, MemorySegment iterator) { try (Arena arena = Arena.ofConfined()) { MemorySegment out = arena.allocate(C_POINTER); @@ -544,6 +630,90 @@ public final class GhosttyLibrary { } } + private boolean buildInfoBoolean(int key) { + try (Arena arena = Arena.ofConfined()) { + MemorySegment out = arena.allocate(C_BOOL); + int result = (int) buildInfo.invoke(key, out); + checkResult("ghostty_build_info", result); + return out.get(C_BOOL, 0); + } catch (Throwable t) { + return rethrow(t); + } + } + + private int buildInfoInt(int key) { + try (Arena arena = Arena.ofConfined()) { + MemorySegment out = arena.allocate(C_INT); + int result = (int) buildInfo.invoke(key, out); + checkResult("ghostty_build_info", result); + return out.get(C_INT, 0); + } catch (Throwable t) { + return rethrow(t); + } + } + + private long buildInfoSizeT(int key) { + try (Arena arena = Arena.ofConfined()) { + MemorySegment out = arena.allocate(C_SIZE_T); + int result = (int) buildInfo.invoke(key, out); + checkResult("ghostty_build_info", result); + return out.get(C_SIZE_T, 0); + } catch (Throwable t) { + return rethrow(t); + } + } + + private String buildInfoString(int key) { + try (Arena arena = Arena.ofConfined()) { + MemorySegment out = arena.allocate(GHOSTTY_STRING); + int result = (int) buildInfo.invoke(key, out); + checkResult("ghostty_build_info", result); + return readGhosttyString(out); + } catch (Throwable t) { + return rethrow(t); + } + } + + private String readGhosttyString(MemorySegment string) { + MemorySegment ptr = string.get(C_POINTER, 0); + long len = string.get(C_SIZE_T, 8); + if (ptr.address() == 0 || len == 0) { + return ""; + } + + byte[] bytes = ptr.reinterpret(len).toArray(JAVA_BYTE); + return new String(bytes, StandardCharsets.UTF_8); + } + + private byte[] encodeBuffer(String operation, Encoder encoder) { + try (Arena arena = Arena.ofConfined()) { + MemorySegment outWritten = arena.allocate(C_SIZE_T); + int result = encoder.invoke(arena, MemorySegment.NULL, 0L, outWritten); + if (result != GHOSTTY_SUCCESS && result != GHOSTTY_OUT_OF_SPACE) { + checkResult(operation, result); + } + + long required = outWritten.get(C_SIZE_T, 0); + if (required == 0) { + return new byte[0]; + } + + MemorySegment out = arena.allocate(required, 1); + result = encoder.invoke(arena, out, required, outWritten); + checkResult(operation, result); + + long written = outWritten.get(C_SIZE_T, 0); + return out.asSlice(0, written).toArray(JAVA_BYTE); + } catch (Throwable t) { + return rethrow(t); + } + } + + @FunctionalInterface + private interface Encoder { + int invoke(Arena arena, MemorySegment out, long outLen, MemorySegment outWritten) throws Throwable; + } + @SuppressWarnings("unchecked") private static T rethrow(Throwable t) { if (t instanceof RuntimeException runtimeException) { diff --git a/src/main/resources/META-INF/native-image/dev.jlibghostty/jlibghostty/reachability-metadata.json b/src/main/resources/META-INF/native-image/dev.jlibghostty/jlibghostty/reachability-metadata.json index c02d604..e1c8337 100644 --- a/src/main/resources/META-INF/native-image/dev.jlibghostty/jlibghostty/reachability-metadata.json +++ b/src/main/resources/META-INF/native-image/dev.jlibghostty/jlibghostty/reachability-metadata.json @@ -72,6 +72,46 @@ "void*" ] }, + { + "returnType": "int", + "parameterTypes": [ + "int", + "void*" + ] + }, + { + "returnType": "void*", + "parameterTypes": [] + }, + { + "returnType": "int", + "parameterTypes": [ + "int", + "void*", + "size_t", + "void*" + ] + }, + { + "returnType": "int", + "parameterTypes": [ + "int", + "int", + "void*", + "size_t", + "void*" + ] + }, + { + "returnType": "int", + "parameterTypes": [ + "int", + "struct(short, short, int, int)", + "void*", + "size_t", + "void*" + ] + }, { "returnType": "void*", "parameterTypes": [ diff --git a/src/test/java/dev/jlibghostty/GhosttySmokeTest.java b/src/test/java/dev/jlibghostty/GhosttySmokeTest.java index 39138df..e313e13 100644 --- a/src/test/java/dev/jlibghostty/GhosttySmokeTest.java +++ b/src/test/java/dev/jlibghostty/GhosttySmokeTest.java @@ -14,6 +14,21 @@ public final class GhosttySmokeTest { if (!"hello".equals(Ghostty.encodePaste("hello", false))) { throw new AssertionError("simple paste encoding changed unexpectedly"); } + if (Ghostty.buildInfo().version().isEmpty()) { + throw new AssertionError("build info version should not be empty"); + } + if (Ghostty.typeJson().isEmpty()) { + throw new AssertionError("type JSON should not be empty"); + } + if (Ghostty.encodeFocus(FocusEvent.GAINED).isEmpty()) { + throw new AssertionError("focus encoding should not be empty"); + } + if (Ghostty.encodeModeReport(25, ModeReportState.SET).isEmpty()) { + throw new AssertionError("mode report encoding should not be empty"); + } + if (Ghostty.encodeSizeReport(SizeReportStyle.CSI_18_T, new SizeReportSize(24, 80, 8, 16)).isEmpty()) { + throw new AssertionError("size report encoding should not be empty"); + } try (Terminal terminal = Ghostty.open(TerminalOptions.of(80, 24))) { terminal.setKittyImageStorageLimit(1024 * 1024);