Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ca055bb25 | ||
|
|
ce7ad530cd | ||
|
|
d0015cbe82 | ||
|
|
9e19217f8f | ||
|
|
048af64093 | ||
|
|
a8f7bf1b6e | ||
|
|
62e229e184 | ||
|
|
36e4d94093 | ||
|
|
d2c9c5a0f0 | ||
|
|
6405180cb8 | ||
|
|
1b6919bb23 | ||
|
|
e52cd2dd41 | ||
|
|
54857d5fd4 | ||
|
|
38dd99e827 | ||
|
|
7256b04317 | ||
|
|
01a1c6de0f | ||
|
|
497fc3ecd0 | ||
|
|
b2b39abacd | ||
|
|
bee305e53f | ||
|
|
c8d2f28ed8 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -5,6 +5,7 @@
|
||||
# Built application files
|
||||
build/
|
||||
*.apk
|
||||
*.so
|
||||
|
||||
# Crashlytics configuations
|
||||
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
|
||||
======================
|
||||
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
|
||||
==================
|
||||
|
||||
@@ -18,8 +18,8 @@ android {
|
||||
applicationId "com.termux"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 23
|
||||
versionCode 26
|
||||
versionName "0.26"
|
||||
versionCode 30
|
||||
versionName "0.30"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
android:sharedUserId="com.termux"
|
||||
android:sharedUserLabel="@string/shared_user_label" >
|
||||
|
||||
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
|
||||
<uses-feature android:name="android.software.leanback" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
|
||||
<uses-feature android:name="android.software.leanback" android:required="false" />
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
@@ -30,10 +30,10 @@
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
|
||||
@@ -525,6 +525,10 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -182,7 +182,7 @@ final class TermuxInstaller {
|
||||
if (arch.startsWith("arm") || arch.equals("aarch64")) {
|
||||
// Handle different arm variants such as armv7l:
|
||||
arch = "arm";
|
||||
} else if (arch.equals("x86_64")) {
|
||||
} else if (arch.startsWith("x86")) { // "x86" on arcwelder, "x86_64" on 64-bit android.
|
||||
arch = "i686";
|
||||
}
|
||||
return new URL("https://termux.net/bootstrap/bootstrap-" + arch + ".zip");
|
||||
|
||||
@@ -132,7 +132,7 @@ final class TermuxPreferences {
|
||||
break;
|
||||
}
|
||||
|
||||
mBackIsEscape = "escape".equals(props.getProperty("back-key", "escape"));
|
||||
mBackIsEscape = "escape".equals(props.getProperty("back-key", "back"));
|
||||
} catch (Exception e) {
|
||||
Toast.makeText(context, "Error loading properties: " + e.getMessage(), Toast.LENGTH_LONG).show();
|
||||
Log.e("termux", "Error loading props", e);
|
||||
|
||||
@@ -7,6 +7,7 @@ import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
@@ -15,7 +16,7 @@ import java.io.FileNotFoundException;
|
||||
public class TermuxFilePickerProvider extends ContentProvider {
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -25,7 +26,20 @@ public class TermuxFilePickerProvider extends ContentProvider {
|
||||
|
||||
@Override
|
||||
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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -218,7 +218,7 @@ public final class TerminalEmulator {
|
||||
*/
|
||||
private int mScrollCounter = 0;
|
||||
|
||||
private int mUtf8ToFollow, mUtf8Index;
|
||||
private byte mUtf8ToFollow, mUtf8Index;
|
||||
private final byte[] mUtf8InputBuffer = new byte[4];
|
||||
|
||||
public final TerminalColors mColors = new TerminalColors();
|
||||
@@ -424,7 +424,11 @@ public final class TerminalEmulator {
|
||||
processCodePoint(/* escape (hexadecimal=0x1B, octal=033): */27);
|
||||
processCodePoint((codePoint & 0x7F) + 0x40);
|
||||
} else {
|
||||
if (Character.UNASSIGNED == Character.getType(codePoint)) codePoint = UNICODE_REPLACEMENT_CHAR;
|
||||
switch (Character.getType(codePoint)) {
|
||||
case Character.UNASSIGNED:
|
||||
case Character.SURROGATE:
|
||||
codePoint = UNICODE_REPLACEMENT_CHAR;
|
||||
}
|
||||
processCodePoint(codePoint);
|
||||
}
|
||||
}
|
||||
@@ -470,16 +474,19 @@ public final class TerminalEmulator {
|
||||
else
|
||||
mSession.onBell();
|
||||
break;
|
||||
case 8: // BS
|
||||
setCursorCol(Math.max(mLeftMargin, mCursorCol - 1));
|
||||
break;
|
||||
case 9: // Horizontal tab - move to next tab stop, but not past edge of screen
|
||||
int nextTabStop = nextTabStop(1);
|
||||
while (mCursorCol < nextTabStop) {
|
||||
// Emit newlines to get background color right.
|
||||
processCodePoint(' ');
|
||||
}
|
||||
break;
|
||||
case 8: // Backspace (BS, ^H).
|
||||
setCursorCol(Math.max(mLeftMargin, mCursorCol - 1));
|
||||
break;
|
||||
case 9: // Horizontal tab (HT, \t) - move to next tab stop, but not past edge of screen
|
||||
// XXX: Should perhaps use color if writing to new cells. Try with
|
||||
// printf "\033[41m\tXX\033[0m\n"
|
||||
// The OSX Terminal.app colors the spaces from the tab red, but xterm does not.
|
||||
// Note that Terminal.app only colors on new cells, in e.g.
|
||||
// printf "\033[41m\t\r\033[42m\tXX\033[0m\n"
|
||||
// the first cells are created with a red background, but when tabbing over
|
||||
// them again with a green background they are not overwritten.
|
||||
mCursorCol = nextTabStop(1);
|
||||
break;
|
||||
case 10: // Line feed (LF, \n).
|
||||
case 11: // Vertical tab (VT, \v).
|
||||
case 12: // Form feed (FF, \f).
|
||||
@@ -907,8 +914,9 @@ public final class TerminalEmulator {
|
||||
/** Process byte while in the {@link #ESC_CSI_QUESTIONMARK} escape state. */
|
||||
private void doCsiQuestionMark(int b) {
|
||||
switch (b) {
|
||||
case 'J': // Selective erase in display (DECSED - http://www.vt100.net/docs/vt510-rm/DECSED).
|
||||
case 'K': // Selective erase in line (DECSEL - http://vt100.net/docs/vt510-rm/DECSEL).
|
||||
case 'J': // Selective erase in display (DECSED) - http://www.vt100.net/docs/vt510-rm/DECSED.
|
||||
case 'K': // Selective erase in line (DECSEL) - http://vt100.net/docs/vt510-rm/DECSEL.
|
||||
mAboutToAutoWrap = false;
|
||||
int fillChar = ' ';
|
||||
int startCol = -1;
|
||||
int startRow = -1;
|
||||
@@ -1229,6 +1237,11 @@ public final class TerminalEmulator {
|
||||
mScreen.blockSet(mRightMargin - 1, mTopMargin, 1, rows, ' ', TextStyle.encode(mForeColor, mBackColor, 0));
|
||||
}
|
||||
break;
|
||||
case 'c': // RIS - Reset to Initial State (http://vt100.net/docs/vt510-rm/RIS).
|
||||
reset();
|
||||
blockClear(0, 0, mColumns, mRows);
|
||||
setCursorPosition(0, 0);
|
||||
break;
|
||||
case 'D': // INDEX
|
||||
doLinefeed();
|
||||
break;
|
||||
@@ -1321,9 +1334,8 @@ public final class TerminalEmulator {
|
||||
continueSequence(ESC_CSI_ARGS_ASTERIX);
|
||||
break;
|
||||
case '@': {
|
||||
// ESC [ Pn @ - ICH Insert Characters.
|
||||
// "This control function inserts one or more space (SP) characters starting at the cursor position."
|
||||
// http://www.vt100.net/docs/vt510-rm/ICH
|
||||
// "CSI{n}@" - Insert ${n} space characters (ICH) - http://www.vt100.net/docs/vt510-rm/ICH.
|
||||
mAboutToAutoWrap = false;
|
||||
int columnsAfterCursor = mColumns - mCursorCol;
|
||||
int spacesToInsert = Math.min(getArg0(1), columnsAfterCursor);
|
||||
int charsToMove = columnsAfterCursor - spacesToInsert;
|
||||
@@ -1360,7 +1372,7 @@ public final class TerminalEmulator {
|
||||
case 'I': // Cursor Horizontal Forward Tabulation (CHT). Move the active position n tabs forward.
|
||||
setCursorCol(nextTabStop(getArg0(1)));
|
||||
break;
|
||||
case 'J': // ESC [ Pn J - ED - Erase in Display
|
||||
case 'J': // "${CSI}${0,1,2}J" - Erase in Display (ED)
|
||||
// ED ignores the scrolling margins.
|
||||
switch (getArg0(0)) {
|
||||
case 0: // Erase from the active position to the end of the screen, inclusive (default).
|
||||
@@ -1377,8 +1389,9 @@ public final class TerminalEmulator {
|
||||
break;
|
||||
default:
|
||||
unknownSequence(b);
|
||||
break;
|
||||
return;
|
||||
}
|
||||
mAboutToAutoWrap = false;
|
||||
break;
|
||||
case 'K': // "CSI{n}K" - Erase in line (EL).
|
||||
switch (getArg0(0)) {
|
||||
@@ -1393,8 +1406,9 @@ public final class TerminalEmulator {
|
||||
break;
|
||||
default:
|
||||
unknownSequence(b);
|
||||
break;
|
||||
return;
|
||||
}
|
||||
mAboutToAutoWrap = false;
|
||||
break;
|
||||
case 'L': // "${CSI}{N}L" - insert ${N} lines (IL).
|
||||
{
|
||||
@@ -1407,6 +1421,7 @@ public final class TerminalEmulator {
|
||||
break;
|
||||
case 'M': // "${CSI}${N}M" - delete N lines (DL).
|
||||
{
|
||||
mAboutToAutoWrap = false;
|
||||
int linesAfterCursor = mBottomMargin - mCursorRow;
|
||||
int linesToDelete = Math.min(getArg0(1), linesAfterCursor);
|
||||
int linesToMove = linesAfterCursor - linesToDelete;
|
||||
@@ -1421,6 +1436,7 @@ public final class TerminalEmulator {
|
||||
// As characters are deleted, the remaining characters between the cursor and right margin move to the left.
|
||||
// Character attributes move with the characters. The terminal adds blank spaces with no visual character
|
||||
// attributes at the right margin. DCH has no effect outside the scrolling margins."
|
||||
mAboutToAutoWrap = false;
|
||||
int cellsAfterCursor = mColumns - mCursorCol;
|
||||
int cellsToDelete = Math.min(getArg0(1), cellsAfterCursor);
|
||||
int cellsToMove = cellsAfterCursor - cellsToDelete;
|
||||
@@ -1451,6 +1467,7 @@ public final class TerminalEmulator {
|
||||
}
|
||||
break;
|
||||
case 'X': // "${CSI}${N}X" - Erase ${N:=1} character(s) (ECH). FIXME: Clears character attributes?
|
||||
mAboutToAutoWrap = false;
|
||||
mScreen.blockSet(mCursorCol, mCursorRow, Math.min(getArg0(1), mColumns - mCursorCol), 1, ' ', getStyle());
|
||||
break;
|
||||
case 'Z': // Cursor Backward Tabulation (CBT). Move the active position n tabs backward.
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -10,6 +10,7 @@ import android.graphics.Canvas;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Typeface;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Build;
|
||||
import android.text.InputType;
|
||||
import android.text.TextUtils;
|
||||
@@ -46,7 +47,7 @@ import java.util.Properties;
|
||||
public final class TerminalView extends View {
|
||||
|
||||
/** 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}. */
|
||||
TerminalSession mTermSession;
|
||||
@@ -692,6 +693,10 @@ public final class TerminalView extends View {
|
||||
// As left alt+b, jumping forward in readline:
|
||||
codePoint = 'b';
|
||||
leftAltDownFromEvent = true;
|
||||
} else if (codePoint == 'v' || codePoint == 'V') {
|
||||
codePoint = -1;
|
||||
AudioManager audio = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
|
||||
audio.adjustSuggestedStreamVolume(AudioManager.ADJUST_SAME, AudioManager.USE_DEFAULT_STREAM_TYPE, AudioManager.FLAG_SHOW_UI);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -12,7 +12,7 @@
|
||||
<string name="help">Help</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="bootstrap_installer_body">Installing…</string>
|
||||
|
||||
@@ -20,4 +20,13 @@ public class ControlSequenceIntroducerTest extends TerminalTestCase {
|
||||
withTerminalSized(3, 4).enterString("1\r\n2\r\n3\r\nhi\033[Sy").assertLinesAre("2 ", "3 ", "hi ", " y");
|
||||
}
|
||||
|
||||
/** CSI Ps X Erase Ps Character(s) (default = 1) (ECH). */
|
||||
public void testCsiX() {
|
||||
// See https://code.google.com/p/chromium/issues/detail?id=212712 where test was extraced from.
|
||||
withTerminalSized(13, 2).enterString("abcdefghijkl\b\b\b\b\b\033[X").assertLinesAre("abcdefg ijkl ", " ");
|
||||
withTerminalSized(13, 2).enterString("abcdefghijkl\b\b\b\b\b\033[1X").assertLinesAre("abcdefg ijkl ", " ");
|
||||
withTerminalSized(13, 2).enterString("abcdefghijkl\b\b\b\b\b\033[2X").assertLinesAre("abcdefg jkl ", " ");
|
||||
withTerminalSized(13, 2).enterString("abcdefghijkl\b\b\b\b\b\033[20X").assertLinesAre("abcdefg ", " ");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -163,7 +163,12 @@ public class CursorAndScreenTest extends TerminalTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
public void testHorizontalTabColorsBackground() {
|
||||
/**
|
||||
* See comments on horizontal tab handling in TerminalEmulator.java.
|
||||
*
|
||||
* We do not want to color already written cells when tabbing over them.
|
||||
*/
|
||||
public void DISABLED_testHorizontalTabColorsBackground() {
|
||||
withTerminalSized(10, 3).enterString("\033[48;5;15m").enterString("\t");
|
||||
assertCursorAt(0, 8);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
@@ -172,4 +177,45 @@ public class CursorAndScreenTest extends TerminalTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test interactions between the cursor overflow bit and various escape sequences.
|
||||
* <p/>
|
||||
* Adapted from hterm:
|
||||
* https://chromium.googlesource.com/chromiumos/platform/assets/+/2337afa5c063127d5ce40ec7fec9b602d096df86%5E%21/#F2
|
||||
*/
|
||||
public void testClearingOfAutowrap() {
|
||||
// Fill a row with the last hyphen wrong, then run a command that
|
||||
// modifies the screen, then add a hyphen. The wrap bit should be
|
||||
// cleared, so the extra hyphen can fix the row.
|
||||
withTerminalSized(15, 6);
|
||||
|
||||
enterString("----- 1 ----X");
|
||||
enterString("\033[K-"); // EL
|
||||
|
||||
enterString("----- 2 ----X");
|
||||
enterString("\033[J-"); // ED
|
||||
|
||||
enterString("----- 3 ----X");
|
||||
enterString("\033[@-"); // ICH
|
||||
|
||||
enterString("----- 4 ----X");
|
||||
enterString("\033[P-"); // DCH
|
||||
|
||||
enterString("----- 5 ----X");
|
||||
enterString("\033[X-"); // ECH
|
||||
|
||||
// DL will delete the entire line but clear the wrap bit, so we
|
||||
// expect a hyphen at the end and nothing else.
|
||||
enterString("XXXXXXXXXXXXXXX");
|
||||
enterString("\033[M-"); // DL
|
||||
|
||||
assertLinesAre(
|
||||
"----- 1 -----",
|
||||
"----- 2 -----",
|
||||
"----- 3 -----",
|
||||
"----- 4 -----",
|
||||
"----- 5 -----",
|
||||
" -");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,6 +28,11 @@ public class DecSetTest extends TerminalTestCase {
|
||||
assertFalse(mTerminal.isShowingCursor());
|
||||
mTerminal.reset();
|
||||
assertTrue("Resetting the terminal should show the cursor", mTerminal.isShowingCursor());
|
||||
|
||||
enterString("\033[?25l");
|
||||
assertFalse(mTerminal.isShowingCursor());
|
||||
enterString("\033c"); // RIS resetting should reveal cursor.
|
||||
assertTrue(mTerminal.isShowingCursor());
|
||||
}
|
||||
|
||||
/** DECSET 2004, controls bracketed paste mode. */
|
||||
|
||||
@@ -92,6 +92,7 @@ public class TerminalTest extends TerminalTestCase {
|
||||
assertEnteringStringGivesResponse("\033[6n", "\033[2;1R");
|
||||
}
|
||||
|
||||
/** Test the cursor shape changes using DECSCUSR. */
|
||||
public void testSetCursorStyle() throws Exception {
|
||||
withTerminalSized(5, 5);
|
||||
assertEquals(TerminalEmulator.CURSOR_STYLE_BLOCK, mTerminal.getCursorStyle());
|
||||
@@ -257,4 +258,9 @@ public class TerminalTest extends TerminalTestCase {
|
||||
withTerminalSized(3, 3).enterString("abc\r ").assertLinesAre(" bc", " ", " ").assertCursorAt(0, 1);
|
||||
}
|
||||
|
||||
public void testTab() {
|
||||
withTerminalSized(11, 2).enterString("01234567890\r\tXX").assertLinesAre("01234567XX0", " ");
|
||||
withTerminalSized(11, 2).enterString("01234567890\033[44m\r\tXX").assertLinesAre("01234567XX0", " ");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,6 +12,47 @@ public class UnicodeInputTest extends TerminalTestCase {
|
||||
withTerminalSized(5, 5);
|
||||
mTerminal.append(new byte[]{(byte) 0b11101111, (byte) 'a'}, 2);
|
||||
assertLineIs(0, ((char) TerminalEmulator.UNICODE_REPLACEMENT_CHAR) + "a ");
|
||||
|
||||
// https://code.google.com/p/chromium/issues/detail?id=212704
|
||||
byte[] input = new byte[]{
|
||||
(byte) 0x61, (byte) 0xF1,
|
||||
(byte) 0x80, (byte) 0x80,
|
||||
(byte) 0xe1, (byte) 0x80,
|
||||
(byte) 0xc2, (byte) 0x62,
|
||||
(byte) 0x80, (byte) 0x63,
|
||||
(byte) 0x80, (byte) 0xbf,
|
||||
(byte) 0x64
|
||||
};
|
||||
withTerminalSized(10, 2);
|
||||
mTerminal.append(input, input.length);
|
||||
assertLinesAre("a\uFFFD\uFFFD\uFFFDb\uFFFDc\uFFFD\uFFFDd", " ");
|
||||
|
||||
// Surrogate pairs.
|
||||
withTerminalSized(5, 2);
|
||||
input = new byte[]{
|
||||
(byte) 0xed, (byte) 0xa0,
|
||||
(byte) 0x80, (byte) 0xed,
|
||||
(byte) 0xad, (byte) 0xbf,
|
||||
(byte) 0xed, (byte) 0xae,
|
||||
(byte) 0x80, (byte) 0xed,
|
||||
(byte) 0xbf, (byte) 0xbf
|
||||
};
|
||||
mTerminal.append(input, input.length);
|
||||
assertLinesAre("\uFFFD\uFFFD\uFFFD\uFFFD ", " ");
|
||||
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=746900: "with this patch 0xe0 0x80 is decoded as two U+FFFDs,
|
||||
// but 0xe0 0xa0 is decoded as a single U+FFFD, and this is correct according to the "Best Practices", but IE
|
||||
// and Chrome (Version 22.0.1229.94) decode both of them as two U+FFFDs. Opera 12.11 decodes both of them as
|
||||
// one U+FFFD".
|
||||
withTerminalSized(5, 2);
|
||||
input = new byte[]{(byte) 0xe0, (byte) 0xa0, ' '};
|
||||
mTerminal.append(input, input.length);
|
||||
assertLinesAre("\uFFFD ", " ");
|
||||
|
||||
// withTerminalSized(5, 2);
|
||||
// input = new byte[]{(byte) 0xe0, (byte) 0x80, 'a'};
|
||||
// mTerminal.append(input, input.length);
|
||||
// assertLinesAre("\uFFFD\uFFFDa ", " ");
|
||||
}
|
||||
|
||||
public void testUnassignedCodePoint() throws UnsupportedEncodingException {
|
||||
|
||||
@@ -39,6 +39,7 @@ public class WcWidthTest extends TestCase {
|
||||
public void testCombining() {
|
||||
assertWidthIs(0, 0x0302);
|
||||
assertWidthIs(0, 0x0308);
|
||||
assertWidthIs(0, 0x2060);
|
||||
}
|
||||
|
||||
public void testWatch() {
|
||||
|
||||
Reference in New Issue
Block a user