Add support for pointer capture

This commit is contained in:
Fredia Huya-Kouadio 2022-10-20 07:16:29 -07:00
parent 92aedd5063
commit 5149311316
12 changed files with 205 additions and 48 deletions

View file

@ -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) { if (!mouse_event_info.valid) {
return; return;
} }
@ -215,8 +215,14 @@ void AndroidInputHandler::_parse_mouse_event_info(int buttons_mask, bool p_press
Ref<InputEventMouseButton> ev; Ref<InputEventMouseButton> ev;
ev.instance(); ev.instance();
_set_key_modifier_state(ev); _set_key_modifier_state(ev);
ev->set_position(mouse_event_info.pos); if (p_source_mouse_relative) {
ev->set_global_position(mouse_event_info.pos); 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); ev->set_pressed(p_pressed);
int changed_button_mask = buttons_state ^ buttons_mask; 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_button_mask(buttons_mask);
ev->set_doubleclick(p_double_click); ev->set_doubleclick(p_double_click);
input->parse_input_event(ev); input->parse_input_event(ev);
hover_prev_pos = mouse_event_info.pos;
} }
void AndroidInputHandler::_release_mouse_event_info() { void AndroidInputHandler::_release_mouse_event_info(bool p_source_mouse_relative) {
_parse_mouse_event_info(0, false, false); _parse_mouse_event_info(0, false, false, p_source_mouse_relative);
mouse_event_info.valid = false; 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); int event_buttons_mask = _android_button_mask_to_godot_button_mask(p_event_android_buttons_mask);
switch (p_event_action) { switch (p_event_action) {
case AMOTION_EVENT_ACTION_HOVER_MOVE: // hover move 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.valid = true;
mouse_event_info.pos = p_event_pos; 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; } break;
case AMOTION_EVENT_ACTION_UP: case AMOTION_EVENT_ACTION_UP:
case AMOTION_EVENT_ACTION_CANCEL: case AMOTION_EVENT_ACTION_CANCEL:
case AMOTION_EVENT_ACTION_BUTTON_RELEASE: { case AMOTION_EVENT_ACTION_BUTTON_RELEASE: {
_release_mouse_event_info(); _release_mouse_event_info(p_source_mouse_relative);
} break; } break;
case AMOTION_EVENT_ACTION_MOVE: { case AMOTION_EVENT_ACTION_MOVE: {
@ -276,21 +281,32 @@ void AndroidInputHandler::process_mouse_event(int p_event_action, int p_event_an
Ref<InputEventMouseMotion> ev; Ref<InputEventMouseMotion> ev;
ev.instance(); ev.instance();
_set_key_modifier_state(ev); _set_key_modifier_state(ev);
ev->set_position(p_event_pos); if (p_source_mouse_relative) {
ev->set_global_position(p_event_pos); ev->set_position(hover_prev_pos);
ev->set_relative(p_event_pos - 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); ev->set_button_mask(event_buttons_mask);
input->parse_input_event(ev); input->parse_input_event(ev);
mouse_event_info.pos = p_event_pos;
hover_prev_pos = p_event_pos;
} break; } break;
case AMOTION_EVENT_ACTION_SCROLL: { case AMOTION_EVENT_ACTION_SCROLL: {
Ref<InputEventMouseButton> ev; Ref<InputEventMouseButton> ev;
ev.instance(); ev.instance();
_set_key_modifier_state(ev); _set_key_modifier_state(ev);
ev->set_position(p_event_pos); if (p_source_mouse_relative) {
ev->set_global_position(p_event_pos); 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); ev->set_pressed(true);
buttons_state = event_buttons_mask; buttons_state = event_buttons_mask;
if (p_delta.y > 0) { if (p_delta.y > 0) {

View file

@ -86,9 +86,9 @@ private:
void _wheel_button_click(int event_buttons_mask, const Ref<InputEventMouseButton> &ev, int wheel_button, float factor); void _wheel_button_click(int event_buttons_mask, const Ref<InputEventMouseButton> &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); void _parse_all_touch(bool p_pressed, bool p_double_tap);
@ -97,7 +97,7 @@ private:
public: public:
void process_joy_event(const JoypadEvent &p_event); 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_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<TouchPos> &p_points, bool p_double_tap); void process_touch_event(int p_event, int p_pointer, const Vector<TouchPos> &p_points, bool p_double_tap);
void process_magnify(Point2 p_pos, float p_factor); void process_magnify(Point2 p_pos, float p_factor);
void process_pan(Point2 p_pos, Vector2 p_delta); void process_pan(Point2 p_pos, Vector2 p_delta);

View file

@ -105,7 +105,7 @@ public class GodotLib {
/** /**
* Dispatch mouse events * 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); public static native void magnify(float x, float y, float factor);

View file

@ -126,6 +126,29 @@ public class GodotView extends GLSurfaceView {
return inputHandler.onGenericMotionEvent(event) || super.onGenericMotionEvent(event); 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 * Called from JNI to change the pointer icon
*/ */

View file

@ -131,7 +131,9 @@ public class GodotEditText extends EditText {
edit.setText(""); edit.setText("");
edit.append(text); edit.append(text);
if (msg.arg2 != -1) { 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); edit.mInputWrapper.setSelection(true);
} else { } else {
edit.mInputWrapper.setSelection(false); edit.mInputWrapper.setSelection(false);

View file

@ -30,7 +30,9 @@
package org.godotengine.godot.input package org.godotengine.godot.input
import android.os.Build
import android.view.GestureDetector.SimpleOnGestureListener import android.view.GestureDetector.SimpleOnGestureListener
import android.view.InputDevice
import android.view.MotionEvent import android.view.MotionEvent
import android.view.ScaleGestureDetector import android.view.ScaleGestureDetector
import android.view.ScaleGestureDetector.OnScaleGestureListener import android.view.ScaleGestureDetector.OnScaleGestureListener
@ -57,6 +59,7 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi
private var dragInProgress = false private var dragInProgress = false
private var scaleInProgress = false private var scaleInProgress = false
private var contextClickInProgress = false private var contextClickInProgress = false
private var pointerCaptureInProgress = false
override fun onDown(event: MotionEvent): Boolean { override fun onDown(event: MotionEvent): Boolean {
GodotInputHandler.handleMotionEvent(event.source, MotionEvent.ACTION_DOWN, event.buttonState, event.x, event.y, nextDownIsDoubleTap) 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) { private fun contextClickRouter(event: MotionEvent) {
if (scaleInProgress) { if (scaleInProgress || nextDownIsDoubleTap) {
return return
} }
@ -97,6 +100,27 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi
contextClickInProgress = true 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 { fun onMotionEvent(event: MotionEvent): Boolean {
return when (event.actionMasked) { return when (event.actionMasked) {
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_BUTTON_RELEASE -> { 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 { private fun onActionUp(event: MotionEvent): Boolean {
if (dragInProgress) { if (event.actionMasked == MotionEvent.ACTION_CANCEL && pointerCaptureInProgress) {
GodotInputHandler.handleMotionEvent(event) // Don't dispatch the ACTION_CANCEL while a capture is in progress
dragInProgress = false
return true return true
} else if (contextClickInProgress) { }
GodotInputHandler.handleMouseEvent(
event.actionMasked, val sourceMouseRelative = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
0, event.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE)
event.x, } else {
event.y 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 contextClickInProgress = false
return true return true
} }
return false return false
} }
private fun onActionMove(event: MotionEvent): Boolean { private fun onActionMove(event: MotionEvent): Boolean {
if (contextClickInProgress) { if (contextClickInProgress) {
val sourceMouseRelative = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
event.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE)
} else {
false
}
GodotInputHandler.handleMouseEvent( GodotInputHandler.handleMouseEvent(
event.actionMasked, event.actionMasked,
MotionEvent.BUTTON_SECONDARY, MotionEvent.BUTTON_SECONDARY,
event.x, event.x,
event.y event.y,
0f,
0f,
false,
sourceMouseRelative
) )
return true return true
} }
@ -178,7 +231,7 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi
val x = terminusEvent.x val x = terminusEvent.x
val y = terminusEvent.y val y = terminusEvent.y
if (terminusEvent.pointerCount >= 2 && panningAndScalingEnabled) { if (terminusEvent.pointerCount >= 2 && panningAndScalingEnabled && !pointerCaptureInProgress) {
GodotLib.pan(x, y, distanceX / 5f, distanceY / 5f) GodotLib.pan(x, y, distanceX / 5f, distanceY / 5f)
} else { } else {
GodotInputHandler.handleMotionEvent(terminusEvent) GodotInputHandler.handleMotionEvent(terminusEvent)
@ -187,7 +240,7 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi
} }
override fun onScale(detector: ScaleGestureDetector?): Boolean { override fun onScale(detector: ScaleGestureDetector?): Boolean {
if (detector == null || !panningAndScalingEnabled) { if (detector == null || !panningAndScalingEnabled || pointerCaptureInProgress) {
return false return false
} }
GodotLib.magnify( GodotLib.magnify(
@ -199,7 +252,7 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi
} }
override fun onScaleBegin(detector: ScaleGestureDetector?): Boolean { override fun onScaleBegin(detector: ScaleGestureDetector?): Boolean {
if (detector == null || !panningAndScalingEnabled) { if (detector == null || !panningAndScalingEnabled || pointerCaptureInProgress) {
return false return false
} }
scaleInProgress = true scaleInProgress = true

View file

@ -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; 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) { public boolean onKeyUp(final int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) { if (keyCode == KeyEvent.KEYCODE_BACK) {
return true; return true;
@ -202,6 +206,11 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
return true; 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) { if (event.isFromSource(InputDevice.SOURCE_JOYSTICK) && event.getActionMasked() == MotionEvent.ACTION_MOVE) {
// Check if the device exists // Check if the device exists
final int deviceId = event.getDeviceId(); final int deviceId = event.getDeviceId();
@ -237,7 +246,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
} }
return true; return true;
} }
} else if (isMouseEvent(event)) { } else {
return handleMouseEvent(event); return handleMouseEvent(event);
} }
@ -414,7 +423,11 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
} }
private static boolean isMouseEvent(int eventSource) { 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) { 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) { static boolean handleMotionEvent(int eventSource, int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY, boolean doubleTap) {
if (isMouseEvent(eventSource)) { 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); 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 verticalFactor = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
final float horizontalFactor = event.getAxisValue(MotionEvent.AXIS_HSCROLL); 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) { 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) { switch (eventAction) {
case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP: case MotionEvent.ACTION_UP:
@ -469,7 +493,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
case MotionEvent.ACTION_HOVER_MOVE: case MotionEvent.ACTION_HOVER_MOVE:
case MotionEvent.ACTION_MOVE: case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_SCROLL: { 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; return true;
} }
} }

View file

@ -283,12 +283,12 @@ JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env,
} }
// Called on the UI thread // 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) { if (step.get() <= 0) {
return; 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 // Called on the UI thread

View file

@ -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 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 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_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_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_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); 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);

View file

@ -42,12 +42,38 @@ GodotJavaViewWrapper::GodotJavaViewWrapper(jobject godot_view) {
if (android_device_api_level >= __ANDROID_API_N__) { if (android_device_api_level >= __ANDROID_API_N__) {
_set_pointer_icon = env->GetMethodID(_cls, "setPointerIcon", "(I)V"); _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 { bool GodotJavaViewWrapper::can_update_pointer_icon() const {
return _set_pointer_icon != nullptr; 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) { void GodotJavaViewWrapper::set_pointer_icon(int pointer_type) {
if (_set_pointer_icon != nullptr) { if (_set_pointer_icon != nullptr) {
JNIEnv *env = get_jni_env(); JNIEnv *env = get_jni_env();

View file

@ -41,12 +41,19 @@ class GodotJavaViewWrapper {
private: private:
jclass _cls; jclass _cls;
jobject _godot_view; jobject _godot_view;
jmethodID _request_pointer_capture = 0;
jmethodID _release_pointer_capture = 0;
jmethodID _set_pointer_icon = 0; jmethodID _set_pointer_icon = 0;
public: public:
GodotJavaViewWrapper(jobject godot_view); GodotJavaViewWrapper(jobject godot_view);
bool can_update_pointer_icon() const; 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); void set_pointer_icon(int pointer_type);
~GodotJavaViewWrapper(); ~GodotJavaViewWrapper();

View file

@ -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) { 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; return;
} }
if (mouse_mode == p_mode || p_mode == MouseMode::MOUSE_MODE_CAPTURED) { if (mouse_mode == p_mode) {
return; return;
} }
@ -287,6 +287,12 @@ void OS_Android::set_mouse_mode(MouseMode p_mode) {
set_cursor_shape(cursor_shape); 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; mouse_mode = p_mode;
} }