mirror of
https://github.com/Genymobile/scrcpy.git
synced 2025-09-03 08:06:02 +00:00
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:
parent
11a60e5767
commit
8984c1a7c4
13 changed files with 246 additions and 96 deletions
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
33
server/src/com/genymobile/scrcpy/Point.java
Normal file
33
server/src/com/genymobile/scrcpy/Point.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
20
server/src/com/genymobile/scrcpy/RawPoint.java
Normal file
20
server/src/com/genymobile/scrcpy/RawPoint.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue