Add missing display server overrides

Improves the base functionality for the Android platform and helps reduce the amount of spurious error logs emitted.
This commit is contained in:
Fredia Huya-Kouadio 2022-11-27 22:03:28 -08:00
parent 4a82d71d73
commit 7cc47613fe
10 changed files with 166 additions and 7 deletions

View File

@ -619,11 +619,11 @@ MouseButton DisplayServerAndroid::mouse_get_button_state() const {
return (MouseButton)Input::get_singleton()->get_mouse_button_mask(); 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()) { if (!OS_Android::get_singleton()->get_godot_java()->get_godot_view()->can_update_pointer_icon()) {
return; return;
} }
if (cursor_shape == p_shape) { if (cursor_shape == p_shape && !force) {
return; 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 { DisplayServer::CursorShape DisplayServerAndroid::cursor_get_shape() const {
return cursor_shape; 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) { void DisplayServerAndroid::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) {
#if defined(VULKAN_ENABLED) #if defined(VULKAN_ENABLED)
context_vulkan->set_vsync_mode(p_window, p_vsync_mode); 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; return DisplayServer::VSYNC_ENABLED;
#endif #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
}

View File

@ -66,6 +66,7 @@ class DisplayServerAndroid : public DisplayServer {
MouseMode mouse_mode = MouseMode::MOUSE_MODE_VISIBLE; MouseMode mouse_mode = MouseMode::MOUSE_MODE_VISIBLE;
bool keep_screen_on; bool keep_screen_on;
bool swap_buffers_flag;
CursorShape cursor_shape = CursorShape::CURSOR_ARROW; CursorShape cursor_shape = CursorShape::CURSOR_ARROW;
@ -188,8 +189,10 @@ public:
void process_magnetometer(const Vector3 &p_magnetometer); void process_magnetometer(const Vector3 &p_magnetometer);
void process_gyroscope(const Vector3 &p_gyroscope); 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 void cursor_set_shape(CursorShape p_shape) override;
virtual CursorShape cursor_get_shape() const 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 void mouse_set_mode(MouseMode p_mode) override;
virtual MouseMode mouse_get_mode() const override; virtual MouseMode mouse_get_mode() const override;
@ -204,6 +207,13 @@ public:
virtual Point2i mouse_get_position() const override; virtual Point2i mouse_get_position() const override;
virtual MouseButton mouse_get_button_state() 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(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(); ~DisplayServerAndroid();
}; };

View File

@ -175,6 +175,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
public GodotIO io; public GodotIO io;
public GodotNetUtils netUtils; public GodotNetUtils netUtils;
public GodotTTS tts; public GodotTTS tts;
DirectoryAccessHandler directoryAccessHandler;
public interface ResultCallback { public interface ResultCallback {
void callback(int requestCode, int resultCode, Intent data); void callback(int requestCode, int resultCode, Intent data);
@ -488,7 +489,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
netUtils = new GodotNetUtils(activity); netUtils = new GodotNetUtils(activity);
tts = new GodotTTS(activity); tts = new GodotTTS(activity);
Context context = getContext(); Context context = getContext();
DirectoryAccessHandler directoryAccessHandler = new DirectoryAccessHandler(context); directoryAccessHandler = new DirectoryAccessHandler(context);
FileAccessHandler fileAccessHandler = new FileAccessHandler(context); FileAccessHandler fileAccessHandler = new FileAccessHandler(context);
mSensorManager = (SensorManager)activity.getSystemService(Context.SENSOR_SERVICE); mSensorManager = (SensorManager)activity.getSystemService(Context.SENSOR_SERVICE);
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

View File

@ -43,8 +43,13 @@ import org.godotengine.godot.xr.regular.RegularFallbackConfigChooser;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.PixelFormat; import android.graphics.PixelFormat;
import android.os.Build; import android.os.Build;
import android.text.TextUtils;
import android.util.SparseArray;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.PointerIcon; import android.view.PointerIcon;
@ -52,6 +57,8 @@ import android.view.SurfaceView;
import androidx.annotation.Keep; import androidx.annotation.Keep;
import java.io.InputStream;
/** /**
* 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
@ -74,6 +81,7 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
private final Godot godot; private final Godot godot;
private final GodotInputHandler inputHandler; private final GodotInputHandler inputHandler;
private final GodotRenderer godotRenderer; private final GodotRenderer godotRenderer;
private final SparseArray<PointerIcon> customPointerIcons = new SparseArray<>();
public GodotGLRenderView(Context context, Godot godot, XRMode xrMode, boolean p_use_debug_opengl) { public GodotGLRenderView(Context context, Godot godot, XRMode xrMode, boolean p_use_debug_opengl) {
super(context); super(context);
@ -168,13 +176,50 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
inputHandler.onPointerCaptureChange(false); inputHandler.onPointerCaptureChange(false);
} }
/**
* 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 * called from JNI to change pointer icon
*/ */
@Keep @Keep
@Override
public void setPointerIcon(int pointerType) { public void setPointerIcon(int pointerType) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 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);
} }
} }

View File

@ -48,5 +48,7 @@ public interface GodotRenderView {
GodotInputHandler getInputHandler(); GodotInputHandler getInputHandler();
void configurePointerIcon(int pointerType, String imagePath, float hotSpotX, float hotSpotY);
void setPointerIcon(int pointerType); void setPointerIcon(int pointerType);
} }

View File

@ -36,7 +36,12 @@ import org.godotengine.godot.vulkan.VkSurfaceView;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Build; import android.os.Build;
import android.text.TextUtils;
import android.util.SparseArray;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.PointerIcon; import android.view.PointerIcon;
@ -44,10 +49,13 @@ import android.view.SurfaceView;
import androidx.annotation.Keep; import androidx.annotation.Keep;
import java.io.InputStream;
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 VkRenderer mRenderer; private final VkRenderer mRenderer;
private final SparseArray<PointerIcon> customPointerIcons = new SparseArray<>();
public GodotVulkanRenderView(Context context, Godot godot) { public GodotVulkanRenderView(Context context, Godot godot) {
super(context); super(context);
@ -142,13 +150,50 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV
mInputHandler.onPointerCaptureChange(hasCapture); mInputHandler.onPointerCaptureChange(hasCapture);
} }
/**
* 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 * called from JNI to change pointer icon
*/ */
@Keep @Keep
@Override
public void setPointerIcon(int pointerType) { public void setPointerIcon(int pointerType) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 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);
} }
} }

View File

@ -79,6 +79,9 @@ class DirectoryAccessHandler(context: Context) {
private val assetsDirAccess = AssetsDirectoryAccess(context) private val assetsDirAccess = AssetsDirectoryAccess(context)
private val fileSystemDirAccess = FilesystemDirectoryAccess(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 { private fun hasDirId(accessType: AccessType, dirId: Int): Boolean {
return when (accessType) { return when (accessType) {
ACCESS_RESOURCES -> assetsDirAccess.hasDirId(dirId) ACCESS_RESOURCES -> assetsDirAccess.hasDirId(dirId)

View File

@ -42,6 +42,7 @@ GodotJavaViewWrapper::GodotJavaViewWrapper(jobject godot_view) {
int android_device_api_level = android_get_device_api_level(); int android_device_api_level = android_get_device_api_level();
if (android_device_api_level >= __ANDROID_API_N__) { 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"); _set_pointer_icon = env->GetMethodID(_cls, "setPointerIcon", "(I)V");
} }
if (android_device_api_level >= __ANDROID_API_O__) { if (android_device_api_level >= __ANDROID_API_O__) {
@ -51,7 +52,7 @@ GodotJavaViewWrapper::GodotJavaViewWrapper(jobject godot_view) {
} }
bool GodotJavaViewWrapper::can_update_pointer_icon() const { 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 { 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) { 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

@ -31,6 +31,7 @@
#ifndef JAVA_GODOT_VIEW_WRAPPER_H #ifndef JAVA_GODOT_VIEW_WRAPPER_H
#define JAVA_GODOT_VIEW_WRAPPER_H #define JAVA_GODOT_VIEW_WRAPPER_H
#include "core/math/vector2.h"
#include <android/log.h> #include <android/log.h>
#include <jni.h> #include <jni.h>
@ -45,6 +46,8 @@ private:
jmethodID _request_pointer_capture = 0; jmethodID _request_pointer_capture = 0;
jmethodID _release_pointer_capture = 0; jmethodID _release_pointer_capture = 0;
jmethodID _configure_pointer_icon = 0;
jmethodID _set_pointer_icon = 0; jmethodID _set_pointer_icon = 0;
public: public:
@ -55,6 +58,8 @@ public:
void request_pointer_capture(); void request_pointer_capture();
void release_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); void set_pointer_icon(int pointer_type);
~GodotJavaViewWrapper(); ~GodotJavaViewWrapper();

View File

@ -268,12 +268,16 @@ bool OS_Android::main_loop_iterate(bool *r_should_swap_buffers) {
if (!main_loop) { if (!main_loop) {
return false; return false;
} }
DisplayServerAndroid::get_singleton()->reset_swap_buffers_flag();
DisplayServerAndroid::get_singleton()->process_events(); DisplayServerAndroid::get_singleton()->process_events();
uint64_t current_frames_drawn = Engine::get_singleton()->get_frames_drawn(); uint64_t current_frames_drawn = Engine::get_singleton()->get_frames_drawn();
bool exit = Main::iteration(); bool exit = Main::iteration();
if (r_should_swap_buffers) { 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; return exit;