Support TTerminalWindow resizing via ptypipe

This commit is contained in:
Kevin Lamonte 2017-08-16 21:18:29 -04:00
parent be72cb5ccb
commit 1d99a38f2b
4 changed files with 88 additions and 15 deletions

View file

@ -196,6 +196,14 @@ The following properties control features of Jexer:
be faster on slower systems but also more likely to have screen
tearing. Default: true.
jexer.TTerminal.ptypipe
-----------------------
Used by jexer.TTerminalWindow. If true, spawn shell using the
'ptypipe' utility rather than 'script'. This permits terminals to
resize with the window. ptypipe is a separate C language utility,
available at https://github.com/klamonte/ptypipe. Default: false.
Known Issues / Arbitrary Decisions
@ -213,19 +221,19 @@ ambiguous. This section describes such issues.
input (see the ENABLE_LINE_INPUT flag for GetConsoleMode() and
SetConsoleMode()).
- TTerminalWindow launches 'script -fqe /dev/null' or 'script -q -F
/dev/null' on non-Windows platforms. This is a workaround for the
C library behavior of checking for a tty: script launches $SHELL
in a pseudo-tty. This works on Linux and Mac but might not on
other Posix-y platforms.
- TTerminalWindow by default launches 'script -fqe /dev/null' or
'script -q -F /dev/null' on non-Windows platforms. This is a
workaround for the C library behavior of checking for a tty:
script launches $SHELL in a pseudo-tty. This works on Linux and
Mac but might not on other Posix-y platforms.
- Closing a TTerminalWindow without exiting the process inside it
may result in a zombie 'script' process.
- TTerminalWindow cannot notify the child process of changes in
window size, due to Java's lack of support for forkpty() and
similar. Solving this requires C, and will be pursued only if
sufficient user requests come in.
- TTerminalWindow can only notify the child process of changes in
window size if using the 'ptypipe' utility, due to Java's lack of
support for forkpty() and similar. ptypipe is available at
https://github.com/klamonte/ptypipe.
- Java's InputStreamReader as used by the ECMA48 backend requires a
valid UTF-8 stream. The default X10 encoding for mouse

View file

@ -80,10 +80,6 @@ Roadmap
1.1.0 Wishlist
--------------
- TTerminal
- Handle resize events (pass to child process). Will need to switch
to forkpty(), or ship a C wrapper process.
- Screen
- Allow complex characters in putCharXY() and detect them in putStringXY().

View file

@ -60,6 +60,13 @@ public class TTerminalWindow extends TScrollableWindow
*/
private Process shell;
/**
* If true, we are using the ptypipe utility to support dynamic window
* resizing. ptypipe is available at
* https://github.com/klamonte/ptypipe .
*/
private boolean ptypipe = false;
/**
* Claim the keystrokes the emulator will need.
*/
@ -165,10 +172,19 @@ public class TTerminalWindow extends TScrollableWindow
String [] cmdShellBSD = {
"script", "-q", "-F", "/dev/null"
};
String [] cmdShellPtypipe = {
"ptypipe", "/bin/bash", "--login"
};
// Spawn a shell and pass its I/O to the other constructor.
ProcessBuilder pb;
if (System.getProperty("os.name").startsWith("Windows")) {
if ((System.getProperty("jexer.TTerminal.ptypipe") != null)
&& (System.getProperty("jexer.TTerminal.ptypipe").
equals("true"))
) {
pb = new ProcessBuilder(cmdShellPtypipe);
ptypipe = true;
} else if (System.getProperty("os.name").startsWith("Windows")) {
pb = new ProcessBuilder(cmdShellWindows);
} else if (System.getProperty("os.name").startsWith("Mac")) {
pb = new ProcessBuilder(cmdShellBSD);
@ -429,6 +445,14 @@ public class TTerminalWindow extends TScrollableWindow
// Get out of scrollback
setVerticalValue(0);
if (ptypipe) {
emulator.setWidth(getWidth() - 2);
emulator.setHeight(getHeight() - 2);
emulator.writeRemote("\033[8;" + (getHeight() - 2) + ";" +
(getWidth() - 2) + "t");
}
}
return;

View file

@ -204,7 +204,7 @@ public class ECMA48 implements Runnable {
*
* @param str string to send
*/
private void writeRemote(final String str) {
public void writeRemote(final String str) {
if (stopReaderThread) {
// Reader hit EOF, bail out now.
close();
@ -221,6 +221,7 @@ public class ECMA48 implements Runnable {
return;
}
try {
outputStream.flush();
for (int i = 0; i < str.length(); i++) {
outputStream.write(str.charAt(i));
}
@ -235,6 +236,7 @@ public class ECMA48 implements Runnable {
return;
}
try {
output.flush();
output.write(str);
output.flush();
} catch (IOException e) {
@ -538,6 +540,22 @@ public class ECMA48 implements Runnable {
return width;
}
/**
* Set the display width.
*
* @param width the new width
*/
public final void setWidth(final int width) {
this.width = width;
rightMargin = width - 1;
if (currentState.cursorX >= width) {
currentState.cursorX = width - 1;
}
if (savedState.cursorX >= width) {
savedState.cursorX = width - 1;
}
}
/**
* Physical display height. We start at 80x24, but the user can resize
* us bigger/smaller.
@ -553,6 +571,33 @@ public class ECMA48 implements Runnable {
return height;
}
/**
* Set the display height.
*
* @param height the new height
*/
public final void setHeight(final int height) {
this.height = height;
scrollRegionBottom = height - 1;
if (scrollRegionTop >= scrollRegionBottom) {
scrollRegionTop = 0;
}
if (currentState.cursorY >= height) {
currentState.cursorY = height - 1;
}
if (savedState.cursorY >= height) {
savedState.cursorY = height - 1;
}
while (display.size() < height) {
DisplayLine line = new DisplayLine(currentState.attr);
line.setReverseColor(reverseVideo);
display.add(line);
}
while (display.size() > height) {
scrollback.add(display.remove(0));
}
}
/**
* Top margin of the scrolling region.
*/