diff --git a/.gradle/9.4.1/checksums/checksums.lock b/.gradle/9.4.1/checksums/checksums.lock index c0e6cae..b61369b 100644 Binary files a/.gradle/9.4.1/checksums/checksums.lock and b/.gradle/9.4.1/checksums/checksums.lock differ diff --git a/.gradle/9.4.1/executionHistory/executionHistory.lock b/.gradle/9.4.1/executionHistory/executionHistory.lock index ffbd1cb..8f8bb0c 100644 Binary files a/.gradle/9.4.1/executionHistory/executionHistory.lock and b/.gradle/9.4.1/executionHistory/executionHistory.lock differ diff --git a/.gradle/9.4.1/fileHashes/fileHashes.lock b/.gradle/9.4.1/fileHashes/fileHashes.lock index fc51bc1..ff40eff 100644 Binary files a/.gradle/9.4.1/fileHashes/fileHashes.lock and b/.gradle/9.4.1/fileHashes/fileHashes.lock differ diff --git a/.gradle/9.5.1/checksums/checksums.lock b/.gradle/9.5.1/checksums/checksums.lock new file mode 100644 index 0000000..5089492 Binary files /dev/null and b/.gradle/9.5.1/checksums/checksums.lock differ diff --git a/.gradle/9.5.1/checksums/md5-checksums.bin b/.gradle/9.5.1/checksums/md5-checksums.bin new file mode 100644 index 0000000..4b8f9c0 Binary files /dev/null and b/.gradle/9.5.1/checksums/md5-checksums.bin differ diff --git a/.gradle/9.5.1/checksums/sha1-checksums.bin b/.gradle/9.5.1/checksums/sha1-checksums.bin new file mode 100644 index 0000000..94928ab Binary files /dev/null and b/.gradle/9.5.1/checksums/sha1-checksums.bin differ diff --git a/.gradle/9.5.1/executionHistory/executionHistory.bin b/.gradle/9.5.1/executionHistory/executionHistory.bin new file mode 100644 index 0000000..e93a0d2 Binary files /dev/null and b/.gradle/9.5.1/executionHistory/executionHistory.bin differ diff --git a/.gradle/9.5.1/executionHistory/executionHistory.lock b/.gradle/9.5.1/executionHistory/executionHistory.lock new file mode 100644 index 0000000..0bdfa81 Binary files /dev/null and b/.gradle/9.5.1/executionHistory/executionHistory.lock differ diff --git a/.gradle/9.5.1/expanded/expanded.lock b/.gradle/9.5.1/expanded/expanded.lock new file mode 100644 index 0000000..7d69f7c Binary files /dev/null and b/.gradle/9.5.1/expanded/expanded.lock differ diff --git a/.gradle/9.5.1/fileChanges/last-build.bin b/.gradle/9.5.1/fileChanges/last-build.bin new file mode 100644 index 0000000..f76dd23 Binary files /dev/null and b/.gradle/9.5.1/fileChanges/last-build.bin differ diff --git a/.gradle/9.5.1/fileHashes/fileHashes.bin b/.gradle/9.5.1/fileHashes/fileHashes.bin new file mode 100644 index 0000000..4ffbe7a Binary files /dev/null and b/.gradle/9.5.1/fileHashes/fileHashes.bin differ diff --git a/.gradle/9.5.1/fileHashes/fileHashes.lock b/.gradle/9.5.1/fileHashes/fileHashes.lock new file mode 100644 index 0000000..792cfb6 Binary files /dev/null and b/.gradle/9.5.1/fileHashes/fileHashes.lock differ diff --git a/.gradle/9.5.1/fileHashes/resourceHashesCache.bin b/.gradle/9.5.1/fileHashes/resourceHashesCache.bin new file mode 100644 index 0000000..34191da Binary files /dev/null and b/.gradle/9.5.1/fileHashes/resourceHashesCache.bin differ diff --git a/.gradle/9.5.1/gc.properties b/.gradle/9.5.1/gc.properties new file mode 100644 index 0000000..e69de29 diff --git a/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/.gradle/buildOutputCleanup/buildOutputCleanup.lock index f41612d..b94b98c 100644 Binary files a/.gradle/buildOutputCleanup/buildOutputCleanup.lock and b/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ diff --git a/.gradle/buildOutputCleanup/cache.properties b/.gradle/buildOutputCleanup/cache.properties index 8ef438f..d0dfc83 100644 --- a/.gradle/buildOutputCleanup/cache.properties +++ b/.gradle/buildOutputCleanup/cache.properties @@ -1,2 +1,2 @@ -#Wed May 27 23:44:22 CEST 2026 -gradle.version=9.4.1 +#Thu May 28 14:41:41 CEST 2026 +gradle.version=9.5.1 diff --git a/.gradle/buildOutputCleanup/outputFiles.bin b/.gradle/buildOutputCleanup/outputFiles.bin index f25149a..0dc5541 100644 Binary files a/.gradle/buildOutputCleanup/outputFiles.bin and b/.gradle/buildOutputCleanup/outputFiles.bin differ diff --git a/.gradle/file-system.probe b/.gradle/file-system.probe index 676e00e..c0b9d47 100644 Binary files a/.gradle/file-system.probe and b/.gradle/file-system.probe differ diff --git a/README.md b/README.md index 0fb3853..4baf7e1 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,9 @@ enabled = true [scrollback] editor_command = "vi {file}" +[env.override] +ZELLIJ_SESSION_NAME = "" + [keybindings] navigate_left = "ALT+H" navigate_down = "ALT+J" diff --git a/build.gradle b/build.gradle index 3635554..4d32a46 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,8 @@ plugins { id 'application' id 'org.openjfx.javafxplugin' version '0.1.0' + // id 'org.graalvm.buildtools.native' version '1.1.1' + id 'com.gluonhq.gluonfx-gradle-plugin' version '1.0.28' } repositories { diff --git a/config.example.toml b/config.example.toml index 8e172df..ddd7b04 100644 --- a/config.example.toml +++ b/config.example.toml @@ -16,6 +16,9 @@ enabled = true [scrollback] editor_command = "vi {file}" +[env.override] +ZELLIJ_SESSION_NAME = "" + [keybindings] navigate_left = "ALT+H" navigate_down = "ALT+J" diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..947a442 --- /dev/null +++ b/flake.nix @@ -0,0 +1,161 @@ +{ + description = "jprototerm"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + jlibghostty.url = "git+https://gitea.gregorlohaus.com/gregor/jlibghostty.git"; + }; + + outputs = { self, nixpkgs, jlibghostty }: + let + supportedSystems = [ "x86_64-linux" ]; + forAllSystems = nixpkgs.lib.genAttrs supportedSystems; + in { + packages = forAllSystems (system: + let + pkgs = import nixpkgs { inherit system; }; + + jlib = jlibghostty.packages.${system}.jlibghostty; + + gluonGraalvm = pkgs.stdenvNoCC.mkDerivation { + pname = "graalvm-java23-gluon"; + version = "23+25.1-dev-2409082136"; + + src = pkgs.fetchurl { + url = "https://github.com/gluonhq/graal/releases/download/gluon-23%2B25.1-dev-2409082136/graalvm-java23-linux-amd64-gluon-23+25.1-dev.tar.gz"; + hash = "sha256-/NyMutn3pT4ZKL2pkzPdBZghxg0ERK5VJ2bFQF0VBfU="; + }; + + installPhase = '' + runHook preInstall + + mkdir -p "$out" + cp -R . "$out/" + + # The GluonFX Gradle plugin expects these two static libraries + # directly under linux-amd64, but this tarball keeps them one + # level deeper under linux-amd64/glibc. + ln -sfn ./glibc/libjvm.a "$out/lib/svm/clibraries/linux-amd64/libjvm.a" + ln -sfn ./glibc/liblibchelper.a "$out/lib/svm/clibraries/linux-amd64/liblibchelper.a" + + runHook postInstall + ''; + }; + + runtimeLibs = [ + pkgs.libglvnd + pkgs.glib + pkgs.gtk3 + pkgs.pango + pkgs.alsa-lib + pkgs.ffmpeg.dev + pkgs.ffmpeg.lib + pkgs.freetype + pkgs.libx11 + pkgs.libx11.dev + pkgs.libxext + pkgs.libxrender + pkgs.libxtst + pkgs.libxtst.dev + pkgs.libxi + pkgs.libxcursor + pkgs.libxrandr + pkgs.libxinerama + pkgs.libxcb + pkgs.libxxf86vm + pkgs.zlib + pkgs.zlib.dev + ]; + in { + default = pkgs.stdenv.mkDerivation { + pname = "jprototerm"; + version = "0.1.0"; + src = ./.; + + nativeBuildInputs = [ + gluonGraalvm + pkgs.gradle_9 + pkgs.makeWrapper + pkgs.pkg-config + ]; + + buildInputs = runtimeLibs; + + buildPhase = '' + runHook preBuild + + export HOME="$TMPDIR/home" + export GRADLE_USER_HOME="$TMPDIR/gradle" + export GRAALVM_HOME="${gluonGraalvm}" + export JAVA_HOME="${gluonGraalvm}" + export JLIBGHOSTTY_MAVEN_REPO="${jlib}/maven" + export LD_LIBRARY_PATH="${pkgs.lib.makeLibraryPath runtimeLibs}:$LD_LIBRARY_PATH" + + gradle --no-daemon --no-build-cache nativeExecutable + + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + + mkdir -p "$out/bin" + binary="$(find build/gluonfx -type f -perm -0100 -name jprototerm | head -n1)" + if [ -z "$binary" ]; then + echo "Could not find native jprototerm binary under build/gluonfx" >&2 + find build/gluonfx -type f -perm -0100 >&2 || true + exit 1 + fi + + cp "$binary" "$out/bin/jprototerm" + wrapProgram "$out/bin/jprototerm" \ + --prefix LD_LIBRARY_PATH : "${pkgs.lib.makeLibraryPath runtimeLibs}" \ + --set GDK_BACKEND x11 + + runHook postInstall + ''; + }; + }); + + devShells = forAllSystems (system: + let + pkgs = import nixpkgs { inherit system; }; + jlib = jlibghostty.packages.${system}.jlibghostty; + runtimeLibs = [ + pkgs.libglvnd + pkgs.glib + pkgs.gtk3 + pkgs.pango + pkgs.alsa-lib + pkgs.ffmpeg.dev + pkgs.ffmpeg.lib + pkgs.freetype + pkgs.libx11 + pkgs.libx11.dev + pkgs.libxext + pkgs.libxrender + pkgs.libxtst + pkgs.libxtst.dev + pkgs.libxi + pkgs.libxcursor + pkgs.libxrandr + pkgs.libxinerama + pkgs.libxcb + pkgs.libxxf86vm + pkgs.zlib + pkgs.zlib.dev + ]; + in { + default = pkgs.mkShell { + packages = [ + pkgs.gradle_9 + pkgs.jdk23 + pkgs.jdt-language-server + ] ++ runtimeLibs; + + JLIBGHOSTTY_MAVEN_REPO = "${jlib}/maven"; + LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath runtimeLibs; + }; + }); + }; +} diff --git a/src/main/java/com/gregor/jprototerm/AppConfig.java b/src/main/java/com/gregor/jprototerm/AppConfig.java index 745a494..1e4a580 100644 --- a/src/main/java/com/gregor/jprototerm/AppConfig.java +++ b/src/main/java/com/gregor/jprototerm/AppConfig.java @@ -3,6 +3,7 @@ package com.gregor.jprototerm; import io.github.wasabithumb.jtoml.JToml; import io.github.wasabithumb.jtoml.document.TomlDocument; import io.github.wasabithumb.jtoml.except.TomlException; +import io.github.wasabithumb.jtoml.key.TomlKey; import io.github.wasabithumb.jtoml.value.TomlValue; import io.github.wasabithumb.jtoml.value.primitive.TomlPrimitive; import io.github.wasabithumb.jtoml.value.table.TomlTable; @@ -26,6 +27,7 @@ public record AppConfig( double windowHeight, boolean kittyGraphics, String scrollbackEditorCommand, + Map envOverride, Map keybindings ) { private static final List KEYBINDING_KEYS = List.of( @@ -62,6 +64,7 @@ public record AppConfig( doubleValue(document, "window.height", defaults.windowHeight), booleanValue(document, "kitty_graphics.enabled", defaults.kittyGraphics), stringValue(document, "scrollback.editor_command", defaults.scrollbackEditorCommand), + envOverride(document, defaults.envOverride), keybindings(document, defaults) ); } catch (TomlException ex) { @@ -82,6 +85,7 @@ public record AppConfig( 760.0, true, defaultScrollbackEditorCommand(), + Map.of(), Map.of( "navigate_left", KeyBinding.parse("ALT+H"), "navigate_down", KeyBinding.parse("ALT+J"), @@ -109,6 +113,7 @@ public record AppConfig( windowHeight, kittyGraphics, scrollbackEditorCommand, + envOverride, keybindings ); } @@ -183,6 +188,11 @@ public record AppConfig( builder.append("enabled = ").append(kittyGraphics).append("\n\n"); builder.append("[scrollback]\n"); builder.append("editor_command = ").append(quoted(scrollbackEditorCommand)).append("\n\n"); + builder.append("[env.override]\n"); + for (Map.Entry entry : envOverride.entrySet()) { + builder.append(entry.getKey()).append(" = ").append(quoted(entry.getValue())).append('\n'); + } + builder.append('\n'); builder.append("[keybindings]\n"); for (String key : KEYBINDING_KEYS) { KeyBinding binding = keybindings.get(key); @@ -222,6 +232,31 @@ public record AppConfig( } } + private static Map envOverride(TomlTable table, Map fallback) { + TomlValue value = table.get("env.override"); + if (value == null || !value.isTable()) { + return fallback; + } + + Map result = new LinkedHashMap<>(); + TomlTable overrides = value.asTable(); + for (TomlKey key : overrides.keys(false)) { + if (key.size() != 1) { + continue; + } + + TomlValue override = overrides.get(key); + if (override != null && override.isPrimitive()) { + try { + result.put(key.get(0), override.asPrimitive().asString()); + } catch (RuntimeException ignored) { + // Ignore non-string values; environment values are strings. + } + } + } + return Map.copyOf(result); + } + private static String stringValue(TomlTable table, String key, String fallback) { TomlPrimitive primitive = primitive(table, key); return primitive == null ? fallback : primitive.asString(); diff --git a/src/main/java/com/gregor/jprototerm/ShellSession.java b/src/main/java/com/gregor/jprototerm/ShellSession.java index e70a6fb..60d988c 100644 --- a/src/main/java/com/gregor/jprototerm/ShellSession.java +++ b/src/main/java/com/gregor/jprototerm/ShellSession.java @@ -29,11 +29,12 @@ public final class ShellSession implements AutoCloseable { }); } - public static ShellSession start(String shell, TerminalPane pane, int columns, int rows) { + public static ShellSession start(String shell, Map envOverride, TerminalPane pane, int columns, int rows) { try { Map environment = new HashMap<>(System.getenv()); environment.put("TERM", "xterm-kitty"); environment.put("COLORTERM", "truecolor"); + environment.putAll(envOverride); PtyProcess process = new PtyProcessBuilder(new String[] {shell, "-i"}) .setEnvironment(environment) diff --git a/src/main/java/com/gregor/jprototerm/TerminalWorkspace.java b/src/main/java/com/gregor/jprototerm/TerminalWorkspace.java index 072fc6c..1360cc5 100644 --- a/src/main/java/com/gregor/jprototerm/TerminalWorkspace.java +++ b/src/main/java/com/gregor/jprototerm/TerminalWorkspace.java @@ -259,7 +259,7 @@ public final class TerminalWorkspace implements AutoCloseable { private TerminalPane openPane(boolean floating) { TerminalPane pane = TerminalPane.create(config.columns(), config.rows(), config.maxScrollback()); pane.setFloating(floating); - pane.attach(ShellSession.start(config.shell(), pane, config.columns(), config.rows())); + pane.attach(ShellSession.start(config.shell(), config.envOverride(), pane, config.columns(), config.rows())); return pane; }