diff options
author | Fredia Huya-Kouadio <fhuyakou@gmail.com> | 2022-11-27 22:03:28 -0800 |
---|---|---|
committer | Fredia Huya-Kouadio <fhuyakou@gmail.com> | 2022-11-27 22:07:51 -0800 |
commit | 7cc47613fed4dbdb22816cca7722b3d34fd0b1fc (patch) | |
tree | 47f0ebabf82d1b2664d02a2c4e0bb559b6d4ede0 /platform | |
parent | 4a82d71d7345d0dde4da2816fd489da1c303f580 (diff) |
Add missing display server overrides
Improves the base functionality for the Android platform and helps reduce the amount of spurious error logs emitted.
Diffstat (limited to 'platform')
10 files changed, 166 insertions, 7 deletions
diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp index 967f5c7dae..1b261b489e 100644 --- a/platform/android/display_server_android.cpp +++ b/platform/android/display_server_android.cpp @@ -619,11 +619,11 @@ MouseButton DisplayServerAndroid::mouse_get_button_state() const { return (MouseButton)Input::get_singleton()->get_mouse_button_mask(); } -void DisplayServerAndroid::cursor_set_shape(DisplayServer::CursorShape p_shape) { +void DisplayServerAndroid::_cursor_set_shape_helper(CursorShape p_shape, bool force) { if (!OS_Android::get_singleton()->get_godot_java()->get_godot_view()->can_update_pointer_icon()) { return; } - if (cursor_shape == p_shape) { + if (cursor_shape == p_shape && !force) { return; } @@ -634,10 +634,23 @@ void DisplayServerAndroid::cursor_set_shape(DisplayServer::CursorShape p_shape) } } +void DisplayServerAndroid::cursor_set_shape(DisplayServer::CursorShape p_shape) { + _cursor_set_shape_helper(p_shape); +} + DisplayServer::CursorShape DisplayServerAndroid::cursor_get_shape() const { return cursor_shape; } +void DisplayServerAndroid::cursor_set_custom_image(const Ref<Resource> &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { + String cursor_path = p_cursor.is_valid() ? p_cursor->get_path() : ""; + if (!cursor_path.is_empty()) { + cursor_path = ProjectSettings::get_singleton()->globalize_path(cursor_path); + } + OS_Android::get_singleton()->get_godot_java()->get_godot_view()->configure_pointer_icon(android_cursors[cursor_shape], cursor_path, p_hotspot); + _cursor_set_shape_helper(p_shape, true); +} + void DisplayServerAndroid::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) { #if defined(VULKAN_ENABLED) context_vulkan->set_vsync_mode(p_window, p_vsync_mode); @@ -651,3 +664,23 @@ DisplayServer::VSyncMode DisplayServerAndroid::window_get_vsync_mode(WindowID p_ return DisplayServer::VSYNC_ENABLED; #endif } + +void DisplayServerAndroid::reset_swap_buffers_flag() { + swap_buffers_flag = false; +} + +bool DisplayServerAndroid::should_swap_buffers() const { + return swap_buffers_flag; +} + +void DisplayServerAndroid::swap_buffers() { + swap_buffers_flag = true; +} + +void DisplayServerAndroid::set_native_icon(const String &p_filename) { + // NOT SUPPORTED +} + +void DisplayServerAndroid::set_icon(const Ref<Image> &p_icon) { + // NOT SUPPORTED +} diff --git a/platform/android/display_server_android.h b/platform/android/display_server_android.h index a6bc88e048..c7f4d8046f 100644 --- a/platform/android/display_server_android.h +++ b/platform/android/display_server_android.h @@ -66,6 +66,7 @@ class DisplayServerAndroid : public DisplayServer { MouseMode mouse_mode = MouseMode::MOUSE_MODE_VISIBLE; bool keep_screen_on; + bool swap_buffers_flag; CursorShape cursor_shape = CursorShape::CURSOR_ARROW; @@ -188,8 +189,10 @@ public: void process_magnetometer(const Vector3 &p_magnetometer); void process_gyroscope(const Vector3 &p_gyroscope); + void _cursor_set_shape_helper(CursorShape p_shape, bool force = false); virtual void cursor_set_shape(CursorShape p_shape) override; virtual CursorShape cursor_get_shape() const override; + virtual void cursor_set_custom_image(const Ref<Resource> &p_cursor, CursorShape p_shape = CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()) override; virtual void mouse_set_mode(MouseMode p_mode) override; virtual MouseMode mouse_get_mode() const override; @@ -204,6 +207,13 @@ public: virtual Point2i mouse_get_position() const override; virtual MouseButton mouse_get_button_state() const override; + void reset_swap_buffers_flag(); + bool should_swap_buffers() const; + virtual void swap_buffers() override; + + virtual void set_native_icon(const String &p_filename) override; + virtual void set_icon(const Ref<Image> &p_icon) override; + DisplayServerAndroid(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error); ~DisplayServerAndroid(); }; diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.java b/platform/android/java/lib/src/org/godotengine/godot/Godot.java index a002a37ab9..3487e5019c 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java @@ -175,6 +175,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC public GodotIO io; public GodotNetUtils netUtils; public GodotTTS tts; + DirectoryAccessHandler directoryAccessHandler; public interface ResultCallback { void callback(int requestCode, int resultCode, Intent data); @@ -488,7 +489,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC netUtils = new GodotNetUtils(activity); tts = new GodotTTS(activity); Context context = getContext(); - DirectoryAccessHandler directoryAccessHandler = new DirectoryAccessHandler(context); + directoryAccessHandler = new DirectoryAccessHandler(context); FileAccessHandler fileAccessHandler = new FileAccessHandler(context); mSensorManager = (SensorManager)activity.getSystemService(Context.SENSOR_SERVICE); mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java index 3dfc37f6b0..252554126d 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java @@ -43,8 +43,13 @@ import org.godotengine.godot.xr.regular.RegularFallbackConfigChooser; import android.annotation.SuppressLint; import android.content.Context; +import android.content.res.AssetManager; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.graphics.PixelFormat; import android.os.Build; +import android.text.TextUtils; +import android.util.SparseArray; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.PointerIcon; @@ -52,6 +57,8 @@ import android.view.SurfaceView; import androidx.annotation.Keep; +import java.io.InputStream; + /** * A simple GLSurfaceView sub-class that demonstrate how to perform * OpenGL ES 2.0 rendering into a GL Surface. Note the following important @@ -74,6 +81,7 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView private final Godot godot; private final GodotInputHandler inputHandler; private final GodotRenderer godotRenderer; + private final SparseArray<PointerIcon> customPointerIcons = new SparseArray<>(); public GodotGLRenderView(Context context, Godot godot, XRMode xrMode, boolean p_use_debug_opengl) { super(context); @@ -169,12 +177,49 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView } /** + * Used to configure the PointerIcon for the given type. + * + * Called from JNI + */ + @Keep + @Override + public void configurePointerIcon(int pointerType, String imagePath, float hotSpotX, float hotSpotY) { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) { + try { + Bitmap bitmap = null; + if (!TextUtils.isEmpty(imagePath)) { + if (godot.directoryAccessHandler.filesystemFileExists(imagePath)) { + // Try to load the bitmap from the file system + bitmap = BitmapFactory.decodeFile(imagePath); + } else if (godot.directoryAccessHandler.assetsFileExists(imagePath)) { + // Try to load the bitmap from the assets directory + AssetManager am = getContext().getAssets(); + InputStream imageInputStream = am.open(imagePath); + bitmap = BitmapFactory.decodeStream(imageInputStream); + } + } + + PointerIcon customPointerIcon = PointerIcon.create(bitmap, hotSpotX, hotSpotY); + customPointerIcons.put(pointerType, customPointerIcon); + } catch (Exception e) { + // Reset the custom pointer icon + customPointerIcons.delete(pointerType); + } + } + } + + /** * called from JNI to change pointer icon */ @Keep + @Override public void setPointerIcon(int pointerType) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - setPointerIcon(PointerIcon.getSystemIcon(getContext(), pointerType)); + PointerIcon pointerIcon = customPointerIcons.get(pointerType); + if (pointerIcon == null) { + pointerIcon = PointerIcon.getSystemIcon(getContext(), pointerType); + } + setPointerIcon(pointerIcon); } } diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java index cb63fd885f..ab74ba037d 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java @@ -48,5 +48,7 @@ public interface GodotRenderView { GodotInputHandler getInputHandler(); + void configurePointerIcon(int pointerType, String imagePath, float hotSpotX, float hotSpotY); + void setPointerIcon(int pointerType); } diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java index 0becf00d93..56bc7f9e76 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java @@ -36,7 +36,12 @@ import org.godotengine.godot.vulkan.VkSurfaceView; import android.annotation.SuppressLint; import android.content.Context; +import android.content.res.AssetManager; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.os.Build; +import android.text.TextUtils; +import android.util.SparseArray; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.PointerIcon; @@ -44,10 +49,13 @@ import android.view.SurfaceView; import androidx.annotation.Keep; +import java.io.InputStream; + public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderView { private final Godot godot; private final GodotInputHandler mInputHandler; private final VkRenderer mRenderer; + private final SparseArray<PointerIcon> customPointerIcons = new SparseArray<>(); public GodotVulkanRenderView(Context context, Godot godot) { super(context); @@ -143,12 +151,49 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV } /** + * Used to configure the PointerIcon for the given type. + * + * Called from JNI + */ + @Keep + @Override + public void configurePointerIcon(int pointerType, String imagePath, float hotSpotX, float hotSpotY) { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) { + try { + Bitmap bitmap = null; + if (!TextUtils.isEmpty(imagePath)) { + if (godot.directoryAccessHandler.filesystemFileExists(imagePath)) { + // Try to load the bitmap from the file system + bitmap = BitmapFactory.decodeFile(imagePath); + } else if (godot.directoryAccessHandler.assetsFileExists(imagePath)) { + // Try to load the bitmap from the assets directory + AssetManager am = getContext().getAssets(); + InputStream imageInputStream = am.open(imagePath); + bitmap = BitmapFactory.decodeStream(imageInputStream); + } + } + + PointerIcon customPointerIcon = PointerIcon.create(bitmap, hotSpotX, hotSpotY); + customPointerIcons.put(pointerType, customPointerIcon); + } catch (Exception e) { + // Reset the custom pointer icon + customPointerIcons.delete(pointerType); + } + } + } + + /** * called from JNI to change pointer icon */ @Keep + @Override public void setPointerIcon(int pointerType) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - setPointerIcon(PointerIcon.getSystemIcon(getContext(), pointerType)); + PointerIcon pointerIcon = customPointerIcons.get(pointerType); + if (pointerIcon == null) { + pointerIcon = PointerIcon.getSystemIcon(getContext(), pointerType); + } + setPointerIcon(pointerIcon); } } diff --git a/platform/android/java/lib/src/org/godotengine/godot/io/directory/DirectoryAccessHandler.kt b/platform/android/java/lib/src/org/godotengine/godot/io/directory/DirectoryAccessHandler.kt index fedcf4843f..6bc317415f 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/io/directory/DirectoryAccessHandler.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/io/directory/DirectoryAccessHandler.kt @@ -79,6 +79,9 @@ class DirectoryAccessHandler(context: Context) { private val assetsDirAccess = AssetsDirectoryAccess(context) private val fileSystemDirAccess = FilesystemDirectoryAccess(context) + fun assetsFileExists(assetsPath: String) = assetsDirAccess.fileExists(assetsPath) + fun filesystemFileExists(path: String) = fileSystemDirAccess.fileExists(path) + private fun hasDirId(accessType: AccessType, dirId: Int): Boolean { return when (accessType) { ACCESS_RESOURCES -> assetsDirAccess.hasDirId(dirId) diff --git a/platform/android/java_godot_view_wrapper.cpp b/platform/android/java_godot_view_wrapper.cpp index 378a467772..23cfc5f2e6 100644 --- a/platform/android/java_godot_view_wrapper.cpp +++ b/platform/android/java_godot_view_wrapper.cpp @@ -42,6 +42,7 @@ GodotJavaViewWrapper::GodotJavaViewWrapper(jobject godot_view) { int android_device_api_level = android_get_device_api_level(); if (android_device_api_level >= __ANDROID_API_N__) { + _configure_pointer_icon = env->GetMethodID(_cls, "configurePointerIcon", "(ILjava/lang/String;FF)V"); _set_pointer_icon = env->GetMethodID(_cls, "setPointerIcon", "(I)V"); } if (android_device_api_level >= __ANDROID_API_O__) { @@ -51,7 +52,7 @@ GodotJavaViewWrapper::GodotJavaViewWrapper(jobject godot_view) { } bool GodotJavaViewWrapper::can_update_pointer_icon() const { - return _set_pointer_icon != nullptr; + return _configure_pointer_icon != nullptr && _set_pointer_icon != nullptr; } bool GodotJavaViewWrapper::can_capture_pointer() const { @@ -76,6 +77,16 @@ void GodotJavaViewWrapper::release_pointer_capture() { } } +void GodotJavaViewWrapper::configure_pointer_icon(int pointer_type, const String &image_path, const Vector2 &p_hotspot) { + if (_configure_pointer_icon != nullptr) { + JNIEnv *env = get_jni_env(); + ERR_FAIL_NULL(env); + + jstring jImagePath = env->NewStringUTF(image_path.utf8().get_data()); + env->CallVoidMethod(_godot_view, _configure_pointer_icon, pointer_type, jImagePath, p_hotspot.x, p_hotspot.y); + } +} + 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 b398c73cac..b58a6607ce 100644 --- a/platform/android/java_godot_view_wrapper.h +++ b/platform/android/java_godot_view_wrapper.h @@ -31,6 +31,7 @@ #ifndef JAVA_GODOT_VIEW_WRAPPER_H #define JAVA_GODOT_VIEW_WRAPPER_H +#include "core/math/vector2.h" #include <android/log.h> #include <jni.h> @@ -45,6 +46,8 @@ private: jmethodID _request_pointer_capture = 0; jmethodID _release_pointer_capture = 0; + + jmethodID _configure_pointer_icon = 0; jmethodID _set_pointer_icon = 0; public: @@ -55,6 +58,8 @@ public: void request_pointer_capture(); void release_pointer_capture(); + + void configure_pointer_icon(int pointer_type, const String &image_path, const Vector2 &p_hotspot); void set_pointer_icon(int pointer_type); ~GodotJavaViewWrapper(); diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp index 97fa90b1d2..cb43f26425 100644 --- a/platform/android/os_android.cpp +++ b/platform/android/os_android.cpp @@ -268,12 +268,16 @@ bool OS_Android::main_loop_iterate(bool *r_should_swap_buffers) { if (!main_loop) { return false; } + DisplayServerAndroid::get_singleton()->reset_swap_buffers_flag(); DisplayServerAndroid::get_singleton()->process_events(); uint64_t current_frames_drawn = Engine::get_singleton()->get_frames_drawn(); bool exit = Main::iteration(); if (r_should_swap_buffers) { - *r_should_swap_buffers = !is_in_low_processor_usage_mode() || RenderingServer::get_singleton()->has_changed() || current_frames_drawn != Engine::get_singleton()->get_frames_drawn(); + *r_should_swap_buffers = !is_in_low_processor_usage_mode() || + DisplayServerAndroid::get_singleton()->should_swap_buffers() || + RenderingServer::get_singleton()->has_changed() || + current_frames_drawn != Engine::get_singleton()->get_frames_drawn(); } return exit; |