everything
This commit is contained in:
20
README.md
20
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
|
||||||
|
|
||||||
Kitty graphics storage can be enabled and inspected:
|
Kitty graphics storage can be enabled and inspected:
|
||||||
|
|||||||
16
src/main/java/dev/jlibghostty/FocusEvent.java
Normal file
16
src/main/java/dev/jlibghostty/FocusEvent.java
Normal file
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,6 +12,14 @@ public final class Ghostty {
|
|||||||
return Terminal.open(options);
|
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) {
|
public static boolean pasteIsSafe(String text) {
|
||||||
return pasteIsSafe(text.getBytes(StandardCharsets.UTF_8));
|
return pasteIsSafe(text.getBytes(StandardCharsets.UTF_8));
|
||||||
}
|
}
|
||||||
@@ -28,4 +36,28 @@ public final class Ghostty {
|
|||||||
public static byte[] encodePaste(byte[] data, boolean bracketed) {
|
public static byte[] encodePaste(byte[] data, boolean bracketed) {
|
||||||
return GhosttyLibrary.loadDefault().pasteEncode(data, 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
15
src/main/java/dev/jlibghostty/GhosttyBuildInfo.java
Normal file
15
src/main/java/dev/jlibghostty/GhosttyBuildInfo.java
Normal file
@@ -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
|
||||||
|
) {
|
||||||
|
}
|
||||||
165
src/main/java/dev/jlibghostty/GhosttyNative.java
Normal file
165
src/main/java/dev/jlibghostty/GhosttyNative.java
Normal file
@@ -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<String> symbolNames() {
|
||||||
|
return Symbols.ALL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Optional<MemorySegment> 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<String> 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"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
19
src/main/java/dev/jlibghostty/ModeReportState.java
Normal file
19
src/main/java/dev/jlibghostty/ModeReportState.java
Normal file
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/main/java/dev/jlibghostty/OptimizeMode.java
Normal file
23
src/main/java/dev/jlibghostty/OptimizeMode.java
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/main/java/dev/jlibghostty/SizeReportSize.java
Normal file
18
src/main/java/dev/jlibghostty/SizeReportSize.java
Normal file
@@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/main/java/dev/jlibghostty/SizeReportStyle.java
Normal file
18
src/main/java/dev/jlibghostty/SizeReportStyle.java
Normal file
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
package dev.jlibghostty.internal;
|
package dev.jlibghostty.internal;
|
||||||
|
|
||||||
|
import dev.jlibghostty.GhosttyBuildInfo;
|
||||||
import dev.jlibghostty.GhosttyException;
|
import dev.jlibghostty.GhosttyException;
|
||||||
|
import dev.jlibghostty.OptimizeMode;
|
||||||
|
import dev.jlibghostty.SizeReportSize;
|
||||||
import dev.jlibghostty.TerminalOptions;
|
import dev.jlibghostty.TerminalOptions;
|
||||||
|
|
||||||
import java.lang.foreign.Arena;
|
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_OUT_OF_SPACE = -3;
|
||||||
private static final int GHOSTTY_NO_VALUE = -4;
|
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 Linker LINKER = Linker.nativeLinker();
|
||||||
private static final AddressLayout C_POINTER = (AddressLayout) LINKER.canonicalLayouts().get("void*");
|
private static final AddressLayout C_POINTER = (AddressLayout) LINKER.canonicalLayouts().get("void*");
|
||||||
private static final ValueLayout.OfBoolean C_BOOL = (ValueLayout.OfBoolean) LINKER.canonicalLayouts().get("bool");
|
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")
|
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(
|
private static final GroupLayout KITTY_RENDER_INFO = MemoryLayout.structLayout(
|
||||||
C_SIZE_T.withName("size"),
|
C_SIZE_T.withName("size"),
|
||||||
C_INT.withName("pixel_width"),
|
C_INT.withName("pixel_width"),
|
||||||
@@ -107,6 +128,11 @@ public final class GhosttyLibrary {
|
|||||||
private final MethodHandle terminalGet;
|
private final MethodHandle terminalGet;
|
||||||
private final MethodHandle pasteIsSafe;
|
private final MethodHandle pasteIsSafe;
|
||||||
private final MethodHandle pasteEncode;
|
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 kittyGraphicsGet;
|
||||||
private final MethodHandle kittyGraphicsImage;
|
private final MethodHandle kittyGraphicsImage;
|
||||||
private final MethodHandle kittyGraphicsImageGet;
|
private final MethodHandle kittyGraphicsImageGet;
|
||||||
@@ -139,6 +165,16 @@ public final class GhosttyLibrary {
|
|||||||
FunctionDescriptor.of(C_BOOL, C_POINTER, C_SIZE_T));
|
FunctionDescriptor.of(C_BOOL, C_POINTER, C_SIZE_T));
|
||||||
pasteEncode = downcall(symbols, "ghostty_paste_encode",
|
pasteEncode = downcall(symbols, "ghostty_paste_encode",
|
||||||
FunctionDescriptor.of(C_INT, C_POINTER, C_SIZE_T, C_BOOL, C_POINTER, C_SIZE_T, C_POINTER));
|
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",
|
kittyGraphicsGet = downcall(symbols, "ghostty_kitty_graphics_get",
|
||||||
FunctionDescriptor.of(C_INT, C_POINTER, C_INT, C_POINTER));
|
FunctionDescriptor.of(C_INT, C_POINTER, C_INT, C_POINTER));
|
||||||
kittyGraphicsImage = downcall(symbols, "ghostty_kitty_graphics_image",
|
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) {
|
public void kittyGraphicsPopulatePlacementIterator(MemorySegment graphics, MemorySegment iterator) {
|
||||||
try (Arena arena = Arena.ofConfined()) {
|
try (Arena arena = Arena.ofConfined()) {
|
||||||
MemorySegment out = arena.allocate(C_POINTER);
|
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")
|
@SuppressWarnings("unchecked")
|
||||||
private static <T> T rethrow(Throwable t) {
|
private static <T> T rethrow(Throwable t) {
|
||||||
if (t instanceof RuntimeException runtimeException) {
|
if (t instanceof RuntimeException runtimeException) {
|
||||||
|
|||||||
@@ -72,6 +72,46 @@
|
|||||||
"void*"
|
"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*",
|
"returnType": "void*",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|||||||
@@ -14,6 +14,21 @@ public final class GhosttySmokeTest {
|
|||||||
if (!"hello".equals(Ghostty.encodePaste("hello", false))) {
|
if (!"hello".equals(Ghostty.encodePaste("hello", false))) {
|
||||||
throw new AssertionError("simple paste encoding changed unexpectedly");
|
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))) {
|
try (Terminal terminal = Ghostty.open(TerminalOptions.of(80, 24))) {
|
||||||
terminal.setKittyImageStorageLimit(1024 * 1024);
|
terminal.setKittyImageStorageLimit(1024 * 1024);
|
||||||
|
|||||||
Reference in New Issue
Block a user