diff --git a/.travis.yml b/.travis.yml index 8c09f5317f..4727539309 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,7 @@ android: components: - platform-tools - tools - - build-tools-25.0.2 + - build-tools-26.0.0 - android-25 - extra-android-m2repository diff --git a/README.md b/README.md index 28691c108d..45666bfccf 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ Termux app * [Termux on Google Play Store](https://play.google.com/store/apps/details?id=com.termux) * [Termux on F-Droid](https://f-droid.org/repository/browse/?fdid=com.termux) +* [Termux Wiki](https://wiki.termux.com/wiki/) * [Termux Help](http://termux.com/help/) * [Termux Google+ community](http://termux.com/community/) diff --git a/app/build.gradle b/app/build.gradle index 88e2be6011..901fa1c2cd 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'com.android.application' android { compileSdkVersion 25 - buildToolsVersion "25.0.2" + buildToolsVersion "26.0.0" dependencies { compile 'com.android.support:support-annotations:25.3.1' @@ -14,8 +14,8 @@ android { applicationId "com.termux" minSdkVersion 21 targetSdkVersion 25 - versionCode 48 - versionName "0.48" + versionCode 53 + versionName "0.53" } buildTypes { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d875d5abe7..aacc24448c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -13,7 +13,7 @@ + + + + + + + + + + + diff --git a/app/src/main/java/com/termux/app/BackgroundJob.java b/app/src/main/java/com/termux/app/BackgroundJob.java index bd76edc2fd..1ff2450eb4 100644 --- a/app/src/main/java/com/termux/app/BackgroundJob.java +++ b/app/src/main/java/com/termux/app/BackgroundJob.java @@ -116,8 +116,9 @@ public static String[] buildEnvironment(boolean failSafe, String cwd) { final String langEnv = "LANG=en_US.UTF-8"; final String pathEnv = "PATH=" + TermuxService.PREFIX_PATH + "/bin:" + TermuxService.PREFIX_PATH + "/bin/applets"; final String pwdEnv = "PWD=" + cwd; + final String tmpdirEnv = "TMPDIR=" + TermuxService.PREFIX_PATH + "/tmp"; - return new String[]{termEnv, homeEnv, prefixEnv, ps1Env, ldEnv, langEnv, pathEnv, pwdEnv, androidRootEnv, androidDataEnv, externalStorageEnv}; + return new String[]{termEnv, homeEnv, prefixEnv, ps1Env, ldEnv, langEnv, pathEnv, pwdEnv, androidRootEnv, androidDataEnv, externalStorageEnv, tmpdirEnv}; } } diff --git a/app/src/main/java/com/termux/app/TermuxActivity.java b/app/src/main/java/com/termux/app/TermuxActivity.java index 8e6c7255db..c869ffeb88 100644 --- a/app/src/main/java/com/termux/app/TermuxActivity.java +++ b/app/src/main/java/com/termux/app/TermuxActivity.java @@ -397,7 +397,6 @@ public void onSessionFinished(final TerminalSession finishedSession) { @Override public void onClipboardText(TerminalSession session, String text) { if (!mIsVisible) return; - showToast("Clipboard:\n\"" + text + "\"", false); ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); clipboard.setPrimaryClip(new ClipData(null, new String[]{"text/plain"}, new ClipData.Item(text))); } diff --git a/build.gradle b/build.gradle index 3ee2d98eba..ee9a47abee 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.3.1' + classpath 'com.android.tools.build:gradle:2.3.3' } } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index eac8c4a1e1..7a3265ee94 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index dd9949e124..f16d26666b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Mon Mar 06 01:34:12 CET 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-bin.zip diff --git a/gradlew b/gradlew index 4453ccea33..cccdd3d517 100755 --- a/gradlew +++ b/gradlew @@ -33,11 +33,11 @@ DEFAULT_JVM_OPTS="" # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" -warn ( ) { +warn () { echo "$*" } -die ( ) { +die () { echo echo "$*" echo @@ -155,7 +155,7 @@ if $cygwin ; then fi # Escape application args -save ( ) { +save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } diff --git a/terminal-emulator/build.gradle b/terminal-emulator/build.gradle index d10a39e15f..63ddeabee3 100644 --- a/terminal-emulator/build.gradle +++ b/terminal-emulator/build.gradle @@ -13,12 +13,12 @@ ext { libraryDescription = 'The terminal emulator used in Termux' siteUrl = 'https://github.com/termux/termux' gitUrl = 'https://github.com/termux/termux.git' - libraryVersion = '0.49' + libraryVersion = '0.50' } android { compileSdkVersion 25 - buildToolsVersion "25.0.2" + buildToolsVersion "26.0.0" defaultConfig { minSdkVersion 21 diff --git a/terminal-emulator/src/main/AndroidManifest.xml b/terminal-emulator/src/main/AndroidManifest.xml index b931189d9f..a293cb643b 100644 --- a/terminal-emulator/src/main/AndroidManifest.xml +++ b/terminal-emulator/src/main/AndroidManifest.xml @@ -1,3 +1,2 @@ - + diff --git a/terminal-emulator/src/main/java/com/termux/terminal/TerminalEmulator.java b/terminal-emulator/src/main/java/com/termux/terminal/TerminalEmulator.java index e172d38ea6..10c7321dd9 100644 --- a/terminal-emulator/src/main/java/com/termux/terminal/TerminalEmulator.java +++ b/terminal-emulator/src/main/java/com/termux/terminal/TerminalEmulator.java @@ -223,6 +223,7 @@ public final class TerminalEmulator { private byte mUtf8ToFollow, mUtf8Index; private final byte[] mUtf8InputBuffer = new byte[4]; + private int mLastEmittedCodePoint = -1; public final TerminalColors mColors = new TerminalColors(); @@ -419,10 +420,11 @@ private void processByte(byte byteToProcess) { mUtf8Index = mUtf8ToFollow = 0; if (codePoint >= 0x80 && codePoint <= 0x9F) { - // Sequence decoded to a C1 control character which is the same as escape followed by - // ((code & 0x7F) + 0x40). - processCodePoint(/* escape (hexadecimal=0x1B, octal=033): */27); - processCodePoint((codePoint & 0x7F) + 0x40); + // Sequence decoded to a C1 control character which we ignore. They are + // not used nowadays and increases the risk of messing up the terminal state + // on binary input. XTerm does not allow them in utf-8: + // "It is not possible to use a C1 control obtained from decoding the + // UTF-8 text" - http://invisible-island.net/xterm/ctlseqs/ctlseqs.html } else { switch (Character.getType(codePoint)) { case Character.UNASSIGNED: @@ -632,6 +634,7 @@ public void processCodePoint(int b) { int bottom = Math.min(getArg(2, mRows, true) + 1, effectiveBottomMargin - 1) + effectiveTopMargin; int right = Math.min(getArg(3, mColumns, true) + 1, effectiveRightMargin - 1) + effectiveLeftMargin; if (mArgIndex >= 4) { + if (mArgIndex >= mArgs.length) mArgIndex = mArgs.length - 1; for (int i = 4; i <= mArgIndex; i++) { int bits = 0; boolean setOrClear = true; // True if setting, false if clearing. @@ -965,6 +968,7 @@ private void doCsiQuestionMark(int b) { break; case 'h': case 'l': + if (mArgIndex >= mArgs.length) mArgIndex = mArgs.length - 1; for (int i = 0; i <= mArgIndex; i++) doDecSetOrReset(b == 'h', mArgs[i]); break; @@ -981,6 +985,7 @@ private void doCsiQuestionMark(int b) { break; case 'r': case 's': + if (mArgIndex >= mArgs.length) mArgIndex = mArgs.length - 1; for (int i = 0; i <= mArgIndex; i++) { int externalBit = mArgs[i]; int internalBit = mapDecSetBitToInternalBit(externalBit); @@ -1307,6 +1312,8 @@ private void saveCursor() { state.mSavedCursorRow = mCursorRow; state.mSavedCursorCol = mCursorCol; state.mSavedEffect = mEffect; + state.mSavedForeColor = mForeColor; + state.mSavedBackColor = mBackColor; state.mSavedDecFlags = mCurrentDecSetFlags; state.mUseLineDrawingG0 = mUseLineDrawingG0; state.mUseLineDrawingG1 = mUseLineDrawingG1; @@ -1318,6 +1325,8 @@ private void restoreCursor() { SavedScreenState state = (mScreen == mMainBuffer) ? mSavedStateMain : mSavedStateAlt; setCursorRowCol(state.mSavedCursorRow, state.mSavedCursorCol); mEffect = state.mSavedEffect; + mForeColor = state.mSavedForeColor; + mBackColor = state.mSavedBackColor; int mask = (DECSET_BIT_AUTOWRAP | DECSET_BIT_ORIGIN_MODE); mCurrentDecSetFlags = (mCurrentDecSetFlags & ~mask) | (state.mSavedDecFlags & mask); mUseLineDrawingG0 = state.mUseLineDrawingG0; @@ -1501,6 +1510,11 @@ private void doCsi(int b) { case '`': // Horizontal position absolute (HPA - http://www.vt100.net/docs/vt510-rm/HPA). setCursorColRespectingOriginMode(getArg0(1) - 1); break; + case 'b': // Repeat the preceding graphic character Ps times (REP). + if (mLastEmittedCodePoint == -1) break; + final int numRepeat = getArg0(1); + for (int i = 0; i < numRepeat; i++) emitCodePoint(mLastEmittedCodePoint); + break; case 'c': // Primary Device Attributes (http://www.vt100.net/docs/vt510-rm/DA1) if argument is missing or zero. // The important part that may still be used by some (tmux stores this value but does not currently use it) // is the first response parameter identifying the terminal service class, where we send 64 for "vt420". @@ -1566,6 +1580,7 @@ private void doCsi(int b) { // Also require that top + 2 <= bottom. mTopMargin = Math.max(0, Math.min(getArg0(1) - 1, mRows - 2)); mBottomMargin = Math.max(mTopMargin + 2, Math.min(getArg1(mRows), mRows)); + // DECSTBM moves the cursor to column 1, line 1 of the page respecting origin mode. setCursorPosition(0, 0); } @@ -1639,6 +1654,7 @@ private void doCsi(int b) { /** Select Graphic Rendition (SGR) - see http://en.wikipedia.org/wiki/ANSI_escape_code#graphics. */ private void selectGraphicRendition() { + if (mArgIndex >= mArgs.length) mArgIndex = mArgs.length - 1; for (int i = 0; i <= mArgIndex; i++) { int code = mArgs[i]; if (code < 0) { @@ -2049,6 +2065,7 @@ private void logError(String errorType) { buf.append(", escapeState="); buf.append(mEscapeState); boolean firstArg = true; + if (mArgIndex >= mArgs.length) mArgIndex = mArgs.length - 1; for (int i = 0; i <= mArgIndex; i++) { int value = mArgs[i]; if (value >= 0) { @@ -2081,6 +2098,7 @@ private void finishSequence() { * @param codePoint The code point of the character to display */ private void emitCodePoint(int codePoint) { + mLastEmittedCodePoint = codePoint; if (mUseLineDrawingUsesG0 ? mUseLineDrawingG0 : mUseLineDrawingG1) { // http://www.vt100.net/docs/vt102-ug/table5-15.html. switch (codePoint) { @@ -2263,8 +2281,8 @@ public void reset() { mBottomMargin = mRows; mRightMargin = mColumns; mAboutToAutoWrap = false; - mForeColor = TextStyle.COLOR_INDEX_FOREGROUND; - mBackColor = TextStyle.COLOR_INDEX_BACKGROUND; + mForeColor = mSavedStateMain.mSavedForeColor = mSavedStateAlt.mSavedForeColor = TextStyle.COLOR_INDEX_FOREGROUND; + mBackColor = mSavedStateMain.mSavedBackColor = mSavedStateAlt.mSavedBackColor = TextStyle.COLOR_INDEX_BACKGROUND; setDefaultTabStops(); mUseLineDrawingG0 = mUseLineDrawingG1 = false; @@ -2318,7 +2336,7 @@ public void paste(String text) { static final class SavedScreenState { /** Saved state of the cursor position, Used to implement the save/restore cursor position escape sequences. */ int mSavedCursorRow, mSavedCursorCol; - int mSavedEffect; + int mSavedEffect, mSavedForeColor, mSavedBackColor; int mSavedDecFlags; boolean mUseLineDrawingG0, mUseLineDrawingG1, mUseLineDrawingUsesG0 = true; } diff --git a/terminal-emulator/src/test/java/com/termux/terminal/ControlSequenceIntroducerTest.java b/terminal-emulator/src/test/java/com/termux/terminal/ControlSequenceIntroducerTest.java index 127b95390c..88b8e0269c 100644 --- a/terminal-emulator/src/test/java/com/termux/terminal/ControlSequenceIntroducerTest.java +++ b/terminal-emulator/src/test/java/com/termux/terminal/ControlSequenceIntroducerTest.java @@ -29,4 +29,21 @@ public void testCsiX() { withTerminalSized(13, 2).enterString("abcdefghijkl\b\b\b\b\b\033[20X").assertLinesAre("abcdefg ", " "); } + /** CSI Pm m Set SGR parameter(s) from semicolon-separated list Pm. */ + public void testCsiSGRParameters() { + // Set more parameters (19) than supported (16). Additional parameters should be silently consumed. + withTerminalSized(3, 2).enterString("\033[0;38;2;255;255;255;48;2;0;0;0;1;2;3;4;5;7;8;9mabc").assertLinesAre("abc", " "); + } + + /** CSI Ps b Repeat the preceding graphic character Ps times (REP). */ + public void testRepeat() { + withTerminalSized(3, 2).enterString("a\033[b").assertLinesAre("aa ", " "); + withTerminalSized(3, 2).enterString("a\033[2b").assertLinesAre("aaa", " "); + // When no char has been output we ignore REP: + withTerminalSized(3, 2).enterString("\033[b").assertLinesAre(" ", " "); + // This shows that REP outputs the last emitted code point and not the one relative to the + // current cursor position: + withTerminalSized(5, 2).enterString("abcde\033[2G\033[2b\n").assertLinesAre("aeede", " "); + } + } diff --git a/terminal-emulator/src/test/java/com/termux/terminal/CursorAndScreenTest.java b/terminal-emulator/src/test/java/com/termux/terminal/CursorAndScreenTest.java index 567e627b9e..92c41f0f4e 100644 --- a/terminal-emulator/src/test/java/com/termux/terminal/CursorAndScreenTest.java +++ b/terminal-emulator/src/test/java/com/termux/terminal/CursorAndScreenTest.java @@ -227,4 +227,44 @@ public void testBackspaceAcrossWrappedLines() { withTerminalSized(3, 3).enterString("\b\b\b\bhi").assertLinesAre("hi ", " ", " "); } + public void testCursorSaveRestoreLocation() { + // DEC save/restore + withTerminalSized(4, 2).enterString("t\0337est\r\nme\0338ry ").assertLinesAre("try ", "me "); + // ANSI.SYS save/restore + withTerminalSized(4, 2).enterString("t\033[sest\r\nme\033[ury ").assertLinesAre("try ", "me "); + // Alternate screen enter/exit + withTerminalSized(4, 2).enterString("t\033[?1049h\033[Hest\r\nme").assertLinesAre("est ", "me ").enterString("\033[?1049lry").assertLinesAre("try ", " "); + } + + public void testCursorSaveRestoreTextStyle() { + long s; + + // DEC save/restore + withTerminalSized(4, 2).enterString("\033[31;42;4m..\0337\033[36;47;24m\0338.."); + s = getStyleAt(0, 3); + Assert.assertEquals(1, TextStyle.decodeForeColor(s)); + Assert.assertEquals(2, TextStyle.decodeBackColor(s)); + Assert.assertEquals(TextStyle.CHARACTER_ATTRIBUTE_UNDERLINE, TextStyle.decodeEffect(s)); + + // ANSI.SYS save/restore + withTerminalSized(4, 2).enterString("\033[31;42;4m..\033[s\033[36;47;24m\033[u.."); + s = getStyleAt(0, 3); + Assert.assertEquals(1, TextStyle.decodeForeColor(s)); + Assert.assertEquals(2, TextStyle.decodeBackColor(s)); + Assert.assertEquals(TextStyle.CHARACTER_ATTRIBUTE_UNDERLINE, TextStyle.decodeEffect(s)); + + // Alternate screen enter/exit + withTerminalSized(4, 2); + enterString("\033[31;42;4m..\033[?1049h\033[H\033[36;47;24m."); + s = getStyleAt(0, 0); + Assert.assertEquals(6, TextStyle.decodeForeColor(s)); + Assert.assertEquals(7, TextStyle.decodeBackColor(s)); + Assert.assertEquals(0, TextStyle.decodeEffect(s)); + enterString("\033[?1049l.."); + s = getStyleAt(0, 3); + Assert.assertEquals(1, TextStyle.decodeForeColor(s)); + Assert.assertEquals(2, TextStyle.decodeBackColor(s)); + Assert.assertEquals(TextStyle.CHARACTER_ATTRIBUTE_UNDERLINE, TextStyle.decodeEffect(s)); + } + } diff --git a/terminal-view/build.gradle b/terminal-view/build.gradle index 7e4c7cfa5e..5f2c367407 100644 --- a/terminal-view/build.gradle +++ b/terminal-view/build.gradle @@ -18,7 +18,7 @@ ext { android { compileSdkVersion 25 - buildToolsVersion "25.0.2" + buildToolsVersion "26.0.0" dependencies { compile 'com.android.support:support-annotations:25.3.1' diff --git a/terminal-view/src/main/AndroidManifest.xml b/terminal-view/src/main/AndroidManifest.xml index a312303c54..f2b0725df4 100644 --- a/terminal-view/src/main/AndroidManifest.xml +++ b/terminal-view/src/main/AndroidManifest.xml @@ -1,3 +1,2 @@ - + diff --git a/terminal-view/src/main/java/com/termux/view/TerminalView.java b/terminal-view/src/main/java/com/termux/view/TerminalView.java index fb01874776..91d0762d90 100644 --- a/terminal-view/src/main/java/com/termux/view/TerminalView.java +++ b/terminal-view/src/main/java/com/termux/view/TerminalView.java @@ -834,6 +834,10 @@ public boolean onPrepareActionMode(ActionMode mode, Menu menu) { @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + if (!mIsSelectingText) { + // Fix issue where the dialog is pressed while being dismissed. + return true; + } switch (item.getItemId()) { case 1: String selectedText = mEmulator.getSelectedText(mSelX1, mSelY1, mSelX2, mSelY2).trim();