diff --git a/app/src/cli.c b/app/src/cli.c index b64e6109..1fccbfd7 100644 --- a/app/src/cli.c +++ b/app/src/cli.c @@ -60,7 +60,7 @@ scrcpy_print_usage(const char *arg0) { " -n, --no-control\n" " Disable device control (mirror the device in read-only).\n" "\n" - " -d, --display\n" + " --display\n" " Specify the display id to mirror, default 0\n" "\n" " -N, --no-display\n" @@ -413,6 +413,7 @@ guess_record_format(const char *filename) { #define OPT_WINDOW_BORDERLESS 1011 #define OPT_MAX_FPS 1012 #define OPT_LOCK_VIDEO_ORIENTATION 1013 +#define OPT_DISPLAY_ID 1014 bool scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) { @@ -446,6 +447,7 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) { {"window-height", required_argument, NULL, OPT_WINDOW_HEIGHT}, {"window-borderless", no_argument, NULL, OPT_WINDOW_BORDERLESS}, + {"display", required_argument, NULL, OPT_DISPLAY_ID}, {NULL, 0, NULL, 0 }, }; @@ -454,7 +456,7 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) { optind = 0; // reset to start from the first argument in tests int c; - while ((c = getopt_long(argc, argv, "b:c:fF:hm:nNpd:r:s:StTv", long_options, + while ((c = getopt_long(argc, argv, "b:c:fF:hm:nNp:r:s:StTv", long_options, NULL)) != -1) { switch (c) { case 'b': @@ -508,7 +510,7 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) { return false; } break; - case 'd': + case OPT_DISPLAY_ID: if (!parse_display_id(optarg, &opts->display_id)) { return false; } diff --git a/server/src/main/java/com/genymobile/scrcpy/Device.java b/server/src/main/java/com/genymobile/scrcpy/Device.java index 1664cb99..6e5d56e2 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Device.java +++ b/server/src/main/java/com/genymobile/scrcpy/Device.java @@ -23,13 +23,26 @@ public final class Device { private final ServiceManager serviceManager = new ServiceManager(); - private final DisplayInfo displayInfo; private ScreenInfo screenInfo; private RotationListener rotationListener; + /** + * The surface flinger layer stack associated with this logical display. + */ + private final int layerStack; + + /** + * Logical display identifier. + */ + private final int displayId; + private final boolean isPresentationDisplay; + public Device(Options options) { - DisplayInfo displayInfo = serviceManager.getDisplayManager().getDisplayInfo(); + DisplayInfo displayInfo = serviceManager.getDisplayManager().getDisplayInfo(options.getDisplayId()); screenInfo = ScreenInfo.computeScreenInfo(displayInfo, options.getCrop(), options.getMaxSize(), options.getLockedVideoOrientation()); + layerStack = displayInfo.getLayerStack(); + displayId = displayInfo.getDisplayId(); + isPresentationDisplay = displayInfo.isPresentation(); registerRotationWatcher(new IRotationWatcher.Stub() { @Override public void onRotationChanged(int rotation) throws RemoteException { @@ -47,7 +60,7 @@ public final class Device { 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"); + Ln.w("Sorry, can't support input events for displays with FLAG_PRESENTATION flag for devices with API lower then 29"); } } @@ -55,6 +68,10 @@ public final class Device { return screenInfo; } + public int getLayerStack() { + return layerStack; + } + public Point getPhysicalPoint(Position position) { // it hides the field on purpose, to read it with a lock @SuppressWarnings("checkstyle:HiddenField") @@ -86,7 +103,8 @@ public final class Device { public boolean injectInputEvent(InputEvent inputEvent, int mode) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){ - InputManager.setDisplayId(inputEvent, displayInfo.getDisplayId()); + if (!InputManager.setDisplayId(inputEvent, displayId)) + return false; } @@ -101,7 +119,7 @@ public final class Device { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) return true; else - return !displayInfo.isPresentation(); + return !isPresentationDisplay; } public void registerRotationWatcher(IRotationWatcher rotationWatcher) { diff --git a/server/src/main/java/com/genymobile/scrcpy/DisplayInfo.java b/server/src/main/java/com/genymobile/scrcpy/DisplayInfo.java index 56f9adec..61b5907d 100644 --- a/server/src/main/java/com/genymobile/scrcpy/DisplayInfo.java +++ b/server/src/main/java/com/genymobile/scrcpy/DisplayInfo.java @@ -7,12 +7,10 @@ public final class DisplayInfo { private final int layerStack; private final int flags; - public static final int DEFAULT_DISPLAY = 0x00000000; + public static final int FLAG_SUPPORTS_PROTECTED_BUFFERS = 0x00000001; 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; @@ -38,11 +36,11 @@ public final class DisplayInfo { } public boolean isPresentation() { - return (flags & FLAG_PRESENTATION) == FLAG_PRESENTATION; + return (flags & FLAG_PRESENTATION) != 0; } public boolean isSupportProtectedBuffers() { - return (flags & FLAG_SUPPORTS_PROTECTED_BUFFERS) == FLAG_SUPPORTS_PROTECTED_BUFFERS; + return (flags & FLAG_SUPPORTS_PROTECTED_BUFFERS) != 0; } } diff --git a/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java b/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java index adcb73ee..af1233db 100644 --- a/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java +++ b/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java @@ -74,7 +74,7 @@ public class ScreenEncoder implements Device.RotationListener { setSize(format, videoRect.width(), videoRect.height()); configure(codec, format); Surface surface = codec.createInputSurface(); - setDisplaySurface(display, surface, videoRotation, contentRect, unlockedVideoRect); + setDisplaySurface(display, surface, videoRotation, contentRect, unlockedVideoRect, device.getLayerStack()); codec.start(); try { alive = encode(codec, fd); diff --git a/server/src/main/java/com/genymobile/scrcpy/ScreenInfo.java b/server/src/main/java/com/genymobile/scrcpy/ScreenInfo.java index a902dad0..0204de82 100644 --- a/server/src/main/java/com/genymobile/scrcpy/ScreenInfo.java +++ b/server/src/main/java/com/genymobile/scrcpy/ScreenInfo.java @@ -14,7 +14,6 @@ public final class ScreenInfo { * However, it does not include the locked video orientation. */ private final Size unlockedVideoSize; - private final int layerStack; /** * Device rotation, related to the natural device orientation (0, 1, 2 or 3) @@ -31,7 +30,6 @@ public final class ScreenInfo { this.unlockedVideoSize = unlockedVideoSize; this.deviceRotation = deviceRotation; this.lockedVideoOrientation = lockedVideoOrientation; - this.layerStack = layerStack; } public Rect getContentRect() { diff --git a/server/src/main/java/com/genymobile/scrcpy/Server.java b/server/src/main/java/com/genymobile/scrcpy/Server.java index c4796d3c..e27978b8 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Server.java +++ b/server/src/main/java/com/genymobile/scrcpy/Server.java @@ -80,8 +80,8 @@ public final class Server { "The server version (" + BuildConfig.VERSION_NAME + ") does not match the client " + "(" + clientVersion + ")"); } - if (args.length != 9) { - throw new IllegalArgumentException("Expecting 9 parameters"); + if (args.length != 10) { + throw new IllegalArgumentException("Expecting 10 parameters"); } Options options = new Options(); @@ -111,7 +111,7 @@ public final class Server { boolean control = Boolean.parseBoolean(args[8]); options.setControl(control); - int displayId = Integer.parseInt(args[8]); + int displayId = Integer.parseInt(args[9]); options.setDisplayId(displayId); return options; 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 b890aaa0..d82c14a1 100644 --- a/server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayManager.java +++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayManager.java @@ -3,7 +3,6 @@ package com.genymobile.scrcpy.wrappers; import android.os.IInterface; import com.genymobile.scrcpy.DisplayInfo; -import com.genymobile.scrcpy.Ln; import com.genymobile.scrcpy.Size; public final class DisplayManager { @@ -16,6 +15,7 @@ public final class DisplayManager { public DisplayInfo getDisplayInfo(int displayId) { try { Object displayInfo = manager.getClass().getMethod("getDisplayInfo", int.class).invoke(manager, displayId); + if (displayInfo == null) throw new IllegalArgumentException(suggestFixMessage(displayId, getDisplayIds())); Class cls = displayInfo.getClass(); // width and height already take the rotation into account int width = cls.getDeclaredField("logicalWidth").getInt(displayInfo); @@ -25,7 +25,6 @@ public final class DisplayManager { 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); } } @@ -38,17 +37,17 @@ public final class DisplayManager { } } - private void suggestFix(int displayId, int[] displayIds) { + private String suggestFixMessage(int displayId, int[] displayIds) { if (displayIds == null || displayIds.length == 0) - return; + return null; StringBuilder sb = new StringBuilder(); sb.append("Failed to get displayId=[") .append(displayId) - .append("]\nTry to user one of these available display ids:\n"); + .append("]\nTry to use one of these available display ids:\n"); for (int id : displayIds) { sb.append("scrcpy --display ").append(id).append("\n"); } - Ln.e(sb.toString()); + return sb.toString(); } } diff --git a/server/src/main/java/com/genymobile/scrcpy/wrappers/InputManager.java b/server/src/main/java/com/genymobile/scrcpy/wrappers/InputManager.java index 8cb35003..5d89746d 100644 --- a/server/src/main/java/com/genymobile/scrcpy/wrappers/InputManager.java +++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/InputManager.java @@ -38,11 +38,13 @@ public final class InputManager { } } - public static void setDisplayId(InputEvent inputEvent, int displayId) { + public static boolean setDisplayId(InputEvent inputEvent, int displayId) { try { inputEvent.getClass().getMethod("setDisplayId", int.class).invoke(inputEvent, displayId); + return true; } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) { Ln.e("Could not invoke method", e); + return false; } } }