new panes gain active pwd
This commit is contained in:
@@ -9,6 +9,9 @@ import java.lang.foreign.MemorySegment;
|
|||||||
import java.lang.foreign.SymbolLookup;
|
import java.lang.foreign.SymbolLookup;
|
||||||
import java.lang.foreign.ValueLayout;
|
import java.lang.foreign.ValueLayout;
|
||||||
import java.lang.invoke.MethodHandle;
|
import java.lang.invoke.MethodHandle;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -74,6 +77,7 @@ public final class LinuxPty implements AutoCloseable {
|
|||||||
private static final long SPAWN_ACTIONS_SIZE = 256;
|
private static final long SPAWN_ACTIONS_SIZE = 256;
|
||||||
private static final long SPAWN_ATTR_SIZE = 512;
|
private static final long SPAWN_ATTR_SIZE = 512;
|
||||||
|
|
||||||
|
private static final MethodHandle TCGETPGRP = handle("tcgetpgrp", FD_INT_INT);
|
||||||
private static final MethodHandle POSIX_OPENPT = handle("posix_openpt", FD_INT_INT);
|
private static final MethodHandle POSIX_OPENPT = handle("posix_openpt", FD_INT_INT);
|
||||||
private static final MethodHandle GRANTPT = handle("grantpt", FD_INT_INT);
|
private static final MethodHandle GRANTPT = handle("grantpt", FD_INT_INT);
|
||||||
private static final MethodHandle UNLOCKPT = handle("unlockpt", FD_INT_INT);
|
private static final MethodHandle UNLOCKPT = handle("unlockpt", FD_INT_INT);
|
||||||
@@ -205,6 +209,25 @@ public final class LinuxPty implements AutoCloseable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Best-effort current working directory of the terminal's foreground process group, read from
|
||||||
|
* {@code /proc}. This tracks the directory the user is actually in (a {@code cd} in the shell,
|
||||||
|
* or a child program that changed dir), so a newly opened pane can start there. Falls back to
|
||||||
|
* the shell's own pid, and returns {@code null} if it cannot be determined.
|
||||||
|
*/
|
||||||
|
public String currentWorkingDirectory() {
|
||||||
|
if (closed) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
int pgid = callInt(TCGETPGRP, masterFd);
|
||||||
|
int target = pgid > 0 ? pgid : pid;
|
||||||
|
try {
|
||||||
|
return Files.readSymbolicLink(Path.of("/proc", Integer.toString(target), "cwd")).toString();
|
||||||
|
} catch (IOException | RuntimeException ex) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Resizes the terminal window. */
|
/** Resizes the terminal window. */
|
||||||
public void setWinSize(int columns, int rows) {
|
public void setWinSize(int columns, int rows) {
|
||||||
if (closed) {
|
if (closed) {
|
||||||
|
|||||||
@@ -20,7 +20,8 @@ public final class ShellSession implements AutoCloseable {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ShellSession start(String shell, Map<String, String> envOverride, TerminalPane pane, int columns, int rows) {
|
public static ShellSession start(String shell, Map<String, String> envOverride, TerminalPane pane,
|
||||||
|
int columns, int rows, String workingDirectory) {
|
||||||
try {
|
try {
|
||||||
Map<String, String> environment = new HashMap<>(System.getenv());
|
Map<String, String> environment = new HashMap<>(System.getenv());
|
||||||
environment.put("TERM", "xterm-kitty");
|
environment.put("TERM", "xterm-kitty");
|
||||||
@@ -31,7 +32,7 @@ public final class ShellSession implements AutoCloseable {
|
|||||||
LinuxPty pty = LinuxPty.spawn(
|
LinuxPty pty = LinuxPty.spawn(
|
||||||
new String[] {shell, "-i"},
|
new String[] {shell, "-i"},
|
||||||
environment,
|
environment,
|
||||||
System.getProperty("user.home"));
|
workingDirectory != null ? workingDirectory : System.getProperty("user.home"));
|
||||||
ShellSession session = new ShellSession(pty);
|
ShellSession session = new ShellSession(pty);
|
||||||
session.resize(columns, rows);
|
session.resize(columns, rows);
|
||||||
return session;
|
return session;
|
||||||
@@ -69,6 +70,11 @@ public final class ShellSession implements AutoCloseable {
|
|||||||
reader.submit(() -> readOutput(pane));
|
reader.submit(() -> readOutput(pane));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Best-effort current working directory of the running shell, or {@code null} if unknown. */
|
||||||
|
public String currentWorkingDirectory() {
|
||||||
|
return closed ? null : pty.currentWorkingDirectory();
|
||||||
|
}
|
||||||
|
|
||||||
public void resize(int columns, int rows) {
|
public void resize(int columns, int rows) {
|
||||||
if (closed) {
|
if (closed) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -308,7 +308,10 @@ final class Tab implements AutoCloseable {
|
|||||||
widthPx = lastWidth / (tiled.size() + 1);
|
widthPx = lastWidth / (tiled.size() + 1);
|
||||||
heightPx = availHeight;
|
heightPx = availHeight;
|
||||||
}
|
}
|
||||||
return TerminalPane.create(config, metrics, this::markContentChanged, widthPx, heightPx);
|
// Open the new pane in the active pane's working directory, so a split/new pane lands
|
||||||
|
// where the user currently is. null (no active pane yet, or cwd unknown) falls back to home.
|
||||||
|
String workingDirectory = active != null ? active.currentWorkingDirectory() : null;
|
||||||
|
return TerminalPane.create(config, metrics, this::markContentChanged, widthPx, heightPx, workingDirectory);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean directionFilter(Direction direction, TerminalPane current, TerminalPane candidate) {
|
private static boolean directionFilter(Direction direction, TerminalPane current, TerminalPane candidate) {
|
||||||
|
|||||||
@@ -69,9 +69,11 @@ public final class TerminalPane implements AutoCloseable, RenderTarget {
|
|||||||
* columns and rows fit, and that grid is handed to ghostty and the shell at start-up. A
|
* columns and rows fit, and that grid is handed to ghostty and the shell at start-up. A
|
||||||
* non-positive size falls back to the configured default grid (used before the first
|
* non-positive size falls back to the configured default grid (used before the first
|
||||||
* layout, when no rect is known yet). The pane owns the shell session it starts and runs
|
* layout, when no rect is known yet). The pane owns the shell session it starts and runs
|
||||||
* {@code onContentChange} on every content change.
|
* {@code onContentChange} on every content change. The shell starts in {@code workingDirectory}
|
||||||
|
* (e.g. the active pane's cwd), or the user's home when {@code null}.
|
||||||
*/
|
*/
|
||||||
public static TerminalPane create(AppConfig config, TerminalMetrics metrics, Runnable onContentChange, double widthPx, double heightPx) {
|
public static TerminalPane create(AppConfig config, TerminalMetrics metrics, Runnable onContentChange,
|
||||||
|
double widthPx, double heightPx, String workingDirectory) {
|
||||||
int columns = widthPx > 0 ? metrics.columnsFor(widthPx) : config.columns();
|
int columns = widthPx > 0 ? metrics.columnsFor(widthPx) : config.columns();
|
||||||
int rows = heightPx > 0 ? metrics.rowsFor(heightPx) : config.rows();
|
int rows = heightPx > 0 ? metrics.rowsFor(heightPx) : config.rows();
|
||||||
Terminal terminal = Ghostty.open(new TerminalOptions(columns, rows, config.maxScrollback()));
|
Terminal terminal = Ghostty.open(new TerminalOptions(columns, rows, config.maxScrollback()));
|
||||||
@@ -79,7 +81,7 @@ public final class TerminalPane implements AutoCloseable, RenderTarget {
|
|||||||
TerminalPane pane = new TerminalPane(terminal, metrics, config.kittyGraphics(), onContentChange,
|
TerminalPane pane = new TerminalPane(terminal, metrics, config.kittyGraphics(), onContentChange,
|
||||||
new GhosttyTerminalRenderer(metrics), columns, rows);
|
new GhosttyTerminalRenderer(metrics), columns, rows);
|
||||||
pane.refresh();
|
pane.refresh();
|
||||||
pane.attach(ShellSession.start(config.shell(), config.envOverride(), pane, columns, rows));
|
pane.attach(ShellSession.start(config.shell(), config.envOverride(), pane, columns, rows, workingDirectory));
|
||||||
return pane;
|
return pane;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,6 +207,12 @@ public final class TerminalPane implements AutoCloseable, RenderTarget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Best-effort current working directory of this pane's shell, or {@code null} if unknown. */
|
||||||
|
public String currentWorkingDirectory() {
|
||||||
|
ShellSession current = session;
|
||||||
|
return current != null ? current.currentWorkingDirectory() : null;
|
||||||
|
}
|
||||||
|
|
||||||
/** This pane's own content revision, bumped on every change (see {@link #refresh()}). */
|
/** This pane's own content revision, bumped on every change (see {@link #refresh()}). */
|
||||||
public long contentVersion() {
|
public long contentVersion() {
|
||||||
return contentVersion.get();
|
return contentVersion.get();
|
||||||
|
|||||||
Reference in New Issue
Block a user