Compare commits

..

152 Commits
v0.35 ... v0.52

Author SHA1 Message Date
Fredrik Fornwall
7913e765d5 Bump version to 0.52 2017-06-13 16:44:33 +02:00
Fredrik Fornwall
ed3a3269d8 Merge pull request #339 from bumper314/master
Add Multi Window support for Samsung devices
2017-06-13 10:13:26 +02:00
Steven Audette
dc3994d2cf Add Multi Window support for Samsung devices
A single meta-data entry is enough to make Termux "Multi Window" aware on supported Samsung devices.
Tested and confirmed working on Samsung Note Pro 12.2 running Android 5.1.1.

This addition should not interfer with Android 6+'s split screen feature.
Tested and confirmed split screen functions as normal on a Nexus 6 running Android 7.0.

This addition should go unnoticed on non-Smasung Android 5 devices.
Tested and confirmed no functionality visible on Nexus 5 running Android 5.1.
2017-06-12 01:47:27 -06:00
Fredrik Fornwall
c972377bd1 Update gradle config 2017-06-11 22:47:59 +02:00
Fredrik Fornwall
d4be782c03 Bump version to 0.51 2017-06-05 22:49:11 +02:00
Fredrik Fornwall
c29909726c Bump terminal-emulator libraryVersion to 0.50 2017-06-05 22:45:44 +02:00
Fredrik Fornwall
45bac89298 Remove unused xmlns:android declaration 2017-06-05 22:45:15 +02:00
Fredrik Fornwall
c06770c353 Merge pull request #330 from whydoubt/cursor_preserve_color
Make cursor save/restore affect color attributes
2017-06-05 22:35:39 +02:00
Fredrik Fornwall
52a627efc8 Merge pull request #331 from whydoubt/fix_parameter_fc
Fix FC from too many control code parameters
2017-06-05 22:35:32 +02:00
Jeff Smith
2e9383720c Make cursor save/restore affect color attributes
SGR attributes are stored in three variables: mEffect, mForeColor, and
mBackColor.  Saving/restoring the cursor only preserves mEffect.

Change the cursor save/restore methods to additionally preserve
mForeColor and mBackColor.  This affects both 'explit' saving/restoring
the cursor and switching to/from the alternate screen buffer.
2017-06-03 17:45:36 -05:00
Jeff Smith
58f9f1be71 Add tests for cursor save/restore 2017-06-03 17:45:36 -05:00
Jeff Smith
058441dda6 Fix FC from too many control code parameters
When the number of parameters in a CSI control code exceeds the size of
the mArgs array, the code may attempt to read past the end of the array,
resulting in a force close of the app.

Stop the index from moving past the last element of the mArgs array.
2017-06-03 00:15:09 -05:00
Jeff Smith
888802a519 Add test for CSI followed by many parameters 2017-06-03 00:14:20 -05:00
Fredrik Fornwall
1a09b6d2a6 Change android:extractNativeLibs to false
We're trying this to see if that fixes an F-Droid build issue.

See #319.
2017-05-18 13:25:41 +02:00
Fredrik Fornwall
9d3f7734a1 Update android build tools 2017-05-16 11:11:57 +02:00
Fredrik Fornwall
61f766b59f Bump build tools in .travis.yml 2017-05-16 00:21:15 +02:00
Fredrik Fornwall
3f08376881 Bump version to 0.49 2017-05-15 23:42:55 +02:00
Fredrik Fornwall
cf06e70429 Update gradle configuration 2017-05-15 23:42:30 +02:00
Fredrik Fornwall
0714e435cb Do not show a Toast on clipboard text
Fixes https://github.com/termux/termux-app/issues/149.
2017-05-15 23:42:13 +02:00
Fredrik Fornwall
c26315185f Export TMPDIR to $PREFIX/tmp
Fixes https://github.com/termux/termux-packages/issues/1010.
Fixes https://github.com/termux/termux-app/issues/306.
2017-05-15 23:40:55 +02:00
Fredrik Fornwall
adc43c40c5 Perform haptic feedback on extra keys
Fixes #269.
2017-04-16 12:18:13 +02:00
Fredrik Fornwall
779b1ca1f8 Update the Android gradle plugin to 2.3.1 2017-04-05 09:10:54 +02:00
Fredrik Fornwall
e375f99b0c Remove float/ module from .idea/gradle.xml 2017-04-05 09:09:48 +02:00
Fredrik Fornwall
0ac55cd77a Remove float/
The Termux:Float app has a new home at:

https://github.com/termux/termux-float
2017-04-04 23:39:05 +02:00
Fredrik Fornwall
4aefa5049b Add gradle setup to publish to jcenter 2017-04-04 23:31:00 +02:00
Fredrik Fornwall
5b14124258 Make GestureAndScaleRecognizer non-public 2017-04-04 20:56:19 +02:00
Fredrik Fornwall
1a9c38374c Fix some invalid html in javadoc 2017-04-02 15:11:56 +02:00
Fredrik Fornwall
eefd504f08 Use getLocationOnScreen() to calculate position
This avoids problem when the window has been prevented from scrolling
outside of the screen, or when the touch keyboard has made it move.
2017-04-02 14:46:47 +02:00
Fredrik Fornwall
19f838e3d1 Show window if launched when hidden 2017-04-02 14:46:00 +02:00
Fredrik Fornwall
63adb2b132 Rename TerminalKeyListener to TerminalViewClient 2017-04-02 14:25:34 +02:00
Fredrik Fornwall
7d3a988d2f Restructure library modules
terminal/ -> terminal-emulator/
view/ -> terminal-view/
2017-04-02 14:21:36 +02:00
Fredrik Fornwall
5cd705d6d1 Reformat all code in float/, getting rid of tabs 2017-04-02 14:10:26 +02:00
Fredrik Fornwall
af8b269b51 Work on Termux:Float input handling 2017-04-02 14:09:43 +02:00
Fredrik Fornwall
8c220eaaea Do not call long press listener when scaling 2017-04-02 14:07:19 +02:00
Fredrik Fornwall
0142e1ed0e Update Termux:Float launcher icon 2017-04-02 07:33:06 +02:00
Fredrik Fornwall
ac0a349ed9 Remove versionCode&versionName from libraries 2017-04-02 07:12:03 +02:00
Fredrik Fornwall
548b0916b6 Clean up {terminal,view}/build.gradle 2017-04-02 06:58:59 +02:00
Fredrik Fornwall
009de5a3ee Split up into modules and add float module
Split the app/ module into three modules

terminal/ - Terminal emulator library module.
view/ - Terminal view library module (depending on terminal/).
app/ - The main Termux app (depending on view/).

Also add the

float/ - The Termux:Float app (depending on view/).
2017-04-01 19:06:02 +02:00
Fredrik Fornwall
41d0d60017 Respect content type termux-open for url:s 2017-03-28 23:52:52 +02:00
Fredrik Fornwall
12ac0fa73c Add android:extractNativeLibs="false" to manifest 2017-03-12 20:55:58 +01:00
Fredrik Fornwall
835dfc0276 Avoid synthetic accessor method 2017-03-06 01:53:09 +01:00
Fredrik Fornwall
f60316835f Fix some android studio lint warnings 2017-03-06 01:47:08 +01:00
Fredrik Fornwall
f74a091be6 Remove useless continue statement 2017-03-06 01:44:16 +01:00
Fredrik Fornwall
dd6cb5221d Work around Android < 7.0 wifi manager leak
http://tools.android.com/tech-docs/lint-in-studio-2-3#TOC-WifiManager-Leak
2017-03-06 01:42:33 +01:00
Fredrik Fornwall
cab6df5c0e Update gradle config 2017-03-06 01:41:12 +01:00
Fredrik Fornwall
f6f0809558 Bump version to 0.48 2017-03-02 00:32:09 +01:00
Fredrik Fornwall
11ed7e45d8 Fix crash when opening URL:s in Android < 7.0
In versions of Android earlier than 7.0 the FLAG_ACTIVITY_NEW_TASK
is needed when starting an activity from a non-activity context.

This was removed in Android 7.0 (possibly by mistake), see
https://code.google.com/p/android/issues/detail?id=226647.

Fixes #802
2017-03-02 00:29:17 +01:00
Fredrik Fornwall
ed1874db05 Bump version to 0.47 2017-02-28 00:47:16 +01:00
Fredrik Fornwall
cb60803a80 Tweak launcher size 2017-02-28 00:46:21 +01:00
Fredrik Fornwall
fc92a27cb2 Extended keyboard: allow inline input
Ff the buffer is empty, send a newline, otherwise send the content of
the buffer with the newline stripped. This way <Enter><Enter> means
"insert the buffer content and send a newline", while a single <Enter>
means just "insert the buffer content". Fixes #261.
2017-02-28 00:42:36 +01:00
Fredrik Fornwall
29e62e608f Use standard actions 2017-02-28 00:42:07 +01:00
Fredrik Fornwall
8a7f93d722 Launch $PREFIX/bin/login by default
The login command takes care of both showing the etc/motd and
launching the login shell.
2017-02-28 00:41:20 +01:00
Fredrik Fornwall
420683fe65 Add built-in broadcast receiver to open files 2017-02-27 22:31:11 +01:00
Fredrik Fornwall
a3256ed551 Travis testing 2017-02-05 22:25:37 +01:00
Fredrik Fornwall
57add98e3c More attempts to get coverity scan working 2017-02-05 21:44:47 +01:00
Fredrik Fornwall
6e5c04e04f Remove the travis certificate workaround 2017-02-05 18:24:59 +01:00
Fredrik Fornwall
528a05ef61 Remove the welcome dialog
Instead of a modal dialog to be dismissed without any way to recall
it, the initial information will be shown in the terminal itself.
2017-02-04 11:31:04 +01:00
Fredrik Fornwall
cb2f892dc5 Adjust text placement on feature graphics 2016-12-30 03:23:53 +01:00
Fredrik Fornwall
8b6e8d7fdd Bump version to 0.46 2016-12-30 02:43:40 +01:00
Fredrik Fornwall
d0eeaa9fc3 Call completeWakefulIntent() in Termux service 2016-12-30 02:42:41 +01:00
Fredrik Fornwall
b793913481 Style for android tv launcher transition 2016-12-30 02:39:26 +01:00
Fredrik Fornwall
d48b438c40 Test wcwidth on two more code points 2016-12-30 02:39:01 +01:00
Fredrik Fornwall
11afe895e1 Try with a bit shorter process completed messages 2016-12-30 02:38:41 +01:00
Fredrik Fornwall
bc96f71a2d Update the tv banner a bit 2016-12-30 02:34:43 +01:00
Fredrik Fornwall
87dfded5e6 Remove unused on_bell.xml file 2016-12-30 01:54:31 +01:00
Fredrik Fornwall
7c0ae4cb54 Remove the bell shake animation
Fixes https://github.com/termux/termux-packages/issues/628
Fixes https://github.com/termux/termux-app/issues/222
2016-12-30 01:53:07 +01:00
Fredrik Fornwall
b917acbbfa Do not use IME_ACTION_NONE in the inputconnection
Using IME_ACTION_NONE prevents enter key to be used with the stock
Android TV keyboard.

Fixes #221.
2016-12-30 01:42:47 +01:00
Fredrik Fornwall
7d9d6fb797 Translate \n to \r when receiving text from an IME
Fixes issue with fzf and return using the stock cyanogenmod
keyboard reported by @mklein994 on gitter.
2016-12-30 00:45:11 +01:00
Fredrik Fornwall
74040dd37f Add feature and tv banner to art/ 2016-12-29 22:29:37 +01:00
Fredrik Fornwall
e94f06d0f7 Silence lint warnings in shortcuts.xml 2016-12-28 02:45:14 +01:00
Fredrik Fornwall
af21b6dc3e Remove unused variable. Use Collections.addAll() 2016-12-28 01:19:57 +01:00
Fredrik Fornwall
e0e8257f1c Update build dependencies 2016-12-28 01:07:12 +01:00
Fredrik Fornwall
743b067cae Bump version to 0.45 2016-12-27 10:43:05 +01:00
Fredrik Fornwall
23333c074a Fix NPE regression in version 0.44 2016-12-27 10:42:41 +01:00
Fredrik Fornwall
f11644fa51 travis: Update build tools 2016-12-26 05:18:45 +01:00
Fredrik Fornwall
212be59fca gradle: Update build tools 2016-12-26 04:48:28 +01:00
Fredrik Fornwall
e3a1f8224f Add art/copy-to-other-apps.sh script 2016-12-26 04:45:00 +01:00
Fredrik Fornwall
4f40d5a26a Bump version to 0.44 2016-12-26 04:07:44 +01:00
Fredrik Fornwall
df92896eef Add Android 7.1 launcher shortcut for new session 2016-12-26 04:07:03 +01:00
Fredrik Fornwall
4c93cb42f1 Use constant for intent extra key 2016-12-26 00:57:09 +01:00
Fredrik Fornwall
34afb9de43 Give up on using vector drawables for launch icons
Instead use svg files in art/ and a script to build the png files.
2016-12-24 01:05:33 +01:00
Fredrik Fornwall
b6ea29d260 Remove duplicate updateNotification() calls 2016-12-24 00:25:24 +01:00
Fredrik Fornwall
289d58a2f0 Try it with launcher icons in drawable-anydpi 2016-12-10 22:51:11 -05:00
Fredrik Fornwall
0501ce924b gradle: Update android gradle plugin 2016-12-07 20:26:09 -05:00
Fredrik Fornwall
d12256f5e5 Add flags to imeOptions
Add IME_FLAG_NO_ENTER_ACTION and IME_ACTION_NONE. I haven't
encountered any issue without them, but specifying them is correct.
2016-12-04 17:32:40 +01:00
Fredrik Fornwall
fcbc036f92 Update gradle to 3.2.1 2016-12-04 17:26:26 +01:00
Fredrik Fornwall
70d5839334 Bump version to 0.43 for preview testing 2016-12-04 04:38:35 +01:00
Fredrik Fornwall
9c19540759 Keep track of background tasks
Also merge wake lock and wifi lock into one, and make it exposed to
termux-wake-lock and termux-wake-unlock commands.
2016-12-04 04:37:13 +01:00
Fredrik Fornwall
9fe0e49473 Render cursor color&shapes (underline and ibeam)
Allow the cursor to be colored by the theme, and support rendering
underline and ibeam cursor styles.
2016-12-04 01:06:50 +01:00
Fredrik Fornwall
357b17e972 Improve setup of symlinks to external storage
The context.getExternalFilesDirs(null) call may return several
elements, and some of them may be null.
2016-12-04 01:04:19 +01:00
Fredrik Fornwall
6334470f81 Try to handle Samsung keyboard better
The stock Samsung keyboard with 'Auto check spelling' enabled may
send multiple backspaces. Note that this auto-correction of
spelling will not work good in general with a terminal, so should
be disabled (or another keyboard used) when using Termux.
2016-11-28 00:26:37 +01:00
Fredrik Fornwall
b8cdd59c68 Use gradle build, not assembly, for coverity 2016-11-25 03:55:07 +01:00
Fredrik Fornwall
6cf36fffd7 Fix typo on coverity setup 2016-11-25 03:43:16 +01:00
Fredrik Fornwall
f10ecd4db5 Try to fix coverity scans in travis 2016-11-25 03:17:50 +01:00
Fredrik Fornwall
26457e8443 Tweak fillcolor in icons for easier svg generation 2016-11-25 02:14:54 +01:00
Fredrik Fornwall
6702846c7c Update build tools version in travis 2016-11-24 23:58:43 +01:00
Fredrik Fornwall
0c6180bbb1 Update gradle deps 2016-11-24 23:26:27 +01:00
Fredrik Fornwall
fcf07f6a19 Coverity: Specify "branch_pattern: master" 2016-11-24 20:15:31 +01:00
Fredrik Fornwall
e272e3b3b2 Update README a bit 2016-11-24 20:05:28 +01:00
Fredrik Fornwall
cdf0e72145 Enable coverity scan for all branches 2016-11-24 19:36:38 +01:00
Fredrik Fornwall
70245eb78c Use BackgroundJob.setupProcessArgs for sessions 2016-11-23 01:55:29 +01:00
Fredrik Fornwall
ee7631dfac Add a round icon for android-25 2016-11-23 01:38:17 +01:00
Fredrik Fornwall
5ecf5d12d1 Tweak the icon somewhat 2016-11-23 01:37:58 +01:00
Fredrik Fornwall
0c8cd90f4e Use $PREFIX/bin/sh for script file without shebang
Also try to handle #!(/usr)/bin/foo shebangs.
2016-11-20 16:43:27 +01:00
Fredrik Fornwall
e1ea68913f Use to a vector notification icon 2016-11-19 22:33:45 +01:00
Fredrik Fornwall
dde854eba7 Switch to a vector icon 2016-11-19 22:18:26 +01:00
Fredrik Fornwall
07a4607c04 Merge pull request #193 from friederbluemle/update-gradle-wrapper
Update Gradle wrapper to 3.2
2016-11-19 21:04:10 +01:00
Fredrik Fornwall
883be37b98 Add a shake animation on a terminal bell 2016-11-19 15:41:24 +01:00
Frieder Bluemle
d939d3d927 Update Gradle wrapper to 3.2 2016-11-18 18:01:00 +08:00
Fredrik Fornwall
a0fa51eb92 Start work on background jobs 2016-10-26 02:26:44 +02:00
Fredrik Fornwall
755513bb33 Update gradle setup 2016-10-20 01:00:28 +02:00
Fredrik Fornwall
8ad7a6669c Switch a commit() to apply() for shared prefs 2016-10-16 00:13:35 +02:00
Fredrik Fornwall
60f7aada9e Remove logging at info level 2016-10-16 00:13:04 +02:00
Fredrik Fornwall
6aa0492434 Add @NonNull annotation 2016-10-16 00:12:42 +02:00
Fredrik Fornwall
019aa44837 Update Android Gradle Plugin 2016-10-10 22:23:39 +02:00
Fredrik Fornwall
8d3d5e147f Remove outdated JNI building instructions 2016-09-29 01:13:45 +02:00
Fredrik Fornwall
44197b90e2 Update .travis.yml 2016-09-29 00:34:12 +02:00
Fredrik Fornwall
794c7ee333 Remove deprecated android:singleLine attribute 2016-09-27 00:30:05 +02:00
Fredrik Fornwall
0457ddbc69 Update build.gradle with latest versions 2016-09-27 00:29:47 +02:00
Fredrik Fornwall
283792af5e Refresh listview after changing session name 2016-09-27 00:29:08 +02:00
Fredrik Fornwall
be7cfa603a Fix gradle syntax for Android Studio 2016-09-25 01:15:49 +02:00
Fredrik Fornwall
3480bf7346 Switch from cmake to ndk-build
We switch from cmake to ndk-build to make it easier for builders
in not requiring an additional tool installed. The JNI build is
so simple so we don't really need much of a build tool anyway.
2016-09-24 20:22:10 +02:00
Fredrik Fornwall
8314a2756c Setup NDK in travis 2016-09-23 08:57:40 +02:00
Fredrik Fornwall
4de82d9fe0 Update gradle to 3.1 2016-09-21 22:34:44 +02:00
Fredrik Fornwall
d658e16801 Use the new cmake support to build JNI code 2016-09-21 22:30:20 +02:00
Fredrik Fornwall
26dcd5af88 Update gradle and Android Studio 2016-09-19 23:26:49 +02:00
Fredrik Fornwall
8056013082 Bump version to 0.42 2016-09-16 23:18:51 +02:00
Fredrik Fornwall
8e90545c4b Remove comment from build.gradle 2016-09-16 23:18:34 +02:00
Fredrik Fornwall
426ddbacbd Remove useless casts 2016-09-16 23:17:47 +02:00
Fredrik Fornwall
7e1f8a551f Change VolumeUp+H to generate ~
Using VolumeUp+H to generate a tilde (~) is more useful than
sending the home key.

Fixes #151.
2016-09-16 23:15:27 +02:00
Fredrik Fornwall
e169af0447 Change shortcuts from Ctrl+Shift to Ctrl+Alt
This works on more language layouts and devices.

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

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

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

Fixes #131.
2016-08-08 23:22:47 +02:00
Fredrik Fornwall
d2b27978e2 Bump version for v0.39 2016-08-08 23:19:49 +02:00
Fredrik Fornwall
30b05e9ab2 Merge pull request #132 from michalbednarski/extrakeysview-alignment
Make ExtraKeysView work on Android 5
2016-08-08 23:10:10 +02:00
Michał Bednarski
c350318c77 Make ExtraKeysView work on Android 5
This is done by explicitly specifying alignment as GridLayout.FILL
as I have figured out that this was fixed in Android 6 in commit
6dafd87fb4%5E%21/#F0
which set default alignment to FILL if weight is nonzero
2016-08-08 10:16:47 +02:00
Fredrik Fornwall
c9b49cef58 Bump version to 0.38 2016-08-05 00:00:53 +02:00
Fredrik Fornwall
f9c642c672 Support Unicode 9 for wcwidth (don't squash emojis) 2016-08-04 23:58:09 +02:00
Fredrik Fornwall
c0a5e5f57a Switch to TYPE_NULL as input type
This fixes #126 where the previous input type put some keyboards into
word mode (no direct echo). The workaround for Google Pinyin does not
seem to be necessary no more.

Also fix backspace after entering emojis on some keyboards (Swype).
2016-08-04 23:56:17 +02:00
Fredrik Fornwall
dfdc9b37e1 Allow predictive text area input to remove session
Fixes #124
2016-08-04 23:27:42 +02:00
Fredrik Fornwall
dfb22e6050 Make user-configurable shortcuts case insensitive 2016-08-04 18:11:50 +02:00
Fredrik Fornwall
b95d84fe13 Use absolute reference for android.R (lint check) 2016-08-02 17:34:13 +02:00
Fredrik Fornwall
a73228b109 Fix Enter to finish session in more cases
Detect the Enter key to finish a session not only on KeyEvent:s,
but also when the IME uses InputConnection.commitText() to send
\n. This seems to be triggered more after switching to the Uri
input type. Closes #124

Also bump app version for a quick release.
2016-08-01 06:37:49 +02:00
111 changed files with 2022 additions and 701 deletions

1
.gitignore vendored
View File

@@ -6,6 +6,7 @@
build/
*.apk
*.so
.externalNativeBuild
# Crashlytics configuations
com_crashlytics_export_strings.xml

9
.idea/gradle.xml generated
View File

@@ -10,14 +10,11 @@
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
<option value="$PROJECT_DIR$/terminal-emulator" />
<option value="$PROJECT_DIR$/terminal-view" />
</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>

View File

@@ -6,16 +6,20 @@ env:
global:
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
# via the "travis encrypt" command using the project repo's public key
- secure: "ACnFJxw0VusS2lnGXL+epP/CNJmftWS39YcPdgN2EurWw5ZfXSo7vi+zpMB+11IBS3LQyLFFUambi2N9L4lbReZkHVkoVcZFGZlwbXNTAeqT8CABPTcuOyEOZU4bJwqeYU87ztYipENMLNECaZrgWx5odbWLKnSJQw7Zkb4ArCstfXfYk9u8q49ThRxQyGwHW2xKp1an5aa+3Y6IY+ywsSHw6AvXbyFH078Kolxy86caagczcfmKcMi15QYzwAvFggUphvsO3M5PHJMQXuaNlQxDcQRGUEXsK8aZE0dPH5PB97SFjDALZqI7NEpjZAk5htWjX48ssW064LDbjcBg/ZLgDd8R8uhA159NVZgvcnP2czCn6pmggx1sW5MBmcj7i+bJS2ejaMO+KoovWlVvsch742H5QR6rQaNkjDZRsGVLYvJaR1gBLs898UoT1hcHWoqLVR22r2VFo7OWWCRfNRvZuZDR2HIrYRdFvn8P3nWVMkvXwgsOlxWG5sN+yQqW+6lZS7hivsFhtYs4CkRdoZIan3Qvi/CkY8Lg+ESkZ3IJ0NnId8qOWH+8Xl1sqZ7xlsWTd1sYYHlpvkdvqw1HNLP22EpwwKW5Kb5zBEd/qs3o1OO0Tqa0MR6JpgGdHHRk1iZ25+qTfRVP06vO2RXsgAx4SZfO7DyB0QZn8tGNMMI="
- secure: "LdajbHNfRlpnqzhX5KY2Vr7KtzU9vXDs1TCNn93J6Dt522f2AaiyUDJvISvz+uslk0WJiS5bB5vGwQmXginxz6Qi6uMgMbjWXulv1vfs6ZviKpUX348DOp1qKPa8WfVNB66F84SwGIfc8cRMAgCFw79l/DFgLErubF8vKo1wZ8Hmvrz//+RJ0BGMa3YRc4VyJhAL0P+0Wc1Q2Im7R9EovAxC5pZXBIMSgr6g5GzLWPisbNLXpMPGsDeYhcenO6XCtCCy+aNxUYM8vcrLDzlVXR5Hy7KEs/MGRTS0Yk13TWUEYa5wBpKelFTszdWYLVn5ANreh/aXRVfHpnW3epotMYguLx1kSvOhWEnc4F+qqv3nle2LpDg9Y9bcLyTTcYnPl9smqEVVjEDu0FoIr1V58xkG4Oc6BPIvLRjlMVU96PXh2HxMLuGsJ/xM+uAFU9oVMbC07xn42Eu5O4NHOHJNOwMWac4/lSKRK8W/7/vWuXj5vhkD9ZsGVpN70UtY5HAfNUGADnTeDblvjgFTNZ2mUN/u0o7Z8ZFURYllZ9YU+Vr2nPf9CAhVBjuwFWx8uRQpAg1aDmc1dVMJijRBeBeU/uWhYqsGp34wkNEl8VGzob4R4QTyI8+T7CndGqKVmbTK/SjqKhjjPpbXIAfOH+JtxvAnNmb8XeQSJ32uK2nexFo="
android:
components:
- platform-tools
- tools
- build-tools-24.0.1
- android-24
- build-tools-25.0.3
- 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
script:
- ./gradlew testDebugUnitTest
@@ -26,5 +30,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

3
LICENSE.md Normal file
View File

@@ -0,0 +1,3 @@
Released under [the GPLv3 license](https://www.gnu.org/licenses/gpl.html).
Contains code from `Terminal Emulator for Android` by which is released under [the Apache License 2.0](https://www.apache.org/licenses/).

View File

@@ -3,24 +3,14 @@ Termux app
[![Travis build status](https://travis-ci.org/termux/termux-app.svg?branch=master)](https://travis-ci.org/termux/termux-app)
[![Join the chat at https://gitter.im/termux/termux](https://badges.gitter.im/termux/termux.svg)](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/)
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.
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)
Terminal resources
==================

View File

@@ -1,26 +1,21 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 24
buildToolsVersion "24.0.1"
compileSdkVersion 25
buildToolsVersion "25.0.3"
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.3.1'
compile "com.android.support:support-v4:25.3.1"
compile project(":terminal-view")
}
defaultConfig {
applicationId "com.termux"
minSdkVersion 21
targetSdkVersion 24
versionCode 36
versionName "0.35"
ndk {
moduleName "libtermux"
abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
cFlags "-std=c11 -Wall -Wextra -Os -fno-stack-protector -nostdlib -Wl,--gc-sections"
}
targetSdkVersion 25
versionCode 52
versionName "0.52"
}
buildTypes {

View File

@@ -1,13 +0,0 @@
package com.termux;
import android.app.Application;
import android.test.ApplicationTestCase;
/**
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
*/
public class ApplicationTest extends ApplicationTestCase<Application> {
public ApplicationTest() {
super(Application.class);
}
}

View File

@@ -13,9 +13,11 @@
<uses-permission android:name="android.permission.VIBRATE" />
<application
android:extractNativeLibs="true"
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 +36,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
@@ -88,6 +91,15 @@
android:name="com.termux.app.TermuxService"
android:exported="false" />
<receiver android:name=".app.TermuxOpenReceiver" />
<provider android:authorities="com.termux.files"
android:readPermission="android.permission.permRead"
android:exported="true"
android:grantUriPermissions="true"
android:name="com.termux.app.TermuxOpenReceiver$ContentProvider" />
<meta-data android:name="com.sec.android.support.multiwindow" android:value="true" />
</application>
</manifest>

View File

@@ -4,58 +4,71 @@ 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.Collections;
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 +84,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 +93,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;
@@ -93,7 +106,6 @@ public final class BackgroundJob {
// 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");
@@ -104,9 +116,78 @@ public final class BackgroundJob {
final String langEnv = "LANG=en_US.UTF-8";
final String pathEnv = "PATH=" + TermuxService.PREFIX_PATH + "/bin:" + TermuxService.PREFIX_PATH + "/bin/applets";
final String pwdEnv = "PWD=" + cwd;
final String tmpdirEnv = "TMPDIR=" + TermuxService.PREFIX_PATH + "/tmp";
return new String[]{termEnv, homeEnv, prefixEnv, ps1Env, ldEnv, langEnv, pathEnv, pwdEnv, androidRootEnv, androidDataEnv, externalStorageEnv};
return new String[]{termEnv, homeEnv, prefixEnv, ps1Env, ldEnv, langEnv, pathEnv, pwdEnv, androidRootEnv, androidDataEnv, externalStorageEnv, tmpdirEnv};
}
}
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.
} 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) Collections.addAll(result, args);
return result.toArray(new String[result.size()]);
}
}

View File

@@ -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

View File

@@ -3,6 +3,7 @@ package com.termux.app;
import android.content.Context;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
import android.view.View;
import android.widget.Button;
@@ -144,6 +145,7 @@ public final class ExtraKeysView extends GridLayout {
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
finalButton.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP);
View root = getRootView();
switch (buttonText) {
case "CTRL":
@@ -165,8 +167,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);

View File

@@ -213,7 +213,7 @@ public final class TermuxActivity extends Activity implements ServiceConnection
setContentView(R.layout.drawer_layout);
mTerminalView = (TerminalView) findViewById(R.id.terminal_view);
mTerminalView.setOnKeyListener(new TermuxKeyListener(this));
mTerminalView.setOnKeyListener(new TermuxViewClient(this));
mTerminalView.setTextSize(mSettings.getFontSize());
mFullScreenHelper.setImmersive(mSettings.isFullScreen());
@@ -245,9 +245,17 @@ 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()) {
String textToSend = editText.getText().toString();
if (textToSend.length() == 0) textToSend = "\n";
session.write(textToSend);
} else {
removeFinishedSession(session);
}
editText.setText("");
}
return true;
}
});
@@ -389,27 +397,26 @@ public final class TermuxActivity extends Activity implements ServiceConnection
@Override
public void onClipboardText(TerminalSession session, String text) {
if (!mIsVisible) return;
showToast("Clipboard:\n\"" + text + "\"", false);
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
clipboard.setPrimaryClip(new ClipData(null, new String[]{"text/plain"}, new ClipData.Item(text)));
}
@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;
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 +430,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();
@@ -485,17 +493,6 @@ public final class TermuxActivity extends Activity implements ServiceConnection
public void run() {
if (mTermService == null) return; // Activity might have been destroyed.
try {
if (TermuxPreferences.isShowWelcomeDialog(TermuxActivity.this)) {
new AlertDialog.Builder(TermuxActivity.this).setTitle(R.string.welcome_dialog_title).setMessage(R.string.welcome_dialog_body)
.setCancelable(false).setPositiveButton(android.R.string.ok, null)
.setNegativeButton(R.string.welcome_dialog_dont_show_again_button, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
TermuxPreferences.disableWelcomeDialog(TermuxActivity.this);
dialog.dismiss();
}
}).show();
}
addNewSession(false, null);
} catch (WindowManager.BadTokenException e) {
// Activity finished - ignore.
@@ -507,7 +504,13 @@ public final class TermuxActivity extends Activity implements ServiceConnection
finish();
}
} else {
switchToSession(getStoredCurrentSessionOrLast());
Intent i = getIntent();
if (i != null && Intent.ACTION_RUN.equals(i.getAction())) {
// Android 7.1 app shortcut from res/xml/shortcuts.xml.
addNewSession(false, null);
} else {
switchToSession(getStoredCurrentSessionOrLast());
}
}
}
@@ -528,6 +531,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 +850,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));
}
}
}

View File

@@ -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);

View File

@@ -0,0 +1,178 @@
package com.termux.app;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.util.Log;
import android.webkit.MimeTypeMap;
import com.termux.terminal.EmulatorDebug;
import java.io.File;
import java.io.FileNotFoundException;
public class TermuxOpenReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
final Uri data = intent.getData();
if (data == null) {
Log.e(EmulatorDebug.LOG_TAG, "termux-open: Called without intent data");
return;
}
final String filePath = data.getPath();
final String contentTypeExtra = intent.getStringExtra("content-type");
final boolean useChooser = intent.getBooleanExtra("chooser", false);
final String intentAction = intent.getAction() == null ? Intent.ACTION_VIEW : intent.getAction();
switch (intentAction) {
case Intent.ACTION_SEND:
case Intent.ACTION_VIEW:
// Ok.
break;
default:
Log.e(EmulatorDebug.LOG_TAG, "Invalid action '" + intentAction + "', using 'view'");
break;
}
final boolean isExternalUrl = data.getScheme() != null && !data.getScheme().equals("file");
if (isExternalUrl) {
Intent urlIntent = new Intent(intentAction, data);
if (intentAction.equals(Intent.ACTION_SEND)) {
urlIntent.putExtra(Intent.EXTRA_TEXT, data.toString());
urlIntent.setData(null);
} else if (contentTypeExtra != null) {
urlIntent.setDataAndType(data, contentTypeExtra);
}
urlIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
context.startActivity(urlIntent);
} catch (ActivityNotFoundException e) {
Log.e(EmulatorDebug.LOG_TAG, "termux-open: No app handles the url " + data);
}
return;
}
final File fileToShare = new File(filePath);
if (!(fileToShare.isFile() && fileToShare.canRead())) {
Log.e(EmulatorDebug.LOG_TAG, "termux-open: Not a readable file: '" + fileToShare.getAbsolutePath() + "'");
return;
}
Intent sendIntent = new Intent();
sendIntent.setAction(intentAction);
sendIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION);
String contentTypeToUse;
if (contentTypeExtra == null) {
String fileName = fileToShare.getName();
int lastDotIndex = fileName.lastIndexOf('.');
String fileExtension = fileName.substring(lastDotIndex + 1, fileName.length());
MimeTypeMap mimeTypes = MimeTypeMap.getSingleton();
// Lower casing makes it work with e.g. "JPG":
contentTypeToUse = mimeTypes.getMimeTypeFromExtension(fileExtension.toLowerCase());
if (contentTypeToUse == null) contentTypeToUse = "application/octet-stream";
} else {
contentTypeToUse = contentTypeExtra;
}
Uri uriToShare = Uri.withAppendedPath(Uri.parse("content://com.termux.files/"), filePath);
if (Intent.ACTION_SEND.equals(intentAction)) {
sendIntent.putExtra(Intent.EXTRA_STREAM, uriToShare);
sendIntent.setType(contentTypeToUse);
} else {
sendIntent.setDataAndType(uriToShare, contentTypeToUse);
}
if (useChooser) {
sendIntent = Intent.createChooser(sendIntent, null).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
try {
context.startActivity(sendIntent);
} catch (ActivityNotFoundException e) {
Log.e(EmulatorDebug.LOG_TAG, "termux-open: No app handles the url " + data);
}
}
public static class ContentProvider extends android.content.ContentProvider {
@Override
public boolean onCreate() {
return true;
}
@Override
public Cursor query(@NonNull Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
File file = new File(uri.getPath());
if (projection == null) {
projection = new String[]{
MediaStore.MediaColumns.DISPLAY_NAME,
MediaStore.MediaColumns.SIZE,
MediaStore.MediaColumns._ID
};
}
Object[] row = new Object[projection.length];
for (int i = 0; i < projection.length; i++) {
String column = projection[i];
Object value;
switch (column) {
case MediaStore.MediaColumns.DISPLAY_NAME:
value = file.getName();
break;
case MediaStore.MediaColumns.SIZE:
value = (int) file.length();
break;
case MediaStore.MediaColumns._ID:
value = 1;
break;
default:
value = null;
}
row[i] = value;
}
MatrixCursor cursor = new MatrixCursor(projection);
cursor.addRow(row);
return cursor;
}
@Override
public String getType(@NonNull Uri uri) {
return null;
}
@Override
public Uri insert(@NonNull Uri uri, ContentValues values) {
return null;
}
@Override
public int delete(@NonNull Uri uri, String selection, String[] selectionArgs) {
return 0;
}
@Override
public int update(@NonNull Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return 0;
}
@Override
public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode) throws FileNotFoundException {
File file = new File(uri.getPath());
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
}
}
}

View File

@@ -36,7 +36,6 @@ final class TermuxPreferences {
private static final String SHOW_EXTRA_KEYS_KEY = "show_extra_keys";
private static final String FONTSIZE_KEY = "fontsize";
private static final String CURRENT_SESSION_KEY = "current_session";
private static final String SHOW_WELCOME_DIALOG_KEY = "intro_dialog";
private boolean mFullScreen;
private int mFontSize;
@@ -105,7 +104,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) {
@@ -117,14 +116,6 @@ final class TermuxPreferences {
return null;
}
public static boolean isShowWelcomeDialog(Context context) {
return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(SHOW_WELCOME_DIALOG_KEY, true);
}
public static void disableWelcomeDialog(Context context) {
PreferenceManager.getDefaultSharedPreferences(context).edit().putBoolean(SHOW_WELCOME_DIALOG_KEY, false).apply();
}
public void reloadFromProperties(Context context) {
try {
File propsFile = new File(TermuxService.HOME_PATH + "/.termux/termux.properties");
@@ -184,9 +175,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;
}

View File

@@ -11,8 +11,10 @@ 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.support.v4.content.WakefulBroadcastReceiver;
import android.util.Log;
import android.widget.ArrayAdapter;
@@ -22,7 +24,6 @@ import com.termux.terminal.TerminalSession;
import com.termux.terminal.TerminalSession.SessionChangedCallback;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@@ -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,45 +103,61 @@ 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);
// http://tools.android.com/tech-docs/lint-in-studio-2-3#TOC-WifiManager-Leak
WifiManager wm = (WifiManager) getApplicationContext().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 + "'");
}
if ((flags & START_FLAG_REDELIVERY) == 0) {
// Service is started by WBR, not restarted by system, so release the WakeLock from WBR.
WakefulBroadcastReceiver.completeWakefulIntent(intent);
}
// If this service really do get killed, there is no point restarting it automatically - let the user do on next
// start of {@link Term):
return Service.START_NOT_STICKY;
@@ -154,9 +174,9 @@ 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.
void updateNotification() {
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 +191,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 +209,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 +221,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,53 +253,15 @@ 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()) {
try {
File canonicalFile = shell.getCanonicalFile();
if (canonicalFile.isFile() && canonicalFile.canExecute()) {
executablePath = canonicalFile.getName().equals("busybox") ? (PREFIX_PATH + "/bin/ash") : canonicalFile.getAbsolutePath();
} else {
Log.w(EmulatorDebug.LOG_TAG, "$HOME/.termux/shell points to non-executable shell: " + canonicalFile.getAbsolutePath());
}
} catch (IOException e) {
Log.e(EmulatorDebug.LOG_TAG, "Error checking $HOME/.termux/shell", e);
}
}
if (executablePath == null) {
// Try bash, zsh and ash in that order:
for (String shellBinary : new String[]{"bash", "zsh", "ash"}) {
File shellFile = new File(PREFIX_PATH + "/bin/" + shellBinary);
if (shellFile.canExecute()) {
executablePath = shellFile.getAbsolutePath();
break;
}
for (String shellBinary : new String[]{"login", "bash", "zsh"}) {
File shellFile = new File(PREFIX_PATH + "/bin/" + shellBinary);
if (shellFile.canExecute()) {
executablePath = shellFile.getAbsolutePath();
break;
}
}
@@ -290,23 +269,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 +332,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();
}
});
}
}

View File

@@ -12,18 +12,18 @@ import android.view.inputmethod.InputMethodManager;
import com.termux.terminal.KeyHandler;
import com.termux.terminal.TerminalEmulator;
import com.termux.terminal.TerminalSession;
import com.termux.view.TerminalKeyListener;
import com.termux.view.TerminalViewClient;
import java.util.List;
public final class TermuxKeyListener implements TerminalKeyListener {
public final class TermuxViewClient implements TerminalViewClient {
final TermuxActivity mActivity;
/** Keeping track of the special keys acting as Ctrl and Fn for the soft keyboard and other hardware keys. */
boolean mVirtualControlKeyDown, mVirtualFnKeyDown;
public TermuxKeyListener(TermuxActivity activity) {
public TermuxViewClient(TermuxActivity activity) {
this.mActivity = activity;
}
@@ -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);
@@ -264,6 +256,11 @@ public final class TermuxKeyListener implements TerminalKeyListener {
return false;
}
@Override
public boolean onLongPress(MotionEvent event) {
return false;
}
/** Handle dedicated volume buttons as virtual keys if applicable. */
private boolean handleVirtualKeys(int keyCode, KeyEvent event, boolean down) {
InputDevice inputDevice = event.getDevice();

View File

@@ -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);
}
}

View File

@@ -1,55 +0,0 @@
package com.termux.terminal;
/**
* Encodes effects, foreground and background colors into a 32 bit integer, 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).
*/
public final class TextStyle {
public final static int CHARACTER_ATTRIBUTE_BOLD = 1;
public final static int CHARACTER_ATTRIBUTE_ITALIC = 1 << 1;
public final static int CHARACTER_ATTRIBUTE_UNDERLINE = 1 << 2;
public final static int CHARACTER_ATTRIBUTE_BLINK = 1 << 3;
public final static int CHARACTER_ATTRIBUTE_INVERSE = 1 << 4;
public final static int CHARACTER_ATTRIBUTE_INVISIBLE = 1 << 5;
public final static int CHARACTER_ATTRIBUTE_STRIKETHROUGH = 1 << 6;
/**
* The selective erase control functions (DECSED and DECSEL) can only erase characters defined as erasable.
* <p/>
* This bit is set if DECSCA (Select Character Protection Attribute) has been used to define the characters that
* come after it as erasable from the screen.
*/
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;
public final static int COLOR_INDEX_FOREGROUND = 256;
public final static int COLOR_INDEX_BACKGROUND = 257;
public final static int COLOR_INDEX_CURSOR = 258;
/** The 256 standard color entries and the three special (foreground, background and cursor) ones. */
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);
static int encode(int foreColor, int backColor, int effect) {
return ((effect & 0b111111111) << 18) | ((foreColor & 0b111111111) << 9) | (backColor & 0b111111111);
}
public static int decodeForeColor(int encodedColor) {
return (encodedColor >> 9) & 0b111111111;
}
public static int decodeBackColor(int encodedColor) {
return encodedColor & 0b111111111;
}
public static int decodeEffect(int encodedColor) {
return (encodedColor >> 18) & 0b111111111;
}
}

View File

@@ -1,108 +0,0 @@
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.
*/
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,};
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};
/** 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;
}
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;
}
/** The width at an index position in a java char array. */
public static int width(char[] chars, int index) {
char c = chars[index];
return Character.isHighSurrogate(c) ? width(Character.toCodePoint(c, chars[index + 1])) : width(c);
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 695 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 786 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 597 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 779 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 983 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View 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>

View 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="M14,14
l5,0
l0,10
l-5,0"
/>
</vector>

View File

@@ -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"

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 331 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 505 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 691 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

@@ -11,10 +11,6 @@
<string name="share_transcript_title">Terminal transcript</string>
<string name="help">Help</string>
<string name="welcome_dialog_title">Welcome to Termux</string>
<string name="welcome_dialog_body">Long press and select <i>More…</i> to show a menu where <i>Help</i> is available.\n\nExecute <b>apt update</b> to update the packages list before installing packages.</string>
<string name="welcome_dialog_dont_show_again_button">Do not show again</string>
<string name="bootstrap_installer_body">Installing…</string>
<string name="bootstrap_error_title">Unable to install</string>
<string name="bootstrap_error_body">Termux was unable to install the bootstrap packages.\n\nCheck your network connection and try again.</string>
@@ -34,10 +30,6 @@
<string name="select_url_copied_to_clipboard">URL copied to clipboard</string>
<string name="share_transcript_chooser_title">Send text to:</string>
<string name="paste_text">Paste</string>
<string name="copy_text">Copy</string>
<string name="text_selection_more">More…</string>
<string name="kill_process">Kill process (%d)</string>
<string name="confirm_kill_process">Really kill this session?</string>
@@ -50,8 +42,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>

View File

@@ -5,6 +5,7 @@
<!-- NOTE: Cannot use "Light." since it hides the terminal scrollbar on the default black background. -->
<style name="Theme.Termux" parent="@android:style/Theme.Material.Light.NoActionBar">
<item name="android:statusBarColor">#000000</item>
<item name="android:colorPrimary">#FF000000</item>
<item name="android:windowBackground">@android:color/black</item>
<!-- Seen in buttons on left drawer: -->
@@ -13,6 +14,10 @@
<!-- Avoid action mode toolbar pushing down terminal content when
selecting text on pre-6.0 (non-floating toolbar). -->
<item name="android:windowActionModeOverlay">true</item>
<!-- https://developer.android.com/training/tv/start/start.html#transition-color -->
<item name="android:windowAllowReturnTransitionOverlap">true</item>
<item name="android:windowAllowEnterTransitionOverlap">true</item>
</style>
<style name="TermuxAlertDialogStyle" parent="@android:style/Theme.Material.Light.Dialog.Alert">

View File

@@ -0,0 +1,14 @@
<shortcuts xmlns:tools="http://schemas.android.com/tools"
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"
tools:targetApi="n_mr1">
<intent
android:action="android.intent.action.RUN"
android:targetPackage="com.termux"
android:targetClass="com.termux.app.TermuxActivity"/>
</shortcut>
</shortcuts>

20
art/copy-to-other-apps.sh Executable file
View File

@@ -0,0 +1,20 @@
#!/bin/sh
for DENSITY in mdpi hdpi xhdpi xxhdpi xxxhdpi; do
FOLDER=../app/src/main/res/mipmap-$DENSITY
for FILE in ic_launcher ic_launcher_round; do
PNG=$FOLDER/$FILE.png
# Update other apps:
for APP in api boot styling tasker widget; do
APPDIR=../../termux-$APP
if [ -d $APPDIR ]; then
APP_FOLDER=$APPDIR/app/src/main/res/mipmap-$DENSITY
mkdir -p $APP_FOLDER
cp $PNG $APP_FOLDER/$FILE.png
fi
done
done
done

30
art/feature-graphic.svg Normal file
View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!--
This is a feature graphic:
https://support.google.com/googleplay/android-developer/answer/1078870
- 1024px by 500px, no alpha
- Don't include any copy or important visual information near the borders of the asset,
specifically near the bottom third of the frame.
- Try to center align any logo/copy information in the vertical and horizontal center of the frame.
- If adding text, use large font sizes.
- Your graphic may be displayed alone without the app icon.
-->
<svg xmlns="http://www.w3.org/2000/svg"
version="1.1"
viewBox="0 0 1024 500">
<rect fill="#0" width="100%" height="100%" />
<text id="shell_prompt"
x="130"
y="330"
style="fill: #ffffff; font-size: 124px; font-family: Menlo;">
<!--
<tspan>$</tspan>
<tspan x="290">Termux</tspan>
<tspan x="734">█</tspan>
-->
<tspan>$ Termux █</tspan>
</text>
</svg>

After

Width:  |  Height:  |  Size: 962 B

View File

@@ -0,0 +1,5 @@
#!/bin/bash
echo "Generating feature graphics to ~/termux-icons/termux-feature-graphic.png..."
mkdir -p ~/termux-icons/
rsvg-convert feature-graphic.svg > ~/termux-icons/feature-graphic.png

20
art/generate-launcher-images.sh Executable file
View 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

9
art/generate-tv-banner.sh Executable file
View File

@@ -0,0 +1,9 @@
#!/bin/bash
echo "Generating feature graphics to ~/termux-icons/termux-feature-graphic.png..."
mkdir -p ~/termux-icons/
# The Android TV banner on google play (1280x720) has same aspect ratio
# as the banner in the app (320x180).
rsvg-convert -w 1280 -h 720 tv-banner.svg > ~/termux-icons/tv-banner.png
rsvg-convert -w 320 -h 180 tv-banner.svg > ../app/src/main/res/drawable/banner.png

26
art/ic_launcher.svg Normal file
View 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="M9,6
l30,0
q3 0,3 3
l0,30
q0 3, -3 3
l-30,0
q-3 0, -3-3
l0 -30
q0 -3, 3 -3"
/>
<!-- Block cursor. -->
<path fill="#FFF"
d="M14,14
l5,0
l0,10
l-5,0"
/>
</svg>

After

Width:  |  Height:  |  Size: 512 B

21
art/ic_launcher_round.svg Normal file
View 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

23
art/tv-banner.svg Normal file
View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!--
This is a tv banner graphic:
https://developer.android.com/design/tv/patterns.html#banner
- Size: 320 x 180 px, xhdpi resource in app
- Size: 1280 x 720 in google play.
- Text must be included in the image. If your app is available in more
than one language, you must provide versions of the banner image for each supported language.
-->
<svg xmlns="http://www.w3.org/2000/svg"
version="1.1"
viewBox="0 0 1280 720">
<rect fill="#0" width="100%" height="100%" />
<text id="shell_prompt"
x="200"
y="410"
style="fill: #ffffff; font-size: 210px; font-family: Menlo, Monospace;">
<tspan>Termux ▌</tspan>
</text>
</svg>

After

Width:  |  Height:  |  Size: 736 B

View File

@@ -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.3.3'
}
}

View File

@@ -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

Binary file not shown.

View File

@@ -1,6 +1,6 @@
#Sat Jul 23 17:08:29 CEST 2016
#Sun Jun 11 22:47:00 CEST 2017
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.5-bin.zip

22
gradlew vendored
View File

@@ -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
View 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

View File

@@ -0,0 +1,78 @@
// Start https://raw.githubusercontent.com/nuuneoi/JCenter/master/installv1.gradle
apply plugin: 'com.github.dcendents.android-maven'
group = publishedGroupId // Maven Group ID for the artifact
install {
repositories.mavenInstaller {
pom {
project {
packaging 'aar'
groupId publishedGroupId
artifactId artifact
name libraryName
description libraryDescription
url siteUrl
licenses {
license {
name 'GNU General Public License version 3'
url 'https://opensource.org/licenses/gpl-3.0.html'
}
}
developers {
developer {
id 'fornwall'
name 'Fredrik Fornwall'
email 'fredrik@fornwall.net'
}
}
scm {
connection gitUrl
developerConnection gitUrl
url siteUrl
}
}
}
}
}
// End https://raw.githubusercontent.com/nuuneoi/JCenter/master/installv1.gradle
// Start https://raw.githubusercontent.com/nuuneoi/JCenter/master/bintrayv1.gradle
apply plugin: 'com.jfrog.bintray'
version = libraryVersion
task sourcesJar(type: Jar) {
classifier = 'sources'
from android.sourceSets.main.java.srcDirs
}
artifacts {
archives sourcesJar
}
bintray {
user = System.getenv('BINTRAY_USER')
key = System.getenv('BINTRAY_API_KEY')
configurations = ['archives']
pkg {
repo = 'maven'
name = bintrayName
userOrg = 'termux'
desc = libraryDescription
websiteUrl = siteUrl
vcsUrl = gitUrl
licenses = ['GPL-3.0']
publish = true
publicDownloadNumbers = true
version {
desc = libraryDescription
gpg {
sign = false //Determines whether to GPG sign the files. The default is false
// passphrase = properties.getProperty("bintray.gpg.password")
}
}
}
}

View File

@@ -1 +1 @@
include ':app'
include ':app', ':terminal-emulator', ':terminal-view'

View File

@@ -0,0 +1,62 @@
plugins {
id "com.jfrog.bintray" version "1.7"
id "com.github.dcendents.android-maven" version "1.5"
}
apply plugin: 'com.android.library'
ext {
bintrayName = 'terminal-emulator'
publishedGroupId = 'com.termux'
libraryName = 'TerminalEmulator'
artifact = 'terminal-emulator'
libraryDescription = 'The terminal emulator used in Termux'
siteUrl = 'https://github.com/termux/termux'
gitUrl = 'https://github.com/termux/termux.git'
libraryVersion = '0.50'
}
android {
compileSdkVersion 25
buildToolsVersion "25.0.3"
defaultConfig {
minSdkVersion 21
targetSdkVersion 25
externalNativeBuild {
ndkBuild {
cFlags "-std=c11", "-Wall", "-Wextra", "-Werror", "-Os", "-fno-stack-protector", "-Wl,--gc-sections"
}
}
ndk {
abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
externalNativeBuild {
ndkBuild {
path "src/main/jni/Android.mk"
}
}
}
tasks.withType(Test) {
testLogging {
events "started", "passed", "skipped", "failed"
}
}
dependencies {
testCompile 'junit:junit:4.12'
}
apply from: '../scripts/bintray-publish.gradle'

25
terminal-emulator/proguard-rules.pro vendored Normal file
View File

@@ -0,0 +1,25 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/fornwall/lib/android-sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@@ -0,0 +1,2 @@
<manifest package="com.termux.terminal">
</manifest>

View File

@@ -26,9 +26,9 @@ import static android.view.KeyEvent.KEYCODE_F7;
import static android.view.KeyEvent.KEYCODE_F8;
import static android.view.KeyEvent.KEYCODE_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:

View File

@@ -3,7 +3,7 @@ package com.termux.terminal;
/**
* A circular buffer of {@link TerminalRow}:s which keeps notes about what is visible on a logical screen and the scroll
* history.
* <p/>
* <p>
* See {@link #externalToInternalRow(int)} for how to map from logical screen rows to array indices.
*/
public final class TerminalBuffer {
@@ -92,22 +92,20 @@ public final class TerminalBuffer {
/**
* Convert a row value from the public external coordinate system to our internal private coordinate system.
* <p/>
* <ul>
* <li>External coordinate system: -mActiveTranscriptRows to mScreenRows-1, with the screen being 0..mScreenRows-1.
* <li>Internal coordinate system: the mScreenRows lines starting at mScreenFirstRow comprise the screen, while the
* mActiveTranscriptRows lines ending at mScreenFirstRow-1 form the transcript (as a circular buffer).
* </ul>
* <p/>
* External <---> Internal:
* <p/>
*
* <pre>
* [ ... ] [ ... ]
* [ -mActiveTranscriptRows ] [ mScreenFirstRow - mActiveTranscriptRows ]
* [ ... ] [ ... ]
* [ 0 (visible screen starts here) ] <-----> [ mScreenFirstRow ]
* [ ... ] [ ... ]
* [ mScreenRows-1 ] [ mScreenFirstRow + mScreenRows-1 ]
* - External coordinate system: -mActiveTranscriptRows to mScreenRows-1, with the screen being 0..mScreenRows-1.
* - Internal coordinate system: the mScreenRows lines starting at mScreenFirstRow comprise the screen, while the
* mActiveTranscriptRows lines ending at mScreenFirstRow-1 form the transcript (as a circular buffer).
*
* External Internal:
*
* [ ... ] [ ... ]
* [ -mActiveTranscriptRows ] [ mScreenFirstRow - mActiveTranscriptRows ]
* [ ... ] [ ... ]
* [ 0 (visible screen starts here) ] [ mScreenFirstRow ]
* [ ... ] [ ... ]
* [ mScreenRows-1 ] [ mScreenFirstRow + mScreenRows-1 ]
* </pre>
*
* @param externalRow a row in the external coordinate system.
@@ -140,7 +138,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 +235,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 +319,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 +372,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 +386,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 +405,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);

View File

@@ -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];

View File

@@ -12,7 +12,7 @@ import java.util.Stack;
/**
* Renders text into a screen. Contains all the terminal-specific knowledge and state. Emulates a subset of the X Window
* 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>
@@ -56,8 +56,6 @@ public final class TerminalEmulator {
private static final int ESC_SELECT_LEFT_PAREN = 3;
/** Escape processing: Have seen ESC and a character-set-select ) char */
private static final int ESC_SELECT_RIGHT_PAREN = 4;
/** Escape processing: Have seen ESC and a character-set-select + char */
// private static final int ESC_SELECT_PLUS = 5;
/** Escape processing: "ESC [" or CSI (Control Sequence Introducer). */
private static final int ESC_CSI = 6;
/** Escape processing: ESC [ ? */
@@ -145,7 +143,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 +204,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 +614,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,12 +626,13 @@ 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;
int right = Math.min(getArg(3, mColumns, true) + 1, effectiveRightMargin - 1) + effectiveLeftMargin;
if (mArgIndex >= 4) {
if (mArgIndex >= mArgs.length) mArgIndex = mArgs.length - 1;
for (int i = 4; i <= mArgIndex; i++) {
int bits = 0;
boolean setOrClear = true; // True if setting, false if clearing.
@@ -717,7 +721,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 +956,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)
@@ -963,6 +966,7 @@ public final class TerminalEmulator {
break;
case 'h':
case 'l':
if (mArgIndex >= mArgs.length) mArgIndex = mArgs.length - 1;
for (int i = 0; i <= mArgIndex; i++)
doDecSetOrReset(b == 'h', mArgs[i]);
break;
@@ -979,6 +983,7 @@ public final class TerminalEmulator {
break;
case 'r':
case 's':
if (mArgIndex >= mArgs.length) mArgIndex = mArgs.length - 1;
for (int i = 0; i <= mArgIndex; i++) {
int externalBit = mArgs[i];
int internalBit = mapDecSetBitToInternalBit(externalBit);
@@ -1305,6 +1310,8 @@ public final class TerminalEmulator {
state.mSavedCursorRow = mCursorRow;
state.mSavedCursorCol = mCursorCol;
state.mSavedEffect = mEffect;
state.mSavedForeColor = mForeColor;
state.mSavedBackColor = mBackColor;
state.mSavedDecFlags = mCurrentDecSetFlags;
state.mUseLineDrawingG0 = mUseLineDrawingG0;
state.mUseLineDrawingG1 = mUseLineDrawingG1;
@@ -1316,6 +1323,8 @@ public final class TerminalEmulator {
SavedScreenState state = (mScreen == mMainBuffer) ? mSavedStateMain : mSavedStateAlt;
setCursorRowCol(state.mSavedCursorRow, state.mSavedCursorCol);
mEffect = state.mSavedEffect;
mForeColor = state.mSavedForeColor;
mBackColor = state.mSavedBackColor;
int mask = (DECSET_BIT_AUTOWRAP | DECSET_BIT_ORIGIN_MODE);
mCurrentDecSetFlags = (mCurrentDecSetFlags & ~mask) | (state.mSavedDecFlags & mask);
mUseLineDrawingG0 = state.mUseLineDrawingG0;
@@ -1637,6 +1646,7 @@ public final class TerminalEmulator {
/** Select Graphic Rendition (SGR) - see http://en.wikipedia.org/wiki/ANSI_escape_code#graphics. */
private void selectGraphicRendition() {
if (mArgIndex >= mArgs.length) mArgIndex = mArgs.length - 1;
for (int i = 0; i <= mArgIndex; i++) {
int code = mArgs[i];
if (code < 0) {
@@ -1687,43 +1697,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 +1933,7 @@ public final class TerminalEmulator {
mScreen.blockSet(sx, sy, w, h, ' ', getStyle());
}
private int getStyle() {
private long getStyle() {
return TextStyle.encode(mForeColor, mBackColor, mEffect);
}
@@ -2048,6 +2057,7 @@ public final class TerminalEmulator {
buf.append(", escapeState=");
buf.append(mEscapeState);
boolean firstArg = true;
if (mArgIndex >= mArgs.length) mArgIndex = mArgs.length - 1;
for (int i = 0; i <= mArgIndex; i++) {
int value = mArgs[i];
if (value >= 0) {
@@ -2262,8 +2272,8 @@ public final class TerminalEmulator {
mBottomMargin = mRows;
mRightMargin = mColumns;
mAboutToAutoWrap = false;
mForeColor = TextStyle.COLOR_INDEX_FOREGROUND;
mBackColor = TextStyle.COLOR_INDEX_BACKGROUND;
mForeColor = mSavedStateMain.mSavedForeColor = mSavedStateAlt.mSavedForeColor = TextStyle.COLOR_INDEX_FOREGROUND;
mBackColor = mSavedStateMain.mSavedBackColor = mSavedStateAlt.mSavedBackColor = TextStyle.COLOR_INDEX_BACKGROUND;
setDefaultTabStops();
mUseLineDrawingG0 = mUseLineDrawingG1 = false;
@@ -2317,7 +2327,7 @@ public final class TerminalEmulator {
static final class SavedScreenState {
/** Saved state of the cursor position, Used to implement the save/restore cursor position escape sequences. */
int mSavedCursorRow, mSavedCursorCol;
int mSavedEffect;
int mSavedEffect, mSavedForeColor, mSavedBackColor;
int mSavedDecFlags;
boolean mUseLineDrawingG0, mUseLineDrawingG1, mUseLineDrawingUsesG0 = true;
}

View File

@@ -4,7 +4,7 @@ import java.util.Arrays;
/**
* A row in a terminal, composed of a fixed number of cells.
* <p/>
* <p>
* The text in the row is stored in a char[] array, {@link #mText}, for quick access during rendering.
*/
public final class TerminalRow {
@@ -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];
}

View File

@@ -19,13 +19,13 @@ import java.util.UUID;
/**
* A terminal session, consisting of a process coupled to a terminal interface.
* <p/>
* <p>
* The subprocess will be executed by the constructor, and when the size is made known by a call to
* {@link #updateSize(int, int)} terminal emulation will begin and threads will be spawned to handle the subprocess I/O.
* All terminal emulation and callback methods will be performed on the main thread.
* <p/>
* <p>
* The child process may be exited forcefully by using the {@link #finishIfRunning()} method.
* <p/>
* <p>
* NOTE: The terminal session may outlive the EmulatorView, so be careful with callbacks!
*/
public final class TerminalSession extends TerminalOutput {
@@ -123,12 +123,12 @@ public final class TerminalSession extends TerminalOutput {
String exitDescription = "\r\n[Process completed";
if (exitCode > 0) {
// Non-zero process exit.
exitDescription += " with code " + exitCode;
exitDescription += " (code " + exitCode + ")";
} else if (exitCode < 0) {
// Negated signal.
exitDescription += " with signal " + (-exitCode);
exitDescription += " (signal " + (-exitCode) + ")";
}
exitDescription += " - press Enter to close]";
exitDescription += " - press Enter]";
byte[] bytesToWrite = exitDescription.getBytes(StandardCharsets.UTF_8);
mEmulator.append(bytesToWrite, bytesToWrite.length);
@@ -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);

View File

@@ -0,0 +1,90 @@
package com.termux.terminal;
/**
* <p>
* 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>
* <p>
* The bit layout is:
* </p>
* - 16 flags (11 currently used).
* - 24 for foreground color (only 9 first bits if a color index).
* - 24 for background color (only 9 first bits if a color index).
*/
public final class TextStyle {
public final static int CHARACTER_ATTRIBUTE_BOLD = 1;
public final static int CHARACTER_ATTRIBUTE_ITALIC = 1 << 1;
public final static int CHARACTER_ATTRIBUTE_UNDERLINE = 1 << 2;
public final static int CHARACTER_ATTRIBUTE_BLINK = 1 << 3;
public final static int CHARACTER_ATTRIBUTE_INVERSE = 1 << 4;
public final static int CHARACTER_ATTRIBUTE_INVISIBLE = 1 << 5;
public final static int CHARACTER_ATTRIBUTE_STRIKETHROUGH = 1 << 6;
/**
* The selective erase control functions (DECSED and DECSEL) can only erase characters defined as erasable.
* <p>
* This bit is set if DECSCA (Select Character Protection Attribute) has been used to define the characters that
* come after it as erasable from the screen.
* </p>
*/
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;
public final static int COLOR_INDEX_CURSOR = 258;
/** The 256 standard color entries and the three special (foreground, background and cursor) ones. */
public final static int NUM_INDEXED_COLORS = 259;
/** Normal foreground and background colors and no effects. */
final static long NORMAL = encode(COLOR_INDEX_FOREGROUND, COLOR_INDEX_BACKGROUND, 0);
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(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(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(long style) {
return (int) (style & 0b11111111111);
}
}

View File

@@ -0,0 +1,458 @@
package com.termux.terminal;
/**
* 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 {
// 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
};
// 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) ..
};
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;
}
}
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. */
public static int width(char[] chars, int index) {
char c = chars[index];
return Character.isHighSurrogate(c) ? width(Character.toCodePoint(c, chars[index + 1])) : width(c);
}
}

View 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)

View File

@@ -29,4 +29,10 @@ public class ControlSequenceIntroducerTest extends TerminalTestCase {
withTerminalSized(13, 2).enterString("abcdefghijkl\b\b\b\b\b\033[20X").assertLinesAre("abcdefg ", " ");
}
/** CSI Pm m Set SGR parameter(s) from semicolon-separated list Pm. */
public void testCsiSGRParameters() {
// Set more parameters (19) than supported (16). Additional parameters should be silently consumed.
withTerminalSized(3, 2).enterString("\033[0;38;2;255;255;255;48;2;0;0;0;1;2;3;4;5;7;8;9mabc").assertLinesAre("abc", " ");
}
}

View File

@@ -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));
}
@@ -227,4 +227,44 @@ public class CursorAndScreenTest extends TerminalTestCase {
withTerminalSized(3, 3).enterString("\b\b\b\bhi").assertLinesAre("hi ", " ", " ");
}
public void testCursorSaveRestoreLocation() {
// DEC save/restore
withTerminalSized(4, 2).enterString("t\0337est\r\nme\0338ry ").assertLinesAre("try ", "me ");
// ANSI.SYS save/restore
withTerminalSized(4, 2).enterString("t\033[sest\r\nme\033[ury ").assertLinesAre("try ", "me ");
// Alternate screen enter/exit
withTerminalSized(4, 2).enterString("t\033[?1049h\033[Hest\r\nme").assertLinesAre("est ", "me ").enterString("\033[?1049lry").assertLinesAre("try ", " ");
}
public void testCursorSaveRestoreTextStyle() {
long s;
// DEC save/restore
withTerminalSized(4, 2).enterString("\033[31;42;4m..\0337\033[36;47;24m\0338..");
s = getStyleAt(0, 3);
Assert.assertEquals(1, TextStyle.decodeForeColor(s));
Assert.assertEquals(2, TextStyle.decodeBackColor(s));
Assert.assertEquals(TextStyle.CHARACTER_ATTRIBUTE_UNDERLINE, TextStyle.decodeEffect(s));
// ANSI.SYS save/restore
withTerminalSized(4, 2).enterString("\033[31;42;4m..\033[s\033[36;47;24m\033[u..");
s = getStyleAt(0, 3);
Assert.assertEquals(1, TextStyle.decodeForeColor(s));
Assert.assertEquals(2, TextStyle.decodeBackColor(s));
Assert.assertEquals(TextStyle.CHARACTER_ATTRIBUTE_UNDERLINE, TextStyle.decodeEffect(s));
// Alternate screen enter/exit
withTerminalSized(4, 2);
enterString("\033[31;42;4m..\033[?1049h\033[H\033[36;47;24m.");
s = getStyleAt(0, 0);
Assert.assertEquals(6, TextStyle.decodeForeColor(s));
Assert.assertEquals(7, TextStyle.decodeBackColor(s));
Assert.assertEquals(0, TextStyle.decodeEffect(s));
enterString("\033[?1049l..");
s = getStyleAt(0, 3);
Assert.assertEquals(1, TextStyle.decodeForeColor(s));
Assert.assertEquals(2, TextStyle.decodeBackColor(s));
Assert.assertEquals(TextStyle.CHARACTER_ATTRIBUTE_UNDERLINE, TextStyle.decodeEffect(s));
}
}

View File

@@ -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));
}
}

View File

@@ -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));
}

View File

@@ -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);

View File

@@ -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));
}
}

View File

@@ -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));
}

View File

@@ -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,

View File

@@ -15,6 +15,13 @@ public class WcWidthTest extends TestCase {
}
}
public void testSomeWidthOne() {
assertWidthIs(1, 'å');
assertWidthIs(1, 'ä');
assertWidthIs(1, 'ö');
assertWidthIs(1, 0x23F2);
}
public void testSomeWide() {
assertWidthIs(2, '');
assertWidthIs(2, '');
@@ -37,13 +44,22 @@ public class WcWidthTest extends TestCase {
}
public void testCombining() {
assertWidthIs(0, 0x0302);
assertWidthIs(0, 0x0308);
assertWidthIs(0, 0x2060);
assertWidthIs(0, 0x0302);
assertWidthIs(0, 0x0308);
assertWidthIs(0, 0xFE0F);
}
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 &#8288;).
// 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 +73,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).
}
}

View File

@@ -0,0 +1,46 @@
plugins {
id "com.jfrog.bintray" version "1.7"
id "com.github.dcendents.android-maven" version "1.5"
}
apply plugin: 'com.android.library'
ext {
bintrayName = 'terminal-view'
publishedGroupId = 'com.termux'
libraryName = 'TerminalView'
artifact = 'terminal-view'
libraryDescription = 'The terminal view used in Termux'
siteUrl = 'https://github.com/termux/termux'
gitUrl = 'https://github.com/termux/termux.git'
libraryVersion = '0.49'
}
android {
compileSdkVersion 25
buildToolsVersion "25.0.3"
dependencies {
compile 'com.android.support:support-annotations:25.3.1'
compile project(":terminal-emulator")
}
defaultConfig {
minSdkVersion 21
targetSdkVersion 25
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
testCompile 'junit:junit:4.12'
}
apply from: '../scripts/bintray-publish.gradle'

Some files were not shown because too many files have changed in this diff Show More