This commit is contained in:
Gregor Lohaus
2026-05-28 21:33:37 +02:00
parent 80cd318c1c
commit c7f734bf64
25 changed files with 209 additions and 4 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

View File

@@ -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

Binary file not shown.

View File

@@ -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"

View File

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

View File

@@ -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"

161
flake.nix Normal file
View File

@@ -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;
};
});
};
}

View File

@@ -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<String, String> envOverride,
Map<String, KeyBinding> keybindings
) {
private static final List<String> 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<String, String> 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<String, String> envOverride(TomlTable table, Map<String, String> fallback) {
TomlValue value = table.get("env.override");
if (value == null || !value.isTable()) {
return fallback;
}
Map<String, String> 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();

View File

@@ -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<String, String> envOverride, TerminalPane pane, int columns, int rows) {
try {
Map<String, String> 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)

View File

@@ -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;
}