From 2b4cea140330d51f198f2a990ff30486c90ee845 Mon Sep 17 00:00:00 2001 From: Patrick Plenefisch Date: Thu, 28 Mar 2019 17:52:24 -0400 Subject: [PATCH 1/2] Make a GenericTerminal to support non-process based terminals --- pom.xml | 2 +- .../kodedu/terminalfx/GenericTerminal.java | 108 +++++++++++++++ .../java/com/kodedu/terminalfx/Terminal.java | 128 +----------------- .../kodedu/terminalfx/TerminalAppStarter.java | 3 +- .../kodedu/terminalfx/TerminalBuilder.java | 5 +- .../kodedu/terminalfx/TerminalGenerator.java | 9 ++ .../com/kodedu/terminalfx/TerminalTab.java | 48 +++---- .../terminalfx/config/TabNameGenerator.java | 2 - .../terminalfx/helper/EmptyInputStream.java | 33 +++++ .../kodedu/terminalfx/helper/FxHelper.java | 4 +- .../processes/ExternalProcessPty.java | 117 ++++++++++++++++ .../com/kodedu/terminalfx/processes/Pty.java | 20 +++ 12 files changed, 326 insertions(+), 153 deletions(-) create mode 100644 src/main/java/com/kodedu/terminalfx/GenericTerminal.java create mode 100644 src/main/java/com/kodedu/terminalfx/TerminalGenerator.java create mode 100644 src/main/java/com/kodedu/terminalfx/helper/EmptyInputStream.java create mode 100644 src/main/java/com/kodedu/terminalfx/processes/ExternalProcessPty.java create mode 100644 src/main/java/com/kodedu/terminalfx/processes/Pty.java diff --git a/pom.xml b/pom.xml index ae294da..34f991d 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.kodedu.terminalfx terminalfx - 1.0.8 + 1.1.0 jar TerminalFX diff --git a/src/main/java/com/kodedu/terminalfx/GenericTerminal.java b/src/main/java/com/kodedu/terminalfx/GenericTerminal.java new file mode 100644 index 0000000..6639b8c --- /dev/null +++ b/src/main/java/com/kodedu/terminalfx/GenericTerminal.java @@ -0,0 +1,108 @@ +package com.kodedu.terminalfx; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.util.concurrent.LinkedBlockingQueue; + +import com.kodedu.terminalfx.annotation.WebkitCall; +import com.kodedu.terminalfx.config.TerminalConfig; +import com.kodedu.terminalfx.helper.ThreadHelper; +import com.kodedu.terminalfx.processes.Pty; + +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; + +public class GenericTerminal extends TerminalView { + + private T process; + private final ObjectProperty outputWriterProperty; + private final LinkedBlockingQueue commandQueue; + + public GenericTerminal() { + this(null, null); + } + + public GenericTerminal(TerminalConfig terminalConfig, T virtualProcess) { + setTerminalConfig(terminalConfig); + this.process = virtualProcess; + outputWriterProperty = new SimpleObjectProperty<>(); + commandQueue = new LinkedBlockingQueue<>(); + } + + @WebkitCall + public void command(String command) { + try { + commandQueue.put(command); + } catch (final InterruptedException e) { + throw new RuntimeException(e); + } + ThreadHelper.start(() -> { + try { + final String commandToExecute = commandQueue.poll(); + getOutputWriter().write(commandToExecute); + getOutputWriter().flush(); + } catch (final IOException e) { + e.printStackTrace(); + } + }); + } + + @Override + public void onTerminalReady() { + ThreadHelper.start(() -> { + try { + initializeProcess(); + } catch (final Exception e) { + } + }); + } + + private void initializeProcess() throws Exception { + process.fork(); + + columnsProperty().addListener(evt -> updateWinSize()); + rowsProperty().addListener(evt -> updateWinSize()); + updateWinSize(); + setInputReader(new BufferedReader(new InputStreamReader(process.getInputStream()))); + setErrorReader(new BufferedReader(new InputStreamReader(process.getErrorStream()))); + setOutputWriter(new BufferedWriter(new OutputStreamWriter(process.getOutputStream()))); + focusCursor(); + + countDownLatch.countDown(); + + process.waitFor(); + } + + private void updateWinSize() { + try { + process.setWinSize(getColumns(), getRows()); + } catch (Exception e) { + // + } + } + + public ObjectProperty outputWriterProperty() { + return outputWriterProperty; + } + + public Writer getOutputWriter() { + return outputWriterProperty.get(); + } + + public void setOutputWriter(Writer writer) { + outputWriterProperty.set(writer); + } + + public T getPty() { + return process; + } + + public void destroy() { + getPty().close(); + } + +} diff --git a/src/main/java/com/kodedu/terminalfx/Terminal.java b/src/main/java/com/kodedu/terminalfx/Terminal.java index 7702849..f9a912c 100644 --- a/src/main/java/com/kodedu/terminalfx/Terminal.java +++ b/src/main/java/com/kodedu/terminalfx/Terminal.java @@ -1,143 +1,27 @@ package com.kodedu.terminalfx; -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.io.Writer; -import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.LinkedBlockingQueue; -import com.kodedu.terminalfx.annotation.WebkitCall; import com.kodedu.terminalfx.config.TerminalConfig; -import com.kodedu.terminalfx.helper.IOHelper; -import com.kodedu.terminalfx.helper.ThreadHelper; +import com.kodedu.terminalfx.processes.ExternalProcessPty; import com.pty4j.PtyProcess; -import com.pty4j.WinSize; -import com.sun.jna.Platform; -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.SimpleObjectProperty; - -public class Terminal extends TerminalView { - - private PtyProcess process; - private final ObjectProperty outputWriterProperty; - private final Path terminalPath; - private String[] termCommand; - private final LinkedBlockingQueue commandQueue; +public class Terminal extends GenericTerminal { public Terminal() { this(null, null); } public Terminal(TerminalConfig terminalConfig, Path terminalPath) { - setTerminalConfig(terminalConfig); - this.terminalPath = terminalPath; - outputWriterProperty = new SimpleObjectProperty<>(); - commandQueue = new LinkedBlockingQueue<>(); - } - - @WebkitCall - public void command(String command) { - try { - commandQueue.put(command); - } catch (final InterruptedException e) { - throw new RuntimeException(e); - } - ThreadHelper.start(() -> { - try { - final String commandToExecute = commandQueue.poll(); - getOutputWriter().write(commandToExecute); - getOutputWriter().flush(); - } catch (final IOException e) { - e.printStackTrace(); - } - }); - } - - @Override - public void onTerminalReady() { - ThreadHelper.start(() -> { - try { - initializeProcess(); - } catch (final Exception e) { - } - }); - } - - private void initializeProcess() throws Exception { - final Path dataDir = getDataDir(); - IOHelper.copyLibPty(dataDir); - - if (Platform.isWindows()) { - this.termCommand = getTerminalConfig().getWindowsTerminalStarter().split("\\s+"); - } else { - this.termCommand = getTerminalConfig().getUnixTerminalStarter().split("\\s+"); - } - - final Map envs = new HashMap<>(System.getenv()); - envs.put("TERM", "xterm"); - - System.setProperty("PTY_LIB_FOLDER", dataDir.resolve("libpty").toString()); - - if (Objects.nonNull(terminalPath) && Files.exists(terminalPath)) { - this.process = PtyProcess.exec(termCommand, envs, terminalPath.toString()); - } else { - this.process = PtyProcess.exec(termCommand, envs); - } - - columnsProperty().addListener(evt -> updateWinSize()); - rowsProperty().addListener(evt -> updateWinSize()); - updateWinSize(); - setInputReader(new BufferedReader(new InputStreamReader(process.getInputStream()))); - setErrorReader(new BufferedReader(new InputStreamReader(process.getErrorStream()))); - setOutputWriter(new BufferedWriter(new OutputStreamWriter(process.getOutputStream()))); - focusCursor(); - - countDownLatch.countDown(); - - process.waitFor(); - } - - private Path getDataDir() { - final String userHome = System.getProperty("user.home"); - final Path dataDir = Paths.get(userHome).resolve(".terminalfx"); - return dataDir; + super(terminalConfig, new ExternalProcessPty(terminalPath)); + getPty().setTerminalConfig(getTerminalConfig()); } public Path getTerminalPath() { - return terminalPath; - } - - private void updateWinSize() { - try { - process.setWinSize(new WinSize(getColumns(), getRows())); - } catch (Exception e) { - // - } - } - - public ObjectProperty outputWriterProperty() { - return outputWriterProperty; - } - - public Writer getOutputWriter() { - return outputWriterProperty.get(); - } - - public void setOutputWriter(Writer writer) { - outputWriterProperty.set(writer); + return getPty().getTerminalPath(); } public PtyProcess getProcess() { - return process; + return getPty().getProcess(); } - } diff --git a/src/main/java/com/kodedu/terminalfx/TerminalAppStarter.java b/src/main/java/com/kodedu/terminalfx/TerminalAppStarter.java index 8dfbf7f..cc36533 100644 --- a/src/main/java/com/kodedu/terminalfx/TerminalAppStarter.java +++ b/src/main/java/com/kodedu/terminalfx/TerminalAppStarter.java @@ -1,5 +1,7 @@ package com.kodedu.terminalfx; +import java.io.InputStream; + import javafx.application.Application; import javafx.application.Platform; import javafx.fxml.FXMLLoader; @@ -7,7 +9,6 @@ import javafx.scene.Scene; import javafx.stage.Stage; -import java.io.InputStream; public class TerminalAppStarter extends Application { diff --git a/src/main/java/com/kodedu/terminalfx/TerminalBuilder.java b/src/main/java/com/kodedu/terminalfx/TerminalBuilder.java index d5eda22..856b350 100644 --- a/src/main/java/com/kodedu/terminalfx/TerminalBuilder.java +++ b/src/main/java/com/kodedu/terminalfx/TerminalBuilder.java @@ -2,6 +2,7 @@ import com.kodedu.terminalfx.config.DefaultTabNameGenerator; import com.kodedu.terminalfx.config.TerminalConfig; +import com.kodedu.terminalfx.processes.ExternalProcessPty; import com.kodedu.terminalfx.config.TabNameGenerator; import java.nio.file.Path; @@ -53,8 +54,8 @@ public void setTerminalPath(Path terminalPath) { this.terminalPath = terminalPath; } - public TerminalTab newTerminal() { - TerminalTab terminalTab = new TerminalTab(getTerminalConfig(), getNameGenerator(), getTerminalPath()); + public TerminalTab newTerminal() { + TerminalTab terminalTab = new TerminalTab<>(() -> new Terminal(getTerminalConfig(), getTerminalPath()), getNameGenerator()); return terminalTab; } } diff --git a/src/main/java/com/kodedu/terminalfx/TerminalGenerator.java b/src/main/java/com/kodedu/terminalfx/TerminalGenerator.java new file mode 100644 index 0000000..b795e9f --- /dev/null +++ b/src/main/java/com/kodedu/terminalfx/TerminalGenerator.java @@ -0,0 +1,9 @@ +package com.kodedu.terminalfx; + +import com.kodedu.terminalfx.processes.Pty; + +@FunctionalInterface +public interface TerminalGenerator { + + GenericTerminal generate(); +} diff --git a/src/main/java/com/kodedu/terminalfx/TerminalTab.java b/src/main/java/com/kodedu/terminalfx/TerminalTab.java index 3f486fa..cb9f1a3 100644 --- a/src/main/java/com/kodedu/terminalfx/TerminalTab.java +++ b/src/main/java/com/kodedu/terminalfx/TerminalTab.java @@ -2,14 +2,12 @@ import java.io.Reader; import java.io.Writer; -import java.nio.file.Path; -import java.util.Objects; import com.kodedu.terminalfx.config.TabNameGenerator; import com.kodedu.terminalfx.config.TerminalConfig; import com.kodedu.terminalfx.helper.IOHelper; import com.kodedu.terminalfx.helper.ThreadHelper; -import com.pty4j.PtyProcess; +import com.kodedu.terminalfx.processes.Pty; import javafx.collections.FXCollections; import javafx.collections.ObservableList; @@ -19,17 +17,23 @@ import javafx.scene.control.Tab; import javafx.scene.input.KeyEvent; -public class TerminalTab extends Tab { +public class TerminalTab extends Tab { - private final Terminal terminal; + private final GenericTerminal terminal; private final TabNameGenerator tabNameGenerator; private static final String NEW_TAB_KEY = "T"; + private final TerminalGenerator generator; - public TerminalTab(TerminalConfig terminalConfig, TabNameGenerator tabNameGenerator, Path terminalPath) { - this(new Terminal(terminalConfig, terminalPath), tabNameGenerator); + public TerminalTab(GenericTerminal terminal, TabNameGenerator tabNameGenerator) { + this(terminal, null, tabNameGenerator); } - public TerminalTab(Terminal terminal, TabNameGenerator tabNameGenerator) { + public TerminalTab(TerminalGenerator generator, TabNameGenerator tabNameGenerator) { + this(generator.generate(), generator, tabNameGenerator); + } + + public TerminalTab(GenericTerminal terminal, TerminalGenerator generator, TabNameGenerator tabNameGenerator) { + this.generator = generator; this.terminal = terminal; this.tabNameGenerator = tabNameGenerator; @@ -59,7 +63,9 @@ public TerminalTab(Terminal terminal, TabNameGenerator tabNameGenerator) { closeAll.setOnAction(this::closeAllTerminal); closeOthers.setOnAction(this::closeOtherTerminals); - contextMenu.getItems().addAll(newTab, closeTab, closeOthers, closeAll); + if (generator != null) + contextMenu.getItems().add(newTab); + contextMenu.getItems().addAll(closeTab, closeOthers, closeAll); this.setContextMenu(contextMenu); setContent(terminal); @@ -70,7 +76,7 @@ private void closeOtherTerminals(ActionEvent actionEvent) { for (final Tab tab : tabs) { if (tab instanceof TerminalTab) { if (tab != this) { - ((TerminalTab) tab).closeTerminal(); + ((TerminalTab) tab).closeTerminal(); } } } @@ -80,13 +86,16 @@ private void closeAllTerminal(ActionEvent actionEvent) { final ObservableList tabs = FXCollections.observableArrayList(this.getTabPane().getTabs()); for (final Tab tab : tabs) { if (tab instanceof TerminalTab) { - ((TerminalTab) tab).closeTerminal(); + ((TerminalTab) tab).closeTerminal(); } } } public void newTerminal(ActionEvent... actionEvent) { - final TerminalTab terminalTab = new TerminalTab(getTerminalConfig(), getTabNameGenerator(), getTerminalPath()); + if (generator == null) + return; + + final TerminalTab terminalTab = new TerminalTab(generator, getTabNameGenerator()); getTabPane().getTabs().add(terminalTab); getTabPane().getSelectionModel().select(terminalTab); } @@ -105,10 +114,7 @@ public void closeTerminal(ActionEvent... actionEvent) { public void destroy() { ThreadHelper.start(() -> { - while (Objects.isNull(getProcess())) { - ThreadHelper.sleep(250); - } - getProcess().destroy(); + terminal.destroy(); IOHelper.close(getInputReader(), getErrorReader(), getOutputWriter()); }); } @@ -121,16 +127,12 @@ public TabNameGenerator getTabNameGenerator() { return tabNameGenerator; } - public Path getTerminalPath() { - return terminal.getTerminalPath(); - } - public TerminalConfig getTerminalConfig() { return terminal.getTerminalConfig(); } - public PtyProcess getProcess() { - return terminal.getProcess(); + public Pty getPty() { + return terminal.getPty(); } public Reader getInputReader() { @@ -145,7 +147,7 @@ public Writer getOutputWriter() { return terminal.getOutputWriter(); } - public Terminal getTerminal() { + public GenericTerminal getTerminalView() { return terminal; } } \ No newline at end of file diff --git a/src/main/java/com/kodedu/terminalfx/config/TabNameGenerator.java b/src/main/java/com/kodedu/terminalfx/config/TabNameGenerator.java index 6004daa..4f8dcab 100644 --- a/src/main/java/com/kodedu/terminalfx/config/TabNameGenerator.java +++ b/src/main/java/com/kodedu/terminalfx/config/TabNameGenerator.java @@ -1,7 +1,5 @@ package com.kodedu.terminalfx.config; -import java.util.concurrent.atomic.AtomicInteger; - /** * Created by usta on 26.09.2016. */ diff --git a/src/main/java/com/kodedu/terminalfx/helper/EmptyInputStream.java b/src/main/java/com/kodedu/terminalfx/helper/EmptyInputStream.java new file mode 100644 index 0000000..dd68c91 --- /dev/null +++ b/src/main/java/com/kodedu/terminalfx/helper/EmptyInputStream.java @@ -0,0 +1,33 @@ +package com.kodedu.terminalfx.helper; + +import java.io.IOException; +import java.io.InputStream; +import java.util.concurrent.Semaphore; + +/** + * An empty input stream that never returns. Handy for using as stderr if you never use stderror + */ +public class EmptyInputStream extends InputStream { + + private Semaphore s; + + public EmptyInputStream() { + s = new Semaphore(0); + } + + @Override + public int read() throws IOException { + try { + s.acquire(); + } catch (InterruptedException e) { + throw new IOException(e); + } + return -1; + } + + @Override + public void close() throws IOException { + super.close(); + s.release(); + } +} diff --git a/src/main/java/com/kodedu/terminalfx/helper/FxHelper.java b/src/main/java/com/kodedu/terminalfx/helper/FxHelper.java index b0dd4c9..cf985be 100644 --- a/src/main/java/com/kodedu/terminalfx/helper/FxHelper.java +++ b/src/main/java/com/kodedu/terminalfx/helper/FxHelper.java @@ -22,7 +22,7 @@ public static String colorToHex(Color color) { public static boolean askQuestion(String message) { CompletableFuture completableFuture = new CompletableFuture(); - completableFuture.runAsync(() -> { + CompletableFuture.runAsync(() -> { ThreadHelper.runActionLater(() -> { Alert alert = new Alert(Alert.AlertType.INFORMATION, message, ButtonType.YES, ButtonType.NO); ButtonType buttonType = alert.showAndWait().orElse(ButtonType.NO); @@ -35,7 +35,7 @@ public static boolean askQuestion(String message) { public static String askInput(String message) { CompletableFuture completableFuture = new CompletableFuture(); - completableFuture.runAsync(() -> { + CompletableFuture.runAsync(() -> { ThreadHelper.runActionLater(() -> { TextInputDialog inputDialog = new TextInputDialog(); inputDialog.setContentText(message); diff --git a/src/main/java/com/kodedu/terminalfx/processes/ExternalProcessPty.java b/src/main/java/com/kodedu/terminalfx/processes/ExternalProcessPty.java new file mode 100644 index 0000000..c3cd81a --- /dev/null +++ b/src/main/java/com/kodedu/terminalfx/processes/ExternalProcessPty.java @@ -0,0 +1,117 @@ +package com.kodedu.terminalfx.processes; + +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import com.kodedu.terminalfx.config.TerminalConfig; +import com.kodedu.terminalfx.helper.IOHelper; +import com.kodedu.terminalfx.helper.ThreadHelper; +import com.pty4j.PtyProcess; +import com.pty4j.WinSize; +import com.sun.jna.Platform; + +public class ExternalProcessPty implements Pty { + private TerminalConfig config; + + final private Path dataDir; + private Path terminalPath; + + private PtyProcess process; + + private String[] termCommand; + + public ExternalProcessPty() { + final String userHome = System.getProperty("user.home"); + dataDir = Paths.get(userHome).resolve(".terminalfx"); + config = null; + terminalPath = null; + } + + public ExternalProcessPty(Path terminalPath) { + this(); + this.terminalPath = terminalPath; + } + + public TerminalConfig getTerminalConfig() { + return config; + } + + public void setTerminalConfig(TerminalConfig config) { + this.config = config; + } + + public Path getDataDir() { + return dataDir; + } + + public PtyProcess getProcess() { + return process; + } + + public Path getTerminalPath() { + return terminalPath; + } + + @Override + public void fork() throws Exception { + final Path dataDir = getDataDir(); + IOHelper.copyLibPty(dataDir); + + if (Platform.isWindows()) { + this.termCommand = getTerminalConfig().getWindowsTerminalStarter().split("\\s+"); + } else { + this.termCommand = getTerminalConfig().getUnixTerminalStarter().split("\\s+"); + } + + final Map envs = new HashMap<>(System.getenv()); + envs.put("TERM", "xterm"); + + System.setProperty("PTY_LIB_FOLDER", dataDir.resolve("libpty").toString()); + + if (Objects.nonNull(terminalPath) && Files.exists(terminalPath)) { + this.process = PtyProcess.exec(termCommand, envs, terminalPath.toString()); + } else { + this.process = PtyProcess.exec(termCommand, envs); + } + } + + @Override + public InputStream getInputStream() { + return getProcess().getInputStream(); + } + + @Override + public InputStream getErrorStream() { + return getProcess().getErrorStream(); + } + + @Override + public OutputStream getOutputStream() { + return getProcess().getOutputStream(); + } + + @Override + public void waitFor() throws InterruptedException { + getProcess().waitFor(); + } + + @Override + public void setWinSize(int columns, int rows) { + getProcess().setWinSize(new WinSize(columns, rows)); + } + + @Override + public void close() { + while (Objects.isNull(getProcess())) { + ThreadHelper.sleep(250); + } + getProcess().destroy(); + } + +} diff --git a/src/main/java/com/kodedu/terminalfx/processes/Pty.java b/src/main/java/com/kodedu/terminalfx/processes/Pty.java new file mode 100644 index 0000000..b5e3f5a --- /dev/null +++ b/src/main/java/com/kodedu/terminalfx/processes/Pty.java @@ -0,0 +1,20 @@ +package com.kodedu.terminalfx.processes; + +import java.io.InputStream; +import java.io.OutputStream; + +public interface Pty { + void fork() throws Exception; + + InputStream getInputStream(); + + InputStream getErrorStream(); + + OutputStream getOutputStream(); + + void waitFor() throws InterruptedException; + + void setWinSize(int columns, int rows); + + void close(); +} From f6ae0ad6f3ff099b8723faf3cd0348193170cf46 Mon Sep 17 00:00:00 2001 From: Patrick Plenefisch Date: Fri, 29 Mar 2019 16:21:50 -0400 Subject: [PATCH 2/2] Add readme and sample non-process pty --- README.adoc | 20 ++++-- .../terminalfx/processes/FileCapturePty.java | 63 +++++++++++++++++++ 2 files changed, 79 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/kodedu/terminalfx/processes/FileCapturePty.java diff --git a/README.adoc b/README.adoc index 7b099ae..d32766a 100644 --- a/README.adoc +++ b/README.adoc @@ -5,7 +5,7 @@ Rahman Usta :lang: en :toc: left :numbered: -:terminalfx-version: 1.0.6 +:terminalfx-version: 1.1.0 == JavaFX Terminal Emulator @@ -21,6 +21,7 @@ We use https://github.com/traff/pty4j[Pty4J] to get VT codes from running proces * CygWin Support * Configurable (Color, Size, Font) * Multi Tab Support +* Support for non-process based terminals == Usage @@ -28,7 +29,7 @@ We use https://github.com/traff/pty4j[Pty4J] to get VT codes from running proces [source,java] ---- TerminalBuilder terminalBuilder = new TerminalBuilder(); -TerminalTab terminal = terminalBuilder.newTerminal(); +TerminalTab terminal = terminalBuilder.newTerminal(); TabPane tabPane = new TabPane(); tabPane.getTabs().add(terminal); @@ -45,7 +46,7 @@ darkConfig.setForegroundColor(Color.rgb(240, 240, 240)); darkConfig.setCursorColor(Color.rgb(255, 0, 0, 0.5)); TerminalBuilder terminalBuilder = new TerminalBuilder(darkConfig); -TerminalTab terminal = terminalBuilder.newTerminal(); +TerminalTab terminal = terminalBuilder.newTerminal(); TabPane tabPane = new TabPane(); tabPane.getTabs().add(terminal); @@ -53,6 +54,17 @@ tabPane.getTabs().add(terminal); image::images\dark.png[] +.Using a non-Process Terminal +[source,java] +---- +FileCapturePty filecap = new FileCapturePty(new File("log.in"), new File("README.adoc"), new File("LICENSE")); +GenericTerminal view = new GenericTerminal(darkConfig, filecap); +TerminalTab terminal = new TerminalTab<>(view, new DefaultTabNameGenerator()); + +TabPane tabPane = new TabPane(); +tabPane.getTabs().add(terminal); +---- + == CygWin Support [source,java] @@ -61,7 +73,7 @@ TerminalConfig cygwinConfig = new TerminalConfig(); cygwinConfig.setWindowsTerminalStarter("C:\\cygwin64\\bin\\bash -i"); <1> TerminalBuilder terminalBuilder = new TerminalBuilder(cygwinConfig); -TerminalTab terminal = terminalBuilder.newTerminal(); +TerminalTab terminal = terminalBuilder.newTerminal(); TabPane tabPane = new TabPane(); tabPane.getTabs().add(terminal); diff --git a/src/main/java/com/kodedu/terminalfx/processes/FileCapturePty.java b/src/main/java/com/kodedu/terminalfx/processes/FileCapturePty.java new file mode 100644 index 0000000..3454662 --- /dev/null +++ b/src/main/java/com/kodedu/terminalfx/processes/FileCapturePty.java @@ -0,0 +1,63 @@ +package com.kodedu.terminalfx.processes; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class FileCapturePty implements Pty { + + private FileInputStream stderr, stdout; + private FileOutputStream stdin; + + public FileCapturePty(File stdinDestination, File stdout, File stderr) throws FileNotFoundException { + this.stdin = new FileOutputStream(stdinDestination); + this.stdout = new FileInputStream(stdout); + this.stderr = new FileInputStream(stderr); + } + + @Override + public void fork() throws Exception { + // nothing to do here + } + + @Override + public InputStream getInputStream() { + return stdout; + } + + @Override + public InputStream getErrorStream() { + return stderr; + } + + @Override + public OutputStream getOutputStream() { + return stdin; + } + + @Override + public void waitFor() throws InterruptedException { + // don't wait + } + + @Override + public void setWinSize(int columns, int rows) { + System.out.println("Window size set to " + columns + "x" + rows); + } + + @Override + public void close() { + try { + stdin.close(); + stdout.close(); + stderr.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + +}