From 6405180cb8d84bb650f9bbcbfe5839dbd09b0481 Mon Sep 17 00:00:00 2001 From: Fredrik Fornwall Date: Tue, 9 Feb 2016 11:24:05 +0100 Subject: [PATCH] Wait for terminal size before starting process This fixes https://github.com/termux/termux-widget/issues/2, which was caused by the terminal launching the terminal session process before the terminal size was known. Also remove the built JNI libraries from source control. --- README.md | 2 +- .../main/java/com/termux/terminal/JNI.java | 2 +- .../com/termux/terminal/TerminalSession.java | 30 +++++++++++++----- app/src/main/jni/termux.c | 26 ++++++++++++--- app/src/main/jniLibs/armeabi-v7a/libtermux.so | Bin 13288 -> 0 bytes app/src/main/jniLibs/x86/libtermux.so | Bin 9084 -> 0 bytes 6 files changed, 45 insertions(+), 15 deletions(-) delete mode 100755 app/src/main/jniLibs/armeabi-v7a/libtermux.so delete mode 100755 app/src/main/jniLibs/x86/libtermux.so diff --git a/README.md b/README.md index 43d1cc36..e191f557 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Released under [the GPLv3 license](https://www.gnu.org/licenses/gpl.html). Conta Building JNI libraries ====================== -For ease of use, the JNI libraries are checked into version control. Execute the `build-jnilibs.sh` script to rebuild them. +Execute the `build-jnilibs.sh` script to build the required JNI libraries. Terminal resources ================== diff --git a/app/src/main/java/com/termux/terminal/JNI.java b/app/src/main/java/com/termux/terminal/JNI.java index 423541a6..c0cff30a 100644 --- a/app/src/main/java/com/termux/terminal/JNI.java +++ b/app/src/main/java/com/termux/terminal/JNI.java @@ -28,7 +28,7 @@ final class JNI { * @return the file descriptor resulting from opening /dev/ptmx master device. The sub process will have opened the * slave device counterpart (/dev/pts/$N) and have it as stdint, stdout and stderr. */ - public static native int createSubprocess(String cmd, String cwd, String[] args, String[] envVars, int[] processId); + public static native int createSubprocess(String cmd, String cwd, String[] args, String[] envVars, int[] processId, int rows, int columns); /** Set the window size for a given pty, which allows connected programs to learn how large their screen is. */ public static native void setPtyWindowSize(int fd, int rows, int cols); diff --git a/app/src/main/java/com/termux/terminal/TerminalSession.java b/app/src/main/java/com/termux/terminal/TerminalSession.java index e55d78ba..1ed9af7d 100644 --- a/app/src/main/java/com/termux/terminal/TerminalSession.java +++ b/app/src/main/java/com/termux/terminal/TerminalSession.java @@ -82,14 +82,17 @@ public final class TerminalSession extends TerminalOutput { /** Callback which gets notified when a session finishes or changes title. */ final SessionChangedCallback mChangeCallback; - /** The pid of the shell process or -1 if not running. */ + /** The pid of the shell process. 0 if not started and -1 if finished running. */ int mShellPid; - int mShellExitStatus = -1; + + /** The exit status of the shell process. Only valid if ${@link #mShellPid} is -1. */ + int mShellExitStatus; + /** * The file descriptor referencing the master half of a pseudo-terminal pair, resulting from calling * {@link JNI#createSubprocess(String, String, String[], String[], int[])}. */ - final int mTerminalFileDescriptor; + private int mTerminalFileDescriptor; /** Set by the application for user identification of session, not by terminal. */ public String mSessionName; @@ -128,20 +131,26 @@ public final class TerminalSession extends TerminalOutput { } }; + private final String mShellPath; + private final String mCwd; + private final String[] mArgs; + private final String[] mEnv; + public TerminalSession(String shellPath, String cwd, String[] args, String[] env, SessionChangedCallback changeCallback) { mChangeCallback = changeCallback; - int[] processId = new int[1]; - mTerminalFileDescriptor = JNI.createSubprocess(shellPath, cwd, args, env, processId); - mShellPid = processId[0]; + this.mShellPath = shellPath; + this.mCwd = cwd; + this.mArgs = args; + this.mEnv = env; } /** Inform the attached pty of the new size and reflow or initialize the emulator. */ public void updateSize(int columns, int rows) { - JNI.setPtyWindowSize(mTerminalFileDescriptor, rows, columns); if (mEmulator == null) { initializeEmulator(columns, rows); } else { + JNI.setPtyWindowSize(mTerminalFileDescriptor, rows, columns); mEmulator.resize(columns, rows); } } @@ -161,6 +170,11 @@ public final class TerminalSession extends TerminalOutput { */ public void initializeEmulator(int columns, int rows) { mEmulator = new TerminalEmulator(this, columns, rows, /* transcript= */5000); + + int[] processId = new int[1]; + mTerminalFileDescriptor = JNI.createSubprocess(mShellPath, mCwd, mArgs, mEnv, processId, rows, columns); + mShellPid = processId[0]; + final FileDescriptor terminalFileDescriptorWrapped = wrapFileDescriptor(mTerminalFileDescriptor); new Thread("TermSessionInputReader[pid=" + mShellPid + "]") { @@ -204,7 +218,7 @@ public final class TerminalSession extends TerminalOutput { /** Write data to the shell process. */ @Override public void write(byte[] data, int offset, int count) { - mTerminalToProcessIOQueue.write(data, offset, count); + if (mShellPid > 0) mTerminalToProcessIOQueue.write(data, offset, count); } /** Write the Unicode code point to the terminal encoded in UTF-8. */ diff --git a/app/src/main/jni/termux.c b/app/src/main/jni/termux.c index ff3a31e0..6c5d8068 100644 --- a/app/src/main/jni/termux.c +++ b/app/src/main/jni/termux.c @@ -22,7 +22,14 @@ static int throw_runtime_exception(JNIEnv* env, char const* message) return -1; } -static int create_subprocess(JNIEnv* env, char const* cmd, char const* cwd, char* const argv[], char** envp, int* pProcessId) +static int create_subprocess(JNIEnv* env, + char const* cmd, + char const* cwd, + char* const argv[], + char** envp, + int* pProcessId, + jint rows, + jint columns) { int ptm = open("/dev/ptmx", O_RDWR | O_CLOEXEC); if (ptm < 0) return throw_runtime_exception(env, "Cannot open /dev/ptmx"); @@ -49,8 +56,8 @@ static int create_subprocess(JNIEnv* env, char const* cmd, char const* cwd, char tios.c_iflag &= ~(IXON | IXOFF); tcsetattr(ptm, TCSANOW, &tios); - /** Set initial winsize (better too small than too large). */ - struct winsize sz = { .ws_row = 20, .ws_col = 20 }; + /** Set initial winsize. */ + struct winsize sz = { .ws_row = rows, .ws_col = columns }; ioctl(ptm, TIOCSWINSZ, &sz); pid_t pid = fork(); @@ -105,7 +112,16 @@ static int create_subprocess(JNIEnv* env, char const* cmd, char const* cwd, char } } -JNIEXPORT jint JNICALL Java_com_termux_terminal_JNI_createSubprocess(JNIEnv* env, jclass TERMUX_UNUSED(clazz), jstring cmd, jstring cwd, jobjectArray args, jobjectArray envVars, jintArray processIdArray) +JNIEXPORT jint JNICALL Java_com_termux_terminal_JNI_createSubprocess( + JNIEnv* env, + jclass TERMUX_UNUSED(clazz), + jstring cmd, + jstring cwd, + jobjectArray args, + jobjectArray envVars, + jintArray processIdArray, + jint rows, + jint columns) { jsize size = args ? (*env)->GetArrayLength(env, args) : 0; char** argv = NULL; @@ -140,7 +156,7 @@ JNIEXPORT jint JNICALL Java_com_termux_terminal_JNI_createSubprocess(JNIEnv* env int procId = 0; char const* cmd_cwd = (*env)->GetStringUTFChars(env, cwd, NULL); char const* cmd_utf8 = (*env)->GetStringUTFChars(env, cmd, NULL); - int ptm = create_subprocess(env, cmd_utf8, cmd_cwd, argv, envp, &procId); + int ptm = create_subprocess(env, cmd_utf8, cmd_cwd, argv, envp, &procId, rows, columns); (*env)->ReleaseStringUTFChars(env, cmd, cmd_utf8); (*env)->ReleaseStringUTFChars(env, cmd, cmd_cwd); diff --git a/app/src/main/jniLibs/armeabi-v7a/libtermux.so b/app/src/main/jniLibs/armeabi-v7a/libtermux.so deleted file mode 100755 index 3e66676607907f93431ea172ddc067ff76ea0b5f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13288 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 SKx`1MGGJhs3t~VqOdS9R60YF@ diff --git a/app/src/main/jniLibs/x86/libtermux.so b/app/src/main/jniLibs/x86/libtermux.so deleted file mode 100755 index aef93ad0902b1e9dce7a279617a09af5fbfe921a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9084 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 Ld|-T#Jctbd&X1+I