From 6e6c20c450a2913c60f26d4f7cc1ac79497b58b4 Mon Sep 17 00:00:00 2001 From: alexytomi <60690056+alexytomi@users.noreply.github.com> Date: Thu, 24 Jul 2025 15:55:15 +0800 Subject: [PATCH] fix(Pointer/Capture): Make non-opportunistic grabbing opt-in Adds a new config option whether or not you want your mouse to be able to click your touch controls! Default is you can! Should also now let DeX users get their cursor out of the window by just moving it by default. This reverts commit 7631e3c9e70af03a294358bbfc7b809e21a1c690. --- .../main/java/net/kdt/pojavlaunch/Tools.java | 16 +++++++ .../mouse/AndroidPointerCapture.java | 47 +++++++++++++++++-- .../prefs/LauncherPreferences.java | 3 ++ .../prefs/QuickSettingSideDialog.java | 18 ++++++- .../main/res/layout/dialog_quick_setting.xml | 11 +++++ .../src/main/res/values/strings.xml | 2 + .../src/main/res/xml/pref_control.xml | 5 ++ 7 files changed, 96 insertions(+), 6 deletions(-) diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Tools.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Tools.java index 9a8b75ad6..84292159c 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Tools.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Tools.java @@ -35,6 +35,7 @@ import android.provider.OpenableColumns; import android.util.ArrayMap; import android.util.DisplayMetrics; import android.util.Log; +import android.view.InputDevice; import android.view.View; import android.view.WindowManager; import android.widget.EditText; @@ -1557,4 +1558,19 @@ public final class Tools { } return Integer.parseInt(iVersionArray[0] + iVersionArray[1] + iVersionArray[2]); } + + public static boolean isPointerDeviceConnected() { + int[] deviceIds = InputDevice.getDeviceIds(); + for (int id : deviceIds) { + InputDevice device = InputDevice.getDevice(id); + if (device == null) continue; + int sources = device.getSources(); + if ((sources & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE + || (sources & InputDevice.SOURCE_TOUCHPAD) == InputDevice.SOURCE_TOUCHPAD + || (sources & InputDevice.SOURCE_TRACKBALL) == InputDevice.SOURCE_TRACKBALL) { + return true; + } + } + return false; + } } diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/AndroidPointerCapture.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/AndroidPointerCapture.java index 38f6dd4ca..4e918cb3b 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/AndroidPointerCapture.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/AndroidPointerCapture.java @@ -1,13 +1,18 @@ package net.kdt.pojavlaunch.customcontrols.mouse; +import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_MOUSE_GRAB_FORCE; + +import android.content.SharedPreferences; import android.os.Build; import android.view.InputDevice; import android.view.MotionEvent; import android.view.View; import android.view.ViewTreeObserver; +import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; +import net.kdt.pojavlaunch.GrabListener; import net.kdt.pojavlaunch.MinecraftGLSurface; import net.kdt.pojavlaunch.Tools; import net.kdt.pojavlaunch.prefs.LauncherPreferences; @@ -15,7 +20,7 @@ import net.kdt.pojavlaunch.prefs.LauncherPreferences; import org.lwjgl.glfw.CallbackBridge; @RequiresApi(api = Build.VERSION_CODES.O) -public class AndroidPointerCapture implements ViewTreeObserver.OnWindowFocusChangeListener, View.OnCapturedPointerListener { +public class AndroidPointerCapture implements ViewTreeObserver.OnWindowFocusChangeListener, View.OnCapturedPointerListener, GrabListener, SharedPreferences.OnSharedPreferenceChangeListener { private static final float TOUCHPAD_SCROLL_THRESHOLD = 1; private final AbstractTouchpad mTouchpad; private final View mHostView; @@ -32,14 +37,44 @@ public class AndroidPointerCapture implements ViewTreeObserver.OnWindowFocusChan this.mHostView = hostView; hostView.setOnCapturedPointerListener(this); hostView.getViewTreeObserver().addOnWindowFocusChangeListener(this); + if (!PREF_MOUSE_GRAB_FORCE) + CallbackBridge.addGrabListener(this); } private void enableTouchpadIfNecessary() { - if(!mTouchpad.getDisplayState()) mTouchpad.enable(true); + if(!mTouchpad.getDisplayState() && !Tools.isPointerDeviceConnected()) mTouchpad.enable(true); + } + + // Needed so it releases the cursor when inside game menu + @Override + public void onGrabState(boolean isGrabbing) { + updateCursorState(isGrabbing); + } + // It's only here so the side-dialog changes it live + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, @Nullable String key) { + updateCursorState(CallbackBridge.isGrabbing()); + } + private void updateCursorState(boolean isGrabbing) { + if (!isGrabbing // Only capture if not in menu and user said so + && !PREF_MOUSE_GRAB_FORCE) { + mHostView.releasePointerCapture(); // Release the capture since user said so + } + // Capture the pointer when not inside a menu + // So user doesn't have to click to get rid of cursor + handleAutomaticCapture(); } public void handleAutomaticCapture() { - if(!mHostView.hasWindowFocus()) { + if (!CallbackBridge.isGrabbing() // Only capture if not in menu and user said so + && !PREF_MOUSE_GRAB_FORCE) { + return; + } + if (mHostView.hasPointerCapture()) { + enableTouchpadIfNecessary(); + return; + } + if (!mHostView.hasWindowFocus()) { mHostView.requestFocus(); } else { mHostView.requestPointerCapture(); @@ -128,7 +163,11 @@ public class AndroidPointerCapture implements ViewTreeObserver.OnWindowFocusChan @Override public void onWindowFocusChanged(boolean hasFocus) { - if(hasFocus && Tools.isAndroid8OrHigher()) mHostView.requestPointerCapture(); + if (!CallbackBridge.isGrabbing() // Only capture if not in menu and user said so + && !PREF_MOUSE_GRAB_FORCE) { + return; + } + if (hasFocus && Tools.isAndroid8OrHigher()) mHostView.requestPointerCapture(); } public void detach() { diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/LauncherPreferences.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/LauncherPreferences.java index dc4dd71ad..ab8c0cddd 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/LauncherPreferences.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/LauncherPreferences.java @@ -72,6 +72,8 @@ public class LauncherPreferences { public static boolean PREF_FORCE_ENABLE_TOUCHCONTROLLER = false; public static int PREF_TOUCHCONTROLLER_VIBRATE_LENGTH = 100; + public static boolean PREF_MOUSE_GRAB_FORCE = false; + public static void loadPreferences(Context ctx) { //Required for CTRLDEF_FILE and MultiRT @@ -114,6 +116,7 @@ public class LauncherPreferences { PREF_VSYNC_IN_ZINK = DEFAULT_PREF.getBoolean("vsync_in_zink", true); PREF_FORCE_ENABLE_TOUCHCONTROLLER = DEFAULT_PREF.getBoolean("forceEnableTouchController", false); PREF_TOUCHCONTROLLER_VIBRATE_LENGTH = DEFAULT_PREF.getInt("touchControllerVibrateLength", 100); + PREF_MOUSE_GRAB_FORCE = DEFAULT_PREF.getBoolean("always_grab_mouse", false); String argLwjglLibname = "-Dorg.lwjgl.opengl.libname="; for (String arg : JREUtils.parseJavaArguments(PREF_CUSTOM_JAVA_ARGS)) { diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/QuickSettingSideDialog.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/QuickSettingSideDialog.java index 5458dbf57..6a2b82dd1 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/QuickSettingSideDialog.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/QuickSettingSideDialog.java @@ -7,6 +7,7 @@ import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_GYRO_INVERT_Y; import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_GYRO_SENSITIVITY; import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_LONGPRESS_TRIGGER; import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_MOUSESPEED; +import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_MOUSE_GRAB_FORCE; import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_SCALE_FACTOR; import android.annotation.SuppressLint; @@ -31,11 +32,11 @@ public abstract class QuickSettingSideDialog extends com.kdt.SideDialogView { private SharedPreferences.Editor mEditor; @SuppressLint("UseSwitchCompatOrMaterialCode") - private Switch mGyroSwitch, mGyroXSwitch, mGyroYSwitch, mGestureSwitch; + private Switch mGyroSwitch, mGyroXSwitch, mGyroYSwitch, mGestureSwitch, mMouseGrabSwitch; private CustomSeekbar mGyroSensitivityBar, mMouseSpeedBar, mGestureDelayBar, mResolutionBar; private TextView mGyroSensitivityText, mGyroSensitivityDisplayText, mMouseSpeedText, mGestureDelayText, mGestureDelayDisplayText, mResolutionText; - private boolean mOriginalGyroEnabled, mOriginalGyroXEnabled, mOriginalGyroYEnabled, mOriginalGestureDisabled; + private boolean mOriginalGyroEnabled, mOriginalGyroXEnabled, mOriginalGyroYEnabled, mOriginalGestureDisabled, mOriginalMouseGrab; private float mOriginalGyroSensitivity, mOriginalMouseSpeed, mOriginalResolution; private int mOriginalGestureDelay; @@ -65,6 +66,7 @@ public abstract class QuickSettingSideDialog extends com.kdt.SideDialogView { mGyroXSwitch = mDialogContent.findViewById(R.id.checkboxGyroX); mGyroYSwitch = mDialogContent.findViewById(R.id.checkboxGyroY); mGestureSwitch = mDialogContent.findViewById(R.id.checkboxGesture); + mMouseGrabSwitch = mDialogContent.findViewById(R.id.always_grab_mouse_side_dialog); mGyroSensitivityBar = mDialogContent.findViewById(R.id.editGyro_seekbar); mMouseSpeedBar = mDialogContent.findViewById(R.id.editMouseSpeed_seekbar); @@ -86,6 +88,7 @@ public abstract class QuickSettingSideDialog extends com.kdt.SideDialogView { mOriginalGyroXEnabled = PREF_GYRO_INVERT_X; mOriginalGyroYEnabled = PREF_GYRO_INVERT_Y; mOriginalGestureDisabled = PREF_DISABLE_GESTURES; + mOriginalMouseGrab = PREF_MOUSE_GRAB_FORCE; mOriginalGyroSensitivity = PREF_GYRO_SENSITIVITY; mOriginalMouseSpeed = PREF_MOUSESPEED; @@ -122,6 +125,10 @@ public abstract class QuickSettingSideDialog extends com.kdt.SideDialogView { mEditor.putBoolean("disableGestures", isChecked); }); + mMouseGrabSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> { + mEditor.putBoolean("always_grab_mouse", isChecked); + }); + mGyroSensitivityBar.setOnSeekBarChangeListener((SimpleSeekBarListener) (seekBar, progress, fromUser) -> { PREF_GYRO_SENSITIVITY = progress / 100f; mEditor.putInt("gyroSensitivity", progress); @@ -156,6 +163,7 @@ public abstract class QuickSettingSideDialog extends com.kdt.SideDialogView { setSeekTextPercent(mResolutionText, mResolutionBar.getProgress()); + updateMouseGrabVisibility(); updateGyroVisibility(mOriginalGyroEnabled); updateGestureVisibility(mOriginalGestureDisabled); } @@ -172,6 +180,10 @@ public abstract class QuickSettingSideDialog extends com.kdt.SideDialogView { target.setText(target.getContext().getString(format, value)); } + private void updateMouseGrabVisibility(){ + mMouseGrabSwitch.setVisibility(Tools.isPointerDeviceConnected()? View.VISIBLE : View.GONE); + } + private void updateGyroVisibility(boolean isEnabled) { int visibility = isEnabled ? View.VISIBLE : View.GONE; mGyroXSwitch.setVisibility(visibility); @@ -202,6 +214,7 @@ public abstract class QuickSettingSideDialog extends com.kdt.SideDialogView { mGyroXSwitch.setOnCheckedChangeListener(null); mGyroYSwitch.setOnCheckedChangeListener(null); mGestureSwitch.setOnCheckedChangeListener(null); + mMouseGrabSwitch.setOnCheckedChangeListener(null); mGyroSensitivityBar.setOnSeekBarChangeListener(null); mMouseSpeedBar.setOnSeekBarChangeListener(null); @@ -225,6 +238,7 @@ public abstract class QuickSettingSideDialog extends com.kdt.SideDialogView { PREF_GYRO_INVERT_X = mOriginalGyroXEnabled; PREF_GYRO_INVERT_Y = mOriginalGyroYEnabled; PREF_DISABLE_GESTURES = mOriginalGestureDisabled; + PREF_MOUSE_GRAB_FORCE = mOriginalMouseGrab; PREF_GYRO_SENSITIVITY = mOriginalGyroSensitivity; PREF_MOUSESPEED = mOriginalMouseSpeed; diff --git a/app_pojavlauncher/src/main/res/layout/dialog_quick_setting.xml b/app_pojavlauncher/src/main/res/layout/dialog_quick_setting.xml index b85a83f7c..1d0ec167e 100644 --- a/app_pojavlauncher/src/main/res/layout/dialog_quick_setting.xml +++ b/app_pojavlauncher/src/main/res/layout/dialog_quick_setting.xml @@ -202,4 +202,15 @@ app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="@id/editGestureDelay_textView" /> + + \ No newline at end of file diff --git a/app_pojavlauncher/src/main/res/values/strings.xml b/app_pojavlauncher/src/main/res/values/strings.xml index 04a41f500..15ff64e01 100644 --- a/app_pojavlauncher/src/main/res/values/strings.xml +++ b/app_pojavlauncher/src/main/res/values/strings.xml @@ -171,6 +171,8 @@ Disables double tapping on the hotbar item to swap it in the second hand. Mouse Speed Changes the speed of the virtual mouse. + Use virtual cursor + Ensures the cursor stays inside the game. Prevents mouse from clicking touch control layout. Mouse pass-thru Swipeable Forward lock diff --git a/app_pojavlauncher/src/main/res/xml/pref_control.xml b/app_pojavlauncher/src/main/res/xml/pref_control.xml index 77c02f91c..0f2a7ec02 100644 --- a/app_pojavlauncher/src/main/res/xml/pref_control.xml +++ b/app_pojavlauncher/src/main/res/xml/pref_control.xml @@ -97,6 +97,11 @@ android:title="@string/preference_mouse_start_title" android:summary="@string/preference_mouse_start_description" /> +