Memory cleanup and optimizations
- Returns an empty list when there's not registered plugins, thus preventing the creation of spurious iterator objects - Inline `Godot#getRotatedValues(...)` given it only had a single caller. This allows to remove the allocation of a float array on each call and replace it with float variables - Disable sensor events by default. Sensor events can fired at 10-100s Hz taking cpu and memory resources. Now the use of sensor data is behind a project setting allowing projects that have use of it to enable it, while other projects don't pay the cost for a feature they don't use - Create a pool of specialized input `Runnable` objects to prevent spurious, unbounded `Runnable` allocations - Disable showing the boot logo for Android XR projects - Delete locale references of jni strings
This commit is contained in:
parent
2f2d1a7e68
commit
a57a99f5bc
|
@ -1570,6 +1570,11 @@ ProjectSettings::ProjectSettings() {
|
|||
|
||||
GLOBAL_DEF("collada/use_ambient", false);
|
||||
|
||||
// Input settings
|
||||
GLOBAL_DEF_BASIC("input_devices/pointing/android/enable_long_press_as_right_click", false);
|
||||
GLOBAL_DEF_BASIC("input_devices/pointing/android/enable_pan_and_scale_gestures", false);
|
||||
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "input_devices/pointing/android/rotary_input_scroll_axis", PROPERTY_HINT_ENUM, "Horizontal,Vertical"), 1);
|
||||
|
||||
// These properties will not show up in the dialog. If you want to exclude whole groups, use add_hidden_prefix().
|
||||
GLOBAL_DEF_INTERNAL("application/config/features", PackedStringArray());
|
||||
GLOBAL_DEF_INTERNAL("internationalization/locale/translation_remaps", PackedStringArray());
|
||||
|
|
|
@ -513,21 +513,49 @@ void Input::joy_connection_changed(int p_idx, bool p_connected, const String &p_
|
|||
|
||||
Vector3 Input::get_gravity() const {
|
||||
_THREAD_SAFE_METHOD_
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (!gravity_enabled) {
|
||||
WARN_PRINT_ONCE("`input_devices/sensors/enable_gravity` is not enabled in project settings.");
|
||||
}
|
||||
#endif
|
||||
|
||||
return gravity;
|
||||
}
|
||||
|
||||
Vector3 Input::get_accelerometer() const {
|
||||
_THREAD_SAFE_METHOD_
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (!accelerometer_enabled) {
|
||||
WARN_PRINT_ONCE("`input_devices/sensors/enable_accelerometer` is not enabled in project settings.");
|
||||
}
|
||||
#endif
|
||||
|
||||
return accelerometer;
|
||||
}
|
||||
|
||||
Vector3 Input::get_magnetometer() const {
|
||||
_THREAD_SAFE_METHOD_
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (!magnetometer_enabled) {
|
||||
WARN_PRINT_ONCE("`input_devices/sensors/enable_magnetometer` is not enabled in project settings.");
|
||||
}
|
||||
#endif
|
||||
|
||||
return magnetometer;
|
||||
}
|
||||
|
||||
Vector3 Input::get_gyroscope() const {
|
||||
_THREAD_SAFE_METHOD_
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (!gyroscope_enabled) {
|
||||
WARN_PRINT_ONCE("`input_devices/sensors/enable_gyroscope` is not enabled in project settings.");
|
||||
}
|
||||
#endif
|
||||
|
||||
return gyroscope;
|
||||
}
|
||||
|
||||
|
@ -1683,6 +1711,11 @@ Input::Input() {
|
|||
// Always use standard behavior in the editor.
|
||||
legacy_just_pressed_behavior = false;
|
||||
}
|
||||
|
||||
accelerometer_enabled = GLOBAL_DEF_RST_BASIC("input_devices/sensors/enable_accelerometer", false);
|
||||
gravity_enabled = GLOBAL_DEF_RST_BASIC("input_devices/sensors/enable_gravity", false);
|
||||
gyroscope_enabled = GLOBAL_DEF_RST_BASIC("input_devices/sensors/enable_gyroscope", false);
|
||||
magnetometer_enabled = GLOBAL_DEF_RST_BASIC("input_devices/sensors/enable_magnetometer", false);
|
||||
}
|
||||
|
||||
Input::~Input() {
|
||||
|
|
|
@ -92,9 +92,13 @@ private:
|
|||
RBSet<JoyButton> joy_buttons_pressed;
|
||||
RBMap<JoyAxis, float> _joy_axis;
|
||||
//RBMap<StringName,int> custom_action_press;
|
||||
bool gravity_enabled = false;
|
||||
Vector3 gravity;
|
||||
bool accelerometer_enabled = false;
|
||||
Vector3 accelerometer;
|
||||
bool magnetometer_enabled = false;
|
||||
Vector3 magnetometer;
|
||||
bool gyroscope_enabled = false;
|
||||
Vector3 gyroscope;
|
||||
Vector2 mouse_pos;
|
||||
int64_t mouse_window = 0;
|
||||
|
|
|
@ -1422,6 +1422,18 @@
|
|||
<member name="input_devices/pointing/emulate_touch_from_mouse" type="bool" setter="" getter="" default="false">
|
||||
If [code]true[/code], sends touch input events when clicking or dragging the mouse.
|
||||
</member>
|
||||
<member name="input_devices/sensors/enable_accelerometer" type="bool" setter="" getter="" default="false">
|
||||
If [code]true[/code], the accelerometer sensor is enabled and [method Input.get_accelerometer] returns valid data.
|
||||
</member>
|
||||
<member name="input_devices/sensors/enable_gravity" type="bool" setter="" getter="" default="false">
|
||||
If [code]true[/code], the gravity sensor is enabled and [method Input.get_gravity] returns valid data.
|
||||
</member>
|
||||
<member name="input_devices/sensors/enable_gyroscope" type="bool" setter="" getter="" default="false">
|
||||
If [code]true[/code], the gyroscope sensor is enabled and [method Input.get_gyroscope] returns valid data.
|
||||
</member>
|
||||
<member name="input_devices/sensors/enable_magnetometer" type="bool" setter="" getter="" default="false">
|
||||
If [code]true[/code], the magnetometer sensor is enabled and [method Input.get_magnetometer] returns valid data.
|
||||
</member>
|
||||
<member name="internationalization/locale/fallback" type="String" setter="" getter="" default=""en"">
|
||||
The locale to fall back to if a translation isn't available in a given language. If left empty, [code]en[/code] (English) will be used.
|
||||
</member>
|
||||
|
|
|
@ -2942,9 +2942,6 @@ Error Main::setup2(bool p_show_boot_logo) {
|
|||
id->set_emulate_mouse_from_touch(bool(GLOBAL_DEF_BASIC("input_devices/pointing/emulate_mouse_from_touch", true)));
|
||||
}
|
||||
|
||||
GLOBAL_DEF_BASIC("input_devices/pointing/android/enable_long_press_as_right_click", false);
|
||||
GLOBAL_DEF_BASIC("input_devices/pointing/android/enable_pan_and_scale_gestures", false);
|
||||
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "input_devices/pointing/android/rotary_input_scroll_axis", PROPERTY_HINT_ENUM, "Horizontal,Vertical"), 1);
|
||||
OS::get_singleton()->benchmark_end_measure("Startup", "Setup Window and Boot");
|
||||
}
|
||||
|
||||
|
|
|
@ -39,8 +39,6 @@ import android.content.res.Configuration
|
|||
import android.content.res.Resources
|
||||
import android.graphics.Color
|
||||
import android.hardware.Sensor
|
||||
import android.hardware.SensorEvent
|
||||
import android.hardware.SensorEventListener
|
||||
import android.hardware.SensorManager
|
||||
import android.os.*
|
||||
import android.util.Log
|
||||
|
@ -53,6 +51,7 @@ import androidx.core.view.WindowInsetsAnimationCompat
|
|||
import androidx.core.view.WindowInsetsCompat
|
||||
import com.google.android.vending.expansion.downloader.*
|
||||
import org.godotengine.godot.input.GodotEditText
|
||||
import org.godotengine.godot.input.GodotInputHandler
|
||||
import org.godotengine.godot.io.directory.DirectoryAccessHandler
|
||||
import org.godotengine.godot.io.file.FileAccessHandler
|
||||
import org.godotengine.godot.plugin.GodotPluginRegistry
|
||||
|
@ -73,6 +72,7 @@ import java.io.InputStream
|
|||
import java.lang.Exception
|
||||
import java.security.MessageDigest
|
||||
import java.util.*
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
|
||||
/**
|
||||
|
@ -81,7 +81,7 @@ import java.util.concurrent.atomic.AtomicReference
|
|||
* Can be hosted by [Activity], [Fragment] or [Service] android components, so long as its
|
||||
* lifecycle methods are properly invoked.
|
||||
*/
|
||||
class Godot(private val context: Context) : SensorEventListener {
|
||||
class Godot(private val context: Context) {
|
||||
|
||||
private companion object {
|
||||
private val TAG = Godot::class.java.simpleName
|
||||
|
@ -99,15 +99,23 @@ class Godot(private val context: Context) : SensorEventListener {
|
|||
private val pluginRegistry: GodotPluginRegistry by lazy {
|
||||
GodotPluginRegistry.getPluginRegistry()
|
||||
}
|
||||
|
||||
private val accelerometer_enabled = AtomicBoolean(false)
|
||||
private val mAccelerometer: Sensor? by lazy {
|
||||
mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
|
||||
}
|
||||
|
||||
private val gravity_enabled = AtomicBoolean(false)
|
||||
private val mGravity: Sensor? by lazy {
|
||||
mSensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY)
|
||||
}
|
||||
|
||||
private val magnetometer_enabled = AtomicBoolean(false)
|
||||
private val mMagnetometer: Sensor? by lazy {
|
||||
mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)
|
||||
}
|
||||
|
||||
private val gyroscope_enabled = AtomicBoolean(false)
|
||||
private val mGyroscope: Sensor? by lazy {
|
||||
mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)
|
||||
}
|
||||
|
@ -127,6 +135,7 @@ class Godot(private val context: Context) : SensorEventListener {
|
|||
val fileAccessHandler = FileAccessHandler(context)
|
||||
val netUtils = GodotNetUtils(context)
|
||||
private val commandLineFileParser = CommandLineFileParser()
|
||||
private val godotInputHandler = GodotInputHandler(context, this)
|
||||
|
||||
/**
|
||||
* Task to run when the engine terminates.
|
||||
|
@ -154,6 +163,17 @@ class Godot(private val context: Context) : SensorEventListener {
|
|||
private var renderViewInitialized = false
|
||||
private var primaryHost: GodotHost? = null
|
||||
|
||||
/**
|
||||
* Tracks whether we're in the RESUMED lifecycle state.
|
||||
* See [onResume] and [onPause]
|
||||
*/
|
||||
private var resumed = false
|
||||
|
||||
/**
|
||||
* Tracks whether [onGodotSetupCompleted] fired.
|
||||
*/
|
||||
private val godotMainLoopStarted = AtomicBoolean(false)
|
||||
|
||||
var io: GodotIO? = null
|
||||
|
||||
private var commandLine : MutableList<String> = ArrayList<String>()
|
||||
|
@ -416,10 +436,10 @@ class Godot(private val context: Context) : SensorEventListener {
|
|||
if (!meetsVulkanRequirements(activity.packageManager)) {
|
||||
throw IllegalStateException(activity.getString(R.string.error_missing_vulkan_requirements_message))
|
||||
}
|
||||
GodotVulkanRenderView(host, this)
|
||||
GodotVulkanRenderView(host, this, godotInputHandler)
|
||||
} else {
|
||||
// Fallback to openGl
|
||||
GodotGLRenderView(host, this, xrMode, useDebugOpengl)
|
||||
GodotGLRenderView(host, this, godotInputHandler, xrMode, useDebugOpengl)
|
||||
}
|
||||
|
||||
if (host == primaryHost) {
|
||||
|
@ -520,23 +540,13 @@ class Godot(private val context: Context) : SensorEventListener {
|
|||
|
||||
fun onResume(host: GodotHost) {
|
||||
Log.v(TAG, "OnResume: $host")
|
||||
resumed = true
|
||||
if (host != primaryHost) {
|
||||
return
|
||||
}
|
||||
|
||||
renderView?.onActivityResumed()
|
||||
if (mAccelerometer != null) {
|
||||
mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME)
|
||||
}
|
||||
if (mGravity != null) {
|
||||
mSensorManager.registerListener(this, mGravity, SensorManager.SENSOR_DELAY_GAME)
|
||||
}
|
||||
if (mMagnetometer != null) {
|
||||
mSensorManager.registerListener(this, mMagnetometer, SensorManager.SENSOR_DELAY_GAME)
|
||||
}
|
||||
if (mGyroscope != null) {
|
||||
mSensorManager.registerListener(this, mGyroscope, SensorManager.SENSOR_DELAY_GAME)
|
||||
}
|
||||
registerSensorsIfNeeded()
|
||||
if (useImmersive) {
|
||||
val window = requireActivity().window
|
||||
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
|
||||
|
@ -551,14 +561,34 @@ class Godot(private val context: Context) : SensorEventListener {
|
|||
}
|
||||
}
|
||||
|
||||
private fun registerSensorsIfNeeded() {
|
||||
if (!resumed || !godotMainLoopStarted.get()) {
|
||||
return
|
||||
}
|
||||
|
||||
if (accelerometer_enabled.get() && mAccelerometer != null) {
|
||||
mSensorManager.registerListener(godotInputHandler, mAccelerometer, SensorManager.SENSOR_DELAY_GAME)
|
||||
}
|
||||
if (gravity_enabled.get() && mGravity != null) {
|
||||
mSensorManager.registerListener(godotInputHandler, mGravity, SensorManager.SENSOR_DELAY_GAME)
|
||||
}
|
||||
if (magnetometer_enabled.get() && mMagnetometer != null) {
|
||||
mSensorManager.registerListener(godotInputHandler, mMagnetometer, SensorManager.SENSOR_DELAY_GAME)
|
||||
}
|
||||
if (gyroscope_enabled.get() && mGyroscope != null) {
|
||||
mSensorManager.registerListener(godotInputHandler, mGyroscope, SensorManager.SENSOR_DELAY_GAME)
|
||||
}
|
||||
}
|
||||
|
||||
fun onPause(host: GodotHost) {
|
||||
Log.v(TAG, "OnPause: $host")
|
||||
resumed = false
|
||||
if (host != primaryHost) {
|
||||
return
|
||||
}
|
||||
|
||||
renderView?.onActivityPaused()
|
||||
mSensorManager.unregisterListener(this)
|
||||
mSensorManager.unregisterListener(godotInputHandler)
|
||||
for (plugin in pluginRegistry.allPlugins) {
|
||||
plugin.onMainPause()
|
||||
}
|
||||
|
@ -659,6 +689,16 @@ class Godot(private val context: Context) : SensorEventListener {
|
|||
*/
|
||||
private fun onGodotMainLoopStarted() {
|
||||
Log.v(TAG, "OnGodotMainLoopStarted")
|
||||
godotMainLoopStarted.set(true)
|
||||
|
||||
accelerometer_enabled.set(java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/sensors/enable_accelerometer")))
|
||||
gravity_enabled.set(java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/sensors/enable_gravity")))
|
||||
gyroscope_enabled.set(java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/sensors/enable_gyroscope")))
|
||||
magnetometer_enabled.set(java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/sensors/enable_magnetometer")))
|
||||
|
||||
runOnUiThread {
|
||||
registerSensorsIfNeeded()
|
||||
}
|
||||
|
||||
for (plugin in pluginRegistry.allPlugins) {
|
||||
plugin.onGodotMainLoopStarted()
|
||||
|
@ -858,77 +898,6 @@ class Godot(private val context: Context) : SensorEventListener {
|
|||
}
|
||||
}
|
||||
|
||||
private fun getRotatedValues(values: FloatArray?): FloatArray? {
|
||||
if (values == null || values.size != 3) {
|
||||
return null
|
||||
}
|
||||
val rotatedValues = FloatArray(3)
|
||||
when (windowManager.defaultDisplay.rotation) {
|
||||
Surface.ROTATION_0 -> {
|
||||
rotatedValues[0] = values[0]
|
||||
rotatedValues[1] = values[1]
|
||||
rotatedValues[2] = values[2]
|
||||
}
|
||||
Surface.ROTATION_90 -> {
|
||||
rotatedValues[0] = -values[1]
|
||||
rotatedValues[1] = values[0]
|
||||
rotatedValues[2] = values[2]
|
||||
}
|
||||
Surface.ROTATION_180 -> {
|
||||
rotatedValues[0] = -values[0]
|
||||
rotatedValues[1] = -values[1]
|
||||
rotatedValues[2] = values[2]
|
||||
}
|
||||
Surface.ROTATION_270 -> {
|
||||
rotatedValues[0] = values[1]
|
||||
rotatedValues[1] = -values[0]
|
||||
rotatedValues[2] = values[2]
|
||||
}
|
||||
}
|
||||
return rotatedValues
|
||||
}
|
||||
|
||||
override fun onSensorChanged(event: SensorEvent) {
|
||||
if (renderView == null) {
|
||||
return
|
||||
}
|
||||
|
||||
val rotatedValues = getRotatedValues(event.values)
|
||||
|
||||
when (event.sensor.type) {
|
||||
Sensor.TYPE_ACCELEROMETER -> {
|
||||
rotatedValues?.let {
|
||||
renderView?.queueOnRenderThread {
|
||||
GodotLib.accelerometer(-it[0], -it[1], -it[2])
|
||||
}
|
||||
}
|
||||
}
|
||||
Sensor.TYPE_GRAVITY -> {
|
||||
rotatedValues?.let {
|
||||
renderView?.queueOnRenderThread {
|
||||
GodotLib.gravity(-it[0], -it[1], -it[2])
|
||||
}
|
||||
}
|
||||
}
|
||||
Sensor.TYPE_MAGNETIC_FIELD -> {
|
||||
rotatedValues?.let {
|
||||
renderView?.queueOnRenderThread {
|
||||
GodotLib.magnetometer(-it[0], -it[1], -it[2])
|
||||
}
|
||||
}
|
||||
}
|
||||
Sensor.TYPE_GYROSCOPE -> {
|
||||
rotatedValues?.let {
|
||||
renderView?.queueOnRenderThread {
|
||||
GodotLib.gyroscope(it[0], it[1], it[2])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {}
|
||||
|
||||
/**
|
||||
* Used by the native code (java_godot_wrapper.h) to vibrate the device.
|
||||
* @param durationMs
|
||||
|
@ -1063,7 +1032,7 @@ class Godot(private val context: Context) : SensorEventListener {
|
|||
|
||||
@Keep
|
||||
private fun initInputDevices() {
|
||||
renderView?.initInputDevices()
|
||||
godotInputHandler.initInputDevices()
|
||||
}
|
||||
|
||||
@Keep
|
||||
|
|
|
@ -83,12 +83,12 @@ class GodotGLRenderView extends GLSurfaceView implements GodotRenderView {
|
|||
private final GodotRenderer godotRenderer;
|
||||
private final SparseArray<PointerIcon> customPointerIcons = new SparseArray<>();
|
||||
|
||||
public GodotGLRenderView(GodotHost host, Godot godot, XRMode xrMode, boolean useDebugOpengl) {
|
||||
public GodotGLRenderView(GodotHost host, Godot godot, GodotInputHandler inputHandler, XRMode xrMode, boolean useDebugOpengl) {
|
||||
super(host.getActivity());
|
||||
|
||||
this.host = host;
|
||||
this.godot = godot;
|
||||
this.inputHandler = new GodotInputHandler(this);
|
||||
this.inputHandler = inputHandler;
|
||||
this.godotRenderer = new GodotRenderer();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
setPointerIcon(PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT));
|
||||
|
@ -101,11 +101,6 @@ class GodotGLRenderView extends GLSurfaceView implements GodotRenderView {
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initInputDevices() {
|
||||
this.inputHandler.initInputDevices();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void queueOnRenderThread(Runnable event) {
|
||||
queueEvent(event);
|
||||
|
@ -144,11 +139,6 @@ class GodotGLRenderView extends GLSurfaceView implements GodotRenderView {
|
|||
requestRenderThreadExitAndWait();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
godot.onBackPressed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GodotInputHandler getInputHandler() {
|
||||
return inputHandler;
|
||||
|
|
|
@ -37,8 +37,6 @@ import android.view.SurfaceView;
|
|||
public interface GodotRenderView {
|
||||
SurfaceView getView();
|
||||
|
||||
void initInputDevices();
|
||||
|
||||
/**
|
||||
* Starts the thread that will drive Godot's rendering.
|
||||
*/
|
||||
|
@ -59,8 +57,6 @@ public interface GodotRenderView {
|
|||
|
||||
void onActivityDestroyed();
|
||||
|
||||
void onBackPressed();
|
||||
|
||||
GodotInputHandler getInputHandler();
|
||||
|
||||
void configurePointerIcon(int pointerType, String imagePath, float hotSpotX, float hotSpotY);
|
||||
|
|
|
@ -57,12 +57,12 @@ class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderView {
|
|||
private final VkRenderer mRenderer;
|
||||
private final SparseArray<PointerIcon> customPointerIcons = new SparseArray<>();
|
||||
|
||||
public GodotVulkanRenderView(GodotHost host, Godot godot) {
|
||||
public GodotVulkanRenderView(GodotHost host, Godot godot, GodotInputHandler inputHandler) {
|
||||
super(host.getActivity());
|
||||
|
||||
this.host = host;
|
||||
this.godot = godot;
|
||||
mInputHandler = new GodotInputHandler(this);
|
||||
mInputHandler = inputHandler;
|
||||
mRenderer = new VkRenderer();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
setPointerIcon(PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT));
|
||||
|
@ -80,11 +80,6 @@ class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderView {
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initInputDevices() {
|
||||
mInputHandler.initInputDevices();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void queueOnRenderThread(Runnable event) {
|
||||
queueOnVkThread(event);
|
||||
|
@ -123,11 +118,6 @@ class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderView {
|
|||
requestRenderThreadExitAndWait();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
godot.onBackPressed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GodotInputHandler getInputHandler() {
|
||||
return mInputHandler;
|
||||
|
|
|
@ -32,10 +32,14 @@ package org.godotengine.godot.input;
|
|||
|
||||
import static org.godotengine.godot.utils.GLUtils.DEBUG;
|
||||
|
||||
import org.godotengine.godot.Godot;
|
||||
import org.godotengine.godot.GodotLib;
|
||||
import org.godotengine.godot.GodotRenderView;
|
||||
|
||||
import android.content.Context;
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
import android.hardware.input.InputManager;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
@ -46,6 +50,10 @@ import android.view.InputDevice;
|
|||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.ScaleGestureDetector;
|
||||
import android.view.Surface;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
|
@ -54,7 +62,7 @@ import java.util.Set;
|
|||
/**
|
||||
* Handles input related events for the {@link GodotRenderView} view.
|
||||
*/
|
||||
public class GodotInputHandler implements InputManager.InputDeviceListener {
|
||||
public class GodotInputHandler implements InputManager.InputDeviceListener, SensorEventListener {
|
||||
private static final String TAG = GodotInputHandler.class.getSimpleName();
|
||||
|
||||
private static final int ROTARY_INPUT_VERTICAL_AXIS = 1;
|
||||
|
@ -64,8 +72,9 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
|
|||
private final SparseArray<Joystick> mJoysticksDevices = new SparseArray<>(4);
|
||||
private final HashSet<Integer> mHardwareKeyboardIds = new HashSet<>();
|
||||
|
||||
private final GodotRenderView mRenderView;
|
||||
private final Godot godot;
|
||||
private final InputManager mInputManager;
|
||||
private final WindowManager windowManager;
|
||||
private final GestureDetector gestureDetector;
|
||||
private final ScaleGestureDetector scaleGestureDetector;
|
||||
private final GodotGestureHandler godotGestureHandler;
|
||||
|
@ -77,12 +86,13 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
|
|||
|
||||
private int rotaryInputAxis = ROTARY_INPUT_VERTICAL_AXIS;
|
||||
|
||||
public GodotInputHandler(GodotRenderView godotView) {
|
||||
final Context context = godotView.getView().getContext();
|
||||
mRenderView = godotView;
|
||||
public GodotInputHandler(Context context, Godot godot) {
|
||||
this.godot = godot;
|
||||
mInputManager = (InputManager)context.getSystemService(Context.INPUT_SERVICE);
|
||||
mInputManager.registerInputDeviceListener(this, null);
|
||||
|
||||
windowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
|
||||
|
||||
this.godotGestureHandler = new GodotGestureHandler(this);
|
||||
this.gestureDetector = new GestureDetector(context, godotGestureHandler);
|
||||
this.gestureDetector.setIsLongpressEnabled(false);
|
||||
|
@ -174,7 +184,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
|
|||
|
||||
public boolean onKeyDown(final int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
mRenderView.onBackPressed();
|
||||
godot.onBackPressed();
|
||||
// press 'back' button should not terminate program
|
||||
//normal handle 'back' event in game logic
|
||||
return true;
|
||||
|
@ -507,7 +517,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
|
|||
return handleTouchEvent(event, eventActionOverride, doubleTap);
|
||||
}
|
||||
|
||||
private static float getEventTiltX(MotionEvent event) {
|
||||
static float getEventTiltX(MotionEvent event) {
|
||||
// Orientation is returned as a radian value between 0 to pi clockwise or 0 to -pi counterclockwise.
|
||||
final float orientation = event.getOrientation();
|
||||
|
||||
|
@ -520,7 +530,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
|
|||
return (float)-Math.sin(orientation) * tiltMult;
|
||||
}
|
||||
|
||||
private static float getEventTiltY(MotionEvent event) {
|
||||
static float getEventTiltY(MotionEvent event) {
|
||||
// Orientation is returned as a radian value between 0 to pi clockwise or 0 to -pi counterclockwise.
|
||||
final float orientation = event.getOrientation();
|
||||
|
||||
|
@ -579,6 +589,11 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
|
|||
}
|
||||
|
||||
boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY, boolean doubleClick, boolean sourceMouseRelative, float pressure, float tiltX, float tiltY) {
|
||||
InputEventRunnable runnable = InputEventRunnable.obtain();
|
||||
if (runnable == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fix the buttonsMask
|
||||
switch (eventAction) {
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
|
@ -594,7 +609,6 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
|
|||
break;
|
||||
}
|
||||
|
||||
final int updatedButtonsMask = buttonsMask;
|
||||
// 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.
|
||||
|
@ -607,11 +621,8 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
|
|||
case MotionEvent.ACTION_HOVER_MOVE:
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
case MotionEvent.ACTION_SCROLL: {
|
||||
if (shouldDispatchInputToRenderThread()) {
|
||||
mRenderView.queueOnRenderThread(() -> GodotLib.dispatchMouseEvent(eventAction, updatedButtonsMask, x, y, deltaX, deltaY, doubleClick, sourceMouseRelative, pressure, tiltX, tiltY));
|
||||
} else {
|
||||
GodotLib.dispatchMouseEvent(eventAction, updatedButtonsMask, x, y, deltaX, deltaY, doubleClick, sourceMouseRelative, pressure, tiltX, tiltY);
|
||||
}
|
||||
runnable.setMouseEvent(eventAction, buttonsMask, x, y, deltaX, deltaY, doubleClick, sourceMouseRelative, pressure, tiltX, tiltY);
|
||||
dispatchInputEventRunnable(runnable);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -627,22 +638,14 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
|
|||
}
|
||||
|
||||
boolean handleTouchEvent(final MotionEvent event, int eventActionOverride, boolean doubleTap) {
|
||||
final int pointerCount = event.getPointerCount();
|
||||
if (pointerCount == 0) {
|
||||
if (event.getPointerCount() == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final float[] positions = new float[pointerCount * 6]; // pointerId1, x1, y1, pressure1, tiltX1, tiltY1, pointerId2, etc...
|
||||
|
||||
for (int i = 0; i < pointerCount; i++) {
|
||||
positions[i * 6 + 0] = event.getPointerId(i);
|
||||
positions[i * 6 + 1] = event.getX(i);
|
||||
positions[i * 6 + 2] = event.getY(i);
|
||||
positions[i * 6 + 3] = event.getPressure(i);
|
||||
positions[i * 6 + 4] = getEventTiltX(event);
|
||||
positions[i * 6 + 5] = getEventTiltY(event);
|
||||
InputEventRunnable runnable = InputEventRunnable.obtain();
|
||||
if (runnable == null) {
|
||||
return false;
|
||||
}
|
||||
final int actionPointerId = event.getPointerId(event.getActionIndex());
|
||||
|
||||
switch (eventActionOverride) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
|
@ -651,11 +654,8 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
|
|||
case MotionEvent.ACTION_MOVE:
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
case MotionEvent.ACTION_POINTER_DOWN: {
|
||||
if (shouldDispatchInputToRenderThread()) {
|
||||
mRenderView.queueOnRenderThread(() -> GodotLib.dispatchTouchEvent(eventActionOverride, actionPointerId, pointerCount, positions, doubleTap));
|
||||
} else {
|
||||
GodotLib.dispatchTouchEvent(eventActionOverride, actionPointerId, pointerCount, positions, doubleTap);
|
||||
}
|
||||
runnable.setTouchEvent(event, eventActionOverride, doubleTap);
|
||||
dispatchInputEventRunnable(runnable);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -663,58 +663,128 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
|
|||
}
|
||||
|
||||
void handleMagnifyEvent(float x, float y, float factor) {
|
||||
if (shouldDispatchInputToRenderThread()) {
|
||||
mRenderView.queueOnRenderThread(() -> GodotLib.magnify(x, y, factor));
|
||||
} else {
|
||||
GodotLib.magnify(x, y, factor);
|
||||
InputEventRunnable runnable = InputEventRunnable.obtain();
|
||||
if (runnable == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
runnable.setMagnifyEvent(x, y, factor);
|
||||
dispatchInputEventRunnable(runnable);
|
||||
}
|
||||
|
||||
void handlePanEvent(float x, float y, float deltaX, float deltaY) {
|
||||
if (shouldDispatchInputToRenderThread()) {
|
||||
mRenderView.queueOnRenderThread(() -> GodotLib.pan(x, y, deltaX, deltaY));
|
||||
} else {
|
||||
GodotLib.pan(x, y, deltaX, deltaY);
|
||||
InputEventRunnable runnable = InputEventRunnable.obtain();
|
||||
if (runnable == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
runnable.setPanEvent(x, y, deltaX, deltaY);
|
||||
dispatchInputEventRunnable(runnable);
|
||||
}
|
||||
|
||||
private void handleJoystickButtonEvent(int device, int button, boolean pressed) {
|
||||
if (shouldDispatchInputToRenderThread()) {
|
||||
mRenderView.queueOnRenderThread(() -> GodotLib.joybutton(device, button, pressed));
|
||||
} else {
|
||||
GodotLib.joybutton(device, button, pressed);
|
||||
InputEventRunnable runnable = InputEventRunnable.obtain();
|
||||
if (runnable == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
runnable.setJoystickButtonEvent(device, button, pressed);
|
||||
dispatchInputEventRunnable(runnable);
|
||||
}
|
||||
|
||||
private void handleJoystickAxisEvent(int device, int axis, float value) {
|
||||
if (shouldDispatchInputToRenderThread()) {
|
||||
mRenderView.queueOnRenderThread(() -> GodotLib.joyaxis(device, axis, value));
|
||||
} else {
|
||||
GodotLib.joyaxis(device, axis, value);
|
||||
InputEventRunnable runnable = InputEventRunnable.obtain();
|
||||
if (runnable == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
runnable.setJoystickAxisEvent(device, axis, value);
|
||||
dispatchInputEventRunnable(runnable);
|
||||
}
|
||||
|
||||
private void handleJoystickHatEvent(int device, int hatX, int hatY) {
|
||||
if (shouldDispatchInputToRenderThread()) {
|
||||
mRenderView.queueOnRenderThread(() -> GodotLib.joyhat(device, hatX, hatY));
|
||||
} else {
|
||||
GodotLib.joyhat(device, hatX, hatY);
|
||||
InputEventRunnable runnable = InputEventRunnable.obtain();
|
||||
if (runnable == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
runnable.setJoystickHatEvent(device, hatX, hatY);
|
||||
dispatchInputEventRunnable(runnable);
|
||||
}
|
||||
|
||||
private void handleJoystickConnectionChangedEvent(int device, boolean connected, String name) {
|
||||
if (shouldDispatchInputToRenderThread()) {
|
||||
mRenderView.queueOnRenderThread(() -> GodotLib.joyconnectionchanged(device, connected, name));
|
||||
} else {
|
||||
GodotLib.joyconnectionchanged(device, connected, name);
|
||||
InputEventRunnable runnable = InputEventRunnable.obtain();
|
||||
if (runnable == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
runnable.setJoystickConnectionChangedEvent(device, connected, name);
|
||||
dispatchInputEventRunnable(runnable);
|
||||
}
|
||||
|
||||
void handleKeyEvent(int physicalKeycode, int unicode, int keyLabel, boolean pressed, boolean echo) {
|
||||
InputEventRunnable runnable = InputEventRunnable.obtain();
|
||||
if (runnable == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
runnable.setKeyEvent(physicalKeycode, unicode, keyLabel, pressed, echo);
|
||||
dispatchInputEventRunnable(runnable);
|
||||
}
|
||||
|
||||
private void dispatchInputEventRunnable(@NonNull InputEventRunnable runnable) {
|
||||
if (shouldDispatchInputToRenderThread()) {
|
||||
mRenderView.queueOnRenderThread(() -> GodotLib.key(physicalKeycode, unicode, keyLabel, pressed, echo));
|
||||
godot.runOnRenderThread(runnable);
|
||||
} else {
|
||||
GodotLib.key(physicalKeycode, unicode, keyLabel, pressed, echo);
|
||||
runnable.run();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSensorChanged(SensorEvent event) {
|
||||
final float[] values = event.values;
|
||||
if (values == null || values.length != 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
InputEventRunnable runnable = InputEventRunnable.obtain();
|
||||
if (runnable == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
float rotatedValue0 = 0f;
|
||||
float rotatedValue1 = 0f;
|
||||
float rotatedValue2 = 0f;
|
||||
switch (windowManager.getDefaultDisplay().getRotation()) {
|
||||
case Surface.ROTATION_0:
|
||||
rotatedValue0 = values[0];
|
||||
rotatedValue1 = values[1];
|
||||
rotatedValue2 = values[2];
|
||||
break;
|
||||
|
||||
case Surface.ROTATION_90:
|
||||
rotatedValue0 = -values[1];
|
||||
rotatedValue1 = values[0];
|
||||
rotatedValue2 = values[2];
|
||||
break;
|
||||
|
||||
case Surface.ROTATION_180:
|
||||
rotatedValue0 = -values[0];
|
||||
rotatedValue1 = -values[1];
|
||||
rotatedValue2 = values[2];
|
||||
break;
|
||||
|
||||
case Surface.ROTATION_270:
|
||||
rotatedValue0 = values[1];
|
||||
rotatedValue1 = -values[0];
|
||||
rotatedValue2 = values[2];
|
||||
break;
|
||||
}
|
||||
|
||||
runnable.setSensorEvent(event.sensor.getType(), rotatedValue0, rotatedValue1, rotatedValue2);
|
||||
godot.runOnRenderThread(runnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccuracyChanged(Sensor sensor, int accuracy) {}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,353 @@
|
|||
/**************************************************************************/
|
||||
/* InputEventRunnable.java */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
package org.godotengine.godot.input;
|
||||
|
||||
import org.godotengine.godot.GodotLib;
|
||||
|
||||
import android.hardware.Sensor;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.util.Pools;
|
||||
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* Used to dispatch input events.
|
||||
*
|
||||
* This is a specialized version of @{@link Runnable} which allows to allocate a finite pool of
|
||||
* objects for input events dispatching, thus avoid the creation (and garbage collection) of
|
||||
* spurious @{@link Runnable} objects.
|
||||
*/
|
||||
final class InputEventRunnable implements Runnable {
|
||||
private static final String TAG = InputEventRunnable.class.getSimpleName();
|
||||
|
||||
private static final int MAX_TOUCH_POINTER_COUNT = 10; // assuming 10 fingers as max supported concurrent touch pointers
|
||||
|
||||
private static final Pools.Pool<InputEventRunnable> POOL = new Pools.Pool<>() {
|
||||
private static final int MAX_POOL_SIZE = 120 * 10; // up to 120Hz input events rate for up to 5 secs (ANR limit) * 2
|
||||
|
||||
private final ArrayBlockingQueue<InputEventRunnable> queue = new ArrayBlockingQueue<>(MAX_POOL_SIZE);
|
||||
private final AtomicInteger createdCount = new AtomicInteger();
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public InputEventRunnable acquire() {
|
||||
InputEventRunnable instance = queue.poll();
|
||||
if (instance == null) {
|
||||
int creationCount = createdCount.incrementAndGet();
|
||||
if (creationCount <= MAX_POOL_SIZE) {
|
||||
instance = new InputEventRunnable(creationCount - 1);
|
||||
}
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean release(@NonNull InputEventRunnable instance) {
|
||||
return queue.offer(instance);
|
||||
}
|
||||
};
|
||||
|
||||
@Nullable
|
||||
static InputEventRunnable obtain() {
|
||||
InputEventRunnable runnable = POOL.acquire();
|
||||
if (runnable == null) {
|
||||
Log.w(TAG, "Input event pool is at capacity");
|
||||
}
|
||||
return runnable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to track when this instance was created and added to the pool. Primarily used for
|
||||
* debug purposes.
|
||||
*/
|
||||
private final int creationRank;
|
||||
|
||||
private InputEventRunnable(int creationRank) {
|
||||
this.creationRank = creationRank;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set of supported input events.
|
||||
*/
|
||||
private enum EventType {
|
||||
MOUSE,
|
||||
TOUCH,
|
||||
MAGNIFY,
|
||||
PAN,
|
||||
JOYSTICK_BUTTON,
|
||||
JOYSTICK_AXIS,
|
||||
JOYSTICK_HAT,
|
||||
JOYSTICK_CONNECTION_CHANGED,
|
||||
KEY,
|
||||
SENSOR
|
||||
}
|
||||
|
||||
private EventType currentEventType = null;
|
||||
|
||||
// common event fields
|
||||
private float eventX;
|
||||
private float eventY;
|
||||
private float eventDeltaX;
|
||||
private float eventDeltaY;
|
||||
private boolean eventPressed;
|
||||
|
||||
// common touch / mouse fields
|
||||
private int eventAction;
|
||||
private boolean doubleTap;
|
||||
|
||||
// Mouse event fields and setter
|
||||
private int buttonsMask;
|
||||
private boolean sourceMouseRelative;
|
||||
private float pressure;
|
||||
private float tiltX;
|
||||
private float tiltY;
|
||||
void setMouseEvent(int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY, boolean doubleClick, boolean sourceMouseRelative, float pressure, float tiltX, float tiltY) {
|
||||
this.currentEventType = EventType.MOUSE;
|
||||
this.eventAction = eventAction;
|
||||
this.buttonsMask = buttonsMask;
|
||||
this.eventX = x;
|
||||
this.eventY = y;
|
||||
this.eventDeltaX = deltaX;
|
||||
this.eventDeltaY = deltaY;
|
||||
this.doubleTap = doubleClick;
|
||||
this.sourceMouseRelative = sourceMouseRelative;
|
||||
this.pressure = pressure;
|
||||
this.tiltX = tiltX;
|
||||
this.tiltY = tiltY;
|
||||
}
|
||||
|
||||
// Touch event fields and setter
|
||||
private int actionPointerId;
|
||||
private int pointerCount;
|
||||
private final float[] positions = new float[MAX_TOUCH_POINTER_COUNT * 6]; // pointerId1, x1, y1, pressure1, tiltX1, tiltY1, pointerId2, etc...
|
||||
void setTouchEvent(MotionEvent event, int eventAction, boolean doubleTap) {
|
||||
this.currentEventType = EventType.TOUCH;
|
||||
this.eventAction = eventAction;
|
||||
this.doubleTap = doubleTap;
|
||||
this.actionPointerId = event.getPointerId(event.getActionIndex());
|
||||
this.pointerCount = Math.min(event.getPointerCount(), MAX_TOUCH_POINTER_COUNT);
|
||||
for (int i = 0; i < pointerCount; i++) {
|
||||
positions[i * 6 + 0] = event.getPointerId(i);
|
||||
positions[i * 6 + 1] = event.getX(i);
|
||||
positions[i * 6 + 2] = event.getY(i);
|
||||
positions[i * 6 + 3] = event.getPressure(i);
|
||||
positions[i * 6 + 4] = GodotInputHandler.getEventTiltX(event);
|
||||
positions[i * 6 + 5] = GodotInputHandler.getEventTiltY(event);
|
||||
}
|
||||
}
|
||||
|
||||
// Magnify event fields and setter
|
||||
private float magnifyFactor;
|
||||
void setMagnifyEvent(float x, float y, float factor) {
|
||||
this.currentEventType = EventType.MAGNIFY;
|
||||
this.eventX = x;
|
||||
this.eventY = y;
|
||||
this.magnifyFactor = factor;
|
||||
}
|
||||
|
||||
// Pan event setter
|
||||
void setPanEvent(float x, float y, float deltaX, float deltaY) {
|
||||
this.currentEventType = EventType.PAN;
|
||||
this.eventX = x;
|
||||
this.eventY = y;
|
||||
this.eventDeltaX = deltaX;
|
||||
this.eventDeltaY = deltaY;
|
||||
}
|
||||
|
||||
// common joystick field
|
||||
private int joystickDevice;
|
||||
|
||||
// Joystick button event fields and setter
|
||||
private int button;
|
||||
void setJoystickButtonEvent(int device, int button, boolean pressed) {
|
||||
this.currentEventType = EventType.JOYSTICK_BUTTON;
|
||||
this.joystickDevice = device;
|
||||
this.button = button;
|
||||
this.eventPressed = pressed;
|
||||
}
|
||||
|
||||
// Joystick axis event fields and setter
|
||||
private int axis;
|
||||
private float value;
|
||||
void setJoystickAxisEvent(int device, int axis, float value) {
|
||||
this.currentEventType = EventType.JOYSTICK_AXIS;
|
||||
this.joystickDevice = device;
|
||||
this.axis = axis;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
// Joystick hat event fields and setter
|
||||
private int hatX;
|
||||
private int hatY;
|
||||
void setJoystickHatEvent(int device, int hatX, int hatY) {
|
||||
this.currentEventType = EventType.JOYSTICK_HAT;
|
||||
this.joystickDevice = device;
|
||||
this.hatX = hatX;
|
||||
this.hatY = hatY;
|
||||
}
|
||||
|
||||
// Joystick connection changed event fields and setter
|
||||
private boolean connected;
|
||||
private String joystickName;
|
||||
void setJoystickConnectionChangedEvent(int device, boolean connected, String name) {
|
||||
this.currentEventType = EventType.JOYSTICK_CONNECTION_CHANGED;
|
||||
this.joystickDevice = device;
|
||||
this.connected = connected;
|
||||
this.joystickName = name;
|
||||
}
|
||||
|
||||
// Key event fields and setter
|
||||
private int physicalKeycode;
|
||||
private int unicode;
|
||||
private int keyLabel;
|
||||
private boolean echo;
|
||||
void setKeyEvent(int physicalKeycode, int unicode, int keyLabel, boolean pressed, boolean echo) {
|
||||
this.currentEventType = EventType.KEY;
|
||||
this.physicalKeycode = physicalKeycode;
|
||||
this.unicode = unicode;
|
||||
this.keyLabel = keyLabel;
|
||||
this.eventPressed = pressed;
|
||||
this.echo = echo;
|
||||
}
|
||||
|
||||
// Sensor event fields and setter
|
||||
private int sensorType;
|
||||
private float rotatedValue0;
|
||||
private float rotatedValue1;
|
||||
private float rotatedValue2;
|
||||
void setSensorEvent(int sensorType, float rotatedValue0, float rotatedValue1, float rotatedValue2) {
|
||||
this.currentEventType = EventType.SENSOR;
|
||||
this.sensorType = sensorType;
|
||||
this.rotatedValue0 = rotatedValue0;
|
||||
this.rotatedValue1 = rotatedValue1;
|
||||
this.rotatedValue2 = rotatedValue2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
if (currentEventType == null) {
|
||||
Log.w(TAG, "Invalid event type");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (currentEventType) {
|
||||
case MOUSE:
|
||||
GodotLib.dispatchMouseEvent(
|
||||
eventAction,
|
||||
buttonsMask,
|
||||
eventX,
|
||||
eventY,
|
||||
eventDeltaX,
|
||||
eventDeltaY,
|
||||
doubleTap,
|
||||
sourceMouseRelative,
|
||||
pressure,
|
||||
tiltX,
|
||||
tiltY);
|
||||
break;
|
||||
|
||||
case TOUCH:
|
||||
GodotLib.dispatchTouchEvent(
|
||||
eventAction,
|
||||
actionPointerId,
|
||||
pointerCount,
|
||||
positions,
|
||||
doubleTap);
|
||||
break;
|
||||
|
||||
case MAGNIFY:
|
||||
GodotLib.magnify(eventX, eventY, magnifyFactor);
|
||||
break;
|
||||
|
||||
case PAN:
|
||||
GodotLib.pan(eventX, eventY, eventDeltaX, eventDeltaY);
|
||||
break;
|
||||
|
||||
case JOYSTICK_BUTTON:
|
||||
GodotLib.joybutton(joystickDevice, button, eventPressed);
|
||||
break;
|
||||
|
||||
case JOYSTICK_AXIS:
|
||||
GodotLib.joyaxis(joystickDevice, axis, value);
|
||||
break;
|
||||
|
||||
case JOYSTICK_HAT:
|
||||
GodotLib.joyhat(joystickDevice, hatX, hatY);
|
||||
break;
|
||||
|
||||
case JOYSTICK_CONNECTION_CHANGED:
|
||||
GodotLib.joyconnectionchanged(joystickDevice, connected, joystickName);
|
||||
break;
|
||||
|
||||
case KEY:
|
||||
GodotLib.key(physicalKeycode, unicode, keyLabel, eventPressed, echo);
|
||||
break;
|
||||
|
||||
case SENSOR:
|
||||
switch (sensorType) {
|
||||
case Sensor.TYPE_ACCELEROMETER:
|
||||
GodotLib.accelerometer(-rotatedValue0, -rotatedValue1, -rotatedValue2);
|
||||
break;
|
||||
|
||||
case Sensor.TYPE_GRAVITY:
|
||||
GodotLib.gravity(-rotatedValue0, -rotatedValue1, -rotatedValue2);
|
||||
break;
|
||||
|
||||
case Sensor.TYPE_MAGNETIC_FIELD:
|
||||
GodotLib.magnetometer(-rotatedValue0, -rotatedValue1, -rotatedValue2);
|
||||
break;
|
||||
|
||||
case Sensor.TYPE_GYROSCOPE:
|
||||
GodotLib.gyroscope(rotatedValue0, rotatedValue1, rotatedValue2);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} finally {
|
||||
recycle();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Release the current instance back to the pool
|
||||
*/
|
||||
private void recycle() {
|
||||
currentEventType = null;
|
||||
POOL.release(this);
|
||||
}
|
||||
}
|
|
@ -43,6 +43,7 @@ import androidx.annotation.Nullable;
|
|||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
|
@ -82,6 +83,9 @@ public final class GodotPluginRegistry {
|
|||
* Retrieve the full set of loaded plugins.
|
||||
*/
|
||||
public Collection<GodotPlugin> getAllPlugins() {
|
||||
if (registry.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return registry.values();
|
||||
}
|
||||
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
#include "core/config/project_settings.h"
|
||||
#include "core/input/input.h"
|
||||
#include "main/main.h"
|
||||
#include "servers/xr_server.h"
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#include "editor/editor_settings.h"
|
||||
|
@ -266,7 +267,18 @@ JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env,
|
|||
}
|
||||
|
||||
if (step.get() == STEP_SHOW_LOGO) {
|
||||
Main::setup_boot_logo();
|
||||
bool xr_enabled;
|
||||
if (XRServer::get_xr_mode() == XRServer::XRMODE_DEFAULT) {
|
||||
xr_enabled = GLOBAL_GET("xr/shaders/enabled");
|
||||
} else {
|
||||
xr_enabled = XRServer::get_xr_mode() == XRServer::XRMODE_ON;
|
||||
}
|
||||
// Unlike PCVR, there's no additional 2D screen onto which to render the boot logo,
|
||||
// so we skip this step if xr is enabled.
|
||||
if (!xr_enabled) {
|
||||
Main::setup_boot_logo();
|
||||
}
|
||||
|
||||
step.increment();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -213,25 +213,27 @@ bool GodotJavaWrapper::has_get_clipboard() {
|
|||
}
|
||||
|
||||
String GodotJavaWrapper::get_clipboard() {
|
||||
String clipboard;
|
||||
if (_get_clipboard) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL_V(env, String());
|
||||
jstring s = (jstring)env->CallObjectMethod(godot_instance, _get_clipboard);
|
||||
return jstring_to_string(s, env);
|
||||
} else {
|
||||
return String();
|
||||
clipboard = jstring_to_string(s, env);
|
||||
env->DeleteLocalRef(s);
|
||||
}
|
||||
return clipboard;
|
||||
}
|
||||
|
||||
String GodotJavaWrapper::get_input_fallback_mapping() {
|
||||
String input_fallback_mapping;
|
||||
if (_get_input_fallback_mapping) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL_V(env, String());
|
||||
jstring fallback_mapping = (jstring)env->CallObjectMethod(godot_instance, _get_input_fallback_mapping);
|
||||
return jstring_to_string(fallback_mapping, env);
|
||||
} else {
|
||||
return String();
|
||||
input_fallback_mapping = jstring_to_string(fallback_mapping, env);
|
||||
env->DeleteLocalRef(fallback_mapping);
|
||||
}
|
||||
return input_fallback_mapping;
|
||||
}
|
||||
|
||||
bool GodotJavaWrapper::has_set_clipboard() {
|
||||
|
@ -324,14 +326,15 @@ Vector<String> GodotJavaWrapper::get_gdextension_list_config_file() const {
|
|||
}
|
||||
|
||||
String GodotJavaWrapper::get_ca_certificates() const {
|
||||
String ca_certificates;
|
||||
if (_get_ca_certificates) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL_V(env, String());
|
||||
jstring s = (jstring)env->CallObjectMethod(godot_instance, _get_ca_certificates);
|
||||
return jstring_to_string(s, env);
|
||||
} else {
|
||||
return String();
|
||||
ca_certificates = jstring_to_string(s, env);
|
||||
env->DeleteLocalRef(s);
|
||||
}
|
||||
return ca_certificates;
|
||||
}
|
||||
|
||||
void GodotJavaWrapper::init_input_devices() {
|
||||
|
|
Loading…
Reference in New Issue