Scale mouse events

The video screen size on the client may differ from the real device
screen size (e.g. the video stream may be scaled down). As a
consequence, mouse events must be scaled to match the real device
coordinates.

For this purpose, make the client send the video screen size along with
the absolute pointer location, and the server scale the location to
match the real device size before injecting mouse events.
This commit is contained in:
Romain Vimont 2018-01-22 16:50:08 +01:00
commit 8984c1a7c4
13 changed files with 246 additions and 96 deletions

View file

@ -16,8 +16,7 @@ public class ControlEvent {
private int action; // KeyEvent.ACTION_* or MotionEvent.ACTION_*
private int keycode; // KeyEvent.KEYCODE_*
private int buttons; // MotionEvent.BUTTON_*
private int x;
private int y;
private Point point;
private int hScroll;
private int vScroll;
@ -40,21 +39,19 @@ public class ControlEvent {
return event;
}
public static ControlEvent createMotionControlEvent(int action, int buttons, int x, int y) {
public static ControlEvent createMotionControlEvent(int action, int buttons, Point point) {
ControlEvent event = new ControlEvent();
event.type = TYPE_MOUSE;
event.action = action;
event.buttons = buttons;
event.x = x;
event.y = y;
event.point = point;
return event;
}
public static ControlEvent createScrollControlEvent(int x, int y, int hScroll, int vScroll) {
public static ControlEvent createScrollControlEvent(Point point, int hScroll, int vScroll) {
ControlEvent event = new ControlEvent();
event.type = TYPE_SCROLL;
event.x = x;
event.y = y;
event.point = point;
event.hScroll = hScroll;
event.vScroll = vScroll;
return event;
@ -84,12 +81,8 @@ public class ControlEvent {
return buttons;
}
public int getX() {
return x;
}
public int getY() {
return y;
public Point getPoint() {
return point;
}
public int getHScroll() {

View file

@ -51,7 +51,7 @@ public class ControlEventReader {
if (buffer.remaining() < KEYCODE_PAYLOAD_LENGTH) {
break;
}
int action = buffer.get() & 0xff; // unsigned
int action = toUnsigned(buffer.get());
int keycode = buffer.getInt();
int metaState = buffer.getInt();
return ControlEvent.createKeycodeControlEvent(action, keycode, metaState);
@ -60,7 +60,7 @@ public class ControlEventReader {
if (buffer.remaining() < 1) {
break;
}
int len = buffer.get() & 0xff; // unsigned
int len = toUnsigned(buffer.get());
if (buffer.remaining() < len) {
break;
}
@ -72,21 +72,19 @@ public class ControlEventReader {
if (buffer.remaining() < MOUSE_PAYLOAD_LENGTH) {
break;
}
int action = buffer.get() & 0xff; // unsigned
int action = toUnsigned(buffer.get());
int buttons = buffer.getInt();
int x = buffer.getInt();
int y = buffer.getInt();
return ControlEvent.createMotionControlEvent(action, buttons, x, y);
Point point = readPoint(buffer);
return ControlEvent.createMotionControlEvent(action, buttons, point);
}
case ControlEvent.TYPE_SCROLL: {
if (buffer.remaining() < SCROLL_PAYLOAD_LENGTH) {
break;
}
int x = buffer.getInt();
int y = buffer.getInt();
Point point = readPoint(buffer);
int hscroll = buffer.getInt();
int vscroll = buffer.getInt();
return ControlEvent.createScrollControlEvent(x, y, hscroll, vscroll);
return ControlEvent.createScrollControlEvent(point, hscroll, vscroll);
}
default:
Ln.w("Unknown event type: " + type);
@ -96,4 +94,20 @@ public class ControlEventReader {
buffer.position(savedPosition);
return null;
}
private static Point readPoint(ByteBuffer buffer) {
int x = toUnsigned(buffer.getShort());
int y = toUnsigned(buffer.getShort());
int screenWidth = toUnsigned(buffer.getShort());
int screenHeight = toUnsigned(buffer.getShort());
return new Point(x, y, screenWidth, screenHeight);
}
private static int toUnsigned(short value) {
return value & 0xffff;
}
private static int toUnsigned(byte value) {
return value & 0xff;
}
}

View file

@ -48,6 +48,15 @@ public class Device {
return screenInfo;
}
public RawPoint getPhysicalPoint(Point point) {
ScreenInfo screenInfo = getScreenInfo();
int deviceWidth = screenInfo.getLogicalWidth();
int deviceHeight = screenInfo.getLogicalHeight();
int scaledX = point.getX() * deviceWidth / point.getScreenWidth();
int scaledY = point.getY() * deviceHeight / point.getScreenHeight();
return new RawPoint(scaledX, scaledY);
}
private ScreenInfo readScreenInfo() {
return serviceManager.getDisplayManager().getScreenInfo();
}

View file

@ -43,10 +43,10 @@ public class EventController {
coords.touchMinor = 1;
}
private void setPointerCoords(int x, int y) {
private void setPointerCoords(RawPoint rawPoint) {
MotionEvent.PointerCoords coords = pointerCoords[0];
coords.x = x;
coords.y = y;
coords.x = rawPoint.getX();
coords.y = rawPoint.getY();
}
private void setScroll(int hScroll, int vScroll) {
@ -72,10 +72,10 @@ public class EventController {
injectText(controlEvent.getText());
break;
case ControlEvent.TYPE_MOUSE:
injectMouse(controlEvent.getAction(), controlEvent.getButtons(), controlEvent.getX(), controlEvent.getY());
injectMouse(controlEvent.getAction(), controlEvent.getButtons(), controlEvent.getPoint());
break;
case ControlEvent.TYPE_SCROLL:
injectScroll(controlEvent.getButtons(), controlEvent.getX(), controlEvent.getY(), controlEvent.getHScroll(), controlEvent.getVScroll());
injectScroll(controlEvent.getPoint(), controlEvent.getHScroll(), controlEvent.getVScroll());
}
return true;
}
@ -97,19 +97,29 @@ public class EventController {
return true;
}
private boolean injectMouse(int action, int buttons, int x, int y) {
private boolean injectMouse(int action, int buttons, Point point) {
long now = SystemClock.uptimeMillis();
if (action == MotionEvent.ACTION_DOWN) {
lastMouseDown = now;
}
setPointerCoords(x, y);
RawPoint rawPoint = Device.getInstance().getPhysicalPoint(point);
if (rawPoint == null) {
// ignore event
return false;
}
setPointerCoords(rawPoint);
MotionEvent event = MotionEvent.obtain(lastMouseDown, now, action, 1, pointerProperties, pointerCoords, 0, buttons, 1f, 1f, 0, 0, InputDevice.SOURCE_MOUSE, 0);
return injectEvent(event);
}
private boolean injectScroll(int buttons, int x, int y, int hScroll, int vScroll) {
private boolean injectScroll(Point point, int hScroll, int vScroll) {
long now = SystemClock.uptimeMillis();
setPointerCoords(x, y);
RawPoint rawPoint = Device.getInstance().getPhysicalPoint(point);
if (rawPoint == null) {
// ignore event
return false;
}
setPointerCoords(rawPoint);
setScroll(hScroll, vScroll);
MotionEvent event = MotionEvent.obtain(lastMouseDown, now, MotionEvent.ACTION_SCROLL, 1, pointerProperties, pointerCoords, 0, 0, 1f, 1f, 0, 0, InputDevice.SOURCE_MOUSE, 0);
return injectEvent(event);

View file

@ -0,0 +1,33 @@
package com.genymobile.scrcpy;
public class Point {
private int x;
private int y;
private int screenWidth;
private int screenHeight;
public Point(int x, int y, int screenWidth, int screenHeight) {
this.x = x;
this.y = y;
this.screenWidth = screenWidth;
this.screenHeight = screenHeight;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getScreenWidth() {
return screenWidth;
}
public int getScreenHeight() {
return screenHeight;
}
}

View file

@ -0,0 +1,20 @@
package com.genymobile.scrcpy;
public class RawPoint {
private int x;
private int y;
public RawPoint(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}