瀏覽代碼

Merge pull request #44201 from thebestnom/android-cursor-icons

Android: Add support for cursor icons
Rémi Verschelde 4 年之前
父節點
當前提交
1450a72bfa

+ 23 - 3
platform/android/display_server_android.cpp

@@ -36,8 +36,6 @@
 #include "java_godot_wrapper.h"
 #include "java_godot_wrapper.h"
 #include "os_android.h"
 #include "os_android.h"
 
 
-#include <android/input.h>
-
 #if defined(VULKAN_ENABLED)
 #if defined(VULKAN_ENABLED)
 #include "drivers/vulkan/rendering_device_vulkan.h"
 #include "drivers/vulkan/rendering_device_vulkan.h"
 #include "platform/android/vulkan/vulkan_context_android.h"
 #include "platform/android/vulkan/vulkan_context_android.h"
@@ -51,7 +49,7 @@ DisplayServerAndroid *DisplayServerAndroid::get_singleton() {
 bool DisplayServerAndroid::has_feature(Feature p_feature) const {
 bool DisplayServerAndroid::has_feature(Feature p_feature) const {
 	switch (p_feature) {
 	switch (p_feature) {
 		//case FEATURE_CONSOLE_WINDOW:
 		//case FEATURE_CONSOLE_WINDOW:
-		//case FEATURE_CURSOR_SHAPE:
+		case FEATURE_CURSOR_SHAPE:
 		//case FEATURE_CUSTOM_CURSOR_SHAPE:
 		//case FEATURE_CUSTOM_CURSOR_SHAPE:
 		//case FEATURE_GLOBAL_MENU:
 		//case FEATURE_GLOBAL_MENU:
 		//case FEATURE_HIDPI:
 		//case FEATURE_HIDPI:
@@ -829,6 +827,12 @@ void DisplayServerAndroid::mouse_set_mode(MouseMode p_mode) {
 		return;
 		return;
 	}
 	}
 
 
+	if (p_mode == MouseMode::MOUSE_MODE_HIDDEN) {
+		OS_Android::get_singleton()->get_godot_java()->get_godot_view()->set_pointer_icon(CURSOR_TYPE_NULL);
+	} else {
+		cursor_set_shape(cursor_shape);
+	}
+
 	if (p_mode == MouseMode::MOUSE_MODE_CAPTURED) {
 	if (p_mode == MouseMode::MOUSE_MODE_CAPTURED) {
 		OS_Android::get_singleton()->get_godot_java()->get_godot_view()->request_pointer_capture();
 		OS_Android::get_singleton()->get_godot_java()->get_godot_view()->request_pointer_capture();
 	} else {
 	} else {
@@ -870,3 +874,19 @@ int DisplayServerAndroid::_android_button_mask_to_godot_button_mask(int android_
 
 
 	return godot_button_mask;
 	return godot_button_mask;
 }
 }
+
+void DisplayServerAndroid::cursor_set_shape(DisplayServer::CursorShape p_shape) {
+	if (cursor_shape == p_shape) {
+		return;
+	}
+
+	cursor_shape = p_shape;
+
+	if (mouse_mode == MouseMode::MOUSE_MODE_VISIBLE || mouse_mode == MouseMode::MOUSE_MODE_CONFINED) {
+		OS_Android::get_singleton()->get_godot_java()->get_godot_view()->set_pointer_icon(android_cursors[cursor_shape]);
+	}
+}
+
+DisplayServer::CursorShape DisplayServerAndroid::cursor_get_shape() const {
+	return cursor_shape;
+}

+ 27 - 0
platform/android/display_server_android.h

@@ -70,6 +70,28 @@ private:
 
 
 	int buttons_state;
 	int buttons_state;
 
 
+	// https://developer.android.com/reference/android/view/PointerIcon
+	// mapping between Godot's cursor shape to Android's'
+	int android_cursors[CURSOR_MAX] = {
+		1000, //CURSOR_ARROW
+		1008, //CURSOR_IBEAM
+		1002, //CURSOR_POINTIN
+		1007, //CURSOR_CROSS
+		1004, //CURSOR_WAIT
+		1004, //CURSOR_BUSY
+		1021, //CURSOR_DRAG
+		1021, //CURSOR_CAN_DRO
+		1000, //CURSOR_FORBIDD (no corresponding icon in Android's icon  so fallback to default)
+		1015, //CURSOR_VSIZE
+		1014, //CURSOR_HSIZE
+		1017, //CURSOR_BDIAGSI
+		1016, //CURSOR_FDIAGSI
+		1020, //CURSOR_MOVE
+		1015, //CURSOR_VSPLIT
+		1014, //CURSOR_HSPLIT
+		1003, //CURSOR_HELP
+	};
+	const int CURSOR_TYPE_NULL = 0;
 	MouseMode mouse_mode;
 	MouseMode mouse_mode;
 
 
 	bool keep_screen_on;
 	bool keep_screen_on;
@@ -78,6 +100,8 @@ private:
 	Point2 hover_prev_pos; // needed to calculate the relative position on hover events
 	Point2 hover_prev_pos; // needed to calculate the relative position on hover events
 	Point2 scroll_prev_pos; // needed to calculate the relative position on scroll events
 	Point2 scroll_prev_pos; // needed to calculate the relative position on scroll events
 
 
+	CursorShape cursor_shape = CursorShape::CURSOR_ARROW;
+
 #if defined(VULKAN_ENABLED)
 #if defined(VULKAN_ENABLED)
 	VulkanContextAndroid *context_vulkan;
 	VulkanContextAndroid *context_vulkan;
 	RenderingDeviceVulkan *rendering_device_vulkan;
 	RenderingDeviceVulkan *rendering_device_vulkan;
@@ -180,6 +204,9 @@ public:
 	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);
 
 
+	virtual void cursor_set_shape(CursorShape p_shape);
+	virtual CursorShape cursor_get_shape() const;
+
 	void mouse_set_mode(MouseMode p_mode);
 	void mouse_set_mode(MouseMode p_mode);
 	MouseMode mouse_get_mode() const;
 	MouseMode mouse_get_mode() const;
 
 

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

@@ -44,11 +44,15 @@ import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.Context;
 import android.graphics.PixelFormat;
 import android.graphics.PixelFormat;
 import android.opengl.GLSurfaceView;
 import android.opengl.GLSurfaceView;
+import android.os.Build;
 import android.view.GestureDetector;
 import android.view.GestureDetector;
 import android.view.KeyEvent;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.MotionEvent;
+import android.view.PointerIcon;
 import android.view.SurfaceView;
 import android.view.SurfaceView;
 
 
+import androidx.annotation.Keep;
+
 /**
 /**
  * A simple GLSurfaceView sub-class that demonstrate how to perform
  * A simple GLSurfaceView sub-class that demonstrate how to perform
  * OpenGL ES 2.0 rendering into a GL Surface. Note the following important
  * OpenGL ES 2.0 rendering into a GL Surface. Note the following important
@@ -72,6 +76,7 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
 	private final GodotInputHandler inputHandler;
 	private final GodotInputHandler inputHandler;
 	private final GestureDetector detector;
 	private final GestureDetector detector;
 	private final GodotRenderer godotRenderer;
 	private final GodotRenderer godotRenderer;
+	private PointerIcon pointerIcon;
 
 
 	public GodotGLRenderView(Context context, Godot godot, XRMode xrMode, boolean p_use_32_bits,
 	public GodotGLRenderView(Context context, Godot godot, XRMode xrMode, boolean p_use_32_bits,
 			boolean p_use_debug_opengl) {
 			boolean p_use_debug_opengl) {
@@ -83,6 +88,9 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
 		this.inputHandler = new GodotInputHandler(this);
 		this.inputHandler = new GodotInputHandler(this);
 		this.detector = new GestureDetector(context, new GodotGestureHandler(this));
 		this.detector = new GestureDetector(context, new GodotGestureHandler(this));
 		this.godotRenderer = new GodotRenderer();
 		this.godotRenderer = new GodotRenderer();
+		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+			pointerIcon = PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT);
+		}
 		init(xrMode, false, 16, 0);
 		init(xrMode, false, 16, 0);
 	}
 	}
 
 
@@ -149,6 +157,21 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
 		return inputHandler.onGenericMotionEvent(event);
 		return inputHandler.onGenericMotionEvent(event);
 	}
 	}
 
 
+	/**
+	 * called from JNI to change pointer icon
+	 */
+	@Keep
+	public void setPointerIcon(int pointerType) {
+		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+			pointerIcon = PointerIcon.getSystemIcon(getContext(), pointerType);
+		}
+	}
+
+	@Override
+	public PointerIcon onResolvePointerIcon(MotionEvent me, int pointerIndex) {
+		return pointerIcon;
+	}
+
 	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);

+ 2 - 0
platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java

@@ -47,4 +47,6 @@ public interface GodotRenderView {
 	abstract public void onBackPressed();
 	abstract public void onBackPressed();
 
 
 	abstract public GodotInputHandler getInputHandler();
 	abstract public GodotInputHandler getInputHandler();
+
+	abstract public void setPointerIcon(int pointerType);
 }
 }

+ 23 - 1
platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java

@@ -37,17 +37,22 @@ import org.godotengine.godot.vulkan.VkSurfaceView;
 
 
 import android.annotation.SuppressLint;
 import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.Context;
+import android.os.Build;
 import android.view.GestureDetector;
 import android.view.GestureDetector;
 import android.view.InputDevice;
 import android.view.InputDevice;
 import android.view.KeyEvent;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.MotionEvent;
+import android.view.PointerIcon;
 import android.view.SurfaceView;
 import android.view.SurfaceView;
 
 
+import androidx.annotation.Keep;
+
 public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderView {
 public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderView {
 	private final Godot godot;
 	private final Godot godot;
 	private final GodotInputHandler mInputHandler;
 	private final GodotInputHandler mInputHandler;
 	private final GestureDetector mGestureDetector;
 	private final GestureDetector mGestureDetector;
 	private final VkRenderer mRenderer;
 	private final VkRenderer mRenderer;
+	private PointerIcon pointerIcon;
 
 
 	public GodotVulkanRenderView(Context context, Godot godot) {
 	public GodotVulkanRenderView(Context context, Godot godot) {
 		super(context);
 		super(context);
@@ -56,7 +61,9 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV
 		mInputHandler = new GodotInputHandler(this);
 		mInputHandler = new GodotInputHandler(this);
 		mGestureDetector = new GestureDetector(context, new GodotGestureHandler(this));
 		mGestureDetector = new GestureDetector(context, new GodotGestureHandler(this));
 		mRenderer = new VkRenderer();
 		mRenderer = new VkRenderer();
-
+		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+			pointerIcon = PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT);
+		}
 		setFocusableInTouchMode(true);
 		setFocusableInTouchMode(true);
 		startRenderer(mRenderer);
 		startRenderer(mRenderer);
 	}
 	}
@@ -124,6 +131,21 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV
 		return mInputHandler.onGenericMotionEvent(event);
 		return mInputHandler.onGenericMotionEvent(event);
 	}
 	}
 
 
+	/**
+	 * called from JNI to change pointer icon
+	 */
+	@Keep
+	public void setPointerIcon(int pointerType) {
+		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+			pointerIcon = PointerIcon.getSystemIcon(getContext(), pointerType);
+		}
+	}
+
+	@Override
+	public PointerIcon onResolvePointerIcon(MotionEvent me, int pointerIndex) {
+		return pointerIcon;
+	}
+
 	@Override
 	@Override
 	public void onResume() {
 	public void onResume() {
 		super.onResume();
 		super.onResume();

+ 10 - 0
platform/android/java_godot_view_wrapper.cpp

@@ -43,6 +43,7 @@ GodotJavaViewWrapper::GodotJavaViewWrapper(jobject godot_view) {
 	if (android_get_device_api_level() >= __ANDROID_API_O__) {
 	if (android_get_device_api_level() >= __ANDROID_API_O__) {
 		_request_pointer_capture = env->GetMethodID(_cls, "requestPointerCapture", "()V");
 		_request_pointer_capture = env->GetMethodID(_cls, "requestPointerCapture", "()V");
 		_release_pointer_capture = env->GetMethodID(_cls, "releasePointerCapture", "()V");
 		_release_pointer_capture = env->GetMethodID(_cls, "releasePointerCapture", "()V");
+		_set_pointer_icon = env->GetMethodID(_cls, "setPointerIcon", "(I)V");
 	}
 	}
 }
 }
 
 
@@ -64,6 +65,15 @@ void GodotJavaViewWrapper::release_pointer_capture() {
 	}
 	}
 }
 }
 
 
+void GodotJavaViewWrapper::set_pointer_icon(int pointer_type) {
+	if (_set_pointer_icon != 0) {
+		JNIEnv *env = get_jni_env();
+		ERR_FAIL_COND(env == nullptr);
+
+		env->CallVoidMethod(_godot_view, _set_pointer_icon, pointer_type);
+	}
+}
+
 GodotJavaViewWrapper::~GodotJavaViewWrapper() {
 GodotJavaViewWrapper::~GodotJavaViewWrapper() {
 	JNIEnv *env = get_jni_env();
 	JNIEnv *env = get_jni_env();
 	ERR_FAIL_COND(env == nullptr);
 	ERR_FAIL_COND(env == nullptr);

+ 2 - 0
platform/android/java_godot_view_wrapper.h

@@ -45,12 +45,14 @@ private:
 
 
 	jmethodID _request_pointer_capture = 0;
 	jmethodID _request_pointer_capture = 0;
 	jmethodID _release_pointer_capture = 0;
 	jmethodID _release_pointer_capture = 0;
+	jmethodID _set_pointer_icon = 0;
 
 
 public:
 public:
 	GodotJavaViewWrapper(jobject godot_view);
 	GodotJavaViewWrapper(jobject godot_view);
 
 
 	void request_pointer_capture();
 	void request_pointer_capture();
 	void release_pointer_capture();
 	void release_pointer_capture();
+	void set_pointer_icon(int pointer_type);
 
 
 	~GodotJavaViewWrapper();
 	~GodotJavaViewWrapper();
 };
 };