Fix ANRs reported by the Google Play Console
- Add support for dispatching input on the render thread (UI thread is the current default) when `input_buffering` and `accumulated_input` are disabled. At the expense of latency, this helps prevent 'heavy' applications / games from blocking the UI thread (the default behavior) which may cause the application to ANR. - Remove GLSurfaceView logic causing the UI thread to wait on the GL thread during lifecycle events. The removed logic would cause the UI thread to ANR when the GL thread is blocked.
This commit is contained in:
parent
c6a23a7a7d
commit
6b6428d779
|
@ -1391,6 +1391,12 @@
|
|||
Enabling this can greatly improve the responsiveness to input, specially in devices that need to run multiple physics frames per visible (process) frame, because they can't run at the target frame rate.
|
||||
[b]Note:[/b] Currently implemented only on Android.
|
||||
</member>
|
||||
<member name="input_devices/buffering/android/use_accumulated_input" type="bool" setter="" getter="" default="true">
|
||||
If [code]true[/code], multiple input events will be accumulated into a single input event when possible.
|
||||
</member>
|
||||
<member name="input_devices/buffering/android/use_input_buffering" type="bool" setter="" getter="" default="true">
|
||||
If [code]true[/code], input events will be buffered prior to being dispatched.
|
||||
</member>
|
||||
<member name="input_devices/compatibility/legacy_just_pressed_behavior" type="bool" setter="" getter="" default="false">
|
||||
If [code]true[/code], [method Input.is_action_just_pressed] and [method Input.is_action_just_released] will only return [code]true[/code] if the action is still in the respective state, i.e. an action that is pressed [i]and[/i] released on the same frame will be missed.
|
||||
If [code]false[/code], no input will be lost.
|
||||
|
|
|
@ -474,6 +474,11 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
|
|||
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "interface/editor/vsync_mode", 1, "Disabled,Enabled,Adaptive,Mailbox")
|
||||
EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/editor/update_continuously", false, "")
|
||||
|
||||
#ifdef ANDROID_ENABLED
|
||||
EDITOR_SETTING_USAGE(Variant::BOOL, PROPERTY_HINT_NONE, "interface/editor/android/use_accumulated_input", true, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)
|
||||
EDITOR_SETTING_USAGE(Variant::BOOL, PROPERTY_HINT_NONE, "interface/editor/android/use_input_buffering", true, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)
|
||||
#endif
|
||||
|
||||
// Inspector
|
||||
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "interface/inspector/max_array_dictionary_items_per_page", 20, "10,100,1")
|
||||
EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/inspector/show_low_level_opentype_features", false, "")
|
||||
|
|
|
@ -2939,6 +2939,8 @@ 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("input_devices/buffering/android/use_accumulated_input", true);
|
||||
GLOBAL_DEF("input_devices/buffering/android/use_input_buffering", 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);
|
||||
|
|
|
@ -24,6 +24,10 @@
|
|||
android:hasFragileUserData="false"
|
||||
android:requestLegacyExternalStorage="false"
|
||||
tools:ignore="GoogleAppIndexingWarning" >
|
||||
<profileable
|
||||
android:shell="true"
|
||||
android:enabled="true"
|
||||
tools:targetApi="29" />
|
||||
|
||||
<!-- Records the version of the Godot editor used for building -->
|
||||
<meta-data
|
||||
|
|
|
@ -117,6 +117,10 @@ open class GodotEditor : GodotActivity() {
|
|||
val longPressEnabled = enableLongPressGestures()
|
||||
val panScaleEnabled = enablePanAndScaleGestures()
|
||||
|
||||
val useInputBuffering = useInputBuffering()
|
||||
val useAccumulatedInput = useAccumulatedInput()
|
||||
GodotLib.updateInputDispatchSettings(useAccumulatedInput, useInputBuffering)
|
||||
|
||||
checkForProjectPermissionsToEnable()
|
||||
|
||||
runOnUiThread {
|
||||
|
@ -124,6 +128,7 @@ open class GodotEditor : GodotActivity() {
|
|||
godotFragment?.godot?.renderView?.inputHandler?.apply {
|
||||
enableLongPress(longPressEnabled)
|
||||
enablePanningAndScalingGestures(panScaleEnabled)
|
||||
enableInputDispatchToRenderThread(!useInputBuffering && !useAccumulatedInput)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -274,6 +279,13 @@ open class GodotEditor : GodotActivity() {
|
|||
protected open fun enablePanAndScaleGestures() =
|
||||
java.lang.Boolean.parseBoolean(GodotLib.getEditorSetting("interface/touchscreen/enable_pan_and_scale_gestures"))
|
||||
|
||||
/**
|
||||
* Use input buffering for the Godot Android editor.
|
||||
*/
|
||||
protected open fun useInputBuffering() = java.lang.Boolean.parseBoolean(GodotLib.getEditorSetting("interface/editor/android/use_input_buffering"))
|
||||
|
||||
protected open fun useAccumulatedInput() = java.lang.Boolean.parseBoolean(GodotLib.getEditorSetting("interface/editor/android/use_accumulated_input"))
|
||||
|
||||
/**
|
||||
* Whether we should launch the new godot instance in an adjacent window
|
||||
* @see https://developer.android.com/reference/android/content/Intent#FLAG_ACTIVITY_LAUNCH_ADJACENT
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
|
||||
package org.godotengine.editor
|
||||
|
||||
import org.godotengine.godot.GodotLib
|
||||
|
||||
/**
|
||||
* Drives the 'run project' window of the Godot Editor.
|
||||
*/
|
||||
|
@ -39,9 +41,13 @@ class GodotGame : GodotEditor() {
|
|||
|
||||
override fun overrideOrientationRequest() = false
|
||||
|
||||
override fun enableLongPressGestures() = false
|
||||
override fun enableLongPressGestures() = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/pointing/android/enable_long_press_as_right_click"))
|
||||
|
||||
override fun enablePanAndScaleGestures() = false
|
||||
override fun enablePanAndScaleGestures() = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/pointing/android/enable_pan_and_scale_gestures"))
|
||||
|
||||
override fun useInputBuffering() = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/buffering/android/use_input_buffering"))
|
||||
|
||||
override fun useAccumulatedInput() = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/buffering/android/use_accumulated_input"))
|
||||
|
||||
override fun checkForProjectPermissionsToEnable() {
|
||||
// Nothing to do.. by the time we get here, the project permissions will have already
|
||||
|
|
|
@ -84,6 +84,10 @@ class Godot(private val context: Context) : SensorEventListener {
|
|||
|
||||
private companion object {
|
||||
private val TAG = Godot::class.java.simpleName
|
||||
|
||||
// Supported build flavors
|
||||
const val EDITOR_FLAVOR = "editor"
|
||||
const val TEMPLATE_FLAVOR = "template"
|
||||
}
|
||||
|
||||
private val windowManager: WindowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
|
||||
|
@ -188,6 +192,8 @@ class Godot(private val context: Context) : SensorEventListener {
|
|||
return
|
||||
}
|
||||
|
||||
Log.v(TAG, "OnCreate: $primaryHost")
|
||||
|
||||
darkMode = context.resources?.configuration?.uiMode?.and(Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES
|
||||
|
||||
beginBenchmarkMeasure("Startup", "Godot::onCreate")
|
||||
|
@ -196,6 +202,8 @@ class Godot(private val context: Context) : SensorEventListener {
|
|||
val activity = requireActivity()
|
||||
val window = activity.window
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON)
|
||||
|
||||
Log.v(TAG, "Initializing Godot plugin registry")
|
||||
GodotPluginRegistry.initializePluginRegistry(this, primaryHost.getHostPlugins(this))
|
||||
if (io == null) {
|
||||
io = GodotIO(activity)
|
||||
|
@ -319,6 +327,8 @@ class Godot(private val context: Context) : SensorEventListener {
|
|||
return false
|
||||
}
|
||||
|
||||
Log.v(TAG, "OnInitNativeLayer: $host")
|
||||
|
||||
beginBenchmarkMeasure("Startup", "Godot::onInitNativeLayer")
|
||||
try {
|
||||
if (expansionPackPath.isNotEmpty()) {
|
||||
|
@ -337,12 +347,15 @@ class Godot(private val context: Context) : SensorEventListener {
|
|||
fileAccessHandler,
|
||||
useApkExpansion,
|
||||
)
|
||||
Log.v(TAG, "Godot native layer initialization completed: $nativeLayerInitializeCompleted")
|
||||
}
|
||||
|
||||
if (nativeLayerInitializeCompleted && !nativeLayerSetupCompleted) {
|
||||
nativeLayerSetupCompleted = GodotLib.setup(commandLine.toTypedArray(), tts)
|
||||
if (!nativeLayerSetupCompleted) {
|
||||
throw IllegalStateException("Unable to setup the Godot engine! Aborting...")
|
||||
} else {
|
||||
Log.v(TAG, "Godot native layer setup completed")
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
|
@ -370,6 +383,8 @@ class Godot(private val context: Context) : SensorEventListener {
|
|||
throw IllegalStateException("onInitNativeLayer() must be invoked successfully prior to initializing the render view")
|
||||
}
|
||||
|
||||
Log.v(TAG, "OnInitRenderView: $host")
|
||||
|
||||
beginBenchmarkMeasure("Startup", "Godot::onInitRenderView")
|
||||
try {
|
||||
val activity: Activity = host.activity
|
||||
|
@ -489,6 +504,7 @@ class Godot(private val context: Context) : SensorEventListener {
|
|||
}
|
||||
|
||||
fun onStart(host: GodotHost) {
|
||||
Log.v(TAG, "OnStart: $host")
|
||||
if (host != primaryHost) {
|
||||
return
|
||||
}
|
||||
|
@ -497,6 +513,7 @@ class Godot(private val context: Context) : SensorEventListener {
|
|||
}
|
||||
|
||||
fun onResume(host: GodotHost) {
|
||||
Log.v(TAG, "OnResume: $host")
|
||||
if (host != primaryHost) {
|
||||
return
|
||||
}
|
||||
|
@ -529,6 +546,7 @@ class Godot(private val context: Context) : SensorEventListener {
|
|||
}
|
||||
|
||||
fun onPause(host: GodotHost) {
|
||||
Log.v(TAG, "OnPause: $host")
|
||||
if (host != primaryHost) {
|
||||
return
|
||||
}
|
||||
|
@ -541,6 +559,7 @@ class Godot(private val context: Context) : SensorEventListener {
|
|||
}
|
||||
|
||||
fun onStop(host: GodotHost) {
|
||||
Log.v(TAG, "OnStop: $host")
|
||||
if (host != primaryHost) {
|
||||
return
|
||||
}
|
||||
|
@ -549,6 +568,7 @@ class Godot(private val context: Context) : SensorEventListener {
|
|||
}
|
||||
|
||||
fun onDestroy(primaryHost: GodotHost) {
|
||||
Log.v(TAG, "OnDestroy: $primaryHost")
|
||||
if (this.primaryHost != primaryHost) {
|
||||
return
|
||||
}
|
||||
|
@ -606,21 +626,28 @@ class Godot(private val context: Context) : SensorEventListener {
|
|||
* Invoked on the render thread when the Godot setup is complete.
|
||||
*/
|
||||
private fun onGodotSetupCompleted() {
|
||||
Log.d(TAG, "OnGodotSetupCompleted")
|
||||
Log.v(TAG, "OnGodotSetupCompleted")
|
||||
|
||||
// These properties are defined after Godot setup completion, so we retrieve them here.
|
||||
val longPressEnabled = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/pointing/android/enable_long_press_as_right_click"))
|
||||
val panScaleEnabled = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/pointing/android/enable_pan_and_scale_gestures"))
|
||||
val rotaryInputAxisValue = GodotLib.getGlobal("input_devices/pointing/android/rotary_input_scroll_axis")
|
||||
if (!isEditorBuild()) {
|
||||
// These properties are defined after Godot setup completion, so we retrieve them here.
|
||||
val longPressEnabled = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/pointing/android/enable_long_press_as_right_click"))
|
||||
val panScaleEnabled = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/pointing/android/enable_pan_and_scale_gestures"))
|
||||
val rotaryInputAxisValue = GodotLib.getGlobal("input_devices/pointing/android/rotary_input_scroll_axis")
|
||||
|
||||
runOnUiThread {
|
||||
renderView?.inputHandler?.apply {
|
||||
enableLongPress(longPressEnabled)
|
||||
enablePanningAndScalingGestures(panScaleEnabled)
|
||||
try {
|
||||
setRotaryInputAxis(Integer.parseInt(rotaryInputAxisValue))
|
||||
} catch (e: NumberFormatException) {
|
||||
Log.w(TAG, e)
|
||||
val useInputBuffering = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/buffering/android/use_input_buffering"))
|
||||
val useAccumulatedInput = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/buffering/android/use_accumulated_input"))
|
||||
GodotLib.updateInputDispatchSettings(useAccumulatedInput, useInputBuffering)
|
||||
|
||||
runOnUiThread {
|
||||
renderView?.inputHandler?.apply {
|
||||
enableLongPress(longPressEnabled)
|
||||
enablePanningAndScalingGestures(panScaleEnabled)
|
||||
enableInputDispatchToRenderThread(!useInputBuffering && !useAccumulatedInput)
|
||||
try {
|
||||
setRotaryInputAxis(Integer.parseInt(rotaryInputAxisValue))
|
||||
} catch (e: NumberFormatException) {
|
||||
Log.w(TAG, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -635,7 +662,7 @@ class Godot(private val context: Context) : SensorEventListener {
|
|||
* Invoked on the render thread when the Godot main loop has started.
|
||||
*/
|
||||
private fun onGodotMainLoopStarted() {
|
||||
Log.d(TAG, "OnGodotMainLoopStarted")
|
||||
Log.v(TAG, "OnGodotMainLoopStarted")
|
||||
|
||||
for (plugin in pluginRegistry.allPlugins) {
|
||||
plugin.onGodotMainLoopStarted()
|
||||
|
@ -762,6 +789,11 @@ class Godot(private val context: Context) : SensorEventListener {
|
|||
return mClipboard.hasPrimaryClip()
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this is an editor build, false if this is a template build
|
||||
*/
|
||||
fun isEditorBuild() = BuildConfig.FLAVOR == EDITOR_FLAVOR
|
||||
|
||||
fun getClipboard(): String {
|
||||
val clipData = mClipboard.primaryClip ?: return ""
|
||||
val text = clipData.getItemAt(0).text ?: return ""
|
||||
|
|
|
@ -240,4 +240,11 @@ public class GodotLib {
|
|||
* @see GodotRenderer#onActivityPaused()
|
||||
*/
|
||||
public static native void onRendererPaused();
|
||||
|
||||
/**
|
||||
* Invoked on the GL thread to update the input dispatch settings
|
||||
* @param useAccumulatedInput True to use accumulated input, false otherwise
|
||||
* @param useInputBuffering True to use input buffering, false otherwise
|
||||
*/
|
||||
public static native void updateInputDispatchSettings(boolean useAccumulatedInput, boolean useInputBuffering);
|
||||
}
|
||||
|
|
|
@ -1704,15 +1704,6 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
|
|||
mHasSurface = true;
|
||||
mFinishedCreatingEglSurface = false;
|
||||
sGLThreadManager.notifyAll();
|
||||
while (mWaitingForSurface
|
||||
&& !mFinishedCreatingEglSurface
|
||||
&& !mExited) {
|
||||
try {
|
||||
sGLThreadManager.wait();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1723,13 +1714,6 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
|
|||
}
|
||||
mHasSurface = false;
|
||||
sGLThreadManager.notifyAll();
|
||||
while((!mWaitingForSurface) && (!mExited)) {
|
||||
try {
|
||||
sGLThreadManager.wait();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1740,16 +1724,6 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
|
|||
}
|
||||
mRequestPaused = true;
|
||||
sGLThreadManager.notifyAll();
|
||||
while ((! mExited) && (! mPaused)) {
|
||||
if (LOG_PAUSE_RESUME) {
|
||||
Log.i("Main thread", "onPause waiting for mPaused.");
|
||||
}
|
||||
try {
|
||||
sGLThreadManager.wait();
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1762,16 +1736,6 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
|
|||
mRequestRender = true;
|
||||
mRenderComplete = false;
|
||||
sGLThreadManager.notifyAll();
|
||||
while ((! mExited) && mPaused && (!mRenderComplete)) {
|
||||
if (LOG_PAUSE_RESUME) {
|
||||
Log.i("Main thread", "onResume waiting for !mPaused.");
|
||||
}
|
||||
try {
|
||||
sGLThreadManager.wait();
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1793,19 +1757,6 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
|
|||
}
|
||||
|
||||
sGLThreadManager.notifyAll();
|
||||
|
||||
// Wait for thread to react to resize and render a frame
|
||||
while (! mExited && !mPaused && !mRenderComplete
|
||||
&& ableToDraw()) {
|
||||
if (LOG_SURFACE) {
|
||||
Log.i("Main thread", "onWindowResize waiting for render complete from tid=" + getId());
|
||||
}
|
||||
try {
|
||||
sGLThreadManager.wait();
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ import org.godotengine.godot.GodotLib
|
|||
* @See https://developer.android.com/reference/android/view/GestureDetector.SimpleOnGestureListener
|
||||
* @See https://developer.android.com/reference/android/view/ScaleGestureDetector.OnScaleGestureListener
|
||||
*/
|
||||
internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureListener {
|
||||
internal class GodotGestureHandler(private val inputHandler: GodotInputHandler) : SimpleOnGestureListener(), OnScaleGestureListener {
|
||||
|
||||
companion object {
|
||||
private val TAG = GodotGestureHandler::class.java.simpleName
|
||||
|
@ -65,13 +65,13 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi
|
|||
private var lastDragY: Float = 0.0f
|
||||
|
||||
override fun onDown(event: MotionEvent): Boolean {
|
||||
GodotInputHandler.handleMotionEvent(event, MotionEvent.ACTION_DOWN, nextDownIsDoubleTap)
|
||||
inputHandler.handleMotionEvent(event, MotionEvent.ACTION_DOWN, nextDownIsDoubleTap)
|
||||
nextDownIsDoubleTap = false
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onSingleTapUp(event: MotionEvent): Boolean {
|
||||
GodotInputHandler.handleMotionEvent(event)
|
||||
inputHandler.handleMotionEvent(event)
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -85,10 +85,10 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi
|
|||
}
|
||||
|
||||
// Cancel the previous down event
|
||||
GodotInputHandler.handleMotionEvent(event, MotionEvent.ACTION_CANCEL)
|
||||
inputHandler.handleMotionEvent(event, MotionEvent.ACTION_CANCEL)
|
||||
|
||||
// Turn a context click into a single tap right mouse button click.
|
||||
GodotInputHandler.handleMouseEvent(
|
||||
inputHandler.handleMouseEvent(
|
||||
event,
|
||||
MotionEvent.ACTION_DOWN,
|
||||
MotionEvent.BUTTON_SECONDARY,
|
||||
|
@ -104,7 +104,7 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi
|
|||
|
||||
if (!hasCapture) {
|
||||
// Dispatch a mouse relative ACTION_UP event to signal the end of the capture
|
||||
GodotInputHandler.handleMouseEvent(MotionEvent.ACTION_UP, true)
|
||||
inputHandler.handleMouseEvent(MotionEvent.ACTION_UP, true)
|
||||
}
|
||||
pointerCaptureInProgress = hasCapture
|
||||
}
|
||||
|
@ -131,9 +131,9 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi
|
|||
if (contextClickInProgress || GodotInputHandler.isMouseEvent(event)) {
|
||||
// This may be an ACTION_BUTTON_RELEASE event which we don't handle,
|
||||
// so we convert it to an ACTION_UP event.
|
||||
GodotInputHandler.handleMouseEvent(event, MotionEvent.ACTION_UP)
|
||||
inputHandler.handleMouseEvent(event, MotionEvent.ACTION_UP)
|
||||
} else {
|
||||
GodotInputHandler.handleTouchEvent(event)
|
||||
inputHandler.handleTouchEvent(event)
|
||||
}
|
||||
pointerCaptureInProgress = false
|
||||
dragInProgress = false
|
||||
|
@ -148,7 +148,7 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi
|
|||
|
||||
private fun onActionMove(event: MotionEvent): Boolean {
|
||||
if (contextClickInProgress) {
|
||||
GodotInputHandler.handleMouseEvent(event, event.actionMasked, MotionEvent.BUTTON_SECONDARY, false)
|
||||
inputHandler.handleMouseEvent(event, event.actionMasked, MotionEvent.BUTTON_SECONDARY, false)
|
||||
return true
|
||||
} else if (!scaleInProgress) {
|
||||
// The 'onScroll' event is triggered with a long delay.
|
||||
|
@ -158,7 +158,7 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi
|
|||
if (lastDragX != event.getX(0) || lastDragY != event.getY(0)) {
|
||||
lastDragX = event.getX(0)
|
||||
lastDragY = event.getY(0)
|
||||
GodotInputHandler.handleMotionEvent(event)
|
||||
inputHandler.handleMotionEvent(event)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -168,9 +168,9 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi
|
|||
override fun onDoubleTapEvent(event: MotionEvent): Boolean {
|
||||
if (event.actionMasked == MotionEvent.ACTION_UP) {
|
||||
nextDownIsDoubleTap = false
|
||||
GodotInputHandler.handleMotionEvent(event)
|
||||
inputHandler.handleMotionEvent(event)
|
||||
} else if (event.actionMasked == MotionEvent.ACTION_MOVE && !panningAndScalingEnabled) {
|
||||
GodotInputHandler.handleMotionEvent(event)
|
||||
inputHandler.handleMotionEvent(event)
|
||||
}
|
||||
|
||||
return true
|
||||
|
@ -191,7 +191,7 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi
|
|||
if (dragInProgress || lastDragX != 0.0f || lastDragY != 0.0f) {
|
||||
if (originEvent != null) {
|
||||
// Cancel the drag
|
||||
GodotInputHandler.handleMotionEvent(originEvent, MotionEvent.ACTION_CANCEL)
|
||||
inputHandler.handleMotionEvent(originEvent, MotionEvent.ACTION_CANCEL)
|
||||
}
|
||||
dragInProgress = false
|
||||
lastDragX = 0.0f
|
||||
|
@ -202,12 +202,12 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi
|
|||
val x = terminusEvent.x
|
||||
val y = terminusEvent.y
|
||||
if (terminusEvent.pointerCount >= 2 && panningAndScalingEnabled && !pointerCaptureInProgress && !dragInProgress) {
|
||||
GodotLib.pan(x, y, distanceX / 5f, distanceY / 5f)
|
||||
inputHandler.handlePanEvent(x, y, distanceX / 5f, distanceY / 5f)
|
||||
} else if (!scaleInProgress) {
|
||||
dragInProgress = true
|
||||
lastDragX = terminusEvent.getX(0)
|
||||
lastDragY = terminusEvent.getY(0)
|
||||
GodotInputHandler.handleMotionEvent(terminusEvent)
|
||||
inputHandler.handleMotionEvent(terminusEvent)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -218,11 +218,7 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi
|
|||
}
|
||||
|
||||
if (detector.scaleFactor >= 0.8f && detector.scaleFactor != 1f && detector.scaleFactor <= 1.2f) {
|
||||
GodotLib.magnify(
|
||||
detector.focusX,
|
||||
detector.focusY,
|
||||
detector.scaleFactor
|
||||
)
|
||||
inputHandler.handleMagnifyEvent(detector.focusX, detector.focusY, detector.scaleFactor)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -75,7 +75,9 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
|
|||
*/
|
||||
private int lastSeenToolType = MotionEvent.TOOL_TYPE_UNKNOWN;
|
||||
|
||||
private static int rotaryInputAxis = ROTARY_INPUT_VERTICAL_AXIS;
|
||||
private int rotaryInputAxis = ROTARY_INPUT_VERTICAL_AXIS;
|
||||
|
||||
private boolean dispatchInputToRenderThread = false;
|
||||
|
||||
public GodotInputHandler(GodotRenderView godotView) {
|
||||
final Context context = godotView.getView().getContext();
|
||||
|
@ -83,7 +85,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
|
|||
mInputManager = (InputManager)context.getSystemService(Context.INPUT_SERVICE);
|
||||
mInputManager.registerInputDeviceListener(this, null);
|
||||
|
||||
this.godotGestureHandler = new GodotGestureHandler();
|
||||
this.godotGestureHandler = new GodotGestureHandler(this);
|
||||
this.gestureDetector = new GestureDetector(context, godotGestureHandler);
|
||||
this.gestureDetector.setIsLongpressEnabled(false);
|
||||
this.scaleGestureDetector = new ScaleGestureDetector(context, godotGestureHandler);
|
||||
|
@ -108,6 +110,22 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
|
|||
this.godotGestureHandler.setPanningAndScalingEnabled(enable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies whether input should be dispatch on the UI thread or on the Render thread.
|
||||
* @param enable true to dispatch input on the Render thread, false to dispatch input on the UI thread
|
||||
*/
|
||||
public void enableInputDispatchToRenderThread(boolean enable) {
|
||||
this.dispatchInputToRenderThread = enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if input must be dispatched from the render thread. If false, input is
|
||||
* dispatched from the UI thread.
|
||||
*/
|
||||
private boolean shouldDispatchInputToRenderThread() {
|
||||
return dispatchInputToRenderThread;
|
||||
}
|
||||
|
||||
/**
|
||||
* On Wear OS devices, sets which axis of the mouse wheel rotary input is mapped to. This is 1 (vertical axis) by default.
|
||||
*/
|
||||
|
@ -151,14 +169,14 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
|
|||
if (mJoystickIds.indexOfKey(deviceId) >= 0) {
|
||||
final int button = getGodotButton(keyCode);
|
||||
final int godotJoyId = mJoystickIds.get(deviceId);
|
||||
GodotLib.joybutton(godotJoyId, button, false);
|
||||
handleJoystickButtonEvent(godotJoyId, button, false);
|
||||
}
|
||||
} else {
|
||||
// getKeyCode(): The physical key that was pressed.
|
||||
final int physical_keycode = event.getKeyCode();
|
||||
final int unicode = event.getUnicodeChar();
|
||||
final int key_label = event.getDisplayLabel();
|
||||
GodotLib.key(physical_keycode, unicode, key_label, false, event.getRepeatCount() > 0);
|
||||
handleKeyEvent(physical_keycode, unicode, key_label, false, event.getRepeatCount() > 0);
|
||||
};
|
||||
|
||||
return true;
|
||||
|
@ -187,13 +205,13 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
|
|||
if (mJoystickIds.indexOfKey(deviceId) >= 0) {
|
||||
final int button = getGodotButton(keyCode);
|
||||
final int godotJoyId = mJoystickIds.get(deviceId);
|
||||
GodotLib.joybutton(godotJoyId, button, true);
|
||||
handleJoystickButtonEvent(godotJoyId, button, true);
|
||||
}
|
||||
} else {
|
||||
final int physical_keycode = event.getKeyCode();
|
||||
final int unicode = event.getUnicodeChar();
|
||||
final int key_label = event.getDisplayLabel();
|
||||
GodotLib.key(physical_keycode, unicode, key_label, true, event.getRepeatCount() > 0);
|
||||
handleKeyEvent(physical_keycode, unicode, key_label, true, event.getRepeatCount() > 0);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -248,7 +266,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
|
|||
if (joystick.axesValues.indexOfKey(axis) < 0 || (float)joystick.axesValues.get(axis) != value) {
|
||||
// save value to prevent repeats
|
||||
joystick.axesValues.put(axis, value);
|
||||
GodotLib.joyaxis(godotJoyId, i, value);
|
||||
handleJoystickAxisEvent(godotJoyId, i, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -258,7 +276,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
|
|||
if (joystick.hatX != hatX || joystick.hatY != hatY) {
|
||||
joystick.hatX = hatX;
|
||||
joystick.hatY = hatY;
|
||||
GodotLib.joyhat(godotJoyId, hatX, hatY);
|
||||
handleJoystickHatEvent(godotJoyId, hatX, hatY);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
@ -284,10 +302,12 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
|
|||
int[] deviceIds = mInputManager.getInputDeviceIds();
|
||||
for (int deviceId : deviceIds) {
|
||||
InputDevice device = mInputManager.getInputDevice(deviceId);
|
||||
if (DEBUG) {
|
||||
Log.v(TAG, String.format("init() deviceId:%d, Name:%s\n", deviceId, device.getName()));
|
||||
if (device != null) {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG, String.format("init() deviceId:%d, Name:%s\n", deviceId, device.getName()));
|
||||
}
|
||||
onInputDeviceAdded(deviceId);
|
||||
}
|
||||
onInputDeviceAdded(deviceId);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -364,7 +384,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
|
|||
}
|
||||
mJoysticksDevices.put(deviceId, joystick);
|
||||
|
||||
GodotLib.joyconnectionchanged(id, true, joystick.name);
|
||||
handleJoystickConnectionChangedEvent(id, true, joystick.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -378,7 +398,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
|
|||
final int godotJoyId = mJoystickIds.get(deviceId);
|
||||
mJoystickIds.delete(deviceId);
|
||||
mJoysticksDevices.delete(deviceId);
|
||||
GodotLib.joyconnectionchanged(godotJoyId, false, "");
|
||||
handleJoystickConnectionChangedEvent(godotJoyId, false, "");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -482,15 +502,15 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
|
|||
}
|
||||
}
|
||||
|
||||
static boolean handleMotionEvent(final MotionEvent event) {
|
||||
boolean handleMotionEvent(final MotionEvent event) {
|
||||
return handleMotionEvent(event, event.getActionMasked());
|
||||
}
|
||||
|
||||
static boolean handleMotionEvent(final MotionEvent event, int eventActionOverride) {
|
||||
boolean handleMotionEvent(final MotionEvent event, int eventActionOverride) {
|
||||
return handleMotionEvent(event, eventActionOverride, false);
|
||||
}
|
||||
|
||||
static boolean handleMotionEvent(final MotionEvent event, int eventActionOverride, boolean doubleTap) {
|
||||
boolean handleMotionEvent(final MotionEvent event, int eventActionOverride, boolean doubleTap) {
|
||||
if (isMouseEvent(event)) {
|
||||
return handleMouseEvent(event, eventActionOverride, doubleTap);
|
||||
}
|
||||
|
@ -523,19 +543,19 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
|
|||
return (float)Math.cos(orientation) * tiltMult;
|
||||
}
|
||||
|
||||
static boolean handleMouseEvent(final MotionEvent event) {
|
||||
boolean handleMouseEvent(final MotionEvent event) {
|
||||
return handleMouseEvent(event, event.getActionMasked());
|
||||
}
|
||||
|
||||
static boolean handleMouseEvent(final MotionEvent event, int eventActionOverride) {
|
||||
boolean handleMouseEvent(final MotionEvent event, int eventActionOverride) {
|
||||
return handleMouseEvent(event, eventActionOverride, false);
|
||||
}
|
||||
|
||||
static boolean handleMouseEvent(final MotionEvent event, int eventActionOverride, boolean doubleTap) {
|
||||
boolean handleMouseEvent(final MotionEvent event, int eventActionOverride, boolean doubleTap) {
|
||||
return handleMouseEvent(event, eventActionOverride, event.getButtonState(), doubleTap);
|
||||
}
|
||||
|
||||
static boolean handleMouseEvent(final MotionEvent event, int eventActionOverride, int buttonMaskOverride, boolean doubleTap) {
|
||||
boolean handleMouseEvent(final MotionEvent event, int eventActionOverride, int buttonMaskOverride, boolean doubleTap) {
|
||||
final float x = event.getX();
|
||||
final float y = event.getY();
|
||||
|
||||
|
@ -564,11 +584,11 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
|
|||
return handleMouseEvent(eventActionOverride, buttonMaskOverride, x, y, horizontalFactor, verticalFactor, doubleTap, sourceMouseRelative, pressure, getEventTiltX(event), getEventTiltY(event));
|
||||
}
|
||||
|
||||
static boolean handleMouseEvent(int eventAction, boolean sourceMouseRelative) {
|
||||
boolean handleMouseEvent(int eventAction, boolean sourceMouseRelative) {
|
||||
return handleMouseEvent(eventAction, 0, 0f, 0f, 0f, 0f, false, sourceMouseRelative, 1f, 0f, 0f);
|
||||
}
|
||||
|
||||
static boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY, boolean doubleClick, boolean sourceMouseRelative, float pressure, float tiltX, float tiltY) {
|
||||
boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY, boolean doubleClick, boolean sourceMouseRelative, float pressure, float tiltX, float tiltY) {
|
||||
// Fix the buttonsMask
|
||||
switch (eventAction) {
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
|
@ -584,6 +604,7 @@ 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.
|
||||
|
@ -596,22 +617,26 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
|
|||
case MotionEvent.ACTION_HOVER_MOVE:
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
case MotionEvent.ACTION_SCROLL: {
|
||||
GodotLib.dispatchMouseEvent(eventAction, buttonsMask, x, y, deltaX, deltaY, doubleClick, sourceMouseRelative, pressure, tiltX, tiltY);
|
||||
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);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static boolean handleTouchEvent(final MotionEvent event) {
|
||||
boolean handleTouchEvent(final MotionEvent event) {
|
||||
return handleTouchEvent(event, event.getActionMasked());
|
||||
}
|
||||
|
||||
static boolean handleTouchEvent(final MotionEvent event, int eventActionOverride) {
|
||||
boolean handleTouchEvent(final MotionEvent event, int eventActionOverride) {
|
||||
return handleTouchEvent(event, eventActionOverride, false);
|
||||
}
|
||||
|
||||
static boolean handleTouchEvent(final MotionEvent event, int eventActionOverride, boolean doubleTap) {
|
||||
boolean handleTouchEvent(final MotionEvent event, int eventActionOverride, boolean doubleTap) {
|
||||
final int pointerCount = event.getPointerCount();
|
||||
if (pointerCount == 0) {
|
||||
return true;
|
||||
|
@ -636,10 +661,70 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
|
|||
case MotionEvent.ACTION_MOVE:
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
case MotionEvent.ACTION_POINTER_DOWN: {
|
||||
GodotLib.dispatchTouchEvent(eventActionOverride, actionPointerId, pointerCount, positions, doubleTap);
|
||||
if (shouldDispatchInputToRenderThread()) {
|
||||
mRenderView.queueOnRenderThread(() -> GodotLib.dispatchTouchEvent(eventActionOverride, actionPointerId, pointerCount, positions, doubleTap));
|
||||
} else {
|
||||
GodotLib.dispatchTouchEvent(eventActionOverride, actionPointerId, pointerCount, positions, doubleTap);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void handleMagnifyEvent(float x, float y, float factor) {
|
||||
if (shouldDispatchInputToRenderThread()) {
|
||||
mRenderView.queueOnRenderThread(() -> GodotLib.magnify(x, y, factor));
|
||||
} else {
|
||||
GodotLib.magnify(x, y, factor);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleJoystickButtonEvent(int device, int button, boolean pressed) {
|
||||
if (shouldDispatchInputToRenderThread()) {
|
||||
mRenderView.queueOnRenderThread(() -> GodotLib.joybutton(device, button, pressed));
|
||||
} else {
|
||||
GodotLib.joybutton(device, button, pressed);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleJoystickAxisEvent(int device, int axis, float value) {
|
||||
if (shouldDispatchInputToRenderThread()) {
|
||||
mRenderView.queueOnRenderThread(() -> GodotLib.joyaxis(device, axis, value));
|
||||
} else {
|
||||
GodotLib.joyaxis(device, axis, value);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleJoystickHatEvent(int device, int hatX, int hatY) {
|
||||
if (shouldDispatchInputToRenderThread()) {
|
||||
mRenderView.queueOnRenderThread(() -> GodotLib.joyhat(device, hatX, hatY));
|
||||
} else {
|
||||
GodotLib.joyhat(device, hatX, hatY);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleJoystickConnectionChangedEvent(int device, boolean connected, String name) {
|
||||
if (shouldDispatchInputToRenderThread()) {
|
||||
mRenderView.queueOnRenderThread(() -> GodotLib.joyconnectionchanged(device, connected, name));
|
||||
} else {
|
||||
GodotLib.joyconnectionchanged(device, connected, name);
|
||||
}
|
||||
}
|
||||
|
||||
void handleKeyEvent(int physicalKeycode, int unicode, int keyLabel, boolean pressed, boolean echo) {
|
||||
if (shouldDispatchInputToRenderThread()) {
|
||||
mRenderView.queueOnRenderThread(() -> GodotLib.key(physicalKeycode, unicode, keyLabel, pressed, echo));
|
||||
} else {
|
||||
GodotLib.key(physicalKeycode, unicode, keyLabel, pressed, echo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -93,8 +93,8 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene
|
|||
@Override
|
||||
public void beforeTextChanged(final CharSequence pCharSequence, final int start, final int count, final int after) {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
GodotLib.key(KeyEvent.KEYCODE_DEL, 0, 0, true, false);
|
||||
GodotLib.key(KeyEvent.KEYCODE_DEL, 0, 0, false, false);
|
||||
mRenderView.getInputHandler().handleKeyEvent(KeyEvent.KEYCODE_DEL, 0, 0, true, false);
|
||||
mRenderView.getInputHandler().handleKeyEvent(KeyEvent.KEYCODE_DEL, 0, 0, false, false);
|
||||
|
||||
if (mHasSelection) {
|
||||
mHasSelection = false;
|
||||
|
@ -115,8 +115,8 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene
|
|||
// Return keys are handled through action events
|
||||
continue;
|
||||
}
|
||||
GodotLib.key(0, character, 0, true, false);
|
||||
GodotLib.key(0, character, 0, false, false);
|
||||
mRenderView.getInputHandler().handleKeyEvent(0, character, 0, true, false);
|
||||
mRenderView.getInputHandler().handleKeyEvent(0, character, 0, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -127,18 +127,16 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene
|
|||
if (characters != null) {
|
||||
for (int i = 0; i < characters.length(); i++) {
|
||||
final int character = characters.codePointAt(i);
|
||||
GodotLib.key(0, character, 0, true, false);
|
||||
GodotLib.key(0, character, 0, false, false);
|
||||
mRenderView.getInputHandler().handleKeyEvent(0, character, 0, true, false);
|
||||
mRenderView.getInputHandler().handleKeyEvent(0, character, 0, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pActionID == EditorInfo.IME_ACTION_DONE) {
|
||||
// Enter key has been pressed
|
||||
mRenderView.queueOnRenderThread(() -> {
|
||||
GodotLib.key(KeyEvent.KEYCODE_ENTER, 0, 0, true, false);
|
||||
GodotLib.key(KeyEvent.KEYCODE_ENTER, 0, 0, false, false);
|
||||
});
|
||||
mRenderView.getInputHandler().handleKeyEvent(KeyEvent.KEYCODE_ENTER, 0, 0, true, false);
|
||||
mRenderView.getInputHandler().handleKeyEvent(KeyEvent.KEYCODE_ENTER, 0, 0, false, false);
|
||||
mRenderView.getView().requestFocus();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -549,4 +549,11 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererPaused(JNIE
|
|||
os_android->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_PAUSED);
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_updateInputDispatchSettings(JNIEnv *env, jclass clazz, jboolean p_use_accumulated_input, jboolean p_use_input_buffering) {
|
||||
if (Input::get_singleton()) {
|
||||
Input::get_singleton()->set_use_accumulated_input(p_use_accumulated_input);
|
||||
Input::get_singleton()->set_use_input_buffering(p_use_input_buffering);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,6 +69,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_requestPermissionResu
|
|||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onNightModeChanged(JNIEnv *env, jclass clazz);
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererResumed(JNIEnv *env, jclass clazz);
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererPaused(JNIEnv *env, jclass clazz);
|
||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_updateInputDispatchSettings(JNIEnv *env, jclass clazz, jboolean p_use_accumulated_input, jboolean p_use_input_buffering);
|
||||
}
|
||||
|
||||
#endif // JAVA_GODOT_LIB_JNI_H
|
||||
|
|
Loading…
Reference in New Issue