Add contact area and orientation for pen/touch events

This commit is contained in:
Cameron Gutman 2023-07-22 17:18:57 -05:00
parent 0e29e13d03
commit 67b2853ef0
5 changed files with 124 additions and 21 deletions

View File

@ -1532,6 +1532,100 @@ public class Game extends Activity implements SurfaceHolder.Callback,
return new float[] { normalizedX, normalizedY }; return new float[] { normalizedX, normalizedY };
} }
private static float normalizeValueInRange(float value, InputDevice.MotionRange range) {
return (value - range.getMin()) / range.getRange();
}
private static float getPressureOrDistance(MotionEvent event) {
InputDevice dev = event.getDevice();
switch (event.getActionMasked()) {
case MotionEvent.ACTION_HOVER_ENTER:
case MotionEvent.ACTION_HOVER_MOVE:
case MotionEvent.ACTION_HOVER_EXIT:
// Hover events report distance
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 0.0f;
default:
// Other events report pressure
return event.getPressure(event.getActionIndex());
}
}
private static short getRotationDegrees(MotionEvent event) {
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()));
if (rotationDegrees < 0) {
rotationDegrees += 360;
}
return rotationDegrees;
}
}
return MoonBridge.LI_ROT_UNKNOWN;
}
private static float[] polarToCartesian(float r, float theta) {
return new float[] { (float)(r * Math.cos(theta)), (float)(r * Math.sin(theta)) };
}
private static float cartesianToR(float[] point) {
return (float)Math.sqrt(Math.pow(point[0], 2) + Math.pow(point[1], 2));
}
private float[] getStreamViewNormalizedContactArea(MotionEvent event) {
float orientation;
// If the orientation is unknown, we'll just assume it's at a 45 degree angle and scale it by
// X and Y scaling factors evenly.
if (event.getDevice() == null || event.getDevice().getMotionRange(MotionEvent.AXIS_ORIENTATION, event.getSource()) == null) {
orientation = (float)(Math.PI / 4);
}
else {
orientation = event.getOrientation(event.getActionIndex());
}
float contactAreaMajor, contactAreaMinor;
switch (event.getActionMasked()) {
// Hover events report the tool size
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());
break;
// Other events report contact area
default:
contactAreaMajor = event.getTouchMajor(event.getActionIndex());
contactAreaMinor = event.getTouchMinor(event.getActionIndex());
break;
}
// The contact area major axis is parallel to the orientation, so we simply convert
// polar to cartesian coordinates using the orientation as theta.
float[] contactAreaMajorCartesian = polarToCartesian(contactAreaMajor, orientation);
// The contact area minor axis is perpendicular to the contact area major axis (and thus
// the orientation), so rotate the orientation angle by 90 degrees.
float[] contactAreaMinorCartesian = polarToCartesian(contactAreaMinor, (float)(orientation + (Math.PI / 2)));
// Normalize the contact area to the stream view size
contactAreaMajorCartesian[0] = Math.min(Math.abs(contactAreaMajorCartesian[0]), streamView.getWidth()) / streamView.getWidth();
contactAreaMinorCartesian[0] = Math.min(Math.abs(contactAreaMinorCartesian[0]), streamView.getWidth()) / streamView.getWidth();
contactAreaMajorCartesian[1] = Math.min(Math.abs(contactAreaMajorCartesian[1]), streamView.getHeight()) / streamView.getHeight();
contactAreaMinorCartesian[1] = Math.min(Math.abs(contactAreaMinorCartesian[1]), streamView.getHeight()) / streamView.getHeight();
// Convert the normalized values back into polar coordinates
return new float[] { cartesianToR(contactAreaMajorCartesian), cartesianToR(contactAreaMinorCartesian) };
}
private boolean trySendPenEvent(View view, MotionEvent event) { private boolean trySendPenEvent(View view, MotionEvent event) {
byte eventType = getLiTouchTypeFromEvent(event); byte eventType = getLiTouchTypeFromEvent(event);
if (eventType < 0) { if (eventType < 0) {
@ -1558,26 +1652,21 @@ public class Game extends Activity implements SurfaceHolder.Callback,
penButtons |= MoonBridge.LI_PEN_BUTTON_SECONDARY; penButtons |= MoonBridge.LI_PEN_BUTTON_SECONDARY;
} }
short rotationDegrees = MoonBridge.LI_ROT_UNKNOWN;
byte tiltDegrees = MoonBridge.LI_TILT_UNKNOWN; byte tiltDegrees = MoonBridge.LI_TILT_UNKNOWN;
InputDevice dev = event.getDevice(); InputDevice dev = event.getDevice();
if (dev != null) { if (dev != null) {
if (dev.getMotionRange(MotionEvent.AXIS_ORIENTATION, event.getSource()) != null) {
rotationDegrees = (short)Math.toDegrees(event.getOrientation(event.getActionIndex()));
if (rotationDegrees < 0) {
rotationDegrees += 360;
}
}
if (dev.getMotionRange(MotionEvent.AXIS_TILT, event.getSource()) != 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, event.getActionIndex()));
} }
} }
float[] normalizedCoords = getStreamViewRelativeNormalizedXY(view, event); float[] normalizedCoords = getStreamViewRelativeNormalizedXY(view, event);
float[] normalizedContactArea = getStreamViewNormalizedContactArea(event);
return conn.sendPenEvent(eventType, toolType, penButtons, return conn.sendPenEvent(eventType, toolType, penButtons,
normalizedCoords[0], normalizedCoords[1], normalizedCoords[0], normalizedCoords[1],
event.getPressure(event.getActionIndex()), getPressureOrDistance(event),
rotationDegrees, tiltDegrees) != MoonBridge.LI_ERR_UNSUPPORTED; normalizedContactArea[0], normalizedContactArea[1],
getRotationDegrees(event), tiltDegrees) != MoonBridge.LI_ERR_UNSUPPORTED;
} }
private boolean trySendTouchEvent(View view, MotionEvent event) { private boolean trySendTouchEvent(View view, MotionEvent event) {
@ -1587,9 +1676,12 @@ public class Game extends Activity implements SurfaceHolder.Callback,
} }
float[] normalizedCoords = getStreamViewRelativeNormalizedXY(view, event); float[] normalizedCoords = getStreamViewRelativeNormalizedXY(view, event);
float[] normalizedContactArea = getStreamViewNormalizedContactArea(event);
return conn.sendTouchEvent(eventType, event.getPointerId(event.getActionIndex()), return conn.sendTouchEvent(eventType, event.getPointerId(event.getActionIndex()),
normalizedCoords[0], normalizedCoords[1], normalizedCoords[0], normalizedCoords[1],
event.getPressure(event.getActionIndex())) != MoonBridge.LI_ERR_UNSUPPORTED; getPressureOrDistance(event),
normalizedContactArea[0], normalizedContactArea[1],
getRotationDegrees(event)) != MoonBridge.LI_ERR_UNSUPPORTED;
} }
// Returns true if the event was consumed // Returns true if the event was consumed

View File

@ -530,9 +530,11 @@ public class NvConnection {
} }
} }
public int sendTouchEvent(byte eventType, int pointerId, float x, float y, float pressure) { public int sendTouchEvent(byte eventType, int pointerId, float x, float y, float pressureOrDistance,
float contactAreaMajor, float contactAreaMinor, short rotation) {
if (!isMonkey) { if (!isMonkey) {
return MoonBridge.sendTouchEvent(eventType, pointerId, x, y, pressure); return MoonBridge.sendTouchEvent(eventType, pointerId, x, y, pressureOrDistance,
contactAreaMajor, contactAreaMinor, rotation);
} }
else { else {
return MoonBridge.LI_ERR_UNSUPPORTED; return MoonBridge.LI_ERR_UNSUPPORTED;
@ -540,9 +542,11 @@ public class NvConnection {
} }
public int sendPenEvent(byte eventType, byte toolType, byte penButtons, float x, float y, public int sendPenEvent(byte eventType, byte toolType, byte penButtons, float x, float y,
float pressure, short rotation, byte tilt) { float pressureOrDistance, float contactAreaMajor, float contactAreaMinor,
short rotation, byte tilt) {
if (!isMonkey) { if (!isMonkey) {
return MoonBridge.sendPenEvent(eventType, toolType, penButtons, x, y, pressure, rotation, tilt); return MoonBridge.sendPenEvent(eventType, toolType, penButtons, x, y, pressureOrDistance,
contactAreaMajor, contactAreaMinor, rotation, tilt);
} }
else { else {
return MoonBridge.LI_ERR_UNSUPPORTED; return MoonBridge.LI_ERR_UNSUPPORTED;

View File

@ -372,10 +372,12 @@ public class MoonBridge {
short leftStickX, short leftStickY, short leftStickX, short leftStickY,
short rightStickX, short rightStickY); short rightStickX, short rightStickY);
public static native int sendTouchEvent(byte eventType, int pointerId, float x, float y, float pressure); public static native int sendTouchEvent(byte eventType, int pointerId, float x, float y, float pressure,
float contactAreaMajor, float contactAreaMinor, short rotation);
public static native int sendPenEvent(byte eventType, byte toolType, byte penButtons, float x, float y, public static native int sendPenEvent(byte eventType, byte toolType, byte penButtons, float x, float y,
float pressure, short rotation, byte tilt); float pressure, float contactAreaMajor, float contactAreaMinor,
short rotation, byte tilt);
public static native int sendControllerArrivalEvent(byte controllerNumber, short activeGamepadMask, byte type, int supportedButtonFlags, short capabilities); public static native int sendControllerArrivalEvent(byte controllerNumber, short activeGamepadMask, byte type, int supportedButtonFlags, short capabilities);

@ -1 +1 @@
Subproject commit 2d0badde9aa9a48480e24a289569de1046c6acba Subproject commit 70a2e305bc7170eccbd48d8c49b64a814e64ecb7

View File

@ -45,16 +45,21 @@ Java_com_limelight_nvstream_jni_MoonBridge_sendMultiControllerInput(JNIEnv *env,
JNIEXPORT jint JNICALL JNIEXPORT jint JNICALL
Java_com_limelight_nvstream_jni_MoonBridge_sendTouchEvent(JNIEnv *env, jclass clazz, Java_com_limelight_nvstream_jni_MoonBridge_sendTouchEvent(JNIEnv *env, jclass clazz,
jbyte eventType, jint pointerId, jbyte eventType, jint pointerId,
jfloat x, jfloat y, jfloat pressure) { jfloat x, jfloat y, jfloat pressureOrDistance,
return LiSendTouchEvent(eventType, pointerId, x, y, pressure); jfloat contactAreaMajor, jfloat contactAreaMinor,
jshort rotation) {
return LiSendTouchEvent(eventType, pointerId, x, y, pressureOrDistance,
contactAreaMajor, contactAreaMinor, rotation);
} }
JNIEXPORT jint JNICALL JNIEXPORT jint JNICALL
Java_com_limelight_nvstream_jni_MoonBridge_sendPenEvent(JNIEnv *env, jclass clazz, jbyte eventType, Java_com_limelight_nvstream_jni_MoonBridge_sendPenEvent(JNIEnv *env, jclass clazz, jbyte eventType,
jbyte toolType, jbyte penButtons, jbyte toolType, jbyte penButtons,
jfloat x, jfloat y, jfloat pressure, jfloat x, jfloat y, jfloat pressureOrDistance,
jfloat contactAreaMajor, jfloat contactAreaMinor,
jshort rotation, jbyte tilt) { jshort rotation, jbyte tilt) {
return LiSendPenEvent(eventType, toolType, penButtons, x, y, pressure, rotation, tilt); return LiSendPenEvent(eventType, toolType, penButtons, x, y, pressureOrDistance,
contactAreaMajor, contactAreaMinor, rotation, tilt);
} }
JNIEXPORT jint JNICALL JNIEXPORT jint JNICALL