Browse Source

Merge pull request #43249 from thebestnom/captured-mouse-android-support

Android: Allow Mouse Capture
Rémi Verschelde 4 năm trước cách đây
mục cha
commit
45d627a845

+ 1 - 0
platform/android/SCsub

@@ -14,6 +14,7 @@ android_files = [
     "java_godot_lib_jni.cpp",
     "java_godot_lib_jni.cpp",
     "java_class_wrapper.cpp",
     "java_class_wrapper.cpp",
     "java_godot_wrapper.cpp",
     "java_godot_wrapper.cpp",
+    "java_godot_view_wrapper.cpp",
     "java_godot_io_wrapper.cpp",
     "java_godot_io_wrapper.cpp",
     "jni_utils.cpp",
     "jni_utils.cpp",
     "android_keys_utils.cpp",
     "android_keys_utils.cpp",

+ 43 - 8
platform/android/display_server_android.cpp

@@ -686,7 +686,7 @@ void DisplayServerAndroid::process_hover(int p_type, Point2 p_pos) {
 	}
 	}
 }
 }
 
 
-void DisplayServerAndroid::process_mouse_event(int event_action, int event_android_buttons_mask, Point2 event_pos, float event_vertical_factor, float event_horizontal_factor) {
+void DisplayServerAndroid::process_mouse_event(int input_device, int event_action, int event_android_buttons_mask, Point2 event_pos, float event_vertical_factor, float event_horizontal_factor) {
 	int event_buttons_mask = _android_button_mask_to_godot_button_mask(event_android_buttons_mask);
 	int event_buttons_mask = _android_button_mask_to_godot_button_mask(event_android_buttons_mask);
 	switch (event_action) {
 	switch (event_action) {
 		case AMOTION_EVENT_ACTION_BUTTON_PRESS:
 		case AMOTION_EVENT_ACTION_BUTTON_PRESS:
@@ -694,8 +694,13 @@ void DisplayServerAndroid::process_mouse_event(int event_action, int event_andro
 			Ref<InputEventMouseButton> ev;
 			Ref<InputEventMouseButton> ev;
 			ev.instance();
 			ev.instance();
 			_set_key_modifier_state(ev);
 			_set_key_modifier_state(ev);
-			ev->set_position(event_pos);
-			ev->set_global_position(event_pos);
+			if ((input_device & AINPUT_SOURCE_MOUSE) == AINPUT_SOURCE_MOUSE) {
+				ev->set_position(event_pos);
+				ev->set_global_position(event_pos);
+			} else {
+				ev->set_position(hover_prev_pos);
+				ev->set_global_position(hover_prev_pos);
+			}
 			ev->set_pressed(event_action == AMOTION_EVENT_ACTION_BUTTON_PRESS);
 			ev->set_pressed(event_action == AMOTION_EVENT_ACTION_BUTTON_PRESS);
 			int changed_button_mask = buttons_state ^ event_buttons_mask;
 			int changed_button_mask = buttons_state ^ event_buttons_mask;
 
 
@@ -710,18 +715,30 @@ void DisplayServerAndroid::process_mouse_event(int event_action, int event_andro
 			Ref<InputEventMouseMotion> ev;
 			Ref<InputEventMouseMotion> ev;
 			ev.instance();
 			ev.instance();
 			_set_key_modifier_state(ev);
 			_set_key_modifier_state(ev);
-			ev->set_position(event_pos);
-			ev->set_global_position(event_pos);
+			if ((input_device & AINPUT_SOURCE_MOUSE) == AINPUT_SOURCE_MOUSE) {
+				ev->set_position(event_pos);
+				ev->set_global_position(event_pos);
+				ev->set_relative(event_pos - hover_prev_pos);
+				hover_prev_pos = event_pos;
+			} else {
+				ev->set_position(hover_prev_pos);
+				ev->set_global_position(hover_prev_pos);
+				ev->set_relative(event_pos);
+			}
 			ev->set_relative(event_pos - hover_prev_pos);
 			ev->set_relative(event_pos - hover_prev_pos);
 			ev->set_button_mask(event_buttons_mask);
 			ev->set_button_mask(event_buttons_mask);
 			Input::get_singleton()->accumulate_input_event(ev);
 			Input::get_singleton()->accumulate_input_event(ev);
-			hover_prev_pos = event_pos;
 		} break;
 		} break;
 		case AMOTION_EVENT_ACTION_SCROLL: {
 		case AMOTION_EVENT_ACTION_SCROLL: {
 			Ref<InputEventMouseButton> ev;
 			Ref<InputEventMouseButton> ev;
 			ev.instance();
 			ev.instance();
-			ev->set_position(event_pos);
-			ev->set_global_position(event_pos);
+			if ((input_device & AINPUT_SOURCE_MOUSE) == AINPUT_SOURCE_MOUSE) {
+				ev->set_position(event_pos);
+				ev->set_global_position(event_pos);
+			} else {
+				ev->set_position(hover_prev_pos);
+				ev->set_global_position(hover_prev_pos);
+			}
 			ev->set_pressed(true);
 			ev->set_pressed(true);
 			buttons_state = event_buttons_mask;
 			buttons_state = event_buttons_mask;
 			if (event_vertical_factor > 0) {
 			if (event_vertical_factor > 0) {
@@ -809,6 +826,24 @@ void DisplayServerAndroid::process_gyroscope(const Vector3 &p_gyroscope) {
 	Input::get_singleton()->set_gyroscope(p_gyroscope);
 	Input::get_singleton()->set_gyroscope(p_gyroscope);
 }
 }
 
 
+void DisplayServerAndroid::mouse_set_mode(MouseMode p_mode) {
+	if (mouse_mode == p_mode) {
+		return;
+	}
+
+	if (p_mode == MouseMode::MOUSE_MODE_CAPTURED) {
+		OS_Android::get_singleton()->get_godot_java()->get_godot_view()->request_pointer_capture();
+	} else {
+		OS_Android::get_singleton()->get_godot_java()->get_godot_view()->release_pointer_capture();
+	}
+
+	mouse_mode = p_mode;
+}
+
+DisplayServer::MouseMode DisplayServerAndroid::mouse_get_mode() const {
+	return mouse_mode;
+}
+
 Point2i DisplayServerAndroid::mouse_get_position() const {
 Point2i DisplayServerAndroid::mouse_get_position() const {
 	return hover_prev_pos;
 	return hover_prev_pos;
 }
 }

+ 6 - 1
platform/android/display_server_android.h

@@ -70,6 +70,8 @@ private:
 
 
 	int buttons_state;
 	int buttons_state;
 
 
+	MouseMode mouse_mode;
+
 	bool keep_screen_on;
 	bool keep_screen_on;
 
 
 	Vector<TouchPos> touch;
 	Vector<TouchPos> touch;
@@ -172,12 +174,15 @@ public:
 	void process_gyroscope(const Vector3 &p_gyroscope);
 	void process_gyroscope(const Vector3 &p_gyroscope);
 	void process_touch(int p_event, int p_pointer, const Vector<TouchPos> &p_points);
 	void process_touch(int p_event, int p_pointer, const Vector<TouchPos> &p_points);
 	void process_hover(int p_type, Point2 p_pos);
 	void process_hover(int p_type, Point2 p_pos);
-	void process_mouse_event(int event_action, int event_android_buttons_mask, Point2 event_pos, float event_vertical_factor = 0, float event_horizontal_factor = 0);
+	void process_mouse_event(int input_device, int event_action, int event_android_buttons_mask, Point2 event_pos, float event_vertical_factor = 0, float event_horizontal_factor = 0);
 	void process_double_tap(int event_android_button_mask, Point2 p_pos);
 	void process_double_tap(int event_android_button_mask, Point2 p_pos);
 	void process_scroll(Point2 p_pos);
 	void process_scroll(Point2 p_pos);
 	void process_joy_event(JoypadEvent p_event);
 	void process_joy_event(JoypadEvent p_event);
 	void process_key_event(int p_keycode, int p_scancode, int p_unicode_char, bool p_pressed);
 	void process_key_event(int p_keycode, int p_scancode, int p_unicode_char, bool p_pressed);
 
 
+	void mouse_set_mode(MouseMode p_mode);
+	MouseMode mouse_get_mode() const;
+
 	static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
 	static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
 	static Vector<String> get_rendering_drivers_func();
 	static Vector<String> get_rendering_drivers_func();
 	static void register_android_driver();
 	static void register_android_driver();

+ 5 - 0
platform/android/java/lib/src/org/godotengine/godot/Godot.java

@@ -988,4 +988,9 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
 	public void initInputDevices() {
 	public void initInputDevices() {
 		mRenderView.initInputDevices();
 		mRenderView.initInputDevices();
 	}
 	}
+
+	@Keep
+	private GodotRenderView getRenderView() { // used by native side to get renderView
+		return mRenderView;
+	}
 }
 }

+ 5 - 0
platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java

@@ -144,6 +144,11 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
 		return inputHandler.onGenericMotionEvent(event) || super.onGenericMotionEvent(event);
 		return inputHandler.onGenericMotionEvent(event) || super.onGenericMotionEvent(event);
 	}
 	}
 
 
+	@Override
+	public boolean onCapturedPointerEvent(MotionEvent event) {
+		return inputHandler.onGenericMotionEvent(event);
+	}
+
 	private void init(XRMode xrMode, boolean translucent, int depth, int stencil) {
 	private void init(XRMode xrMode, boolean translucent, int depth, int stencil) {
 		setPreserveEGLContextOnPause(true);
 		setPreserveEGLContextOnPause(true);
 		setFocusableInTouchMode(true);
 		setFocusableInTouchMode(true);

+ 5 - 0
platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java

@@ -119,6 +119,11 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV
 		return mInputHandler.onGenericMotionEvent(event);
 		return mInputHandler.onGenericMotionEvent(event);
 	}
 	}
 
 
+	@Override
+	public boolean onCapturedPointerEvent(MotionEvent event) {
+		return mInputHandler.onGenericMotionEvent(event);
+	}
+
 	@Override
 	@Override
 	public void onResume() {
 	public void onResume() {
 		super.onResume();
 		super.onResume();

+ 6 - 1
platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java

@@ -245,7 +245,7 @@ public class GodotInputHandler implements InputDeviceListener {
 				}
 				}
 			});
 			});
 			return true;
 			return true;
-		} else if ((event.getSource() & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) {
+		} else if (event.isFromSource(InputDevice.SOURCE_MOUSE) || event.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE)) {
 			if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
 			if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
 				return handleMouseEvent(event);
 				return handleMouseEvent(event);
 			}
 			}
@@ -462,6 +462,11 @@ public class GodotInputHandler implements InputDeviceListener {
 					}
 					}
 				});
 				});
 			}
 			}
+			case MotionEvent.ACTION_DOWN:
+			case MotionEvent.ACTION_UP: {
+				// we can safely ignore these cases because they are always come beside ACTION_BUTTON_PRESS and ACTION_BUTTON_RELEASE
+				return true;
+			}
 		}
 		}
 		return false;
 		return false;
 	}
 	}

+ 2 - 3
platform/android/java_godot_lib_jni.cpp

@@ -251,9 +251,8 @@ void touch_preprocessing(JNIEnv *env, jclass clazz, jint input_device, jint ev,
 		tp.id = (int)p[0];
 		tp.id = (int)p[0];
 		points.push_back(tp);
 		points.push_back(tp);
 	}
 	}
-
-	if ((input_device & AINPUT_SOURCE_MOUSE) == AINPUT_SOURCE_MOUSE) {
-		DisplayServerAndroid::get_singleton()->process_mouse_event(ev, buttons_mask, points[0].pos, vertical_factor, horizontal_factor);
+	if ((input_device & AINPUT_SOURCE_MOUSE) == AINPUT_SOURCE_MOUSE || (input_device & AINPUT_SOURCE_MOUSE_RELATIVE) == AINPUT_SOURCE_MOUSE_RELATIVE) {
+		DisplayServerAndroid::get_singleton()->process_mouse_event(input_device, ev, buttons_mask, points[0].pos, vertical_factor, horizontal_factor);
 	} else {
 	} else {
 		DisplayServerAndroid::get_singleton()->process_touch(ev, pointer, points);
 		DisplayServerAndroid::get_singleton()->process_touch(ev, pointer, points);
 	}
 	}

+ 66 - 0
platform/android/java_godot_view_wrapper.cpp

@@ -0,0 +1,66 @@
+/*************************************************************************/
+/*  java_godot_view_wrapper.cpp                                          */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#include "java_godot_view_wrapper.h"
+
+#include "thread_jandroid.h"
+
+GodotJavaViewWrapper::GodotJavaViewWrapper(jobject godot_view) {
+	JNIEnv *env = ThreadAndroid::get_env();
+
+	_godot_view = env->NewGlobalRef(godot_view);
+
+	_cls = (jclass)env->NewGlobalRef(env->GetObjectClass(godot_view));
+
+	if (android_get_device_api_level() >= __ANDROID_API_O__) {
+		_request_pointer_capture = env->GetMethodID(_cls, "requestPointerCapture", "()V");
+		_release_pointer_capture = env->GetMethodID(_cls, "releasePointerCapture", "()V");
+	}
+}
+
+void GodotJavaViewWrapper::request_pointer_capture() {
+	if (_request_pointer_capture != 0) {
+		JNIEnv *env = ThreadAndroid::get_env();
+		env->CallVoidMethod(_godot_view, _request_pointer_capture);
+	}
+}
+
+void GodotJavaViewWrapper::release_pointer_capture() {
+	if (_request_pointer_capture != 0) {
+		JNIEnv *env = ThreadAndroid::get_env();
+		env->CallVoidMethod(_godot_view, _release_pointer_capture);
+	}
+}
+
+GodotJavaViewWrapper::~GodotJavaViewWrapper() {
+	JNIEnv *env = ThreadAndroid::get_env();
+	env->DeleteGlobalRef(_godot_view);
+	env->DeleteGlobalRef(_cls);
+}

+ 56 - 0
platform/android/java_godot_view_wrapper.h

@@ -0,0 +1,56 @@
+/*************************************************************************/
+/*  java_godot_view_wrapper.h                                            */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifndef GODOT_JAVA_GODOT_VIEW_WRAPPER_H
+#define GODOT_JAVA_GODOT_VIEW_WRAPPER_H
+
+#include <android/log.h>
+#include <jni.h>
+
+// Class that makes functions in java/src/org/godotengine/godot/GodotView.java callable from C++
+class GodotJavaViewWrapper {
+private:
+	jclass _cls;
+
+	jobject _godot_view;
+
+	jmethodID _request_pointer_capture = 0;
+	jmethodID _release_pointer_capture = 0;
+
+public:
+	GodotJavaViewWrapper(jobject godot_view);
+
+	void request_pointer_capture();
+	void release_pointer_capture();
+
+	~GodotJavaViewWrapper();
+};
+
+#endif //GODOT_JAVA_GODOT_VIEW_WRAPPER_H

+ 10 - 0
platform/android/java_godot_wrapper.cpp

@@ -109,6 +109,16 @@ jobject GodotJavaWrapper::get_class_loader() {
 	}
 	}
 }
 }
 
 
+GodotJavaViewWrapper *GodotJavaWrapper::get_godot_view() {
+	if (_godot_view != nullptr) {
+		return _godot_view;
+	}
+	JNIEnv *env = ThreadAndroid::get_env();
+	jmethodID godot_view_getter = env->GetMethodID(godot_class, "getRenderView", "()Lorg/godotengine/godot/GodotRenderView;");
+	_godot_view = new GodotJavaViewWrapper(env->CallObjectMethod(godot_instance, godot_view_getter));
+	return _godot_view;
+}
+
 void GodotJavaWrapper::on_video_init(JNIEnv *p_env) {
 void GodotJavaWrapper::on_video_init(JNIEnv *p_env) {
 	if (_on_video_init)
 	if (_on_video_init)
 		if (p_env == nullptr)
 		if (p_env == nullptr)

+ 4 - 0
platform/android/java_godot_wrapper.h

@@ -37,6 +37,7 @@
 #include <android/log.h>
 #include <android/log.h>
 #include <jni.h>
 #include <jni.h>
 
 
+#include "java_godot_view_wrapper.h"
 #include "string_android.h"
 #include "string_android.h"
 
 
 // Class that makes functions in java/src/org/godotengine/godot/Godot.java callable from C++
 // Class that makes functions in java/src/org/godotengine/godot/Godot.java callable from C++
@@ -47,6 +48,8 @@ private:
 	jclass godot_class;
 	jclass godot_class;
 	jclass activity_class;
 	jclass activity_class;
 
 
+	GodotJavaViewWrapper *_godot_view = nullptr;
+
 	jmethodID _on_video_init = 0;
 	jmethodID _on_video_init = 0;
 	jmethodID _restart = 0;
 	jmethodID _restart = 0;
 	jmethodID _finish = 0;
 	jmethodID _finish = 0;
@@ -74,6 +77,7 @@ public:
 	jobject get_member_object(const char *p_name, const char *p_class, JNIEnv *p_env = nullptr);
 	jobject get_member_object(const char *p_name, const char *p_class, JNIEnv *p_env = nullptr);
 
 
 	jobject get_class_loader();
 	jobject get_class_loader();
+	GodotJavaViewWrapper *get_godot_view();
 
 
 	void on_video_init(JNIEnv *p_env = nullptr);
 	void on_video_init(JNIEnv *p_env = nullptr);
 	void on_godot_main_loop_started(JNIEnv *p_env = nullptr);
 	void on_godot_main_loop_started(JNIEnv *p_env = nullptr);