diff --git a/app/src/cli.c b/app/src/cli.c index f40fbca0..b64e6109 100644 --- a/app/src/cli.c +++ b/app/src/cli.c @@ -60,6 +60,9 @@ scrcpy_print_usage(const char *arg0) { " -n, --no-control\n" " Disable device control (mirror the device in read-only).\n" "\n" + " -d, --display\n" + " Specify the display id to mirror, default 0\n" + "\n" " -N, --no-display\n" " Do not display device (only when screen recording is\n" " enabled).\n" diff --git a/server/src/main/java/com/genymobile/scrcpy/Controller.java b/server/src/main/java/com/genymobile/scrcpy/Controller.java index ae3ac0f8..be035fce 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Controller.java +++ b/server/src/main/java/com/genymobile/scrcpy/Controller.java @@ -2,7 +2,6 @@ package com.genymobile.scrcpy; import com.genymobile.scrcpy.wrappers.InputManager; -import android.os.Build; import android.os.SystemClock; import android.view.InputDevice; import android.view.InputEvent; @@ -11,8 +10,6 @@ import android.view.KeyEvent; import android.view.MotionEvent; import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; public class Controller { @@ -222,10 +219,10 @@ public class Controller { } private boolean injectEvent(InputEvent event) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){ - InputManager.setDisplayId(event, device.getDisplayId()); - } - return device.injectInputEvent(event, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); + if (device.isSupportInputEvents()) + return device.injectInputEvent(event, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); + else + return false; } private boolean pressBackOrTurnScreenOn() { diff --git a/server/src/main/java/com/genymobile/scrcpy/Device.java b/server/src/main/java/com/genymobile/scrcpy/Device.java index d692b752..1664cb99 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Device.java +++ b/server/src/main/java/com/genymobile/scrcpy/Device.java @@ -1,5 +1,6 @@ package com.genymobile.scrcpy; +import com.genymobile.scrcpy.wrappers.InputManager; import com.genymobile.scrcpy.wrappers.ServiceManager; import com.genymobile.scrcpy.wrappers.SurfaceControl; import com.genymobile.scrcpy.wrappers.WindowManager; @@ -22,7 +23,7 @@ public final class Device { private final ServiceManager serviceManager = new ServiceManager(); - private final int displayId; + private final DisplayInfo displayInfo; private ScreenInfo screenInfo; private RotationListener rotationListener; @@ -42,6 +43,12 @@ public final class Device { } } }); + if (!displayInfo.isSupportProtectedBuffers()) { + Ln.w("Display doesn't have FLAG_SUPPORTS_PROTECTED_BUFFERS flag, mirroring can be restricted"); + } + if (!isSupportInputEvents()) { + Ln.w("Input events for display with flag FLAG_PRESENTATION, can be supported since API 29"); + } } public synchronized ScreenInfo getScreenInfo() { @@ -77,11 +84,12 @@ public final class Device { return Build.MODEL; } - public int getDisplayId() { - return displayId; - } - public boolean injectInputEvent(InputEvent inputEvent, int mode) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){ + InputManager.setDisplayId(inputEvent, displayInfo.getDisplayId()); + } + + return serviceManager.getInputManager().injectInputEvent(inputEvent, mode); } @@ -89,6 +97,13 @@ public final class Device { return serviceManager.getPowerManager().isScreenOn(); } + public boolean isSupportInputEvents(){ + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) + return true; + else + return !displayInfo.isPresentation(); + } + public void registerRotationWatcher(IRotationWatcher rotationWatcher) { serviceManager.getWindowManager().registerRotationWatcher(rotationWatcher); } diff --git a/server/src/main/java/com/genymobile/scrcpy/DisplayInfo.java b/server/src/main/java/com/genymobile/scrcpy/DisplayInfo.java index 54ed644f..56f9adec 100644 --- a/server/src/main/java/com/genymobile/scrcpy/DisplayInfo.java +++ b/server/src/main/java/com/genymobile/scrcpy/DisplayInfo.java @@ -1,14 +1,28 @@ package com.genymobile.scrcpy; public final class DisplayInfo { + private final int displayId; private final Size size; private final int rotation; private final int layerStack; + private final int flags; - public DisplayInfo(Size size, int rotation, int layerStack) { + public static final int DEFAULT_DISPLAY = 0x00000000; + + public static final int FLAG_PRESENTATION = 0x00000008; + + public static final int FLAG_SUPPORTS_PROTECTED_BUFFERS = 0x00000001; + + public DisplayInfo(int displayId, Size size, int rotation, int layerStack, int flags) { + this.displayId = displayId; this.size = size; this.rotation = rotation; this.layerStack = layerStack; + this.flags = flags; + } + + public int getDisplayId() { + return displayId; } public Size getSize() { @@ -22,5 +36,13 @@ public final class DisplayInfo { public int getLayerStack() { return layerStack; } + + public boolean isPresentation() { + return (flags & FLAG_PRESENTATION) == FLAG_PRESENTATION; + } + + public boolean isSupportProtectedBuffers() { + return (flags & FLAG_SUPPORTS_PROTECTED_BUFFERS) == FLAG_SUPPORTS_PROTECTED_BUFFERS; + } } diff --git a/server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayManager.java b/server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayManager.java index 3e82fcc5..b890aaa0 100644 --- a/server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayManager.java +++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayManager.java @@ -1,10 +1,11 @@ package com.genymobile.scrcpy.wrappers; -import com.genymobile.scrcpy.DisplayInfo; -import com.genymobile.scrcpy.Size; - import android.os.IInterface; +import com.genymobile.scrcpy.DisplayInfo; +import com.genymobile.scrcpy.Ln; +import com.genymobile.scrcpy.Size; + public final class DisplayManager { private final IInterface manager; @@ -21,9 +22,33 @@ public final class DisplayManager { int height = cls.getDeclaredField("logicalHeight").getInt(displayInfo); int rotation = cls.getDeclaredField("rotation").getInt(displayInfo); int layerStack = cls.getDeclaredField("layerStack").getInt(displayInfo); - return new DisplayInfo(new Size(width, height), rotation, layerStack); + int flags = cls.getDeclaredField("flags").getInt(displayInfo); + return new DisplayInfo(displayId, new Size(width, height), rotation, layerStack, flags); + } catch (Exception e) { + if (e instanceof NullPointerException) suggestFix(displayId, getDisplayIds()); + throw new AssertionError(e); + } + } + + public int[] getDisplayIds() { + try { + return (int[]) manager.getClass().getMethod("getDisplayIds").invoke(manager); } catch (Exception e) { throw new AssertionError(e); } } + + private void suggestFix(int displayId, int[] displayIds) { + if (displayIds == null || displayIds.length == 0) + return; + + StringBuilder sb = new StringBuilder(); + sb.append("Failed to get displayId=[") + .append(displayId) + .append("]\nTry to user one of these available display ids:\n"); + for (int id : displayIds) { + sb.append("scrcpy --display ").append(id).append("\n"); + } + Ln.e(sb.toString()); + } }