Fix mishandling ACTION_MOVE events for native pen/touch events

This commit is contained in:
Cameron Gutman 2023-07-27 23:15:26 -05:00
parent 554fee037c
commit c9c1ef91fd
2 changed files with 118 additions and 53 deletions

View File

@ -1509,9 +1509,9 @@ public class Game extends Activity implements SurfaceHolder.Callback,
}
}
private float[] getStreamViewRelativeNormalizedXY(View view, MotionEvent event) {
float normalizedX = event.getX(event.getActionIndex());
float normalizedY = event.getY(event.getActionIndex());
private float[] getStreamViewRelativeNormalizedXY(View view, MotionEvent event, int pointerIndex) {
float normalizedX = event.getX(pointerIndex);
float normalizedY = event.getY(pointerIndex);
// For the containing background view, we must subtract the origin
// of the StreamView to get video-relative coordinates.
@ -1536,7 +1536,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
return (value - range.getMin()) / range.getRange();
}
private static float getPressureOrDistance(MotionEvent event) {
private static float getPressureOrDistance(MotionEvent event, int pointerIndex) {
InputDevice dev = event.getDevice();
switch (event.getActionMasked()) {
case MotionEvent.ACTION_HOVER_ENTER:
@ -1546,22 +1546,22 @@ public class Game extends Activity implements SurfaceHolder.Callback,
if (dev != null) {
InputDevice.MotionRange distanceRange = dev.getMotionRange(MotionEvent.AXIS_DISTANCE, event.getSource());
if (distanceRange != null) {
return normalizeValueInRange(event.getAxisValue(MotionEvent.AXIS_DISTANCE, event.getActionIndex()), distanceRange);
return normalizeValueInRange(event.getAxisValue(MotionEvent.AXIS_DISTANCE, pointerIndex), distanceRange);
}
}
return 0.0f;
default:
// Other events report pressure
return event.getPressure(event.getActionIndex());
return event.getPressure(pointerIndex);
}
}
private static short getRotationDegrees(MotionEvent event) {
private static short getRotationDegrees(MotionEvent event, int pointerIndex) {
InputDevice dev = event.getDevice();
if (dev != null) {
if (dev.getMotionRange(MotionEvent.AXIS_ORIENTATION, event.getSource()) != null) {
short rotationDegrees = (short) Math.toDegrees(event.getOrientation(event.getActionIndex()));
short rotationDegrees = (short) Math.toDegrees(event.getOrientation(pointerIndex));
if (rotationDegrees < 0) {
rotationDegrees += 360;
}
@ -1579,7 +1579,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
return (float)Math.sqrt(Math.pow(point[0], 2) + Math.pow(point[1], 2));
}
private float[] getStreamViewNormalizedContactArea(MotionEvent event) {
private float[] getStreamViewNormalizedContactArea(MotionEvent event, int pointerIndex) {
float orientation;
// If the orientation is unknown, we'll just assume it's at a 45 degree angle and scale it by
@ -1588,7 +1588,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
orientation = (float)(Math.PI / 4);
}
else {
orientation = event.getOrientation(event.getActionIndex());
orientation = event.getOrientation(pointerIndex);
}
float contactAreaMajor, contactAreaMinor;
@ -1597,14 +1597,14 @@ public class Game extends Activity implements SurfaceHolder.Callback,
case MotionEvent.ACTION_HOVER_ENTER:
case MotionEvent.ACTION_HOVER_MOVE:
case MotionEvent.ACTION_HOVER_EXIT:
contactAreaMajor = event.getToolMajor(event.getActionIndex());
contactAreaMinor = event.getToolMinor(event.getActionIndex());
contactAreaMajor = event.getToolMajor(pointerIndex);
contactAreaMinor = event.getToolMinor(pointerIndex);
break;
// Other events report contact area
default:
contactAreaMajor = event.getTouchMajor(event.getActionIndex());
contactAreaMinor = event.getTouchMinor(event.getActionIndex());
contactAreaMajor = event.getTouchMajor(pointerIndex);
contactAreaMinor = event.getTouchMinor(pointerIndex);
break;
}
@ -1626,24 +1626,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
return new float[] { cartesianToR(contactAreaMajorCartesian), cartesianToR(contactAreaMinorCartesian) };
}
private boolean trySendPenEvent(View view, MotionEvent event) {
byte eventType = getLiTouchTypeFromEvent(event);
if (eventType < 0) {
return false;
}
byte toolType;
switch (event.getToolType(event.getActionIndex())) {
case MotionEvent.TOOL_TYPE_ERASER:
toolType = MoonBridge.LI_TOOL_TYPE_ERASER;
break;
case MotionEvent.TOOL_TYPE_STYLUS:
toolType = MoonBridge.LI_TOOL_TYPE_PEN;
break;
default:
return false;
}
private boolean sendPenEventForPointer(View view, MotionEvent event, byte eventType, byte toolType, int pointerIndex) {
byte penButtons = 0;
if ((event.getButtonState() & MotionEvent.BUTTON_STYLUS_PRIMARY) != 0) {
penButtons |= MoonBridge.LI_PEN_BUTTON_PRIMARY;
@ -1656,17 +1639,76 @@ public class Game extends Activity implements SurfaceHolder.Callback,
InputDevice dev = event.getDevice();
if (dev != null) {
if (dev.getMotionRange(MotionEvent.AXIS_TILT, event.getSource()) != null) {
tiltDegrees = (byte)Math.toDegrees(event.getAxisValue(MotionEvent.AXIS_TILT, event.getActionIndex()));
tiltDegrees = (byte)Math.toDegrees(event.getAxisValue(MotionEvent.AXIS_TILT, pointerIndex));
}
}
float[] normalizedCoords = getStreamViewRelativeNormalizedXY(view, event);
float[] normalizedContactArea = getStreamViewNormalizedContactArea(event);
float[] normalizedCoords = getStreamViewRelativeNormalizedXY(view, event, pointerIndex);
float[] normalizedContactArea = getStreamViewNormalizedContactArea(event, pointerIndex);
return conn.sendPenEvent(eventType, toolType, penButtons,
normalizedCoords[0], normalizedCoords[1],
getPressureOrDistance(event),
getPressureOrDistance(event, pointerIndex),
normalizedContactArea[0], normalizedContactArea[1],
getRotationDegrees(event), tiltDegrees) != MoonBridge.LI_ERR_UNSUPPORTED;
getRotationDegrees(event, pointerIndex), tiltDegrees) != MoonBridge.LI_ERR_UNSUPPORTED;
}
private static byte convertToolTypeToStylusToolType(MotionEvent event, int pointerIndex) {
switch (event.getToolType(pointerIndex)) {
case MotionEvent.TOOL_TYPE_ERASER:
return MoonBridge.LI_TOOL_TYPE_ERASER;
case MotionEvent.TOOL_TYPE_STYLUS:
return MoonBridge.LI_TOOL_TYPE_PEN;
default:
return MoonBridge.LI_TOOL_TYPE_UNKNOWN;
}
}
private boolean trySendPenEvent(View view, MotionEvent event) {
byte eventType = getLiTouchTypeFromEvent(event);
if (eventType < 0) {
return false;
}
if (event.getActionMasked() == MotionEvent.ACTION_MOVE) {
// Move events may impact all active pointers
boolean handledStylusEvent = false;
for (int i = 0; i < event.getPointerCount(); i++) {
byte toolType = convertToolTypeToStylusToolType(event, i);
if (toolType == MoonBridge.LI_TOOL_TYPE_UNKNOWN) {
// Not a stylus pointer, so skip it
continue;
}
else {
// This pointer is a stylus, so we'll report that we handled this event
handledStylusEvent = true;
}
if (!sendPenEventForPointer(view, event, eventType, toolType, i)) {
// Pen events aren't supported by the host
return false;
}
}
return handledStylusEvent;
}
else {
// Up, Down, Hover, and Cancel events are specific to the action index
byte toolType = convertToolTypeToStylusToolType(event, event.getActionIndex());
if (toolType == MoonBridge.LI_TOOL_TYPE_UNKNOWN) {
// Not a stylus event
return false;
}
return sendPenEventForPointer(view, event, eventType, toolType, event.getActionIndex());
}
}
private boolean sendTouchEventForPointer(View view, MotionEvent event, byte eventType, int pointerIndex) {
float[] normalizedCoords = getStreamViewRelativeNormalizedXY(view, event, pointerIndex);
float[] normalizedContactArea = getStreamViewNormalizedContactArea(event, pointerIndex);
return conn.sendTouchEvent(eventType, event.getPointerId(pointerIndex),
normalizedCoords[0], normalizedCoords[1],
getPressureOrDistance(event, pointerIndex),
normalizedContactArea[0], normalizedContactArea[1],
getRotationDegrees(event, pointerIndex)) != MoonBridge.LI_ERR_UNSUPPORTED;
}
private boolean trySendTouchEvent(View view, MotionEvent event) {
@ -1675,13 +1717,19 @@ public class Game extends Activity implements SurfaceHolder.Callback,
return false;
}
float[] normalizedCoords = getStreamViewRelativeNormalizedXY(view, event);
float[] normalizedContactArea = getStreamViewNormalizedContactArea(event);
return conn.sendTouchEvent(eventType, event.getPointerId(event.getActionIndex()),
normalizedCoords[0], normalizedCoords[1],
getPressureOrDistance(event),
normalizedContactArea[0], normalizedContactArea[1],
getRotationDegrees(event)) != MoonBridge.LI_ERR_UNSUPPORTED;
if (event.getActionMasked() == MotionEvent.ACTION_MOVE) {
// Move events may impact all active pointers
for (int i = 0; i < event.getPointerCount(); i++) {
if (!sendTouchEventForPointer(view, event, eventType, i)) {
return false;
}
}
return true;
}
else {
// Up, Down, Hover, and Cancel events are specific to the action index
return sendTouchEventForPointer(view, event, eventType, event.getActionIndex());
}
}
// Returns true if the event was consumed

View File

@ -1502,6 +1502,18 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
return value / range.getRange();
}
private boolean sendTouchpadEventForPointer(InputDeviceContext context, MotionEvent event, byte touchType, int pointerIndex) {
float normalizedX = normalizeRawValueWithRange(event.getX(pointerIndex), context.touchpadXRange);
float normalizedY = normalizeRawValueWithRange(event.getY(pointerIndex), context.touchpadYRange);
float normalizedPressure = context.touchpadPressureRange != null ?
normalizeRawValueWithRange(event.getPressure(pointerIndex), context.touchpadPressureRange)
: 0;
return conn.sendControllerTouchEvent((byte)context.controllerNumber, touchType,
event.getPointerId(pointerIndex),
normalizedX, normalizedY, normalizedPressure) != MoonBridge.LI_ERR_UNSUPPORTED;
}
public boolean tryHandleTouchpadEvent(MotionEvent event) {
// Bail if this is not a touchpad event
if (event.getSource() != InputDevice.SOURCE_TOUCHPAD) {
@ -1573,15 +1585,20 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
return false;
}
float normalizedX = normalizeRawValueWithRange(event.getX(event.getActionIndex()), context.touchpadXRange);
float normalizedY = normalizeRawValueWithRange(event.getY(event.getActionIndex()), context.touchpadYRange);
float normalizedPressure = context.touchpadPressureRange != null ?
normalizeRawValueWithRange(event.getPressure(event.getActionIndex()), context.touchpadPressureRange)
: 0;
return conn.sendControllerTouchEvent((byte)context.controllerNumber, touchType,
event.getPointerId(event.getActionIndex()),
normalizedX, normalizedY, normalizedPressure) != MoonBridge.LI_ERR_UNSUPPORTED;
if (event.getActionMasked() == MotionEvent.ACTION_MOVE) {
// Move events may impact all active pointers
for (int i = 0; i < event.getPointerCount(); i++) {
if (!sendTouchpadEventForPointer(context, event, touchType, i)) {
// Controller touch events are not supported by the host
return false;
}
}
return true;
}
else {
// Down and Up events impact the action index pointer
return sendTouchpadEventForPointer(context, event, touchType, event.getActionIndex());
}
}
public boolean handleMotionEvent(MotionEvent event) {