From e2323c136744ce5fe60800fb359257b268bde4a5 Mon Sep 17 00:00:00 2001
From: hwsmm <[EMAIL REDACTED]>
Date: Tue, 12 Nov 2024 23:04:40 +0900
Subject: [PATCH] Refactor Android input handling and add pen support
---
.../main/java/org/libsdl/app/SDLActivity.java | 6 +-
.../org/libsdl/app/SDLControllerManager.java | 183 ++++++-----------
.../main/java/org/libsdl/app/SDLSurface.java | 193 ++++++++----------
src/video/android/SDL_androidpen.c | 22 +-
4 files changed, 163 insertions(+), 241 deletions(-)
diff --git a/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
index a1edff7f407de..6067fa74dcafc 100644
--- a/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
+++ b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
@@ -223,7 +223,7 @@ public enum NativeState {
protected static SDLClipboardHandler mClipboardHandler;
protected static Hashtable<Integer, PointerIcon> mCursors;
protected static int mLastCursorID;
- protected static SDLGenericMotionListener_API12 mMotionListener;
+ protected static SDLGenericMotionListener_API14 mMotionListener;
protected static HIDDeviceManager mHIDDeviceManager;
// This is what SDL runs in. It invokes SDL_main(), eventually
@@ -232,14 +232,14 @@ public enum NativeState {
protected static boolean mActivityCreated = false;
private static SDLFileDialogState mFileDialogState = null;
- protected static SDLGenericMotionListener_API12 getMotionListener() {
+ protected static SDLGenericMotionListener_API14 getMotionListener() {
if (mMotionListener == null) {
if (Build.VERSION.SDK_INT >= 26 /* Android 8.0 (O) */) {
mMotionListener = new SDLGenericMotionListener_API26();
} else if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
mMotionListener = new SDLGenericMotionListener_API24();
} else {
- mMotionListener = new SDLGenericMotionListener_API12();
+ mMotionListener = new SDLGenericMotionListener_API14();
}
}
diff --git a/android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java b/android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java
index b7faee89972e3..dc6fb9d12a6ce 100644
--- a/android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java
+++ b/android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java
@@ -662,44 +662,61 @@ protected SDLHaptic getHaptic(int device_id) {
}
}
-class SDLGenericMotionListener_API12 implements View.OnGenericMotionListener {
+class SDLGenericMotionListener_API14 implements View.OnGenericMotionListener {
// Generic Motion (mouse hover, joystick...) events go here
@Override
public boolean onGenericMotion(View v, MotionEvent event) {
+ if (event.getSource() == InputDevice.SOURCE_JOYSTICK)
+ return SDLControllerManager.handleJoystickMotionEvent(event);
+
float x, y;
- int action;
+ int action = event.getActionMasked();
+ int pointerCount = event.getPointerCount();
+ boolean consumed = false;
- switch ( event.getSource() ) {
- case InputDevice.SOURCE_JOYSTICK:
- return SDLControllerManager.handleJoystickMotionEvent(event);
+ for (int i = 0; i < pointerCount; i++) {
+ int toolType = event.getToolType(i);
- case InputDevice.SOURCE_MOUSE:
- action = event.getActionMasked();
+ if (toolType == MotionEvent.TOOL_TYPE_MOUSE) {
switch (action) {
case MotionEvent.ACTION_SCROLL:
- x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
- y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
+ x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, i);
+ y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, i);
SDLActivity.onNativeMouse(0, action, x, y, false);
- return true;
+ consumed = true;
+ break;
case MotionEvent.ACTION_HOVER_MOVE:
- x = event.getX(0);
- y = event.getY(0);
+ x = getEventX(event, i);
+ y = getEventY(event, i);
- SDLActivity.onNativeMouse(0, action, x, y, false);
- return true;
+ SDLActivity.onNativeMouse(0, action, x, y, checkRelativeEvent(event));
+ consumed = true;
+ break;
default:
break;
}
- break;
+ } else if (toolType == MotionEvent.TOOL_TYPE_STYLUS || toolType == MotionEvent.TOOL_TYPE_ERASER) {
+ switch (action) {
+ case MotionEvent.ACTION_HOVER_ENTER:
+ case MotionEvent.ACTION_HOVER_MOVE:
+ case MotionEvent.ACTION_HOVER_EXIT:
+ x = event.getX(i);
+ y = event.getY(i);
+ float p = event.getPressure(i);
+
+ // BUTTON_STYLUS_PRIMARY is 2^5, so shift by 4
+ int buttons = event.getButtonState() >> 4;
- default:
- break;
+ SDLActivity.onNativePen(event.getPointerId(i), buttons, action, x, y, p);
+ consumed = true;
+ break;
+ }
+ }
}
- // Event was not managed
- return false;
+ return consumed;
}
public boolean supportsRelativeMouse() {
@@ -714,46 +731,29 @@ public boolean setRelativeMouseEnabled(boolean enabled) {
return false;
}
- public void reclaimRelativeMouseModeIfNeeded()
- {
+ public void reclaimRelativeMouseModeIfNeeded() {
+
+ }
+ public boolean checkRelativeEvent(MotionEvent event) {
+ return inRelativeMode();
}
- public float getEventX(MotionEvent event) {
- return event.getX(0);
+ public float getEventX(MotionEvent event, int pointerIndex) {
+ return event.getX(pointerIndex);
}
- public float getEventY(MotionEvent event) {
- return event.getY(0);
+ public float getEventY(MotionEvent event, int pointerIndex) {
+ return event.getY(pointerIndex);
}
}
-class SDLGenericMotionListener_API24 extends SDLGenericMotionListener_API12 {
+class SDLGenericMotionListener_API24 extends SDLGenericMotionListener_API14 {
// Generic Motion (mouse hover, joystick...) events go here
private boolean mRelativeModeEnabled;
- @Override
- public boolean onGenericMotion(View v, MotionEvent event) {
-
- // Handle relative mouse mode
- if (mRelativeModeEnabled) {
- if (event.getSource() == InputDevice.SOURCE_MOUSE) {
- int action = event.getActionMasked();
- if (action == MotionEvent.ACTION_HOVER_MOVE) {
- float x = event.getAxisValue(MotionEvent.AXIS_RELATIVE_X);
- float y = event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y);
- SDLActivity.onNativeMouse(0, action, x, y, true);
- return true;
- }
- }
- }
-
- // Event was not managed, call SDLGenericMotionListener_API12 method
- return super.onGenericMotion(v, event);
- }
-
@Override
public boolean supportsRelativeMouse() {
return true;
@@ -771,20 +771,20 @@ public boolean setRelativeMouseEnabled(boolean enabled) {
}
@Override
- public float getEventX(MotionEvent event) {
- if (mRelativeModeEnabled) {
- return event.getAxisValue(MotionEvent.AXIS_RELATIVE_X);
+ public float getEventX(MotionEvent event, int pointerIndex) {
+ if (mRelativeModeEnabled && event.getToolType(pointerIndex) == MotionEvent.TOOL_TYPE_MOUSE) {
+ return event.getAxisValue(MotionEvent.AXIS_RELATIVE_X, pointerIndex);
} else {
- return event.getX(0);
+ return event.getX(pointerIndex);
}
}
@Override
- public float getEventY(MotionEvent event) {
- if (mRelativeModeEnabled) {
- return event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y);
+ public float getEventY(MotionEvent event, int pointerIndex) {
+ if (mRelativeModeEnabled && event.getToolType(pointerIndex) == MotionEvent.TOOL_TYPE_MOUSE) {
+ return event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y, pointerIndex);
} else {
- return event.getY(0);
+ return event.getY(pointerIndex);
}
}
}
@@ -793,65 +793,6 @@ class SDLGenericMotionListener_API26 extends SDLGenericMotionListener_API24 {
// Generic Motion (mouse hover, joystick...) events go here
private boolean mRelativeModeEnabled;
- @Override
- public boolean onGenericMotion(View v, MotionEvent event) {
- float x, y;
- int action;
-
- switch ( event.getSource() ) {
- case InputDevice.SOURCE_JOYSTICK:
- return SDLControllerManager.handleJoystickMotionEvent(event);
-
- case InputDevice.SOURCE_MOUSE:
- // DeX desktop mouse cursor is a separate non-standard input type.
- case InputDevice.SOURCE_MOUSE | InputDevice.SOURCE_TOUCHSCREEN:
- action = event.getActionMasked();
- switch (action) {
- case MotionEvent.ACTION_SCROLL:
- x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
- y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
- SDLActivity.onNativeMouse(0, action, x, y, false);
- return true;
-
- case MotionEvent.ACTION_HOVER_MOVE:
- x = event.getX(0);
- y = event.getY(0);
- SDLActivity.onNativeMouse(0, action, x, y, false);
- return true;
-
- default:
- break;
- }
- break;
-
- case InputDevice.SOURCE_MOUSE_RELATIVE:
- action = event.getActionMasked();
- switch (action) {
- case MotionEvent.ACTION_SCROLL:
- x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
- y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
- SDLActivity.onNativeMouse(0, action, x, y, false);
- return true;
-
- case MotionEvent.ACTION_HOVER_MOVE:
- x = event.getX(0);
- y = event.getY(0);
- SDLActivity.onNativeMouse(0, action, x, y, true);
- return true;
-
- default:
- break;
- }
- break;
-
- default:
- break;
- }
-
- // Event was not managed
- return false;
- }
-
@Override
public boolean supportsRelativeMouse() {
return (!SDLActivity.isDeXMode() || Build.VERSION.SDK_INT >= 27 /* Android 8.1 (O_MR1) */);
@@ -878,22 +819,26 @@ public boolean setRelativeMouseEnabled(boolean enabled) {
}
@Override
- public void reclaimRelativeMouseModeIfNeeded()
- {
+ public void reclaimRelativeMouseModeIfNeeded() {
if (mRelativeModeEnabled && !SDLActivity.isDeXMode()) {
SDLActivity.getContentView().requestPointerCapture();
}
}
@Override
- public float getEventX(MotionEvent event) {
+ public boolean checkRelativeEvent(MotionEvent event) {
+ return event.getSource() == InputDevice.SOURCE_MOUSE_RELATIVE;
+ }
+
+ @Override
+ public float getEventX(MotionEvent event, int pointerIndex) {
// Relative mouse in capture mode will only have relative for X/Y
- return event.getX(0);
+ return event.getX(pointerIndex);
}
@Override
- public float getEventY(MotionEvent event) {
+ public float getEventY(MotionEvent event, int pointerIndex) {
// Relative mouse in capture mode will only have relative for X/Y
- return event.getY(0);
+ return event.getY(pointerIndex);
}
}
diff --git a/android-project/app/src/main/java/org/libsdl/app/SDLSurface.java b/android-project/app/src/main/java/org/libsdl/app/SDLSurface.java
index d896f6ce4a663..6b5f4346622db 100644
--- a/android-project/app/src/main/java/org/libsdl/app/SDLSurface.java
+++ b/android-project/app/src/main/java/org/libsdl/app/SDLSurface.java
@@ -239,93 +239,65 @@ public boolean onTouch(View v, MotionEvent event) {
int touchDevId = event.getDeviceId();
final int pointerCount = event.getPointerCount();
int action = event.getActionMasked();
- int pointerFingerId;
- int i = -1;
+ int pointerId;
+ int i = 0;
float x,y,p;
- // 12290 = Samsung DeX mode desktop mouse
- // 12290 = 0x3002 = 0x2002 | 0x1002 = SOURCE_MOUSE | SOURCE_TOUCHSCREEN
- // 0x2 = SOURCE_CLASS_POINTER
- if (event.getSource() == InputDevice.SOURCE_MOUSE || event.getSource() == (InputDevice.SOURCE_MOUSE | InputDevice.SOURCE_TOUCHSCREEN)) {
- int mouseButton = 1;
- try {
- Object object = event.getClass().getMethod("getButtonState").invoke(event);
- if (object != null) {
- mouseButton = (Integer) object;
+ if (action == MotionEvent.ACTION_POINTER_UP || action == MotionEvent.ACTION_POINTER_DOWN)
+ i = event.getActionIndex();
+
+ do {
+ int toolType = event.getToolType(i);
+
+ if (toolType == MotionEvent.TOOL_TYPE_MOUSE) {
+ int buttonState = event.getButtonState();
+ boolean relative = false;
+
+ // We need to check if we're in relative mouse mode and get the axis offset rather than the x/y values
+ // if we are. We'll leverage our existing mouse motion listener
+ SDLGenericMotionListener_API14 motionListener = SDLActivity.getMotionListener();
+ x = motionListener.getEventX(event, i);
+ y = motionListener.getEventY(event, i);
+ relative = motionListener.inRelativeMode();
+
+ SDLActivity.onNativeMouse(buttonState, action, x, y, relative);
+ } else if (toolType == MotionEvent.TOOL_TYPE_STYLUS || toolType == MotionEvent.TOOL_TYPE_ERASER) {
+ pointerId = event.getPointerId(i);
+ x = event.getX(i);
+ y = event.getY(i);
+ p = event.getPressure(i);
+ if (p > 1.0f) {
+ // may be larger than 1.0f on some devices
+ // see the documentation of getPressure(i)
+ p = 1.0f;
}
- } catch(Exception ignored) {
- }
-
- // We need to check if we're in relative mouse mode and get the axis offset rather than the x/y values
- // if we are. We'll leverage our existing mouse motion listener
- SDLGenericMotionListener_API12 motionListener = SDLActivity.getMotionListener();
- x = motionListener.getEventX(event);
- y = motionListener.getEventY(event);
-
- SDLActivity.onNativeMouse(mouseButton, action, x, y, motionListener.inRelativeMode());
- } else {
- switch(action) {
- case MotionEvent.ACTION_MOVE:
- for (i = 0; i < pointerCount; i++) {
- pointerFingerId = event.getPointerId(i);
- x = getNormalizedX(event.getX(i));
- y = getNormalizedY(event.getY(i));
- p = event.getPressure(i);
- if (p > 1.0f) {
- // may be larger than 1.0f on some devices
- // see the documentation of getPressure(i)
- p = 1.0f;
- }
- SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
- }
- break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_DOWN:
- // Primary pointer up/down, the index is always zero
- i = 0;
- /* fallthrough */
- case MotionEvent.ACTION_POINTER_UP:
- case MotionEvent.ACTION_POINTER_DOWN:
- // Non primary pointer up/down
- if (i == -1) {
- i = event.getActionIndex();
- }
-
- pointerFingerId = event.getPointerId(i);
- x = getNormalizedX(event.getX(i));
- y = getNormalizedY(event.getY(i));
- p = event.getPressure(i);
- if (p > 1.0f) {
- // may be larger than 1.0f on some devices
- // see the documentation of getPressure(i)
- p = 1.0f;
- }
- SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
- break;
-
- case MotionEvent.ACTION_CANCEL:
- for (i = 0; i < pointerCount; i++) {
- pointerFingerId = event.getPointerId(i);
- x = getNormalizedX(event.getX(i));
- y = getNormalizedY(event.getY(i));
- p = event.getPressure(i);
- if (p > 1.0f) {
- // may be larger than 1.0f on some devices
- // see the documentation of getPressure(i)
- p = 1.0f;
- }
- SDLActivity.onNativeTouch(touchDevId, pointerFingerId, MotionEvent.ACTION_UP, x, y, p);
- }
- break;
+ // BUTTON_STYLUS_PRIMARY is 2^5, so shift by 4, and apply SDL_PEN_INPUT_DOWN/SDL_PEN_INPUT_ERASER_TIP
+ int buttonState = (event.getButtonState() >> 4) | (1 << (toolType == MotionEvent.TOOL_TYPE_STYLUS ? 0 : 30));
+
+ SDLActivity.onNativePen(pointerId, buttonState, action, x, y, p);
+ } else if (toolType == MotionEvent.TOOL_TYPE_FINGER) {
+ pointerId = event.getPointerId(i);
+ x = getNormalizedX(event.getX(i));
+ y = getNormalizedY(event.getY(i));
+ p = event.getPressure(i);
+ if (p > 1.0f) {
+ // may be larger than 1.0f on some devices
+ // see the documentation of getPressure(i)
+ p = 1.0f;
+ }
- default:
- break;
+ SDLActivity.onNativeTouch(touchDevId, pointerId,
+ action == MotionEvent.ACTION_CANCEL ? MotionEvent.ACTION_UP : action, x, y, p);
}
- }
+
+ // Non-primary up/down
+ if (action == MotionEvent.ACTION_POINTER_UP || action == MotionEvent.ACTION_POINTER_DOWN)
+ break;
+ } while (++i < pointerCount);
return true;
- }
+ }
// Sensor events
public void enableSensor(int sensortype, boolean enabled) {
@@ -395,39 +367,42 @@ public void onSensorChanged(SensorEvent event) {
public boolean onCapturedPointerEvent(MotionEvent event)
{
int action = event.getActionMasked();
+ int pointerCount = event.getPointerCount();
- float x, y;
- switch (action) {
- case MotionEvent.ACTION_SCROLL:
- x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
- y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
- SDLActivity.onNativeMouse(0, action, x, y, false);
- return true;
-
- case MotionEvent.ACTION_HOVER_MOVE:
- case MotionEvent.ACTION_MOVE:
- x = event.getX(0);
- y = event.getY(0);
- SDLActivity.onNativeMouse(0, action, x, y, true);
- return true;
-
- case MotionEvent.ACTION_BUTTON_PRESS:
- case MotionEvent.ACTION_BUTTON_RELEASE:
-
- // Change our action value to what SDL's code expects.
- if (action == MotionEvent.ACTION_BUTTON_PRESS) {
- action = MotionEvent.ACTION_DOWN;
- } else { /* MotionEvent.ACTION_BUTTON_RELEASE */
- action = MotionEvent.ACTION_UP;
- }
+ for (int i = 0; i < pointerCount; i++) {
+ float x, y;
+ switch (action) {
+ case MotionEvent.ACTION_SCROLL:
+ x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, i);
+ y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, i);
+ SDLActivity.onNativeMouse(0, action, x, y, false);
+ return true;
+
+ case MotionEvent.ACTION_HOVER_MOVE:
+ case MotionEvent.ACTION_MOVE:
+ x = event.getX(i);
+ y = event.getY(i);
+ SDLActivity.onNativeMouse(0, action, x, y, true);
+ return true;
+
+ case MotionEvent.ACTION_BUTTON_PRESS:
+ case MotionEvent.ACTION_BUTTON_RELEASE:
+
+ // Change our action value to what SDL's code expects.
+ if (action == MotionEvent.ACTION_BUTTON_PRESS) {
+ action = MotionEvent.ACTION_DOWN;
+ } else { /* MotionEvent.ACTION_BUTTON_RELEASE */
+ action = MotionEvent.ACTION_UP;
+ }
- x = event.getX(0);
- y = event.getY(0);
- int button = event.getButtonState();
+ x = event.getX(i);
+ y = event.getY(i);
+ int button = event.getButtonState();
- SDLActivity.onNativeMouse(button, action, x, y, true);
- return true;
- }
+ SDLActivity.onNativeMouse(button, action, x, y, true);
+ return true;
+ }
+ }
return false;
}
diff --git a/src/video/android/SDL_androidpen.c b/src/video/android/SDL_androidpen.c
index 65730bd75dce3..a691b2e146ba3 100644
--- a/src/video/android/SDL_androidpen.c
+++ b/src/video/android/SDL_androidpen.c
@@ -26,11 +26,12 @@
#include "../../events/SDL_pen_c.h"
#include "../../core/android/SDL_android.h"
-#define ACTION_DOWN 0
-#define ACTION_UP 1
-#define ACTION_POINTER_DOWN 5
-#define ACTION_POINTER_UP 6
-#define ACTION_HOVER_EXIT 10
+#define ACTION_DOWN 0
+#define ACTION_UP 1
+#define ACTION_CANCEL 3
+#define ACTION_POINTER_DOWN 5
+#define ACTION_POINTER_UP 6
+#define ACTION_HOVER_EXIT 10
void Android_OnPen(SDL_Window *window, int pen_id_in, int button, int action, float x, float y, float p)
{
@@ -56,11 +57,6 @@ void Android_OnPen(SDL_Window *window, int pen_id_in, int button, int action, fl
}
}
- if (action == ACTION_HOVER_EXIT) {
- SDL_RemovePenDevice(0, pen);
- return;
- }
-
SDL_SendPenMotion(0, pen, window, x, y);
SDL_SendPenAxis(0, pen, window, SDL_PEN_AXIS_PRESSURE, p);
// TODO: add more axis
@@ -77,7 +73,13 @@ void Android_OnPen(SDL_Window *window, int pen_id_in, int button, int action, fl
}
// button contains DOWN/ERASER_TIP on DOWN/UP regardless of pressed state, use action to distinguish
+ // we don't compare tip flags above because MotionEvent.getButtonState doesn't return stylus tip/eraser state.
switch (action) {
+ case ACTION_CANCEL:
+ case ACTION_HOVER_EXIT:
+ SDL_RemovePenDevice(0, pen);
+ break;
+
case ACTION_DOWN:
case ACTION_POINTER_DOWN:
SDL_SendPenTouch(0, pen, window, (button & SDL_PEN_INPUT_ERASER_TIP) != 0, true);