diff --git a/.gitignore b/.gitignore index 7f46a51..0c7bb71 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ devenv.local.yaml # pre-commit .pre-commit-config.yaml build +build diff --git a/.gradle/9.4.1/checksums/checksums.lock b/.gradle/9.4.1/checksums/checksums.lock index 21b1625..f74a924 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/checksums/md5-checksums.bin b/.gradle/9.4.1/checksums/md5-checksums.bin index 6c6e39e..9f1f3c5 100644 Binary files a/.gradle/9.4.1/checksums/md5-checksums.bin and b/.gradle/9.4.1/checksums/md5-checksums.bin differ diff --git a/.gradle/9.4.1/checksums/sha1-checksums.bin b/.gradle/9.4.1/checksums/sha1-checksums.bin index 163b8e6..6f9cee4 100644 Binary files a/.gradle/9.4.1/checksums/sha1-checksums.bin and b/.gradle/9.4.1/checksums/sha1-checksums.bin differ diff --git a/.gradle/9.4.1/executionHistory/executionHistory.bin b/.gradle/9.4.1/executionHistory/executionHistory.bin index b89587b..f5d8b1d 100644 Binary files a/.gradle/9.4.1/executionHistory/executionHistory.bin and b/.gradle/9.4.1/executionHistory/executionHistory.bin differ diff --git a/.gradle/9.4.1/executionHistory/executionHistory.lock b/.gradle/9.4.1/executionHistory/executionHistory.lock index e066b43..181a746 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.bin b/.gradle/9.4.1/fileHashes/fileHashes.bin index 6ca9c89..75da2f8 100644 Binary files a/.gradle/9.4.1/fileHashes/fileHashes.bin and b/.gradle/9.4.1/fileHashes/fileHashes.bin differ diff --git a/.gradle/9.4.1/fileHashes/fileHashes.lock b/.gradle/9.4.1/fileHashes/fileHashes.lock index 547b775..682cde6 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.4.1/fileHashes/resourceHashesCache.bin b/.gradle/9.4.1/fileHashes/resourceHashesCache.bin index 139d13a..45933e9 100644 Binary files a/.gradle/9.4.1/fileHashes/resourceHashesCache.bin and b/.gradle/9.4.1/fileHashes/resourceHashesCache.bin differ diff --git a/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/.gradle/buildOutputCleanup/buildOutputCleanup.lock index 1b09cf3..5b7c9de 100644 Binary files a/.gradle/buildOutputCleanup/buildOutputCleanup.lock and b/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ diff --git a/build/classes/java/main/com/gregor/jprototerm/AppConfig.class b/build/classes/java/main/com/gregor/jprototerm/AppConfig.class deleted file mode 100644 index 96dce22..0000000 Binary files a/build/classes/java/main/com/gregor/jprototerm/AppConfig.class and /dev/null differ diff --git a/build/classes/java/main/com/gregor/jprototerm/Direction.class b/build/classes/java/main/com/gregor/jprototerm/Direction.class deleted file mode 100644 index e540aa0..0000000 Binary files a/build/classes/java/main/com/gregor/jprototerm/Direction.class and /dev/null differ diff --git a/build/classes/java/main/com/gregor/jprototerm/KeyBinding.class b/build/classes/java/main/com/gregor/jprototerm/KeyBinding.class deleted file mode 100644 index 017b2da..0000000 Binary files a/build/classes/java/main/com/gregor/jprototerm/KeyBinding.class and /dev/null differ diff --git a/build/classes/java/main/com/gregor/jprototerm/KeyEncoder$1.class b/build/classes/java/main/com/gregor/jprototerm/KeyEncoder$1.class deleted file mode 100644 index 09505a7..0000000 Binary files a/build/classes/java/main/com/gregor/jprototerm/KeyEncoder$1.class and /dev/null differ diff --git a/build/classes/java/main/com/gregor/jprototerm/KeyEncoder.class b/build/classes/java/main/com/gregor/jprototerm/KeyEncoder.class deleted file mode 100644 index 56c0172..0000000 Binary files a/build/classes/java/main/com/gregor/jprototerm/KeyEncoder.class and /dev/null differ diff --git a/build/classes/java/main/com/gregor/jprototerm/Main$1.class b/build/classes/java/main/com/gregor/jprototerm/Main$1.class deleted file mode 100644 index fc23ee6..0000000 Binary files a/build/classes/java/main/com/gregor/jprototerm/Main$1.class and /dev/null differ diff --git a/build/classes/java/main/com/gregor/jprototerm/Main.class b/build/classes/java/main/com/gregor/jprototerm/Main.class deleted file mode 100644 index e05e6bd..0000000 Binary files a/build/classes/java/main/com/gregor/jprototerm/Main.class and /dev/null differ diff --git a/build/classes/java/main/com/gregor/jprototerm/ShellSession.class b/build/classes/java/main/com/gregor/jprototerm/ShellSession.class deleted file mode 100644 index 95437f4..0000000 Binary files a/build/classes/java/main/com/gregor/jprototerm/ShellSession.class and /dev/null differ diff --git a/build/classes/java/main/com/gregor/jprototerm/TerminalCanvasView$FontMetrics.class b/build/classes/java/main/com/gregor/jprototerm/TerminalCanvasView$FontMetrics.class deleted file mode 100644 index 9f0f6d1..0000000 Binary files a/build/classes/java/main/com/gregor/jprototerm/TerminalCanvasView$FontMetrics.class and /dev/null differ diff --git a/build/classes/java/main/com/gregor/jprototerm/TerminalCanvasView.class b/build/classes/java/main/com/gregor/jprototerm/TerminalCanvasView.class deleted file mode 100644 index 51ad634..0000000 Binary files a/build/classes/java/main/com/gregor/jprototerm/TerminalCanvasView.class and /dev/null differ diff --git a/build/classes/java/main/com/gregor/jprototerm/TerminalPane.class b/build/classes/java/main/com/gregor/jprototerm/TerminalPane.class deleted file mode 100644 index b860482..0000000 Binary files a/build/classes/java/main/com/gregor/jprototerm/TerminalPane.class and /dev/null differ diff --git a/build/classes/java/main/com/gregor/jprototerm/TerminalWorkspace$1.class b/build/classes/java/main/com/gregor/jprototerm/TerminalWorkspace$1.class deleted file mode 100644 index 2bd6d75..0000000 Binary files a/build/classes/java/main/com/gregor/jprototerm/TerminalWorkspace$1.class and /dev/null differ diff --git a/build/classes/java/main/com/gregor/jprototerm/TerminalWorkspace.class b/build/classes/java/main/com/gregor/jprototerm/TerminalWorkspace.class deleted file mode 100644 index b81a68f..0000000 Binary files a/build/classes/java/main/com/gregor/jprototerm/TerminalWorkspace.class and /dev/null differ diff --git a/build/tmp/compileJava/previous-compilation-data.bin b/build/tmp/compileJava/previous-compilation-data.bin deleted file mode 100644 index 50a8bb1..0000000 Binary files a/build/tmp/compileJava/previous-compilation-data.bin and /dev/null differ diff --git a/config.example.toml b/config.example.toml index 6289750..4f262c9 100644 --- a/config.example.toml +++ b/config.example.toml @@ -1,6 +1,7 @@ [terminal] columns = 100 rows = 30 +max_scrollback = 100000 shell = "/bin/bash" font_family = "JetBrainsMono Nerd Font" font_size = 15 diff --git a/src/main/java/com/gregor/jprototerm/AppConfig.java b/src/main/java/com/gregor/jprototerm/AppConfig.java index 2684e78..af6f1e1 100644 --- a/src/main/java/com/gregor/jprototerm/AppConfig.java +++ b/src/main/java/com/gregor/jprototerm/AppConfig.java @@ -18,6 +18,7 @@ import java.util.Map; public record AppConfig( int columns, int rows, + long maxScrollback, String shell, String fontFamily, double fontSize, @@ -51,6 +52,7 @@ public record AppConfig( return new AppConfig( intValue(document, "terminal.columns", defaults.columns), intValue(document, "terminal.rows", defaults.rows), + longValue(document, "terminal.max_scrollback", defaults.maxScrollback), stringValue(document, "terminal.shell", defaults.shell), stringValue(document, "terminal.font_family", defaults.fontFamily), doubleValue(document, "terminal.font_size", defaults.fontSize), @@ -69,6 +71,7 @@ public record AppConfig( return new AppConfig( 100, 30, + 100_000, defaultShell(), "JetBrainsMono Nerd Font", 15.0, @@ -93,6 +96,7 @@ public record AppConfig( return new AppConfig( columns, rows, + maxScrollback, shell, family, size, @@ -154,6 +158,7 @@ public record AppConfig( builder.append("[terminal]\n"); builder.append("columns = ").append(columns).append('\n'); builder.append("rows = ").append(rows).append('\n'); + builder.append("max_scrollback = ").append(maxScrollback).append('\n'); builder.append("shell = ").append(quoted(shell)).append('\n'); builder.append("font_family = ").append(quoted(fontFamily)).append('\n'); builder.append("font_size = ").append(trimDouble(fontSize)).append("\n\n"); @@ -218,6 +223,18 @@ public record AppConfig( } } + private static long longValue(TomlTable table, String key, long fallback) { + TomlPrimitive primitive = primitive(table, key); + if (primitive == null) { + return fallback; + } + try { + return primitive.asInteger(); + } catch (RuntimeException ex) { + return fallback; + } + } + private static double doubleValue(TomlTable table, String key, double fallback) { TomlPrimitive primitive = primitive(table, key); if (primitive == null) { diff --git a/src/main/java/com/gregor/jprototerm/Main.java b/src/main/java/com/gregor/jprototerm/Main.java index 351871c..c2ac771 100644 --- a/src/main/java/com/gregor/jprototerm/Main.java +++ b/src/main/java/com/gregor/jprototerm/Main.java @@ -30,7 +30,6 @@ public final class Main extends Application { StackPane root = new StackPane(terminalView.canvas()); terminalView.canvas().widthProperty().bind(root.widthProperty()); terminalView.canvas().heightProperty().bind(root.heightProperty()); - terminalView.canvas().setOnMousePressed(event -> terminalView.canvas().requestFocus()); Scene scene = new Scene(root, config.windowWidth(), config.windowHeight()); scene.addEventFilter(KeyEvent.KEY_PRESSED, this::handlePressed); diff --git a/src/main/java/com/gregor/jprototerm/TerminalCanvasView.java b/src/main/java/com/gregor/jprototerm/TerminalCanvasView.java index 9b1a0d0..80d84cb 100644 --- a/src/main/java/com/gregor/jprototerm/TerminalCanvasView.java +++ b/src/main/java/com/gregor/jprototerm/TerminalCanvasView.java @@ -5,6 +5,10 @@ import dev.jlibghostty.KittyImageFormat; import dev.jlibghostty.KittyImageSnapshot; import dev.jlibghostty.KittyPlacement; import dev.jlibghostty.KittyRenderInfo; +import dev.jlibghostty.KeyModifiers; +import dev.jlibghostty.MouseButton; +import dev.jlibghostty.MouseEncoderSize; +import dev.jlibghostty.MouseInput; import dev.jlibghostty.RenderCell; import dev.jlibghostty.RenderColor; import dev.jlibghostty.RenderCursorStyle; @@ -15,6 +19,10 @@ import javafx.scene.canvas.GraphicsContext; import javafx.scene.image.Image; import javafx.scene.image.PixelFormat; import javafx.scene.image.WritableImage; +import javafx.scene.input.InputEvent; +import javafx.scene.input.MouseEvent; +import javafx.scene.input.ScrollEvent; +import javafx.scene.input.ScrollEvent.VerticalTextScrollUnits; import javafx.scene.paint.Color; import javafx.scene.text.Font; import javafx.scene.text.FontSmoothingType; @@ -34,6 +42,8 @@ public final class TerminalCanvasView { private final Map kittyImageCache = new HashMap<>(); private String fontFamily; private double fontSize; + private boolean mouseButtonPressed; + private MouseButton pressedButton = MouseButton.UNKNOWN; public TerminalCanvasView(TerminalWorkspace workspace, AppConfig config) { this.workspace = workspace; @@ -41,6 +51,11 @@ public final class TerminalCanvasView { this.fontFamily = config.fontFamily(); this.fontSize = config.fontSize(); canvas.setFocusTraversable(true); + canvas.setOnMousePressed(this::handleMousePressed); + canvas.setOnMouseReleased(this::handleMouseReleased); + canvas.setOnMouseDragged(this::handleMouseDragged); + canvas.setOnMouseMoved(this::handleMouseMoved); + canvas.setOnScroll(this::handleScroll); } public Canvas canvas() { @@ -119,12 +134,187 @@ public final class TerminalCanvasView { double lineHeight = Math.max(1.0, text.getLayoutBounds().getHeight()); double baselineOffset = -text.getLayoutBounds().getMinY(); - Text cell = new Text("M"); + String sample = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + Text cell = new Text(sample); cell.setFont(font); - double cellWidth = Math.max(1.0, cell.getLayoutBounds().getWidth()); + double cellWidth = Math.max(1.0, cell.getLayoutBounds().getWidth() / sample.length()); return new FontMetrics(cellWidth, lineHeight, baselineOffset); } + private void handleMousePressed(MouseEvent event) { + canvas.requestFocus(); + TerminalPane pane = paneAt(event.getX(), event.getY()); + if (pane == null) { + return; + } + + workspace.focus(pane); + pressedButton = mouseButton(event); + mouseButtonPressed = true; + sendMouse(pane, MouseInput.press(pressedButton, eventX(pane, event.getX()), eventY(pane, event.getY()), modifiers(event)), true, event); + } + + private void handleMouseReleased(MouseEvent event) { + TerminalPane pane = paneAt(event.getX(), event.getY()); + if (pane == null) { + pane = workspace.activePane(); + } + + MouseButton button = pressedButton == MouseButton.UNKNOWN ? mouseButton(event) : pressedButton; + sendMouse(pane, MouseInput.release(button, eventX(pane, event.getX()), eventY(pane, event.getY()), modifiers(event)), false, event); + mouseButtonPressed = false; + pressedButton = MouseButton.UNKNOWN; + } + + private void handleMouseDragged(MouseEvent event) { + TerminalPane pane = paneAt(event.getX(), event.getY()); + if (pane == null) { + pane = workspace.activePane(); + } + + MouseButton button = pressedButton == MouseButton.UNKNOWN ? mouseButton(event) : pressedButton; + sendMouse(pane, MouseInput.drag(button, eventX(pane, event.getX()), eventY(pane, event.getY()), modifiers(event)), true, event); + } + + private void handleMouseMoved(MouseEvent event) { + TerminalPane pane = paneAt(event.getX(), event.getY()); + if (pane == null) { + return; + } + + sendMouse(pane, MouseInput.motion(eventX(pane, event.getX()), eventY(pane, event.getY()), modifiers(event)), mouseButtonPressed, event); + } + + private void handleScroll(ScrollEvent event) { + TerminalPane pane = paneAt(event.getX(), event.getY()); + if (pane == null) { + return; + } + + canvas.requestFocus(); + workspace.focus(pane); + int direction = scrollDirection(event); + if (direction == 0) { + return; + } + + MouseButton wheelButton = direction > 0 ? MouseButton.FOUR : MouseButton.FIVE; + int rows = scrollRows(event); + boolean sent = false; + for (int i = 0; i < rows; i++) { + sent |= sendMouse( + pane, + MouseInput.press(wheelButton, eventX(pane, event.getX()), eventY(pane, event.getY()), modifiers(event)), + mouseButtonPressed, + event + ); + } + if (!sent) { + pane.scrollViewport(direction > 0 ? -rows : rows); + event.consume(); + } + } + + private boolean sendMouse(TerminalPane pane, MouseInput input, boolean anyButtonPressed, InputEvent event) { + MouseTarget target = mouseTarget(pane); + if (target == null) { + return false; + } + + boolean sent = pane.sendMouse(input, target.size(), anyButtonPressed); + if (sent) { + event.consume(); + } + return sent; + } + + private TerminalPane paneAt(double x, double y) { + java.util.List panes = workspace.panes(); + for (int i = panes.size() - 1; i >= 0; i--) { + TerminalPane pane = panes.get(i); + if (x >= pane.x() && x < pane.x() + pane.width() && y >= pane.y() && y < pane.y() + pane.height()) { + return pane; + } + } + return null; + } + + private MouseTarget mouseTarget(TerminalPane pane) { + if (pane.width() <= 24.0 || pane.height() <= 24.0) { + return null; + } + + FontMetrics metrics = measureFontMetrics(Font.font(fontFamily, fontSize)); + int columns = Math.max(1, (int) ((pane.width() - 24.0) / metrics.cellWidth)); + int rows = Math.max(1, (int) ((pane.height() - 24.0) / metrics.lineHeight)); + long cellWidth = Math.max(1L, Math.round(metrics.cellWidth)); + long cellHeight = Math.max(1L, Math.round(metrics.lineHeight)); + long screenWidth = Math.max(1L, Math.round(columns * metrics.cellWidth)); + long screenHeight = Math.max(1L, Math.round(rows * metrics.lineHeight)); + return new MouseTarget(MouseEncoderSize.of(screenWidth, screenHeight, cellWidth, cellHeight), screenWidth, screenHeight); + } + + private double eventX(TerminalPane pane, double canvasX) { + MouseTarget target = mouseTarget(pane); + if (target == null) { + return 0.0; + } + return clamp(canvasX - pane.x() - 12.0, 0.0, target.screenWidth() - 1.0); + } + + private double eventY(TerminalPane pane, double canvasY) { + MouseTarget target = mouseTarget(pane); + if (target == null) { + return 0.0; + } + return clamp(canvasY - pane.y() - 12.0, 0.0, target.screenHeight() - 1.0); + } + + private static double clamp(double value, double min, double max) { + return Math.max(min, Math.min(max, value)); + } + + private static KeyModifiers modifiers(MouseEvent event) { + return KeyModifiers.of(event.isShiftDown(), event.isControlDown(), event.isAltDown(), event.isMetaDown()); + } + + private static KeyModifiers modifiers(ScrollEvent event) { + return KeyModifiers.of(event.isShiftDown(), event.isControlDown(), event.isAltDown(), event.isMetaDown()); + } + + private static int scrollRows(ScrollEvent event) { + double rows; + if (event.getTextDeltaYUnits() == VerticalTextScrollUnits.LINES && event.getTextDeltaY() != 0.0) { + rows = Math.abs(event.getTextDeltaY()); + } else if (event.getTextDeltaYUnits() == VerticalTextScrollUnits.PAGES && event.getTextDeltaY() != 0.0) { + rows = Math.abs(event.getTextDeltaY()) * 24.0; + } else if (event.getMultiplierY() > 0.0) { + rows = Math.abs(event.getDeltaY()) / event.getMultiplierY(); + } else { + rows = Math.abs(event.getDeltaY()) / 40.0; + } + return Math.max(1, Math.min(64, (int) Math.ceil(rows))); + } + + private static int scrollDirection(ScrollEvent event) { + if (event.getDeltaY() != 0.0) { + return event.getDeltaY() > 0.0 ? 1 : -1; + } + if (event.getTextDeltaYUnits() != VerticalTextScrollUnits.NONE && event.getTextDeltaY() != 0.0) { + return event.getTextDeltaY() > 0.0 ? 1 : -1; + } + return 0; + } + + private static MouseButton mouseButton(MouseEvent event) { + return switch (event.getButton()) { + case PRIMARY -> MouseButton.LEFT; + case SECONDARY -> MouseButton.RIGHT; + case MIDDLE -> MouseButton.MIDDLE; + default -> MouseButton.UNKNOWN; + }; + } + private static void drawRow( GraphicsContext gc, RenderRow row, @@ -139,11 +329,11 @@ public final class TerminalCanvasView { double cellTop = top + (row.row() * lineHeight); cell.background().ifPresent(background -> { gc.setFill(toFxColor(background)); - gc.fillRect(x, cellTop, cellWidth, lineHeight); + fillCellRect(gc, x, cellTop, cellWidth, lineHeight); }); if (cell.selected()) { gc.setFill(SELECTED_BACKGROUND); - gc.fillRect(x, cellTop, cellWidth, lineHeight); + fillCellRect(gc, x, cellTop, cellWidth, lineHeight); } if (cell.codepoints().length == 0) { continue; @@ -156,6 +346,14 @@ public final class TerminalCanvasView { } } + private static void fillCellRect(GraphicsContext gc, double x, double y, double width, double height) { + double x1 = Math.floor(x); + double y1 = Math.floor(y); + double x2 = Math.ceil(x + width); + double y2 = Math.ceil(y + height); + gc.fillRect(x1, y1, Math.max(1.0, x2 - x1), Math.max(1.0, y2 - y1)); + } + private static Color toFxColor(RenderColor color) { return Color.rgb(color.red(), color.green(), color.blue()); } @@ -253,4 +451,7 @@ public final class TerminalCanvasView { private record FontMetrics(double cellWidth, double lineHeight, double baselineOffset) { } + + private record MouseTarget(MouseEncoderSize size, long screenWidth, long screenHeight) { + } } diff --git a/src/main/java/com/gregor/jprototerm/TerminalPane.java b/src/main/java/com/gregor/jprototerm/TerminalPane.java index 2ce4e00..f6e179d 100644 --- a/src/main/java/com/gregor/jprototerm/TerminalPane.java +++ b/src/main/java/com/gregor/jprototerm/TerminalPane.java @@ -2,7 +2,12 @@ package com.gregor.jprototerm; import dev.jlibghostty.Ghostty; import dev.jlibghostty.KittyGraphics; +import dev.jlibghostty.MouseAction; +import dev.jlibghostty.MouseEncoder; +import dev.jlibghostty.MouseEncoderSize; +import dev.jlibghostty.MouseInput; import dev.jlibghostty.RenderStateSnapshot; +import dev.jlibghostty.ScrollViewport; import dev.jlibghostty.Terminal; import dev.jlibghostty.TerminalOptions; import dev.jlibghostty.DeviceAttributes; @@ -12,6 +17,7 @@ import java.util.concurrent.atomic.AtomicReference; public final class TerminalPane implements AutoCloseable { private final Terminal terminal; + private final MouseEncoder mouseEncoder = new MouseEncoder(); private final AtomicReference renderSnapshot = new AtomicReference<>(); private ShellSession session; private boolean floating; @@ -31,8 +37,8 @@ public final class TerminalPane implements AutoCloseable { this.rows = rows; } - public static TerminalPane create(int columns, int rows) { - Terminal terminal = Ghostty.open(TerminalOptions.of(columns, rows)); + public static TerminalPane create(int columns, int rows, long maxScrollback) { + Terminal terminal = Ghostty.open(new TerminalOptions(columns, rows, maxScrollback)); terminal.setDeviceAttributesProvider(DeviceAttributes::xtermCompatible); TerminalPane pane = new TerminalPane(terminal, columns, rows); pane.refresh(); @@ -65,11 +71,45 @@ public final class TerminalPane implements AutoCloseable { } public void send(String text) { + scrollViewportToBottom(); if (session != null) { session.send(text); } } + public boolean sendMouse(MouseInput input, MouseEncoderSize size, boolean anyButtonPressed) { + synchronized (terminal) { + mouseEncoder.syncFromTerminal(terminal); + mouseEncoder.setSize(size); + mouseEncoder.setAnyButtonPressed(anyButtonPressed); + mouseEncoder.setTrackLastCell(input.action() == MouseAction.MOTION && input.button().isEmpty()); + + byte[] encoded = mouseEncoder.encode(input); + if (encoded.length == 0) { + return false; + } + + if (session != null) { + session.send(encoded); + } + return true; + } + } + + public void scrollViewport(long rows) { + synchronized (terminal) { + terminal.scrollViewport(ScrollViewport.delta(rows)); + refresh(); + } + } + + public void scrollViewportToBottom() { + synchronized (terminal) { + terminal.scrollViewport(ScrollViewport.bottom()); + refresh(); + } + } + public RenderStateSnapshot renderSnapshot() { return renderSnapshot.get(); } @@ -150,6 +190,7 @@ public final class TerminalPane implements AutoCloseable { session.close(); session = null; } + mouseEncoder.close(); terminal.close(); } } diff --git a/src/main/java/com/gregor/jprototerm/TerminalWorkspace.java b/src/main/java/com/gregor/jprototerm/TerminalWorkspace.java index 770dd45..e0ad305 100644 --- a/src/main/java/com/gregor/jprototerm/TerminalWorkspace.java +++ b/src/main/java/com/gregor/jprototerm/TerminalWorkspace.java @@ -38,6 +38,13 @@ public final class TerminalWorkspace implements AutoCloseable { return activePane() == pane; } + public void focus(TerminalPane pane) { + int index = panes.indexOf(pane); + if (index >= 0 && pane.visible()) { + activeIndex = index; + } + } + public void layout(double width, double height) { List tiled = panes.stream() .filter(TerminalPane::visible) @@ -235,7 +242,7 @@ public final class TerminalWorkspace implements AutoCloseable { } private TerminalPane openPane(boolean floating) { - TerminalPane pane = TerminalPane.create(config.columns(), config.rows()); + TerminalPane pane = TerminalPane.create(config.columns(), config.rows(), config.maxScrollback()); pane.setFloating(floating); pane.attach(ShellSession.start(config.shell(), pane, config.columns(), config.rows())); return pane;