mirror of
https://github.com/barry-ran/QtScrcpy.git
synced 2025-07-28 11:48:39 +00:00
update:同步scrcpy
This commit is contained in:
parent
d2e83da5b1
commit
dcffea679f
21 changed files with 277 additions and 113 deletions
|
@ -14,14 +14,15 @@ public final class ControlMessage {
|
|||
public static final int TYPE_COLLAPSE_NOTIFICATION_PANEL = 6;
|
||||
public static final int TYPE_GET_CLIPBOARD = 7;
|
||||
public static final int TYPE_SET_CLIPBOARD = 8;
|
||||
public static final int TYPE_SET_SCREEN_POWER_MODE = 9;
|
||||
|
||||
public static final int TYPE_INJECT_TOUCH = 9;
|
||||
public static final int TYPE_INJECT_TOUCH = 10;
|
||||
|
||||
|
||||
private int type;
|
||||
private String text;
|
||||
private int metaState; // KeyEvent.META_*
|
||||
private int action; // KeyEvent.ACTION_* or MotionEvent.ACTION_*
|
||||
private int action; // KeyEvent.ACTION_* or MotionEvent.ACTION_* or POWER_MODE_*
|
||||
private int keycode; // KeyEvent.KEYCODE_*
|
||||
private int buttons; // MotionEvent.BUTTON_*
|
||||
private int id;
|
||||
|
@ -82,6 +83,16 @@ public final class ControlMessage {
|
|||
return event;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mode one of the {@code Device.SCREEN_POWER_MODE_*} constants
|
||||
*/
|
||||
public static ControlMessage createSetScreenPowerMode(int mode) {
|
||||
ControlMessage event = new ControlMessage();
|
||||
event.type = TYPE_SET_SCREEN_POWER_MODE;
|
||||
event.action = mode;
|
||||
return event;
|
||||
}
|
||||
|
||||
public static ControlMessage createEmpty(int type) {
|
||||
ControlMessage event = new ControlMessage();
|
||||
event.type = type;
|
||||
|
|
|
@ -12,6 +12,7 @@ public class ControlMessageReader {
|
|||
private static final int INJECT_MOUSE_PAYLOAD_LENGTH = 13;
|
||||
private static final int INJECT_SCROLL_PAYLOAD_LENGTH = 16;
|
||||
private static final int INJECT_TOUCH_PAYLOAD_LENGTH = 10;
|
||||
private static final int SET_SCREEN_POWER_MODE_PAYLOAD_LENGTH = 1;
|
||||
|
||||
public static final int TEXT_MAX_LENGTH = 300;
|
||||
public static final int CLIPBOARD_TEXT_MAX_LENGTH = 4093;
|
||||
|
@ -50,43 +51,46 @@ public class ControlMessageReader {
|
|||
}
|
||||
int savedPosition = buffer.position();
|
||||
int type = buffer.get();
|
||||
ControlMessage controlEvent;
|
||||
ControlMessage msg;
|
||||
switch (type) {
|
||||
case ControlMessage.TYPE_INJECT_KEYCODE:
|
||||
controlEvent = parseInjectKeycode();
|
||||
msg = parseInjectKeycode();
|
||||
break;
|
||||
case ControlMessage.TYPE_INJECT_TEXT:
|
||||
controlEvent = parseInjectText();
|
||||
msg = parseInjectText();
|
||||
break;
|
||||
case ControlMessage.TYPE_INJECT_MOUSE:
|
||||
controlEvent = parseInjectMouse();
|
||||
msg = parseInjectMouse();
|
||||
break;
|
||||
case ControlMessage.TYPE_INJECT_TOUCH:
|
||||
controlEvent = parseInjectMouseTouch();
|
||||
msg = parseInjectMouseTouch();
|
||||
break;
|
||||
case ControlMessage.TYPE_INJECT_SCROLL:
|
||||
controlEvent = parseInjectScroll();
|
||||
msg = parseInjectScroll();
|
||||
break;
|
||||
case ControlMessage.TYPE_SET_CLIPBOARD:
|
||||
controlEvent = parseSetClipboard();
|
||||
msg = parseSetClipboard();
|
||||
break;
|
||||
case ControlMessage.TYPE_SET_SCREEN_POWER_MODE:
|
||||
msg = parseSetScreenPowerMode();
|
||||
break;
|
||||
case ControlMessage.TYPE_BACK_OR_SCREEN_ON:
|
||||
case ControlMessage.TYPE_EXPAND_NOTIFICATION_PANEL:
|
||||
case ControlMessage.TYPE_COLLAPSE_NOTIFICATION_PANEL:
|
||||
case ControlMessage.TYPE_GET_CLIPBOARD:
|
||||
controlEvent = ControlMessage.createEmpty(type);
|
||||
msg = ControlMessage.createEmpty(type);
|
||||
break;
|
||||
default:
|
||||
Ln.w("Unknown event type: " + type);
|
||||
controlEvent = null;
|
||||
msg = null;
|
||||
break;
|
||||
}
|
||||
|
||||
if (controlEvent == null) {
|
||||
if (msg == null) {
|
||||
// failure, reset savedPosition
|
||||
buffer.position(savedPosition);
|
||||
}
|
||||
return controlEvent;
|
||||
return msg;
|
||||
}
|
||||
|
||||
private ControlMessage parseInjectKeycode() {
|
||||
|
@ -157,6 +161,14 @@ public class ControlMessageReader {
|
|||
return ControlMessage.createSetClipboard(text);
|
||||
}
|
||||
|
||||
private ControlMessage parseSetScreenPowerMode() {
|
||||
if (buffer.remaining() < SET_SCREEN_POWER_MODE_PAYLOAD_LENGTH) {
|
||||
return null;
|
||||
}
|
||||
int mode = buffer.get();
|
||||
return ControlMessage.createSetScreenPowerMode(mode);
|
||||
}
|
||||
|
||||
private static Position readPosition(ByteBuffer buffer) {
|
||||
int x = toUnsigned(buffer.getShort());
|
||||
int y = toUnsigned(buffer.getShort());
|
||||
|
|
|
@ -102,9 +102,21 @@ public class Controller {
|
|||
return sender;
|
||||
}
|
||||
|
||||
@SuppressWarnings("checkstyle:MagicNumber")
|
||||
public void control() throws IOException {
|
||||
// on start, turn screen on
|
||||
turnScreenOn();
|
||||
// on start, power on the device
|
||||
if (!device.isScreenOn()) {
|
||||
injectKeycode(KeyEvent.KEYCODE_POWER);
|
||||
|
||||
// dirty hack
|
||||
// After POWER is injected, the device is powered on asynchronously.
|
||||
// To turn the device screen off while mirroring, the client will send a message that
|
||||
// would be handled before the device is actually powered on, so its effect would
|
||||
// be "canceled" once the device is turned back on.
|
||||
// Adding this delay prevents to handle the message before the device is actually
|
||||
// powered on.
|
||||
SystemClock.sleep(500);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
handleEvent();
|
||||
|
@ -145,6 +157,9 @@ public class Controller {
|
|||
case ControlMessage.TYPE_SET_CLIPBOARD:
|
||||
device.setClipboardText(msg.getText());
|
||||
break;
|
||||
case ControlMessage.TYPE_SET_SCREEN_POWER_MODE:
|
||||
device.setScreenPowerMode(msg.getAction());
|
||||
break;
|
||||
default:
|
||||
// do nothing
|
||||
}
|
||||
|
@ -309,10 +324,6 @@ public class Controller {
|
|||
return device.injectInputEvent(event, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
|
||||
}
|
||||
|
||||
private boolean turnScreenOn() {
|
||||
return device.isScreenOn() || injectKeycode(KeyEvent.KEYCODE_POWER);
|
||||
}
|
||||
|
||||
private boolean pressBackOrTurnScreenOn() {
|
||||
int keycode = device.isScreenOn() ? KeyEvent.KEYCODE_BACK : KeyEvent.KEYCODE_POWER;
|
||||
return injectKeycode(keycode);
|
||||
|
|
|
@ -1,16 +1,21 @@
|
|||
package com.genymobile.scrcpy;
|
||||
|
||||
import com.genymobile.scrcpy.wrappers.ServiceManager;
|
||||
import com.genymobile.scrcpy.wrappers.SurfaceControl;
|
||||
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.view.IRotationWatcher;
|
||||
import android.view.InputEvent;
|
||||
|
||||
public final class Device {
|
||||
|
||||
public static final int POWER_MODE_OFF = SurfaceControl.POWER_MODE_OFF;
|
||||
public static final int POWER_MODE_NORMAL = SurfaceControl.POWER_MODE_NORMAL;
|
||||
|
||||
public interface RotationListener {
|
||||
void onRotationChanged(int rotation);
|
||||
}
|
||||
|
@ -190,6 +195,16 @@ public final class Device {
|
|||
|
||||
public void setClipboardText(String text) {
|
||||
serviceManager.getClipboardManager().setText(text);
|
||||
Ln.i("Device clipboard set");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mode one of the {@code SCREEN_POWER_MODE_*} constants
|
||||
*/
|
||||
public void setScreenPowerMode(int mode) {
|
||||
IBinder d = SurfaceControl.getBuiltInDisplay(0);
|
||||
SurfaceControl.setDisplayPowerMode(d, mode);
|
||||
Ln.i("Device screen turned " + (mode == Device.POWER_MODE_OFF ? "off" : "on"));
|
||||
}
|
||||
|
||||
static Rect flipRect(Rect crop) {
|
||||
|
|
|
@ -8,7 +8,8 @@ import android.util.Log;
|
|||
*/
|
||||
public final class Ln {
|
||||
|
||||
private static final String TAG = "scrcpy";
|
||||
private static final String TAG = "qtscrcpy";
|
||||
private static final String PREFIX = "[server] ";
|
||||
|
||||
enum Level {
|
||||
DEBUG,
|
||||
|
@ -30,28 +31,28 @@ public final class Ln {
|
|||
public static void d(String message) {
|
||||
if (isEnabled(Level.DEBUG)) {
|
||||
Log.d(TAG, message);
|
||||
System.out.println("DEBUG: " + message);
|
||||
System.out.println(PREFIX + "DEBUG: " + message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void i(String message) {
|
||||
if (isEnabled(Level.INFO)) {
|
||||
Log.i(TAG, message);
|
||||
System.out.println("INFO: " + message);
|
||||
System.out.println(PREFIX + "INFO: " + message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void w(String message) {
|
||||
if (isEnabled(Level.WARN)) {
|
||||
Log.w(TAG, message);
|
||||
System.out.println("WARN: " + message);
|
||||
System.out.println(PREFIX + "WARN: " + message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void e(String message, Throwable throwable) {
|
||||
if (isEnabled(Level.ERROR)) {
|
||||
Log.e(TAG, message, throwable);
|
||||
System.out.println("ERROR: " + message);
|
||||
System.out.println(PREFIX + "ERROR: " + message);
|
||||
if (throwable != null) {
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ public class Options {
|
|||
private boolean tunnelForward;
|
||||
private Rect crop;
|
||||
private boolean sendFrameMeta;
|
||||
private boolean control;
|
||||
|
||||
public int getMaxSize() {
|
||||
return maxSize;
|
||||
|
@ -48,4 +49,12 @@ public class Options {
|
|||
public void setSendFrameMeta(boolean sendFrameMeta) {
|
||||
this.sendFrameMeta = sendFrameMeta;
|
||||
}
|
||||
|
||||
public boolean getControl() {
|
||||
return control;
|
||||
}
|
||||
|
||||
public void setControl(boolean control) {
|
||||
this.control = control;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,11 +20,13 @@ public final class Server {
|
|||
try (DesktopConnection connection = DesktopConnection.open(device, tunnelForward)) {
|
||||
ScreenEncoder screenEncoder = new ScreenEncoder(options.getSendFrameMeta(), options.getBitRate());
|
||||
|
||||
Controller controller = new Controller(device, connection);
|
||||
if (options.getControl()) {
|
||||
Controller controller = new Controller(device, connection);
|
||||
|
||||
// asynchronous
|
||||
startController(controller);
|
||||
startDeviceMessageSender(controller.getSender());
|
||||
// asynchronous
|
||||
startController(controller);
|
||||
startDeviceMessageSender(controller.getSender());
|
||||
}
|
||||
|
||||
try {
|
||||
// synchronous
|
||||
|
@ -66,7 +68,7 @@ public final class Server {
|
|||
|
||||
@SuppressWarnings("checkstyle:MagicNumber")
|
||||
private static Options createOptions(String... args) {
|
||||
if (args.length != 5) {
|
||||
if (args.length != 6) {
|
||||
throw new IllegalArgumentException("Expecting 5 parameters");
|
||||
}
|
||||
|
||||
|
@ -88,6 +90,9 @@ public final class Server {
|
|||
boolean sendFrameMeta = Boolean.parseBoolean(args[4]);
|
||||
options.setSendFrameMeta(sendFrameMeta);
|
||||
|
||||
boolean control = Boolean.parseBoolean(args[5]);
|
||||
options.setControl(control);
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.genymobile.scrcpy.wrappers;
|
|||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
import android.view.Surface;
|
||||
|
||||
|
@ -10,6 +11,10 @@ public final class SurfaceControl {
|
|||
|
||||
private static final Class<?> CLASS;
|
||||
|
||||
// see <https://android.googlesource.com/platform/frameworks/base.git/+/pie-release-2/core/java/android/view/SurfaceControl.java#305>
|
||||
public static final int POWER_MODE_OFF = 0;
|
||||
public static final int POWER_MODE_NORMAL = 2;
|
||||
|
||||
static {
|
||||
try {
|
||||
CLASS = Class.forName("android.view.SurfaceControl");
|
||||
|
@ -71,6 +76,27 @@ public final class SurfaceControl {
|
|||
}
|
||||
}
|
||||
|
||||
public static IBinder getBuiltInDisplay(int builtInDisplayId) {
|
||||
try {
|
||||
// the method signature has changed in Android Q
|
||||
// <https://github.com/Genymobile/scrcpy/issues/586>
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||
return (IBinder) CLASS.getMethod("getBuiltInDisplay", int.class).invoke(null, builtInDisplayId);
|
||||
}
|
||||
return (IBinder) CLASS.getMethod("getPhysicalDisplayToken", long.class).invoke(null, builtInDisplayId);
|
||||
} catch (Exception e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setDisplayPowerMode(IBinder displayToken, int mode) {
|
||||
try {
|
||||
CLASS.getMethod("setDisplayPowerMode", IBinder.class, int.class).invoke(null, displayToken, mode);
|
||||
} catch (Exception e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void destroyDisplay(IBinder displayToken) {
|
||||
try {
|
||||
CLASS.getMethod("destroyDisplay", IBinder.class).invoke(null, displayToken);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue