Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android: Improve mouse input #19915

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Common/Input/InputState.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ enum {
TOUCH_RELEASE_ALL = 1 << 6, // Useful for app focus switches when events may be lost.
TOUCH_HOVER = 1 << 7,

// These are the Android getToolType() codes, shifted by 10.
// These are the Android getToolType() codes, shifted by 10. Unused currently.
TOUCH_TOOL_MASK = 7 << 10,
TOUCH_TOOL_UNKNOWN = 0 << 10,
TOUCH_TOOL_FINGER = 1 << 10,
Expand Down
4 changes: 2 additions & 2 deletions Qt/QtMain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -613,7 +613,7 @@ bool MainUI::event(QEvent *e) {
case Qt::LeftButton:
input.x = ((QMouseEvent*)e)->pos().x() * g_display.dpi_scale_x * xscale;
input.y = ((QMouseEvent*)e)->pos().y() * g_display.dpi_scale_y * yscale;
input.flags = (e->type() == QEvent::MouseButtonPress) ? TOUCH_DOWN : TOUCH_UP;
input.flags = ((e->type() == QEvent::MouseButtonPress) ? TOUCH_DOWN : TOUCH_UP) | TOUCH_MOUSE;
input.id = 0;
NativeTouch(input);
break;
Expand All @@ -636,7 +636,7 @@ bool MainUI::event(QEvent *e) {
case QEvent::MouseMove:
input.x = ((QMouseEvent*)e)->pos().x() * g_display.dpi_scale_x * xscale;
input.y = ((QMouseEvent*)e)->pos().y() * g_display.dpi_scale_y * yscale;
input.flags = TOUCH_MOVE;
input.flags = TOUCH_MOVE | TOUCH_MOUSE;
input.id = 0;
NativeTouch(input);
break;
Expand Down
1 change: 1 addition & 0 deletions android/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
<uses-feature android:name="android.hardware.location.network" android:required="false" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
<uses-feature android:name="android.hardware.type.pc" android:required="false" />

<!-- I tried using android:maxSdkVersion="29" on WRITE/READ_EXTERNAL_STORAGE, but that made
it so that in legacy mode, you can't ask for permission anymore. So removed that. -->
Expand Down
64 changes: 64 additions & 0 deletions android/jni/app-android.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1252,6 +1252,70 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_joystickAxis(
env->ReleaseFloatArrayElements(values, valueBuffer, JNI_ABORT); // ABORT just means we don't want changes copied back!
}

extern "C" jboolean Java_org_ppsspp_ppsspp_NativeApp_mouse(
JNIEnv *env, jclass, jfloat x, jfloat y, int button, int action) {
if (!renderer_inited)
return false;
TouchInput input{};

static float last_x = 0.0f;
static float last_y = 0.0f;

if (x == -1.0f) {
x = last_x;
} else {
last_x = x;
}
if (y == -1.0f) {
y = last_y;
} else {
last_y = y;
}

x *= g_display.dpi_scale_x;
y *= g_display.dpi_scale_y;

if (button == 0) {
// It's a pure mouse move.
input.flags = TOUCH_MOUSE | TOUCH_MOVE;
input.x = x;
input.y = y;
input.id = 0;
} else {
input.buttons = button;
input.x = x;
input.y = y;
switch (action) {
case 1:
input.flags = TOUCH_MOUSE | TOUCH_DOWN;
break;
case 2:
input.flags = TOUCH_MOUSE | TOUCH_UP;
break;
}
input.id = 0;
}
INFO_LOG(Log::System, "New-style mouse event: %f %f %d %d -> x: %f y: %f buttons: %d flags: %04x", x, y, button, action, input.x, input.y, input.buttons, input.flags);
NativeTouch(input);

// Also send mouse button key events, for binding.
if (button) {
KeyInput input{};
input.deviceId = DEVICE_ID_MOUSE;
switch (button) {
case 1: input.keyCode = NKCODE_EXT_MOUSEBUTTON_1; break;
case 2: input.keyCode = NKCODE_EXT_MOUSEBUTTON_2; break;
case 3: input.keyCode = NKCODE_EXT_MOUSEBUTTON_3; break;
default: WARN_LOG(Log::System, "Unexpected mouse button %d", button);
}
input.flags = action == 1 ? KEY_DOWN : KEY_UP;
if (input.keyCode != 0) {
NativeKey(input);
}
}
return true;
}

extern "C" jboolean Java_org_ppsspp_ppsspp_NativeApp_mouseWheelEvent(
JNIEnv *env, jclass, jfloat x, jfloat y) {
if (!renderer_inited)
Expand Down
2 changes: 1 addition & 1 deletion android/src/org/ppsspp/ppsspp/MogaHack.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public static void init(Controller controller, Context context) {
// Convert implicit intent to explicit intent, see http://stackoverflow.com/a/26318757
Intent intent = new Intent(IControllerService.class.getName());
List<ResolveInfo> resolveInfos = context.getPackageManager().queryIntentServices(intent, 0);
if (resolveInfos == null || resolveInfos.size() != 1) {
if (resolveInfos.size() != 1) {
// What? this doesn't do anything.
// Log.e("MogaHack", "Somebody is trying to intercept our intent. Disabling MOGA controller for security.");
}
Expand Down
63 changes: 47 additions & 16 deletions android/src/org/ppsspp/ppsspp/NativeActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ private static ArrayList<String> getSdCardPaths(final Context context) {
for (String var : varNames) {
Log.i(TAG, "getSdCardPaths: Checking env " + var);
String secStore = System.getenv("SECONDARY_STORAGE");
if (secStore != null && secStore.length() > 0) {
if (secStore != null && !secStore.isEmpty()) {
list = new ArrayList<String>();
list.add(secStore);
break;
Expand Down Expand Up @@ -573,10 +573,8 @@ private void updateSystemUiVisibility() {
// Need API 11 to check for existence of a vibrator? Zany.
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public void checkForVibrator() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
if (!vibrator.hasVibrator()) {
vibrator = null;
}
if (!vibrator.hasVibrator()) {
vibrator = null;
}
}

Expand Down Expand Up @@ -786,7 +784,7 @@ protected void onDestroy() {
do {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
} catch (InterruptedException ignored) {
}
tries--;
} while (nativeRenderer.isRenderingFrame() && tries > 0);
Expand Down Expand Up @@ -979,7 +977,7 @@ protected String getInputDeviceDebugString() {
for (InputDeviceState input : inputPlayers) {
buffer += input.getDebugString();
}
if (buffer.length() == 0) {
if (buffer.isEmpty()) {
buffer = "(no devices)";
}
return buffer;
Expand All @@ -997,6 +995,23 @@ public boolean IsXperiaPlay() {
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1 && !isXperiaPlay) {
Log.i(TAG, "key event" + event.getSource());
if (NativeSurfaceView.isFromSource(event, InputDevice.SOURCE_MOUSE)) {
Log.i(TAG, "Forwarding key event from mouse");
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
// Probably a right click
switch (event.getAction()) {
case KeyEvent.ACTION_DOWN:
NativeApp.mouse(-1, -1, 2, 1);
break;
case KeyEvent.ACTION_UP:
NativeApp.mouse(-1, -1, 2, 2);
break;
}
}
return true;
}

InputDeviceState state = getInputDeviceState(event);
if (state == null) {
return super.dispatchKeyEvent(event);
Expand Down Expand Up @@ -1067,17 +1082,15 @@ void sendMouseDelta(float dx, float dy) {
@Override
@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
public boolean onGenericMotionEvent(MotionEvent event) {
// Log.d(TAG, "onGenericMotionEvent: " + event);
// Log.i(TAG, "NativeActivity onGenericMotionEvent: " + event);
if (InputDeviceState.inputSourceIsJoystick(event.getSource())) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {
InputDeviceState state = getInputDeviceState(event);
if (state == null) {
Log.w(TAG, "Joystick event but failed to get input device state.");
return super.onGenericMotionEvent(event);
}
state.onJoystickMotion(event);
return true;
InputDeviceState state = getInputDeviceState(event);
if (state == null) {
Log.w(TAG, "Joystick event but failed to get input device state.");
return super.onGenericMotionEvent(event);
}
state.onJoystickMotion(event);
return true;
}

if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
Expand All @@ -1092,12 +1105,30 @@ public boolean onGenericMotionEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_HOVER_MOVE:
// process the mouse hover movement...
NativeApp.mouse(event.getX(), event.getY(), 0, 0);
return true;
case MotionEvent.ACTION_SCROLL:
NativeApp.mouseWheelEvent(event.getAxisValue(MotionEvent.AXIS_HSCROLL), event.getAxisValue(MotionEvent.AXIS_VSCROLL));
return true;
}
}

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_BUTTON_PRESS: {
int button = event.getActionButton();
NativeApp.mouse(event.getX(), event.getY(), button, 1);
return true;
}
case MotionEvent.ACTION_BUTTON_RELEASE: {
int button = event.getActionButton();
NativeApp.mouse(event.getX(), event.getY(), button, 2);
return true;
}
default:
break;
}
}
return super.onGenericMotionEvent(event);
}

Expand Down
1 change: 1 addition & 0 deletions android/src/org/ppsspp/ppsspp/NativeApp.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ public class NativeApp {

public static native void accelerometer(float x, float y, float z);

public static native void mouse(float x, float y, int button, int action);
public static native void mouseDelta(float x, float y);
public static native void sendMessageFromJava(String msg, String arg);
public static native void sendRequestResult(int seqID, boolean result, String value, int iValue);
Expand Down
38 changes: 38 additions & 0 deletions android/src/org/ppsspp/ppsspp/NativeGLView.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import android.os.Build;
import android.os.Handler;
import android.util.Log;
import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceControl;
Expand Down Expand Up @@ -60,11 +61,48 @@ private int getToolType(final MotionEvent ev, int pointer) {
return ev.getToolType(pointer);
}


@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)
private boolean onMouseEventModern(final MotionEvent ev) {
if (!NativeSurfaceView.isFromSource(ev, InputDevice.SOURCE_MOUSE)) {
return false;
}
switch (ev.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
// Log.i(TAG, "Action down. button state: " + ev.getButtonState());
NativeApp.mouse(ev.getX(), ev.getY(), 1, 1);
break;
}
case MotionEvent.ACTION_UP: {
// Log.i(TAG, "Action up. button state: " + ev.getButtonState());
NativeApp.mouse(ev.getX(), ev.getY(), 1, 2);
break;
}
case MotionEvent.ACTION_MOVE: {
// Log.i(TAG, "Action move. button state: " + ev.getButtonState());
NativeApp.mouse(ev.getX(), ev.getY(), 0, 0);
break;
}
default: {
Log.i(TAG, "Unhandled modern mouse action: " + ev.getAction());
break;
}
}
return true;
}

@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(final MotionEvent ev) {
boolean canReadToolType = Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH;

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
// This is where workable mouse support arrived.
if (onMouseEventModern(ev)) {
return true;
}
}

for (int i = 0; i < ev.getPointerCount(); i++) {
int pid = ev.getPointerId(i);
int code = 0;
Expand Down
43 changes: 42 additions & 1 deletion android/src/org/ppsspp/ppsspp/NativeSurfaceView.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import android.os.Handler;
import android.util.Log;
import android.view.InputDevice;
import android.view.InputEvent;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceControl;
Expand Down Expand Up @@ -66,15 +67,56 @@ private void processMouseDelta(final MotionEvent ev) {
if ((ev.getSource() & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) {
float dx = ev.getAxisValue(MotionEvent.AXIS_RELATIVE_X);
float dy = ev.getAxisValue(MotionEvent.AXIS_RELATIVE_Y);
Log.i(TAG, "Mouse delta: " + dx + " " + dy);
NativeApp.mouseDelta(dx, dy);
}
}

public static boolean isFromSource(final InputEvent ev, int source) {
return (ev.getSource() & source) == source;
}

@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)
private boolean onMouseEventModern(final MotionEvent ev) {
if (!isFromSource(ev, InputDevice.SOURCE_MOUSE)) {
return false;
}
switch (ev.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
// Log.i(TAG, "Action down. button state: " + ev.getButtonState());
NativeApp.mouse(ev.getX(), ev.getY(), 1, 1);
break;
}
case MotionEvent.ACTION_UP: {
// Log.i(TAG, "Action up. button state: " + ev.getButtonState());
NativeApp.mouse(ev.getX(), ev.getY(), 1, 2);
break;
}
case MotionEvent.ACTION_MOVE: {
// Log.i(TAG, "Action move. button state: " + ev.getButtonState());
NativeApp.mouse(ev.getX(), ev.getY(), 0, 0);
break;
}
default: {
Log.i(TAG, "Unhandled modern mouse action: " + ev.getAction());
break;
}
}
return true;
}

@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(final MotionEvent ev) {
boolean canReadToolType = Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH;

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
// This is where workable mouse support arrived.
if (onMouseEventModern(ev)) {
return true;
}
}

for (int i = 0; i < ev.getPointerCount(); i++) {
int pid = ev.getPointerId(i);
int code = 0;
Expand Down Expand Up @@ -109,7 +151,6 @@ public boolean onTouchEvent(final MotionEvent ev) {
int tool = getToolType(ev, i);
code |= tool << 10; // We use the Android tool type codes
}
// Can't use || due to short circuit evaluation
NativeApp.touch(ev.getX(i), ev.getY(i), code, pid);
}
}
Expand Down
Loading