diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index b1556783d52..5c97a975fe6 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -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.
+
+ If [code]true[/code], multiple input events will be accumulated into a single input event when possible.
+
+
+ If [code]true[/code], input events will be buffered prior to being dispatched.
+
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.
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index e021be96682..85e60df1dd3 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -474,6 +474,11 @@ void EditorSettings::_load_defaults(Ref 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, "")
diff --git a/main/main.cpp b/main/main.cpp
index 0209f1cc4ff..e6be23034d5 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -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);
diff --git a/platform/android/java/app/AndroidManifest.xml b/platform/android/java/app/AndroidManifest.xml
index 4abc6548bff..0cc929d226c 100644
--- a/platform/android/java/app/AndroidManifest.xml
+++ b/platform/android/java/app/AndroidManifest.xml
@@ -24,6 +24,10 @@
android:hasFragileUserData="false"
android:requestLegacyExternalStorage="false"
tools:ignore="GoogleAppIndexingWarning" >
+
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);
+ }
+ }
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java
index 06b565c30ff..e5456699701 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java
@@ -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;
}
diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp
index 8493a8e9326..87d4281c5a8 100644
--- a/platform/android/java_godot_lib_jni.cpp
+++ b/platform/android/java_godot_lib_jni.cpp
@@ -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);
+ }
+}
}
diff --git a/platform/android/java_godot_lib_jni.h b/platform/android/java_godot_lib_jni.h
index f32ffc291a0..852c475e7e9 100644
--- a/platform/android/java_godot_lib_jni.h
+++ b/platform/android/java_godot_lib_jni.h
@@ -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