diff --git a/platform/android/android_input_handler.cpp b/platform/android/android_input_handler.cpp index 4506d4ca68b..3489fa73ed2 100644 --- a/platform/android/android_input_handler.cpp +++ b/platform/android/android_input_handler.cpp @@ -207,7 +207,7 @@ void AndroidInputHandler::process_touch_event(int p_event, int p_pointer, const } } -void AndroidInputHandler::_parse_mouse_event_info(int buttons_mask, bool p_pressed, bool p_double_click) { +void AndroidInputHandler::_parse_mouse_event_info(int buttons_mask, bool p_pressed, bool p_double_click, bool p_source_mouse_relative) { if (!mouse_event_info.valid) { return; } @@ -215,8 +215,14 @@ void AndroidInputHandler::_parse_mouse_event_info(int buttons_mask, bool p_press Ref ev; ev.instance(); _set_key_modifier_state(ev); - ev->set_position(mouse_event_info.pos); - ev->set_global_position(mouse_event_info.pos); + if (p_source_mouse_relative) { + ev->set_position(hover_prev_pos); + ev->set_global_position(hover_prev_pos); + } else { + ev->set_position(mouse_event_info.pos); + ev->set_global_position(mouse_event_info.pos); + hover_prev_pos = mouse_event_info.pos; + } ev->set_pressed(p_pressed); int changed_button_mask = buttons_state ^ buttons_mask; @@ -226,15 +232,14 @@ void AndroidInputHandler::_parse_mouse_event_info(int buttons_mask, bool p_press ev->set_button_mask(buttons_mask); ev->set_doubleclick(p_double_click); input->parse_input_event(ev); - hover_prev_pos = mouse_event_info.pos; } -void AndroidInputHandler::_release_mouse_event_info() { - _parse_mouse_event_info(0, false, false); +void AndroidInputHandler::_release_mouse_event_info(bool p_source_mouse_relative) { + _parse_mouse_event_info(0, false, false, p_source_mouse_relative); mouse_event_info.valid = false; } -void AndroidInputHandler::process_mouse_event(int p_event_action, int p_event_android_buttons_mask, Point2 p_event_pos, Vector2 p_delta, bool p_double_click) { +void AndroidInputHandler::process_mouse_event(int p_event_action, int p_event_android_buttons_mask, Point2 p_event_pos, Vector2 p_delta, bool p_double_click, bool p_source_mouse_relative) { int event_buttons_mask = _android_button_mask_to_godot_button_mask(p_event_android_buttons_mask); switch (p_event_action) { case AMOTION_EVENT_ACTION_HOVER_MOVE: // hover move @@ -259,13 +264,13 @@ void AndroidInputHandler::process_mouse_event(int p_event_action, int p_event_an mouse_event_info.valid = true; mouse_event_info.pos = p_event_pos; - _parse_mouse_event_info(event_buttons_mask, true, p_double_click); + _parse_mouse_event_info(event_buttons_mask, true, p_double_click, p_source_mouse_relative); } break; case AMOTION_EVENT_ACTION_UP: case AMOTION_EVENT_ACTION_CANCEL: case AMOTION_EVENT_ACTION_BUTTON_RELEASE: { - _release_mouse_event_info(); + _release_mouse_event_info(p_source_mouse_relative); } break; case AMOTION_EVENT_ACTION_MOVE: { @@ -276,21 +281,32 @@ void AndroidInputHandler::process_mouse_event(int p_event_action, int p_event_an Ref ev; ev.instance(); _set_key_modifier_state(ev); - ev->set_position(p_event_pos); - ev->set_global_position(p_event_pos); - ev->set_relative(p_event_pos - hover_prev_pos); + if (p_source_mouse_relative) { + ev->set_position(hover_prev_pos); + ev->set_global_position(hover_prev_pos); + ev->set_relative(p_event_pos); + } else { + ev->set_position(p_event_pos); + ev->set_global_position(p_event_pos); + ev->set_relative(p_event_pos - hover_prev_pos); + mouse_event_info.pos = p_event_pos; + hover_prev_pos = p_event_pos; + } ev->set_button_mask(event_buttons_mask); input->parse_input_event(ev); - mouse_event_info.pos = p_event_pos; - hover_prev_pos = p_event_pos; } break; case AMOTION_EVENT_ACTION_SCROLL: { Ref ev; ev.instance(); _set_key_modifier_state(ev); - ev->set_position(p_event_pos); - ev->set_global_position(p_event_pos); + if (p_source_mouse_relative) { + ev->set_position(hover_prev_pos); + ev->set_global_position(hover_prev_pos); + } else { + ev->set_position(p_event_pos); + ev->set_global_position(p_event_pos); + } ev->set_pressed(true); buttons_state = event_buttons_mask; if (p_delta.y > 0) { diff --git a/platform/android/android_input_handler.h b/platform/android/android_input_handler.h index ac4d6927dc5..fd0b858259e 100644 --- a/platform/android/android_input_handler.h +++ b/platform/android/android_input_handler.h @@ -86,9 +86,9 @@ private: void _wheel_button_click(int event_buttons_mask, const Ref &ev, int wheel_button, float factor); - void _parse_mouse_event_info(int buttons_mask, bool p_pressed, bool p_double_click); + void _parse_mouse_event_info(int buttons_mask, bool p_pressed, bool p_double_click, bool p_source_mouse_relative); - void _release_mouse_event_info(); + void _release_mouse_event_info(bool p_source_mouse_relative = false); void _parse_all_touch(bool p_pressed, bool p_double_tap); @@ -97,7 +97,7 @@ private: public: void process_joy_event(const JoypadEvent &p_event); void process_key_event(int p_scancode, int p_physical_scancode, int p_unicode, bool p_pressed); - void process_mouse_event(int p_event_action, int p_event_android_buttons_mask, Point2 p_event_pos, Vector2 p_delta, bool p_double_click); + void process_mouse_event(int p_event_action, int p_event_android_buttons_mask, Point2 p_event_pos, Vector2 p_delta, bool p_double_click, bool p_source_mouse_relative); void process_touch_event(int p_event, int p_pointer, const Vector &p_points, bool p_double_tap); void process_magnify(Point2 p_pos, float p_factor); void process_pan(Point2 p_pos, Vector2 p_delta); diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java index f1e054050fe..b3c7d75ed72 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java @@ -105,7 +105,7 @@ public class GodotLib { /** * Dispatch mouse events */ - public static native void dispatchMouseEvent(int event, int buttonMask, float x, float y, float deltaX, float deltaY, boolean doubleClick); + public static native void dispatchMouseEvent(int event, int buttonMask, float x, float y, float deltaX, float deltaY, boolean doubleClick, boolean sourceMouseRelative); public static native void magnify(float x, float y, float factor); diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotView.java index de5a86a397a..2c552acd01e 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotView.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotView.java @@ -126,6 +126,29 @@ public class GodotView extends GLSurfaceView { return inputHandler.onGenericMotionEvent(event) || super.onGenericMotionEvent(event); } + @Override + public boolean onCapturedPointerEvent(MotionEvent event) { + return inputHandler.onGenericMotionEvent(event); + } + + @Override + public void onPointerCaptureChange(boolean hasCapture) { + super.onPointerCaptureChange(hasCapture); + inputHandler.onPointerCaptureChange(hasCapture); + } + + @Override + public void requestPointerCapture() { + super.requestPointerCapture(); + inputHandler.onPointerCaptureChange(true); + } + + @Override + public void releasePointerCapture() { + super.releasePointerCapture(); + inputHandler.onPointerCaptureChange(false); + } + /** * Called from JNI to change the pointer icon */ diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java index d2350a635d7..e741ff7fdce 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java @@ -131,7 +131,9 @@ public class GodotEditText extends EditText { edit.setText(""); edit.append(text); if (msg.arg2 != -1) { - edit.setSelection(msg.arg1, msg.arg2); + int selectionStart = Math.min(msg.arg1, edit.length()); + int selectionEnd = Math.min(msg.arg2, edit.length()); + edit.setSelection(selectionStart, selectionEnd); edit.mInputWrapper.setSelection(true); } else { edit.mInputWrapper.setSelection(false); diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt index 82df9264b6b..cde8d7cdae6 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt @@ -30,7 +30,9 @@ package org.godotengine.godot.input +import android.os.Build import android.view.GestureDetector.SimpleOnGestureListener +import android.view.InputDevice import android.view.MotionEvent import android.view.ScaleGestureDetector import android.view.ScaleGestureDetector.OnScaleGestureListener @@ -57,6 +59,7 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi private var dragInProgress = false private var scaleInProgress = false private var contextClickInProgress = false + private var pointerCaptureInProgress = false override fun onDown(event: MotionEvent): Boolean { GodotInputHandler.handleMotionEvent(event.source, MotionEvent.ACTION_DOWN, event.buttonState, event.x, event.y, nextDownIsDoubleTap) @@ -74,7 +77,7 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi } private fun contextClickRouter(event: MotionEvent) { - if (scaleInProgress) { + if (scaleInProgress || nextDownIsDoubleTap) { return } @@ -97,6 +100,27 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi contextClickInProgress = true } + fun onPointerCaptureChange(hasCapture: Boolean) { + if (pointerCaptureInProgress == hasCapture) { + return + } + + if (!hasCapture) { + // Dispatch a mouse relative ACTION_UP event to signal the end of the capture + GodotInputHandler.handleMouseEvent( + MotionEvent.ACTION_UP, + 0, + 0f, + 0f, + 0f, + 0f, + false, + true + ) + } + pointerCaptureInProgress = hasCapture + } + fun onMotionEvent(event: MotionEvent): Boolean { return when (event.actionMasked) { MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_BUTTON_RELEASE -> { @@ -110,30 +134,59 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi } private fun onActionUp(event: MotionEvent): Boolean { - if (dragInProgress) { - GodotInputHandler.handleMotionEvent(event) - dragInProgress = false + if (event.actionMasked == MotionEvent.ACTION_CANCEL && pointerCaptureInProgress) { + // Don't dispatch the ACTION_CANCEL while a capture is in progress return true - } else if (contextClickInProgress) { - GodotInputHandler.handleMouseEvent( - event.actionMasked, - 0, - event.x, - event.y - ) + } + + val sourceMouseRelative = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + event.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE) + } else { + false + } + + if (pointerCaptureInProgress || dragInProgress || contextClickInProgress) { + if (contextClickInProgress || GodotInputHandler.isMouseEvent(event)) { + // This may be an ACTION_BUTTON_RELEASE event which we don't handle, + // so we convert it to an ACTION_UP event. + GodotInputHandler.handleMouseEvent( + MotionEvent.ACTION_UP, + event.buttonState, + event.x, + event.y, + 0f, + 0f, + false, + sourceMouseRelative + ) + } else { + GodotInputHandler.handleTouchEvent(event) + } + pointerCaptureInProgress = false + dragInProgress = false contextClickInProgress = false return true } + return false } private fun onActionMove(event: MotionEvent): Boolean { if (contextClickInProgress) { + val sourceMouseRelative = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + event.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE) + } else { + false + } GodotInputHandler.handleMouseEvent( event.actionMasked, MotionEvent.BUTTON_SECONDARY, event.x, - event.y + event.y, + 0f, + 0f, + false, + sourceMouseRelative ) return true } @@ -178,7 +231,7 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi val x = terminusEvent.x val y = terminusEvent.y - if (terminusEvent.pointerCount >= 2 && panningAndScalingEnabled) { + if (terminusEvent.pointerCount >= 2 && panningAndScalingEnabled && !pointerCaptureInProgress) { GodotLib.pan(x, y, distanceX / 5f, distanceY / 5f) } else { GodotInputHandler.handleMotionEvent(terminusEvent) @@ -187,7 +240,7 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi } override fun onScale(detector: ScaleGestureDetector?): Boolean { - if (detector == null || !panningAndScalingEnabled) { + if (detector == null || !panningAndScalingEnabled || pointerCaptureInProgress) { return false } GodotLib.magnify( @@ -199,7 +252,7 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi } override fun onScaleBegin(detector: ScaleGestureDetector?): Boolean { - if (detector == null || !panningAndScalingEnabled) { + if (detector == null || !panningAndScalingEnabled || pointerCaptureInProgress) { return false } scaleInProgress = true diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java index 3fbf8667ae6..53a9d1a2607 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java @@ -105,6 +105,10 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { return (source & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK || (source & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD || (source & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD; } + public void onPointerCaptureChange(boolean hasCapture) { + godotGestureHandler.onPointerCaptureChange(hasCapture); + } + public boolean onKeyUp(final int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { return true; @@ -202,6 +206,11 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { return true; } + if (godotGestureHandler.onMotionEvent(event)) { + // The gesture handler has handled the event. + return true; + } + if (event.isFromSource(InputDevice.SOURCE_JOYSTICK) && event.getActionMasked() == MotionEvent.ACTION_MOVE) { // Check if the device exists final int deviceId = event.getDeviceId(); @@ -237,7 +246,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { } return true; } - } else if (isMouseEvent(event)) { + } else { return handleMouseEvent(event); } @@ -414,7 +423,11 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { } private static boolean isMouseEvent(int eventSource) { - return ((eventSource & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) || ((eventSource & InputDevice.SOURCE_STYLUS) == InputDevice.SOURCE_STYLUS); + boolean mouseSource = ((eventSource & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) || ((eventSource & InputDevice.SOURCE_STYLUS) == InputDevice.SOURCE_STYLUS); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + mouseSource = mouseSource || ((eventSource & InputDevice.SOURCE_MOUSE_RELATIVE) == InputDevice.SOURCE_MOUSE_RELATIVE); + } + return mouseSource; } static boolean handleMotionEvent(final MotionEvent event) { @@ -435,7 +448,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { static boolean handleMotionEvent(int eventSource, int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY, boolean doubleTap) { if (isMouseEvent(eventSource)) { - return handleMouseEvent(eventAction, buttonsMask, x, y, deltaX, deltaY, doubleTap); + return handleMouseEvent(eventAction, buttonsMask, x, y, deltaX, deltaY, doubleTap, false); } return handleTouchEvent(eventAction, x, y, doubleTap); @@ -449,14 +462,25 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { final float verticalFactor = event.getAxisValue(MotionEvent.AXIS_VSCROLL); final float horizontalFactor = event.getAxisValue(MotionEvent.AXIS_HSCROLL); - return handleMouseEvent(eventAction, buttonsMask, x, y, horizontalFactor, verticalFactor, false); + boolean sourceMouseRelative = false; + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + sourceMouseRelative = event.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE); + } + return handleMouseEvent(eventAction, buttonsMask, x, y, horizontalFactor, verticalFactor, false, sourceMouseRelative); } static boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y) { - return handleMouseEvent(eventAction, buttonsMask, x, y, 0, 0, false); + return handleMouseEvent(eventAction, buttonsMask, x, y, 0, 0, false, false); } - static boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY, boolean doubleClick) { + static boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y, boolean doubleClick) { + return handleMouseEvent(eventAction, buttonsMask, x, y, 0, 0, doubleClick, false); + } + + static boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY, boolean doubleClick, boolean sourceMouseRelative) { + // We don't handle ACTION_BUTTON_PRESS and ACTION_BUTTON_RELEASE events as they typically + // follow ACTION_DOWN and ACTION_UP events. As such, handling them would result in duplicate + // stream of events to the engine. switch (eventAction) { case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: @@ -469,7 +493,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { case MotionEvent.ACTION_HOVER_MOVE: case MotionEvent.ACTION_MOVE: case MotionEvent.ACTION_SCROLL: { - GodotLib.dispatchMouseEvent(eventAction, buttonsMask, x, y, deltaX, deltaY, doubleClick); + GodotLib.dispatchMouseEvent(eventAction, buttonsMask, x, y, deltaX, deltaY, doubleClick, sourceMouseRelative); return true; } } diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp index 7a2d34d79f5..5ee3e6cee23 100644 --- a/platform/android/java_godot_lib_jni.cpp +++ b/platform/android/java_godot_lib_jni.cpp @@ -283,12 +283,12 @@ JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, } // Called on the UI thread -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchMouseEvent(JNIEnv *env, jclass clazz, jint p_event_type, jint p_button_mask, jfloat p_x, jfloat p_y, jfloat p_delta_x, jfloat p_delta_y, jboolean p_double_click) { +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchMouseEvent(JNIEnv *env, jclass clazz, jint p_event_type, jint p_button_mask, jfloat p_x, jfloat p_y, jfloat p_delta_x, jfloat p_delta_y, jboolean p_double_click, jboolean p_source_mouse_relative) { if (step.get() <= 0) { return; } - input_handler->process_mouse_event(p_event_type, p_button_mask, Point2(p_x, p_y), Vector2(p_delta_x, p_delta_y), p_double_click); + input_handler->process_mouse_event(p_event_type, p_button_mask, Point2(p_x, p_y), Vector2(p_delta_x, p_delta_y), p_double_click, p_source_mouse_relative); } // Called on the UI thread diff --git a/platform/android/java_godot_lib_jni.h b/platform/android/java_godot_lib_jni.h index 89f3fd5e86a..a3580783623 100644 --- a/platform/android/java_godot_lib_jni.h +++ b/platform/android/java_godot_lib_jni.h @@ -45,7 +45,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *en JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ttsCallback(JNIEnv *env, jclass clazz, jint event, jint id, jint pos); JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jclass clazz); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jclass clazz); -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchMouseEvent(JNIEnv *env, jclass clazz, jint p_event_type, jint p_button_mask, jfloat p_x, jfloat p_y, jfloat p_delta_x, jfloat p_delta_y, jboolean p_double_click); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchMouseEvent(JNIEnv *env, jclass clazz, jint p_event_type, jint p_button_mask, jfloat p_x, jfloat p_y, jfloat p_delta_x, jfloat p_delta_y, jboolean p_double_click, jboolean p_source_mouse_relative); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchTouchEvent(JNIEnv *env, jclass clazz, jint ev, jint pointer, jint pointer_count, jfloatArray positions, jboolean p_double_tap); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_magnify(JNIEnv *env, jclass clazz, jfloat p_x, jfloat p_y, jfloat p_factor); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_pan(JNIEnv *env, jclass clazz, jfloat p_x, jfloat p_y, jfloat p_delta_x, jfloat p_delta_y); diff --git a/platform/android/java_godot_view_wrapper.cpp b/platform/android/java_godot_view_wrapper.cpp index ad647f87474..8a8520c0c7d 100644 --- a/platform/android/java_godot_view_wrapper.cpp +++ b/platform/android/java_godot_view_wrapper.cpp @@ -42,12 +42,38 @@ GodotJavaViewWrapper::GodotJavaViewWrapper(jobject godot_view) { if (android_device_api_level >= __ANDROID_API_N__) { _set_pointer_icon = env->GetMethodID(_cls, "setPointerIcon", "(I)V"); } + if (android_device_api_level >= __ANDROID_API_O__) { + _request_pointer_capture = env->GetMethodID(_cls, "requestPointerCapture", "()V"); + _release_pointer_capture = env->GetMethodID(_cls, "releasePointerCapture", "()V"); + } } bool GodotJavaViewWrapper::can_update_pointer_icon() const { return _set_pointer_icon != nullptr; } +bool GodotJavaViewWrapper::can_capture_pointer() const { + return _request_pointer_capture != nullptr && _release_pointer_capture != nullptr; +} + +void GodotJavaViewWrapper::request_pointer_capture() { + if (_request_pointer_capture != nullptr) { + JNIEnv *env = get_jni_env(); + ERR_FAIL_NULL(env); + + env->CallVoidMethod(_godot_view, _request_pointer_capture); + } +} + +void GodotJavaViewWrapper::release_pointer_capture() { + if (_release_pointer_capture != nullptr) { + JNIEnv *env = get_jni_env(); + ERR_FAIL_NULL(env); + + env->CallVoidMethod(_godot_view, _release_pointer_capture); + } +} + void GodotJavaViewWrapper::set_pointer_icon(int pointer_type) { if (_set_pointer_icon != nullptr) { JNIEnv *env = get_jni_env(); diff --git a/platform/android/java_godot_view_wrapper.h b/platform/android/java_godot_view_wrapper.h index 5c7e15735f3..0ad765462d7 100644 --- a/platform/android/java_godot_view_wrapper.h +++ b/platform/android/java_godot_view_wrapper.h @@ -41,12 +41,19 @@ class GodotJavaViewWrapper { private: jclass _cls; jobject _godot_view; + + jmethodID _request_pointer_capture = 0; + jmethodID _release_pointer_capture = 0; jmethodID _set_pointer_icon = 0; public: GodotJavaViewWrapper(jobject godot_view); bool can_update_pointer_icon() const; + bool can_capture_pointer() const; + + void request_pointer_capture(); + void release_pointer_capture(); void set_pointer_icon(int pointer_type); ~GodotJavaViewWrapper(); diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp index 3aaec945e95..c2a5f979835 100644 --- a/platform/android/os_android.cpp +++ b/platform/android/os_android.cpp @@ -274,10 +274,10 @@ Error OS_Android::open_dynamic_library(const String p_path, void *&p_library_han } void OS_Android::set_mouse_mode(MouseMode p_mode) { - if (!godot_java->get_godot_view()->can_update_pointer_icon()) { + if (!godot_java->get_godot_view()->can_update_pointer_icon() || !godot_java->get_godot_view()->can_capture_pointer()) { return; } - if (mouse_mode == p_mode || p_mode == MouseMode::MOUSE_MODE_CAPTURED) { + if (mouse_mode == p_mode) { return; } @@ -287,6 +287,12 @@ void OS_Android::set_mouse_mode(MouseMode p_mode) { set_cursor_shape(cursor_shape); } + if (p_mode == MouseMode::MOUSE_MODE_CAPTURED) { + godot_java->get_godot_view()->request_pointer_capture(); + } else { + godot_java->get_godot_view()->release_pointer_capture(); + } + mouse_mode = p_mode; }