Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions app/src/main/java/com/termux/app/TermuxActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,12 @@ public void onStart() {
public void onResume() {
super.onResume();

// Use the existing mTerminalView variable to get the current terminal view
TerminalView terminalView = mTerminalView;
if (terminalView != null) {
terminalView.reloadPreferences();
}

Logger.logVerbose(LOG_TAG, "onResume");

if (mIsInvalidState) return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {

setPreferencesFromResource(R.xml.termux_terminal_view_preferences, rootKey);
}

}

class TerminalViewPreferencesDataStore extends PreferenceDataStore {
Expand All @@ -46,16 +45,16 @@ public static synchronized TerminalViewPreferencesDataStore getInstance(Context
return mInstance;
}



@Override
public void putBoolean(String key, boolean value) {
if (mPreferences == null) return;
if (key == null) return;
if (mPreferences == null || key == null) return;

switch (key) {
case "terminal_margin_adjustment":
mPreferences.setTerminalMarginAdjustment(value);
mPreferences.setTerminalMarginAdjustment(value);
break;
case "mouse_cursor_movement_enabled":
mPreferences.setMouseCursorMovementEnabled(value);
break;
default:
break;
Expand All @@ -64,14 +63,15 @@ public void putBoolean(String key, boolean value) {

@Override
public boolean getBoolean(String key, boolean defValue) {
if (mPreferences == null) return false;
if (mPreferences == null) return defValue;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this catch though.


switch (key) {
case "terminal_margin_adjustment":
return mPreferences.isTerminalMarginAdjustmentEnabled();
case "mouse_cursor_movement_enabled":
return mPreferences.isMouseCursorMovementEnabled();
default:
return false;
return defValue;
}
}

}
1 change: 0 additions & 1 deletion app/src/main/res/xml/root_preferences.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,4 @@
app:title="@string/donate_preference_title"
app:persistent="false"
app:isPreferenceVisible="false"/>

</PreferenceScreen>
1 change: 0 additions & 1 deletion app/src/main/res/xml/termux_preferences.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,4 @@
app:title="@string/termux_terminal_view_preferences_title"
app:summary="@string/termux_terminal_view_preferences_summary"
app:fragment="com.termux.app.fragments.settings.termux.TerminalViewPreferencesFragment"/>

</PreferenceScreen>
6 changes: 6 additions & 0 deletions app/src/main/res/xml/termux_terminal_view_preferences.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@
app:summaryOn="@string/termux_terminal_view_terminal_margin_adjustment_on"
app:title="@string/termux_terminal_view_terminal_margin_adjustment_title" />

<SwitchPreferenceCompat
app:key="mouse_cursor_movement_enabled"
app:title="Move Cursor With Tap"
app:summaryOn="The cursor will move to the tapped location in the command line."
app:summaryOff="Standard terminal behavior (long-press to select text)."
app:defaultValue="false" />
</PreferenceCategory>

</PreferenceScreen>
1 change: 1 addition & 0 deletions terminal-view/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ android {
dependencies {
implementation "androidx.annotation:annotation:1.3.0"
api project(":terminal-emulator")
implementation "androidx.preference:preference:1.1.1"
}

defaultConfig {
Expand Down
57 changes: 54 additions & 3 deletions terminal-view/src/main/java/com/termux/view/TerminalView.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ public final class TerminalView extends View {
/** Our terminal emulator whose session is {@link #mTermSession}. */
public TerminalEmulator mEmulator;

private boolean mIsMouseCursorMovementEnabled = false;

public TerminalRenderer mRenderer;

public TerminalViewClient mClient;
Expand Down Expand Up @@ -295,6 +297,9 @@ public boolean attachSession(TerminalSession session) {
mEmulator = null;
mCombiningAccent = 0;

android.content.SharedPreferences prefs = androidx.preference.PreferenceManager.getDefaultSharedPreferences(getContext());
mIsMouseCursorMovementEnabled = prefs.getBoolean("mouse_cursor_movement_enabled", false);

Comment on lines +300 to +302
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Termux does not use androidx preferences. Probably it would be better to use existing termux.properties file and related code.

updateSize();

// Wait with enabling the scrollbar until we have a terminal to get scroll position from.
Expand Down Expand Up @@ -611,11 +616,11 @@ public boolean onTouchEvent(MotionEvent event) {
updateFloatingToolbarVisibility(event);
mGestureRecognizer.onTouchEvent(event);
return true;
} else if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
if (event.isButtonPressed(MotionEvent.BUTTON_SECONDARY)) {
} else if (event.isFromSource(InputDevice.SOURCE_MOUSE) || event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) {
if (event.isFromSource(InputDevice.SOURCE_MOUSE) && event.isButtonPressed(MotionEvent.BUTTON_SECONDARY)) {
if (action == MotionEvent.ACTION_DOWN) showContextMenu();
return true;
} else if (event.isButtonPressed(MotionEvent.BUTTON_TERTIARY)) {
} else if (event.isFromSource(InputDevice.SOURCE_MOUSE) && event.isButtonPressed(MotionEvent.BUTTON_TERTIARY)) {
ClipboardManager clipboardManager = (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clipData = clipboardManager.getPrimaryClip();
if (clipData != null) {
Expand All @@ -636,6 +641,47 @@ public boolean onTouchEvent(MotionEvent event) {
break;
}
}
else { // Mouse tracking is OFF, handle for the shell
if (mIsMouseCursorMovementEnabled && event.getAction() == MotionEvent.ACTION_DOWN) {
// Get target row and column from the click
int[] targetCoords = getColumnAndRow(event, false);
int targetColumn = targetCoords[0];
int targetRow = targetCoords[1];
// Boundary Check
try {
String clickedChar = mEmulator.getScreen().getSelectedText(targetColumn, targetRow, targetColumn + 1, targetRow).toString();
if (clickedChar == null || clickedChar.trim().isEmpty()) {
return true;
}
} catch (Exception e) {
return true;
}

// Get cursor's current visual position
int currentRow = mEmulator.getCursorRow();
int currentColumn = mEmulator.getCursorCol();

// Get the width of the terminal in characters
int terminalWidth = mEmulator.mColumns;

// Calculate the 1D index for both the click position and the cursor position.
int targetIndex = (targetRow * terminalWidth) + targetColumn;
int currentIndex = (currentRow * terminalWidth) + currentColumn;

int diff = targetIndex - currentIndex;

// Send Left or Right commands
if (diff > 0) { // Need to move right
for (int i = 0; i < diff; i++) {
handleKeyCode(android.view.KeyEvent.KEYCODE_DPAD_RIGHT, 0);
}
} else if (diff < 0) { // Need to move left
for (int i = 0; i < -diff; i++) {
handleKeyCode(android.view.KeyEvent.KEYCODE_DPAD_LEFT, 0);
}
}
Comment on lines +674 to +682
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like a bad idea to me. It will not work fine in the case if text editing widget is not as wide as screen (i.e. widget has borders or other decorations).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have tested this PR on my device Samsung Galaxy S7 SM-G930U.

This PR is working, however, as should be expected,

(and as also applies to the previous, similar PR):

this mode does work in regular shells like bash, fish,

but it does not work very well (but does a little bit) in curses or curses-like programs like nano.

that is why I asked for it to be disabled by default, and only enabled by a setting (which I can confirm, in the current version of this PR, the setting is not enabled by default)

}
}
}

mGestureRecognizer.onTouchEvent(event);
Expand Down Expand Up @@ -1495,4 +1541,9 @@ public void updateFloatingToolbarVisibility(MotionEvent event) {
}
}

public void reloadPreferences() {
android.content.SharedPreferences prefs = androidx.preference.PreferenceManager.getDefaultSharedPreferences(getContext());
mIsMouseCursorMovementEnabled = prefs.getBoolean("mouse_cursor_movement_enabled", false);
}

Comment on lines +1544 to +1548
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I said before, it will be better to use termux.properties for this.

Copy link
Member

@robertkirkman robertkirkman Sep 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, however, it might be relevant to mention that currently this preexisting setting:

image

and this preexisting setting:

image

both do very similar (but subtly different) things, and there appears to currently be no way to configure the first setting purely from termux.properties, or vice versa, the second setting purely from the GUI,

therefore, if either setting is modified from the default, software keyboard will not work, and both must be set to the default before software keyboard works again.

this might be a different topic from what this PR is about, but this summary explains why I didn't mention termux.properties in my suggestion - it is because the currently-existing settings are confusing, so there is not really, as far as I can see, a good example to follow.

Copy link
Member

@agnostic-apollo agnostic-apollo Oct 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The setting in PR should go in properties, but in my local changes, both props and prefs have been rewritten, so would be moot to work on it currently.

The first setting for soft keyboard enabled is in settings because if it was in properties file and soft keyboard gets disabled, you wouldn't be able to enable it again without an external keyboard to type into the terminal to open the properties file, hence its in a UI. The later hide property only hides soft keyboard at startup and tapping screen brings it up again.

Eventually all settings will be moved to properties or yaml file and shown in a UI, but that's in distant future.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The later hide property only hides soft keyboard at startup and tapping screen brings it up again.

In my testing, both settings caused the software keyboard to remain closed and simply tapping the screen did not make it appear on my device if either setting was changed from its default. The subtle difference between them I mentioned is something else that is very subtle and harder to explain. However, the problem of the "hide-soft-keyboard-on-startup" setting making the software keyboard difficult to access because of it no longer appearing when touching the Termux Activity might be a bug that isn't present in your rewritten version.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My rewritten solution doesn't make any changes for keyboard.

Hide on startup only hides keyboard and should show on tap, while disable setting, completely disables it until toggled with keyboard key or setting. You likely need to restart termux app after making changes for them to properly take effect. Soft keyboard enabled should be enabled, and hide set to true, and then restart.

What does it take for keyboard to appear again with hide?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you are correct, when I test again I am unable to reproduce the problem I saw before on any of my devices, so I must have been wrong. I don't know what happened because I thought that I set everything to default and restarted Termux using the exit command in between tests. thank you for pointing out what the actual behavior is.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Welcome and thanks for the confirmation for saving me work, there was a chance there could have been a bug on some devices. Android does not provide any API to check if keyboard is open or not, so termux code for it is very hacky, which could have caused problems, but it was well tested before I merged it and I haven't received any other reports. I tend not to touch that code.

}
Original file line number Diff line number Diff line change
Expand Up @@ -258,4 +258,14 @@ public void setCrashReportNotificationsEnabled(boolean value) {
SharedPreferenceUtils.setBoolean(mSharedPreferences, TERMUX_APP.KEY_CRASH_REPORT_NOTIFICATIONS_ENABLED, value, false);
}

// Method to READ the setting
public boolean isMouseCursorMovementEnabled() {
return SharedPreferenceUtils.getBoolean(mSharedPreferences, "mouse_cursor_movement_enabled", false);
}

// Method to SAVE the setting
public void setMouseCursorMovementEnabled(boolean value) {
SharedPreferenceUtils.setBoolean(mSharedPreferences, "mouse_cursor_movement_enabled", value, false);
}

}