From 1d477fa2481be563a01cf4e8b60fb4e0d61ed2d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 2 Nov 2026 08:52:24 +0100 Subject: [PATCH] Android: Request unbuffered dispatch of touch and joystick input Android batches touch moves and joystick axis events until the next UI vsync, up to a frame of latency. On API 30+, ask for them unbatched. Non-pointer sources only apply to the focused view, so the surface view now stays focused. Co-Authored-By: Claude Opus 5.5 --- .../org/ppsspp/ppsspp/NativeSurfaceView.java | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/android/src/org/ppsspp/ppsspp/NativeSurfaceView.java b/android/src/org/ppsspp/ppsspp/NativeSurfaceView.java index 06d518d5b1..8d5cc1a8a9 100644 --- a/android/src/org/ppsspp/ppsspp/NativeSurfaceView.java +++ b/android/src/org/ppsspp/ppsspp/NativeSurfaceView.java @@ -7,8 +7,10 @@ package org.ppsspp.ppsspp; import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; +import android.os.Build; import android.os.SystemClock; import android.text.InputType; +import android.view.InputDevice; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.SurfaceView; @@ -28,6 +30,21 @@ public class NativeSurfaceView extends SurfaceView { super(context); } + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + // Without this, touch moves and joystick axis events are batched until the next UI vsync, + // adding up to a frame of latency before the emulator sees them. Non-pointer sources only + // apply while this view is focused, so keep it focusable. + setFocusable(true); + setFocusableInTouchMode(true); + setDefaultFocusHighlightEnabled(false); + requestFocus(); + requestUnbufferedDispatch(InputDevice.SOURCE_CLASS_POINTER | InputDevice.SOURCE_CLASS_JOYSTICK); + } + } + @SuppressLint("ClickableViewAccessibility") @Override public boolean onTouchEvent(final MotionEvent ev) { @@ -55,9 +72,12 @@ public class NativeSurfaceView extends SurfaceView { } else { imm.hideSoftInputFromWindow(getWindowToken(), 0); imm.restartInput(this); - clearFocus(); - setFocusableInTouchMode(false); - setFocusable(false); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { + // On R+ we stay focused for unbuffered dispatch, see onAttachedToWindow. + clearFocus(); + setFocusableInTouchMode(false); + setFocusable(false); + } } }