Titan 2 Elite: physical keyboard fix + streaming scroll-hold
Some checks failed
Build / build (apt-android-5) (push) Has been cancelled
Build / build (apt-android-7) (push) Has been cancelled
Automatic Dependency Submission / dependency-submission (push) Has been cancelled
Validate Gradle Wrapper / Validation (push) Has been cancelled
Unit tests / testing (push) Has been cancelled
Attach Debug APKs To Release / attach-apks (apt-android-5) (release) Has been cancelled
Attach Debug APKs To Release / attach-apks (apt-android-7) (release) Has been cancelled
Trigger Termux Library Builds on Jitpack / trigger-termux-library-builds (release) Has been cancelled
Some checks failed
Build / build (apt-android-5) (push) Has been cancelled
Build / build (apt-android-7) (push) Has been cancelled
Automatic Dependency Submission / dependency-submission (push) Has been cancelled
Validate Gradle Wrapper / Validation (push) Has been cancelled
Unit tests / testing (push) Has been cancelled
Attach Debug APKs To Release / attach-apks (apt-android-5) (release) Has been cancelled
Attach Debug APKs To Release / attach-apks (apt-android-7) (release) Has been cancelled
Trigger Termux Library Builds on Jitpack / trigger-termux-library-builds (release) Has been cancelled
- Only bubble real system keys (isSystem() synthetic letter events injected by the Titan 2 keyboard layer now reach the KCM path). - Latch hardware Shift/Alt from modifier key events; injected letter events arrive without meta state. Cleared on keyup, focus loss and session attach. - Consult the kcm alt layer for Alt+<key> (Alt+W -> '1') and send the resolved char without the ESC alt-prefix. - Hold the scroll viewport when the user scrolled up while output is streaming; auto-follow resumes on scroll-to-bottom or typing. - versionCode 1023 / versionName 0.119.0-titan2.1
This commit is contained in:
@@ -43,8 +43,8 @@ android {
|
||||
defaultConfig {
|
||||
minSdkVersion project.properties.minSdkVersion.toInteger()
|
||||
targetSdkVersion project.properties.targetSdkVersion.toInteger()
|
||||
versionCode 118
|
||||
versionName "0.118.0"
|
||||
versionCode 1023
|
||||
versionName "0.119.0-titan2.1"
|
||||
|
||||
if (appVersionName) versionName = appVersionName
|
||||
validateVersionName(versionName)
|
||||
|
||||
@@ -68,6 +68,15 @@ public final class TerminalView extends View {
|
||||
|
||||
/** The top row of text to display. Ranges from -activeTranscriptRows to 0. */
|
||||
int mTopRow;
|
||||
|
||||
/** TITAN2 PATCH: user scrolled away from the bottom; hold the viewport until they return. */
|
||||
boolean mUserScrollLock;
|
||||
|
||||
/** TITAN2 PATCH: latched hardware Shift/Alt state. The Titan 2 keyboard layer re-injects
|
||||
* synthetic key events (FLAG_FROM_SYSTEM) whose letter events often arrive without meta
|
||||
* state, so the modifier state is tracked from the modifier key events themselves. */
|
||||
boolean mHardwareShiftDown;
|
||||
boolean mHardwareAltDown;
|
||||
int[] mDefaultSelectors = new int[]{-1,-1,-1,-1};
|
||||
|
||||
float mScaleFactor = 1.f;
|
||||
@@ -290,6 +299,9 @@ public final class TerminalView extends View {
|
||||
public boolean attachSession(TerminalSession session) {
|
||||
if (session == mTermSession) return false;
|
||||
mTopRow = 0;
|
||||
mUserScrollLock = false; // TITAN2 PATCH
|
||||
mHardwareShiftDown = false; // TITAN2 PATCH
|
||||
mHardwareAltDown = false; // TITAN2 PATCH
|
||||
|
||||
mTermSession = session;
|
||||
mEmulator = null;
|
||||
@@ -479,6 +491,17 @@ public final class TerminalView extends View {
|
||||
mTopRow -= rowShift;
|
||||
decrementYTextSelectionCursors(rowShift);
|
||||
}
|
||||
} else if (mUserScrollLock && mTopRow != 0) {
|
||||
// TITAN2 PATCH: the user scrolled up, so hold the viewport instead of snapping
|
||||
// back to the bottom on every screen update (e.g. while an app streams output).
|
||||
// Keep the same content in view when new lines are appended.
|
||||
int rowShift = mEmulator.getScrollCounter();
|
||||
if (-mTopRow + rowShift > rowsInHistory) {
|
||||
mTopRow = -rowsInHistory;
|
||||
} else {
|
||||
mTopRow -= rowShift;
|
||||
}
|
||||
skipScrolling = true;
|
||||
}
|
||||
|
||||
if (!skipScrolling && mTopRow != 0) {
|
||||
@@ -583,6 +606,9 @@ public final class TerminalView extends View {
|
||||
handleKeyCode(up ? KeyEvent.KEYCODE_DPAD_UP : KeyEvent.KEYCODE_DPAD_DOWN, 0);
|
||||
} else {
|
||||
mTopRow = Math.min(0, Math.max(-(mEmulator.getScreen().getActiveTranscriptRows()), mTopRow + (up ? -1 : 1)));
|
||||
// TITAN2 PATCH: scrolling away from the bottom pauses auto-follow;
|
||||
// scrolling back to the bottom (mTopRow == 0) resumes it.
|
||||
mUserScrollLock = mTopRow != 0;
|
||||
if (!awakenScrollBars()) invalidate();
|
||||
}
|
||||
}
|
||||
@@ -770,6 +796,21 @@ public final class TerminalView extends View {
|
||||
if (TERMINAL_VIEW_KEY_LOGGING_ENABLED)
|
||||
mClient.logInfo(LOG_TAG, "onKeyDown(keyCode=" + keyCode + ", isSystem()=" + event.isSystem() + ", event=" + event + ")");
|
||||
if (mEmulator == null) return true;
|
||||
|
||||
// TITAN2 PATCH: latch hardware Shift/Alt state from the modifier key events
|
||||
// themselves. The synthetic letter events injected by the Titan 2 keyboard layer
|
||||
// are flagged FLAG_FROM_SYSTEM and often arrive without meta state.
|
||||
switch (keyCode) {
|
||||
case KeyEvent.KEYCODE_SHIFT_LEFT:
|
||||
case KeyEvent.KEYCODE_SHIFT_RIGHT:
|
||||
mHardwareShiftDown = true;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_ALT_LEFT:
|
||||
case KeyEvent.KEYCODE_ALT_RIGHT:
|
||||
mHardwareAltDown = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (isSelectingText()) {
|
||||
stopTextSelectionMode();
|
||||
}
|
||||
@@ -777,7 +818,11 @@ public final class TerminalView extends View {
|
||||
if (mClient.onKeyDown(keyCode, event, mTermSession)) {
|
||||
invalidate();
|
||||
return true;
|
||||
} else if (event.isSystem() && (!mClient.shouldBackButtonBeMappedToEscape() || keyCode != KeyEvent.KEYCODE_BACK)) {
|
||||
} else if (event.isSystem() && isRealSystemKey(keyCode)
|
||||
&& (!mClient.shouldBackButtonBeMappedToEscape() || keyCode != KeyEvent.KEYCODE_BACK)) {
|
||||
// TITAN2 PATCH: only bubble *real* system keys (back, home, volume, ...). The
|
||||
// Titan 2 keyboard layer re-injects letter events with FLAG_FROM_SYSTEM, which
|
||||
// must reach the KCM path below instead of being dropped on the activity.
|
||||
return super.onKeyDown(keyCode, event);
|
||||
} else if (event.getAction() == KeyEvent.ACTION_MULTIPLE && keyCode == KeyEvent.KEYCODE_UNKNOWN) {
|
||||
mTermSession.write(event.getCharacters());
|
||||
@@ -788,8 +833,9 @@ public final class TerminalView extends View {
|
||||
|
||||
final int metaState = event.getMetaState();
|
||||
final boolean controlDown = event.isCtrlPressed() || mClient.readControlKey();
|
||||
final boolean leftAltDown = (metaState & KeyEvent.META_ALT_LEFT_ON) != 0 || mClient.readAltKey();
|
||||
final boolean shiftDown = event.isShiftPressed() || mClient.readShiftKey();
|
||||
// TITAN2 PATCH: also use the latched hardware modifier state (injected events lack meta).
|
||||
final boolean leftAltDown = (metaState & KeyEvent.META_ALT_LEFT_ON) != 0 || mClient.readAltKey() || mHardwareAltDown;
|
||||
final boolean shiftDown = event.isShiftPressed() || mClient.readShiftKey() || mHardwareShiftDown;
|
||||
final boolean rightAltDownFromEvent = (metaState & KeyEvent.META_ALT_RIGHT_ON) != 0;
|
||||
|
||||
int keyMod = 0;
|
||||
@@ -817,8 +863,19 @@ public final class TerminalView extends View {
|
||||
if (mClient.readFnKey()) effectiveMetaState |= KeyEvent.META_FUNCTION_ON;
|
||||
|
||||
int result = event.getUnicodeChar(effectiveMetaState);
|
||||
boolean altLayerUsed = false;
|
||||
if (!rightAltDownFromEvent && leftAltDown && result != 0) {
|
||||
// TITAN2 PATCH: consult the kcm alt layer (e.g. Alt+W -> '1' in TitanKey.kcm).
|
||||
// The base lookup above strips left-alt from the meta state, so upstream never
|
||||
// uses the alt layer and instead sends ESC + <char>.
|
||||
int altLayerResult = event.getUnicodeChar(effectiveMetaState | KeyEvent.META_ALT_LEFT_ON | KeyEvent.META_ALT_ON);
|
||||
if (altLayerResult != 0 && altLayerResult != result) {
|
||||
result = altLayerResult;
|
||||
altLayerUsed = true;
|
||||
}
|
||||
}
|
||||
if (TERMINAL_VIEW_KEY_LOGGING_ENABLED)
|
||||
mClient.logInfo(LOG_TAG, "KeyEvent#getUnicodeChar(" + effectiveMetaState + ") returned: " + result);
|
||||
mClient.logInfo(LOG_TAG, "KeyEvent#getUnicodeChar(" + effectiveMetaState + ") returned: " + result + (altLayerUsed ? " (kcm alt layer)" : ""));
|
||||
if (result == 0) {
|
||||
return false;
|
||||
}
|
||||
@@ -835,7 +892,7 @@ public final class TerminalView extends View {
|
||||
if (combinedChar > 0) result = combinedChar;
|
||||
mCombiningAccent = 0;
|
||||
}
|
||||
inputCodePoint(event.getDeviceId(), result, controlDown, leftAltDown);
|
||||
inputCodePoint(event.getDeviceId(), result, controlDown, leftAltDown, altLayerUsed);
|
||||
}
|
||||
|
||||
if (mCombiningAccent != oldCombiningAccent) invalidate();
|
||||
@@ -844,6 +901,10 @@ public final class TerminalView extends View {
|
||||
}
|
||||
|
||||
public void inputCodePoint(int eventSource, int codePoint, boolean controlDownFromEvent, boolean leftAltDownFromEvent) {
|
||||
inputCodePoint(eventSource, codePoint, controlDownFromEvent, leftAltDownFromEvent, false);
|
||||
}
|
||||
|
||||
public void inputCodePoint(int eventSource, int codePoint, boolean controlDownFromEvent, boolean leftAltDownFromEvent, boolean altConsumedByKcm) {
|
||||
if (TERMINAL_VIEW_KEY_LOGGING_ENABLED) {
|
||||
mClient.logInfo(LOG_TAG, "inputCodePoint(eventSource=" + eventSource + ", codePoint=" + codePoint + ", controlDownFromEvent=" + controlDownFromEvent + ", leftAltDownFromEvent="
|
||||
+ leftAltDownFromEvent + ")");
|
||||
@@ -856,7 +917,11 @@ public final class TerminalView extends View {
|
||||
mEmulator.setCursorBlinkState(true);
|
||||
|
||||
final boolean controlDown = controlDownFromEvent || mClient.readControlKey();
|
||||
final boolean altDown = leftAltDownFromEvent || mClient.readAltKey();
|
||||
// TITAN2 PATCH: if the kcm alt layer already produced the character (altConsumedByKcm),
|
||||
// do not also send the ESC alt-prefix.
|
||||
final boolean altDown = !altConsumedByKcm && (leftAltDownFromEvent || mClient.readAltKey());
|
||||
|
||||
mUserScrollLock = false; // TITAN2 PATCH: input returns the view to the live bottom
|
||||
|
||||
if (mClient.onCodePoint(codePoint, controlDown, mTermSession)) return;
|
||||
|
||||
@@ -921,6 +986,7 @@ public final class TerminalView extends View {
|
||||
String code = KeyHandler.getCode(keyCode, keyMod, term.isCursorKeysApplicationMode(), term.isKeypadApplicationMode());
|
||||
if (code == null) return false;
|
||||
mTermSession.write(code);
|
||||
mUserScrollLock = false; // TITAN2 PATCH: input returns the view to the live bottom
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -956,6 +1022,18 @@ public final class TerminalView extends View {
|
||||
if (TERMINAL_VIEW_KEY_LOGGING_ENABLED)
|
||||
mClient.logInfo(LOG_TAG, "onKeyUp(keyCode=" + keyCode + ", event=" + event + ")");
|
||||
|
||||
// TITAN2 PATCH: release latched hardware modifier state (see onKeyDown).
|
||||
switch (keyCode) {
|
||||
case KeyEvent.KEYCODE_SHIFT_LEFT:
|
||||
case KeyEvent.KEYCODE_SHIFT_RIGHT:
|
||||
mHardwareShiftDown = false;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_ALT_LEFT:
|
||||
case KeyEvent.KEYCODE_ALT_RIGHT:
|
||||
mHardwareAltDown = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Do not return for KEYCODE_BACK and send it to the client since user may be trying
|
||||
// to exit the activity.
|
||||
if (mEmulator == null && keyCode != KeyEvent.KEYCODE_BACK) return true;
|
||||
@@ -971,6 +1049,60 @@ public final class TerminalView extends View {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWindowFocusChanged(boolean hasWindowFocus) {
|
||||
super.onWindowFocusChanged(hasWindowFocus);
|
||||
// TITAN2 PATCH: drop latched hardware modifier state when focus is lost, in case
|
||||
// the keyup was delivered elsewhere (app switch, notification shade, dialog, ...).
|
||||
if (!hasWindowFocus) {
|
||||
mHardwareShiftDown = false;
|
||||
mHardwareAltDown = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TITAN2 PATCH: mirror of the @hide {@code KeyEvent.isSystemKey(int)}. Used to tell
|
||||
* real system keys (back, home, volume, ...) apart from the synthetic letter events
|
||||
* that the Titan 2 keyboard layer injects with FLAG_FROM_SYSTEM.
|
||||
*/
|
||||
private static boolean isRealSystemKey(int keyCode) {
|
||||
switch (keyCode) {
|
||||
case KeyEvent.KEYCODE_MENU:
|
||||
case KeyEvent.KEYCODE_SOFT_RIGHT:
|
||||
case KeyEvent.KEYCODE_HOME:
|
||||
case KeyEvent.KEYCODE_BACK:
|
||||
case KeyEvent.KEYCODE_CALL:
|
||||
case KeyEvent.KEYCODE_ENDCALL:
|
||||
case KeyEvent.KEYCODE_VOLUME_UP:
|
||||
case KeyEvent.KEYCODE_VOLUME_DOWN:
|
||||
case KeyEvent.KEYCODE_MUTE:
|
||||
case KeyEvent.KEYCODE_HEADSETHOOK:
|
||||
case KeyEvent.KEYCODE_MEDIA_PLAY:
|
||||
case KeyEvent.KEYCODE_MEDIA_PAUSE:
|
||||
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
|
||||
case KeyEvent.KEYCODE_MEDIA_STOP:
|
||||
case KeyEvent.KEYCODE_MEDIA_NEXT:
|
||||
case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
|
||||
case KeyEvent.KEYCODE_MEDIA_REWIND:
|
||||
case KeyEvent.KEYCODE_MEDIA_RECORD:
|
||||
case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
|
||||
case KeyEvent.KEYCODE_CAMERA:
|
||||
case KeyEvent.KEYCODE_FOCUS:
|
||||
case KeyEvent.KEYCODE_SEARCH:
|
||||
case KeyEvent.KEYCODE_BRIGHTNESS_DOWN:
|
||||
case KeyEvent.KEYCODE_BRIGHTNESS_UP:
|
||||
case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK:
|
||||
case KeyEvent.KEYCODE_SLEEP:
|
||||
case KeyEvent.KEYCODE_WAKEUP:
|
||||
case KeyEvent.KEYCODE_VOICE_ASSIST:
|
||||
case KeyEvent.KEYCODE_ASSIST:
|
||||
case KeyEvent.KEYCODE_APP_SWITCH:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called during layout when the size of this view has changed. If you were just added to the view
|
||||
* hierarchy, you're called with the old values of 0.
|
||||
@@ -1000,6 +1132,7 @@ public final class TerminalView extends View {
|
||||
mTerminalCursorBlinkerRunnable.setEmulator(mEmulator);
|
||||
|
||||
mTopRow = 0;
|
||||
mUserScrollLock = false; // TITAN2 PATCH
|
||||
scrollTo(0, 0);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user