Compare commits
75 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f40d5a26a | ||
|
|
df92896eef | ||
|
|
4c93cb42f1 | ||
|
|
34afb9de43 | ||
|
|
b6ea29d260 | ||
|
|
289d58a2f0 | ||
|
|
0501ce924b | ||
|
|
d12256f5e5 | ||
|
|
fcbc036f92 | ||
|
|
70d5839334 | ||
|
|
9c19540759 | ||
|
|
9fe0e49473 | ||
|
|
357b17e972 | ||
|
|
6334470f81 | ||
|
|
b8cdd59c68 | ||
|
|
6cf36fffd7 | ||
|
|
f10ecd4db5 | ||
|
|
26457e8443 | ||
|
|
6702846c7c | ||
|
|
0c6180bbb1 | ||
|
|
fcf07f6a19 | ||
|
|
e272e3b3b2 | ||
|
|
cdf0e72145 | ||
|
|
70245eb78c | ||
|
|
ee7631dfac | ||
|
|
5ecf5d12d1 | ||
|
|
0c8cd90f4e | ||
|
|
e1ea68913f | ||
|
|
dde854eba7 | ||
|
|
07a4607c04 | ||
|
|
883be37b98 | ||
|
|
d939d3d927 | ||
|
|
a0fa51eb92 | ||
|
|
755513bb33 | ||
|
|
8ad7a6669c | ||
|
|
60f7aada9e | ||
|
|
6aa0492434 | ||
|
|
019aa44837 | ||
|
|
8d3d5e147f | ||
|
|
44197b90e2 | ||
|
|
794c7ee333 | ||
|
|
0457ddbc69 | ||
|
|
283792af5e | ||
|
|
be7cfa603a | ||
|
|
3480bf7346 | ||
|
|
8314a2756c | ||
|
|
4de82d9fe0 | ||
|
|
d658e16801 | ||
|
|
26dcd5af88 | ||
|
|
8056013082 | ||
|
|
8e90545c4b | ||
|
|
426ddbacbd | ||
|
|
7e1f8a551f | ||
|
|
e169af0447 | ||
|
|
a2cb3fafee | ||
|
|
166710f14a | ||
|
|
c1a9b7726f | ||
|
|
afb339e9d8 | ||
|
|
64c23f498f | ||
|
|
1dc92b2a12 | ||
|
|
990a957383 | ||
|
|
7bb64d724c | ||
|
|
4609dd71c6 | ||
|
|
8d00f22d4c | ||
|
|
5532421ab2 | ||
|
|
d2b27978e2 | ||
|
|
30b05e9ab2 | ||
|
|
c350318c77 | ||
|
|
c9b49cef58 | ||
|
|
f9c642c672 | ||
|
|
c0a5e5f57a | ||
|
|
dfdc9b37e1 | ||
|
|
dfb22e6050 | ||
|
|
b95d84fe13 | ||
|
|
a73228b109 |
1
.gitignore
vendored
@@ -6,6 +6,7 @@
|
||||
build/
|
||||
*.apk
|
||||
*.so
|
||||
.externalNativeBuild
|
||||
|
||||
# Crashlytics configuations
|
||||
com_crashlytics_export_strings.xml
|
||||
|
||||
7
.idea/gradle.xml
generated
@@ -12,12 +12,7 @@
|
||||
<option value="$PROJECT_DIR$/app" />
|
||||
</set>
|
||||
</option>
|
||||
<option name="myModules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/app" />
|
||||
</set>
|
||||
</option>
|
||||
<option name="resolveModulePerSourceSet" value="false" />
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
|
||||
15
.travis.yml
@@ -1,4 +1,4 @@
|
||||
sudo: false
|
||||
sudo: true
|
||||
language: android
|
||||
jdk: oraclejdk8
|
||||
|
||||
@@ -12,10 +12,15 @@ android:
|
||||
components:
|
||||
- platform-tools
|
||||
- tools
|
||||
- build-tools-24.0.1
|
||||
- android-24
|
||||
- build-tools-25.0.1
|
||||
- android-25
|
||||
- extra-android-m2repository
|
||||
|
||||
before_install:
|
||||
- git clone https://github.com/urho3d/android-ndk.git $HOME/android-ndk
|
||||
- export ANDROID_NDK_HOME=$HOME/android-ndk
|
||||
- echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca-certificates.crt
|
||||
|
||||
script:
|
||||
- ./gradlew testDebugUnitTest
|
||||
|
||||
@@ -26,5 +31,5 @@ addons:
|
||||
description: "Terminal emulator and Linux environment for Android"
|
||||
notification_email: fredrik@fornwall.net
|
||||
build_command_prepend: "./gradlew clean"
|
||||
build_command: "./gradlew assemble"
|
||||
branch_pattern: coverity_scan
|
||||
build_command: "./gradlew build"
|
||||
branch_pattern: master
|
||||
|
||||
12
README.md
@@ -3,25 +3,19 @@ Termux app
|
||||
[](https://travis-ci.org/termux/termux-app)
|
||||
[](https://gitter.im/termux/termux)
|
||||
|
||||
|
||||
Termux is an Android terminal app and Linux environment.
|
||||
[Termux](https://termux.com) is an Android terminal app and Linux environment.
|
||||
|
||||
* [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.com](http://termux.com)
|
||||
* [Termux Help](http://termux.com/help/)
|
||||
* [Termux app on GitHub](https://github.com/termux/termux-app)
|
||||
* [Termux packages on GitHub](https://github.com/termux/termux-packages)
|
||||
* [Termux Google+ community](http://termux.com/community/)
|
||||
|
||||
Note that this repository is for the app itself (the user interface and the terminal emulation). For the packages installable inside the app, see [termux/termux-packages](https://github.com/termux/termux-packages)
|
||||
|
||||
License
|
||||
=======
|
||||
Released under [the GPLv3 license](https://www.gnu.org/licenses/gpl.html). Contains code from `Terminal Emulator for Android` which is released under [the Apache License 2.0](https://www.apache.org/licenses/).
|
||||
|
||||
Building JNI libraries
|
||||
======================
|
||||
Execute the `build-jnilibs.sh` script to build the required JNI libraries.
|
||||
|
||||
Terminal resources
|
||||
==================
|
||||
* [XTerm control sequences](http://invisible-island.net/xterm/ctlseqs/ctlseqs.html)
|
||||
|
||||
@@ -1,25 +1,29 @@
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
compileSdkVersion 24
|
||||
buildToolsVersion "24.0.1"
|
||||
compileSdkVersion 25
|
||||
buildToolsVersion "25.0.1"
|
||||
|
||||
dependencies {
|
||||
compile 'com.android.support:support-annotations:24.1.1'
|
||||
compile "com.android.support:support-v4:24.1.1"
|
||||
compile 'com.android.support:support-annotations:25.0.1'
|
||||
compile "com.android.support:support-v4:25.0.1"
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.termux"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 24
|
||||
versionCode 36
|
||||
versionName "0.35"
|
||||
targetSdkVersion 25
|
||||
versionCode 44
|
||||
versionName "0.44"
|
||||
|
||||
externalNativeBuild {
|
||||
ndkBuild {
|
||||
cFlags "-std=c11", "-Wall", "-Wextra", "-Werror", "-Os", "-fno-stack-protector", "-Wl,--gc-sections"
|
||||
}
|
||||
}
|
||||
|
||||
ndk {
|
||||
moduleName "libtermux"
|
||||
abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
|
||||
cFlags "-std=c11 -Wall -Wextra -Os -fno-stack-protector -nostdlib -Wl,--gc-sections"
|
||||
abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +34,12 @@ android {
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
|
||||
externalNativeBuild {
|
||||
ndkBuild {
|
||||
path "src/main/jni/Android.mk"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:fullBackupContent="@xml/backupscheme"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:banner="@drawable/banner"
|
||||
android:label="@string/application_name"
|
||||
android:theme="@style/Theme.Termux"
|
||||
@@ -34,6 +35,7 @@
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
|
||||
</intent-filter>
|
||||
<meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcuts" />
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
|
||||
@@ -4,58 +4,70 @@ import android.util.Log;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A background job launched by Termux.
|
||||
*/
|
||||
public final class BackgroundJob {
|
||||
|
||||
private static final String LOG_TAG = "termux-background";
|
||||
private static final String LOG_TAG = "termux-task";
|
||||
|
||||
final Process mProcess;
|
||||
|
||||
public BackgroundJob(File cwd, File fileToExecute, String[] args) throws IOException {
|
||||
String[] env = buildEnvironment(false, cwd.getAbsolutePath());
|
||||
public BackgroundJob(String cwd, String fileToExecute, final String[] args, final TermuxService service) {
|
||||
String[] env = buildEnvironment(false, cwd);
|
||||
if (cwd == null) cwd = TermuxService.HOME_PATH;
|
||||
|
||||
String[] progArray = new String[args.length + 1];
|
||||
final String[] progArray = setupProcessArgs(fileToExecute, args);
|
||||
final String processDescription = Arrays.toString(progArray);
|
||||
|
||||
mProcess = Runtime.getRuntime().exec(progArray, env, cwd);
|
||||
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
int exitCode = mProcess.waitFor();
|
||||
if (exitCode == 0) {
|
||||
Log.i(LOG_TAG, "exited normally");
|
||||
return;
|
||||
} else {
|
||||
Log.i(LOG_TAG, "exited with exit code: " + exitCode);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
// Ignore.
|
||||
}
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
Process process;
|
||||
try {
|
||||
process = Runtime.getRuntime().exec(progArray, env, new File(cwd));
|
||||
} catch (IOException e) {
|
||||
mProcess = null;
|
||||
// TODO: Visible error message?
|
||||
Log.e(LOG_TAG, "Failed running background job: " + processDescription, e);
|
||||
return;
|
||||
}
|
||||
|
||||
mProcess = process;
|
||||
final int pid = getPid(mProcess);
|
||||
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
Log.i(LOG_TAG, "[" + pid + "] starting: " + processDescription);
|
||||
InputStream stdout = mProcess.getInputStream();
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(stdout, StandardCharsets.UTF_8));
|
||||
String line;
|
||||
try {
|
||||
// FIXME: Long lines.
|
||||
while ((line = reader.readLine()) != null) {
|
||||
Log.i(LOG_TAG, line);
|
||||
Log.i(LOG_TAG, "[" + pid + "] stdout: " + line);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(LOG_TAG, "Error reading output", e);
|
||||
}
|
||||
|
||||
try {
|
||||
int exitCode = mProcess.waitFor();
|
||||
service.onBackgroundJobExited(BackgroundJob.this);
|
||||
if (exitCode == 0) {
|
||||
Log.i(LOG_TAG, "[" + pid + "] exited normally");
|
||||
} else {
|
||||
Log.w(LOG_TAG, "[" + pid + "] exited with code: " + exitCode);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
// Ignore.
|
||||
}
|
||||
}
|
||||
@@ -71,7 +83,7 @@ public final class BackgroundJob {
|
||||
try {
|
||||
// FIXME: Long lines.
|
||||
while ((line = reader.readLine()) != null) {
|
||||
Log.e(LOG_TAG, line);
|
||||
Log.i(LOG_TAG, "[" + pid + "] stderr: " + line);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// Ignore.
|
||||
@@ -80,7 +92,7 @@ public final class BackgroundJob {
|
||||
};
|
||||
}
|
||||
|
||||
public String[] buildEnvironment(boolean failSafe, String cwd) {
|
||||
public static String[] buildEnvironment(boolean failSafe, String cwd) {
|
||||
new File(TermuxService.HOME_PATH).mkdirs();
|
||||
|
||||
if (cwd == null) cwd = TermuxService.HOME_PATH;
|
||||
@@ -109,4 +121,75 @@ public final class BackgroundJob {
|
||||
}
|
||||
}
|
||||
|
||||
public static int getPid(Process p) {
|
||||
try {
|
||||
Field f = p.getClass().getDeclaredField("pid");
|
||||
f.setAccessible(true);
|
||||
try {
|
||||
return f.getInt(p);
|
||||
} finally {
|
||||
f.setAccessible(false);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static String[] setupProcessArgs(String fileToExecute, String[] args) {
|
||||
// The file to execute may either be:
|
||||
// - An elf file, in which we execute it directly.
|
||||
// - A script file without shebang, which we execute with our standard shell $PREFIX/bin/sh instead of the
|
||||
// system /system/bin/sh. The system shell may vary and may not work at all due to LD_LIBRARY_PATH.
|
||||
// - A file with shebang, which we try to handle with e.g. /bin/foo -> $PREFIX/bin/foo.
|
||||
String interpreter = null;
|
||||
try {
|
||||
File file = new File(fileToExecute);
|
||||
try (FileInputStream in = new FileInputStream(file)) {
|
||||
byte[] buffer = new byte[256];
|
||||
int bytesRead = in.read(buffer);
|
||||
if (bytesRead > 4) {
|
||||
if (buffer[0] == 0x7F && buffer[1] == 'E' && buffer[2] == 'L' && buffer[3] == 'F') {
|
||||
// Elf file, do nothing.
|
||||
} else if (buffer[0] == '#' && buffer[1] == '!') {
|
||||
// Try to parse shebang.
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (int i = 2; i < bytesRead; i++) {
|
||||
char c = (char) buffer[i];
|
||||
if (c == ' ' || c == '\n') {
|
||||
if (builder.length() == 0) {
|
||||
// Skip whitespace after shebang.
|
||||
continue;
|
||||
} else {
|
||||
// End of shebang.
|
||||
String executable = builder.toString();
|
||||
if (executable.startsWith("/usr") || executable.startsWith("/bin")) {
|
||||
String[] parts = executable.split("/");
|
||||
String binary = parts[parts.length - 1];
|
||||
interpreter = TermuxService.PREFIX_PATH + "/bin/" + binary;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
builder.append(c);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No shebang and no ELF, use standard shell.
|
||||
interpreter = TermuxService.PREFIX_PATH + "/bin/sh";
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// Ignore.
|
||||
}
|
||||
|
||||
List<String> result = new ArrayList<>();
|
||||
if (interpreter != null) result.add(interpreter);
|
||||
result.add(fileToExecute);
|
||||
if (args != null) {
|
||||
for (String arg : args) result.add(arg);
|
||||
}
|
||||
return result.toArray(new String[result.size()]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,8 +11,6 @@ import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import android.R;
|
||||
|
||||
public final class DialogUtils {
|
||||
|
||||
public interface TextSetListener {
|
||||
@@ -72,7 +70,7 @@ public final class DialogUtils {
|
||||
}
|
||||
|
||||
if (onNegative == null) {
|
||||
builder.setNegativeButton(R.string.cancel, null);
|
||||
builder.setNegativeButton(android.R.string.cancel, null);
|
||||
} else {
|
||||
builder.setNegativeButton(negativeButtonText, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
|
||||
@@ -165,8 +165,8 @@ public final class ExtraKeysView extends GridLayout {
|
||||
param.rightMargin = param.topMargin = 0;
|
||||
param.setGravity(Gravity.LEFT);
|
||||
float weight = "▲▼◀▶".contains(buttonText) ? 0.7f : 1.f;
|
||||
param.columnSpec = GridLayout.spec(col, weight);
|
||||
param.rowSpec = GridLayout.spec(row, 1.f);
|
||||
param.columnSpec = GridLayout.spec(col, GridLayout.FILL, weight);
|
||||
param.rowSpec = GridLayout.spec(row, GridLayout.FILL, 1.f);
|
||||
button.setLayoutParams(param);
|
||||
|
||||
addView(button);
|
||||
|
||||
@@ -49,6 +49,8 @@ import android.view.View.OnClickListener;
|
||||
import android.view.View.OnLongClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.OnItemClickListener;
|
||||
@@ -139,6 +141,8 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
||||
.setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION).build()).build();
|
||||
int mBellSoundId;
|
||||
|
||||
Animation mOnBellAnimation;
|
||||
|
||||
private final BroadcastReceiver mBroadcastReceiever = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
@@ -209,6 +213,8 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
||||
public void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
|
||||
mOnBellAnimation = AnimationUtils.loadAnimation(this, R.anim.on_bell);
|
||||
|
||||
mSettings = new TermuxPreferences(this);
|
||||
|
||||
setContentView(R.layout.drawer_layout);
|
||||
@@ -245,9 +251,15 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
||||
editText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
||||
@Override
|
||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||
String s = editText.getText().toString() + "\n";
|
||||
getCurrentTermSession().write(s);
|
||||
editText.setText("");
|
||||
TerminalSession session = getCurrentTermSession();
|
||||
if (session != null) {
|
||||
if (session.isRunning()) {
|
||||
session.write(editText.getText().toString() + "\n");
|
||||
} else {
|
||||
removeFinishedSession(session);
|
||||
}
|
||||
editText.setText("");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
@@ -396,20 +408,22 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
||||
|
||||
@Override
|
||||
public void onBell(TerminalSession session) {
|
||||
if (mIsVisible) {
|
||||
switch (mSettings.mBellBehaviour) {
|
||||
case TermuxPreferences.BELL_BEEP:
|
||||
mBellSoundPool.play(mBellSoundId, 1.f, 1.f, 1, 0, 1.f);
|
||||
break;
|
||||
case TermuxPreferences.BELL_VIBRATE:
|
||||
((Vibrator) getSystemService(VIBRATOR_SERVICE)).vibrate(50);
|
||||
break;
|
||||
case TermuxPreferences.BELL_IGNORE:
|
||||
// Ignore the bell character.
|
||||
break;
|
||||
}
|
||||
if (!mIsVisible) return;
|
||||
|
||||
mTerminalView.startAnimation(mOnBellAnimation);
|
||||
|
||||
switch (mSettings.mBellBehaviour) {
|
||||
case TermuxPreferences.BELL_BEEP:
|
||||
mBellSoundPool.play(mBellSoundId, 1.f, 1.f, 1, 0, 1.f);
|
||||
break;
|
||||
case TermuxPreferences.BELL_VIBRATE:
|
||||
((Vibrator) getSystemService(VIBRATOR_SERVICE)).vibrate(50);
|
||||
break;
|
||||
case TermuxPreferences.BELL_IGNORE:
|
||||
// Ignore the bell character.
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -423,8 +437,9 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
||||
final StyleSpan boldSpan = new StyleSpan(Typeface.BOLD);
|
||||
final StyleSpan italicSpan = new StyleSpan(Typeface.ITALIC);
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
public View getView(int position, View convertView, @NonNull ViewGroup parent) {
|
||||
View row = convertView;
|
||||
if (row == null) {
|
||||
LayoutInflater inflater = getLayoutInflater();
|
||||
@@ -507,7 +522,13 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
||||
finish();
|
||||
}
|
||||
} else {
|
||||
switchToSession(getStoredCurrentSessionOrLast());
|
||||
Intent i = getIntent();
|
||||
if (i != null && i.getAction().equals(Intent.ACTION_RUN)) {
|
||||
// Android 7.1 app shortcut from res/xml/shortcuts.xml.
|
||||
addNewSession(false, null);
|
||||
} else {
|
||||
switchToSession(getStoredCurrentSessionOrLast());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -528,6 +549,7 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
||||
@Override
|
||||
public void onTextSet(String text) {
|
||||
sessionToRename.mSessionName = text;
|
||||
mListViewAdapter.notifyDataSetChanged();
|
||||
}
|
||||
}, -1, null, -1, null, null);
|
||||
}
|
||||
@@ -846,4 +868,21 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
||||
mLastToast.show();
|
||||
}
|
||||
|
||||
public void removeFinishedSession(TerminalSession finishedSession) {
|
||||
// Return pressed with finished session - remove it.
|
||||
TermuxService service = mTermService;
|
||||
|
||||
int index = service.removeTermSession(finishedSession);
|
||||
mListViewAdapter.notifyDataSetChanged();
|
||||
if (mTermService.getSessions().isEmpty()) {
|
||||
// There are no sessions to show, so finish the activity.
|
||||
finish();
|
||||
} else {
|
||||
if (index >= service.getSessions().size()) {
|
||||
index = service.getSessions().size() - 1;
|
||||
}
|
||||
switchToSession(service.getSessions().get(index));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -184,25 +184,28 @@ final class TermuxInstaller {
|
||||
|
||||
/** Get bootstrap zip url for this systems cpu architecture. */
|
||||
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"
|
||||
// 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:
|
||||
// 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
|
||||
// an x86 system where arm emulation is available.
|
||||
final String[] androidArchNames = {"arm64-v8a", "x86_64", "x86", "armeabi-v7a"};
|
||||
final String[] termuxArchNames = {"aarch64", "x86_64", "i686", "arm"};
|
||||
|
||||
final List<String> supportedArches = Arrays.asList(Build.SUPPORTED_ABIS);
|
||||
for (int i = 0; i < termuxArchNames.length; i++) {
|
||||
if (supportedArches.contains(androidArchNames[i])) {
|
||||
termuxArch = termuxArchNames[i];
|
||||
break;
|
||||
// Note that we search for abi:s in preferred order (the ordering of the
|
||||
// Build.SUPPORTED_ABIS list) to avoid e.g. installing arm on an x86 system where arm
|
||||
// emulation is available.
|
||||
for (String androidArch : Build.SUPPORTED_ABIS) {
|
||||
switch (androidArch) {
|
||||
case "arm64-v8a": return "aarch64";
|
||||
case "armeabi-v7a": return "arm";
|
||||
case "x86_64": return "x86_64";
|
||||
case "x86": return "i686";
|
||||
}
|
||||
}
|
||||
|
||||
return new URL("https://termux.net/bootstrap/bootstrap-" + termuxArch + ".zip");
|
||||
throw new RuntimeException("Unable to determine arch from Build.SUPPORTED_ABIS = " +
|
||||
Arrays.toString(Build.SUPPORTED_ABIS));
|
||||
}
|
||||
|
||||
/** Delete a folder and all its content or throw. */
|
||||
@@ -254,9 +257,13 @@ final class TermuxInstaller {
|
||||
Os.symlink(moviesDir.getAbsolutePath(), new File(storageDir, "movies").getAbsolutePath());
|
||||
|
||||
final File[] dirs = context.getExternalFilesDirs(null);
|
||||
if (dirs != null && dirs.length >= 2) {
|
||||
final File externalDir = dirs[1];
|
||||
Os.symlink(externalDir.getAbsolutePath(), new File(storageDir, "external").getAbsolutePath());
|
||||
if (dirs != null && dirs.length > 1) {
|
||||
for (int i = 1; i < dirs.length; i++) {
|
||||
File dir = dirs[i];
|
||||
if (dir == null) continue;
|
||||
String symlinkName = "external-" + i;
|
||||
Os.symlink(dir.getAbsolutePath(), new File(storageDir, symlinkName).getAbsolutePath());
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(LOG_TAG, "Error setting up link", e);
|
||||
|
||||
@@ -58,25 +58,10 @@ public final class TermuxKeyListener implements TerminalKeyListener {
|
||||
public boolean onKeyDown(int keyCode, KeyEvent e, TerminalSession currentSession) {
|
||||
if (handleVirtualKeys(keyCode, e, true)) return true;
|
||||
|
||||
TermuxService service = mActivity.mTermService;
|
||||
|
||||
if (keyCode == KeyEvent.KEYCODE_ENTER && !currentSession.isRunning()) {
|
||||
// Return pressed with finished session - remove it.
|
||||
currentSession.finishIfRunning();
|
||||
|
||||
int index = service.removeTermSession(currentSession);
|
||||
mActivity.mListViewAdapter.notifyDataSetChanged();
|
||||
if (mActivity.mTermService.getSessions().isEmpty()) {
|
||||
// There are no sessions to show, so finish the activity.
|
||||
mActivity.finish();
|
||||
} else {
|
||||
if (index >= service.getSessions().size()) {
|
||||
index = service.getSessions().size() - 1;
|
||||
}
|
||||
mActivity.switchToSession(service.getSessions().get(index));
|
||||
}
|
||||
mActivity.removeFinishedSession(currentSession);
|
||||
return true;
|
||||
} else if (e.isCtrlPressed() && e.isShiftPressed()) {
|
||||
} else if (e.isCtrlPressed() && e.isAltPressed()) {
|
||||
// Get the unmodified code point:
|
||||
int unicodeChar = e.getUnicodeChar(0);
|
||||
|
||||
@@ -111,6 +96,7 @@ public final class TermuxKeyListener implements TerminalKeyListener {
|
||||
mActivity.changeFontSize(false);
|
||||
} else if (unicodeChar >= '1' && unicodeChar <= '9') {
|
||||
int num = unicodeChar - '1';
|
||||
TermuxService service = mActivity.mTermService;
|
||||
if (service.getSessions().size() > num)
|
||||
mActivity.switchToSession(service.getSessions().get(num));
|
||||
}
|
||||
@@ -174,7 +160,7 @@ public final class TermuxKeyListener implements TerminalKeyListener {
|
||||
resultingKeyCode = KeyEvent.KEYCODE_INSERT;
|
||||
break;
|
||||
case 'h':
|
||||
resultingKeyCode = KeyEvent.KEYCODE_MOVE_HOME;
|
||||
resultingCodePoint = '~';
|
||||
break;
|
||||
|
||||
// Special characters to input.
|
||||
@@ -237,11 +223,17 @@ public final class TermuxKeyListener implements TerminalKeyListener {
|
||||
}
|
||||
return true;
|
||||
} else if (ctrlDown) {
|
||||
if (codePoint == 106 /* Ctrl+j or \n */ && !session.isRunning()) {
|
||||
mActivity.removeFinishedSession(session);
|
||||
return true;
|
||||
}
|
||||
|
||||
List<TermuxPreferences.KeyboardShortcut> shortcuts = mActivity.mSettings.shortcuts;
|
||||
if (!shortcuts.isEmpty()) {
|
||||
int codePointLowerCase = Character.toLowerCase(codePoint);
|
||||
for (int i = shortcuts.size() - 1; i >= 0; i--) {
|
||||
TermuxPreferences.KeyboardShortcut shortcut = shortcuts.get(i);
|
||||
if (codePoint == shortcut.codePoint) {
|
||||
if (codePointLowerCase == shortcut.codePoint) {
|
||||
switch (shortcut.shortcutAction) {
|
||||
case TermuxPreferences.SHORTCUT_ACTION_CREATE_SESSION:
|
||||
mActivity.addNewSession(false, null);
|
||||
|
||||
@@ -105,7 +105,7 @@ final class TermuxPreferences {
|
||||
}
|
||||
|
||||
static void storeCurrentSession(Context context, TerminalSession session) {
|
||||
PreferenceManager.getDefaultSharedPreferences(context).edit().putString(TermuxPreferences.CURRENT_SESSION_KEY, session.mHandle).commit();
|
||||
PreferenceManager.getDefaultSharedPreferences(context).edit().putString(TermuxPreferences.CURRENT_SESSION_KEY, session.mHandle).apply();
|
||||
}
|
||||
|
||||
static TerminalSession getCurrentSession(TermuxActivity context) {
|
||||
@@ -184,9 +184,9 @@ final class TermuxPreferences {
|
||||
private void parseAction(String name, int shortcutAction, Properties props) {
|
||||
String value = props.getProperty(name);
|
||||
if (value == null) return;
|
||||
String[] parts = value.trim().split("\\+");
|
||||
String[] parts = value.toLowerCase().trim().split("\\+");
|
||||
String input = parts.length == 2 ? parts[1].trim() : null;
|
||||
if (!(parts.length == 2 && parts[0].trim().equalsIgnoreCase("ctrl")) || input.isEmpty() || input.length() > 2) {
|
||||
if (!(parts.length == 2 && parts[0].trim().equals("ctrl")) || input.isEmpty() || input.length() > 2) {
|
||||
Log.e("termux", "Keyboard shortcut '" + name + "' is not Ctrl+<something>");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import android.content.res.Resources;
|
||||
import android.net.Uri;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.os.Binder;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.PowerManager;
|
||||
import android.util.Log;
|
||||
@@ -48,18 +49,16 @@ public final class TermuxService extends Service implements SessionChangedCallba
|
||||
|
||||
private static final int NOTIFICATION_ID = 1337;
|
||||
|
||||
/** Intent action to stop the service. */
|
||||
private static final String ACTION_STOP_SERVICE = "com.termux.service_stop";
|
||||
/** Intent action to toggle the wake lock, {@link #mWakeLock}, which this service may hold. */
|
||||
private static final String ACTION_LOCK_WAKE = "com.termux.service_toggle_wake_lock";
|
||||
/** Intent action to toggle the wifi lock, {@link #mWifiLock}, which this service may hold. */
|
||||
private static final String ACTION_LOCK_WIFI = "com.termux.service_toggle_wifi_lock";
|
||||
private static final String ACTION_LOCK_WAKE = "com.termux.service_wake_lock";
|
||||
private static final String ACTION_UNLOCK_WAKE = "com.termux.service_wake_unlock";
|
||||
/** Intent action to launch a new terminal session. Executed from TermuxWidgetProvider. */
|
||||
public static final String ACTION_EXECUTE = "com.termux.service_execute";
|
||||
|
||||
public static final String EXTRA_ARGUMENTS = "com.termux.execute.arguments";
|
||||
|
||||
public static final String EXTRA_CURRENT_WORKING_DIRECTORY = "com.termux.execute.cwd";
|
||||
private static final String EXTRA_EXECUTE_IN_BACKGROUND = "com.termux.execute.background";
|
||||
|
||||
/** This service is only bound from inside the same process and never uses IPC. */
|
||||
class LocalBinder extends Binder {
|
||||
@@ -68,6 +67,8 @@ public final class TermuxService extends Service implements SessionChangedCallba
|
||||
|
||||
private final IBinder mBinder = new LocalBinder();
|
||||
|
||||
private final Handler mHandler = new Handler();
|
||||
|
||||
/**
|
||||
* The terminal sessions which this service manages.
|
||||
* <p/>
|
||||
@@ -76,9 +77,12 @@ public final class TermuxService extends Service implements SessionChangedCallba
|
||||
*/
|
||||
final List<TerminalSession> mTerminalSessions = new ArrayList<>();
|
||||
|
||||
final List<BackgroundJob> mBackgroundTasks = new ArrayList<>();
|
||||
|
||||
/** Note that the service may often outlive the activity, so need to clear this reference. */
|
||||
SessionChangedCallback mSessionChangeCallback;
|
||||
|
||||
/** The wake lock and wifi lock are always acquired and released together. */
|
||||
private PowerManager.WakeLock mWakeLock;
|
||||
private WifiManager.WifiLock mWifiLock;
|
||||
|
||||
@@ -99,41 +103,51 @@ public final class TermuxService extends Service implements SessionChangedCallba
|
||||
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
|
||||
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, EmulatorDebug.LOG_TAG);
|
||||
mWakeLock.acquire();
|
||||
} else {
|
||||
mWakeLock.release();
|
||||
mWakeLock = null;
|
||||
}
|
||||
updateNotification();
|
||||
} else if (ACTION_LOCK_WIFI.equals(action)) {
|
||||
if (mWifiLock == null) {
|
||||
|
||||
WifiManager wm = (WifiManager) getSystemService(Context.WIFI_SERVICE);
|
||||
mWifiLock = wm.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, EmulatorDebug.LOG_TAG);
|
||||
mWifiLock.acquire();
|
||||
} else {
|
||||
|
||||
updateNotification();
|
||||
}
|
||||
} else if (ACTION_UNLOCK_WAKE.equals(action)) {
|
||||
if (mWakeLock != null) {
|
||||
mWakeLock.release();
|
||||
mWakeLock = null;
|
||||
|
||||
mWifiLock.release();
|
||||
mWifiLock = null;
|
||||
|
||||
updateNotification();
|
||||
}
|
||||
updateNotification();
|
||||
} else if (ACTION_EXECUTE.equals(action)) {
|
||||
Uri executableUri = intent.getData();
|
||||
String executablePath = (executableUri == null ? null : executableUri.getPath());
|
||||
|
||||
String[] arguments = (executableUri == null ? null : intent.getStringArrayExtra(EXTRA_ARGUMENTS));
|
||||
String cwd = intent.getStringExtra(EXTRA_CURRENT_WORKING_DIRECTORY);
|
||||
TerminalSession newSession = createTermSession(executablePath, arguments, cwd, false);
|
||||
|
||||
// Transform executable path to session name, e.g. "/bin/do-something.sh" => "do something.sh".
|
||||
if (executablePath != null) {
|
||||
int lastSlash = executablePath.lastIndexOf('/');
|
||||
String name = (lastSlash == -1) ? executablePath : executablePath.substring(lastSlash + 1);
|
||||
name = name.replace('-', ' ');
|
||||
newSession.mSessionName = name;
|
||||
if (intent.getBooleanExtra(EXTRA_EXECUTE_IN_BACKGROUND, false)) {
|
||||
BackgroundJob task = new BackgroundJob(cwd, executablePath, arguments, this);
|
||||
mBackgroundTasks.add(task);
|
||||
updateNotification();
|
||||
} else {
|
||||
TerminalSession newSession = createTermSession(executablePath, arguments, cwd, false);
|
||||
|
||||
// Transform executable path to session name, e.g. "/bin/do-something.sh" => "do something.sh".
|
||||
if (executablePath != null) {
|
||||
int lastSlash = executablePath.lastIndexOf('/');
|
||||
String name = (lastSlash == -1) ? executablePath : executablePath.substring(lastSlash + 1);
|
||||
name = name.replace('-', ' ');
|
||||
newSession.mSessionName = name;
|
||||
}
|
||||
|
||||
// Make the newly created session the current one to be displayed:
|
||||
TermuxPreferences.storeCurrentSession(this, newSession);
|
||||
|
||||
// Launch the main Termux app, which will now show the current session:
|
||||
startActivity(new Intent(this, TermuxActivity.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
|
||||
}
|
||||
|
||||
// Make the newly created session the current one to be displayed:
|
||||
TermuxPreferences.storeCurrentSession(this, newSession);
|
||||
|
||||
// Launch the main Termux app, which will now show to current session:
|
||||
startActivity(new Intent(this, TermuxActivity.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
|
||||
} else if (action != null) {
|
||||
Log.e(EmulatorDebug.LOG_TAG, "Unknown TermuxService action: '" + action + "'");
|
||||
}
|
||||
@@ -155,8 +169,8 @@ public final class TermuxService extends Service implements SessionChangedCallba
|
||||
|
||||
/** Update the shown foreground service notification after making any changes that affect it. */
|
||||
private void updateNotification() {
|
||||
if (mWakeLock == null && mWifiLock == null && getSessions().isEmpty()) {
|
||||
// Exit if we are updating after the user disabled all locks with no sessions.
|
||||
if (mWakeLock == null && mTerminalSessions.isEmpty() && mBackgroundTasks.isEmpty()) {
|
||||
// Exit if we are updating after the user disabled all locks with no sessions or tasks running.
|
||||
stopSelf();
|
||||
} else {
|
||||
((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).notify(NOTIFICATION_ID, buildNotification());
|
||||
@@ -171,18 +185,15 @@ public final class TermuxService extends Service implements SessionChangedCallba
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notifyIntent, 0);
|
||||
|
||||
int sessionCount = mTerminalSessions.size();
|
||||
String contentText = sessionCount + " terminal session" + (sessionCount == 1 ? "" : "s");
|
||||
|
||||
boolean wakeLockHeld = mWakeLock != null;
|
||||
boolean wifiLockHeld = mWifiLock != null;
|
||||
if (wakeLockHeld && wifiLockHeld) {
|
||||
contentText += " (wake&wifi lock held)";
|
||||
} else if (wakeLockHeld) {
|
||||
contentText += " (wake lock held)";
|
||||
} else if (wifiLockHeld) {
|
||||
contentText += " (wifi lock held)";
|
||||
int taskCount = mBackgroundTasks.size();
|
||||
String contentText = sessionCount + " session" + (sessionCount == 1 ? "" : "s");
|
||||
if (taskCount > 0) {
|
||||
contentText += ", " + taskCount + " task" + (taskCount == 1 ? "" : "s");
|
||||
}
|
||||
|
||||
final boolean wakeLockHeld = mWakeLock != null;
|
||||
if (wakeLockHeld) contentText += " (wake lock held)";
|
||||
|
||||
Notification.Builder builder = new Notification.Builder(this);
|
||||
builder.setContentTitle(getText(R.string.application_name));
|
||||
builder.setContentText(contentText);
|
||||
@@ -192,7 +203,7 @@ public final class TermuxService extends Service implements SessionChangedCallba
|
||||
|
||||
// If holding a wake or wifi lock consider the notification of high priority since it's using power,
|
||||
// otherwise use a minimal priority since this is just a background service notification:
|
||||
builder.setPriority((wakeLockHeld || wifiLockHeld) ? Notification.PRIORITY_HIGH : Notification.PRIORITY_MIN);
|
||||
builder.setPriority((wakeLockHeld) ? Notification.PRIORITY_HIGH : Notification.PRIORITY_MIN);
|
||||
|
||||
// No need to show a timestamp:
|
||||
builder.setShowWhen(false);
|
||||
@@ -204,13 +215,13 @@ public final class TermuxService extends Service implements SessionChangedCallba
|
||||
Intent exitIntent = new Intent(this, TermuxService.class).setAction(ACTION_STOP_SERVICE);
|
||||
builder.addAction(android.R.drawable.ic_delete, res.getString(R.string.notification_action_exit), PendingIntent.getService(this, 0, exitIntent, 0));
|
||||
|
||||
Intent toggleWakeLockIntent = new Intent(this, TermuxService.class).setAction(ACTION_LOCK_WAKE);
|
||||
builder.addAction(android.R.drawable.ic_lock_lock, res.getString(R.string.notification_action_wakelock),
|
||||
PendingIntent.getService(this, 0, toggleWakeLockIntent, 0));
|
||||
|
||||
Intent toggleWifiLockIntent = new Intent(this, TermuxService.class).setAction(ACTION_LOCK_WIFI);
|
||||
builder.addAction(android.R.drawable.ic_lock_lock, res.getString(R.string.notification_action_wifilock),
|
||||
PendingIntent.getService(this, 0, toggleWifiLockIntent, 0));
|
||||
String newWakeAction = wakeLockHeld ? ACTION_UNLOCK_WAKE : ACTION_LOCK_WAKE;
|
||||
Intent toggleWakeLockIntent = new Intent(this, TermuxService.class).setAction(newWakeAction);
|
||||
String actionTitle = res.getString(wakeLockHeld ?
|
||||
R.string.notification_action_wake_unlock :
|
||||
R.string.notification_action_wake_lock);
|
||||
int actionIcon = wakeLockHeld ? android.R.drawable.ic_lock_idle_lock : android.R.drawable.ic_lock_lock;
|
||||
builder.addAction(actionIcon, actionTitle, PendingIntent.getService(this, 0, toggleWakeLockIntent, 0));
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
@@ -236,30 +247,9 @@ public final class TermuxService extends Service implements SessionChangedCallba
|
||||
|
||||
if (cwd == null) cwd = HOME_PATH;
|
||||
|
||||
final String termEnv = "TERM=xterm-256color";
|
||||
final String homeEnv = "HOME=" + HOME_PATH;
|
||||
final String prefixEnv = "PREFIX=" + PREFIX_PATH;
|
||||
final String androidRootEnv = "ANDROID_ROOT=" + System.getenv("ANDROID_ROOT");
|
||||
final String androidDataEnv = "ANDROID_DATA=" + System.getenv("ANDROID_DATA");
|
||||
// EXTERNAL_STORAGE is needed for /system/bin/am to work on at least
|
||||
// Samsung S7 - see https://plus.google.com/110070148244138185604/posts/gp8Lk3aCGp3.
|
||||
final String externalStorageEnv = "EXTERNAL_STORAGE=" + System.getenv("EXTERNAL_STORAGE");
|
||||
String[] env;
|
||||
if (failSafe) {
|
||||
// Keep the default path so that system binaries can be used in the failsafe session.
|
||||
final String pathEnv = "PATH=" + System.getenv("PATH");
|
||||
env = new String[]{termEnv, homeEnv, prefixEnv, androidRootEnv, androidDataEnv, pathEnv, externalStorageEnv};
|
||||
} else {
|
||||
final String ps1Env = "PS1=$ ";
|
||||
final String ldEnv = "LD_LIBRARY_PATH=" + PREFIX_PATH + "/lib";
|
||||
final String langEnv = "LANG=en_US.UTF-8";
|
||||
final String pathEnv = "PATH=" + PREFIX_PATH + "/bin:" + PREFIX_PATH + "/bin/applets";
|
||||
final String pwdEnv = "PWD=" + cwd;
|
||||
String[] env = BackgroundJob.buildEnvironment(failSafe, cwd);
|
||||
boolean isLoginShell = false;
|
||||
|
||||
env = new String[]{termEnv, homeEnv, prefixEnv, ps1Env, ldEnv, langEnv, pathEnv, pwdEnv, androidRootEnv, androidDataEnv, externalStorageEnv};
|
||||
}
|
||||
|
||||
String shellName;
|
||||
if (executablePath == null) {
|
||||
File shell = new File(HOME_PATH, ".termux/shell");
|
||||
if (shell.exists()) {
|
||||
@@ -290,23 +280,18 @@ public final class TermuxService extends Service implements SessionChangedCallba
|
||||
// Fall back to system shell as last resort:
|
||||
executablePath = "/system/bin/sh";
|
||||
}
|
||||
|
||||
String[] parts = executablePath.split("/");
|
||||
shellName = "-" + parts[parts.length - 1];
|
||||
} else {
|
||||
int lastSlashIndex = executablePath.lastIndexOf('/');
|
||||
shellName = lastSlashIndex == -1 ? executablePath : executablePath.substring(lastSlashIndex + 1);
|
||||
isLoginShell = true;
|
||||
}
|
||||
|
||||
String[] args;
|
||||
if (arguments == null) {
|
||||
args = new String[]{shellName};
|
||||
} else {
|
||||
args = new String[arguments.length + 1];
|
||||
args[0] = shellName;
|
||||
String[] processArgs = BackgroundJob.setupProcessArgs(executablePath, arguments);
|
||||
executablePath = processArgs[0];
|
||||
int lastSlashIndex = executablePath.lastIndexOf('/');
|
||||
String processName = (isLoginShell ? "-" : "") +
|
||||
(lastSlashIndex == -1 ? executablePath : executablePath.substring(lastSlashIndex + 1));
|
||||
|
||||
System.arraycopy(arguments, 0, args, 1, arguments.length);
|
||||
}
|
||||
String[] args = new String[processArgs.length];
|
||||
args[0] = processName;
|
||||
if (processArgs.length > 1) System.arraycopy(processArgs, 1, args, 1, processArgs.length - 1);
|
||||
|
||||
TerminalSession session = new TerminalSession(executablePath, cwd, args, env, this);
|
||||
mTerminalSessions.add(session);
|
||||
@@ -358,4 +343,13 @@ public final class TermuxService extends Service implements SessionChangedCallba
|
||||
if (mSessionChangeCallback != null) mSessionChangeCallback.onColorsChanged(session);
|
||||
}
|
||||
|
||||
public void onBackgroundJobExited(final BackgroundJob task) {
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mBackgroundTasks.remove(task);
|
||||
updateNotification();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ public class TermuxDocumentsProvider extends DocumentsProvider {
|
||||
row.add(Root.COLUMN_TITLE, applicationName);
|
||||
row.add(Root.COLUMN_MIME_TYPES, ALL_MIME_TYPES);
|
||||
row.add(Root.COLUMN_AVAILABLE_BYTES, BASE_DIR.getFreeSpace());
|
||||
row.add(Root.COLUMN_ICON, R.drawable.ic_launcher);
|
||||
row.add(Root.COLUMN_ICON, R.mipmap.ic_launcher);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -236,7 +236,7 @@ public class TermuxDocumentsProvider extends DocumentsProvider {
|
||||
row.add(Document.COLUMN_MIME_TYPE, mimeType);
|
||||
row.add(Document.COLUMN_LAST_MODIFIED, file.lastModified());
|
||||
row.add(Document.COLUMN_FLAGS, flags);
|
||||
row.add(Document.COLUMN_ICON, R.drawable.ic_launcher);
|
||||
row.add(Document.COLUMN_ICON, R.mipmap.ic_launcher);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,9 +26,9 @@ import static android.view.KeyEvent.KEYCODE_F7;
|
||||
import static android.view.KeyEvent.KEYCODE_F8;
|
||||
import static android.view.KeyEvent.KEYCODE_F9;
|
||||
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_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_1;
|
||||
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
|
||||
// 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("#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("*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("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("kr", KEYCODE_DPAD_RIGHT);
|
||||
|
||||
@@ -107,7 +107,7 @@ public final class KeyHandler {
|
||||
// t_K3 <kPageUp> keypad page-up key
|
||||
// t_K4 <kEnd> keypad end 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("K4", KEYCODE_MOVE_END);
|
||||
TERMCAP_TO_KEYCODE.put("K5", KEYCODE_PAGE_DOWN);
|
||||
@@ -162,7 +162,9 @@ public final class KeyHandler {
|
||||
case KEYCODE_DPAD_LEFT:
|
||||
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');
|
||||
case KEYCODE_MOVE_END:
|
||||
return (keyMode == 0) ? (cursorApp ? "\033OF" : "\033[F") : transformForModifiers("\033[1", keyMode, 'F');
|
||||
@@ -217,9 +219,6 @@ public final class KeyHandler {
|
||||
case KEYCODE_FORWARD_DEL:
|
||||
return transformForModifiers("\033[3", keyMode, '~');
|
||||
|
||||
case KEYCODE_NUMPAD_DOT:
|
||||
return keypadApplication ? "\033On" : "\033[3~";
|
||||
|
||||
case KEYCODE_PAGE_UP:
|
||||
return "\033[5~";
|
||||
case KEYCODE_PAGE_DOWN:
|
||||
@@ -249,12 +248,14 @@ public final class KeyHandler {
|
||||
return keypadApplication ? transformForModifiers("\033O", keyMode, 'k') : "+";
|
||||
case KEYCODE_NUMPAD_COMMA:
|
||||
return ",";
|
||||
case KEYCODE_NUMPAD_DOT:
|
||||
return keypadApplication ? "\033On" : ".";
|
||||
case KEYCODE_NUMPAD_SUBTRACT:
|
||||
return keypadApplication ? transformForModifiers("\033O", keyMode, 'm') : "-";
|
||||
case KEYCODE_NUMPAD_DIVIDE:
|
||||
return keypadApplication ? transformForModifiers("\033O", keyMode, 'o') : "/";
|
||||
case KEYCODE_NUMPAD_0:
|
||||
return keypadApplication ? transformForModifiers("\033O", keyMode, 'p') : "1";
|
||||
return keypadApplication ? transformForModifiers("\033O", keyMode, 'p') : "0";
|
||||
case KEYCODE_NUMPAD_1:
|
||||
return keypadApplication ? transformForModifiers("\033O", keyMode, 'q') : "1";
|
||||
case KEYCODE_NUMPAD_2:
|
||||
|
||||
@@ -140,7 +140,7 @@ public final class TerminalBuffer {
|
||||
* @param newRows The number of rows the screen should have.
|
||||
* @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):
|
||||
if (newColumns == mColumns && newRows <= mTotalRows) {
|
||||
// Fast resize where just the rows changed.
|
||||
@@ -237,7 +237,7 @@ public final class TerminalBuffer {
|
||||
}
|
||||
|
||||
int currentOldCol = 0;
|
||||
int styleAtCol = 0;
|
||||
long styleAtCol = 0;
|
||||
for (int i = 0; i < lastNonSpaceIndex; i++) {
|
||||
// Note that looping over java character, not cells.
|
||||
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 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)
|
||||
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
|
||||
* 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) {
|
||||
throw new IllegalArgumentException(
|
||||
"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];
|
||||
}
|
||||
|
||||
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)
|
||||
throw new IllegalArgumentException("row=" + row + ", column=" + column + ", mScreenRows=" + mScreenRows + ", mColumns=" + mColumns);
|
||||
row = externalToInternalRow(row);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -407,7 +407,7 @@ public final class TerminalBuffer {
|
||||
int startOfLine = (rectangular || y == top) ? left : leftMargin;
|
||||
int endOfLine = (rectangular || y + 1 == bottom) ? right : rightMargin;
|
||||
for (int x = startOfLine; x < endOfLine; x++) {
|
||||
int currentStyle = line.getStyle(x);
|
||||
long currentStyle = line.getStyle(x);
|
||||
int foreColor = TextStyle.decodeForeColor(currentStyle);
|
||||
int backColor = TextStyle.decodeBackColor(currentStyle);
|
||||
int effect = TextStyle.decodeEffect(currentStyle);
|
||||
|
||||
@@ -57,7 +57,7 @@ public final class TerminalColorScheme {
|
||||
0xff808080, 0xff8a8a8a, 0xff949494, 0xff9e9e9e, 0xffa8a8a8, 0xffb2b2b2, 0xffbcbcbc, 0xffc6c6c6, 0xffd0d0d0, 0xffdadada, 0xffe4e4e4, 0xffeeeeee,
|
||||
|
||||
// COLOR_INDEX_DEFAULT_FOREGROUND, COLOR_INDEX_DEFAULT_BACKGROUND and COLOR_INDEX_DEFAULT_CURSOR:
|
||||
0xffffffff, 0xff000000, 0xffffffff};
|
||||
0xffffffff, 0xff000000, 0xffA9AAA9};
|
||||
|
||||
public final int[] mDefaultColors = new int[TextStyle.NUM_INDEXED_COLORS];
|
||||
|
||||
|
||||
@@ -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
|
||||
* System xterm terminal, which in turn is an emulator for a subset of the Digital Equipment Corporation vt100 terminal.
|
||||
* <p/>
|
||||
* <p>
|
||||
* References:
|
||||
* <ul>
|
||||
* <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 is active, you cannot scroll back to view saved lines).
|
||||
* <p/>
|
||||
* <p>
|
||||
* See http://www.xfree86.org/current/ctlseqs.html#The%20Alternate%20Screen%20Buffer
|
||||
*/
|
||||
final TerminalBuffer mAltBuffer;
|
||||
@@ -206,10 +206,15 @@ public final class TerminalEmulator {
|
||||
*/
|
||||
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;
|
||||
|
||||
/** Current TextStyle effect */
|
||||
/** Current {@link TextStyle} effect. */
|
||||
private int mEffect;
|
||||
|
||||
/**
|
||||
@@ -611,7 +616,7 @@ public final class TerminalEmulator {
|
||||
int left = Math.min(getArg(argIndex++, 1, true) + effectiveLeftMargin, effectiveRightMargin + 1);
|
||||
int bottom = Math.min(getArg(argIndex++, mRows, true) + effectiveTopMargin, effectiveBottomMargin);
|
||||
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 col = left - 1; col < right; col++)
|
||||
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"
|
||||
// Reverse attributes in rectangular area (DECRARA - http://www.vt100.net/docs/vt510-rm/DECRARA).
|
||||
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 left = Math.min(getArg(1, 1, true) - 1, effectiveRightMargin) + effectiveLeftMargin;
|
||||
int bottom = Math.min(getArg(2, mRows, true) + 1, effectiveBottomMargin - 1) + effectiveTopMargin;
|
||||
@@ -717,7 +722,6 @@ public final class TerminalEmulator {
|
||||
}
|
||||
break;
|
||||
case ESC_PERCENT:
|
||||
Log.i(EmulatorDebug.LOG_TAG, "Ignoring character set sequence 'ESC % " + (char) b + "'");
|
||||
break;
|
||||
case ESC_OSC:
|
||||
doOsc(b);
|
||||
@@ -953,7 +957,7 @@ public final class TerminalEmulator {
|
||||
unknownSequence(b);
|
||||
break;
|
||||
}
|
||||
int style = getStyle();
|
||||
long style = getStyle();
|
||||
for (int row = startRow; row < endRow; row++) {
|
||||
for (int col = startCol; col < endCol; col++) {
|
||||
if ((TextStyle.decodeEffect(mScreen.getStyleAt(row, col)) & TextStyle.CHARACTER_ATTRIBUTE_PROTECTED) == 0)
|
||||
@@ -1687,43 +1691,42 @@ public final class TerminalEmulator {
|
||||
} else if (code >= 30 && code <= 37) {
|
||||
mForeColor = code - 30;
|
||||
} else if (code == 38 || code == 48) {
|
||||
// ISO-8613-3 controls to set foreground (38) or background (48) colors.
|
||||
// P_s = (38|48) ; 2 ; P_r ; P_g ; P_b => Set to RGB value in range (0-255).
|
||||
// P_s = (38|48) ; 5 ; P_s => Set to indexed color.
|
||||
if (i + 2 <= mArgIndex) {
|
||||
int color = -1;
|
||||
int firstArg = mArgs[i + 1];
|
||||
if (firstArg == 2) {
|
||||
if (i + 4 > mArgIndex) {
|
||||
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"
|
||||
// Extended set foreground(38)/background (48) color.
|
||||
// This is followed by either "2;$R;$G;$B" to set a 24-bit color or
|
||||
// "5;$INDEX" to set an indexed color.
|
||||
if (i + 2 > mArgIndex) continue;
|
||||
int firstArg = mArgs[i + 1];
|
||||
if (firstArg == 2) {
|
||||
if (i + 4 > mArgIndex) {
|
||||
Log.w(EmulatorDebug.LOG_TAG, "Too few CSI" + code + ";2 RGB arguments");
|
||||
} else {
|
||||
finishSequenceAndLogError("Invalid ISO-8613-3 SGR first argument: " + firstArg);
|
||||
}
|
||||
if (i != -1) {
|
||||
if (color >= 0 && color < TextStyle.NUM_INDEXED_COLORS) {
|
||||
if (code == 38) {
|
||||
mForeColor = color;
|
||||
} else {
|
||||
mBackColor = color;
|
||||
}
|
||||
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 {
|
||||
if (LOG_ESCAPE_SEQUENCES)
|
||||
Log.w(EmulatorDebug.LOG_TAG, "Invalid color index: " + color);
|
||||
int argbColor = 0xff000000 | (red << 16) | (green << 8) | blue;
|
||||
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.
|
||||
mForeColor = TextStyle.COLOR_INDEX_FOREGROUND;
|
||||
@@ -1924,7 +1927,7 @@ public final class TerminalEmulator {
|
||||
mScreen.blockSet(sx, sy, w, h, ' ', getStyle());
|
||||
}
|
||||
|
||||
private int getStyle() {
|
||||
private long getStyle() {
|
||||
return TextStyle.encode(mForeColor, mBackColor, mEffect);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,13 +20,13 @@ public final class TerminalRow {
|
||||
/** If this row has been line wrapped due to text output at the end of line. */
|
||||
boolean mLineWrap;
|
||||
/** 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. */
|
||||
public TerminalRow(int columns, int style) {
|
||||
public TerminalRow(int columns, long style) {
|
||||
mColumns = columns;
|
||||
mText = new char[(int) (SPARE_CAPACITY_FACTOR * columns)];
|
||||
mStyle = new int[columns];
|
||||
mStyle = new long[columns];
|
||||
clear(style);
|
||||
}
|
||||
|
||||
@@ -112,14 +112,14 @@ public final class TerminalRow {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void clear(int style) {
|
||||
public void clear(long style) {
|
||||
Arrays.fill(mText, ' ');
|
||||
Arrays.fill(mStyle, style);
|
||||
mSpaceUsed = (short) mColumns;
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
final int newCodePointDisplayWidth = WcWidth.width(codePoint);
|
||||
@@ -225,7 +225,7 @@ public final class TerminalRow {
|
||||
return true;
|
||||
}
|
||||
|
||||
public final int getStyle(int column) {
|
||||
public final long getStyle(int column) {
|
||||
return mStyle[column];
|
||||
}
|
||||
|
||||
|
||||
@@ -173,7 +173,7 @@ public final class TerminalSession extends TerminalOutput {
|
||||
* @param rows The number of rows in the terminal window.
|
||||
*/
|
||||
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];
|
||||
mTerminalFileDescriptor = JNI.createSubprocess(mShellPath, mCwd, mArgs, mEnv, processId, rows, columns);
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
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}.
|
||||
* <p/>
|
||||
* The foreground and background colors take 9 bits each, leaving (32-9-9)=14 bits for effect flags. Using 9 for now
|
||||
* (the different CHARACTER_ATTRIBUTE_* bits).
|
||||
* The bit layout is:
|
||||
* - 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 {
|
||||
|
||||
@@ -25,6 +27,10 @@ public final class TextStyle {
|
||||
public final static int CHARACTER_ATTRIBUTE_PROTECTED = 1 << 7;
|
||||
/** Dim colors. Also known as faint or half intensity. */
|
||||
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_BACKGROUND = 257;
|
||||
@@ -34,22 +40,47 @@ public final class TextStyle {
|
||||
public final static int NUM_INDEXED_COLORS = 259;
|
||||
|
||||
/** 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) {
|
||||
return ((effect & 0b111111111) << 18) | ((foreColor & 0b111111111) << 9) | (backColor & 0b111111111);
|
||||
static long encode(int foreColor, int backColor, int effect) {
|
||||
long result = effect & 0b111111111;
|
||||
if ((0xff000000 & foreColor) == 0xff000000) {
|
||||
// 24-bit color.
|
||||
result |= CHARACTER_ATTRIBUTE_TRUECOLOR_FOREGROUND | ((foreColor & 0x00ffffffL) << 40L);
|
||||
} else {
|
||||
// Indexed color.
|
||||
result |= (foreColor & 0b111111111L) << 40;
|
||||
}
|
||||
if ((0xff000000 & backColor) == 0xff000000) {
|
||||
// 24-bit color.
|
||||
result |= CHARACTER_ATTRIBUTE_TRUECOLOR_BACKGROUND | ((backColor & 0x00ffffffL) << 16L);
|
||||
} else {
|
||||
// Indexed color.
|
||||
result |= (backColor & 0b111111111L) << 16L;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static int decodeForeColor(int encodedColor) {
|
||||
return (encodedColor >> 9) & 0b111111111;
|
||||
public static int decodeForeColor(long style) {
|
||||
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) {
|
||||
return encodedColor & 0b111111111;
|
||||
public static int decodeBackColor(long style) {
|
||||
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) {
|
||||
return (encodedColor >> 18) & 0b111111111;
|
||||
public static int decodeEffect(long style) {
|
||||
return (int) (style & 0b11111111111);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,102 +1,452 @@
|
||||
package com.termux.terminal;
|
||||
|
||||
/**
|
||||
* wcwidth() implementation from http://git.musl-libc.org/cgit/musl/tree/src/ctype
|
||||
* <p/>
|
||||
* Modified to return 0 instead of -1.
|
||||
* Implementation of wcwidth(3) for Unicode 9.
|
||||
*
|
||||
* Implementation from https://github.com/jquast/wcwidth but we return 0 for unprintable characters.
|
||||
*/
|
||||
public final class WcWidth {
|
||||
|
||||
private static final short table[] = {16, 16, 16, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 16, 16, 32, 16, 16, 16, 33, 34, 35, 36, 37, 38,
|
||||
39, 16, 16, 40, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 41, 42, 16, 16, 43, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
|
||||
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
|
||||
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
|
||||
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 44, 16, 45, 46, 47, 48, 16, 16, 16, 16, 16,
|
||||
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
|
||||
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
|
||||
49, 16, 16, 50, 51, 16, 52, 16, 16, 16, 16, 16, 16, 16, 16, 53, 16, 16, 16, 16, 16, 54, 55, 16, 16, 16, 16, 56, 16, 16, 16, 16, 16, 16, 16, 16, 16,
|
||||
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
|
||||
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
|
||||
16, 16, 16, 16, 16, 57, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
|
||||
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
|
||||
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 58, 59, 16, 16, 16, 16, 16, 16,
|
||||
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
|
||||
16, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
248, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 254, 255, 255, 255, 255, 191, 182, 0, 0, 0,
|
||||
0, 0, 0, 0, 31, 0, 255, 7, 0, 0, 0, 0, 0, 248, 255, 255, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 191, 159, 61, 0, 0, 0, 128, 2, 0, 0, 0,
|
||||
255, 255, 255, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 1, 0, 0, 0, 0, 0, 0, 248, 15, 0, 0, 0, 192, 251, 239, 62, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 127, 7, 0, 0, 0, 0, 0, 0, 20, 254, 33, 254, 0, 12, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 16, 30, 32, 0,
|
||||
0, 12, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 16, 134, 57, 2, 0, 0, 0, 35, 0, 6, 0, 0, 0, 0, 0, 0, 16, 190, 33, 0, 0, 12, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 144,
|
||||
30, 32, 64, 0, 12, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 1, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 193, 61, 96, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 144, 64, 48, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 32, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 92, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 242, 7, 128, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 242, 27, 0, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 160, 2, 0, 0, 0, 0, 0, 0, 254,
|
||||
127, 223, 224, 255, 254, 255, 255, 255, 31, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 253, 102, 0, 0, 0, 195, 1, 0, 30, 0, 100, 32, 0, 32, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 0, 0, 0,
|
||||
28, 0, 0, 0, 12, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 176, 63, 64, 254, 15, 32, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 135, 1, 4, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
128, 1, 0, 0, 0, 0, 0, 0, 64, 127, 229, 31, 248, 159, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 208, 23, 4, 0, 0, 0, 0,
|
||||
248, 15, 0, 3, 0, 0, 0, 60, 11, 0, 0, 0, 0, 0, 0, 64, 163, 3, 0, 0, 0, 0, 0, 0, 240, 207, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
247, 255, 253, 33, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 127, 0, 0, 240, 0, 248, 0, 0,
|
||||
0, 124, 0, 0, 0, 0, 0, 0, 31, 252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
|
||||
255, 0, 0, 0, 0, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128,
|
||||
247, 63, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 68, 8, 0, 0, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0,
|
||||
255, 255, 3, 0, 0, 0, 0, 0, 192, 63, 0, 0, 128, 255, 3, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 200, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 126, 102,
|
||||
0, 8, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 157, 193, 2, 0, 0, 0, 0, 48, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 32, 33, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0,
|
||||
127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 110, 240, 0,
|
||||
0, 0, 0, 0, 135, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 255, 127, 0, 0, 0, 0, 0, 0, 0, 3, 0,
|
||||
0, 0, 0, 0, 120, 38, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 128, 239, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 128, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 3, 248, 255, 231, 15, 0, 0, 0, 60, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,};
|
||||
// From https://github.com/jquast/wcwidth/blob/master/wcwidth/table_zero.py
|
||||
// t commit 0d7de112202cc8b2ebe9232ff4a5c954f19d561a (2016-07-02):
|
||||
private static final int[][] ZERO_WIDTH = {
|
||||
{0x0300, 0x036f}, // Combining Grave Accent ..Combining Latin Small Le
|
||||
{0x0483, 0x0489}, // Combining Cyrillic Titlo..Combining Cyrillic Milli
|
||||
{0x0591, 0x05bd}, // Hebrew Accent Etnahta ..Hebrew Point Meteg
|
||||
{0x05bf, 0x05bf}, // Hebrew Point Rafe ..Hebrew Point Rafe
|
||||
{0x05c1, 0x05c2}, // Hebrew Point Shin Dot ..Hebrew Point Sin Dot
|
||||
{0x05c4, 0x05c5}, // Hebrew Mark Upper Dot ..Hebrew Mark Lower Dot
|
||||
{0x05c7, 0x05c7}, // Hebrew Point Qamats Qata..Hebrew Point Qamats Qata
|
||||
{0x0610, 0x061a}, // Arabic Sign Sallallahou ..Arabic Small Kasra
|
||||
{0x064b, 0x065f}, // Arabic Fathatan ..Arabic Wavy Hamza Below
|
||||
{0x0670, 0x0670}, // Arabic Letter Superscrip..Arabic Letter Superscrip
|
||||
{0x06d6, 0x06dc}, // Arabic Small High Ligatu..Arabic Small High Seen
|
||||
{0x06df, 0x06e4}, // Arabic Small High Rounde..Arabic Small High Madda
|
||||
{0x06e7, 0x06e8}, // Arabic Small High Yeh ..Arabic Small High Noon
|
||||
{0x06ea, 0x06ed}, // Arabic Empty Centre Low ..Arabic Small Low Meem
|
||||
{0x0711, 0x0711}, // Syriac Letter Superscrip..Syriac Letter Superscrip
|
||||
{0x0730, 0x074a}, // Syriac Pthaha Above ..Syriac Barrekh
|
||||
{0x07a6, 0x07b0}, // Thaana Abafili ..Thaana Sukun
|
||||
{0x07eb, 0x07f3}, // Nko Combining Sh||t High..Nko Combining Double Dot
|
||||
{0x0816, 0x0819}, // Samaritan Mark In ..Samaritan Mark Dagesh
|
||||
{0x081b, 0x0823}, // Samaritan Mark Epentheti..Samaritan Vowel Sign A
|
||||
{0x0825, 0x0827}, // Samaritan Vowel Sign Sho..Samaritan Vowel Sign U
|
||||
{0x0829, 0x082d}, // Samaritan Vowel Sign Lon..Samaritan Mark Nequdaa
|
||||
{0x0859, 0x085b}, // Mandaic Affrication Mark..Mandaic Gemination Mark
|
||||
{0x08d4, 0x08e1}, // (nil) ..
|
||||
{0x08e3, 0x0902}, // Arabic Turned Damma Belo..Devanagari Sign Anusvara
|
||||
{0x093a, 0x093a}, // Devanagari Vowel Sign Oe..Devanagari Vowel Sign Oe
|
||||
{0x093c, 0x093c}, // Devanagari Sign Nukta ..Devanagari Sign Nukta
|
||||
{0x0941, 0x0948}, // Devanagari Vowel Sign U ..Devanagari Vowel Sign Ai
|
||||
{0x094d, 0x094d}, // Devanagari Sign Virama ..Devanagari Sign Virama
|
||||
{0x0951, 0x0957}, // Devanagari Stress Sign U..Devanagari Vowel Sign Uu
|
||||
{0x0962, 0x0963}, // Devanagari Vowel Sign Vo..Devanagari Vowel Sign Vo
|
||||
{0x0981, 0x0981}, // Bengali Sign Candrabindu..Bengali Sign Candrabindu
|
||||
{0x09bc, 0x09bc}, // Bengali Sign Nukta ..Bengali Sign Nukta
|
||||
{0x09c1, 0x09c4}, // Bengali Vowel Sign U ..Bengali Vowel Sign Vocal
|
||||
{0x09cd, 0x09cd}, // Bengali Sign Virama ..Bengali Sign Virama
|
||||
{0x09e2, 0x09e3}, // Bengali Vowel Sign Vocal..Bengali Vowel Sign Vocal
|
||||
{0x0a01, 0x0a02}, // Gurmukhi Sign Adak Bindi..Gurmukhi Sign Bindi
|
||||
{0x0a3c, 0x0a3c}, // Gurmukhi Sign Nukta ..Gurmukhi Sign Nukta
|
||||
{0x0a41, 0x0a42}, // Gurmukhi Vowel Sign U ..Gurmukhi Vowel Sign Uu
|
||||
{0x0a47, 0x0a48}, // Gurmukhi Vowel Sign Ee ..Gurmukhi Vowel Sign Ai
|
||||
{0x0a4b, 0x0a4d}, // Gurmukhi Vowel Sign Oo ..Gurmukhi Sign Virama
|
||||
{0x0a51, 0x0a51}, // Gurmukhi Sign Udaat ..Gurmukhi Sign Udaat
|
||||
{0x0a70, 0x0a71}, // Gurmukhi Tippi ..Gurmukhi Addak
|
||||
{0x0a75, 0x0a75}, // Gurmukhi Sign Yakash ..Gurmukhi Sign Yakash
|
||||
{0x0a81, 0x0a82}, // Gujarati Sign Candrabind..Gujarati Sign Anusvara
|
||||
{0x0abc, 0x0abc}, // Gujarati Sign Nukta ..Gujarati Sign Nukta
|
||||
{0x0ac1, 0x0ac5}, // Gujarati Vowel Sign U ..Gujarati Vowel Sign Cand
|
||||
{0x0ac7, 0x0ac8}, // Gujarati Vowel Sign E ..Gujarati Vowel Sign Ai
|
||||
{0x0acd, 0x0acd}, // Gujarati Sign Virama ..Gujarati Sign Virama
|
||||
{0x0ae2, 0x0ae3}, // Gujarati Vowel Sign Voca..Gujarati Vowel Sign Voca
|
||||
{0x0b01, 0x0b01}, // ||iya Sign Candrabindu ..||iya Sign Candrabindu
|
||||
{0x0b3c, 0x0b3c}, // ||iya Sign Nukta ..||iya Sign Nukta
|
||||
{0x0b3f, 0x0b3f}, // ||iya Vowel Sign I ..||iya Vowel Sign I
|
||||
{0x0b41, 0x0b44}, // ||iya Vowel Sign U ..||iya Vowel Sign Vocalic
|
||||
{0x0b4d, 0x0b4d}, // ||iya Sign Virama ..||iya Sign Virama
|
||||
{0x0b56, 0x0b56}, // ||iya Ai Length Mark ..||iya Ai Length Mark
|
||||
{0x0b62, 0x0b63}, // ||iya Vowel Sign Vocalic..||iya Vowel Sign Vocalic
|
||||
{0x0b82, 0x0b82}, // Tamil Sign Anusvara ..Tamil Sign Anusvara
|
||||
{0x0bc0, 0x0bc0}, // Tamil Vowel Sign Ii ..Tamil Vowel Sign Ii
|
||||
{0x0bcd, 0x0bcd}, // Tamil Sign Virama ..Tamil Sign Virama
|
||||
{0x0c00, 0x0c00}, // Telugu Sign Combining Ca..Telugu Sign Combining Ca
|
||||
{0x0c3e, 0x0c40}, // Telugu Vowel Sign Aa ..Telugu Vowel Sign Ii
|
||||
{0x0c46, 0x0c48}, // Telugu Vowel Sign E ..Telugu Vowel Sign Ai
|
||||
{0x0c4a, 0x0c4d}, // Telugu Vowel Sign O ..Telugu Sign Virama
|
||||
{0x0c55, 0x0c56}, // Telugu Length Mark ..Telugu Ai Length Mark
|
||||
{0x0c62, 0x0c63}, // Telugu Vowel Sign Vocali..Telugu Vowel Sign Vocali
|
||||
{0x0c81, 0x0c81}, // Kannada Sign Candrabindu..Kannada Sign Candrabindu
|
||||
{0x0cbc, 0x0cbc}, // Kannada Sign Nukta ..Kannada Sign Nukta
|
||||
{0x0cbf, 0x0cbf}, // Kannada Vowel Sign I ..Kannada Vowel Sign I
|
||||
{0x0cc6, 0x0cc6}, // Kannada Vowel Sign E ..Kannada Vowel Sign E
|
||||
{0x0ccc, 0x0ccd}, // Kannada Vowel Sign Au ..Kannada Sign Virama
|
||||
{0x0ce2, 0x0ce3}, // Kannada Vowel Sign Vocal..Kannada Vowel Sign Vocal
|
||||
{0x0d01, 0x0d01}, // Malayalam Sign Candrabin..Malayalam Sign Candrabin
|
||||
{0x0d41, 0x0d44}, // Malayalam Vowel Sign U ..Malayalam Vowel Sign Voc
|
||||
{0x0d4d, 0x0d4d}, // Malayalam Sign Virama ..Malayalam Sign Virama
|
||||
{0x0d62, 0x0d63}, // Malayalam Vowel Sign Voc..Malayalam Vowel Sign Voc
|
||||
{0x0dca, 0x0dca}, // Sinhala Sign Al-lakuna ..Sinhala Sign Al-lakuna
|
||||
{0x0dd2, 0x0dd4}, // Sinhala Vowel Sign Ketti..Sinhala Vowel Sign Ketti
|
||||
{0x0dd6, 0x0dd6}, // Sinhala Vowel Sign Diga ..Sinhala Vowel Sign Diga
|
||||
{0x0e31, 0x0e31}, // Thai Character Mai Han-a..Thai Character Mai Han-a
|
||||
{0x0e34, 0x0e3a}, // Thai Character Sara I ..Thai Character Phinthu
|
||||
{0x0e47, 0x0e4e}, // Thai Character Maitaikhu..Thai Character Yamakkan
|
||||
{0x0eb1, 0x0eb1}, // Lao Vowel Sign Mai Kan ..Lao Vowel Sign Mai Kan
|
||||
{0x0eb4, 0x0eb9}, // Lao Vowel Sign I ..Lao Vowel Sign Uu
|
||||
{0x0ebb, 0x0ebc}, // Lao Vowel Sign Mai Kon ..Lao Semivowel Sign Lo
|
||||
{0x0ec8, 0x0ecd}, // Lao Tone Mai Ek ..Lao Niggahita
|
||||
{0x0f18, 0x0f19}, // Tibetan Astrological Sig..Tibetan Astrological Sig
|
||||
{0x0f35, 0x0f35}, // Tibetan Mark Ngas Bzung ..Tibetan Mark Ngas Bzung
|
||||
{0x0f37, 0x0f37}, // Tibetan Mark Ngas Bzung ..Tibetan Mark Ngas Bzung
|
||||
{0x0f39, 0x0f39}, // Tibetan Mark Tsa -phru ..Tibetan Mark Tsa -phru
|
||||
{0x0f71, 0x0f7e}, // Tibetan Vowel Sign Aa ..Tibetan Sign Rjes Su Nga
|
||||
{0x0f80, 0x0f84}, // Tibetan Vowel Sign Rever..Tibetan Mark Halanta
|
||||
{0x0f86, 0x0f87}, // Tibetan Sign Lci Rtags ..Tibetan Sign Yang Rtags
|
||||
{0x0f8d, 0x0f97}, // Tibetan Subjoined Sign L..Tibetan Subjoined Letter
|
||||
{0x0f99, 0x0fbc}, // Tibetan Subjoined Letter..Tibetan Subjoined Letter
|
||||
{0x0fc6, 0x0fc6}, // Tibetan Symbol Padma Gda..Tibetan Symbol Padma Gda
|
||||
{0x102d, 0x1030}, // Myanmar Vowel Sign I ..Myanmar Vowel Sign Uu
|
||||
{0x1032, 0x1037}, // Myanmar Vowel Sign Ai ..Myanmar Sign Dot Below
|
||||
{0x1039, 0x103a}, // Myanmar Sign Virama ..Myanmar Sign Asat
|
||||
{0x103d, 0x103e}, // Myanmar Consonant Sign M..Myanmar Consonant Sign M
|
||||
{0x1058, 0x1059}, // Myanmar Vowel Sign Vocal..Myanmar Vowel Sign Vocal
|
||||
{0x105e, 0x1060}, // Myanmar Consonant Sign M..Myanmar Consonant Sign M
|
||||
{0x1071, 0x1074}, // Myanmar Vowel Sign Geba ..Myanmar Vowel Sign Kayah
|
||||
{0x1082, 0x1082}, // Myanmar Consonant Sign S..Myanmar Consonant Sign S
|
||||
{0x1085, 0x1086}, // Myanmar Vowel Sign Shan ..Myanmar Vowel Sign Shan
|
||||
{0x108d, 0x108d}, // Myanmar Sign Shan Counci..Myanmar Sign Shan Counci
|
||||
{0x109d, 0x109d}, // Myanmar Vowel Sign Aiton..Myanmar Vowel Sign Aiton
|
||||
{0x135d, 0x135f}, // Ethiopic Combining Gemin..Ethiopic Combining Gemin
|
||||
{0x1712, 0x1714}, // Tagalog Vowel Sign I ..Tagalog Sign Virama
|
||||
{0x1732, 0x1734}, // Hanunoo Vowel Sign I ..Hanunoo Sign Pamudpod
|
||||
{0x1752, 0x1753}, // Buhid Vowel Sign I ..Buhid Vowel Sign U
|
||||
{0x1772, 0x1773}, // Tagbanwa Vowel Sign I ..Tagbanwa Vowel Sign U
|
||||
{0x17b4, 0x17b5}, // Khmer Vowel Inherent Aq ..Khmer Vowel Inherent Aa
|
||||
{0x17b7, 0x17bd}, // Khmer Vowel Sign I ..Khmer Vowel Sign Ua
|
||||
{0x17c6, 0x17c6}, // Khmer Sign Nikahit ..Khmer Sign Nikahit
|
||||
{0x17c9, 0x17d3}, // Khmer Sign Muusikatoan ..Khmer Sign Bathamasat
|
||||
{0x17dd, 0x17dd}, // Khmer Sign Atthacan ..Khmer Sign Atthacan
|
||||
{0x180b, 0x180d}, // Mongolian Free Variation..Mongolian Free Variation
|
||||
{0x1885, 0x1886}, // Mongolian Letter Ali Gal..Mongolian Letter Ali Gal
|
||||
{0x18a9, 0x18a9}, // Mongolian Letter Ali Gal..Mongolian Letter Ali Gal
|
||||
{0x1920, 0x1922}, // Limbu Vowel Sign A ..Limbu Vowel Sign U
|
||||
{0x1927, 0x1928}, // Limbu Vowel Sign E ..Limbu Vowel Sign O
|
||||
{0x1932, 0x1932}, // Limbu Small Letter Anusv..Limbu Small Letter Anusv
|
||||
{0x1939, 0x193b}, // Limbu Sign Mukphreng ..Limbu Sign Sa-i
|
||||
{0x1a17, 0x1a18}, // Buginese Vowel Sign I ..Buginese Vowel Sign U
|
||||
{0x1a1b, 0x1a1b}, // Buginese Vowel Sign Ae ..Buginese Vowel Sign Ae
|
||||
{0x1a56, 0x1a56}, // Tai Tham Consonant Sign ..Tai Tham Consonant Sign
|
||||
{0x1a58, 0x1a5e}, // Tai Tham Sign Mai Kang L..Tai Tham Consonant Sign
|
||||
{0x1a60, 0x1a60}, // Tai Tham Sign Sakot ..Tai Tham Sign Sakot
|
||||
{0x1a62, 0x1a62}, // Tai Tham Vowel Sign Mai ..Tai Tham Vowel Sign Mai
|
||||
{0x1a65, 0x1a6c}, // Tai Tham Vowel Sign I ..Tai Tham Vowel Sign Oa B
|
||||
{0x1a73, 0x1a7c}, // Tai Tham Vowel Sign Oa A..Tai Tham Sign Khuen-lue
|
||||
{0x1a7f, 0x1a7f}, // Tai Tham Combining Crypt..Tai Tham Combining Crypt
|
||||
{0x1ab0, 0x1abe}, // Combining Doubled Circum..Combining Parentheses Ov
|
||||
{0x1b00, 0x1b03}, // Balinese Sign Ulu Ricem ..Balinese Sign Surang
|
||||
{0x1b34, 0x1b34}, // Balinese Sign Rerekan ..Balinese Sign Rerekan
|
||||
{0x1b36, 0x1b3a}, // Balinese Vowel Sign Ulu ..Balinese Vowel Sign Ra R
|
||||
{0x1b3c, 0x1b3c}, // Balinese Vowel Sign La L..Balinese Vowel Sign La L
|
||||
{0x1b42, 0x1b42}, // Balinese Vowel Sign Pepe..Balinese Vowel Sign Pepe
|
||||
{0x1b6b, 0x1b73}, // Balinese Musical Symbol ..Balinese Musical Symbol
|
||||
{0x1b80, 0x1b81}, // Sundanese Sign Panyecek ..Sundanese Sign Panglayar
|
||||
{0x1ba2, 0x1ba5}, // Sundanese Consonant Sign..Sundanese Vowel Sign Pan
|
||||
{0x1ba8, 0x1ba9}, // Sundanese Vowel Sign Pam..Sundanese Vowel Sign Pan
|
||||
{0x1bab, 0x1bad}, // Sundanese Sign Virama ..Sundanese Consonant Sign
|
||||
{0x1be6, 0x1be6}, // Batak Sign Tompi ..Batak Sign Tompi
|
||||
{0x1be8, 0x1be9}, // Batak Vowel Sign Pakpak ..Batak Vowel Sign Ee
|
||||
{0x1bed, 0x1bed}, // Batak Vowel Sign Karo O ..Batak Vowel Sign Karo O
|
||||
{0x1bef, 0x1bf1}, // Batak Vowel Sign U F|| S..Batak Consonant Sign H
|
||||
{0x1c2c, 0x1c33}, // Lepcha Vowel Sign E ..Lepcha Consonant Sign T
|
||||
{0x1c36, 0x1c37}, // Lepcha Sign Ran ..Lepcha Sign Nukta
|
||||
{0x1cd0, 0x1cd2}, // Vedic Tone Karshana ..Vedic Tone Prenkha
|
||||
{0x1cd4, 0x1ce0}, // Vedic Sign Yajurvedic Mi..Vedic Tone Rigvedic Kash
|
||||
{0x1ce2, 0x1ce8}, // Vedic Sign Visarga Svari..Vedic Sign Visarga Anuda
|
||||
{0x1ced, 0x1ced}, // Vedic Sign Tiryak ..Vedic Sign Tiryak
|
||||
{0x1cf4, 0x1cf4}, // Vedic Tone Candra Above ..Vedic Tone Candra Above
|
||||
{0x1cf8, 0x1cf9}, // Vedic Tone Ring Above ..Vedic Tone Double Ring A
|
||||
{0x1dc0, 0x1df5}, // Combining Dotted Grave A..Combining Up Tack Above
|
||||
{0x1dfb, 0x1dff}, // (nil) ..Combining Right Arrowhea
|
||||
{0x20d0, 0x20f0}, // Combining Left Harpoon A..Combining Asterisk Above
|
||||
{0x2cef, 0x2cf1}, // Coptic Combining Ni Abov..Coptic Combining Spiritu
|
||||
{0x2d7f, 0x2d7f}, // Tifinagh Consonant Joine..Tifinagh Consonant Joine
|
||||
{0x2de0, 0x2dff}, // Combining Cyrillic Lette..Combining Cyrillic Lette
|
||||
{0x302a, 0x302d}, // Ideographic Level Tone M..Ideographic Entering Ton
|
||||
{0x3099, 0x309a}, // Combining Katakana-hirag..Combining Katakana-hirag
|
||||
{0xa66f, 0xa672}, // Combining Cyrillic Vzmet..Combining Cyrillic Thous
|
||||
{0xa674, 0xa67d}, // Combining Cyrillic Lette..Combining Cyrillic Payer
|
||||
{0xa69e, 0xa69f}, // Combining Cyrillic Lette..Combining Cyrillic Lette
|
||||
{0xa6f0, 0xa6f1}, // Bamum Combining Mark Koq..Bamum Combining Mark Tuk
|
||||
{0xa802, 0xa802}, // Syloti Nagri Sign Dvisva..Syloti Nagri Sign Dvisva
|
||||
{0xa806, 0xa806}, // Syloti Nagri Sign Hasant..Syloti Nagri Sign Hasant
|
||||
{0xa80b, 0xa80b}, // Syloti Nagri Sign Anusva..Syloti Nagri Sign Anusva
|
||||
{0xa825, 0xa826}, // Syloti Nagri Vowel Sign ..Syloti Nagri Vowel Sign
|
||||
{0xa8c4, 0xa8c5}, // Saurashtra Sign Virama ..
|
||||
{0xa8e0, 0xa8f1}, // Combining Devanagari Dig..Combining Devanagari Sig
|
||||
{0xa926, 0xa92d}, // Kayah Li Vowel Ue ..Kayah Li Tone Calya Plop
|
||||
{0xa947, 0xa951}, // Rejang Vowel Sign I ..Rejang Consonant Sign R
|
||||
{0xa980, 0xa982}, // Javanese Sign Panyangga ..Javanese Sign Layar
|
||||
{0xa9b3, 0xa9b3}, // Javanese Sign Cecak Telu..Javanese Sign Cecak Telu
|
||||
{0xa9b6, 0xa9b9}, // Javanese Vowel Sign Wulu..Javanese Vowel Sign Suku
|
||||
{0xa9bc, 0xa9bc}, // Javanese Vowel Sign Pepe..Javanese Vowel Sign Pepe
|
||||
{0xa9e5, 0xa9e5}, // Myanmar Sign Shan Saw ..Myanmar Sign Shan Saw
|
||||
{0xaa29, 0xaa2e}, // Cham Vowel Sign Aa ..Cham Vowel Sign Oe
|
||||
{0xaa31, 0xaa32}, // Cham Vowel Sign Au ..Cham Vowel Sign Ue
|
||||
{0xaa35, 0xaa36}, // Cham Consonant Sign La ..Cham Consonant Sign Wa
|
||||
{0xaa43, 0xaa43}, // Cham Consonant Sign Fina..Cham Consonant Sign Fina
|
||||
{0xaa4c, 0xaa4c}, // Cham Consonant Sign Fina..Cham Consonant Sign Fina
|
||||
{0xaa7c, 0xaa7c}, // Myanmar Sign Tai Laing T..Myanmar Sign Tai Laing T
|
||||
{0xaab0, 0xaab0}, // Tai Viet Mai Kang ..Tai Viet Mai Kang
|
||||
{0xaab2, 0xaab4}, // Tai Viet Vowel I ..Tai Viet Vowel U
|
||||
{0xaab7, 0xaab8}, // Tai Viet Mai Khit ..Tai Viet Vowel Ia
|
||||
{0xaabe, 0xaabf}, // Tai Viet Vowel Am ..Tai Viet Tone Mai Ek
|
||||
{0xaac1, 0xaac1}, // Tai Viet Tone Mai Tho ..Tai Viet Tone Mai Tho
|
||||
{0xaaec, 0xaaed}, // Meetei Mayek Vowel Sign ..Meetei Mayek Vowel Sign
|
||||
{0xaaf6, 0xaaf6}, // Meetei Mayek Virama ..Meetei Mayek Virama
|
||||
{0xabe5, 0xabe5}, // Meetei Mayek Vowel Sign ..Meetei Mayek Vowel Sign
|
||||
{0xabe8, 0xabe8}, // Meetei Mayek Vowel Sign ..Meetei Mayek Vowel Sign
|
||||
{0xabed, 0xabed}, // Meetei Mayek Apun Iyek ..Meetei Mayek Apun Iyek
|
||||
{0xfb1e, 0xfb1e}, // Hebrew Point Judeo-spani..Hebrew Point Judeo-spani
|
||||
{0xfe00, 0xfe0f}, // Variation Select||-1 ..Variation Select||-16
|
||||
{0xfe20, 0xfe2f}, // Combining Ligature Left ..Combining Cyrillic Titlo
|
||||
{0x101fd, 0x101fd}, // Phaistos Disc Sign Combi..Phaistos Disc Sign Combi
|
||||
{0x102e0, 0x102e0}, // Coptic Epact Thousands M..Coptic Epact Thousands M
|
||||
{0x10376, 0x1037a}, // Combining Old Permic Let..Combining Old Permic Let
|
||||
{0x10a01, 0x10a03}, // Kharoshthi Vowel Sign I ..Kharoshthi Vowel Sign Vo
|
||||
{0x10a05, 0x10a06}, // Kharoshthi Vowel Sign E ..Kharoshthi Vowel Sign O
|
||||
{0x10a0c, 0x10a0f}, // Kharoshthi Vowel Length ..Kharoshthi Sign Visarga
|
||||
{0x10a38, 0x10a3a}, // Kharoshthi Sign Bar Abov..Kharoshthi Sign Dot Belo
|
||||
{0x10a3f, 0x10a3f}, // Kharoshthi Virama ..Kharoshthi Virama
|
||||
{0x10ae5, 0x10ae6}, // Manichaean Abbreviation ..Manichaean Abbreviation
|
||||
{0x11001, 0x11001}, // Brahmi Sign Anusvara ..Brahmi Sign Anusvara
|
||||
{0x11038, 0x11046}, // Brahmi Vowel Sign Aa ..Brahmi Virama
|
||||
{0x1107f, 0x11081}, // Brahmi Number Joiner ..Kaithi Sign Anusvara
|
||||
{0x110b3, 0x110b6}, // Kaithi Vowel Sign U ..Kaithi Vowel Sign Ai
|
||||
{0x110b9, 0x110ba}, // Kaithi Sign Virama ..Kaithi Sign Nukta
|
||||
{0x11100, 0x11102}, // Chakma Sign Candrabindu ..Chakma Sign Visarga
|
||||
{0x11127, 0x1112b}, // Chakma Vowel Sign A ..Chakma Vowel Sign Uu
|
||||
{0x1112d, 0x11134}, // Chakma Vowel Sign Ai ..Chakma Maayyaa
|
||||
{0x11173, 0x11173}, // Mahajani Sign Nukta ..Mahajani Sign Nukta
|
||||
{0x11180, 0x11181}, // Sharada Sign Candrabindu..Sharada Sign Anusvara
|
||||
{0x111b6, 0x111be}, // Sharada Vowel Sign U ..Sharada Vowel Sign O
|
||||
{0x111ca, 0x111cc}, // Sharada Sign Nukta ..Sharada Extra Sh||t Vowe
|
||||
{0x1122f, 0x11231}, // Khojki Vowel Sign U ..Khojki Vowel Sign Ai
|
||||
{0x11234, 0x11234}, // Khojki Sign Anusvara ..Khojki Sign Anusvara
|
||||
{0x11236, 0x11237}, // Khojki Sign Nukta ..Khojki Sign Shadda
|
||||
{0x1123e, 0x1123e}, // (nil) ..
|
||||
{0x112df, 0x112df}, // Khudawadi Sign Anusvara ..Khudawadi Sign Anusvara
|
||||
{0x112e3, 0x112ea}, // Khudawadi Vowel Sign U ..Khudawadi Sign Virama
|
||||
{0x11300, 0x11301}, // Grantha Sign Combining A..Grantha Sign Candrabindu
|
||||
{0x1133c, 0x1133c}, // Grantha Sign Nukta ..Grantha Sign Nukta
|
||||
{0x11340, 0x11340}, // Grantha Vowel Sign Ii ..Grantha Vowel Sign Ii
|
||||
{0x11366, 0x1136c}, // Combining Grantha Digit ..Combining Grantha Digit
|
||||
{0x11370, 0x11374}, // Combining Grantha Letter..Combining Grantha Letter
|
||||
{0x11438, 0x1143f}, // (nil) ..
|
||||
{0x11442, 0x11444}, // (nil) ..
|
||||
{0x11446, 0x11446}, // (nil) ..
|
||||
{0x114b3, 0x114b8}, // Tirhuta Vowel Sign U ..Tirhuta Vowel Sign Vocal
|
||||
{0x114ba, 0x114ba}, // Tirhuta Vowel Sign Sh||t..Tirhuta Vowel Sign Sh||t
|
||||
{0x114bf, 0x114c0}, // Tirhuta Sign Candrabindu..Tirhuta Sign Anusvara
|
||||
{0x114c2, 0x114c3}, // Tirhuta Sign Virama ..Tirhuta Sign Nukta
|
||||
{0x115b2, 0x115b5}, // Siddham Vowel Sign U ..Siddham Vowel Sign Vocal
|
||||
{0x115bc, 0x115bd}, // Siddham Sign Candrabindu..Siddham Sign Anusvara
|
||||
{0x115bf, 0x115c0}, // Siddham Sign Virama ..Siddham Sign Nukta
|
||||
{0x115dc, 0x115dd}, // Siddham Vowel Sign Alter..Siddham Vowel Sign Alter
|
||||
{0x11633, 0x1163a}, // Modi Vowel Sign U ..Modi Vowel Sign Ai
|
||||
{0x1163d, 0x1163d}, // Modi Sign Anusvara ..Modi Sign Anusvara
|
||||
{0x1163f, 0x11640}, // Modi Sign Virama ..Modi Sign Ardhacandra
|
||||
{0x116ab, 0x116ab}, // Takri Sign Anusvara ..Takri Sign Anusvara
|
||||
{0x116ad, 0x116ad}, // Takri Vowel Sign Aa ..Takri Vowel Sign Aa
|
||||
{0x116b0, 0x116b5}, // Takri Vowel Sign U ..Takri Vowel Sign Au
|
||||
{0x116b7, 0x116b7}, // Takri Sign Nukta ..Takri Sign Nukta
|
||||
{0x1171d, 0x1171f}, // Ahom Consonant Sign Medi..Ahom Consonant Sign Medi
|
||||
{0x11722, 0x11725}, // Ahom Vowel Sign I ..Ahom Vowel Sign Uu
|
||||
{0x11727, 0x1172b}, // Ahom Vowel Sign Aw ..Ahom Sign Killer
|
||||
{0x11c30, 0x11c36}, // (nil) ..
|
||||
{0x11c38, 0x11c3d}, // (nil) ..
|
||||
{0x11c3f, 0x11c3f}, // (nil) ..
|
||||
{0x11c92, 0x11ca7}, // (nil) ..
|
||||
{0x11caa, 0x11cb0}, // (nil) ..
|
||||
{0x11cb2, 0x11cb3}, // (nil) ..
|
||||
{0x11cb5, 0x11cb6}, // (nil) ..
|
||||
{0x16af0, 0x16af4}, // Bassa Vah Combining High..Bassa Vah Combining High
|
||||
{0x16b30, 0x16b36}, // Pahawh Hmong Mark Cim Tu..Pahawh Hmong Mark Cim Ta
|
||||
{0x16f8f, 0x16f92}, // Miao Tone Right ..Miao Tone Below
|
||||
{0x1bc9d, 0x1bc9e}, // Duployan Thick Letter Se..Duployan Double Mark
|
||||
{0x1d167, 0x1d169}, // Musical Symbol Combining..Musical Symbol Combining
|
||||
{0x1d17b, 0x1d182}, // Musical Symbol Combining..Musical Symbol Combining
|
||||
{0x1d185, 0x1d18b}, // Musical Symbol Combining..Musical Symbol Combining
|
||||
{0x1d1aa, 0x1d1ad}, // Musical Symbol Combining..Musical Symbol Combining
|
||||
{0x1d242, 0x1d244}, // Combining Greek Musical ..Combining Greek Musical
|
||||
{0x1da00, 0x1da36}, // Signwriting Head Rim ..Signwriting Air Sucking
|
||||
{0x1da3b, 0x1da6c}, // Signwriting Mouth Closed..Signwriting Excitement
|
||||
{0x1da75, 0x1da75}, // Signwriting Upper Body T..Signwriting Upper Body T
|
||||
{0x1da84, 0x1da84}, // Signwriting Location Hea..Signwriting Location Hea
|
||||
{0x1da9b, 0x1da9f}, // Signwriting Fill Modifie..Signwriting Fill Modifie
|
||||
{0x1daa1, 0x1daaf}, // Signwriting Rotation Mod..Signwriting Rotation Mod
|
||||
{0x1e000, 0x1e006}, // (nil) ..
|
||||
{0x1e008, 0x1e018}, // (nil) ..
|
||||
{0x1e01b, 0x1e021}, // (nil) ..
|
||||
{0x1e023, 0x1e024}, // (nil) ..
|
||||
{0x1e026, 0x1e02a}, // (nil) ..
|
||||
{0x1e8d0, 0x1e8d6}, // Mende Kikakui Combining ..Mende Kikakui Combining
|
||||
{0x1e944, 0x1e94a}, // (nil) ..
|
||||
{0xe0100, 0xe01ef}, // Variation Select||-17 ..Variation Select||-256
|
||||
};
|
||||
|
||||
private static final short wtable[] = {16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 18, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
|
||||
16, 16, 16, 16, 16, 16, 19, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 20, 21, 22, 23, 24, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
|
||||
17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 25, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
|
||||
17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
|
||||
17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 26, 16, 16, 16, 16, 27, 16, 16, 17, 17, 17, 17, 17,
|
||||
17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
|
||||
17, 28, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17,
|
||||
16, 16, 16, 29, 30, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
|
||||
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
|
||||
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
|
||||
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
|
||||
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 31, 16, 16, 16,
|
||||
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
|
||||
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 32, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
|
||||
16, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 251, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 15, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 63, 0, 0, 0, 255, 15, 255, 255, 255, 255, 255, 255, 255, 127, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127, 254, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 224, 255, 255, 255, 255, 63, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127, 255, 255,
|
||||
255, 255, 255, 7, 255, 255, 255, 255, 15, 0, 255, 255, 255, 255, 255, 127, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 31, 255, 255, 255, 255, 255, 255, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 15, 0, 255, 255, 127, 248,
|
||||
255, 255, 255, 255, 255, 15, 0, 0, 255, 3, 0, 0, 255, 255, 255, 255, 247, 255, 127, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 254,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 255, 255, 255, 255, 255, 7, 255, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
// https://github.com/jquast/wcwidth/blob/master/wcwidth/table_wide.py
|
||||
// at commit 0d7de112202cc8b2ebe9232ff4a5c954f19d561a (2016-07-02):
|
||||
private static final int[][] WIDE_EASTASIAN = {
|
||||
{0x1100, 0x115f}, // Hangul Choseong Kiyeok ..Hangul Choseong Filler
|
||||
{0x231a, 0x231b}, // Watch ..Hourglass
|
||||
{0x2329, 0x232a}, // Left-pointing Angle Brac..Right-pointing Angle Bra
|
||||
{0x23e9, 0x23ec}, // Black Right-pointing Dou..Black Down-pointing Doub
|
||||
{0x23f0, 0x23f0}, // Alarm Clock ..Alarm Clock
|
||||
{0x23f3, 0x23f3}, // Hourglass With Flowing S..Hourglass With Flowing S
|
||||
{0x25fd, 0x25fe}, // White Medium Small Squar..Black Medium Small Squar
|
||||
{0x2614, 0x2615}, // Umbrella With Rain Drops..Hot Beverage
|
||||
{0x2648, 0x2653}, // Aries ..Pisces
|
||||
{0x267f, 0x267f}, // Wheelchair Symbol ..Wheelchair Symbol
|
||||
{0x2693, 0x2693}, // Anch|| ..Anch||
|
||||
{0x26a1, 0x26a1}, // High Voltage Sign ..High Voltage Sign
|
||||
{0x26aa, 0x26ab}, // Medium White Circle ..Medium Black Circle
|
||||
{0x26bd, 0x26be}, // Soccer Ball ..Baseball
|
||||
{0x26c4, 0x26c5}, // Snowman Without Snow ..Sun Behind Cloud
|
||||
{0x26ce, 0x26ce}, // Ophiuchus ..Ophiuchus
|
||||
{0x26d4, 0x26d4}, // No Entry ..No Entry
|
||||
{0x26ea, 0x26ea}, // Church ..Church
|
||||
{0x26f2, 0x26f3}, // Fountain ..Flag In Hole
|
||||
{0x26f5, 0x26f5}, // Sailboat ..Sailboat
|
||||
{0x26fa, 0x26fa}, // Tent ..Tent
|
||||
{0x26fd, 0x26fd}, // Fuel Pump ..Fuel Pump
|
||||
{0x2705, 0x2705}, // White Heavy Check Mark ..White Heavy Check Mark
|
||||
{0x270a, 0x270b}, // Raised Fist ..Raised Hand
|
||||
{0x2728, 0x2728}, // Sparkles ..Sparkles
|
||||
{0x274c, 0x274c}, // Cross Mark ..Cross Mark
|
||||
{0x274e, 0x274e}, // Negative Squared Cross M..Negative Squared Cross M
|
||||
{0x2753, 0x2755}, // Black Question Mark ||na..White Exclamation Mark O
|
||||
{0x2757, 0x2757}, // Heavy Exclamation Mark S..Heavy Exclamation Mark S
|
||||
{0x2795, 0x2797}, // Heavy Plus Sign ..Heavy Division Sign
|
||||
{0x27b0, 0x27b0}, // Curly Loop ..Curly Loop
|
||||
{0x27bf, 0x27bf}, // Double Curly Loop ..Double Curly Loop
|
||||
{0x2b1b, 0x2b1c}, // Black Large Square ..White Large Square
|
||||
{0x2b50, 0x2b50}, // White Medium Star ..White Medium Star
|
||||
{0x2b55, 0x2b55}, // Heavy Large Circle ..Heavy Large Circle
|
||||
{0x2e80, 0x2e99}, // Cjk Radical Repeat ..Cjk Radical Rap
|
||||
{0x2e9b, 0x2ef3}, // Cjk Radical Choke ..Cjk Radical C-simplified
|
||||
{0x2f00, 0x2fd5}, // Kangxi Radical One ..Kangxi Radical Flute
|
||||
{0x2ff0, 0x2ffb}, // Ideographic Description ..Ideographic Description
|
||||
{0x3000, 0x303e}, // Ideographic Space ..Ideographic Variation In
|
||||
{0x3041, 0x3096}, // Hiragana Letter Small A ..Hiragana Letter Small Ke
|
||||
{0x3099, 0x30ff}, // Combining Katakana-hirag..Katakana Digraph Koto
|
||||
{0x3105, 0x312d}, // Bopomofo Letter B ..Bopomofo Letter Ih
|
||||
{0x3131, 0x318e}, // Hangul Letter Kiyeok ..Hangul Letter Araeae
|
||||
{0x3190, 0x31ba}, // Ideographic Annotation L..Bopomofo Letter Zy
|
||||
{0x31c0, 0x31e3}, // Cjk Stroke T ..Cjk Stroke Q
|
||||
{0x31f0, 0x321e}, // Katakana Letter Small Ku..Parenthesized K||ean Cha
|
||||
{0x3220, 0x3247}, // Parenthesized Ideograph ..Circled Ideograph Koto
|
||||
{0x3250, 0x32fe}, // Partnership Sign ..Circled Katakana Wo
|
||||
{0x3300, 0x4dbf}, // Square Apaato ..
|
||||
{0x4e00, 0xa48c}, // Cjk Unified Ideograph-4e..Yi Syllable Yyr
|
||||
{0xa490, 0xa4c6}, // Yi Radical Qot ..Yi Radical Ke
|
||||
{0xa960, 0xa97c}, // Hangul Choseong Tikeut-m..Hangul Choseong Ssangyeo
|
||||
{0xac00, 0xd7a3}, // Hangul Syllable Ga ..Hangul Syllable Hih
|
||||
{0xf900, 0xfaff}, // Cjk Compatibility Ideogr..
|
||||
{0xfe10, 0xfe19}, // Presentation F||m F|| Ve..Presentation F||m F|| Ve
|
||||
{0xfe30, 0xfe52}, // Presentation F||m F|| Ve..Small Full Stop
|
||||
{0xfe54, 0xfe66}, // Small Semicolon ..Small Equals Sign
|
||||
{0xfe68, 0xfe6b}, // Small Reverse Solidus ..Small Commercial At
|
||||
{0xff01, 0xff60}, // Fullwidth Exclamation Ma..Fullwidth Right White Pa
|
||||
{0xffe0, 0xffe6}, // Fullwidth Cent Sign ..Fullwidth Won Sign
|
||||
{0x16fe0, 0x16fe0}, // (nil) ..
|
||||
{0x17000, 0x187ec}, // (nil) ..
|
||||
{0x18800, 0x18af2}, // (nil) ..
|
||||
{0x1b000, 0x1b001}, // Katakana Letter Archaic ..Hiragana Letter Archaic
|
||||
{0x1f004, 0x1f004}, // Mahjong Tile Red Dragon ..Mahjong Tile Red Dragon
|
||||
{0x1f0cf, 0x1f0cf}, // Playing Card Black Joker..Playing Card Black Joker
|
||||
{0x1f18e, 0x1f18e}, // Negative Squared Ab ..Negative Squared Ab
|
||||
{0x1f191, 0x1f19a}, // Squared Cl ..Squared Vs
|
||||
{0x1f200, 0x1f202}, // Square Hiragana Hoka ..Squared Katakana Sa
|
||||
{0x1f210, 0x1f23b}, // Squared Cjk Unified Ideo..
|
||||
{0x1f240, 0x1f248}, // T||toise Shell Bracketed..T||toise Shell Bracketed
|
||||
{0x1f250, 0x1f251}, // Circled Ideograph Advant..Circled Ideograph Accept
|
||||
{0x1f300, 0x1f320}, // Cyclone ..Shooting Star
|
||||
{0x1f32d, 0x1f335}, // Hot Dog ..Cactus
|
||||
{0x1f337, 0x1f37c}, // Tulip ..Baby Bottle
|
||||
{0x1f37e, 0x1f393}, // Bottle With Popping C||k..Graduation Cap
|
||||
{0x1f3a0, 0x1f3ca}, // Carousel H||se ..Swimmer
|
||||
{0x1f3cf, 0x1f3d3}, // Cricket Bat And Ball ..Table Tennis Paddle And
|
||||
{0x1f3e0, 0x1f3f0}, // House Building ..European Castle
|
||||
{0x1f3f4, 0x1f3f4}, // Waving Black Flag ..Waving Black Flag
|
||||
{0x1f3f8, 0x1f43e}, // Badminton Racquet And Sh..Paw Prints
|
||||
{0x1f440, 0x1f440}, // Eyes ..Eyes
|
||||
{0x1f442, 0x1f4fc}, // Ear ..Videocassette
|
||||
{0x1f4ff, 0x1f53d}, // Prayer Beads ..Down-pointing Small Red
|
||||
{0x1f54b, 0x1f54e}, // Kaaba ..Men||ah With Nine Branch
|
||||
{0x1f550, 0x1f567}, // Clock Face One Oclock ..Clock Face Twelve-thirty
|
||||
{0x1f57a, 0x1f57a}, // (nil) ..
|
||||
{0x1f595, 0x1f596}, // Reversed Hand With Middl..Raised Hand With Part Be
|
||||
{0x1f5a4, 0x1f5a4}, // (nil) ..
|
||||
{0x1f5fb, 0x1f64f}, // Mount Fuji ..Person With Folded Hands
|
||||
{0x1f680, 0x1f6c5}, // Rocket ..Left Luggage
|
||||
{0x1f6cc, 0x1f6cc}, // Sleeping Accommodation ..Sleeping Accommodation
|
||||
{0x1f6d0, 0x1f6d2}, // Place Of W||ship ..
|
||||
{0x1f6eb, 0x1f6ec}, // Airplane Departure ..Airplane Arriving
|
||||
{0x1f6f4, 0x1f6f6}, // (nil) ..
|
||||
{0x1f910, 0x1f91e}, // Zipper-mouth Face ..
|
||||
{0x1f920, 0x1f927}, // (nil) ..
|
||||
{0x1f930, 0x1f930}, // (nil) ..
|
||||
{0x1f933, 0x1f93e}, // (nil) ..
|
||||
{0x1f940, 0x1f94b}, // (nil) ..
|
||||
{0x1f950, 0x1f95e}, // (nil) ..
|
||||
{0x1f980, 0x1f991}, // Crab ..
|
||||
{0x1f9c0, 0x1f9c0}, // Cheese Wedge ..Cheese Wedge
|
||||
{0x20000, 0x2fffd}, // Cjk Unified Ideograph-20..
|
||||
{0x30000, 0x3fffd}, // (nil) ..
|
||||
};
|
||||
|
||||
/** Return the terminal display width of a code point: 0, 1 or 2. */
|
||||
public static int width(int wc) {
|
||||
if (wc < 0xff) return (wc + 1 & 0x7f) >= 0x21 ? 1 : (wc != 0) ? 0 : 0;
|
||||
if ((wc & 0xfffeffff) < 0xfffe) {
|
||||
if (((table[table[wc >> 8] * 32 + ((wc & 255) >> 3)] >> (wc & 7)) & 1) != 0) return 0;
|
||||
if (((wtable[wtable[wc >> 8] * 32 + ((wc & 255) >> 3)] >> (wc & 7)) & 1) != 0) return 2;
|
||||
return 1;
|
||||
|
||||
private static boolean intable(int[][] table, int c) {
|
||||
// First quick check f|| Latin1 etc. characters.
|
||||
if (c < table[0][0]) return false;
|
||||
|
||||
// Binary search in table.
|
||||
int bot = 0;
|
||||
int top = table.length - 1; // (int)(size / sizeof(struct interval) - 1);
|
||||
while (top >= bot) {
|
||||
int mid = (bot + top) / 2;
|
||||
if (table[mid][1] < c) {
|
||||
bot = mid + 1;
|
||||
} else if (table[mid][0] > c) {
|
||||
top = mid - 1;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if ((wc & 0xfffe) == 0xfffe) return 0;
|
||||
if (wc - 0x20000 < 0x20000) return 2;
|
||||
if (wc == 0xe0001 || wc - 0xe0020 < 0x5f || wc - 0xe0100 < 0xef) return 0;
|
||||
return 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Return the terminal display width of a code point: 0, 1 || 2. */
|
||||
public static int width(int ucs) {
|
||||
if (ucs == 0 ||
|
||||
ucs == 0x034F ||
|
||||
(0x200B <= ucs && ucs <= 0x200F) ||
|
||||
ucs == 0x2028 ||
|
||||
ucs == 0x2029 ||
|
||||
(0x202A <= ucs && ucs <= 0x202E) ||
|
||||
(0x2060 <= ucs && ucs <= 0x2063)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// C0/C1 control characters
|
||||
// Termux change: Return 0 instead of -1.
|
||||
if (ucs < 32 || (0x07F <= ucs && ucs < 0x0A0)) return 0;
|
||||
|
||||
// combining characters with zero width
|
||||
if (intable(ZERO_WIDTH, ucs)) return 0;
|
||||
|
||||
return intable(WIDE_EASTASIAN, ucs) ? 2 : 1;
|
||||
}
|
||||
|
||||
/** The width at an index position in a java char array. */
|
||||
|
||||
@@ -54,7 +54,8 @@ final class TerminalRenderer {
|
||||
}
|
||||
|
||||
/** Render the terminal to a canvas with at a specified row scroll, and an optional rectangular selection. */
|
||||
public final void render(TerminalEmulator mEmulator, Canvas canvas, int topRow, int selectionY1, int selectionY2, int selectionX1, int selectionX2) {
|
||||
public final void render(TerminalEmulator mEmulator, Canvas canvas, int topRow,
|
||||
int selectionY1, int selectionY2, int selectionX1, int selectionX2) {
|
||||
final boolean reverseVideo = mEmulator.isReverseVideo();
|
||||
final int endRow = topRow + mEmulator.mRows;
|
||||
final int columns = mEmulator.mColumns;
|
||||
@@ -63,6 +64,7 @@ final class TerminalRenderer {
|
||||
final boolean cursorVisible = mEmulator.isShowingCursor();
|
||||
final TerminalBuffer screen = mEmulator.getScreen();
|
||||
final int[] palette = mEmulator.mColors.mCurrentColors;
|
||||
final int cursorShape = mEmulator.getCursorStyle();
|
||||
|
||||
if (reverseVideo)
|
||||
canvas.drawColor(palette[TextStyle.COLOR_INDEX_FOREGROUND], PorterDuff.Mode.SRC);
|
||||
@@ -82,7 +84,7 @@ final class TerminalRenderer {
|
||||
final char[] line = lineObject.mText;
|
||||
final int charsUsedInLine = lineObject.getSpaceUsed();
|
||||
|
||||
int lastRunStyle = 0;
|
||||
long lastRunStyle = 0;
|
||||
boolean lastRunInsideCursor = false;
|
||||
int lastRunStartColumn = -1;
|
||||
int lastRunStartIndex = 0;
|
||||
@@ -97,7 +99,7 @@ final class TerminalRenderer {
|
||||
final int codePoint = charIsHighsurrogate ? Character.toCodePoint(charAtIndex, line[currentCharIndex + 1]) : charAtIndex;
|
||||
final int codePointWcWidth = WcWidth.width(codePoint);
|
||||
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().
|
||||
// This could happen for some fonts which are not truly monospace, or for more exotic characters such as
|
||||
@@ -113,8 +115,10 @@ final class TerminalRenderer {
|
||||
} else {
|
||||
final int columnWidthSinceLastRun = column - lastRunStartColumn;
|
||||
final int charsSinceLastRun = currentCharIndex - lastRunStartIndex;
|
||||
drawTextRun(canvas, line, palette, heightOffset, lastRunStartColumn, columnWidthSinceLastRun, lastRunStartIndex, charsSinceLastRun,
|
||||
measuredWidthForRun, lastRunInsideCursor, lastRunStyle, reverseVideo);
|
||||
int cursorColor = lastRunInsideCursor ? mEmulator.mColors.mCurrentColors[TextStyle.COLOR_INDEX_CURSOR] : 0;
|
||||
drawTextRun(canvas, line, palette, heightOffset, lastRunStartColumn, columnWidthSinceLastRun,
|
||||
lastRunStartIndex, charsSinceLastRun, measuredWidthForRun,
|
||||
cursorColor, cursorShape, lastRunStyle, reverseVideo);
|
||||
}
|
||||
measuredWidthForRun = 0.f;
|
||||
lastRunStyle = style;
|
||||
@@ -135,29 +139,42 @@ final class TerminalRenderer {
|
||||
|
||||
final int columnWidthSinceLastRun = columns - lastRunStartColumn;
|
||||
final int charsSinceLastRun = currentCharIndex - lastRunStartIndex;
|
||||
int cursorColor = lastRunInsideCursor ? mEmulator.mColors.mCurrentColors[TextStyle.COLOR_INDEX_CURSOR] : 0;
|
||||
drawTextRun(canvas, line, palette, heightOffset, lastRunStartColumn, columnWidthSinceLastRun, lastRunStartIndex, charsSinceLastRun,
|
||||
measuredWidthForRun, lastRunInsideCursor, lastRunStyle, reverseVideo);
|
||||
measuredWidthForRun, cursorColor, cursorShape, lastRunStyle, reverseVideo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param canvas the canvas to render on
|
||||
* @param palette the color palette to look up colors from textStyle
|
||||
* @param y height offset into the canvas where to render the line: line * {@link #mFontLineSpacing}
|
||||
* @param startColumn the run offset in columns
|
||||
* @param runWidthColumns the run width in columns - this is computed from wcwidth() and may not be what the font measures to
|
||||
* @param text the java char array to render text from
|
||||
* @param startCharIndex index into the text array where to start
|
||||
* @param runWidthChars number of java characters from the text array to render
|
||||
* @param cursor true if rendering a cursor or selection
|
||||
* @param textStyle the background, foreground and effect encoded using {@link TextStyle}
|
||||
* @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,
|
||||
float mes, boolean cursor, int textStyle, boolean reverseVideo) {
|
||||
private void drawTextRun(Canvas canvas, char[] text, int[] palette, float y, int startColumn, int runWidthColumns,
|
||||
int startCharIndex, int runWidthChars, float mes, int cursor, int cursorStyle,
|
||||
long textStyle, boolean reverseVideo) {
|
||||
int foreColor = TextStyle.decodeForeColor(textStyle);
|
||||
int backColor = TextStyle.decodeBackColor(textStyle);
|
||||
final int effect = TextStyle.decodeEffect(textStyle);
|
||||
int backColor = TextStyle.decodeBackColor(textStyle);
|
||||
final boolean bold = (effect & (TextStyle.CHARACTER_ATTRIBUTE_BOLD | TextStyle.CHARACTER_ATTRIBUTE_BLINK)) != 0;
|
||||
final boolean underline = (effect & TextStyle.CHARACTER_ATTRIBUTE_UNDERLINE) != 0;
|
||||
final boolean italic = (effect & TextStyle.CHARACTER_ATTRIBUTE_ITALIC) != 0;
|
||||
final boolean strikeThrough = (effect & TextStyle.CHARACTER_ATTRIBUTE_STRIKETHROUGH) != 0;
|
||||
final boolean dim = (effect & TextStyle.CHARACTER_ATTRIBUTE_DIM) != 0;
|
||||
|
||||
if ((foreColor & 0xff000000) != 0xff000000) {
|
||||
// Let bold have bright colors if applicable (one of the first 8):
|
||||
if (bold && foreColor >= 0 && foreColor < 8) foreColor += 8;
|
||||
foreColor = palette[foreColor];
|
||||
}
|
||||
|
||||
if ((backColor & 0xff000000) != 0xff000000) {
|
||||
backColor = palette[backColor];
|
||||
}
|
||||
|
||||
// Reverse video here if _one and only one_ of the reverse flags are set:
|
||||
final boolean reverseVideoHere = reverseVideo ^ (effect & (TextStyle.CHARACTER_ATTRIBUTE_INVERSE)) != 0;
|
||||
if (reverseVideoHere) {
|
||||
int tmp = foreColor;
|
||||
foreColor = backColor;
|
||||
backColor = tmp;
|
||||
}
|
||||
|
||||
float left = startColumn * mFontWidth;
|
||||
float right = left + runWidthColumns * mFontWidth;
|
||||
|
||||
@@ -171,50 +188,38 @@ final class TerminalRenderer {
|
||||
savedMatrix = true;
|
||||
}
|
||||
|
||||
// Reverse video here if _one and only one_ of the reverse flags are set:
|
||||
boolean reverseVideoHere = reverseVideo ^ (effect & (TextStyle.CHARACTER_ATTRIBUTE_INVERSE)) != 0;
|
||||
// Switch if _one and only one_ of reverse video and cursor is set:
|
||||
if (reverseVideoHere ^ cursor) {
|
||||
int tmp = foreColor;
|
||||
foreColor = backColor;
|
||||
backColor = tmp;
|
||||
}
|
||||
|
||||
if (backColor != TextStyle.COLOR_INDEX_BACKGROUND) {
|
||||
if (backColor != palette[TextStyle.COLOR_INDEX_BACKGROUND]) {
|
||||
// Only draw non-default background.
|
||||
mTextPaint.setColor(palette[backColor]);
|
||||
mTextPaint.setColor(backColor);
|
||||
canvas.drawRect(left, y - mFontLineSpacingAndAscent + mFontAscent, right, y, mTextPaint);
|
||||
}
|
||||
|
||||
if (cursor != 0) {
|
||||
mTextPaint.setColor(cursor);
|
||||
float cursorHeight = mFontLineSpacingAndAscent - mFontAscent;
|
||||
if (cursorStyle == TerminalEmulator.CURSOR_STYLE_UNDERLINE) cursorHeight /= 4.;
|
||||
else if (cursorStyle == TerminalEmulator.CURSOR_STYLE_BAR) right -= ((right - left) * 3) / 4.;
|
||||
canvas.drawRect(left, y - cursorHeight, right, y, mTextPaint);
|
||||
}
|
||||
|
||||
if ((effect & TextStyle.CHARACTER_ATTRIBUTE_INVISIBLE) == 0) {
|
||||
// Treat blink as bold:
|
||||
final boolean bold = (effect & (TextStyle.CHARACTER_ATTRIBUTE_BOLD | TextStyle.CHARACTER_ATTRIBUTE_BLINK)) != 0;
|
||||
final boolean underline = (effect & TextStyle.CHARACTER_ATTRIBUTE_UNDERLINE) != 0;
|
||||
final boolean italic = (effect & TextStyle.CHARACTER_ATTRIBUTE_ITALIC) != 0;
|
||||
final boolean strikeThrough = (effect & TextStyle.CHARACTER_ATTRIBUTE_STRIKETHROUGH) != 0;
|
||||
final boolean dim = (effect & TextStyle.CHARACTER_ATTRIBUTE_DIM) != 0;
|
||||
|
||||
// Let bold have bright colors if applicable (one of the first 8):
|
||||
final int actualForeColor = foreColor + (bold && foreColor < 8 ? 8 : 0);
|
||||
|
||||
int foreColorARGB = palette[actualForeColor];
|
||||
if (dim) {
|
||||
int red = (0xFF & (foreColorARGB >> 16));
|
||||
int green = (0xFF & (foreColorARGB >> 8));
|
||||
int blue = (0xFF & foreColorARGB);
|
||||
int red = (0xFF & (foreColor >> 16));
|
||||
int green = (0xFF & (foreColor >> 8));
|
||||
int blue = (0xFF & foreColor);
|
||||
// Dim color handling used by libvte which in turn took it from xterm
|
||||
// (https://bug735245.bugzilla-attachments.gnome.org/attachment.cgi?id=284267):
|
||||
red = red * 2 / 3;
|
||||
green = green * 2 / 3;
|
||||
blue = blue * 2 / 3;
|
||||
foreColorARGB = 0xFF000000 + (red << 16) + (green << 8) + blue;
|
||||
foreColor = 0xFF000000 + (red << 16) + (green << 8) + blue;
|
||||
}
|
||||
|
||||
mTextPaint.setFakeBoldText(bold);
|
||||
mTextPaint.setUnderlineText(underline);
|
||||
mTextPaint.setTextSkewX(italic ? -0.35f : 0.f);
|
||||
mTextPaint.setStrikeThruText(strikeThrough);
|
||||
mTextPaint.setColor(foreColorARGB);
|
||||
mTextPaint.setColor(foreColor);
|
||||
|
||||
// The text alignment is the default Paint.Align.LEFT.
|
||||
canvas.drawText(text, startCharIndex, runWidthChars, left, y - mFontLineSpacingAndAscent, mTextPaint);
|
||||
|
||||
@@ -10,6 +10,7 @@ import android.graphics.Rect;
|
||||
import android.graphics.Typeface;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.os.Build;
|
||||
import android.text.Editable;
|
||||
import android.text.InputType;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
@@ -228,40 +229,58 @@ public final class TerminalView extends View {
|
||||
|
||||
@Override
|
||||
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
|
||||
// Using InputType.TYPE_TEXT_VARIATION_URI avoids having an extra row of numbers on the
|
||||
// Google keyboard. https://github.com/termux/termux-app/issues/87.
|
||||
// It also makes the '/' keyboard more accessible, and makes some sense.
|
||||
// Using InputType.NULL is the most correct input type and avoids issues with other hacks.
|
||||
//
|
||||
// If using just "TYPE_NULL", there is a problem with the "Google Pinyin Input" being in
|
||||
// word mode when used with the "En" tab available when the "Show English keyboard" option
|
||||
// is enabled - see https://github.com/termux/termux-packages/issues/25.
|
||||
// Adding TYPE_TEXT_FLAG_NO_SUGGESTIONS fixes Pinyin Input and removes the row of numbers
|
||||
// on the Google keyboard. . It also causes Swype to be put in
|
||||
// word mode, but using TYPE_TEXT_VARIATION_VISIBLE_PASSWORD would fix that. But for now
|
||||
// use InputType.TYPE_TEXT_VARIATION_URI as it makes more sense.
|
||||
outAttrs.inputType = InputType.TYPE_TEXT_VARIATION_URI;
|
||||
// Previous keyboard issues:
|
||||
// https://github.com/termux/termux-packages/issues/25
|
||||
// https://github.com/termux/termux-app/issues/87.
|
||||
// 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;
|
||||
|
||||
// Let part of the application show behind when in landscape:
|
||||
outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_FULLSCREEN;
|
||||
outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_FULLSCREEN |
|
||||
EditorInfo.IME_FLAG_NO_ENTER_ACTION |
|
||||
EditorInfo.IME_ACTION_NONE;
|
||||
|
||||
return new BaseInputConnection(this, true) {
|
||||
|
||||
@Override
|
||||
public boolean finishComposingText() {
|
||||
if (LOG_KEY_EVENTS) Log.i(EmulatorDebug.LOG_TAG, "IME: finishComposingText()");
|
||||
commitText(getEditable(), 0);
|
||||
super.finishComposingText();
|
||||
|
||||
// Clear the editable.
|
||||
sendTextToTerminal(getEditable());
|
||||
getEditable().clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean commitText(CharSequence text, int newCursorPosition) {
|
||||
if (LOG_KEY_EVENTS)
|
||||
if (LOG_KEY_EVENTS) {
|
||||
Log.i(EmulatorDebug.LOG_TAG, "IME: commitText(\"" + text + "\", " + newCursorPosition + ")");
|
||||
}
|
||||
super.commitText(text, newCursorPosition);
|
||||
|
||||
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 + ")");
|
||||
}
|
||||
// The stock Samsung keyboard with 'Auto check spelling' enabled sends leftLength > 1.
|
||||
KeyEvent deleteKey = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL);
|
||||
for (int i = 0; i < leftLength; i++) sendKeyEvent(deleteKey);
|
||||
return super.deleteSurroundingText(leftLength, rightLength);
|
||||
}
|
||||
|
||||
void sendTextToTerminal(CharSequence text) {
|
||||
final int textLengthInChars = text.length();
|
||||
for (int i = 0; i < textLengthInChars; i++) {
|
||||
char firstChar = text.charAt(i);
|
||||
@@ -302,34 +321,8 @@ public final class TerminalView extends View {
|
||||
|
||||
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 + ")");
|
||||
|
||||
// Swype keyboard sometimes(?) sends this on backspace:
|
||||
if (leftLength == 0 && rightLength == 0) leftLength = 1;
|
||||
|
||||
for (int i = 0; i < leftLength; i++)
|
||||
sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setComposingText(CharSequence text, int 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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
5
app/src/main/jni/Android.mk
Normal file
@@ -0,0 +1,5 @@
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE:= libtermux
|
||||
LOCAL_SRC_FILES:= termux.c
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
7
app/src/main/res/anim/on_bell.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<translate android:duration="30"
|
||||
android:fromXDelta="-1%"
|
||||
android:repeatCount="1"
|
||||
android:repeatMode="reverse"
|
||||
android:toXDelta="1%"/>
|
||||
</set>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 695 B |
|
Before Width: | Height: | Size: 786 B |
|
Before Width: | Height: | Size: 597 B |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 779 B |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 983 B |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
17
app/src/main/res/drawable/ic_new_session.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
|
||||
<path
|
||||
android:fillColor="#FFF"
|
||||
android:pathData="M 12, 12
|
||||
m -10.5, 0
|
||||
a 10.5,10.5 0 1,0 21,0
|
||||
a 10.5,10.5 0 1,0 -21,0"/>
|
||||
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM17,13h-4v4h-2v-4L7,13v-2h4L11,7h2v4h4v2z"/>
|
||||
</vector>
|
||||
33
app/src/main/res/drawable/ic_service_notification.xml
Normal file
@@ -0,0 +1,33 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="48dp"
|
||||
android:width="48dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
<!--
|
||||
https://material.google.com/style/icons.html
|
||||
-->
|
||||
|
||||
<!-- Screen border. -->
|
||||
<path android:fillColor="#00000000"
|
||||
android:strokeColor="#000"
|
||||
android:strokeWidth="3"
|
||||
android:pathData="M7,4
|
||||
l34,0
|
||||
q3 0,3 3
|
||||
l0,34
|
||||
q0 3, -3 3
|
||||
l-34,0
|
||||
q-3 0, -3-3
|
||||
l0 -34
|
||||
q0 -3, 3 -3"
|
||||
/>
|
||||
|
||||
<!-- Block cursor. -->
|
||||
<path android:fillColor="#000"
|
||||
android:pathData="M12,12
|
||||
l5,0
|
||||
l0,10
|
||||
l-5,0"
|
||||
/>
|
||||
|
||||
</vector>
|
||||
@@ -7,7 +7,6 @@
|
||||
android:imeOptions="actionSend|flagNoFullscreen"
|
||||
android:maxLines="1"
|
||||
android:inputType="text"
|
||||
android:singleLine="true"
|
||||
android:textColor="@android:color/white"
|
||||
android:paddingTop="0dp"
|
||||
android:textCursorDrawable="@null"
|
||||
|
||||
BIN
app/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 343 B |
BIN
app/src/main/res/mipmap-hdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 204 B |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 324 B |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 510 B |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 685 B |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
@@ -50,8 +50,8 @@
|
||||
<string name="styling_install">Install</string>
|
||||
|
||||
<string name="notification_action_exit">Exit</string>
|
||||
<string name="notification_action_wakelock">Wake</string>
|
||||
<string name="notification_action_wifilock">Wifi</string>
|
||||
<string name="notification_action_wake_lock">Acquire wakelock</string>
|
||||
<string name="notification_action_wake_unlock">Release wakelock</string>
|
||||
|
||||
<string name="file_received_title">Save file in ~/downloads/</string>
|
||||
<string name="file_received_edit_button">Edit</string>
|
||||
|
||||
12
app/src/main/res/xml/shortcuts.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<shortcut
|
||||
android:shortcutId="new_session"
|
||||
android:enabled="true"
|
||||
android:icon="@drawable/ic_new_session"
|
||||
android:shortcutShortLabel="@string/new_session">
|
||||
<intent
|
||||
android:action="android.intent.action.RUN"
|
||||
android:targetPackage="com.termux"
|
||||
android:targetClass="com.termux.app.TermuxActivity"/>
|
||||
</shortcut>
|
||||
</shortcuts>
|
||||
@@ -18,7 +18,7 @@ public class CursorAndScreenTest extends TerminalTestCase {
|
||||
assertLinesAre("ABCDE", "FGHIJ", "KLMNO", "PQRST", "UVWXY");
|
||||
for (int row = 0; row < 5; row++) {
|
||||
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(row, TextStyle.decodeBackColor(s));
|
||||
}
|
||||
@@ -28,7 +28,7 @@ public class CursorAndScreenTest extends TerminalTestCase {
|
||||
assertLinesAre("KLMNO", "PQRST", "UVWXY", " ", " ");
|
||||
for (int row = 0; row < 3; row++) {
|
||||
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(row + 2, TextStyle.decodeBackColor(s));
|
||||
}
|
||||
@@ -43,7 +43,7 @@ public class CursorAndScreenTest extends TerminalTestCase {
|
||||
for (int col = 0; col < 5; col++) {
|
||||
int wantedForeground = (row == 1 || row == 2) ? 98 : col;
|
||||
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(wantedBackground, TextStyle.decodeBackColor(s));
|
||||
}
|
||||
|
||||
@@ -141,10 +141,10 @@ public class KeyHandlerTest extends TestCase {
|
||||
assertKeysEquals("\033[1;6D", KeyHandler.getCode(KeyEvent.KEYCODE_DPAD_LEFT, mod, false, false));
|
||||
|
||||
// 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));
|
||||
// ... 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));
|
||||
|
||||
// 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[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("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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ public class ResizeTest extends TerminalTestCase {
|
||||
enterString("\033[2J");
|
||||
for (int r = 0; r < rows; r++) {
|
||||
for (int c = 0; c < cols; c++) {
|
||||
int style = getStyleAt(r, c);
|
||||
long style = getStyleAt(r, c);
|
||||
assertEquals(119, TextStyle.decodeForeColor(style));
|
||||
assertEquals(129, TextStyle.decodeBackColor(style));
|
||||
}
|
||||
@@ -105,7 +105,7 @@ public class ResizeTest extends TerminalTestCase {
|
||||
// After resize, screen should still be same color:
|
||||
for (int r = 0; r < rows - 2; r++) {
|
||||
for (int c = 0; c < cols; c++) {
|
||||
int style = getStyleAt(r, c);
|
||||
long style = getStyleAt(r, c);
|
||||
assertEquals(119, TextStyle.decodeForeColor(style));
|
||||
assertEquals(129, TextStyle.decodeBackColor(style));
|
||||
}
|
||||
@@ -116,7 +116,7 @@ public class ResizeTest extends TerminalTestCase {
|
||||
resize(cols, rows);
|
||||
for (int r = 0; r < rows; r++) {
|
||||
for (int c = 0; c < cols; c++) {
|
||||
int style = getStyleAt(r, c);
|
||||
long style = getStyleAt(r, c);
|
||||
assertEquals(119, TextStyle.decodeForeColor(style));
|
||||
assertEquals("wrong at row=" + r, r >= 3 ? 200 : 129, TextStyle.decodeBackColor(style));
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.termux.terminal;
|
||||
|
||||
public class ScreenBufferTest extends TerminalTest {
|
||||
public class ScreenBufferTest extends TerminalTestCase {
|
||||
|
||||
public void testBasics() {
|
||||
TerminalBuffer screen = new TerminalBuffer(5, 3, 3);
|
||||
|
||||
@@ -147,12 +147,11 @@ public class TerminalTest extends TerminalTestCase {
|
||||
enterString("\033[38;5;119m");
|
||||
assertEquals(119, mTerminal.mForeColor);
|
||||
assertEquals(TextStyle.COLOR_INDEX_BACKGROUND, mTerminal.mBackColor);
|
||||
|
||||
enterString("\033[48;5;129m");
|
||||
assertEquals(119, mTerminal.mForeColor);
|
||||
assertEquals(129, mTerminal.mBackColor);
|
||||
|
||||
// Invalid parameter:
|
||||
// Invalid parameter:
|
||||
enterString("\033[48;8;129m");
|
||||
assertEquals(119, mTerminal.mForeColor);
|
||||
assertEquals(129, mTerminal.mBackColor);
|
||||
@@ -161,7 +160,31 @@ public class TerminalTest extends TerminalTestCase {
|
||||
enterString("\033[38;5;178;48;5;179;m");
|
||||
assertEquals(178, mTerminal.mForeColor);
|
||||
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() {
|
||||
final int rows = 3;
|
||||
@@ -169,7 +192,7 @@ public class TerminalTest extends TerminalTestCase {
|
||||
withTerminalSized(cols, rows);
|
||||
for (int r = 0; r < rows; r++) {
|
||||
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_BACKGROUND, TextStyle.decodeBackColor(style));
|
||||
}
|
||||
@@ -182,7 +205,7 @@ public class TerminalTest extends TerminalTestCase {
|
||||
enterString("\033[2J");
|
||||
for (int r = 0; r < rows; r++) {
|
||||
for (int c = 0; c < cols; c++) {
|
||||
int style = getStyleAt(r, c);
|
||||
long style = getStyleAt(r, c);
|
||||
assertEquals(119, TextStyle.decodeForeColor(style));
|
||||
assertEquals(129, TextStyle.decodeBackColor(style));
|
||||
}
|
||||
@@ -193,7 +216,7 @@ public class TerminalTest extends TerminalTestCase {
|
||||
enterString("\033[2L");
|
||||
for (int r = 0; r < rows; r++) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,7 +240,7 @@ public abstract class TerminalTestCase extends TestCase {
|
||||
}
|
||||
|
||||
/** 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);
|
||||
}
|
||||
|
||||
@@ -296,7 +296,7 @@ public abstract class TerminalTestCase extends TestCase {
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ public class TextStyleTest extends TestCase {
|
||||
for (int fx : ALL_EFFECTS) {
|
||||
for (int fg = 0; fg < TextStyle.NUM_INDEXED_COLORS; fg++) {
|
||||
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(bg, TextStyle.decodeBackColor(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 f2 : ALL_EFFECTS) {
|
||||
int combined = f1 | f2;
|
||||
@@ -32,13 +48,13 @@ public class TextStyleTest extends TestCase {
|
||||
}
|
||||
|
||||
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);
|
||||
assertTrue((TextStyle.decodeEffect(encoded) & TextStyle.CHARACTER_ATTRIBUTE_STRIKETHROUGH) != 0);
|
||||
}
|
||||
|
||||
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);
|
||||
assertTrue((TextStyle.decodeEffect(encoded) & TextStyle.CHARACTER_ATTRIBUTE_PROTECTED) == 0);
|
||||
encoded = TextStyle.encode(TextStyle.COLOR_INDEX_FOREGROUND, TextStyle.COLOR_INDEX_BACKGROUND,
|
||||
|
||||
@@ -15,6 +15,12 @@ public class WcWidthTest extends TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
public void testSomeWidthOne() {
|
||||
assertWidthIs(1, 'å');
|
||||
assertWidthIs(1, 'ä');
|
||||
assertWidthIs(1, 'ö');
|
||||
}
|
||||
|
||||
public void testSomeWide() {
|
||||
assertWidthIs(2, 'A');
|
||||
assertWidthIs(2, 'B');
|
||||
@@ -37,13 +43,21 @@ public class WcWidthTest extends TestCase {
|
||||
}
|
||||
|
||||
public void testCombining() {
|
||||
assertWidthIs(0, 0x0302);
|
||||
assertWidthIs(0, 0x0308);
|
||||
assertWidthIs(0, 0x2060);
|
||||
assertWidthIs(0, 0x0302);
|
||||
assertWidthIs(0, 0x0308);
|
||||
}
|
||||
|
||||
public void testWordJoiner() {
|
||||
// https://en.wikipedia.org/wiki/Word_joiner
|
||||
// The word joiner (WJ) is a code point in Unicode used to separate words when using scripts
|
||||
// that do not use explicit spacing. It is encoded since Unicode version 3.2
|
||||
// (released in 2002) as U+2060 WORD JOINER (HTML ⁠).
|
||||
// The word joiner does not produce any space, and prohibits a line break at its position.
|
||||
assertWidthIs(0, 0x2060);
|
||||
}
|
||||
|
||||
public void testWatch() {
|
||||
assertWidthIs(1, 0x231a);
|
||||
|
||||
}
|
||||
|
||||
public void testSofthyphen() {
|
||||
@@ -57,11 +71,13 @@ public class WcWidthTest extends TestCase {
|
||||
}
|
||||
|
||||
public void testHangul() {
|
||||
assertWidthIs(2, 0x11A3);
|
||||
assertWidthIs(1, 0x11A3);
|
||||
}
|
||||
|
||||
public void testKoala() {
|
||||
assertWidthIs(1, 0x1F428);
|
||||
public void testEmojis() {
|
||||
assertWidthIs(2, 0x1F428); // KOALA.
|
||||
assertWidthIs(2, 0x231a); // WATCH.
|
||||
assertWidthIs(2, 0x1F643); // UPSIDE-DOWN FACE (Unicode 8).
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
20
art/generate-pngs.sh
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/bin/sh
|
||||
|
||||
for DENSITY in mdpi hdpi xhdpi xxhdpi xxxhdpi; do
|
||||
case $DENSITY in
|
||||
mdpi) SIZE=48;;
|
||||
hdpi) SIZE=72;;
|
||||
xhdpi) SIZE=96;;
|
||||
xxhdpi) SIZE=144;;
|
||||
xxxhdpi) SIZE=192;;
|
||||
esac
|
||||
|
||||
FOLDER=../app/src/main/res/mipmap-$DENSITY
|
||||
mkdir -p $FOLDER
|
||||
|
||||
for FILE in ic_launcher ic_launcher_round; do
|
||||
PNG=$FOLDER/$FILE.png
|
||||
rsvg-convert -w $SIZE -h $SIZE $FILE.svg > $PNG
|
||||
zopflipng -y $PNG $PNG
|
||||
done
|
||||
done
|
||||
26
art/ic_launcher.svg
Normal file
@@ -0,0 +1,26 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
|
||||
|
||||
<!-- Screen and border. -->
|
||||
<path fill="#000"
|
||||
stroke="#BFCBCD"
|
||||
stroke-width="2"
|
||||
d="M7,4
|
||||
l34,0
|
||||
q3 0,3 3
|
||||
l0,34
|
||||
q0 3, -3 3
|
||||
l-34,0
|
||||
q-3 0, -3-3
|
||||
l0 -34
|
||||
q0 -3, 3 -3"
|
||||
/>
|
||||
|
||||
<!-- Block cursor. -->
|
||||
<path fill="#FFF"
|
||||
d="M12,12
|
||||
l5,0
|
||||
l0,10
|
||||
l-5,0"
|
||||
/>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 512 B |
21
art/ic_launcher_round.svg
Normal file
@@ -0,0 +1,21 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
|
||||
|
||||
<!-- Screen and border. -->
|
||||
<path fill="#000"
|
||||
stroke="#BFCBCD"
|
||||
stroke-width="2"
|
||||
d="M 24, 24
|
||||
m -21, 0
|
||||
a 21,21 0 1,0 42,0
|
||||
a 21,21 0 1,0 -42,0"
|
||||
/>
|
||||
|
||||
<!-- Block cursor. -->
|
||||
<path fill="#FFF"
|
||||
d="M15,15
|
||||
l5,0
|
||||
l0,10
|
||||
l-5,0"
|
||||
/>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 429 B |
@@ -5,10 +5,7 @@ buildscript {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:2.1.2'
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
classpath 'com.android.tools.build:gradle:2.2.3'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,4 @@
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
#Fri May 13 01:11:09 CEST 2016
|
||||
org.gradle.jvmargs=-Xmx2048M
|
||||
android.useDeprecatedNdk=true
|
||||
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
||||
#Sat Jul 23 17:08:29 CEST 2016
|
||||
#Sun Dec 04 17:26:05 CET 2016
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-3.2.1-bin.zip
|
||||
|
||||
22
gradlew
vendored
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env sh
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
@@ -154,11 +154,19 @@ if $cygwin ; then
|
||||
esac
|
||||
fi
|
||||
|
||||
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
||||
function splitJvmOpts() {
|
||||
JVM_OPTS=("$@")
|
||||
# Escape application args
|
||||
save ( ) {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
||||
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
|
||||
84
gradlew.bat
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||