Skip to content

Commit

Permalink
Accept touch control events without display
Browse files Browse the repository at this point in the history
Fixes #5542 <#5542>
  • Loading branch information
rom1v committed Nov 28, 2024
1 parent 017a367 commit f8d1e7c
Showing 1 changed file with 46 additions and 29 deletions.
75 changes: 46 additions & 29 deletions server/src/main/java/com/genymobile/scrcpy/control/Controller.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import android.content.Intent;
import android.os.Build;
import android.os.SystemClock;
import android.util.Pair;
import android.view.InputDevice;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
Expand Down Expand Up @@ -350,24 +351,47 @@ private int injectText(String text) {
return successCount;
}

private boolean injectTouch(int action, long pointerId, Position position, float pressure, int actionButton, int buttons) {
long now = SystemClock.uptimeMillis();

private Pair<Point, Integer> getEventPointAndDisplayId(Position position) {
// it hides the field on purpose, to read it with atomic access
@SuppressWarnings("checkstyle:HiddenField")
DisplayData displayData = this.displayData.get();
assert displayData != null : "Cannot receive a touch 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 touch event generated for size " + eventSize + " (current size is " + currentSize + ")");
// 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;
int targetDisplayId;
if (displayData != null) {
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 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;
}

Point point = pair.first;
int targetDisplayId = pair.second;

int pointerIndex = pointersState.getPointerIndex(pointerId);
if (pointerIndex == -1) {
Ln.w("Too many pointers for touch event");
Expand Down Expand Up @@ -421,7 +445,7 @@ private boolean injectTouch(int action, long pointerId, Position position, float
// First button pressed: ACTION_DOWN
MotionEvent downEvent = MotionEvent.obtain(lastTouchDown, now, MotionEvent.ACTION_DOWN, pointerCount, pointerProperties,
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;
}
}
Expand All @@ -432,7 +456,7 @@ private boolean injectTouch(int action, long pointerId, Position position, float
if (!InputManager.setActionButton(pressEvent, actionButton)) {
return false;
}
if (!Device.injectEvent(pressEvent, displayData.virtualDisplayId, Device.INJECT_MODE_ASYNC)) {
if (!Device.injectEvent(pressEvent, targetDisplayId, Device.INJECT_MODE_ASYNC)) {
return false;
}

Expand All @@ -446,15 +470,15 @@ private boolean injectTouch(int action, long pointerId, Position position, float
if (!InputManager.setActionButton(releaseEvent, actionButton)) {
return false;
}
if (!Device.injectEvent(releaseEvent, displayData.virtualDisplayId, Device.INJECT_MODE_ASYNC)) {
if (!Device.injectEvent(releaseEvent, targetDisplayId, Device.INJECT_MODE_ASYNC)) {
return false;
}

if (buttons == 0) {
// Last button released: ACTION_UP
MotionEvent upEvent = MotionEvent.obtain(lastTouchDown, now, MotionEvent.ACTION_UP, pointerCount, pointerProperties,
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;
}
}
Expand All @@ -465,27 +489,20 @@ private boolean injectTouch(int action, long pointerId, Position position, float

MotionEvent event = MotionEvent.obtain(lastTouchDown, now, action, pointerCount, pointerProperties, pointerCoords, 0, buttons, 1f, 1f,
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) {
long now = SystemClock.uptimeMillis();

// it hides the field on purpose, to read it with atomic access
@SuppressWarnings("checkstyle:HiddenField")
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 + ")");
}
Pair<Point, Integer> pair = getEventPointAndDisplayId(position);
if (pair == null) {
return false;
}

Point point = pair.first;
int targetDisplayId = pair.second;

MotionEvent.PointerProperties props = pointerProperties[0];
props.id = 0;

Expand All @@ -497,7 +514,7 @@ private boolean injectScroll(Position position, float hScroll, float vScroll, in

MotionEvent event = MotionEvent.obtain(lastTouchDown, now, MotionEvent.ACTION_SCROLL, 1, pointerProperties, pointerCoords, 0, buttons, 1f, 1f,
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);
}

/**
Expand Down

0 comments on commit f8d1e7c

Please sign in to comment.