Gregor Lohaus a03bc2ec48 graal conf
2026-05-27 14:11:20 +02:00
wip
2026-05-27 14:04:26 +02:00
2026-05-27 14:11:20 +02:00
wip
2026-05-27 14:04:26 +02:00
wip
2026-05-27 14:04:26 +02:00
wip
2026-05-27 14:04:26 +02:00
2026-05-27 14:11:20 +02:00
2026-05-27 14:11:20 +02:00
wip
2026-05-27 14:04:26 +02:00

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

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/<platform>/.

Gradle Consumer

After nix build, another Gradle project can consume the generated Maven repository:

repositories {
    maven {
        url = uri("/home/anon/Dev/jlibghostty/result/maven")
    }
}

dependencies {
    implementation("dev.jlibghostty:jlibghostty:0.1.0-SNAPSHOT")
}

tasks.withType<JavaExec>().configureEach {
    jvmArgs("--enable-native-access=ALL-UNNAMED")
}

GraalVM Native Image Consumer

jlibghostty ships GraalVM reachability metadata at:

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:

native-image --enable-native-access=ALL-UNNAMED ...

If your app uses the module path, prefer:

native-image --enable-native-access=dev.jlibghostty ...

For a Nix-built downstream project, the usual shape is:

{
  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:

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:

--enable-native-access=dev.jlibghostty

External Native Library

The library normally loads the bundled native libghostty-vt. To override it:

java -Djlibghostty.library.path=/path/to/libghostty-vt.so ...

or set:

export JLIBGHOSTTY_LIBRARY=/path/to/libghostty-vt.so

Example

try (Terminal terminal = Ghostty.open(TerminalOptions.of(80, 24))) {
    terminal.write("hello\r\n");
    System.out.println(terminal.snapshot());
}

Development Shell

nix develop

The shell provides Java, Gradle, and JLIBGHOSTTY_LIBRARY pointing at the Nix-built libghostty-vt.

Description
No description provided
Readme 303 KiB
Languages
Java 95.8%
Nix 4.2%