# jlibghostty Java FFM bindings for Ghostty's `libghostty-vt`. This targets Java 22+ and uses `java.lang.foreign`, not JNI. The public API is intentionally small while Ghostty's C API is still marked unstable upstream. ## Build ```sh nix build ``` The default Nix package builds: - `share/java/jlibghostty-0.1.0-SNAPSHOT.jar` - `maven/dev/jlibghostty/jlibghostty/0.1.0-SNAPSHOT/...` The jar contains the host platform `libghostty-vt` under `dev/jlibghostty/native//`. ## Gradle Consumer After `nix build`, another Gradle project can consume the generated Maven repository: ```kotlin repositories { maven { url = uri("/home/anon/Dev/jlibghostty/result/maven") } } dependencies { implementation("dev.jlibghostty:jlibghostty:0.1.0-SNAPSHOT") } tasks.withType().configureEach { jvmArgs("--enable-native-access=ALL-UNNAMED") } ``` ## GraalVM Native Image Consumer `jlibghostty` ships GraalVM reachability metadata at: ```text META-INF/native-image/dev.jlibghostty/jlibghostty/reachability-metadata.json ``` That metadata registers the FFM downcalls used by this library and includes the bundled native library resource. GraalVM 25 enables Native Image FFM support by default, but the build still needs native access: ```sh native-image --enable-native-access=ALL-UNNAMED ... ``` If your app uses the module path, prefer: ```sh native-image --enable-native-access=dev.jlibghostty ... ``` For a Nix-built downstream project, the usual shape is: ```nix { inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; jlibghostty.url = "path:/home/anon/Dev/jlibghostty"; }; outputs = { nixpkgs, jlibghostty, ... }: let system = "x86_64-linux"; pkgs = import nixpkgs { inherit system; }; jlib = jlibghostty.packages.${system}.jlibghostty; in { packages.${system}.default = pkgs.stdenvNoCC.mkDerivation { pname = "my-native-app"; version = "0.1.0"; src = ./.; nativeBuildInputs = [ pkgs.graalvmPackages.graalvm-ce pkgs.gradle ]; buildPhase = '' export GRADLE_USER_HOME=$TMPDIR/gradle gradle --offline -PjlibghosttyMavenRepo=${jlib}/maven installDist native-image \ --enable-native-access=ALL-UNNAMED \ -cp build/install/my-app/lib/'*' \ -o my-app \ com.example.Main ''; installPhase = '' mkdir -p "$out/bin" cp my-app "$out/bin/" ''; }; }; } ``` In that downstream Gradle build: ```kotlin repositories { mavenCentral() maven { url = uri(providers.gradleProperty("jlibghosttyMavenRepo").get()) } } dependencies { implementation("dev.jlibghostty:jlibghostty:0.1.0-SNAPSHOT") } ``` If the app runs on the module path, use: ```sh --enable-native-access=dev.jlibghostty ``` ## External Native Library The library normally loads the bundled native `libghostty-vt`. To override it: ```sh java -Djlibghostty.library.path=/path/to/libghostty-vt.so ... ``` or set: ```sh export JLIBGHOSTTY_LIBRARY=/path/to/libghostty-vt.so ``` ## Example ```java try (Terminal terminal = Ghostty.open(TerminalOptions.of(80, 24))) { terminal.write("hello\r\n"); System.out.println(terminal.snapshot()); } ``` ## 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: ```java try (Terminal terminal = Ghostty.open(TerminalOptions.of(80, 24))) { terminal.setKittyImageStorageLimit(64 * 1024 * 1024); terminal.setKittyImageMediumFile(true); terminal.setKittyImageMediumTemporaryFile(true); terminal.setKittyImageMediumSharedMemory(true); terminal.write(kittyGraphicsSequenceBytes); for (KittyPlacement placement : terminal.kittyGraphics().orElseThrow().placements()) { placement.image().ifPresent(image -> { // Hand image.data() and placement.renderInfo() to your renderer. }); } } ``` The Kitty handles returned by `libghostty-vt` are borrowed from the terminal and are invalidated by mutating terminal calls. The Java API returns snapshots for images and placements to make renderer handoff simpler. PNG decode callbacks from `ghostty_sys_set(GHOSTTY_SYS_OPT_DECODE_PNG, ...)` are not exposed yet. Raw Kitty image formats can be inspected; PNG image ingestion will need a Java callback bridge or a small native helper that allocates decoded RGBA data through Ghostty's allocator. ## Development Shell ```sh nix develop ``` The shell provides Java, Gradle, and `JLIBGHOSTTY_LIBRARY` pointing at the Nix-built `libghostty-vt`.