Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce7ad530cd | ||
|
|
d0015cbe82 | ||
|
|
9e19217f8f | ||
|
|
048af64093 | ||
|
|
a8f7bf1b6e | ||
|
|
62e229e184 | ||
|
|
36e4d94093 | ||
|
|
d2c9c5a0f0 | ||
|
|
6405180cb8 | ||
|
|
1b6919bb23 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -5,6 +5,7 @@
|
|||||||
# Built application files
|
# Built application files
|
||||||
build/
|
build/
|
||||||
*.apk
|
*.apk
|
||||||
|
*.so
|
||||||
|
|
||||||
# Crashlytics configuations
|
# Crashlytics configuations
|
||||||
com_crashlytics_export_strings.xml
|
com_crashlytics_export_strings.xml
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ Released under [the GPLv3 license](https://www.gnu.org/licenses/gpl.html). Conta
|
|||||||
|
|
||||||
Building JNI libraries
|
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
|
Terminal resources
|
||||||
==================
|
==================
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ android {
|
|||||||
applicationId "com.termux"
|
applicationId "com.termux"
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 23
|
targetSdkVersion 23
|
||||||
versionCode 27
|
versionCode 29
|
||||||
versionName "0.27"
|
versionName "0.29"
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
|
|||||||
@@ -525,6 +525,10 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
|||||||
}
|
}
|
||||||
|
|
||||||
registerReceiver(mBroadcastReceiever, new IntentFilter(RELOAD_STYLE_ACTION));
|
registerReceiver(mBroadcastReceiever, new IntentFilter(RELOAD_STYLE_ACTION));
|
||||||
|
|
||||||
|
// The current terminal session may have changed while being away, force
|
||||||
|
// a refresh of the displayed terminal:
|
||||||
|
mTerminalView.onScreenUpdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ final class TermuxInstaller {
|
|||||||
if (arch.startsWith("arm") || arch.equals("aarch64")) {
|
if (arch.startsWith("arm") || arch.equals("aarch64")) {
|
||||||
// Handle different arm variants such as armv7l:
|
// Handle different arm variants such as armv7l:
|
||||||
arch = "arm";
|
arch = "arm";
|
||||||
} else if (arch.equals("x86_64")) {
|
} else if (arch.startsWith("x86")) { // "x86" on arcwelder, "x86_64" on 64-bit android.
|
||||||
arch = "i686";
|
arch = "i686";
|
||||||
}
|
}
|
||||||
return new URL("https://termux.net/bootstrap/bootstrap-" + arch + ".zip");
|
return new URL("https://termux.net/bootstrap/bootstrap-" + arch + ".zip");
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import android.database.Cursor;
|
|||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
|
import android.webkit.MimeTypeMap;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
@@ -15,7 +16,7 @@ import java.io.FileNotFoundException;
|
|||||||
public class TermuxFilePickerProvider extends ContentProvider {
|
public class TermuxFilePickerProvider extends ContentProvider {
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreate() {
|
public boolean onCreate() {
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -25,7 +26,20 @@ public class TermuxFilePickerProvider extends ContentProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getType(@NonNull Uri uri) {
|
public String getType(@NonNull Uri uri) {
|
||||||
return null;
|
String contentType = null;
|
||||||
|
String path = uri.getPath();
|
||||||
|
int lastDotIndex = path.lastIndexOf('.');
|
||||||
|
String possibleFileExtension = path.substring(lastDotIndex + 1, path.length());
|
||||||
|
if (possibleFileExtension.contains("/")) {
|
||||||
|
// The dot was in the path, so not a file extension.
|
||||||
|
} else {
|
||||||
|
MimeTypeMap mimeTypes = MimeTypeMap.getSingleton();
|
||||||
|
// Lower casing makes it work with e.g. "JPG":
|
||||||
|
contentType = mimeTypes.getMimeTypeFromExtension(possibleFileExtension.toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contentType == null) contentType = "application/octet-stream";
|
||||||
|
return contentType;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -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
|
* @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.
|
* 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. */
|
/** 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);
|
public static native void setPtyWindowSize(int fd, int rows, int cols);
|
||||||
|
|||||||
@@ -82,14 +82,17 @@ public final class TerminalSession extends TerminalOutput {
|
|||||||
/** Callback which gets notified when a session finishes or changes title. */
|
/** Callback which gets notified when a session finishes or changes title. */
|
||||||
final SessionChangedCallback mChangeCallback;
|
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 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
|
* The file descriptor referencing the master half of a pseudo-terminal pair, resulting from calling
|
||||||
* {@link JNI#createSubprocess(String, String, String[], String[], int[])}.
|
* {@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. */
|
/** Set by the application for user identification of session, not by terminal. */
|
||||||
public String mSessionName;
|
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) {
|
public TerminalSession(String shellPath, String cwd, String[] args, String[] env, SessionChangedCallback changeCallback) {
|
||||||
mChangeCallback = changeCallback;
|
mChangeCallback = changeCallback;
|
||||||
|
|
||||||
int[] processId = new int[1];
|
this.mShellPath = shellPath;
|
||||||
mTerminalFileDescriptor = JNI.createSubprocess(shellPath, cwd, args, env, processId);
|
this.mCwd = cwd;
|
||||||
mShellPid = processId[0];
|
this.mArgs = args;
|
||||||
|
this.mEnv = env;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Inform the attached pty of the new size and reflow or initialize the emulator. */
|
/** Inform the attached pty of the new size and reflow or initialize the emulator. */
|
||||||
public void updateSize(int columns, int rows) {
|
public void updateSize(int columns, int rows) {
|
||||||
JNI.setPtyWindowSize(mTerminalFileDescriptor, rows, columns);
|
|
||||||
if (mEmulator == null) {
|
if (mEmulator == null) {
|
||||||
initializeEmulator(columns, rows);
|
initializeEmulator(columns, rows);
|
||||||
} else {
|
} else {
|
||||||
|
JNI.setPtyWindowSize(mTerminalFileDescriptor, rows, columns);
|
||||||
mEmulator.resize(columns, rows);
|
mEmulator.resize(columns, rows);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -161,6 +170,11 @@ public final class TerminalSession extends TerminalOutput {
|
|||||||
*/
|
*/
|
||||||
public void initializeEmulator(int columns, int rows) {
|
public void initializeEmulator(int columns, int rows) {
|
||||||
mEmulator = new TerminalEmulator(this, columns, rows, /* transcript= */5000);
|
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);
|
final FileDescriptor terminalFileDescriptorWrapped = wrapFileDescriptor(mTerminalFileDescriptor);
|
||||||
|
|
||||||
new Thread("TermSessionInputReader[pid=" + mShellPid + "]") {
|
new Thread("TermSessionInputReader[pid=" + mShellPid + "]") {
|
||||||
@@ -204,7 +218,7 @@ public final class TerminalSession extends TerminalOutput {
|
|||||||
/** Write data to the shell process. */
|
/** Write data to the shell process. */
|
||||||
@Override
|
@Override
|
||||||
public void write(byte[] data, int offset, int count) {
|
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. */
|
/** Write the Unicode code point to the terminal encoded in UTF-8. */
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ import java.util.Properties;
|
|||||||
public final class TerminalView extends View {
|
public final class TerminalView extends View {
|
||||||
|
|
||||||
/** Log view key and IME events. */
|
/** Log view key and IME events. */
|
||||||
private static final boolean LOG_KEY_EVENTS = true;
|
private static final boolean LOG_KEY_EVENTS = false;
|
||||||
|
|
||||||
/** The currently displayed terminal session, whose emulator is {@link #mEmulator}. */
|
/** The currently displayed terminal session, whose emulator is {@link #mEmulator}. */
|
||||||
TerminalSession mTermSession;
|
TerminalSession mTermSession;
|
||||||
|
|||||||
@@ -22,7 +22,14 @@ static int throw_runtime_exception(JNIEnv* env, char const* message)
|
|||||||
return -1;
|
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);
|
int ptm = open("/dev/ptmx", O_RDWR | O_CLOEXEC);
|
||||||
if (ptm < 0) return throw_runtime_exception(env, "Cannot open /dev/ptmx");
|
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);
|
tios.c_iflag &= ~(IXON | IXOFF);
|
||||||
tcsetattr(ptm, TCSANOW, &tios);
|
tcsetattr(ptm, TCSANOW, &tios);
|
||||||
|
|
||||||
/** Set initial winsize (better too small than too large). */
|
/** Set initial winsize. */
|
||||||
struct winsize sz = { .ws_row = 20, .ws_col = 20 };
|
struct winsize sz = { .ws_row = rows, .ws_col = columns };
|
||||||
ioctl(ptm, TIOCSWINSZ, &sz);
|
ioctl(ptm, TIOCSWINSZ, &sz);
|
||||||
|
|
||||||
pid_t pid = fork();
|
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;
|
jsize size = args ? (*env)->GetArrayLength(env, args) : 0;
|
||||||
char** argv = NULL;
|
char** argv = NULL;
|
||||||
@@ -140,7 +156,7 @@ JNIEXPORT jint JNICALL Java_com_termux_terminal_JNI_createSubprocess(JNIEnv* env
|
|||||||
int procId = 0;
|
int procId = 0;
|
||||||
char const* cmd_cwd = (*env)->GetStringUTFChars(env, cwd, NULL);
|
char const* cmd_cwd = (*env)->GetStringUTFChars(env, cwd, NULL);
|
||||||
char const* cmd_utf8 = (*env)->GetStringUTFChars(env, cmd, 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_utf8);
|
||||||
(*env)->ReleaseStringUTFChars(env, cmd, cmd_cwd);
|
(*env)->ReleaseStringUTFChars(env, cmd, cmd_cwd);
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -12,7 +12,7 @@
|
|||||||
<string name="help">Help</string>
|
<string name="help">Help</string>
|
||||||
|
|
||||||
<string name="welcome_dialog_title">Welcome to Termux</string>
|
<string name="welcome_dialog_title">Welcome to Termux</string>
|
||||||
<string name="welcome_dialog_body">Long press anywhere on the terminal for a context menu where Help is available.\n\nExecute \'apt update\' to update the packages list before installing packages.</string>
|
<string name="welcome_dialog_body">Long press and select <i>More…</i> to show a menu where <i>Help</i> is available.\n\nExecute <b>apt update</b> to update the packages list before installing packages.</string>
|
||||||
<string name="welcome_dialog_dont_show_again_button">Do not show again</string>
|
<string name="welcome_dialog_dont_show_again_button">Do not show again</string>
|
||||||
|
|
||||||
<string name="bootstrap_installer_body">Installing…</string>
|
<string name="bootstrap_installer_body">Installing…</string>
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ public class TerminalTest extends TerminalTestCase {
|
|||||||
assertEnteringStringGivesResponse("\033[6n", "\033[2;1R");
|
assertEnteringStringGivesResponse("\033[6n", "\033[2;1R");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Test the cursor shape changes using DECSCUSR. */
|
||||||
public void testSetCursorStyle() throws Exception {
|
public void testSetCursorStyle() throws Exception {
|
||||||
withTerminalSized(5, 5);
|
withTerminalSized(5, 5);
|
||||||
assertEquals(TerminalEmulator.CURSOR_STYLE_BLOCK, mTerminal.getCursorStyle());
|
assertEquals(TerminalEmulator.CURSOR_STYLE_BLOCK, mTerminal.getCursorStyle());
|
||||||
|
|||||||
Reference in New Issue
Block a user