Accept touch control events without display
Fixes #5542 <https://github.com/Genymobile/scrcpy/issues/5542>
This commit is contained in:
parent
017a3672a4
commit
f8d1e7ce3c
@ -21,6 +21,7 @@ import android.content.IOnPrimaryClipChangedListener;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
|
import android.util.Pair;
|
||||||
import android.view.InputDevice;
|
import android.view.InputDevice;
|
||||||
import android.view.KeyCharacterMap;
|
import android.view.KeyCharacterMap;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
@ -350,24 +351,47 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
|
|||||||
return successCount;
|
return successCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean injectTouch(int action, long pointerId, Position position, float pressure, int actionButton, int buttons) {
|
private Pair<Point, Integer> getEventPointAndDisplayId(Position position) {
|
||||||
long now = SystemClock.uptimeMillis();
|
|
||||||
|
|
||||||
// it hides the field on purpose, to read it with atomic access
|
// it hides the field on purpose, to read it with atomic access
|
||||||
@SuppressWarnings("checkstyle:HiddenField")
|
@SuppressWarnings("checkstyle:HiddenField")
|
||||||
DisplayData displayData = this.displayData.get();
|
DisplayData displayData = this.displayData.get();
|
||||||
assert displayData != null : "Cannot receive a touch event without a display";
|
// In scrcpy, displayData should never be null (a touch event can only be generated from the client when a video frame is present).
|
||||||
|
// However, it is possible to send events without video playback when using scrcpy-server alone (except for virtual displays).
|
||||||
|
assert displayData != null || displayId != Device.DISPLAY_ID_NONE : "Cannot receive a touch event without a display";
|
||||||
|
|
||||||
Point point = displayData.positionMapper.map(position);
|
Point point;
|
||||||
if (point == null) {
|
int targetDisplayId;
|
||||||
if (Ln.isEnabled(Ln.Level.VERBOSE)) {
|
if (displayData != null) {
|
||||||
Size eventSize = position.getScreenSize();
|
point = displayData.positionMapper.map(position);
|
||||||
Size currentSize = displayData.positionMapper.getVideoSize();
|
if (point == null) {
|
||||||
Ln.v("Ignore touch event generated for size " + eventSize + " (current size is " + currentSize + ")");
|
if (Ln.isEnabled(Ln.Level.VERBOSE)) {
|
||||||
|
Size eventSize = position.getScreenSize();
|
||||||
|
Size currentSize = displayData.positionMapper.getVideoSize();
|
||||||
|
Ln.v("Ignore event generated for size " + eventSize + " (current size is " + currentSize + ")");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
targetDisplayId = displayData.virtualDisplayId;
|
||||||
|
} else {
|
||||||
|
// No display, use the raw coordinates
|
||||||
|
point = position.getPoint();
|
||||||
|
targetDisplayId = displayId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Pair.create(point, targetDisplayId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean injectTouch(int action, long pointerId, Position position, float pressure, int actionButton, int buttons) {
|
||||||
|
long now = SystemClock.uptimeMillis();
|
||||||
|
|
||||||
|
Pair<Point, Integer> pair = getEventPointAndDisplayId(position);
|
||||||
|
if (pair == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Point point = pair.first;
|
||||||
|
int targetDisplayId = pair.second;
|
||||||
|
|
||||||
int pointerIndex = pointersState.getPointerIndex(pointerId);
|
int pointerIndex = pointersState.getPointerIndex(pointerId);
|
||||||
if (pointerIndex == -1) {
|
if (pointerIndex == -1) {
|
||||||
Ln.w("Too many pointers for touch event");
|
Ln.w("Too many pointers for touch event");
|
||||||
@ -421,7 +445,7 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
|
|||||||
// First button pressed: ACTION_DOWN
|
// First button pressed: ACTION_DOWN
|
||||||
MotionEvent downEvent = MotionEvent.obtain(lastTouchDown, now, MotionEvent.ACTION_DOWN, pointerCount, pointerProperties,
|
MotionEvent downEvent = MotionEvent.obtain(lastTouchDown, now, MotionEvent.ACTION_DOWN, pointerCount, pointerProperties,
|
||||||
pointerCoords, 0, buttons, 1f, 1f, DEFAULT_DEVICE_ID, 0, source, 0);
|
pointerCoords, 0, buttons, 1f, 1f, DEFAULT_DEVICE_ID, 0, source, 0);
|
||||||
if (!Device.injectEvent(downEvent, displayData.virtualDisplayId, Device.INJECT_MODE_ASYNC)) {
|
if (!Device.injectEvent(downEvent, targetDisplayId, Device.INJECT_MODE_ASYNC)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -432,7 +456,7 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
|
|||||||
if (!InputManager.setActionButton(pressEvent, actionButton)) {
|
if (!InputManager.setActionButton(pressEvent, actionButton)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!Device.injectEvent(pressEvent, displayData.virtualDisplayId, Device.INJECT_MODE_ASYNC)) {
|
if (!Device.injectEvent(pressEvent, targetDisplayId, Device.INJECT_MODE_ASYNC)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -446,7 +470,7 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
|
|||||||
if (!InputManager.setActionButton(releaseEvent, actionButton)) {
|
if (!InputManager.setActionButton(releaseEvent, actionButton)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!Device.injectEvent(releaseEvent, displayData.virtualDisplayId, Device.INJECT_MODE_ASYNC)) {
|
if (!Device.injectEvent(releaseEvent, targetDisplayId, Device.INJECT_MODE_ASYNC)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -454,7 +478,7 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
|
|||||||
// Last button released: ACTION_UP
|
// Last button released: ACTION_UP
|
||||||
MotionEvent upEvent = MotionEvent.obtain(lastTouchDown, now, MotionEvent.ACTION_UP, pointerCount, pointerProperties,
|
MotionEvent upEvent = MotionEvent.obtain(lastTouchDown, now, MotionEvent.ACTION_UP, pointerCount, pointerProperties,
|
||||||
pointerCoords, 0, buttons, 1f, 1f, DEFAULT_DEVICE_ID, 0, source, 0);
|
pointerCoords, 0, buttons, 1f, 1f, DEFAULT_DEVICE_ID, 0, source, 0);
|
||||||
if (!Device.injectEvent(upEvent, displayData.virtualDisplayId, Device.INJECT_MODE_ASYNC)) {
|
if (!Device.injectEvent(upEvent, targetDisplayId, Device.INJECT_MODE_ASYNC)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -465,27 +489,20 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
|
|||||||
|
|
||||||
MotionEvent event = MotionEvent.obtain(lastTouchDown, now, action, pointerCount, pointerProperties, pointerCoords, 0, buttons, 1f, 1f,
|
MotionEvent event = MotionEvent.obtain(lastTouchDown, now, action, pointerCount, pointerProperties, pointerCoords, 0, buttons, 1f, 1f,
|
||||||
DEFAULT_DEVICE_ID, 0, source, 0);
|
DEFAULT_DEVICE_ID, 0, source, 0);
|
||||||
return Device.injectEvent(event, displayData.virtualDisplayId, Device.INJECT_MODE_ASYNC);
|
return Device.injectEvent(event, targetDisplayId, Device.INJECT_MODE_ASYNC);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean injectScroll(Position position, float hScroll, float vScroll, int buttons) {
|
private boolean injectScroll(Position position, float hScroll, float vScroll, int buttons) {
|
||||||
long now = SystemClock.uptimeMillis();
|
long now = SystemClock.uptimeMillis();
|
||||||
|
|
||||||
// it hides the field on purpose, to read it with atomic access
|
Pair<Point, Integer> pair = getEventPointAndDisplayId(position);
|
||||||
@SuppressWarnings("checkstyle:HiddenField")
|
if (pair == null) {
|
||||||
DisplayData displayData = this.displayData.get();
|
|
||||||
assert displayData != null : "Cannot receive a scroll event without a display";
|
|
||||||
|
|
||||||
Point point = displayData.positionMapper.map(position);
|
|
||||||
if (point == null) {
|
|
||||||
if (Ln.isEnabled(Ln.Level.VERBOSE)) {
|
|
||||||
Size eventSize = position.getScreenSize();
|
|
||||||
Size currentSize = displayData.positionMapper.getVideoSize();
|
|
||||||
Ln.v("Ignore scroll event generated for size " + eventSize + " (current size is " + currentSize + ")");
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Point point = pair.first;
|
||||||
|
int targetDisplayId = pair.second;
|
||||||
|
|
||||||
MotionEvent.PointerProperties props = pointerProperties[0];
|
MotionEvent.PointerProperties props = pointerProperties[0];
|
||||||
props.id = 0;
|
props.id = 0;
|
||||||
|
|
||||||
@ -497,7 +514,7 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
|
|||||||
|
|
||||||
MotionEvent event = MotionEvent.obtain(lastTouchDown, now, MotionEvent.ACTION_SCROLL, 1, pointerProperties, pointerCoords, 0, buttons, 1f, 1f,
|
MotionEvent event = MotionEvent.obtain(lastTouchDown, now, MotionEvent.ACTION_SCROLL, 1, pointerProperties, pointerCoords, 0, buttons, 1f, 1f,
|
||||||
DEFAULT_DEVICE_ID, 0, InputDevice.SOURCE_MOUSE, 0);
|
DEFAULT_DEVICE_ID, 0, InputDevice.SOURCE_MOUSE, 0);
|
||||||
return Device.injectEvent(event, displayData.virtualDisplayId, Device.INJECT_MODE_ASYNC);
|
return Device.injectEvent(event, targetDisplayId, Device.INJECT_MODE_ASYNC);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
x
Reference in New Issue
Block a user