Compare commits
29 Commits
master
...
github-rel
| Author | SHA1 | Date |
|---|---|---|
|
|
43317b78c9 | |
|
|
2a008d836e | |
|
|
daa7ca4d43 | |
|
|
2c82a5581f | |
|
|
708281cea2 | |
|
|
eb0cb408a4 | |
|
|
d11c95b996 | |
|
|
9735ae284d | |
|
|
0813e46330 | |
|
|
6ece249c03 | |
|
|
fc8245bba3 | |
|
|
63833d9c2d | |
|
|
c9e2a75e82 | |
|
|
9433f10757 | |
|
|
fbf55fd40c | |
|
|
160ab68e5b | |
|
|
903f2496cb | |
|
|
2dc7381b89 | |
|
|
e55639e41d | |
|
|
087da0b576 | |
|
|
d7f22982a1 | |
|
|
f222315b0f | |
|
|
e11bcfc9a1 | |
|
|
e3a50cbf32 | |
|
|
af5fef4c4a | |
|
|
03e31d190d | |
|
|
d24a04a10d | |
|
|
aee0da49a0 | |
|
|
87c8f3d35a |
|
|
@ -58,19 +58,18 @@ jobs:
|
|||
"${APK_BASENAME_PREFIX}_armeabi-v7a.apk" \
|
||||
"${APK_BASENAME_PREFIX}_x86_64.apk" \
|
||||
"${APK_BASENAME_PREFIX}_x86.apk" \
|
||||
> sha256sums); then
|
||||
> "${APK_BASENAME_PREFIX}_sha256sums"); then
|
||||
exit_on_error "Generate sha25sums failed for '$RELEASE_VERSION_NAME' release."
|
||||
fi
|
||||
|
||||
echo "Attaching APKs to github release"
|
||||
if ! hub release edit \
|
||||
-m "" \
|
||||
-a "$APK_DIR_PATH/${APK_BASENAME_PREFIX}_universal.apk" \
|
||||
-a "$APK_DIR_PATH/${APK_BASENAME_PREFIX}_arm64-v8a.apk" \
|
||||
-a "$APK_DIR_PATH/${APK_BASENAME_PREFIX}_armeabi-v7a.apk" \
|
||||
-a "$APK_DIR_PATH/${APK_BASENAME_PREFIX}_x86_64.apk" \
|
||||
-a "$APK_DIR_PATH/${APK_BASENAME_PREFIX}_x86.apk" \
|
||||
-a "$APK_DIR_PATH/sha256sums" \
|
||||
"$RELEASE_VERSION_NAME"; then
|
||||
if ! gh release upload "$RELEASE_VERSION_NAME" \
|
||||
"$APK_DIR_PATH/${APK_BASENAME_PREFIX}_universal.apk" \
|
||||
"$APK_DIR_PATH/${APK_BASENAME_PREFIX}_arm64-v8a.apk" \
|
||||
"$APK_DIR_PATH/${APK_BASENAME_PREFIX}_armeabi-v7a.apk" \
|
||||
"$APK_DIR_PATH/${APK_BASENAME_PREFIX}_x86_64.apk" \
|
||||
"$APK_DIR_PATH/${APK_BASENAME_PREFIX}_x86.apk" \
|
||||
"$APK_DIR_PATH/${APK_BASENAME_PREFIX}_sha256sums" \
|
||||
; then
|
||||
exit_on_error "Attach APKs to release failed for '$RELEASE_VERSION_NAME' release."
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ on:
|
|||
push:
|
||||
branches:
|
||||
- master
|
||||
- 'github-releases/**'
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
|
@ -13,7 +14,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Clone repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Build APKs
|
||||
shell: bash {0}
|
||||
|
|
@ -65,7 +66,7 @@ jobs:
|
|||
fi
|
||||
|
||||
- name: Attach universal APK file
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ env.APK_BASENAME_PREFIX }}_universal
|
||||
path: |
|
||||
|
|
@ -73,7 +74,7 @@ jobs:
|
|||
${{ env.APK_DIR_PATH }}/output-metadata.json
|
||||
|
||||
- name: Attach arm64-v8a APK file
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ env.APK_BASENAME_PREFIX }}_arm64-v8a
|
||||
path: |
|
||||
|
|
@ -81,7 +82,7 @@ jobs:
|
|||
${{ env.APK_DIR_PATH }}/output-metadata.json
|
||||
|
||||
- name: Attach armeabi-v7a APK file
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ env.APK_BASENAME_PREFIX }}_armeabi-v7a
|
||||
path: |
|
||||
|
|
@ -89,7 +90,7 @@ jobs:
|
|||
${{ env.APK_DIR_PATH }}/output-metadata.json
|
||||
|
||||
- name: Attach x86_64 APK file
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ env.APK_BASENAME_PREFIX }}_x86_64
|
||||
path: |
|
||||
|
|
@ -97,7 +98,7 @@ jobs:
|
|||
${{ env.APK_DIR_PATH }}/output-metadata.json
|
||||
|
||||
- name: Attach x86 APK file
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ env.APK_BASENAME_PREFIX }}_x86
|
||||
path: |
|
||||
|
|
@ -105,7 +106,7 @@ jobs:
|
|||
${{ env.APK_DIR_PATH }}/output-metadata.json
|
||||
|
||||
- name: Attach sha256sums file
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: sha256sums
|
||||
path: |
|
||||
|
|
|
|||
|
|
@ -15,5 +15,5 @@ jobs:
|
|||
name: "Validation"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- uses: gradle/wrapper-validation-action@v1
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Clone repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
- name: Execute tests
|
||||
run: |
|
||||
./gradlew test
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@ The main ones are the following.
|
|||
- [Termux Matrix Channel](https://matrix.to/#termux_termux:gitter.im)
|
||||
- [Termux Dev Matrix Channel](https://matrix.to/#termux_dev:gitter.im)
|
||||
- [Termux Twitter](https://twitter.com/termux/)
|
||||
- [Termux Reports Email](mailto:termuxreports@groups.io)
|
||||
- [Termux Reports Email](mailto:support@termux.dev)
|
||||
|
||||
### Wikis
|
||||
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@ android {
|
|||
applicationId "com.termux"
|
||||
minSdkVersion project.properties.minSdkVersion.toInteger()
|
||||
targetSdkVersion project.properties.targetSdkVersion.toInteger()
|
||||
versionCode 118
|
||||
versionName "0.118.0"
|
||||
versionCode 1000
|
||||
versionName "0.118.1"
|
||||
|
||||
if (appVersionName) versionName = appVersionName
|
||||
validateVersionName(versionName)
|
||||
|
|
@ -195,11 +195,11 @@ clean {
|
|||
|
||||
task downloadBootstraps() {
|
||||
doLast {
|
||||
def version = "2022.01.07-r1"
|
||||
downloadBootstrap("aarch64", "0fe6d0159d12fcb8baf7750ce9072b9b36f742662b02ad4da145ab85873614cd", version)
|
||||
downloadBootstrap("arm", "0a6014e2ec3b7079524fee3caabd02be05bcb4add3c6cd9e5ad98408b428c717", version)
|
||||
downloadBootstrap("i686", "c55369a9af1316dc3d99457aa23cce64b29c1e60e375159352c0d4b9cfed4ac6", version)
|
||||
downloadBootstrap("x86_64", "e93a7c66d15edb7d1b5ceca906255c85ead35a8b6a52f93524e117cfb07e6778", version)
|
||||
def version = "2024.06.17-r1+apt-android-7"
|
||||
downloadBootstrap("aarch64", "91a90661597fe14bb3c3563f5f65b243c0baaec42f2bc3d2243ff459e3942fb6", version)
|
||||
downloadBootstrap("arm", "d54b5eb2a305d72f267f9704deaca721b2bebbd3d4cca134aec31da719707997", version)
|
||||
downloadBootstrap("i686", "06a51ac1c679d68d52045509f1a705622c8f41748ef753660e31e3b6a846eba2", version)
|
||||
downloadBootstrap("x86_64", "4c8e43474c8d9543e01d4cbf3c4d7f59bbe4d696c38f6dece2b6ab3ba8881f2e", version)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" tools:ignore="ProtectedPermissions" />
|
||||
<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
|
||||
|
||||
<application
|
||||
android:name=".app.TermuxApplication"
|
||||
|
|
@ -180,13 +181,24 @@
|
|||
<meta-data
|
||||
android:name="android.max_aspect"
|
||||
android:value="10.0" />
|
||||
|
||||
|
||||
<!-- https://developer.samsung.com/samsung-dex/modify-optimizing.html -->
|
||||
|
||||
<!-- Version < 3.0. DeX Mode and Screen Mirroring support -->
|
||||
<meta-data
|
||||
android:name="com.sec.android.support.multiwindow"
|
||||
android:name="com.samsung.android.keepalive.density"
|
||||
android:value="true" />
|
||||
|
||||
<!-- Version >= 3.0. DeX Dual Mode support -->
|
||||
<meta-data
|
||||
android:name="com.samsung.android.multidisplay.keep_process_alive"
|
||||
android:value="true" />
|
||||
|
||||
<meta-data
|
||||
android:name="com.sec.android.support.multiwindow"
|
||||
android:value="true" />
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
|
|
|||
|
|
@ -16,8 +16,11 @@ import com.termux.shared.file.TermuxFileUtils;
|
|||
import com.termux.shared.interact.MessageDialogUtils;
|
||||
import com.termux.shared.logger.Logger;
|
||||
import com.termux.shared.markdown.MarkdownUtils;
|
||||
import com.termux.shared.models.ExecutionCommand;
|
||||
import com.termux.shared.models.errors.Error;
|
||||
import com.termux.shared.packages.PackageUtils;
|
||||
import com.termux.shared.shell.TermuxShellEnvironmentClient;
|
||||
import com.termux.shared.shell.TermuxTask;
|
||||
import com.termux.shared.termux.TermuxConstants;
|
||||
import com.termux.shared.termux.TermuxUtils;
|
||||
|
||||
|
|
@ -187,7 +190,8 @@ final class TermuxInstaller {
|
|||
outStream.write(buffer, 0, readBytes);
|
||||
}
|
||||
if (zipEntryName.startsWith("bin/") || zipEntryName.startsWith("libexec") ||
|
||||
zipEntryName.startsWith("lib/apt/apt-helper") || zipEntryName.startsWith("lib/apt/methods")) {
|
||||
zipEntryName.startsWith("lib/apt/apt-helper") || zipEntryName.startsWith("lib/apt/methods") ||
|
||||
zipEntryName.equals("etc/termux/bootstrap/termux-bootstrap-second-stage.sh")) {
|
||||
//noinspection OctalInteger
|
||||
Os.chmod(targetFile.getAbsolutePath(), 0700);
|
||||
}
|
||||
|
|
@ -208,6 +212,28 @@ final class TermuxInstaller {
|
|||
throw new RuntimeException("Moving termux prefix staging to prefix directory failed");
|
||||
}
|
||||
|
||||
// Run Termux bootstrap second stage
|
||||
Logger.logInfo(LOG_TAG, "Running Termux bootstrap second stage.");
|
||||
String termuxBootstrapSecondStageFile = TERMUX_PREFIX_DIR_PATH + "/etc/termux/bootstrap/termux-bootstrap-second-stage.sh";
|
||||
if (FileUtils.fileExists(termuxBootstrapSecondStageFile, false)) {
|
||||
ExecutionCommand executionCommand = new ExecutionCommand(-1,
|
||||
termuxBootstrapSecondStageFile, null, null,
|
||||
null, true, false);
|
||||
executionCommand.commandLabel = "Termux Bootstrap Second Stage Command";
|
||||
executionCommand.backgroundCustomLogLevel = Logger.LOG_LEVEL_NORMAL;
|
||||
TermuxTask termuxTask = TermuxTask.execute(activity, executionCommand, null, new TermuxShellEnvironmentClient(), true);
|
||||
boolean stderrSet = !executionCommand.resultData.stderr.toString().isEmpty();
|
||||
if (termuxTask == null || !executionCommand.isSuccessful() || executionCommand.resultData.exitCode != 0 || stderrSet) {
|
||||
// Delete prefix directory as otherwise when app is restarted, the broken prefix directory would be used and logged into
|
||||
error = FileUtils.deleteFile("termux prefix directory", TERMUX_PREFIX_DIR_PATH, true);
|
||||
if (error != null)
|
||||
Logger.logErrorExtended(LOG_TAG, error.toString());
|
||||
|
||||
showBootstrapErrorDialog(activity, whenDone, MarkdownUtils.getMarkdownCodeForString(executionCommand.toString(), true));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Logger.logInfo(LOG_TAG, "Bootstrap packages installed successfully.");
|
||||
activity.runOnUiThread(whenDone);
|
||||
|
||||
|
|
|
|||
|
|
@ -213,7 +213,7 @@ public class TermuxTerminalViewClient extends TermuxTerminalViewClientBase {
|
|||
|
||||
@Override
|
||||
public boolean isTerminalViewSelected() {
|
||||
return mActivity.getTerminalToolbarViewPager() == null || mActivity.isTerminalViewSelected();
|
||||
return mActivity.getTerminalToolbarViewPager() == null || mActivity.isTerminalViewSelected() || mActivity.getTerminalView().hasFocus();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -541,7 +541,8 @@ public class TermuxTerminalViewClient extends TermuxTerminalViewClientBase {
|
|||
// disabled or hidden at startup, otherwise if hardware keyboard is attached and user
|
||||
// starts typing on hardware keyboard without tapping on the terminal first, then a colour
|
||||
// tint will be added to the terminal as highlight for the focussed view. Test with a light
|
||||
// theme.
|
||||
// theme. For android 8.+, the "defaultFocusHighlightEnabled" attribute is also set to false
|
||||
// in TerminalView layout to fix the issue.
|
||||
|
||||
// If soft keyboard is disabled by user for Termux (check function docs for Termux behaviour info)
|
||||
if (KeyboardUtils.shouldSoftKeyboardBeDisabled(mActivity,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
<com.termux.app.terminal.TermuxActivityRootView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/activity_termux_root_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
|
|
@ -25,11 +27,13 @@
|
|||
android:id="@+id/terminal_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:defaultFocusHighlightEnabled="false"
|
||||
android:focusableInTouchMode="true"
|
||||
android:scrollbarThumbVertical="@drawable/terminal_scroll_shape"
|
||||
android:scrollbars="vertical"
|
||||
android:importantForAutofill="no"
|
||||
android:autofillHints="password" />
|
||||
android:autofillHints="password"
|
||||
tools:ignore="UnusedAttribute" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/left_drawer"
|
||||
|
|
@ -96,7 +100,7 @@
|
|||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="37.5dp"
|
||||
android:background="@android:drawable/screen_background_dark_transparent"
|
||||
android:background="@color/black"
|
||||
android:layout_alignParentBottom="true" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,80 @@
|
|||
package com.termux.terminal;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
|
||||
public class Logger {
|
||||
|
||||
public static void logError(TerminalSessionClient client, String logTag, String message) {
|
||||
if (client != null)
|
||||
client.logError(logTag, message);
|
||||
else
|
||||
Log.e(logTag, message);
|
||||
}
|
||||
|
||||
public static void logWarn(TerminalSessionClient client, String logTag, String message) {
|
||||
if (client != null)
|
||||
client.logWarn(logTag, message);
|
||||
else
|
||||
Log.w(logTag, message);
|
||||
}
|
||||
|
||||
public static void logInfo(TerminalSessionClient client, String logTag, String message) {
|
||||
if (client != null)
|
||||
client.logInfo(logTag, message);
|
||||
else
|
||||
Log.i(logTag, message);
|
||||
}
|
||||
|
||||
public static void logDebug(TerminalSessionClient client, String logTag, String message) {
|
||||
if (client != null)
|
||||
client.logDebug(logTag, message);
|
||||
else
|
||||
Log.d(logTag, message);
|
||||
}
|
||||
|
||||
public static void logVerbose(TerminalSessionClient client, String logTag, String message) {
|
||||
if (client != null)
|
||||
client.logVerbose(logTag, message);
|
||||
else
|
||||
Log.v(logTag, message);
|
||||
}
|
||||
|
||||
public static void logStackTraceWithMessage(TerminalSessionClient client, String tag, String message, Throwable throwable) {
|
||||
logError(client, tag, getMessageAndStackTraceString(message, throwable));
|
||||
}
|
||||
|
||||
public static String getMessageAndStackTraceString(String message, Throwable throwable) {
|
||||
if (message == null && throwable == null)
|
||||
return null;
|
||||
else if (message != null && throwable != null)
|
||||
return message + ":\n" + getStackTraceString(throwable);
|
||||
else if (throwable == null)
|
||||
return message;
|
||||
else
|
||||
return getStackTraceString(throwable);
|
||||
}
|
||||
|
||||
public static String getStackTraceString(Throwable throwable) {
|
||||
if (throwable == null) return null;
|
||||
|
||||
String stackTraceString = null;
|
||||
|
||||
try {
|
||||
StringWriter errors = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(errors);
|
||||
throwable.printStackTrace(pw);
|
||||
pw.close();
|
||||
stackTraceString = errors.toString();
|
||||
errors.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return stackTraceString;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -54,7 +54,7 @@ public final class TerminalBuffer {
|
|||
}
|
||||
|
||||
public String getSelectedText(int selX1, int selY1, int selX2, int selY2, boolean joinBackLines) {
|
||||
return getSelectedText(selX1, selY1, selX2, selY2, true, false);
|
||||
return getSelectedText(selX1, selY1, selX2, selY2, joinBackLines, false);
|
||||
}
|
||||
|
||||
public String getSelectedText(int selX1, int selY1, int selX2, int selY2, boolean joinBackLines, boolean joinFullLines) {
|
||||
|
|
@ -93,8 +93,11 @@ public final class TerminalBuffer {
|
|||
if (c != ' ') lastPrintingCharIndex = i;
|
||||
}
|
||||
}
|
||||
if (lastPrintingCharIndex != -1)
|
||||
builder.append(line, x1Index, lastPrintingCharIndex - x1Index + 1);
|
||||
|
||||
int len = lastPrintingCharIndex - x1Index + 1;
|
||||
if (lastPrintingCharIndex != -1 && len > 0)
|
||||
builder.append(line, x1Index, len);
|
||||
|
||||
boolean lineFillsWidth = lastPrintingCharIndex == x2Index - 1;
|
||||
if ((!joinBackLines || !rowLineWrap) && (!joinFullLines || !lineFillsWidth)
|
||||
&& row < selY2 && row < mScreenRows - 1) builder.append('\n');
|
||||
|
|
@ -446,8 +449,8 @@ public final class TerminalBuffer {
|
|||
}
|
||||
|
||||
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);
|
||||
if (row < 0 || row >= mScreenRows || column < 0 || column >= mColumns)
|
||||
throw new IllegalArgumentException("TerminalBuffer.setChar(): row=" + row + ", column=" + column + ", mScreenRows=" + mScreenRows + ", mColumns=" + mColumns);
|
||||
row = externalToInternalRow(row);
|
||||
allocateFullLineIfNecessary(row).setChar(column, codePoint, style);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ public final class TerminalColorScheme {
|
|||
|
||||
public void updateWith(Properties props) {
|
||||
reset();
|
||||
boolean cursorPropExists = false;
|
||||
for (Map.Entry<Object, Object> entries : props.entrySet()) {
|
||||
String key = (String) entries.getKey();
|
||||
String value = (String) entries.getValue();
|
||||
|
|
@ -82,6 +83,7 @@ public final class TerminalColorScheme {
|
|||
colorIndex = TextStyle.COLOR_INDEX_BACKGROUND;
|
||||
} else if (key.equals("cursor")) {
|
||||
colorIndex = TextStyle.COLOR_INDEX_CURSOR;
|
||||
cursorPropExists = true;
|
||||
} else if (key.startsWith("color")) {
|
||||
try {
|
||||
colorIndex = Integer.parseInt(key.substring(5));
|
||||
|
|
@ -98,6 +100,27 @@ public final class TerminalColorScheme {
|
|||
|
||||
mDefaultColors[colorIndex] = colorValue;
|
||||
}
|
||||
|
||||
if (!cursorPropExists)
|
||||
setCursorColorForBackground();
|
||||
}
|
||||
|
||||
/**
|
||||
* If the "cursor" color is not set by user, we need to decide on the appropriate color that will
|
||||
* be visible on the current terminal background. White will not be visible on light backgrounds
|
||||
* and black won't be visible on dark backgrounds. So we find the perceived brightness of the
|
||||
* background color and if its below the threshold (too dark), we use white cursor and if its
|
||||
* above (too bright), we use black cursor.
|
||||
*/
|
||||
public void setCursorColorForBackground() {
|
||||
int backgroundColor = mDefaultColors[TextStyle.COLOR_INDEX_BACKGROUND];
|
||||
int brightness = TerminalColors.getPerceivedBrightnessOfColor(backgroundColor);
|
||||
if (brightness > 0) {
|
||||
if (brightness < 130)
|
||||
mDefaultColors[TextStyle.COLOR_INDEX_CURSOR] = 0xffffffff;
|
||||
else
|
||||
mDefaultColors[TextStyle.COLOR_INDEX_CURSOR] = 0xff000000;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package com.termux.terminal;
|
||||
|
||||
import android.graphics.Color;
|
||||
|
||||
/** Current terminal colors (if different from default). */
|
||||
public final class TerminalColors {
|
||||
|
||||
|
|
@ -73,4 +75,22 @@ public final class TerminalColors {
|
|||
if (c != 0) mCurrentColors[intoIndex] = c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the perceived brightness of the color based on its RGB components.
|
||||
*
|
||||
* https://www.nbdtech.com/Blog/archive/2008/04/27/Calculating-the-Perceived-Brightness-of-a-Color.aspx
|
||||
* http://alienryderflex.com/hsp.html
|
||||
*
|
||||
* @param color The color code int.
|
||||
* @return Returns value between 0-255.
|
||||
*/
|
||||
public static int getPerceivedBrightnessOfColor(int color) {
|
||||
return (int)
|
||||
Math.floor(Math.sqrt(
|
||||
Math.pow(Color.red(color), 2) * 0.241 +
|
||||
Math.pow(Color.green(color), 2) * 0.691 +
|
||||
Math.pow(Color.blue(color), 2) * 0.068
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -126,6 +126,10 @@ public final class TerminalEmulator {
|
|||
private String mTitle;
|
||||
private final Stack<String> mTitleStack = new Stack<>();
|
||||
|
||||
/** If processing first character of first parameter of {@link #ESC_CSI}. */
|
||||
private boolean mIsCSIStart;
|
||||
/** The last character processed of a parameter of {@link #ESC_CSI}. */
|
||||
private Integer mLastCSIArg;
|
||||
|
||||
/** The cursor position. Between (0,0) and (mRows-1, mColumns-1). */
|
||||
private int mCursorRow, mCursorCol;
|
||||
|
|
@ -796,7 +800,6 @@ public final class TerminalEmulator {
|
|||
int columnsToDelete = Math.min(getArg0(1), columnsAfterCursor);
|
||||
int columnsToMove = columnsAfterCursor - columnsToDelete;
|
||||
mScreen.blockCopy(mCursorCol + columnsToDelete, 0, columnsToMove, mRows, mCursorCol, 0);
|
||||
blockClear(mCursorRow + columnsToMove, 0, columnsToDelete, mRows);
|
||||
} else {
|
||||
unknownSequence(b);
|
||||
}
|
||||
|
|
@ -825,7 +828,7 @@ public final class TerminalEmulator {
|
|||
if (internalBit != -1) {
|
||||
value = isDecsetInternalBitSet(internalBit) ? 1 : 2; // 1=set, 2=reset.
|
||||
} else {
|
||||
mClient.logError(LOG_TAG, "Got DECRQM for unrecognized private DEC mode=" + mode);
|
||||
Logger.logError(mClient, LOG_TAG, "Got DECRQM for unrecognized private DEC mode=" + mode);
|
||||
value = 0; // 0=not recognized, 3=permanently set, 4=permanently reset
|
||||
}
|
||||
}
|
||||
|
|
@ -936,10 +939,17 @@ public final class TerminalEmulator {
|
|||
for (String part : dcs.substring(2).split(";")) {
|
||||
if (part.length() % 2 == 0) {
|
||||
StringBuilder transBuffer = new StringBuilder();
|
||||
char c;
|
||||
for (int i = 0; i < part.length(); i += 2) {
|
||||
char c = (char) Long.decode("0x" + part.charAt(i) + "" + part.charAt(i + 1)).longValue();
|
||||
try {
|
||||
c = (char) Long.decode("0x" + part.charAt(i) + "" + part.charAt(i + 1)).longValue();
|
||||
} catch (NumberFormatException e) {
|
||||
Logger.logStackTraceWithMessage(mClient, LOG_TAG, "Invalid device termcap/terminfo encoded name \"" + part + "\"", e);
|
||||
continue;
|
||||
}
|
||||
transBuffer.append(c);
|
||||
}
|
||||
|
||||
String trans = transBuffer.toString();
|
||||
String responseValue;
|
||||
switch (trans) {
|
||||
|
|
@ -962,7 +972,7 @@ public final class TerminalEmulator {
|
|||
case "&8": // Undo key - ignore.
|
||||
break;
|
||||
default:
|
||||
mClient.logWarn(LOG_TAG, "Unhandled termcap/terminfo name: '" + trans + "'");
|
||||
Logger.logWarn(mClient, LOG_TAG, "Unhandled termcap/terminfo name: '" + trans + "'");
|
||||
}
|
||||
// Respond with invalid request:
|
||||
mSession.write("\033P0+r" + part + "\033\\");
|
||||
|
|
@ -974,12 +984,12 @@ public final class TerminalEmulator {
|
|||
mSession.write("\033P1+r" + part + "=" + hexEncoded + "\033\\");
|
||||
}
|
||||
} else {
|
||||
mClient.logError(LOG_TAG, "Invalid device termcap/terminfo name of odd length: " + part);
|
||||
Logger.logError(mClient, LOG_TAG, "Invalid device termcap/terminfo name of odd length: " + part);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (LOG_ESCAPE_SEQUENCES)
|
||||
mClient.logError(LOG_TAG, "Unrecognized device control string: " + dcs);
|
||||
Logger.logError(mClient, LOG_TAG, "Unrecognized device control string: " + dcs);
|
||||
}
|
||||
finishSequence();
|
||||
}
|
||||
|
|
@ -1069,7 +1079,7 @@ public final class TerminalEmulator {
|
|||
int externalBit = mArgs[i];
|
||||
int internalBit = mapDecSetBitToInternalBit(externalBit);
|
||||
if (internalBit == -1) {
|
||||
mClient.logWarn(LOG_TAG, "Ignoring request to save/recall decset bit=" + externalBit);
|
||||
Logger.logWarn(mClient, LOG_TAG, "Ignoring request to save/recall decset bit=" + externalBit);
|
||||
} else {
|
||||
if (b == 's') {
|
||||
mSavedDecSetFlags |= internalBit;
|
||||
|
|
@ -1259,7 +1269,7 @@ public final class TerminalEmulator {
|
|||
// (1) enables this feature for keys except for those with well-known behavior, e.g., Tab, Backarrow and
|
||||
// some special control character cases, e.g., Control-Space to make a NUL.
|
||||
// (2) enables this feature for keys including the exceptions listed.
|
||||
mClient.logError(LOG_TAG, "(ignored) CSI > MODIFY RESOURCE: " + getArg0(-1) + " to " + getArg1(-1));
|
||||
Logger.logError(mClient, LOG_TAG, "(ignored) CSI > MODIFY RESOURCE: " + getArg0(-1) + " to " + getArg1(-1));
|
||||
break;
|
||||
default:
|
||||
parseArg(b);
|
||||
|
|
@ -1380,6 +1390,8 @@ public final class TerminalEmulator {
|
|||
break;
|
||||
case '[':
|
||||
continueSequence(ESC_CSI);
|
||||
mIsCSIStart = true;
|
||||
mLastCSIArg = null;
|
||||
break;
|
||||
case '=': // DECKPAM
|
||||
setDecsetinternalBit(DECSET_BIT_APPLICATION_KEYPAD, true);
|
||||
|
|
@ -1806,7 +1818,7 @@ public final class TerminalEmulator {
|
|||
int firstArg = mArgs[i + 1];
|
||||
if (firstArg == 2) {
|
||||
if (i + 4 > mArgIndex) {
|
||||
mClient.logWarn(LOG_TAG, "Too few CSI" + code + ";2 RGB arguments");
|
||||
Logger.logWarn(mClient, 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) {
|
||||
|
|
@ -1831,7 +1843,7 @@ public final class TerminalEmulator {
|
|||
mBackColor = color;
|
||||
}
|
||||
} else {
|
||||
if (LOG_ESCAPE_SEQUENCES) mClient.logWarn(LOG_TAG, "Invalid color index: " + color);
|
||||
if (LOG_ESCAPE_SEQUENCES) Logger.logWarn(mClient, LOG_TAG, "Invalid color index: " + color);
|
||||
}
|
||||
} else {
|
||||
finishSequenceAndLogError("Invalid ISO-8613-3 SGR first argument: " + firstArg);
|
||||
|
|
@ -1848,7 +1860,7 @@ public final class TerminalEmulator {
|
|||
mBackColor = code - 100 + 8;
|
||||
} else {
|
||||
if (LOG_ESCAPE_SEQUENCES)
|
||||
mClient.logWarn(LOG_TAG, String.format("SGR unknown code %d", code));
|
||||
Logger.logWarn(mClient, LOG_TAG, String.format("SGR unknown code %d", code));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1982,7 +1994,7 @@ public final class TerminalEmulator {
|
|||
String clipboardText = new String(Base64.decode(textParameter.substring(startIndex), 0), StandardCharsets.UTF_8);
|
||||
mSession.onCopyTextToClipboard(clipboardText);
|
||||
} catch (Exception e) {
|
||||
mClient.logError(LOG_TAG, "OSC Manipulate selection, invalid string '" + textParameter + "");
|
||||
Logger.logError(mClient, LOG_TAG, "OSC Manipulate selection, invalid string '" + textParameter + "");
|
||||
}
|
||||
break;
|
||||
case 104:
|
||||
|
|
@ -2087,28 +2099,57 @@ public final class TerminalEmulator {
|
|||
}
|
||||
}
|
||||
|
||||
/** Process the next ASCII character of a parameter. */
|
||||
private void parseArg(int b) {
|
||||
if (b >= '0' && b <= '9') {
|
||||
if (mArgIndex < mArgs.length) {
|
||||
int oldValue = mArgs[mArgIndex];
|
||||
int thisDigit = b - '0';
|
||||
int value;
|
||||
if (oldValue >= 0) {
|
||||
value = oldValue * 10 + thisDigit;
|
||||
} else {
|
||||
value = thisDigit;
|
||||
/**
|
||||
* Process the next ASCII character of a parameter.
|
||||
*
|
||||
* Parameter characters modify the action or interpretation of the sequence. You can use up to
|
||||
* 16 parameters per sequence. You must use the ; character to separate parameters.
|
||||
* All parameters are unsigned, positive decimal integers, with the most significant
|
||||
* digit sent first. Any parameter greater than 9999 (decimal) is set to 9999
|
||||
* (decimal). If you do not specify a value, a 0 value is assumed. A 0 value
|
||||
* or omitted parameter indicates a default value for the sequence. For most
|
||||
* sequences, the default value is 1.
|
||||
*
|
||||
* https://vt100.net/docs/vt510-rm/chapter4.html#S4.3.3
|
||||
* */
|
||||
private void parseArg(int inputByte) {
|
||||
int[] bytes = new int[]{inputByte};
|
||||
// Only doing this for ESC_CSI and not for other ESC_CSI_* since they seem to be using their
|
||||
// own defaults with getArg*() calls, but there may be missed cases
|
||||
if (mEscapeState == ESC_CSI) {
|
||||
if ((mIsCSIStart && inputByte == ';') || // If sequence starts with a ; character, like \033[;m
|
||||
(!mIsCSIStart && mLastCSIArg != null && mLastCSIArg == ';' && inputByte == ';')) { // If sequence contains sequential ; characters, like \033[;;m
|
||||
bytes = new int[]{'0', ';'}; // Assume 0 was passed
|
||||
}
|
||||
}
|
||||
|
||||
mIsCSIStart = false;
|
||||
|
||||
for (int b : bytes) {
|
||||
if (b >= '0' && b <= '9') {
|
||||
if (mArgIndex < mArgs.length) {
|
||||
int oldValue = mArgs[mArgIndex];
|
||||
int thisDigit = b - '0';
|
||||
int value;
|
||||
if (oldValue >= 0) {
|
||||
value = oldValue * 10 + thisDigit;
|
||||
} else {
|
||||
value = thisDigit;
|
||||
}
|
||||
if (value > 9999)
|
||||
value = 9999;
|
||||
mArgs[mArgIndex] = value;
|
||||
}
|
||||
mArgs[mArgIndex] = value;
|
||||
continueSequence(mEscapeState);
|
||||
} else if (b == ';') {
|
||||
if (mArgIndex < mArgs.length) {
|
||||
mArgIndex++;
|
||||
}
|
||||
continueSequence(mEscapeState);
|
||||
} else {
|
||||
unknownSequence(b);
|
||||
}
|
||||
continueSequence(mEscapeState);
|
||||
} else if (b == ';') {
|
||||
if (mArgIndex < mArgs.length) {
|
||||
mArgIndex++;
|
||||
}
|
||||
continueSequence(mEscapeState);
|
||||
} else {
|
||||
unknownSequence(b);
|
||||
mLastCSIArg = b;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2178,7 +2219,7 @@ public final class TerminalEmulator {
|
|||
}
|
||||
|
||||
private void finishSequenceAndLogError(String error) {
|
||||
if (LOG_ESCAPE_SEQUENCES) mClient.logWarn(LOG_TAG, error);
|
||||
if (LOG_ESCAPE_SEQUENCES) Logger.logWarn(mClient, LOG_TAG, error);
|
||||
finishSequence();
|
||||
}
|
||||
|
||||
|
|
@ -2326,7 +2367,14 @@ public final class TerminalEmulator {
|
|||
}
|
||||
|
||||
int offsetDueToCombiningChar = ((displayWidth <= 0 && mCursorCol > 0 && !mAboutToAutoWrap) ? 1 : 0);
|
||||
mScreen.setChar(mCursorCol - offsetDueToCombiningChar, mCursorRow, codePoint, getStyle());
|
||||
int column = mCursorCol - offsetDueToCombiningChar;
|
||||
|
||||
// Fix TerminalRow.setChar() ArrayIndexOutOfBoundsException index=-1 exception reported
|
||||
// The offsetDueToCombiningChar would never be 1 if mCursorCol was 0 to get column/index=-1,
|
||||
// so was mCursorCol changed after the offsetDueToCombiningChar conditional by another thread?
|
||||
// TODO: Check if there are thread synchronization issues with mCursorCol and mCursorRow, possibly causing others bugs too.
|
||||
if (column < 0) column = 0;
|
||||
mScreen.setChar(column, mCursorRow, codePoint, getStyle());
|
||||
|
||||
if (autoWrap && displayWidth > 0)
|
||||
mAboutToAutoWrap = (mCursorCol == mRightMargin - displayWidth);
|
||||
|
|
|
|||
|
|
@ -11,11 +11,37 @@ public final class TerminalRow {
|
|||
|
||||
private static final float SPARE_CAPACITY_FACTOR = 1.5f;
|
||||
|
||||
/**
|
||||
* Max combining characters that can exist in a column, that are separate from the base character
|
||||
* itself. Any additional combining characters will be ignored and not added to the column.
|
||||
*
|
||||
* There does not seem to be limit in unicode standard for max number of combination characters
|
||||
* that can be combined but such characters are primarily under 10.
|
||||
*
|
||||
* "Section 3.6 Combination" of unicode standard contains combining characters info.
|
||||
* - https://www.unicode.org/versions/Unicode15.0.0/ch03.pdf
|
||||
* - https://en.wikipedia.org/wiki/Combining_character#Unicode_ranges
|
||||
* - https://stackoverflow.com/questions/71237212/what-is-the-maximum-number-of-unicode-combined-characters-that-may-be-needed-to
|
||||
*
|
||||
* UAX15-D3 Stream-Safe Text Format limits to max 30 combining characters.
|
||||
* > The value of 30 is chosen to be significantly beyond what is required for any linguistic or technical usage.
|
||||
* > While it would have been feasible to chose a smaller number, this value provides a very wide margin,
|
||||
* > yet is well within the buffer size limits of practical implementations.
|
||||
* - https://unicode.org/reports/tr15/#Stream_Safe_Text_Format
|
||||
* - https://stackoverflow.com/a/11983435/14686958
|
||||
*
|
||||
* We choose the value 15 because it should be enough for terminal based applications and keep
|
||||
* the memory usage low for a terminal row, won't affect performance or cause terminal to
|
||||
* lag or hang, and will keep malicious applications from causing harm. The value can be
|
||||
* increased if ever needed for legitimate applications.
|
||||
*/
|
||||
private static final int MAX_COMBINING_CHARACTERS_PER_COLUMN = 15;
|
||||
|
||||
/** The number of columns in this terminal row. */
|
||||
private final int mColumns;
|
||||
/** The text filling this terminal row. */
|
||||
public char[] mText;
|
||||
/** The number of java char:s used in {@link #mText}. */
|
||||
/** The number of java chars used in {@link #mText}. */
|
||||
private short mSpaceUsed;
|
||||
/** If this row has been line wrapped due to text output at the end of line. */
|
||||
boolean mLineWrap;
|
||||
|
|
@ -124,6 +150,9 @@ public final class TerminalRow {
|
|||
|
||||
// https://github.com/steven676/Android-Terminal-Emulator/commit/9a47042620bec87617f0b4f5d50568535668fe26
|
||||
public void setChar(int columnToSet, int codePoint, long style) {
|
||||
if (columnToSet < 0 || columnToSet >= mStyle.length)
|
||||
throw new IllegalArgumentException("TerminalRow.setChar(): columnToSet=" + columnToSet + ", codePoint=" + codePoint + ", style=" + style);
|
||||
|
||||
mStyle[columnToSet] = style;
|
||||
|
||||
final int newCodePointDisplayWidth = WcWidth.width(codePoint);
|
||||
|
|
@ -160,18 +189,25 @@ public final class TerminalRow {
|
|||
// Get the number of elements in the mText array this column uses now
|
||||
int oldCharactersUsedForColumn;
|
||||
if (columnToSet + oldCodePointDisplayWidth < mColumns) {
|
||||
oldCharactersUsedForColumn = findStartOfColumn(columnToSet + oldCodePointDisplayWidth) - oldStartOfColumnIndex;
|
||||
int oldEndOfColumnIndex = findStartOfColumn(columnToSet + oldCodePointDisplayWidth);
|
||||
oldCharactersUsedForColumn = oldEndOfColumnIndex - oldStartOfColumnIndex;
|
||||
} else {
|
||||
// Last character.
|
||||
oldCharactersUsedForColumn = mSpaceUsed - oldStartOfColumnIndex;
|
||||
}
|
||||
|
||||
// If MAX_COMBINING_CHARACTERS_PER_COLUMN already exist in column, then ignore adding additional combining characters.
|
||||
if (newIsCombining) {
|
||||
int combiningCharsCount = WcWidth.zeroWidthCharsCount(mText, oldStartOfColumnIndex, oldStartOfColumnIndex + oldCharactersUsedForColumn);
|
||||
if (combiningCharsCount >= MAX_COMBINING_CHARACTERS_PER_COLUMN)
|
||||
return;
|
||||
}
|
||||
|
||||
// Find how many chars this column will need
|
||||
int newCharactersUsedForColumn = Character.charCount(codePoint);
|
||||
if (newIsCombining) {
|
||||
// Combining characters are added to the contents of the column instead of overwriting them, so that they
|
||||
// modify the existing contents.
|
||||
// FIXME: Put a limit of combining characters.
|
||||
// FIXME: Unassigned characters also get width=0.
|
||||
newCharactersUsedForColumn += oldCharactersUsedForColumn;
|
||||
}
|
||||
|
|
@ -186,7 +222,7 @@ public final class TerminalRow {
|
|||
if (mSpaceUsed + javaCharDifference > text.length) {
|
||||
// We need to grow the array
|
||||
char[] newText = new char[text.length + mColumns];
|
||||
System.arraycopy(text, 0, newText, 0, oldStartOfColumnIndex + oldCharactersUsedForColumn);
|
||||
System.arraycopy(text, 0, newText, 0, oldNextColumnIndex);
|
||||
System.arraycopy(text, oldNextColumnIndex, newText, newNextColumnIndex, oldCharactersAfterColumn);
|
||||
mText = text = newText;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -236,7 +236,7 @@ public final class TerminalSession extends TerminalOutput {
|
|||
try {
|
||||
Os.kill(mShellPid, OsConstants.SIGKILL);
|
||||
} catch (ErrnoException e) {
|
||||
mClient.logWarn(LOG_TAG, "Failed sending SIGKILL: " + e.getMessage());
|
||||
Logger.logWarn(mClient, LOG_TAG, "Failed sending SIGKILL: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -308,7 +308,7 @@ public final class TerminalSession extends TerminalOutput {
|
|||
return outputPath;
|
||||
}
|
||||
} catch (IOException | SecurityException e) {
|
||||
mClient.logStackTraceWithMessage(LOG_TAG, "Error getting current directory", e);
|
||||
Logger.logStackTraceWithMessage(mClient, LOG_TAG, "Error getting current directory", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
@ -326,7 +326,7 @@ public final class TerminalSession extends TerminalOutput {
|
|||
descriptorField.setAccessible(true);
|
||||
descriptorField.set(result, fileDescriptor);
|
||||
} catch (NoSuchFieldException | IllegalAccessException | IllegalArgumentException e) {
|
||||
client.logStackTraceWithMessage(LOG_TAG, "Error accessing FileDescriptor#descriptor private field", e);
|
||||
Logger.logStackTraceWithMessage(client, LOG_TAG, "Error accessing FileDescriptor#descriptor private field", e);
|
||||
System.exit(1);
|
||||
}
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.termux.terminal;
|
||||
|
||||
/**
|
||||
* Implementation of wcwidth(3) for Unicode 9.
|
||||
* Implementation of wcwidth(3) for Unicode 15.
|
||||
*
|
||||
* Implementation from https://github.com/jquast/wcwidth but we return 0 for unprintable characters.
|
||||
*
|
||||
|
|
@ -9,12 +9,13 @@ package com.termux.terminal;
|
|||
* Must be kept in sync with the following:
|
||||
* https://github.com/termux/wcwidth
|
||||
* https://github.com/termux/libandroid-support
|
||||
* https://github.com/termux/termux-packages/tree/master/libandroid-support
|
||||
* https://github.com/termux/termux-packages/tree/master/packages/libandroid-support
|
||||
*/
|
||||
public final class WcWidth {
|
||||
|
||||
// From https://github.com/jquast/wcwidth/blob/master/wcwidth/table_zero.py
|
||||
// at commit b29897e5a1b403a0e36f7fc991614981cbc42475 (2020-07-14):
|
||||
// from https://github.com/jquast/wcwidth/pull/64
|
||||
// at commit 1b9b6585b0080ea5cb88dc9815796505724793fe (2022-12-16):
|
||||
private static final int[][] ZERO_WIDTH = {
|
||||
{0x00300, 0x0036f}, // Combining Grave Accent ..Combining Latin Small Le
|
||||
{0x00483, 0x00489}, // Combining Cyrillic Titlo..Combining Cyrillic Milli
|
||||
|
|
@ -40,7 +41,8 @@ public final class WcWidth {
|
|||
{0x00825, 0x00827}, // Samaritan Vowel Sign Sho..Samaritan Vowel Sign U
|
||||
{0x00829, 0x0082d}, // Samaritan Vowel Sign Lon..Samaritan Mark Nequdaa
|
||||
{0x00859, 0x0085b}, // Mandaic Affrication Mark..Mandaic Gemination Mark
|
||||
{0x008d3, 0x008e1}, // Arabic Small Low Waw ..Arabic Small High Sign S
|
||||
{0x00898, 0x0089f}, // Arabic Small High Word A..Arabic Half Madda Over M
|
||||
{0x008ca, 0x008e1}, // Arabic Small High Farsi ..Arabic Small High Sign S
|
||||
{0x008e3, 0x00902}, // Arabic Turned Damma Belo..Devanagari Sign Anusvara
|
||||
{0x0093a, 0x0093a}, // Devanagari Vowel Sign Oe..Devanagari Vowel Sign Oe
|
||||
{0x0093c, 0x0093c}, // Devanagari Sign Nukta ..Devanagari Sign Nukta
|
||||
|
|
@ -74,13 +76,14 @@ public final class WcWidth {
|
|||
{0x00b3f, 0x00b3f}, // Oriya Vowel Sign I ..Oriya Vowel Sign I
|
||||
{0x00b41, 0x00b44}, // Oriya Vowel Sign U ..Oriya Vowel Sign Vocalic
|
||||
{0x00b4d, 0x00b4d}, // Oriya Sign Virama ..Oriya Sign Virama
|
||||
{0x00b55, 0x00b56}, // (nil) ..Oriya Ai Length Mark
|
||||
{0x00b55, 0x00b56}, // Oriya Sign Overline ..Oriya Ai Length Mark
|
||||
{0x00b62, 0x00b63}, // Oriya Vowel Sign Vocalic..Oriya Vowel Sign Vocalic
|
||||
{0x00b82, 0x00b82}, // Tamil Sign Anusvara ..Tamil Sign Anusvara
|
||||
{0x00bc0, 0x00bc0}, // Tamil Vowel Sign Ii ..Tamil Vowel Sign Ii
|
||||
{0x00bcd, 0x00bcd}, // Tamil Sign Virama ..Tamil Sign Virama
|
||||
{0x00c00, 0x00c00}, // Telugu Sign Combining Ca..Telugu Sign Combining Ca
|
||||
{0x00c04, 0x00c04}, // Telugu Sign Combining An..Telugu Sign Combining An
|
||||
{0x00c3c, 0x00c3c}, // Telugu Sign Nukta ..Telugu Sign Nukta
|
||||
{0x00c3e, 0x00c40}, // Telugu Vowel Sign Aa ..Telugu Vowel Sign Ii
|
||||
{0x00c46, 0x00c48}, // Telugu Vowel Sign E ..Telugu Vowel Sign Ai
|
||||
{0x00c4a, 0x00c4d}, // Telugu Vowel Sign O ..Telugu Sign Virama
|
||||
|
|
@ -97,7 +100,7 @@ public final class WcWidth {
|
|||
{0x00d41, 0x00d44}, // Malayalam Vowel Sign U ..Malayalam Vowel Sign Voc
|
||||
{0x00d4d, 0x00d4d}, // Malayalam Sign Virama ..Malayalam Sign Virama
|
||||
{0x00d62, 0x00d63}, // Malayalam Vowel Sign Voc..Malayalam Vowel Sign Voc
|
||||
{0x00d81, 0x00d81}, // (nil) ..(nil)
|
||||
{0x00d81, 0x00d81}, // Sinhala Sign Candrabindu..Sinhala Sign Candrabindu
|
||||
{0x00dca, 0x00dca}, // Sinhala Sign Al-lakuna ..Sinhala Sign Al-lakuna
|
||||
{0x00dd2, 0x00dd4}, // Sinhala Vowel Sign Ketti..Sinhala Vowel Sign Ketti
|
||||
{0x00dd6, 0x00dd6}, // Sinhala Vowel Sign Diga ..Sinhala Vowel Sign Diga
|
||||
|
|
@ -106,7 +109,7 @@ public final class WcWidth {
|
|||
{0x00e47, 0x00e4e}, // Thai Character Maitaikhu..Thai Character Yamakkan
|
||||
{0x00eb1, 0x00eb1}, // Lao Vowel Sign Mai Kan ..Lao Vowel Sign Mai Kan
|
||||
{0x00eb4, 0x00ebc}, // Lao Vowel Sign I ..Lao Semivowel Sign Lo
|
||||
{0x00ec8, 0x00ecd}, // Lao Tone Mai Ek ..Lao Niggahita
|
||||
{0x00ec8, 0x00ece}, // Lao Tone Mai Ek ..(nil)
|
||||
{0x00f18, 0x00f19}, // Tibetan Astrological Sig..Tibetan Astrological Sig
|
||||
{0x00f35, 0x00f35}, // Tibetan Mark Ngas Bzung ..Tibetan Mark Ngas Bzung
|
||||
{0x00f37, 0x00f37}, // Tibetan Mark Ngas Bzung ..Tibetan Mark Ngas Bzung
|
||||
|
|
@ -130,7 +133,7 @@ public final class WcWidth {
|
|||
{0x0109d, 0x0109d}, // Myanmar Vowel Sign Aiton..Myanmar Vowel Sign Aiton
|
||||
{0x0135d, 0x0135f}, // Ethiopic Combining Gemin..Ethiopic Combining Gemin
|
||||
{0x01712, 0x01714}, // Tagalog Vowel Sign I ..Tagalog Sign Virama
|
||||
{0x01732, 0x01734}, // Hanunoo Vowel Sign I ..Hanunoo Sign Pamudpod
|
||||
{0x01732, 0x01733}, // Hanunoo Vowel Sign I ..Hanunoo Vowel Sign U
|
||||
{0x01752, 0x01753}, // Buhid Vowel Sign I ..Buhid Vowel Sign U
|
||||
{0x01772, 0x01773}, // Tagbanwa Vowel Sign I ..Tagbanwa Vowel Sign U
|
||||
{0x017b4, 0x017b5}, // Khmer Vowel Inherent Aq ..Khmer Vowel Inherent Aa
|
||||
|
|
@ -139,6 +142,7 @@ public final class WcWidth {
|
|||
{0x017c9, 0x017d3}, // Khmer Sign Muusikatoan ..Khmer Sign Bathamasat
|
||||
{0x017dd, 0x017dd}, // Khmer Sign Atthacan ..Khmer Sign Atthacan
|
||||
{0x0180b, 0x0180d}, // Mongolian Free Variation..Mongolian Free Variation
|
||||
{0x0180f, 0x0180f}, // Mongolian Free Variation..Mongolian Free Variation
|
||||
{0x01885, 0x01886}, // Mongolian Letter Ali Gal..Mongolian Letter Ali Gal
|
||||
{0x018a9, 0x018a9}, // Mongolian Letter Ali Gal..Mongolian Letter Ali Gal
|
||||
{0x01920, 0x01922}, // Limbu Vowel Sign A ..Limbu Vowel Sign U
|
||||
|
|
@ -154,7 +158,7 @@ public final class WcWidth {
|
|||
{0x01a65, 0x01a6c}, // Tai Tham Vowel Sign I ..Tai Tham Vowel Sign Oa B
|
||||
{0x01a73, 0x01a7c}, // Tai Tham Vowel Sign Oa A..Tai Tham Sign Khuen-lue
|
||||
{0x01a7f, 0x01a7f}, // Tai Tham Combining Crypt..Tai Tham Combining Crypt
|
||||
{0x01ab0, 0x01ac0}, // Combining Doubled Circum..(nil)
|
||||
{0x01ab0, 0x01ace}, // Combining Doubled Circum..Combining Latin Small Le
|
||||
{0x01b00, 0x01b03}, // Balinese Sign Ulu Ricem ..Balinese Sign Surang
|
||||
{0x01b34, 0x01b34}, // Balinese Sign Rerekan ..Balinese Sign Rerekan
|
||||
{0x01b36, 0x01b3a}, // Balinese Vowel Sign Ulu ..Balinese Vowel Sign Ra R
|
||||
|
|
@ -177,8 +181,7 @@ public final class WcWidth {
|
|||
{0x01ced, 0x01ced}, // Vedic Sign Tiryak ..Vedic Sign Tiryak
|
||||
{0x01cf4, 0x01cf4}, // Vedic Tone Candra Above ..Vedic Tone Candra Above
|
||||
{0x01cf8, 0x01cf9}, // Vedic Tone Ring Above ..Vedic Tone Double Ring A
|
||||
{0x01dc0, 0x01df9}, // Combining Dotted Grave A..Combining Wide Inverted
|
||||
{0x01dfb, 0x01dff}, // Combining Deletion Mark ..Combining Right Arrowhea
|
||||
{0x01dc0, 0x01dff}, // Combining Dotted Grave A..Combining Right Arrowhea
|
||||
{0x020d0, 0x020f0}, // Combining Left Harpoon A..Combining Asterisk Above
|
||||
{0x02cef, 0x02cf1}, // Coptic Combining Ni Abov..Coptic Combining Spiritu
|
||||
{0x02d7f, 0x02d7f}, // Tifinagh Consonant Joine..Tifinagh Consonant Joine
|
||||
|
|
@ -193,7 +196,7 @@ public final class WcWidth {
|
|||
{0x0a806, 0x0a806}, // Syloti Nagri Sign Hasant..Syloti Nagri Sign Hasant
|
||||
{0x0a80b, 0x0a80b}, // Syloti Nagri Sign Anusva..Syloti Nagri Sign Anusva
|
||||
{0x0a825, 0x0a826}, // Syloti Nagri Vowel Sign ..Syloti Nagri Vowel Sign
|
||||
{0x0a82c, 0x0a82c}, // (nil) ..(nil)
|
||||
{0x0a82c, 0x0a82c}, // Syloti Nagri Sign Altern..Syloti Nagri Sign Altern
|
||||
{0x0a8c4, 0x0a8c5}, // Saurashtra Sign Virama ..Saurashtra Sign Candrabi
|
||||
{0x0a8e0, 0x0a8f1}, // Combining Devanagari Dig..Combining Devanagari Sig
|
||||
{0x0a8ff, 0x0a8ff}, // Devanagari Vowel Sign Ay..Devanagari Vowel Sign Ay
|
||||
|
|
@ -233,13 +236,18 @@ public final class WcWidth {
|
|||
{0x10a3f, 0x10a3f}, // Kharoshthi Virama ..Kharoshthi Virama
|
||||
{0x10ae5, 0x10ae6}, // Manichaean Abbreviation ..Manichaean Abbreviation
|
||||
{0x10d24, 0x10d27}, // Hanifi Rohingya Sign Har..Hanifi Rohingya Sign Tas
|
||||
{0x10eab, 0x10eac}, // (nil) ..(nil)
|
||||
{0x10eab, 0x10eac}, // Yezidi Combining Hamza M..Yezidi Combining Madda M
|
||||
{0x10efd, 0x10eff}, // (nil) ..(nil)
|
||||
{0x10f46, 0x10f50}, // Sogdian Combining Dot Be..Sogdian Combining Stroke
|
||||
{0x10f82, 0x10f85}, // Old Uyghur Combining Dot..Old Uyghur Combining Two
|
||||
{0x11001, 0x11001}, // Brahmi Sign Anusvara ..Brahmi Sign Anusvara
|
||||
{0x11038, 0x11046}, // Brahmi Vowel Sign Aa ..Brahmi Virama
|
||||
{0x11070, 0x11070}, // Brahmi Sign Old Tamil Vi..Brahmi Sign Old Tamil Vi
|
||||
{0x11073, 0x11074}, // Brahmi Vowel Sign Old Ta..Brahmi Vowel Sign Old Ta
|
||||
{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
|
||||
{0x110c2, 0x110c2}, // Kaithi Vowel Sign Vocali..Kaithi Vowel Sign Vocali
|
||||
{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
|
||||
|
|
@ -247,11 +255,12 @@ public final class WcWidth {
|
|||
{0x11180, 0x11181}, // Sharada Sign Candrabindu..Sharada Sign Anusvara
|
||||
{0x111b6, 0x111be}, // Sharada Vowel Sign U ..Sharada Vowel Sign O
|
||||
{0x111c9, 0x111cc}, // Sharada Sandhi Mark ..Sharada Extra Short Vowe
|
||||
{0x111cf, 0x111cf}, // (nil) ..(nil)
|
||||
{0x111cf, 0x111cf}, // Sharada Sign Inverted Ca..Sharada Sign Inverted Ca
|
||||
{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}, // Khojki Sign Sukun ..Khojki Sign Sukun
|
||||
{0x11241, 0x11241}, // (nil) ..(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
|
||||
|
|
@ -283,9 +292,9 @@ public final class WcWidth {
|
|||
{0x11727, 0x1172b}, // Ahom Vowel Sign Aw ..Ahom Sign Killer
|
||||
{0x1182f, 0x11837}, // Dogra Vowel Sign U ..Dogra Sign Anusvara
|
||||
{0x11839, 0x1183a}, // Dogra Sign Virama ..Dogra Sign Nukta
|
||||
{0x1193b, 0x1193c}, // (nil) ..(nil)
|
||||
{0x1193e, 0x1193e}, // (nil) ..(nil)
|
||||
{0x11943, 0x11943}, // (nil) ..(nil)
|
||||
{0x1193b, 0x1193c}, // Dives Akuru Sign Anusvar..Dives Akuru Sign Candrab
|
||||
{0x1193e, 0x1193e}, // Dives Akuru Virama ..Dives Akuru Virama
|
||||
{0x11943, 0x11943}, // Dives Akuru Sign Nukta ..Dives Akuru Sign Nukta
|
||||
{0x119d4, 0x119d7}, // Nandinagari Vowel Sign U..Nandinagari Vowel Sign V
|
||||
{0x119da, 0x119db}, // Nandinagari Vowel Sign E..Nandinagari Vowel Sign A
|
||||
{0x119e0, 0x119e0}, // Nandinagari Sign Virama ..Nandinagari Sign Virama
|
||||
|
|
@ -313,12 +322,20 @@ public final class WcWidth {
|
|||
{0x11d95, 0x11d95}, // Gunjala Gondi Sign Anusv..Gunjala Gondi Sign Anusv
|
||||
{0x11d97, 0x11d97}, // Gunjala Gondi Virama ..Gunjala Gondi Virama
|
||||
{0x11ef3, 0x11ef4}, // Makasar Vowel Sign I ..Makasar Vowel Sign U
|
||||
{0x11f00, 0x11f01}, // (nil) ..(nil)
|
||||
{0x11f36, 0x11f3a}, // (nil) ..(nil)
|
||||
{0x11f40, 0x11f40}, // (nil) ..(nil)
|
||||
{0x11f42, 0x11f42}, // (nil) ..(nil)
|
||||
{0x13440, 0x13440}, // (nil) ..(nil)
|
||||
{0x13447, 0x13455}, // (nil) ..(nil)
|
||||
{0x16af0, 0x16af4}, // Bassa Vah Combining High..Bassa Vah Combining High
|
||||
{0x16b30, 0x16b36}, // Pahawh Hmong Mark Cim Tu..Pahawh Hmong Mark Cim Ta
|
||||
{0x16f4f, 0x16f4f}, // Miao Sign Consonant Modi..Miao Sign Consonant Modi
|
||||
{0x16f8f, 0x16f92}, // Miao Tone Right ..Miao Tone Below
|
||||
{0x16fe4, 0x16fe4}, // (nil) ..(nil)
|
||||
{0x16fe4, 0x16fe4}, // Khitan Small Script Fill..Khitan Small Script Fill
|
||||
{0x1bc9d, 0x1bc9e}, // Duployan Thick Letter Se..Duployan Double Mark
|
||||
{0x1cf00, 0x1cf2d}, // Znamenny Combining Mark ..Znamenny Combining Mark
|
||||
{0x1cf30, 0x1cf46}, // Znamenny Combining Tonal..Znamenny Priznak Modifie
|
||||
{0x1d167, 0x1d169}, // Musical Symbol Combining..Musical Symbol Combining
|
||||
{0x1d17b, 0x1d182}, // Musical Symbol Combining..Musical Symbol Combining
|
||||
{0x1d185, 0x1d18b}, // Musical Symbol Combining..Musical Symbol Combining
|
||||
|
|
@ -335,15 +352,19 @@ public final class WcWidth {
|
|||
{0x1e01b, 0x1e021}, // Combining Glagolitic Let..Combining Glagolitic Let
|
||||
{0x1e023, 0x1e024}, // Combining Glagolitic Let..Combining Glagolitic Let
|
||||
{0x1e026, 0x1e02a}, // Combining Glagolitic Let..Combining Glagolitic Let
|
||||
{0x1e08f, 0x1e08f}, // (nil) ..(nil)
|
||||
{0x1e130, 0x1e136}, // Nyiakeng Puachue Hmong T..Nyiakeng Puachue Hmong T
|
||||
{0x1e2ae, 0x1e2ae}, // Toto Sign Rising Tone ..Toto Sign Rising Tone
|
||||
{0x1e2ec, 0x1e2ef}, // Wancho Tone Tup ..Wancho Tone Koini
|
||||
{0x1e4ec, 0x1e4ef}, // (nil) ..(nil)
|
||||
{0x1e8d0, 0x1e8d6}, // Mende Kikakui Combining ..Mende Kikakui Combining
|
||||
{0x1e944, 0x1e94a}, // Adlam Alif Lengthener ..Adlam Nukta
|
||||
{0xe0100, 0xe01ef}, // Variation Selector-17 ..Variation Selector-256
|
||||
};
|
||||
|
||||
// https://github.com/jquast/wcwidth/blob/master/wcwidth/table_wide.py
|
||||
// at commit b29897e5a1b403a0e36f7fc991614981cbc42475 (2020-07-14):
|
||||
// from https://github.com/jquast/wcwidth/pull/64
|
||||
// at commit 1b9b6585b0080ea5cb88dc9815796505724793fe (2022-12-16):
|
||||
private static final int[][] WIDE_EASTASIAN = {
|
||||
{0x01100, 0x0115f}, // Hangul Choseong Kiyeok ..Hangul Choseong Filler
|
||||
{0x0231a, 0x0231b}, // Watch ..Hourglass
|
||||
|
|
@ -392,7 +413,7 @@ public final class WcWidth {
|
|||
{0x03190, 0x031e3}, // Ideographic Annotation L..Cjk Stroke Q
|
||||
{0x031f0, 0x0321e}, // Katakana Letter Small Ku..Parenthesized Korean Cha
|
||||
{0x03220, 0x03247}, // Parenthesized Ideograph ..Circled Ideograph Koto
|
||||
{0x03250, 0x04dbf}, // Partnership Sign ..(nil)
|
||||
{0x03250, 0x04dbf}, // Partnership Sign ..Cjk Unified Ideograph-4d
|
||||
{0x04e00, 0x0a48c}, // Cjk Unified Ideograph-4e..Yi Syllable Yyr
|
||||
{0x0a490, 0x0a4c6}, // Yi Radical Qot ..Yi Radical Ke
|
||||
{0x0a960, 0x0a97c}, // Hangul Choseong Tikeut-m..Hangul Choseong Ssangyeo
|
||||
|
|
@ -404,13 +425,18 @@ public final class WcWidth {
|
|||
{0x0fe68, 0x0fe6b}, // Small Reverse Solidus ..Small Commercial At
|
||||
{0x0ff01, 0x0ff60}, // Fullwidth Exclamation Ma..Fullwidth Right White Pa
|
||||
{0x0ffe0, 0x0ffe6}, // Fullwidth Cent Sign ..Fullwidth Won Sign
|
||||
{0x16fe0, 0x16fe4}, // Tangut Iteration Mark ..(nil)
|
||||
{0x16ff0, 0x16ff1}, // (nil) ..(nil)
|
||||
{0x16fe0, 0x16fe4}, // Tangut Iteration Mark ..Khitan Small Script Fill
|
||||
{0x16ff0, 0x16ff1}, // Vietnamese Alternate Rea..Vietnamese Alternate Rea
|
||||
{0x17000, 0x187f7}, // (nil) ..(nil)
|
||||
{0x18800, 0x18cd5}, // Tangut Component-001 ..(nil)
|
||||
{0x18800, 0x18cd5}, // Tangut Component-001 ..Khitan Small Script Char
|
||||
{0x18d00, 0x18d08}, // (nil) ..(nil)
|
||||
{0x1b000, 0x1b11e}, // Katakana Letter Archaic ..Hentaigana Letter N-mu-m
|
||||
{0x1aff0, 0x1aff3}, // Katakana Letter Minnan T..Katakana Letter Minnan T
|
||||
{0x1aff5, 0x1affb}, // Katakana Letter Minnan T..Katakana Letter Minnan N
|
||||
{0x1affd, 0x1affe}, // Katakana Letter Minnan N..Katakana Letter Minnan N
|
||||
{0x1b000, 0x1b122}, // Katakana Letter Archaic ..Katakana Letter Archaic
|
||||
{0x1b132, 0x1b132}, // (nil) ..(nil)
|
||||
{0x1b150, 0x1b152}, // Hiragana Letter Small Wi..Hiragana Letter Small Wo
|
||||
{0x1b155, 0x1b155}, // (nil) ..(nil)
|
||||
{0x1b164, 0x1b167}, // Katakana Letter Small Wi..Katakana Letter Small N
|
||||
{0x1b170, 0x1b2fb}, // Nushu Character-1b170 ..Nushu Character-1b2fb
|
||||
{0x1f004, 0x1f004}, // Mahjong Tile Red Dragon ..Mahjong Tile Red Dragon
|
||||
|
|
@ -443,24 +469,24 @@ public final class WcWidth {
|
|||
{0x1f680, 0x1f6c5}, // Rocket ..Left Luggage
|
||||
{0x1f6cc, 0x1f6cc}, // Sleeping Accommodation ..Sleeping Accommodation
|
||||
{0x1f6d0, 0x1f6d2}, // Place Of Worship ..Shopping Trolley
|
||||
{0x1f6d5, 0x1f6d7}, // Hindu Temple ..(nil)
|
||||
{0x1f6d5, 0x1f6d7}, // Hindu Temple ..Elevator
|
||||
{0x1f6dc, 0x1f6df}, // (nil) ..Ring Buoy
|
||||
{0x1f6eb, 0x1f6ec}, // Airplane Departure ..Airplane Arriving
|
||||
{0x1f6f4, 0x1f6fc}, // Scooter ..(nil)
|
||||
{0x1f6f4, 0x1f6fc}, // Scooter ..Roller Skate
|
||||
{0x1f7e0, 0x1f7eb}, // Large Orange Circle ..Large Brown Square
|
||||
{0x1f90c, 0x1f93a}, // (nil) ..Fencer
|
||||
{0x1f7f0, 0x1f7f0}, // Heavy Equals Sign ..Heavy Equals Sign
|
||||
{0x1f90c, 0x1f93a}, // Pinched Fingers ..Fencer
|
||||
{0x1f93c, 0x1f945}, // Wrestlers ..Goal Net
|
||||
{0x1f947, 0x1f978}, // First Place Medal ..(nil)
|
||||
{0x1f97a, 0x1f9cb}, // Face With Pleading Eyes ..(nil)
|
||||
{0x1f9cd, 0x1f9ff}, // Standing Person ..Nazar Amulet
|
||||
{0x1fa70, 0x1fa74}, // Ballet Shoes ..(nil)
|
||||
{0x1fa78, 0x1fa7a}, // Drop Of Blood ..Stethoscope
|
||||
{0x1fa80, 0x1fa86}, // Yo-yo ..(nil)
|
||||
{0x1fa90, 0x1faa8}, // Ringed Planet ..(nil)
|
||||
{0x1fab0, 0x1fab6}, // (nil) ..(nil)
|
||||
{0x1fac0, 0x1fac2}, // (nil) ..(nil)
|
||||
{0x1fad0, 0x1fad6}, // (nil) ..(nil)
|
||||
{0x1f947, 0x1f9ff}, // First Place Medal ..Nazar Amulet
|
||||
{0x1fa70, 0x1fa7c}, // Ballet Shoes ..Crutch
|
||||
{0x1fa80, 0x1fa88}, // Yo-yo ..(nil)
|
||||
{0x1fa90, 0x1fabd}, // Ringed Planet ..(nil)
|
||||
{0x1fabf, 0x1fac5}, // (nil) ..Person With Crown
|
||||
{0x1face, 0x1fadb}, // (nil) ..(nil)
|
||||
{0x1fae0, 0x1fae8}, // Melting Face ..(nil)
|
||||
{0x1faf0, 0x1faf8}, // Hand With Index Finger A..(nil)
|
||||
{0x20000, 0x2fffd}, // Cjk Unified Ideograph-20..(nil)
|
||||
{0x30000, 0x3fffd}, // (nil) ..(nil)
|
||||
{0x30000, 0x3fffd}, // Cjk Unified Ideograph-30..(nil)
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -512,4 +538,29 @@ public final class WcWidth {
|
|||
return Character.isHighSurrogate(c) ? width(Character.toCodePoint(c, chars[index + 1])) : width(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* The zero width characters count like combining characters in the `chars` array from start
|
||||
* index to end index (exclusive).
|
||||
*/
|
||||
public static int zeroWidthCharsCount(char[] chars, int start, int end) {
|
||||
if (start < 0 || start >= chars.length)
|
||||
return 0;
|
||||
|
||||
int count = 0;
|
||||
for (int i = start; i < end && i < chars.length;) {
|
||||
if (Character.isHighSurrogate(chars[i])) {
|
||||
if (width(Character.toCodePoint(chars[i], chars[i + 1])) <= 0) {
|
||||
count++;
|
||||
}
|
||||
i += 2;
|
||||
} else {
|
||||
if (width(chars[i]) <= 0) {
|
||||
count++;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -151,6 +151,19 @@ public class TerminalTest extends TerminalTestCase {
|
|||
assertEquals(TextStyle.COLOR_INDEX_FOREGROUND, mTerminal.mForeColor);
|
||||
assertEquals(TextStyle.COLOR_INDEX_BACKGROUND, mTerminal.mBackColor);
|
||||
|
||||
// Test CSI resetting to default if sequence starts with ; or has sequential ;;
|
||||
// Check TerminalEmulator.parseArg()
|
||||
enterString("\033[31m\033[m");
|
||||
assertEquals(TextStyle.COLOR_INDEX_FOREGROUND, mTerminal.mForeColor);
|
||||
enterString("\033[31m\033[;m");
|
||||
assertEquals(TextStyle.COLOR_INDEX_FOREGROUND, mTerminal.mForeColor);
|
||||
enterString("\033[31m\033[0m");
|
||||
assertEquals(TextStyle.COLOR_INDEX_FOREGROUND, mTerminal.mForeColor);
|
||||
enterString("\033[31m\033[0;m");
|
||||
assertEquals(TextStyle.COLOR_INDEX_FOREGROUND, mTerminal.mForeColor);
|
||||
enterString("\033[31;;m");
|
||||
assertEquals(TextStyle.COLOR_INDEX_FOREGROUND, mTerminal.mForeColor);
|
||||
|
||||
// 256 colors:
|
||||
enterString("\033[38;5;119m");
|
||||
assertEquals(119, mTerminal.mForeColor);
|
||||
|
|
|
|||
|
|
@ -183,14 +183,19 @@ public class TextSelectionCursorController implements CursorController {
|
|||
int y1 = Math.round((mSelY1 - 1 - terminalView.getTopRow()) * terminalView.mRenderer.getFontLineSpacing());
|
||||
int y2 = Math.round((mSelY2 + 1 - terminalView.getTopRow()) * terminalView.mRenderer.getFontLineSpacing());
|
||||
|
||||
|
||||
if (x1 > x2) {
|
||||
int tmp = x1;
|
||||
x1 = x2;
|
||||
x2 = tmp;
|
||||
}
|
||||
|
||||
outRect.set(x1, y1 + mHandleHeight, x2, y2 + mHandleHeight);
|
||||
int terminalBottom = terminalView.getBottom();
|
||||
int top = y1 + mHandleHeight;
|
||||
int bottom = y2 + mHandleHeight;
|
||||
if (top > terminalBottom) top = terminalBottom;
|
||||
if (bottom > terminalBottom) bottom = terminalBottom;
|
||||
|
||||
outRect.set(x1, top, x2, bottom);
|
||||
}
|
||||
}, ActionMode.TYPE_FLOATING);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,11 +4,14 @@ import android.app.Activity;
|
|||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.graphics.Color;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.termux.shared.R;
|
||||
import com.termux.shared.logger.Logger;
|
||||
|
||||
public class MessageDialogUtils {
|
||||
|
||||
|
|
@ -74,7 +77,19 @@ public class MessageDialogUtils {
|
|||
if (onDismiss != null)
|
||||
builder.setOnDismissListener(onDismiss);
|
||||
|
||||
builder.show();
|
||||
AlertDialog dialog = builder.create();
|
||||
|
||||
dialog.setOnShowListener(dialogInterface -> {
|
||||
Logger.logError("dialog");
|
||||
Button button = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
|
||||
if (button != null)
|
||||
button.setTextColor(Color.BLACK);
|
||||
button = dialog.getButton(AlertDialog.BUTTON_NEGATIVE);
|
||||
if (button != null)
|
||||
button.setTextColor(Color.BLACK);
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
public static void exitAppWithErrorMessage(Context context, String titleText, String messageText) {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import android.util.Log;
|
|||
import android.widget.Toast;
|
||||
|
||||
import com.termux.shared.R;
|
||||
import com.termux.shared.data.DataUtils;
|
||||
import com.termux.shared.termux.TermuxConstants;
|
||||
|
||||
import java.io.IOException;
|
||||
|
|
@ -363,7 +364,7 @@ public class Logger {
|
|||
|
||||
|
||||
public static void showToast(final Context context, final String toastText, boolean longDuration) {
|
||||
if (context == null) return;
|
||||
if (context == null || DataUtils.isNullOrEmpty(toastText)) return;
|
||||
|
||||
new Handler(Looper.getMainLooper()).post(() -> Toast.makeText(context, toastText, longDuration ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT).show());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -191,9 +191,8 @@ public class MarkdownUtils {
|
|||
}
|
||||
|
||||
public static Spanned getSpannedMarkdownText(Context context, String string) {
|
||||
|
||||
if (context == null || string == null) return null;
|
||||
final Markwon markwon = getSpannedMarkwonBuilder(context);
|
||||
|
||||
return markwon.toMarkdown(string);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -485,6 +485,12 @@ public final class TermuxConstants {
|
|||
* Termux miscellaneous urls.
|
||||
*/
|
||||
|
||||
/** Termux Site */
|
||||
public static final String TERMUX_SITE = TERMUX_APP_NAME + " Site"; // Default: "Termux Site"
|
||||
|
||||
/** Termux Site url */
|
||||
public static final String TERMUX_SITE_URL = "https://termux.dev"; // Default: "https://termux.dev"
|
||||
|
||||
/** Termux Wiki */
|
||||
public static final String TERMUX_WIKI = TERMUX_APP_NAME + " Wiki"; // Default: "Termux Wiki"
|
||||
|
||||
|
|
@ -499,10 +505,10 @@ public final class TermuxConstants {
|
|||
|
||||
|
||||
/** Termux support email url */
|
||||
public static final String TERMUX_SUPPORT_EMAIL_URL = "termuxreports@groups.io"; // Default: "termuxreports@groups.io"
|
||||
public static final String TERMUX_SUPPORT_EMAIL_URL = "support@termux.dev"; // Default: "support@termux.dev"
|
||||
|
||||
/** Termux support email mailto url */
|
||||
public static final String TERMUX_SUPPORT_EMAIL_MAILTO_URL = "mailto:" + TERMUX_SUPPORT_EMAIL_URL; // Default: "mailto:termuxreports@groups.io"
|
||||
public static final String TERMUX_SUPPORT_EMAIL_MAILTO_URL = "mailto:" + TERMUX_SUPPORT_EMAIL_URL; // Default: "mailto:support@termux.dev"
|
||||
|
||||
|
||||
/** Termux Reddit subreddit */
|
||||
|
|
@ -513,7 +519,7 @@ public final class TermuxConstants {
|
|||
|
||||
|
||||
/** Termux donate url */
|
||||
public static final String TERMUX_DONATE_URL = TERMUX_PACKAGES_GITHUB_REPO_URL + "/wiki/Donate"; // Default: "https://github.com/termux/termux-packages/wiki/Donate"
|
||||
public static final String TERMUX_DONATE_URL = TERMUX_SITE_URL + "/donate"; // Default: "https://termux.dev/donate"
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue