Compare commits

..

14 Commits
v0.38 ... v0.41

Author SHA1 Message Date
Fredrik Fornwall
a2cb3fafee Fix numpad 0 and . key handling
Fixes #146.
2016-09-05 23:11:25 +02:00
Fredrik Fornwall
166710f14a Bump version to 0.40 2016-09-04 19:00:29 +02:00
Fredrik Fornwall
c1a9b7726f Tweak InputConnection implementation 2016-09-04 18:56:28 +02:00
Fredrik Fornwall
afb339e9d8 Format code 2016-08-30 13:47:30 +02:00
Fredrik Fornwall
64c23f498f Implement true (24-bit) color 2016-08-27 00:32:38 +02:00
Fredrik Fornwall
1dc92b2a12 Remove unused imports 2016-08-22 16:54:55 +02:00
Fredrik Fornwall
990a957383 Bump android support library version 2016-08-22 16:53:42 +02:00
Fredrik Fornwall
7bb64d724c Update android plugin for gradle 2016-08-16 10:31:10 +02:00
Fredrik Fornwall
4609dd71c6 Switch KEYCODE_HOME -> KEYCODE_MOVE_HOME in tests 2016-08-12 06:13:26 +02:00
Fredrik Fornwall
8d00f22d4c Catch KEYCODE_MOVE_HOME and not KEYCODE_HOME
The KEYCODE_HOME event is handled by the system and never delivered
to applications, it's KEYCODE_MOVE_HOME (FN+LeftArrow) we want to
handle ourselves and send as an escape sequence.

Fixes #138.
2016-08-12 04:18:09 +02:00
Fredrik Fornwall
5532421ab2 Check arches in order of preference
The documentation for Build.SUPPORTED_ABIS says:
"An ordered list of ABIs supported by this device. The most preferred
ABI is the first element in the list."

Respect that preference when checking for which arch to install
packages for.

Fixes #131.
2016-08-08 23:22:47 +02:00
Fredrik Fornwall
d2b27978e2 Bump version for v0.39 2016-08-08 23:19:49 +02:00
Fredrik Fornwall
30b05e9ab2 Merge pull request #132 from michalbednarski/extrakeysview-alignment
Make ExtraKeysView work on Android 5
2016-08-08 23:10:10 +02:00
Michał Bednarski
c350318c77 Make ExtraKeysView work on Android 5
This is done by explicitly specifying alignment as GridLayout.FILL
as I have figured out that this was fixed in Android 6 in commit
6dafd87fb4%5E%21/#F0
which set default alignment to FILL if weight is nonzero
2016-08-08 10:16:47 +02:00
20 changed files with 258 additions and 163 deletions

View File

@@ -5,16 +5,16 @@ android {
buildToolsVersion "24.0.1" buildToolsVersion "24.0.1"
dependencies { dependencies {
compile 'com.android.support:support-annotations:24.1.1' compile 'com.android.support:support-annotations:24.2.0'
compile "com.android.support:support-v4:24.1.1" compile "com.android.support:support-v4:24.2.0"
} }
defaultConfig { defaultConfig {
applicationId "com.termux" applicationId "com.termux"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 24 targetSdkVersion 24
versionCode 38 versionCode 41
versionName "0.38" versionName "0.41"
ndk { ndk {
moduleName "libtermux" moduleName "libtermux"

View File

@@ -165,8 +165,8 @@ public final class ExtraKeysView extends GridLayout {
param.rightMargin = param.topMargin = 0; param.rightMargin = param.topMargin = 0;
param.setGravity(Gravity.LEFT); param.setGravity(Gravity.LEFT);
float weight = "▲▼◀▶".contains(buttonText) ? 0.7f : 1.f; float weight = "▲▼◀▶".contains(buttonText) ? 0.7f : 1.f;
param.columnSpec = GridLayout.spec(col, weight); param.columnSpec = GridLayout.spec(col, GridLayout.FILL, weight);
param.rowSpec = GridLayout.spec(row, 1.f); param.rowSpec = GridLayout.spec(row, GridLayout.FILL, 1.f);
button.setLayoutParams(param); button.setLayoutParams(param);
addView(button); addView(button);

View File

@@ -184,25 +184,28 @@ final class TermuxInstaller {
/** Get bootstrap zip url for this systems cpu architecture. */ /** Get bootstrap zip url for this systems cpu architecture. */
static URL determineZipUrl() throws MalformedURLException { static URL determineZipUrl() throws MalformedURLException {
String termuxArch = null; String archName = determineTermuxArchName();
return new URL("https://termux.net/bootstrap/bootstrap-" + archName + ".zip");
}
private static String determineTermuxArchName() {
// Note that we cannot use System.getProperty("os.arch") since that may give e.g. "aarch64" // Note that we cannot use System.getProperty("os.arch") since that may give e.g. "aarch64"
// while a 64-bit runtime may not be installed (like on the Samsung Galaxy S5 Neo). // while a 64-bit runtime may not be installed (like on the Samsung Galaxy S5 Neo).
// Instead we search through the supported abi:s on the device, see: // Instead we search through the supported abi:s on the device, see:
// http://developer.android.com/ndk/guides/abis.html // http://developer.android.com/ndk/guides/abis.html
// Note that we search for abi:s in preferred order, and want to avoid installing arm on // Note that we search for abi:s in preferred order (the ordering of the
// an x86 system where arm emulation is available. // Build.SUPPORTED_ABIS list) to avoid e.g. installing arm on an x86 system where arm
final String[] androidArchNames = {"arm64-v8a", "x86_64", "x86", "armeabi-v7a"}; // emulation is available.
final String[] termuxArchNames = {"aarch64", "x86_64", "i686", "arm"}; for (String androidArch : Build.SUPPORTED_ABIS) {
switch (androidArch) {
final List<String> supportedArches = Arrays.asList(Build.SUPPORTED_ABIS); case "arm64-v8a": return "aarch64";
for (int i = 0; i < termuxArchNames.length; i++) { case "armeabi-v7a": return "arm";
if (supportedArches.contains(androidArchNames[i])) { case "x86_64": return "x86_64";
termuxArch = termuxArchNames[i]; case "x86": return "i686";
break;
} }
} }
throw new RuntimeException("Unable to determine arch from Build.SUPPORTED_ABIS = " +
return new URL("https://termux.net/bootstrap/bootstrap-" + termuxArch + ".zip"); Arrays.toString(Build.SUPPORTED_ABIS));
} }
/** Delete a folder and all its content or throw. */ /** Delete a folder and all its content or throw. */

View File

@@ -3,7 +3,6 @@ package com.termux.app;
import android.content.Context; import android.content.Context;
import android.media.AudioManager; import android.media.AudioManager;
import android.support.v4.widget.DrawerLayout; import android.support.v4.widget.DrawerLayout;
import android.util.Log;
import android.view.Gravity; import android.view.Gravity;
import android.view.InputDevice; import android.view.InputDevice;
import android.view.KeyEvent; import android.view.KeyEvent;

View File

@@ -26,9 +26,9 @@ import static android.view.KeyEvent.KEYCODE_F7;
import static android.view.KeyEvent.KEYCODE_F8; import static android.view.KeyEvent.KEYCODE_F8;
import static android.view.KeyEvent.KEYCODE_F9; import static android.view.KeyEvent.KEYCODE_F9;
import static android.view.KeyEvent.KEYCODE_FORWARD_DEL; import static android.view.KeyEvent.KEYCODE_FORWARD_DEL;
import static android.view.KeyEvent.KEYCODE_HOME;
import static android.view.KeyEvent.KEYCODE_INSERT; import static android.view.KeyEvent.KEYCODE_INSERT;
import static android.view.KeyEvent.KEYCODE_MOVE_END; import static android.view.KeyEvent.KEYCODE_MOVE_END;
import static android.view.KeyEvent.KEYCODE_MOVE_HOME;
import static android.view.KeyEvent.KEYCODE_NUMPAD_0; import static android.view.KeyEvent.KEYCODE_NUMPAD_0;
import static android.view.KeyEvent.KEYCODE_NUMPAD_1; import static android.view.KeyEvent.KEYCODE_NUMPAD_1;
import static android.view.KeyEvent.KEYCODE_NUMPAD_2; import static android.view.KeyEvent.KEYCODE_NUMPAD_2;
@@ -66,7 +66,7 @@ public final class KeyHandler {
// terminfo: http://pubs.opengroup.org/onlinepubs/7990989799/xcurses/terminfo.html // terminfo: http://pubs.opengroup.org/onlinepubs/7990989799/xcurses/terminfo.html
// termcap: http://man7.org/linux/man-pages/man5/termcap.5.html // termcap: http://man7.org/linux/man-pages/man5/termcap.5.html
TERMCAP_TO_KEYCODE.put("%i", KEYMOD_SHIFT | KEYCODE_DPAD_RIGHT); TERMCAP_TO_KEYCODE.put("%i", KEYMOD_SHIFT | KEYCODE_DPAD_RIGHT);
TERMCAP_TO_KEYCODE.put("#2", KEYMOD_SHIFT | KEYCODE_HOME); // Shifted home TERMCAP_TO_KEYCODE.put("#2", KEYMOD_SHIFT | KEYCODE_MOVE_HOME); // Shifted home
TERMCAP_TO_KEYCODE.put("#4", KEYMOD_SHIFT | KEYCODE_DPAD_LEFT); TERMCAP_TO_KEYCODE.put("#4", KEYMOD_SHIFT | KEYCODE_DPAD_LEFT);
TERMCAP_TO_KEYCODE.put("*7", KEYMOD_SHIFT | KEYCODE_MOVE_END); // Shifted end key TERMCAP_TO_KEYCODE.put("*7", KEYMOD_SHIFT | KEYCODE_MOVE_END); // Shifted end key
@@ -98,7 +98,7 @@ public final class KeyHandler {
TERMCAP_TO_KEYCODE.put("kb", KEYCODE_DEL); // backspace key TERMCAP_TO_KEYCODE.put("kb", KEYCODE_DEL); // backspace key
TERMCAP_TO_KEYCODE.put("kd", KEYCODE_DPAD_DOWN); // terminfo=kcud1, down-arrow key TERMCAP_TO_KEYCODE.put("kd", KEYCODE_DPAD_DOWN); // terminfo=kcud1, down-arrow key
TERMCAP_TO_KEYCODE.put("kh", KEYCODE_HOME); TERMCAP_TO_KEYCODE.put("kh", KEYCODE_MOVE_HOME);
TERMCAP_TO_KEYCODE.put("kl", KEYCODE_DPAD_LEFT); TERMCAP_TO_KEYCODE.put("kl", KEYCODE_DPAD_LEFT);
TERMCAP_TO_KEYCODE.put("kr", KEYCODE_DPAD_RIGHT); TERMCAP_TO_KEYCODE.put("kr", KEYCODE_DPAD_RIGHT);
@@ -107,7 +107,7 @@ public final class KeyHandler {
// t_K3 <kPageUp> keypad page-up key // t_K3 <kPageUp> keypad page-up key
// t_K4 <kEnd> keypad end key // t_K4 <kEnd> keypad end key
// t_K5 <kPageDown> keypad page-down key // t_K5 <kPageDown> keypad page-down key
TERMCAP_TO_KEYCODE.put("K1", KEYCODE_HOME); TERMCAP_TO_KEYCODE.put("K1", KEYCODE_MOVE_HOME);
TERMCAP_TO_KEYCODE.put("K3", KEYCODE_PAGE_UP); TERMCAP_TO_KEYCODE.put("K3", KEYCODE_PAGE_UP);
TERMCAP_TO_KEYCODE.put("K4", KEYCODE_MOVE_END); TERMCAP_TO_KEYCODE.put("K4", KEYCODE_MOVE_END);
TERMCAP_TO_KEYCODE.put("K5", KEYCODE_PAGE_DOWN); TERMCAP_TO_KEYCODE.put("K5", KEYCODE_PAGE_DOWN);
@@ -162,7 +162,9 @@ public final class KeyHandler {
case KEYCODE_DPAD_LEFT: case KEYCODE_DPAD_LEFT:
return (keyMode == 0) ? (cursorApp ? "\033OD" : "\033[D") : transformForModifiers("\033[1", keyMode, 'D'); return (keyMode == 0) ? (cursorApp ? "\033OD" : "\033[D") : transformForModifiers("\033[1", keyMode, 'D');
case KEYCODE_HOME: case KEYCODE_MOVE_HOME:
// Note that KEYCODE_HOME is handled by the system and never delivered to applications.
// On a Logitech k810 keyboard KEYCODE_MOVE_HOME is sent by FN+LeftArrow.
return (keyMode == 0) ? (cursorApp ? "\033OH" : "\033[H") : transformForModifiers("\033[1", keyMode, 'H'); return (keyMode == 0) ? (cursorApp ? "\033OH" : "\033[H") : transformForModifiers("\033[1", keyMode, 'H');
case KEYCODE_MOVE_END: case KEYCODE_MOVE_END:
return (keyMode == 0) ? (cursorApp ? "\033OF" : "\033[F") : transformForModifiers("\033[1", keyMode, 'F'); return (keyMode == 0) ? (cursorApp ? "\033OF" : "\033[F") : transformForModifiers("\033[1", keyMode, 'F');
@@ -217,9 +219,6 @@ public final class KeyHandler {
case KEYCODE_FORWARD_DEL: case KEYCODE_FORWARD_DEL:
return transformForModifiers("\033[3", keyMode, '~'); return transformForModifiers("\033[3", keyMode, '~');
case KEYCODE_NUMPAD_DOT:
return keypadApplication ? "\033On" : "\033[3~";
case KEYCODE_PAGE_UP: case KEYCODE_PAGE_UP:
return "\033[5~"; return "\033[5~";
case KEYCODE_PAGE_DOWN: case KEYCODE_PAGE_DOWN:
@@ -249,12 +248,14 @@ public final class KeyHandler {
return keypadApplication ? transformForModifiers("\033O", keyMode, 'k') : "+"; return keypadApplication ? transformForModifiers("\033O", keyMode, 'k') : "+";
case KEYCODE_NUMPAD_COMMA: case KEYCODE_NUMPAD_COMMA:
return ","; return ",";
case KEYCODE_NUMPAD_DOT:
return keypadApplication ? "\033On" : ".";
case KEYCODE_NUMPAD_SUBTRACT: case KEYCODE_NUMPAD_SUBTRACT:
return keypadApplication ? transformForModifiers("\033O", keyMode, 'm') : "-"; return keypadApplication ? transformForModifiers("\033O", keyMode, 'm') : "-";
case KEYCODE_NUMPAD_DIVIDE: case KEYCODE_NUMPAD_DIVIDE:
return keypadApplication ? transformForModifiers("\033O", keyMode, 'o') : "/"; return keypadApplication ? transformForModifiers("\033O", keyMode, 'o') : "/";
case KEYCODE_NUMPAD_0: case KEYCODE_NUMPAD_0:
return keypadApplication ? transformForModifiers("\033O", keyMode, 'p') : "1"; return keypadApplication ? transformForModifiers("\033O", keyMode, 'p') : "0";
case KEYCODE_NUMPAD_1: case KEYCODE_NUMPAD_1:
return keypadApplication ? transformForModifiers("\033O", keyMode, 'q') : "1"; return keypadApplication ? transformForModifiers("\033O", keyMode, 'q') : "1";
case KEYCODE_NUMPAD_2: case KEYCODE_NUMPAD_2:

View File

@@ -140,7 +140,7 @@ public final class TerminalBuffer {
* @param newRows The number of rows the screen should have. * @param newRows The number of rows the screen should have.
* @param cursor An int[2] containing the (column, row) cursor location. * @param cursor An int[2] containing the (column, row) cursor location.
*/ */
public void resize(int newColumns, int newRows, int newTotalRows, int[] cursor, int currentStyle, boolean altScreen) { public void resize(int newColumns, int newRows, int newTotalRows, int[] cursor, long currentStyle, boolean altScreen) {
// newRows > mTotalRows should not normally happen since mTotalRows is TRANSCRIPT_ROWS (10000): // newRows > mTotalRows should not normally happen since mTotalRows is TRANSCRIPT_ROWS (10000):
if (newColumns == mColumns && newRows <= mTotalRows) { if (newColumns == mColumns && newRows <= mTotalRows) {
// Fast resize where just the rows changed. // Fast resize where just the rows changed.
@@ -237,7 +237,7 @@ public final class TerminalBuffer {
} }
int currentOldCol = 0; int currentOldCol = 0;
int styleAtCol = 0; long styleAtCol = 0;
for (int i = 0; i < lastNonSpaceIndex; i++) { for (int i = 0; i < lastNonSpaceIndex; i++) {
// Note that looping over java character, not cells. // Note that looping over java character, not cells.
char c = oldLine.mText[i]; char c = oldLine.mText[i];
@@ -321,7 +321,7 @@ public final class TerminalBuffer {
* @param bottomMargin One line after the last line that is scrolled. * @param bottomMargin One line after the last line that is scrolled.
* @param style the style for the newly exposed line. * @param style the style for the newly exposed line.
*/ */
public void scrollDownOneLine(int topMargin, int bottomMargin, int style) { public void scrollDownOneLine(int topMargin, int bottomMargin, long style) {
if (topMargin > bottomMargin - 1 || topMargin < 0 || bottomMargin > mScreenRows) if (topMargin > bottomMargin - 1 || topMargin < 0 || bottomMargin > mScreenRows)
throw new IllegalArgumentException("topMargin=" + topMargin + ", bottomMargin=" + bottomMargin + ", mScreenRows=" + mScreenRows); throw new IllegalArgumentException("topMargin=" + topMargin + ", bottomMargin=" + bottomMargin + ", mScreenRows=" + mScreenRows);
@@ -374,7 +374,7 @@ public final class TerminalBuffer {
* InvalidParemeterException will be thrown. Typically this is called with a "val" argument of 32 to clear a block * InvalidParemeterException will be thrown. Typically this is called with a "val" argument of 32 to clear a block
* of characters. * of characters.
*/ */
public void blockSet(int sx, int sy, int w, int h, int val, int style) { public void blockSet(int sx, int sy, int w, int h, int val, long style) {
if (sx < 0 || sx + w > mColumns || sy < 0 || sy + h > mScreenRows) { if (sx < 0 || sx + w > mColumns || sy < 0 || sy + h > mScreenRows) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Illegal arguments! blockSet(" + sx + ", " + sy + ", " + w + ", " + h + ", " + val + ", " + mColumns + ", " + mScreenRows + ")"); "Illegal arguments! blockSet(" + sx + ", " + sy + ", " + w + ", " + h + ", " + val + ", " + mColumns + ", " + mScreenRows + ")");
@@ -388,14 +388,14 @@ public final class TerminalBuffer {
return (mLines[row] == null) ? (mLines[row] = new TerminalRow(mColumns, 0)) : mLines[row]; return (mLines[row] == null) ? (mLines[row] = new TerminalRow(mColumns, 0)) : mLines[row];
} }
public void setChar(int column, int row, int codePoint, int style) { public void setChar(int column, int row, int codePoint, long style) {
if (row >= mScreenRows || column >= mColumns) if (row >= mScreenRows || column >= mColumns)
throw new IllegalArgumentException("row=" + row + ", column=" + column + ", mScreenRows=" + mScreenRows + ", mColumns=" + mColumns); throw new IllegalArgumentException("row=" + row + ", column=" + column + ", mScreenRows=" + mScreenRows + ", mColumns=" + mColumns);
row = externalToInternalRow(row); row = externalToInternalRow(row);
allocateFullLineIfNecessary(row).setChar(column, codePoint, style); allocateFullLineIfNecessary(row).setChar(column, codePoint, style);
} }
public int getStyleAt(int externalRow, int column) { public long getStyleAt(int externalRow, int column) {
return allocateFullLineIfNecessary(externalToInternalRow(externalRow)).getStyle(column); return allocateFullLineIfNecessary(externalToInternalRow(externalRow)).getStyle(column);
} }
@@ -407,7 +407,7 @@ public final class TerminalBuffer {
int startOfLine = (rectangular || y == top) ? left : leftMargin; int startOfLine = (rectangular || y == top) ? left : leftMargin;
int endOfLine = (rectangular || y + 1 == bottom) ? right : rightMargin; int endOfLine = (rectangular || y + 1 == bottom) ? right : rightMargin;
for (int x = startOfLine; x < endOfLine; x++) { for (int x = startOfLine; x < endOfLine; x++) {
int currentStyle = line.getStyle(x); long currentStyle = line.getStyle(x);
int foreColor = TextStyle.decodeForeColor(currentStyle); int foreColor = TextStyle.decodeForeColor(currentStyle);
int backColor = TextStyle.decodeBackColor(currentStyle); int backColor = TextStyle.decodeBackColor(currentStyle);
int effect = TextStyle.decodeEffect(currentStyle); int effect = TextStyle.decodeEffect(currentStyle);

View File

@@ -12,7 +12,7 @@ import java.util.Stack;
/** /**
* Renders text into a screen. Contains all the terminal-specific knowledge and state. Emulates a subset of the X Window * Renders text into a screen. Contains all the terminal-specific knowledge and state. Emulates a subset of the X Window
* System xterm terminal, which in turn is an emulator for a subset of the Digital Equipment Corporation vt100 terminal. * System xterm terminal, which in turn is an emulator for a subset of the Digital Equipment Corporation vt100 terminal.
* <p/> * <p>
* References: * References:
* <ul> * <ul>
* <li>http://invisible-island.net/xterm/ctlseqs/ctlseqs.html</li> * <li>http://invisible-island.net/xterm/ctlseqs/ctlseqs.html</li>
@@ -145,7 +145,7 @@ public final class TerminalEmulator {
/** /**
* The alternate screen buffer, exactly as large as the display and contains no additional saved lines (so that when * The alternate screen buffer, exactly as large as the display and contains no additional saved lines (so that when
* the alternate screen buffer is active, you cannot scroll back to view saved lines). * the alternate screen buffer is active, you cannot scroll back to view saved lines).
* <p/> * <p>
* See http://www.xfree86.org/current/ctlseqs.html#The%20Alternate%20Screen%20Buffer * See http://www.xfree86.org/current/ctlseqs.html#The%20Alternate%20Screen%20Buffer
*/ */
final TerminalBuffer mAltBuffer; final TerminalBuffer mAltBuffer;
@@ -206,10 +206,15 @@ public final class TerminalEmulator {
*/ */
private boolean mAboutToAutoWrap; private boolean mAboutToAutoWrap;
/** Foreground and background color indices, 0..255. */ /**
* Current foreground and background colors. Can either be a color index in [0,259] or a truecolor (24-bit) value.
* For a 24-bit value the top byte (0xff000000) is set.
*
* @see TextStyle
*/
int mForeColor, mBackColor; int mForeColor, mBackColor;
/** Current TextStyle effect */ /** Current {@link TextStyle} effect. */
private int mEffect; private int mEffect;
/** /**
@@ -611,7 +616,7 @@ public final class TerminalEmulator {
int left = Math.min(getArg(argIndex++, 1, true) + effectiveLeftMargin, effectiveRightMargin + 1); int left = Math.min(getArg(argIndex++, 1, true) + effectiveLeftMargin, effectiveRightMargin + 1);
int bottom = Math.min(getArg(argIndex++, mRows, true) + effectiveTopMargin, effectiveBottomMargin); int bottom = Math.min(getArg(argIndex++, mRows, true) + effectiveTopMargin, effectiveBottomMargin);
int right = Math.min(getArg(argIndex, mColumns, true) + effectiveLeftMargin, effectiveRightMargin); int right = Math.min(getArg(argIndex, mColumns, true) + effectiveLeftMargin, effectiveRightMargin);
int style = getStyle(); long style = getStyle();
for (int row = top - 1; row < bottom; row++) for (int row = top - 1; row < bottom; row++)
for (int col = left - 1; col < right; col++) for (int col = left - 1; col < right; col++)
if (!selective || (TextStyle.decodeEffect(mScreen.getStyleAt(row, col)) & TextStyle.CHARACTER_ATTRIBUTE_PROTECTED) == 0) if (!selective || (TextStyle.decodeEffect(mScreen.getStyleAt(row, col)) & TextStyle.CHARACTER_ATTRIBUTE_PROTECTED) == 0)
@@ -623,7 +628,7 @@ public final class TerminalEmulator {
case 't': // "${CSI}${TOP}${LEFT}${BOTTOM}${RIGHT}${ATTRIBUTES}$t" case 't': // "${CSI}${TOP}${LEFT}${BOTTOM}${RIGHT}${ATTRIBUTES}$t"
// Reverse attributes in rectangular area (DECRARA - http://www.vt100.net/docs/vt510-rm/DECRARA). // Reverse attributes in rectangular area (DECRARA - http://www.vt100.net/docs/vt510-rm/DECRARA).
boolean reverse = b == 't'; boolean reverse = b == 't';
// FIXME: "coordinates of the rectangular area are affected by the setting of origin mode (DECOM)".s // FIXME: "coordinates of the rectangular area are affected by the setting of origin mode (DECOM)".
int top = Math.min(getArg(0, 1, true) - 1, effectiveBottomMargin) + effectiveTopMargin; int top = Math.min(getArg(0, 1, true) - 1, effectiveBottomMargin) + effectiveTopMargin;
int left = Math.min(getArg(1, 1, true) - 1, effectiveRightMargin) + effectiveLeftMargin; int left = Math.min(getArg(1, 1, true) - 1, effectiveRightMargin) + effectiveLeftMargin;
int bottom = Math.min(getArg(2, mRows, true) + 1, effectiveBottomMargin - 1) + effectiveTopMargin; int bottom = Math.min(getArg(2, mRows, true) + 1, effectiveBottomMargin - 1) + effectiveTopMargin;
@@ -953,7 +958,7 @@ public final class TerminalEmulator {
unknownSequence(b); unknownSequence(b);
break; break;
} }
int style = getStyle(); long style = getStyle();
for (int row = startRow; row < endRow; row++) { for (int row = startRow; row < endRow; row++) {
for (int col = startCol; col < endCol; col++) { for (int col = startCol; col < endCol; col++) {
if ((TextStyle.decodeEffect(mScreen.getStyleAt(row, col)) & TextStyle.CHARACTER_ATTRIBUTE_PROTECTED) == 0) if ((TextStyle.decodeEffect(mScreen.getStyleAt(row, col)) & TextStyle.CHARACTER_ATTRIBUTE_PROTECTED) == 0)
@@ -1687,43 +1692,42 @@ public final class TerminalEmulator {
} else if (code >= 30 && code <= 37) { } else if (code >= 30 && code <= 37) {
mForeColor = code - 30; mForeColor = code - 30;
} else if (code == 38 || code == 48) { } else if (code == 38 || code == 48) {
// ISO-8613-3 controls to set foreground (38) or background (48) colors. // Extended set foreground(38)/background (48) color.
// P_s = (38|48) ; 2 ; P_r ; P_g ; P_b => Set to RGB value in range (0-255). // This is followed by either "2;$R;$G;$B" to set a 24-bit color or
// P_s = (38|48) ; 5 ; P_s => Set to indexed color. // "5;$INDEX" to set an indexed color.
if (i + 2 <= mArgIndex) { if (i + 2 > mArgIndex) continue;
int color = -1; int firstArg = mArgs[i + 1];
int firstArg = mArgs[i + 1]; if (firstArg == 2) {
if (firstArg == 2) { if (i + 4 > mArgIndex) {
if (i + 4 > mArgIndex) { Log.w(EmulatorDebug.LOG_TAG, "Too few CSI" + code + ";2 RGB arguments");
Log.w(EmulatorDebug.LOG_TAG, "Too few CSI" + code + ";2 RGB arguments");
} else {
int red = mArgs[i + 2], green = mArgs[i + 3], blue = mArgs[i + 4];
if (red < 0 || green < 0 || blue < 0 || red > 255 || green > 255 || blue > 255) {
finishSequenceAndLogError("Invalid RGB: " + red + "," + green + "," + blue);
} else {
// TODO: Implement 24 bit color.
finishSequenceAndLogError("Unimplemented RGB: " + red + "," + green + "," + blue);
}
i += 4; // "2;P_r;P_g;P_r"
}
} else if (firstArg == 5) {
color = mArgs[i + 2];
i += 2; // "5;P_s"
} else { } else {
finishSequenceAndLogError("Invalid ISO-8613-3 SGR first argument: " + firstArg); int red = mArgs[i + 2], green = mArgs[i + 3], blue = mArgs[i + 4];
} if (red < 0 || green < 0 || blue < 0 || red > 255 || green > 255 || blue > 255) {
if (i != -1) { finishSequenceAndLogError("Invalid RGB: " + red + "," + green + "," + blue);
if (color >= 0 && color < TextStyle.NUM_INDEXED_COLORS) {
if (code == 38) {
mForeColor = color;
} else {
mBackColor = color;
}
} else { } else {
if (LOG_ESCAPE_SEQUENCES) int argbColor = 0xff000000 | (red << 16) | (green << 8) | blue;
Log.w(EmulatorDebug.LOG_TAG, "Invalid color index: " + color); if (code == 38) {
mForeColor = argbColor;
} else {
mBackColor = argbColor;
}
} }
i += 4; // "2;P_r;P_g;P_r"
} }
} else if (firstArg == 5) {
int color = mArgs[i + 2];
i += 2; // "5;P_s"
if (color >= 0 && color < TextStyle.NUM_INDEXED_COLORS) {
if (code == 38) {
mForeColor = color;
} else {
mBackColor = color;
}
} else {
if (LOG_ESCAPE_SEQUENCES) Log.w(EmulatorDebug.LOG_TAG, "Invalid color index: " + color);
}
} else {
finishSequenceAndLogError("Invalid ISO-8613-3 SGR first argument: " + firstArg);
} }
} else if (code == 39) { // Set default foreground color. } else if (code == 39) { // Set default foreground color.
mForeColor = TextStyle.COLOR_INDEX_FOREGROUND; mForeColor = TextStyle.COLOR_INDEX_FOREGROUND;
@@ -1924,7 +1928,7 @@ public final class TerminalEmulator {
mScreen.blockSet(sx, sy, w, h, ' ', getStyle()); mScreen.blockSet(sx, sy, w, h, ' ', getStyle());
} }
private int getStyle() { private long getStyle() {
return TextStyle.encode(mForeColor, mBackColor, mEffect); return TextStyle.encode(mForeColor, mBackColor, mEffect);
} }

View File

@@ -20,13 +20,13 @@ public final class TerminalRow {
/** If this row has been line wrapped due to text output at the end of line. */ /** If this row has been line wrapped due to text output at the end of line. */
boolean mLineWrap; boolean mLineWrap;
/** The style bits of each cell in the row. See {@link TextStyle}. */ /** The style bits of each cell in the row. See {@link TextStyle}. */
final int[] mStyle; final long[] mStyle;
/** Construct a blank row (containing only whitespace, ' ') with a specified style. */ /** Construct a blank row (containing only whitespace, ' ') with a specified style. */
public TerminalRow(int columns, int style) { public TerminalRow(int columns, long style) {
mColumns = columns; mColumns = columns;
mText = new char[(int) (SPARE_CAPACITY_FACTOR * columns)]; mText = new char[(int) (SPARE_CAPACITY_FACTOR * columns)];
mStyle = new int[columns]; mStyle = new long[columns];
clear(style); clear(style);
} }
@@ -112,14 +112,14 @@ public final class TerminalRow {
return false; return false;
} }
public void clear(int style) { public void clear(long style) {
Arrays.fill(mText, ' '); Arrays.fill(mText, ' ');
Arrays.fill(mStyle, style); Arrays.fill(mStyle, style);
mSpaceUsed = (short) mColumns; mSpaceUsed = (short) mColumns;
} }
// https://github.com/steven676/Android-Terminal-Emulator/commit/9a47042620bec87617f0b4f5d50568535668fe26 // https://github.com/steven676/Android-Terminal-Emulator/commit/9a47042620bec87617f0b4f5d50568535668fe26
public void setChar(int columnToSet, int codePoint, int style) { public void setChar(int columnToSet, int codePoint, long style) {
mStyle[columnToSet] = style; mStyle[columnToSet] = style;
final int newCodePointDisplayWidth = WcWidth.width(codePoint); final int newCodePointDisplayWidth = WcWidth.width(codePoint);
@@ -225,7 +225,7 @@ public final class TerminalRow {
return true; return true;
} }
public final int getStyle(int column) { public final long getStyle(int column) {
return mStyle[column]; return mStyle[column];
} }

View File

@@ -173,7 +173,7 @@ public final class TerminalSession extends TerminalOutput {
* @param rows The number of rows in the terminal window. * @param rows The number of rows in the terminal window.
*/ */
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= */2000);
int[] processId = new int[1]; int[] processId = new int[1];
mTerminalFileDescriptor = JNI.createSubprocess(mShellPath, mCwd, mArgs, mEnv, processId, rows, columns); mTerminalFileDescriptor = JNI.createSubprocess(mShellPath, mCwd, mArgs, mEnv, processId, rows, columns);

View File

@@ -1,11 +1,13 @@
package com.termux.terminal; package com.termux.terminal;
/** /**
* Encodes effects, foreground and background colors into a 32 bit integer, which are stored for each cell in a terminal * Encodes effects, foreground and background colors into a 64 bit long, which are stored for each cell in a terminal
* row in {@link TerminalRow#mStyle}. * row in {@link TerminalRow#mStyle}.
* <p/> * <p/>
* The foreground and background colors take 9 bits each, leaving (32-9-9)=14 bits for effect flags. Using 9 for now * The bit layout is:
* (the different CHARACTER_ATTRIBUTE_* bits). * - 16 flags (11 currently used).
* - 24 for foreground color (only 9 first bits if a color index).
* - 24 for background color (only 9 first bits if a color index).
*/ */
public final class TextStyle { public final class TextStyle {
@@ -25,6 +27,10 @@ public final class TextStyle {
public final static int CHARACTER_ATTRIBUTE_PROTECTED = 1 << 7; public final static int CHARACTER_ATTRIBUTE_PROTECTED = 1 << 7;
/** Dim colors. Also known as faint or half intensity. */ /** Dim colors. Also known as faint or half intensity. */
public final static int CHARACTER_ATTRIBUTE_DIM = 1 << 8; public final static int CHARACTER_ATTRIBUTE_DIM = 1 << 8;
/** If true (24-bit) color is used for the cell for foreground. */
private final static int CHARACTER_ATTRIBUTE_TRUECOLOR_FOREGROUND = 1 << 9;
/** If true (24-bit) color is used for the cell for foreground. */
private final static int CHARACTER_ATTRIBUTE_TRUECOLOR_BACKGROUND= 1 << 10;
public final static int COLOR_INDEX_FOREGROUND = 256; public final static int COLOR_INDEX_FOREGROUND = 256;
public final static int COLOR_INDEX_BACKGROUND = 257; public final static int COLOR_INDEX_BACKGROUND = 257;
@@ -34,22 +40,47 @@ public final class TextStyle {
public final static int NUM_INDEXED_COLORS = 259; public final static int NUM_INDEXED_COLORS = 259;
/** Normal foreground and background colors and no effects. */ /** Normal foreground and background colors and no effects. */
final static int NORMAL = encode(COLOR_INDEX_FOREGROUND, COLOR_INDEX_BACKGROUND, 0); final static long NORMAL = encode(COLOR_INDEX_FOREGROUND, COLOR_INDEX_BACKGROUND, 0);
static int encode(int foreColor, int backColor, int effect) { static long encode(int foreColor, int backColor, int effect) {
return ((effect & 0b111111111) << 18) | ((foreColor & 0b111111111) << 9) | (backColor & 0b111111111); long result = effect & 0b111111111;
if ((0xff000000 & foreColor) == 0xff000000) {
// 24-bit color.
result |= CHARACTER_ATTRIBUTE_TRUECOLOR_FOREGROUND | (((long) foreColor & 0x00ffffffL) << 40L);
} else {
// Indexed color.
result |= (((long) foreColor) & 0b111111111L) << 40;
}
if ((0xff000000 & backColor) == 0xff000000) {
// 24-bit color.
result |= CHARACTER_ATTRIBUTE_TRUECOLOR_BACKGROUND | (((long) backColor & 0x00ffffffL) << 16L);
} else {
// Indexed color.
result |= (((long) backColor) & 0b111111111L) << 16L;
}
return result;
} }
public static int decodeForeColor(int encodedColor) { public static int decodeForeColor(long style) {
return (encodedColor >> 9) & 0b111111111; if ((style & CHARACTER_ATTRIBUTE_TRUECOLOR_FOREGROUND) == 0) {
return (int) ((style >>> 40) & 0b111111111L);
} else {
return 0xff000000 | (int) ((style >>> 40) & 0x00ffffffL);
}
} }
public static int decodeBackColor(int encodedColor) { public static int decodeBackColor(long style) {
return encodedColor & 0b111111111; if ((style & CHARACTER_ATTRIBUTE_TRUECOLOR_BACKGROUND) == 0) {
return (int) ((style >>> 16) & 0b111111111L);
} else {
return 0xff000000 | (int) ((style >>> 16) & 0x00ffffffL);
}
} }
public static int decodeEffect(int encodedColor) { public static int decodeEffect(long style) {
return (encodedColor >> 18) & 0b111111111; return (int) (style & 0b11111111111);
} }
} }

View File

@@ -82,7 +82,7 @@ final class TerminalRenderer {
final char[] line = lineObject.mText; final char[] line = lineObject.mText;
final int charsUsedInLine = lineObject.getSpaceUsed(); final int charsUsedInLine = lineObject.getSpaceUsed();
int lastRunStyle = 0; long lastRunStyle = 0;
boolean lastRunInsideCursor = false; boolean lastRunInsideCursor = false;
int lastRunStartColumn = -1; int lastRunStartColumn = -1;
int lastRunStartIndex = 0; int lastRunStartIndex = 0;
@@ -97,7 +97,7 @@ final class TerminalRenderer {
final int codePoint = charIsHighsurrogate ? Character.toCodePoint(charAtIndex, line[currentCharIndex + 1]) : charAtIndex; final int codePoint = charIsHighsurrogate ? Character.toCodePoint(charAtIndex, line[currentCharIndex + 1]) : charAtIndex;
final int codePointWcWidth = WcWidth.width(codePoint); final int codePointWcWidth = WcWidth.width(codePoint);
final boolean insideCursor = (column >= selx1 && column <= selx2) || (cursorX == column || (codePointWcWidth == 2 && cursorX == column + 1)); final boolean insideCursor = (column >= selx1 && column <= selx2) || (cursorX == column || (codePointWcWidth == 2 && cursorX == column + 1));
final int style = lineObject.getStyle(column); final long style = lineObject.getStyle(column);
// Check if the measured text width for this code point is not the same as that expected by wcwidth(). // Check if the measured text width for this code point is not the same as that expected by wcwidth().
// This could happen for some fonts which are not truly monospace, or for more exotic characters such as // This could happen for some fonts which are not truly monospace, or for more exotic characters such as
@@ -154,9 +154,17 @@ final class TerminalRenderer {
* @param reverseVideo if the screen is rendered with the global reverse video flag set * @param reverseVideo if the screen is rendered with the global reverse video flag set
*/ */
private void drawTextRun(Canvas canvas, char[] text, int[] palette, float y, int startColumn, int runWidthColumns, int startCharIndex, int runWidthChars, private void drawTextRun(Canvas canvas, char[] text, int[] palette, float y, int startColumn, int runWidthColumns, int startCharIndex, int runWidthChars,
float mes, boolean cursor, int textStyle, boolean reverseVideo) { float mes, boolean cursor, long textStyle, boolean reverseVideo) {
int foreColor = TextStyle.decodeForeColor(textStyle); int foreColor = TextStyle.decodeForeColor(textStyle);
int backColor = TextStyle.decodeBackColor(textStyle); int backColor = TextStyle.decodeBackColor(textStyle);
int foreColorIndex = -1;
if ((foreColor & 0xff000000) != 0xff000000) {
foreColorIndex = foreColor;
foreColor = palette[foreColor];
}
if ((backColor & 0xff000000) != 0xff000000) backColor = palette[backColor];
final int effect = TextStyle.decodeEffect(textStyle); final int effect = TextStyle.decodeEffect(textStyle);
float left = startColumn * mFontWidth; float left = startColumn * mFontWidth;
float right = left + runWidthColumns * mFontWidth; float right = left + runWidthColumns * mFontWidth;
@@ -180,9 +188,9 @@ final class TerminalRenderer {
backColor = tmp; backColor = tmp;
} }
if (backColor != TextStyle.COLOR_INDEX_BACKGROUND) { if (backColor != palette[TextStyle.COLOR_INDEX_BACKGROUND]) {
// Only draw non-default background. // Only draw non-default background.
mTextPaint.setColor(palette[backColor]); mTextPaint.setColor(backColor);
canvas.drawRect(left, y - mFontLineSpacingAndAscent + mFontAscent, right, y, mTextPaint); canvas.drawRect(left, y - mFontLineSpacingAndAscent + mFontAscent, right, y, mTextPaint);
} }
@@ -195,26 +203,25 @@ final class TerminalRenderer {
final boolean dim = (effect & TextStyle.CHARACTER_ATTRIBUTE_DIM) != 0; final boolean dim = (effect & TextStyle.CHARACTER_ATTRIBUTE_DIM) != 0;
// Let bold have bright colors if applicable (one of the first 8): // Let bold have bright colors if applicable (one of the first 8):
final int actualForeColor = foreColor + (bold && foreColor < 8 ? 8 : 0); if (bold && foreColorIndex >= 0 && foreColorIndex < 8) foreColor = palette[foreColorIndex + 8];
int foreColorARGB = palette[actualForeColor];
if (dim) { if (dim) {
int red = (0xFF & (foreColorARGB >> 16)); int red = (0xFF & (foreColor >> 16));
int green = (0xFF & (foreColorARGB >> 8)); int green = (0xFF & (foreColor >> 8));
int blue = (0xFF & foreColorARGB); int blue = (0xFF & foreColor);
// Dim color handling used by libvte which in turn took it from xterm // Dim color handling used by libvte which in turn took it from xterm
// (https://bug735245.bugzilla-attachments.gnome.org/attachment.cgi?id=284267): // (https://bug735245.bugzilla-attachments.gnome.org/attachment.cgi?id=284267):
red = red * 2 / 3; red = red * 2 / 3;
green = green * 2 / 3; green = green * 2 / 3;
blue = blue * 2 / 3; blue = blue * 2 / 3;
foreColorARGB = 0xFF000000 + (red << 16) + (green << 8) + blue; foreColor = 0xFF000000 + (red << 16) + (green << 8) + blue;
} }
mTextPaint.setFakeBoldText(bold); mTextPaint.setFakeBoldText(bold);
mTextPaint.setUnderlineText(underline); mTextPaint.setUnderlineText(underline);
mTextPaint.setTextSkewX(italic ? -0.35f : 0.f); mTextPaint.setTextSkewX(italic ? -0.35f : 0.f);
mTextPaint.setStrikeThruText(strikeThrough); mTextPaint.setStrikeThruText(strikeThrough);
mTextPaint.setColor(foreColorARGB); mTextPaint.setColor(foreColor);
// The text alignment is the default Paint.Align.LEFT. // The text alignment is the default Paint.Align.LEFT.
canvas.drawText(text, startCharIndex, runWidthChars, left, y - mFontLineSpacingAndAscent, mTextPaint); canvas.drawText(text, startCharIndex, runWidthChars, left, y - mFontLineSpacingAndAscent, mTextPaint);

View File

@@ -10,6 +10,7 @@ import android.graphics.Rect;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.BitmapDrawable;
import android.os.Build; import android.os.Build;
import android.text.Editable;
import android.text.InputType; import android.text.InputType;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.AttributeSet; import android.util.AttributeSet;
@@ -233,7 +234,8 @@ public final class TerminalView extends View {
// Previous keyboard issues: // Previous keyboard issues:
// https://github.com/termux/termux-packages/issues/25 // https://github.com/termux/termux-packages/issues/25
// https://github.com/termux/termux-app/issues/87. // https://github.com/termux/termux-app/issues/87.
// https://github.com/termux/termux-app/issues/126 for breakage from that. // https://github.com/termux/termux-app/issues/126.
// https://github.com/termux/termux-app/issues/137 (japanese chars and TYPE_NULL).
outAttrs.inputType = InputType.TYPE_NULL; outAttrs.inputType = InputType.TYPE_NULL;
// Let part of the application show behind when in landscape: // Let part of the application show behind when in landscape:
@@ -244,19 +246,41 @@ public final class TerminalView extends View {
@Override @Override
public boolean finishComposingText() { public boolean finishComposingText() {
if (LOG_KEY_EVENTS) Log.i(EmulatorDebug.LOG_TAG, "IME: finishComposingText()"); if (LOG_KEY_EVENTS) Log.i(EmulatorDebug.LOG_TAG, "IME: finishComposingText()");
commitText(getEditable(), 0); super.finishComposingText();
// Clear the editable. sendTextToTerminal(getEditable());
getEditable().clear(); getEditable().clear();
return true; return true;
} }
@Override @Override
public boolean commitText(CharSequence text, int newCursorPosition) { public boolean commitText(CharSequence text, int newCursorPosition) {
if (LOG_KEY_EVENTS) if (LOG_KEY_EVENTS) {
Log.i(EmulatorDebug.LOG_TAG, "IME: commitText(\"" + text + "\", " + newCursorPosition + ")"); Log.i(EmulatorDebug.LOG_TAG, "IME: commitText(\"" + text + "\", " + newCursorPosition + ")");
}
super.commitText(text, newCursorPosition);
if (mEmulator == null) return true; if (mEmulator == null) return true;
Editable content = getEditable();
sendTextToTerminal(content);
content.clear();
return true;
}
@Override
public boolean deleteSurroundingText(int leftLength, int rightLength) {
if (LOG_KEY_EVENTS)
Log.i(EmulatorDebug.LOG_TAG, "IME: deleteSurroundingText(" + leftLength + ", " + rightLength + ")");
// If leftLength=2 it may be due to a UTF-16 surrogate pair. So we cannot send
// multiple key events for that. Let's just hope that keyboards don't use
// leftLength > 1 for other purposes (such as holding down backspace for repeat).
sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL));
return super.deleteSurroundingText(leftLength, rightLength);
}
void sendTextToTerminal(CharSequence text) {
final int textLengthInChars = text.length(); final int textLengthInChars = text.length();
for (int i = 0; i < textLengthInChars; i++) { for (int i = 0; i < textLengthInChars; i++) {
char firstChar = text.charAt(i); char firstChar = text.charAt(i);
@@ -297,35 +321,8 @@ public final class TerminalView extends View {
inputCodePoint(codePoint, ctrlHeld, false); inputCodePoint(codePoint, ctrlHeld, false);
} }
return true;
} }
@Override
public boolean deleteSurroundingText(int leftLength, int rightLength) {
if (LOG_KEY_EVENTS)
Log.i(EmulatorDebug.LOG_TAG, "IME: deleteSurroundingText(" + leftLength + ", " + rightLength + ")");
// If leftLength=2 it may be due to a UTF-16 surrogate pair. So we cannot send
// multiple key events for that. Let's just hope that keyboards don't use
// leftLength > 1 for other purposes (such as holding down backspace for repeat).
sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL));
return true;
}
@Override
public boolean setComposingText(CharSequence text, int newCursorPosition) {
if (LOG_KEY_EVENTS)
Log.i(EmulatorDebug.LOG_TAG, "IME: setComposingText(\"" + text + "\", " + newCursorPosition + ")");
if (text.length() == 0) {
// Avoid log spam "SpannableStringBuilder: SPAN_EXCLUSIVE_EXCLUSIVE spans cannot
// have a zero length" when backspacing with the Google keyboard.
getEditable().clear();
} else {
super.setComposingText(text, newCursorPosition);
}
return true;
}
}; };
} }

View File

@@ -18,7 +18,7 @@ public class CursorAndScreenTest extends TerminalTestCase {
assertLinesAre("ABCDE", "FGHIJ", "KLMNO", "PQRST", "UVWXY"); assertLinesAre("ABCDE", "FGHIJ", "KLMNO", "PQRST", "UVWXY");
for (int row = 0; row < 5; row++) { for (int row = 0; row < 5; row++) {
for (int col = 0; col < 5; col++) { for (int col = 0; col < 5; col++) {
int s = getStyleAt(row, col); long s = getStyleAt(row, col);
Assert.assertEquals(col, TextStyle.decodeForeColor(s)); Assert.assertEquals(col, TextStyle.decodeForeColor(s));
Assert.assertEquals(row, TextStyle.decodeBackColor(s)); Assert.assertEquals(row, TextStyle.decodeBackColor(s));
} }
@@ -28,7 +28,7 @@ public class CursorAndScreenTest extends TerminalTestCase {
assertLinesAre("KLMNO", "PQRST", "UVWXY", " ", " "); assertLinesAre("KLMNO", "PQRST", "UVWXY", " ", " ");
for (int row = 0; row < 3; row++) { for (int row = 0; row < 3; row++) {
for (int col = 0; col < 5; col++) { for (int col = 0; col < 5; col++) {
int s = getStyleAt(row, col); long s = getStyleAt(row, col);
Assert.assertEquals(col, TextStyle.decodeForeColor(s)); Assert.assertEquals(col, TextStyle.decodeForeColor(s));
Assert.assertEquals(row + 2, TextStyle.decodeBackColor(s)); Assert.assertEquals(row + 2, TextStyle.decodeBackColor(s));
} }
@@ -43,7 +43,7 @@ public class CursorAndScreenTest extends TerminalTestCase {
for (int col = 0; col < 5; col++) { for (int col = 0; col < 5; col++) {
int wantedForeground = (row == 1 || row == 2) ? 98 : col; int wantedForeground = (row == 1 || row == 2) ? 98 : col;
int wantedBackground = (row == 1 || row == 2) ? 99 : (row == 0 ? 2 : row); int wantedBackground = (row == 1 || row == 2) ? 99 : (row == 0 ? 2 : row);
int s = getStyleAt(row, col); long s = getStyleAt(row, col);
Assert.assertEquals(wantedForeground, TextStyle.decodeForeColor(s)); Assert.assertEquals(wantedForeground, TextStyle.decodeForeColor(s));
Assert.assertEquals(wantedBackground, TextStyle.decodeBackColor(s)); Assert.assertEquals(wantedBackground, TextStyle.decodeBackColor(s));
} }

View File

@@ -141,10 +141,10 @@ public class KeyHandlerTest extends TestCase {
assertKeysEquals("\033[1;6D", KeyHandler.getCode(KeyEvent.KEYCODE_DPAD_LEFT, mod, false, false)); assertKeysEquals("\033[1;6D", KeyHandler.getCode(KeyEvent.KEYCODE_DPAD_LEFT, mod, false, false));
// Home/end keys: // Home/end keys:
assertKeysEquals("\033[H", KeyHandler.getCode(KeyEvent.KEYCODE_HOME, 0, false, false)); assertKeysEquals("\033[H", KeyHandler.getCode(KeyEvent.KEYCODE_MOVE_HOME, 0, false, false));
assertKeysEquals("\033[F", KeyHandler.getCode(KeyEvent.KEYCODE_MOVE_END, 0, false, false)); assertKeysEquals("\033[F", KeyHandler.getCode(KeyEvent.KEYCODE_MOVE_END, 0, false, false));
// ... shifted: // ... shifted:
assertKeysEquals("\033[1;2H", KeyHandler.getCode(KeyEvent.KEYCODE_HOME, KeyHandler.KEYMOD_SHIFT, false, false)); assertKeysEquals("\033[1;2H", KeyHandler.getCode(KeyEvent.KEYCODE_MOVE_HOME, KeyHandler.KEYMOD_SHIFT, false, false));
assertKeysEquals("\033[1;2F", KeyHandler.getCode(KeyEvent.KEYCODE_MOVE_END, KeyHandler.KEYMOD_SHIFT, false, false)); assertKeysEquals("\033[1;2F", KeyHandler.getCode(KeyEvent.KEYCODE_MOVE_END, KeyHandler.KEYMOD_SHIFT, false, false));
// Function keys F1-F12: // Function keys F1-F12:
@@ -173,5 +173,19 @@ public class KeyHandlerTest extends TestCase {
assertKeysEquals("\033[21;2~", KeyHandler.getCode(KeyEvent.KEYCODE_F10, KeyHandler.KEYMOD_SHIFT, false, false)); assertKeysEquals("\033[21;2~", KeyHandler.getCode(KeyEvent.KEYCODE_F10, KeyHandler.KEYMOD_SHIFT, false, false));
assertKeysEquals("\033[23;2~", KeyHandler.getCode(KeyEvent.KEYCODE_F11, KeyHandler.KEYMOD_SHIFT, false, false)); assertKeysEquals("\033[23;2~", KeyHandler.getCode(KeyEvent.KEYCODE_F11, KeyHandler.KEYMOD_SHIFT, false, false));
assertKeysEquals("\033[24;2~", KeyHandler.getCode(KeyEvent.KEYCODE_F12, KeyHandler.KEYMOD_SHIFT, false, false)); assertKeysEquals("\033[24;2~", KeyHandler.getCode(KeyEvent.KEYCODE_F12, KeyHandler.KEYMOD_SHIFT, false, false));
}
assertKeysEquals("0", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_0, 0, false, false));
assertKeysEquals("1", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_1, 0, false, false));
assertKeysEquals("2", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_2, 0, false, false));
assertKeysEquals("3", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_3, 0, false, false));
assertKeysEquals("4", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_4, 0, false, false));
assertKeysEquals("5", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_5, 0, false, false));
assertKeysEquals("6", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_6, 0, false, false));
assertKeysEquals("7", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_7, 0, false, false));
assertKeysEquals("8", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_8, 0, false, false));
assertKeysEquals("9", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_9, 0, false, false));
assertKeysEquals(",", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_COMMA, 0, false, false));
assertKeysEquals(".", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_DOT, 0, false, false));
}
} }

View File

@@ -93,7 +93,7 @@ public class ResizeTest extends TerminalTestCase {
enterString("\033[2J"); enterString("\033[2J");
for (int r = 0; r < rows; r++) { for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) { for (int c = 0; c < cols; c++) {
int style = getStyleAt(r, c); long style = getStyleAt(r, c);
assertEquals(119, TextStyle.decodeForeColor(style)); assertEquals(119, TextStyle.decodeForeColor(style));
assertEquals(129, TextStyle.decodeBackColor(style)); assertEquals(129, TextStyle.decodeBackColor(style));
} }
@@ -105,7 +105,7 @@ public class ResizeTest extends TerminalTestCase {
// After resize, screen should still be same color: // After resize, screen should still be same color:
for (int r = 0; r < rows - 2; r++) { for (int r = 0; r < rows - 2; r++) {
for (int c = 0; c < cols; c++) { for (int c = 0; c < cols; c++) {
int style = getStyleAt(r, c); long style = getStyleAt(r, c);
assertEquals(119, TextStyle.decodeForeColor(style)); assertEquals(119, TextStyle.decodeForeColor(style));
assertEquals(129, TextStyle.decodeBackColor(style)); assertEquals(129, TextStyle.decodeBackColor(style));
} }
@@ -116,7 +116,7 @@ public class ResizeTest extends TerminalTestCase {
resize(cols, rows); resize(cols, rows);
for (int r = 0; r < rows; r++) { for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) { for (int c = 0; c < cols; c++) {
int style = getStyleAt(r, c); long style = getStyleAt(r, c);
assertEquals(119, TextStyle.decodeForeColor(style)); assertEquals(119, TextStyle.decodeForeColor(style));
assertEquals("wrong at row=" + r, r >= 3 ? 200 : 129, TextStyle.decodeBackColor(style)); assertEquals("wrong at row=" + r, r >= 3 ? 200 : 129, TextStyle.decodeBackColor(style));
} }

View File

@@ -1,6 +1,6 @@
package com.termux.terminal; package com.termux.terminal;
public class ScreenBufferTest extends TerminalTest { public class ScreenBufferTest extends TerminalTestCase {
public void testBasics() { public void testBasics() {
TerminalBuffer screen = new TerminalBuffer(5, 3, 3); TerminalBuffer screen = new TerminalBuffer(5, 3, 3);

View File

@@ -147,12 +147,11 @@ public class TerminalTest extends TerminalTestCase {
enterString("\033[38;5;119m"); enterString("\033[38;5;119m");
assertEquals(119, mTerminal.mForeColor); assertEquals(119, mTerminal.mForeColor);
assertEquals(TextStyle.COLOR_INDEX_BACKGROUND, mTerminal.mBackColor); assertEquals(TextStyle.COLOR_INDEX_BACKGROUND, mTerminal.mBackColor);
enterString("\033[48;5;129m"); enterString("\033[48;5;129m");
assertEquals(119, mTerminal.mForeColor); assertEquals(119, mTerminal.mForeColor);
assertEquals(129, mTerminal.mBackColor); assertEquals(129, mTerminal.mBackColor);
// Invalid parameter: // Invalid parameter:
enterString("\033[48;8;129m"); enterString("\033[48;8;129m");
assertEquals(119, mTerminal.mForeColor); assertEquals(119, mTerminal.mForeColor);
assertEquals(129, mTerminal.mBackColor); assertEquals(129, mTerminal.mBackColor);
@@ -161,7 +160,31 @@ public class TerminalTest extends TerminalTestCase {
enterString("\033[38;5;178;48;5;179;m"); enterString("\033[38;5;178;48;5;179;m");
assertEquals(178, mTerminal.mForeColor); assertEquals(178, mTerminal.mForeColor);
assertEquals(179, mTerminal.mBackColor); assertEquals(179, mTerminal.mBackColor);
}
// 24 bit colors:
enterString(("\033[0m")); // Reset fg and bg colors.
enterString("\033[38;2;255;127;2m");
int expectedForeground = 0xff000000 | (255 << 16) | (127 << 8) | 2;
assertEquals(expectedForeground, mTerminal.mForeColor);
assertEquals(TextStyle.COLOR_INDEX_BACKGROUND, mTerminal.mBackColor);
enterString("\033[48;2;1;2;254m");
int expectedBackground = 0xff000000 | (1 << 16) | (2 << 8) | 254;
assertEquals(expectedForeground, mTerminal.mForeColor);
assertEquals(expectedBackground, mTerminal.mBackColor);
// 24 bit colors, set fg and bg at once:
enterString(("\033[0m")); // Reset fg and bg colors.
assertEquals(TextStyle.COLOR_INDEX_FOREGROUND, mTerminal.mForeColor);
assertEquals(TextStyle.COLOR_INDEX_BACKGROUND, mTerminal.mBackColor);
enterString("\033[38;2;255;127;2;48;2;1;2;254m");
assertEquals(expectedForeground, mTerminal.mForeColor);
assertEquals(expectedBackground, mTerminal.mBackColor);
// 24 bit colors, invalid input:
enterString("\033[38;2;300;127;2;48;2;1;300;254m");
assertEquals(expectedForeground, mTerminal.mForeColor);
assertEquals(expectedBackground, mTerminal.mBackColor);
}
public void testBackgroundColorErase() { public void testBackgroundColorErase() {
final int rows = 3; final int rows = 3;
@@ -169,7 +192,7 @@ public class TerminalTest extends TerminalTestCase {
withTerminalSized(cols, rows); withTerminalSized(cols, rows);
for (int r = 0; r < rows; r++) { for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) { for (int c = 0; c < cols; c++) {
int style = getStyleAt(r, c); long style = getStyleAt(r, c);
assertEquals(TextStyle.COLOR_INDEX_FOREGROUND, TextStyle.decodeForeColor(style)); assertEquals(TextStyle.COLOR_INDEX_FOREGROUND, TextStyle.decodeForeColor(style));
assertEquals(TextStyle.COLOR_INDEX_BACKGROUND, TextStyle.decodeBackColor(style)); assertEquals(TextStyle.COLOR_INDEX_BACKGROUND, TextStyle.decodeBackColor(style));
} }
@@ -182,7 +205,7 @@ public class TerminalTest extends TerminalTestCase {
enterString("\033[2J"); enterString("\033[2J");
for (int r = 0; r < rows; r++) { for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) { for (int c = 0; c < cols; c++) {
int style = getStyleAt(r, c); long style = getStyleAt(r, c);
assertEquals(119, TextStyle.decodeForeColor(style)); assertEquals(119, TextStyle.decodeForeColor(style));
assertEquals(129, TextStyle.decodeBackColor(style)); assertEquals(129, TextStyle.decodeBackColor(style));
} }
@@ -193,7 +216,7 @@ public class TerminalTest extends TerminalTestCase {
enterString("\033[2L"); enterString("\033[2L");
for (int r = 0; r < rows; r++) { for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) { for (int c = 0; c < cols; c++) {
int style = getStyleAt(r, c); long style = getStyleAt(r, c);
assertEquals((r == 0 || r == 1) ? 139 : 129, TextStyle.decodeBackColor(style)); assertEquals((r == 0 || r == 1) ? 139 : 129, TextStyle.decodeBackColor(style));
} }
} }

View File

@@ -240,7 +240,7 @@ public abstract class TerminalTestCase extends TestCase {
} }
/** For testing only. Encoded style according to {@link TextStyle}. */ /** For testing only. Encoded style according to {@link TextStyle}. */
public int getStyleAt(int externalRow, int column) { public long getStyleAt(int externalRow, int column) {
return mTerminal.getScreen().getStyleAt(externalRow, column); return mTerminal.getScreen().getStyleAt(externalRow, column);
} }
@@ -296,7 +296,7 @@ public abstract class TerminalTestCase extends TestCase {
} }
public void assertForegroundColorAt(int externalRow, int column, int color) { public void assertForegroundColorAt(int externalRow, int column, int color) {
int style = mTerminal.getScreen().mLines[mTerminal.getScreen().externalToInternalRow(externalRow)].getStyle(column); long style = mTerminal.getScreen().mLines[mTerminal.getScreen().externalToInternalRow(externalRow)].getStyle(column);
assertEquals(color, TextStyle.decodeForeColor(style)); assertEquals(color, TextStyle.decodeForeColor(style));
} }

View File

@@ -13,7 +13,7 @@ public class TextStyleTest extends TestCase {
for (int fx : ALL_EFFECTS) { for (int fx : ALL_EFFECTS) {
for (int fg = 0; fg < TextStyle.NUM_INDEXED_COLORS; fg++) { for (int fg = 0; fg < TextStyle.NUM_INDEXED_COLORS; fg++) {
for (int bg = 0; bg < TextStyle.NUM_INDEXED_COLORS; bg++) { for (int bg = 0; bg < TextStyle.NUM_INDEXED_COLORS; bg++) {
int encoded = TextStyle.encode(fg, bg, fx); long encoded = TextStyle.encode(fg, bg, fx);
assertEquals(fg, TextStyle.decodeForeColor(encoded)); assertEquals(fg, TextStyle.decodeForeColor(encoded));
assertEquals(bg, TextStyle.decodeBackColor(encoded)); assertEquals(bg, TextStyle.decodeBackColor(encoded));
assertEquals(fx, TextStyle.decodeEffect(encoded)); assertEquals(fx, TextStyle.decodeEffect(encoded));
@@ -22,7 +22,23 @@ public class TextStyleTest extends TestCase {
} }
} }
public void testEncodingCombinations() { public void testEncoding24Bit() {
int[] values = {255, 240, 127, 1, 0};
for (int red : values) {
for (int green : values) {
for (int blue : values) {
int argb = 0xFF000000 | (red << 16) | (green << 8) | blue;
long encoded = TextStyle.encode(argb, 0, 0);
assertEquals(argb, TextStyle.decodeForeColor(encoded));
encoded = TextStyle.encode(0, argb, 0);
assertEquals(argb, TextStyle.decodeBackColor(encoded));
}
}
}
}
public void testEncodingCombinations() {
for (int f1 : ALL_EFFECTS) { for (int f1 : ALL_EFFECTS) {
for (int f2 : ALL_EFFECTS) { for (int f2 : ALL_EFFECTS) {
int combined = f1 | f2; int combined = f1 | f2;
@@ -32,13 +48,13 @@ public class TextStyleTest extends TestCase {
} }
public void testEncodingStrikeThrough() { public void testEncodingStrikeThrough() {
int encoded = TextStyle.encode(TextStyle.COLOR_INDEX_FOREGROUND, TextStyle.COLOR_INDEX_BACKGROUND, long encoded = TextStyle.encode(TextStyle.COLOR_INDEX_FOREGROUND, TextStyle.COLOR_INDEX_BACKGROUND,
TextStyle.CHARACTER_ATTRIBUTE_STRIKETHROUGH); TextStyle.CHARACTER_ATTRIBUTE_STRIKETHROUGH);
assertTrue((TextStyle.decodeEffect(encoded) & TextStyle.CHARACTER_ATTRIBUTE_STRIKETHROUGH) != 0); assertTrue((TextStyle.decodeEffect(encoded) & TextStyle.CHARACTER_ATTRIBUTE_STRIKETHROUGH) != 0);
} }
public void testEncodingProtected() { public void testEncodingProtected() {
int encoded = TextStyle.encode(TextStyle.COLOR_INDEX_FOREGROUND, TextStyle.COLOR_INDEX_BACKGROUND, long encoded = TextStyle.encode(TextStyle.COLOR_INDEX_FOREGROUND, TextStyle.COLOR_INDEX_BACKGROUND,
TextStyle.CHARACTER_ATTRIBUTE_STRIKETHROUGH); TextStyle.CHARACTER_ATTRIBUTE_STRIKETHROUGH);
assertTrue((TextStyle.decodeEffect(encoded) & TextStyle.CHARACTER_ATTRIBUTE_PROTECTED) == 0); assertTrue((TextStyle.decodeEffect(encoded) & TextStyle.CHARACTER_ATTRIBUTE_PROTECTED) == 0);
encoded = TextStyle.encode(TextStyle.COLOR_INDEX_FOREGROUND, TextStyle.COLOR_INDEX_BACKGROUND, encoded = TextStyle.encode(TextStyle.COLOR_INDEX_FOREGROUND, TextStyle.COLOR_INDEX_BACKGROUND,

View File

@@ -5,7 +5,7 @@ buildscript {
jcenter() jcenter()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:2.1.2' classpath 'com.android.tools.build:gradle:2.1.3'
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files