|
|
|
|
@ -2,7 +2,6 @@ package com.genymobile.scrcpy;
|
|
|
|
|
|
|
|
|
|
import com.genymobile.scrcpy.wrappers.InputManager;
|
|
|
|
|
|
|
|
|
|
import android.graphics.Point;
|
|
|
|
|
import android.os.SystemClock;
|
|
|
|
|
import android.view.InputDevice;
|
|
|
|
|
import android.view.InputEvent;
|
|
|
|
|
@ -11,95 +10,41 @@ import android.view.KeyEvent;
|
|
|
|
|
import android.view.MotionEvent;
|
|
|
|
|
|
|
|
|
|
import java.io.IOException;
|
|
|
|
|
import java.util.Vector;
|
|
|
|
|
|
|
|
|
|
public class Controller {
|
|
|
|
|
|
|
|
|
|
private static final int DEVICE_ID_VIRTUAL = -1;
|
|
|
|
|
|
|
|
|
|
private final Device device;
|
|
|
|
|
private final DesktopConnection connection;
|
|
|
|
|
private final DeviceMessageSender sender;
|
|
|
|
|
|
|
|
|
|
private final KeyCharacterMap charMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
|
|
|
|
|
|
|
|
|
|
private long lastMouseDown;
|
|
|
|
|
private Vector<MotionEvent.PointerProperties> pointerProperties = new Vector<MotionEvent.PointerProperties>();
|
|
|
|
|
private Vector<MotionEvent.PointerCoords> pointerCoords = new Vector<MotionEvent.PointerCoords>();
|
|
|
|
|
private long lastTouchDown;
|
|
|
|
|
private final PointersState pointersState = new PointersState();
|
|
|
|
|
private final MotionEvent.PointerProperties[] pointerProperties = new MotionEvent.PointerProperties[PointersState.MAX_POINTERS];
|
|
|
|
|
private final MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[PointersState.MAX_POINTERS];
|
|
|
|
|
|
|
|
|
|
public Controller(Device device, DesktopConnection connection) {
|
|
|
|
|
this.device = device;
|
|
|
|
|
this.connection = connection;
|
|
|
|
|
initPointers();
|
|
|
|
|
sender = new DeviceMessageSender(connection);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private int getPointer(int id) {
|
|
|
|
|
for (int i = 0; i < pointerProperties.size(); i++) {
|
|
|
|
|
if (id == pointerProperties.get(i).id) {
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void initPointers() {
|
|
|
|
|
for (int i = 0; i < PointersState.MAX_POINTERS; ++i) {
|
|
|
|
|
MotionEvent.PointerProperties props = new MotionEvent.PointerProperties();
|
|
|
|
|
props.id = id;
|
|
|
|
|
props.toolType = MotionEvent.TOOL_TYPE_FINGER;
|
|
|
|
|
pointerProperties.addElement(props);
|
|
|
|
|
|
|
|
|
|
MotionEvent.PointerCoords coords = new MotionEvent.PointerCoords();
|
|
|
|
|
coords.orientation = 0;
|
|
|
|
|
coords.pressure = 1;
|
|
|
|
|
coords.size = 1;
|
|
|
|
|
pointerCoords.addElement(coords);
|
|
|
|
|
return pointerProperties.size() - 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void releasePointer(int id) {
|
|
|
|
|
int index = -1;
|
|
|
|
|
for (int i = 0; i < pointerProperties.size(); i++) {
|
|
|
|
|
if (id == pointerProperties.get(i).id) {
|
|
|
|
|
index = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( -1 != index) {
|
|
|
|
|
pointerProperties.remove(index);
|
|
|
|
|
pointerCoords.remove(index);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void setPointerCoords(int id, Point point) {
|
|
|
|
|
int index = -1;
|
|
|
|
|
for (int i = 0; i < pointerProperties.size(); i++) {
|
|
|
|
|
if (id == pointerProperties.get(i).id) {
|
|
|
|
|
index = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( -1 != index) {
|
|
|
|
|
MotionEvent.PointerCoords coords = pointerCoords.get(index);
|
|
|
|
|
coords.x = point.x;
|
|
|
|
|
coords.y = point.y;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void setScroll(int id, int hScroll, int vScroll) {
|
|
|
|
|
int index = -1;
|
|
|
|
|
for (int i = 0; i < pointerProperties.size(); i++) {
|
|
|
|
|
if (id == pointerProperties.get(i).id) {
|
|
|
|
|
index = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
pointerProperties[i] = props;
|
|
|
|
|
pointerCoords[i] = coords;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( -1 != index) {
|
|
|
|
|
MotionEvent.PointerCoords coords = pointerCoords.get(index);
|
|
|
|
|
coords.setAxisValue(MotionEvent.AXIS_HSCROLL, hScroll);
|
|
|
|
|
coords.setAxisValue(MotionEvent.AXIS_VSCROLL, vScroll);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public DeviceMessageSender getSender() {
|
|
|
|
|
return sender;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@SuppressWarnings("checkstyle:MagicNumber")
|
|
|
|
|
@ -123,6 +68,10 @@ public class Controller {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public DeviceMessageSender getSender() {
|
|
|
|
|
return sender;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void handleEvent() throws IOException {
|
|
|
|
|
ControlMessage msg = connection.receiveControlMessage();
|
|
|
|
|
switch (msg.getType()) {
|
|
|
|
|
@ -132,13 +81,10 @@ public class Controller {
|
|
|
|
|
case ControlMessage.TYPE_INJECT_TEXT:
|
|
|
|
|
injectText(msg.getText());
|
|
|
|
|
break;
|
|
|
|
|
case ControlMessage.TYPE_INJECT_MOUSE:
|
|
|
|
|
injectMouse(msg.getAction(), msg.getButtons(), msg.getPosition());
|
|
|
|
|
break;
|
|
|
|
|
case ControlMessage.TYPE_INJECT_TOUCH:
|
|
|
|
|
injectTouch(msg.getId(), msg.getAction(), msg.getPosition());
|
|
|
|
|
case ControlMessage.TYPE_INJECT_TOUCH_EVENT:
|
|
|
|
|
injectTouch(msg.getAction(), msg.getPointerId(), msg.getPosition(), msg.getPressure(), msg.getButtons());
|
|
|
|
|
break;
|
|
|
|
|
case ControlMessage.TYPE_INJECT_SCROLL:
|
|
|
|
|
case ControlMessage.TYPE_INJECT_SCROLL_EVENT:
|
|
|
|
|
injectScroll(msg.getPosition(), msg.getHScroll(), msg.getVScroll());
|
|
|
|
|
break;
|
|
|
|
|
case ControlMessage.TYPE_BACK_OR_SCREEN_ON:
|
|
|
|
|
@ -160,6 +106,9 @@ public class Controller {
|
|
|
|
|
case ControlMessage.TYPE_SET_SCREEN_POWER_MODE:
|
|
|
|
|
device.setScreenPowerMode(msg.getAction());
|
|
|
|
|
break;
|
|
|
|
|
case ControlMessage.TYPE_ROTATE_DEVICE:
|
|
|
|
|
device.rotateDevice();
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
// do nothing
|
|
|
|
|
}
|
|
|
|
|
@ -196,37 +145,8 @@ public class Controller {
|
|
|
|
|
return successCount;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private boolean injectTouch(int id, int action, Position position) {
|
|
|
|
|
if (action != MotionEvent.ACTION_DOWN
|
|
|
|
|
&& action != MotionEvent.ACTION_UP
|
|
|
|
|
&& action != MotionEvent.ACTION_MOVE) {
|
|
|
|
|
Ln.w("Unsupported action: " + action);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (id < 0 || id > 9) {
|
|
|
|
|
Ln.w("Unsupported id[0-9]: " + id);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int index = getPointer(id);
|
|
|
|
|
int convertAction = action;
|
|
|
|
|
switch (action) {
|
|
|
|
|
case MotionEvent.ACTION_DOWN:
|
|
|
|
|
if (1 != pointerProperties.size()) {
|
|
|
|
|
convertAction = (index << 8) | MotionEvent.ACTION_POINTER_DOWN;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case MotionEvent.ACTION_MOVE:
|
|
|
|
|
if (1 != pointerProperties.size()) {
|
|
|
|
|
convertAction = (index << 8) | convertAction;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case MotionEvent.ACTION_UP:
|
|
|
|
|
if (1 != pointerProperties.size()) {
|
|
|
|
|
convertAction = (index << 8) | MotionEvent.ACTION_POINTER_UP;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
private boolean injectTouch(int action, long pointerId, Position position, float pressure, int buttons) {
|
|
|
|
|
long now = SystemClock.uptimeMillis();
|
|
|
|
|
|
|
|
|
|
Point point = device.getPhysicalPoint(position);
|
|
|
|
|
if (point == null) {
|
|
|
|
|
@ -234,49 +154,34 @@ public class Controller {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pointerProperties.isEmpty()) {
|
|
|
|
|
// ignore event
|
|
|
|
|
int pointerIndex = pointersState.getPointerIndex(pointerId);
|
|
|
|
|
if (pointerIndex == -1) {
|
|
|
|
|
Ln.w("Too many pointers for touch event");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
setPointerCoords(id, point);
|
|
|
|
|
MotionEvent.PointerProperties[] props = pointerProperties.toArray(new MotionEvent.PointerProperties[pointerProperties.size()]);
|
|
|
|
|
MotionEvent.PointerCoords[] coords = pointerCoords.toArray(new MotionEvent.PointerCoords[pointerCoords.size()]);
|
|
|
|
|
MotionEvent event = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), convertAction,
|
|
|
|
|
pointerProperties.size(), props, coords, 0, 0, 1f, 1f, 0, 0,
|
|
|
|
|
InputDevice.SOURCE_TOUCHSCREEN, 0);
|
|
|
|
|
Pointer pointer = pointersState.get(pointerIndex);
|
|
|
|
|
pointer.setPoint(point);
|
|
|
|
|
pointer.setPressure(pressure);
|
|
|
|
|
pointer.setUp(action == MotionEvent.ACTION_UP);
|
|
|
|
|
|
|
|
|
|
if (action == MotionEvent.ACTION_UP) {
|
|
|
|
|
releasePointer(id);
|
|
|
|
|
}
|
|
|
|
|
return injectEvent(event);
|
|
|
|
|
}
|
|
|
|
|
int pointerCount = pointersState.update(pointerProperties, pointerCoords);
|
|
|
|
|
|
|
|
|
|
private boolean injectMouse(int action, int buttons, Position position) {
|
|
|
|
|
long now = SystemClock.uptimeMillis();
|
|
|
|
|
if (pointerCount == 1) {
|
|
|
|
|
if (action == MotionEvent.ACTION_DOWN) {
|
|
|
|
|
getPointer(0);
|
|
|
|
|
lastMouseDown = now;
|
|
|
|
|
lastTouchDown = now;
|
|
|
|
|
}
|
|
|
|
|
Point point = device.getPhysicalPoint(position);
|
|
|
|
|
if (point == null) {
|
|
|
|
|
// ignore event
|
|
|
|
|
return false;
|
|
|
|
|
} 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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pointerProperties.isEmpty()) {
|
|
|
|
|
// ignore event
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
setPointerCoords(0, point);
|
|
|
|
|
MotionEvent.PointerProperties[] props = pointerProperties.toArray(new MotionEvent.PointerProperties[pointerProperties.size()]);
|
|
|
|
|
MotionEvent.PointerCoords[] coords = pointerCoords.toArray(new MotionEvent.PointerCoords[pointerCoords.size()]);
|
|
|
|
|
MotionEvent event = MotionEvent.obtain(lastMouseDown, now, action,
|
|
|
|
|
pointerProperties.size(), props, coords, 0, buttons, 1f, 1f, 0, 0,
|
|
|
|
|
InputDevice.SOURCE_TOUCHSCREEN, 0);
|
|
|
|
|
|
|
|
|
|
if (action == MotionEvent.ACTION_UP) {
|
|
|
|
|
releasePointer(0);
|
|
|
|
|
}
|
|
|
|
|
MotionEvent event = MotionEvent
|
|
|
|
|
.obtain(lastTouchDown, now, action, pointerCount, pointerProperties, pointerCoords, 0, buttons, 1f, 1f, DEVICE_ID_VIRTUAL, 0,
|
|
|
|
|
InputDevice.SOURCE_TOUCHSCREEN, 0);
|
|
|
|
|
return injectEvent(event);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -288,23 +193,18 @@ public class Controller {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// init
|
|
|
|
|
MotionEvent.PointerProperties[] props = {new MotionEvent.PointerProperties()};
|
|
|
|
|
props[0].id = 0;
|
|
|
|
|
props[0].toolType = MotionEvent.TOOL_TYPE_FINGER;
|
|
|
|
|
MotionEvent.PointerCoords[] coords = {new MotionEvent.PointerCoords()};
|
|
|
|
|
coords[0].orientation = 0;
|
|
|
|
|
coords[0].pressure = 1;
|
|
|
|
|
coords[0].size = 1;
|
|
|
|
|
|
|
|
|
|
// set data
|
|
|
|
|
coords[0].x = point.x;
|
|
|
|
|
coords[0].y = point.y;
|
|
|
|
|
coords[0].setAxisValue(MotionEvent.AXIS_HSCROLL, hScroll);
|
|
|
|
|
coords[0].setAxisValue(MotionEvent.AXIS_VSCROLL, vScroll);
|
|
|
|
|
|
|
|
|
|
MotionEvent event = MotionEvent.obtain(lastMouseDown, now, MotionEvent.ACTION_SCROLL, 1, props, coords, 0, 0, 1f, 1f, 0,
|
|
|
|
|
0, InputDevice.SOURCE_MOUSE, 0);
|
|
|
|
|
MotionEvent.PointerProperties props = pointerProperties[0];
|
|
|
|
|
props.id = 0;
|
|
|
|
|
|
|
|
|
|
MotionEvent.PointerCoords coords = pointerCoords[0];
|
|
|
|
|
coords.x = point.getX();
|
|
|
|
|
coords.y = point.getY();
|
|
|
|
|
coords.setAxisValue(MotionEvent.AXIS_HSCROLL, hScroll);
|
|
|
|
|
coords.setAxisValue(MotionEvent.AXIS_VSCROLL, vScroll);
|
|
|
|
|
|
|
|
|
|
MotionEvent event = MotionEvent
|
|
|
|
|
.obtain(lastTouchDown, now, MotionEvent.ACTION_SCROLL, 1, pointerProperties, pointerCoords, 0, 0, 1f, 1f, DEVICE_ID_VIRTUAL, 0,
|
|
|
|
|
InputDevice.SOURCE_MOUSE, 0);
|
|
|
|
|
return injectEvent(event);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -316,8 +216,7 @@ public class Controller {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private boolean injectKeycode(int keyCode) {
|
|
|
|
|
return injectKeyEvent(KeyEvent.ACTION_DOWN, keyCode, 0, 0)
|
|
|
|
|
&& injectKeyEvent(KeyEvent.ACTION_UP, keyCode, 0, 0);
|
|
|
|
|
return injectKeyEvent(KeyEvent.ACTION_DOWN, keyCode, 0, 0) && injectKeyEvent(KeyEvent.ACTION_UP, keyCode, 0, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private boolean injectEvent(InputEvent event) {
|
|
|
|
|
|