A terminal for JavaFX applications: the screen, and a contract for whatever is on the other end of it.
2.0.0 is a rewrite, not a migration. The 1.x API — TerminalBuilder, Terminal, TerminalTab,
TerminalConfig — is gone rather than deprecated, because it could not express the thing this
library was being asked for: a terminal that is not a local shell. In 1.x the view started a
PtyProcess itself, so every consumer inherited pty4j and its native payload whether it wanted a
local shell or not, and there was nowhere to put an SSH channel.
What the rewrite is built around:
-
Two artifacts.
terminalfx-coreis the terminal and the session contract, with no native library and no idea what it is connected to.terminalfx-localis a shell on this machine and is the only place pty4j and JNA appear. -
xterm.js instead of hterm. Measured side by side in the same WebView: the same Unicode, box drawing and emoji, about 2.8 times the output rate, and a scrollback that stops growing instead of keeping every line ever printed.
-
The ordering rules are in one place.
TerminalSessionowns the single ordered writer, the bounded queues in both directions, the coalescing of resizes, the two readinesses, and a close that is idempotent, works before the start and cannot be undone by a late callback. -
Output is not trusted. A title from the far end is handed over as text and never executed; a clipboard write (OSC 52) is refused unless the embedder allowed it; output crosses into the page as an argument, never as script.
terminalfx-repo
https://github.com/javaterminal/TerminalFX/raw/master/releases
com.kodedu.terminalfx
terminalfx-local
2.0.0A window with a shell in it, which is the whole of example/:
ExecutorService workers = Executors.newThreadPerTaskExecutor(Thread.ofVirtual().factory());
TerminalView view = new TerminalView(TerminalLook.dark());
view.onCopy(text -> { /* put it on the clipboard */ });
view.onPasteRequested(() -> view.paste(clipboardText()));
TerminalSession session = new TerminalSession(
view, new LocalShell(LocalShellSpec.defaultShell(), workers), workers);
session.onExit(how -> System.out.println("the shell " + how));
session.start();
stage.setScene(new Scene(view, 900, 520));Implement TerminalConnection: four methods, none of them about JavaFX.
public interface TerminalConnection {
void open(Wiring wiring) throws Exception; // off the UI thread; may block
void send(byte[] bytes) throws IOException; // called in order, from one thread
void resize(TerminalSize size) throws IOException;
void close(); // idempotent, and callable before open
}Wiring is what the connection says back: ready(), output(bytes, offset, length) and
ended(TerminalExit). The first ended wins, so a callback arriving after a close is harmless.
Everything else — ordering, bounds, coalescing, the UI thread — is the session’s.
The library starts no threads. TerminalSession and LocalShell are handed the Executor the
embedding application already has, and take two tasks each for the life of a session. The way
onto the UI thread can be handed over as well; it defaults to Platform.runLater.