Inject touch events on the server
On receiving an "inject touch" control message, update the local pointers state and inject touches.
This commit is contained in:
parent
77f876e29c
commit
f765aae352
3 changed files with 222 additions and 0 deletions
|
@ -23,10 +23,18 @@ public class Controller {
|
||||||
private final MotionEvent.PointerProperties[] mousePointerProperties = {new MotionEvent.PointerProperties()};
|
private final MotionEvent.PointerProperties[] mousePointerProperties = {new MotionEvent.PointerProperties()};
|
||||||
private final MotionEvent.PointerCoords[] mousePointerCoords = {new MotionEvent.PointerCoords()};
|
private final MotionEvent.PointerCoords[] mousePointerCoords = {new MotionEvent.PointerCoords()};
|
||||||
|
|
||||||
|
private long lastTouchDown;
|
||||||
|
private final PointersState pointersState = new PointersState();
|
||||||
|
private final MotionEvent.PointerProperties[] touchPointerProperties =
|
||||||
|
new MotionEvent.PointerProperties[PointersState.MAX_POINTERS];
|
||||||
|
private final MotionEvent.PointerCoords[] touchPointerCoords =
|
||||||
|
new MotionEvent.PointerCoords[PointersState.MAX_POINTERS];
|
||||||
|
|
||||||
public Controller(Device device, DesktopConnection connection) {
|
public Controller(Device device, DesktopConnection connection) {
|
||||||
this.device = device;
|
this.device = device;
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
initMousePointer();
|
initMousePointer();
|
||||||
|
initTouchPointers();
|
||||||
sender = new DeviceMessageSender(connection);
|
sender = new DeviceMessageSender(connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,6 +49,20 @@ public class Controller {
|
||||||
coords.size = 1;
|
coords.size = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void initTouchPointers() {
|
||||||
|
for (int i = 0; i < PointersState.MAX_POINTERS; ++i) {
|
||||||
|
MotionEvent.PointerProperties props = new MotionEvent.PointerProperties();
|
||||||
|
props.toolType = MotionEvent.TOOL_TYPE_FINGER;
|
||||||
|
|
||||||
|
MotionEvent.PointerCoords coords = new MotionEvent.PointerCoords();
|
||||||
|
coords.orientation = 0;
|
||||||
|
coords.size = 1;
|
||||||
|
|
||||||
|
touchPointerProperties[i] = props;
|
||||||
|
touchPointerCoords[i] = coords;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void setMousePointerCoords(Point point) {
|
private void setMousePointerCoords(Point point) {
|
||||||
MotionEvent.PointerCoords coords = mousePointerCoords[0];
|
MotionEvent.PointerCoords coords = mousePointerCoords[0];
|
||||||
coords.x = point.getX();
|
coords.x = point.getX();
|
||||||
|
@ -90,6 +112,9 @@ public class Controller {
|
||||||
case ControlMessage.TYPE_INJECT_MOUSE_EVENT:
|
case ControlMessage.TYPE_INJECT_MOUSE_EVENT:
|
||||||
injectMouse(msg.getAction(), msg.getButtons(), msg.getPosition());
|
injectMouse(msg.getAction(), msg.getButtons(), msg.getPosition());
|
||||||
break;
|
break;
|
||||||
|
case ControlMessage.TYPE_INJECT_TOUCH_EVENT:
|
||||||
|
injectTouch(msg.getAction(), msg.getPointerId(), msg.getPosition(), msg.getPressure());
|
||||||
|
break;
|
||||||
case ControlMessage.TYPE_INJECT_SCROLL_EVENT:
|
case ControlMessage.TYPE_INJECT_SCROLL_EVENT:
|
||||||
injectScroll(msg.getPosition(), msg.getHScroll(), msg.getVScroll());
|
injectScroll(msg.getPosition(), msg.getHScroll(), msg.getVScroll());
|
||||||
break;
|
break;
|
||||||
|
@ -164,6 +189,45 @@ public class Controller {
|
||||||
return injectEvent(event);
|
return injectEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean injectTouch(int action, long pointerId, Position position, float pressure) {
|
||||||
|
long now = SystemClock.uptimeMillis();
|
||||||
|
|
||||||
|
Point point = device.getPhysicalPoint(position);
|
||||||
|
if (point == null) {
|
||||||
|
// ignore event
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pointerIndex = pointersState.getPointerIndex(pointerId);
|
||||||
|
if (pointerIndex == -1) {
|
||||||
|
Ln.w("Too many pointers for touch event");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Pointer pointer = pointersState.get(pointerIndex);
|
||||||
|
pointer.setPoint(point);
|
||||||
|
pointer.setPressure(pressure);
|
||||||
|
pointer.setUp(action == MotionEvent.ACTION_UP);
|
||||||
|
|
||||||
|
int pointerCount = pointersState.update(touchPointerProperties, touchPointerCoords);
|
||||||
|
|
||||||
|
if (pointerCount == 1) {
|
||||||
|
if (action == MotionEvent.ACTION_DOWN) {
|
||||||
|
lastTouchDown = now;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// secondary pointers must use ACTION_POINTER_* ORed with the pointerIndex
|
||||||
|
if (action == MotionEvent.ACTION_UP) {
|
||||||
|
action = MotionEvent.ACTION_POINTER_UP | (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
|
||||||
|
} else if (action == MotionEvent.ACTION_DOWN) {
|
||||||
|
action = MotionEvent.ACTION_POINTER_DOWN | (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MotionEvent event = MotionEvent.obtain(lastTouchDown, now, action, pointerCount, touchPointerProperties,
|
||||||
|
touchPointerCoords, 0, 0, 1f, 1f, 0, 0, InputDevice.SOURCE_TOUCHSCREEN, 0);
|
||||||
|
return injectEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
private boolean injectScroll(Position position, int hScroll, int vScroll) {
|
private boolean injectScroll(Position position, int hScroll, int vScroll) {
|
||||||
long now = SystemClock.uptimeMillis();
|
long now = SystemClock.uptimeMillis();
|
||||||
Point point = device.getPhysicalPoint(position);
|
Point point = device.getPhysicalPoint(position);
|
||||||
|
|
55
server/src/main/java/com/genymobile/scrcpy/Pointer.java
Normal file
55
server/src/main/java/com/genymobile/scrcpy/Pointer.java
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
package com.genymobile.scrcpy;
|
||||||
|
|
||||||
|
public class Pointer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pointer id as received from the client.
|
||||||
|
*/
|
||||||
|
private final long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local pointer id, using the lowest possible values to fill the {@link android.view.MotionEvent.PointerProperties PointerProperties}.
|
||||||
|
*/
|
||||||
|
private final int localId;
|
||||||
|
|
||||||
|
private Point point;
|
||||||
|
private float pressure;
|
||||||
|
private boolean up;
|
||||||
|
|
||||||
|
public Pointer(long id, int localId) {
|
||||||
|
this.id = id;
|
||||||
|
this.localId = localId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLocalId() {
|
||||||
|
return localId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Point getPoint() {
|
||||||
|
return point;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPoint(Point point) {
|
||||||
|
this.point = point;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getPressure() {
|
||||||
|
return pressure;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPressure(float pressure) {
|
||||||
|
this.pressure = pressure;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUp() {
|
||||||
|
return up;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUp(boolean up) {
|
||||||
|
this.up = up;
|
||||||
|
}
|
||||||
|
}
|
103
server/src/main/java/com/genymobile/scrcpy/PointersState.java
Normal file
103
server/src/main/java/com/genymobile/scrcpy/PointersState.java
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
package com.genymobile.scrcpy;
|
||||||
|
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class PointersState {
|
||||||
|
|
||||||
|
public static final int MAX_POINTERS = 10;
|
||||||
|
|
||||||
|
private final List<Pointer> pointers = new ArrayList<>();
|
||||||
|
|
||||||
|
private int indexOf(long id) {
|
||||||
|
for (int i = 0; i < pointers.size(); ++i) {
|
||||||
|
Pointer pointer = pointers.get(i);
|
||||||
|
if (pointer.getId() == id) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isLocalIdAvailable(int localId) {
|
||||||
|
for (int i = 0; i < pointers.size(); ++i) {
|
||||||
|
Pointer pointer = pointers.get(i);
|
||||||
|
if (pointer.getLocalId() == localId) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int nextUnusedLocalId() {
|
||||||
|
for (int localId = 0; localId < MAX_POINTERS; ++localId) {
|
||||||
|
if (isLocalIdAvailable(localId)) {
|
||||||
|
return localId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Pointer get(int index) {
|
||||||
|
return pointers.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPointerIndex(long id) {
|
||||||
|
int index = indexOf(id);
|
||||||
|
if (index != -1) {
|
||||||
|
// already exists, return it
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
if (pointers.size() >= MAX_POINTERS) {
|
||||||
|
// it's full
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
// id 0 is reserved for mouse events
|
||||||
|
int localId = nextUnusedLocalId();
|
||||||
|
if (localId == -1) {
|
||||||
|
throw new AssertionError("pointers.size() < maxFingers implies that a local id is available");
|
||||||
|
}
|
||||||
|
Pointer pointer = new Pointer(id, localId);
|
||||||
|
pointers.add(pointer);
|
||||||
|
// return the index of the pointer
|
||||||
|
return pointers.size() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the motion event parameters.
|
||||||
|
*
|
||||||
|
* @param props the pointer properties
|
||||||
|
* @param coords the pointer coordinates
|
||||||
|
* @return The number of items initialized (the number of pointers).
|
||||||
|
*/
|
||||||
|
public int update(MotionEvent.PointerProperties[] props, MotionEvent.PointerCoords[] coords) {
|
||||||
|
int count = pointers.size();
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
Pointer pointer = pointers.get(i);
|
||||||
|
|
||||||
|
// id 0 is reserved for mouse events
|
||||||
|
props[i].id = pointer.getLocalId();
|
||||||
|
|
||||||
|
Point point = pointer.getPoint();
|
||||||
|
coords[i].x = point.getX();
|
||||||
|
coords[i].y = point.getY();
|
||||||
|
coords[i].pressure = pointer.getPressure();
|
||||||
|
}
|
||||||
|
cleanUp();
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all pointers which are UP.
|
||||||
|
*/
|
||||||
|
private void cleanUp() {
|
||||||
|
for (int i = pointers.size() - 1; i >= 0; --i) {
|
||||||
|
Pointer pointer = pointers.get(i);
|
||||||
|
if (pointer.isUp()) {
|
||||||
|
pointers.remove(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue