diff --git a/app/src/cli.c b/app/src/cli.c index d9e1013a..646a4d60 100644 --- a/app/src/cli.c +++ b/app/src/cli.c @@ -297,6 +297,18 @@ parse_port(const char *s, uint16_t *port) { return true; } +static bool +parse_display_id(const char *s, uint16_t *display_id) { + long value; + bool ok = parse_integer_arg(s, &value, false, 0, 0xFFFF, "display id"); + if (!ok) { + return false; + } + + *display_id = (uint16_t) value; + return true; +} + static bool parse_record_format(const char *optarg, enum recorder_format *format) { if (!strcmp(optarg, "mp4")) { @@ -369,8 +381,8 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) { {"window-y", required_argument, NULL, OPT_WINDOW_Y}, {"window-width", required_argument, NULL, OPT_WINDOW_WIDTH}, {"window-height", required_argument, NULL, OPT_WINDOW_HEIGHT}, - {"window-borderless", no_argument, NULL, - OPT_WINDOW_BORDERLESS}, + {"window-borderless", no_argument, NULL, OPT_WINDOW_BORDERLESS}, + {"display", required_argument, NULL, 'd'}, {NULL, 0, NULL, 0 }, }; @@ -379,7 +391,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:nNp:r:s:StTv", long_options, + while ((c = getopt_long(argc, argv, "b:c:fF:hm:nNpd:r:s:StTv", long_options, NULL)) != -1) { switch (c) { case 'b': @@ -428,6 +440,11 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) { return false; } break; + case 'd': + if (!parse_display_id(optarg, &opts->display_id)) { + return false; + } + break; case 'r': opts->record_filename = optarg; break; diff --git a/app/src/scrcpy.c b/app/src/scrcpy.c index 17be1ed4..41a253f1 100644 --- a/app/src/scrcpy.c +++ b/app/src/scrcpy.c @@ -285,6 +285,7 @@ scrcpy(const struct scrcpy_options *options) { .bit_rate = options->bit_rate, .max_fps = options->max_fps, .control = options->control, + .display_id = options->display_id, }; if (!server_start(&server, options->serial, ¶ms)) { return false; diff --git a/app/src/scrcpy.h b/app/src/scrcpy.h index 75de8717..46a1ca45 100644 --- a/app/src/scrcpy.h +++ b/app/src/scrcpy.h @@ -23,6 +23,7 @@ struct scrcpy_options { int16_t window_y; uint16_t window_width; uint16_t window_height; + uint16_t display_id; bool show_touches; bool fullscreen; bool always_on_top; @@ -49,6 +50,7 @@ struct scrcpy_options { .window_y = -1, \ .window_width = 0, \ .window_height = 0, \ + .display_id = 0, \ .show_touches = false, \ .fullscreen = false, \ .always_on_top = false, \ diff --git a/app/src/server.c b/app/src/server.c index ff167aeb..a189a8e4 100644 --- a/app/src/server.c +++ b/app/src/server.c @@ -124,9 +124,11 @@ execute_server(struct server *server, const struct server_params *params) { char max_size_string[6]; char bit_rate_string[11]; char max_fps_string[6]; + char display_id_string[6]; sprintf(max_size_string, "%"PRIu16, params->max_size); sprintf(bit_rate_string, "%"PRIu32, params->bit_rate); sprintf(max_fps_string, "%"PRIu16, params->max_fps); + sprintf(display_id_string, "%"PRIu16, params->display_id); const char *const cmd[] = { "shell", "CLASSPATH=" DEVICE_SERVER_PATH, @@ -146,6 +148,7 @@ execute_server(struct server *server, const struct server_params *params) { params->crop ? params->crop : "-", "true", // always send frame meta (packet boundaries + timestamp) params->control ? "true" : "false", + display_id_string, }; #ifdef SERVER_DEBUGGER LOGI("Server debugger waiting for a client on device port " diff --git a/app/src/server.h b/app/src/server.h index 0cb1ab3a..68a8f3ea 100644 --- a/app/src/server.h +++ b/app/src/server.h @@ -37,6 +37,7 @@ struct server_params { uint32_t bit_rate; uint16_t max_fps; bool control; + uint16_t display_id; }; // init default values diff --git a/server/src/main/java/com/genymobile/scrcpy/Device.java b/server/src/main/java/com/genymobile/scrcpy/Device.java index 9448098a..dd6d6dee 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Device.java +++ b/server/src/main/java/com/genymobile/scrcpy/Device.java @@ -26,7 +26,7 @@ public final class Device { private RotationListener rotationListener; public Device(Options options) { - screenInfo = computeScreenInfo(options.getCrop(), options.getMaxSize()); + screenInfo = computeScreenInfo(options.getCrop(), options.getMaxSize(), options.getDisplayId()); registerRotationWatcher(new IRotationWatcher.Stub() { @Override public void onRotationChanged(int rotation) throws RemoteException { @@ -46,8 +46,8 @@ public final class Device { return screenInfo; } - private ScreenInfo computeScreenInfo(Rect crop, int maxSize) { - DisplayInfo displayInfo = serviceManager.getDisplayManager().getDisplayInfo(); + private ScreenInfo computeScreenInfo(Rect crop, int maxSize, int displayId) { + DisplayInfo displayInfo = serviceManager.getDisplayManager().getDisplayInfo(displayId); boolean rotated = (displayInfo.getRotation() & 1) != 0; Size deviceSize = displayInfo.getSize(); Rect contentRect = new Rect(0, 0, deviceSize.getWidth(), deviceSize.getHeight()); @@ -64,7 +64,7 @@ public final class Device { } Size videoSize = computeVideoSize(contentRect.width(), contentRect.height(), maxSize); - return new ScreenInfo(contentRect, videoSize, rotated); + return new ScreenInfo(contentRect, videoSize, rotated, displayInfo.getLayerStack()); } private static String formatCrop(Rect rect) { diff --git a/server/src/main/java/com/genymobile/scrcpy/DisplayInfo.java b/server/src/main/java/com/genymobile/scrcpy/DisplayInfo.java index 639869b5..54ed644f 100644 --- a/server/src/main/java/com/genymobile/scrcpy/DisplayInfo.java +++ b/server/src/main/java/com/genymobile/scrcpy/DisplayInfo.java @@ -3,10 +3,12 @@ package com.genymobile.scrcpy; public final class DisplayInfo { private final Size size; private final int rotation; + private final int layerStack; - public DisplayInfo(Size size, int rotation) { + public DisplayInfo(Size size, int rotation, int layerStack) { this.size = size; this.rotation = rotation; + this.layerStack = layerStack; } public Size getSize() { @@ -16,5 +18,9 @@ public final class DisplayInfo { public int getRotation() { return rotation; } + + public int getLayerStack() { + return layerStack; + } } diff --git a/server/src/main/java/com/genymobile/scrcpy/Options.java b/server/src/main/java/com/genymobile/scrcpy/Options.java index 5b993f30..34ef052f 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Options.java +++ b/server/src/main/java/com/genymobile/scrcpy/Options.java @@ -10,6 +10,7 @@ public class Options { private Rect crop; private boolean sendFrameMeta; // send PTS so that the client may record properly private boolean control; + private int displayId; public int getMaxSize() { return maxSize; @@ -66,4 +67,12 @@ public class Options { public void setControl(boolean control) { this.control = control; } + + public int getDisplayId() { + return displayId; + } + + public void setDisplayId(int displayId) { + this.displayId = displayId; + } } diff --git a/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java b/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java index c9a37f84..1af50b35 100644 --- a/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java +++ b/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java @@ -67,7 +67,7 @@ public class ScreenEncoder implements Device.RotationListener { setSize(format, videoRect.width(), videoRect.height()); configure(codec, format); Surface surface = codec.createInputSurface(); - setDisplaySurface(display, surface, contentRect, videoRect); + setDisplaySurface(display, surface, contentRect, videoRect, device.getScreenInfo().getLayerStack()); codec.start(); try { alive = encode(codec, fd); @@ -172,12 +172,12 @@ public class ScreenEncoder implements Device.RotationListener { format.setInteger(MediaFormat.KEY_HEIGHT, height); } - private static void setDisplaySurface(IBinder display, Surface surface, Rect deviceRect, Rect displayRect) { + private static void setDisplaySurface(IBinder display, Surface surface, Rect deviceRect, Rect displayRect, int layerStack) { SurfaceControl.openTransaction(); try { SurfaceControl.setDisplaySurface(display, surface); SurfaceControl.setDisplayProjection(display, 0, deviceRect, displayRect); - SurfaceControl.setDisplayLayerStack(display, 0); + SurfaceControl.setDisplayLayerStack(display, layerStack); } finally { SurfaceControl.closeTransaction(); } diff --git a/server/src/main/java/com/genymobile/scrcpy/ScreenInfo.java b/server/src/main/java/com/genymobile/scrcpy/ScreenInfo.java index f2fce1d6..056dec5c 100644 --- a/server/src/main/java/com/genymobile/scrcpy/ScreenInfo.java +++ b/server/src/main/java/com/genymobile/scrcpy/ScreenInfo.java @@ -6,11 +6,13 @@ public final class ScreenInfo { private final Rect contentRect; // device size, possibly cropped private final Size videoSize; private final boolean rotated; + private final int layerStack; - public ScreenInfo(Rect contentRect, Size videoSize, boolean rotated) { + public ScreenInfo(Rect contentRect, Size videoSize, boolean rotated, int layerStack) { this.contentRect = contentRect; this.videoSize = videoSize; this.rotated = rotated; + this.layerStack = layerStack; } public Rect getContentRect() { @@ -26,6 +28,10 @@ public final class ScreenInfo { if (rotated == newRotated) { return this; } - return new ScreenInfo(Device.flipRect(contentRect), videoSize.rotate(), newRotated); + return new ScreenInfo(Device.flipRect(contentRect), videoSize.rotate(), newRotated, layerStack); + } + + public int getLayerStack(){ + return layerStack; } } diff --git a/server/src/main/java/com/genymobile/scrcpy/Server.java b/server/src/main/java/com/genymobile/scrcpy/Server.java index 56b738fb..08690592 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Server.java +++ b/server/src/main/java/com/genymobile/scrcpy/Server.java @@ -79,8 +79,8 @@ public final class Server { "The server version (" + clientVersion + ") does not match the client " + "(" + BuildConfig.VERSION_NAME + ")"); } - if (args.length != 8) { - throw new IllegalArgumentException("Expecting 8 parameters"); + if (args.length != 9) { + throw new IllegalArgumentException("Expecting 9 parameters"); } Options options = new Options(); @@ -107,6 +107,9 @@ public final class Server { boolean control = Boolean.parseBoolean(args[7]); options.setControl(control); + int displayId = Integer.parseInt(args[8]); + 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 568afacd..3e82fcc5 100644 --- a/server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayManager.java +++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayManager.java @@ -12,15 +12,16 @@ public final class DisplayManager { this.manager = manager; } - public DisplayInfo getDisplayInfo() { + public DisplayInfo getDisplayInfo(int displayId) { try { - Object displayInfo = manager.getClass().getMethod("getDisplayInfo", int.class).invoke(manager, 0); + Object displayInfo = manager.getClass().getMethod("getDisplayInfo", int.class).invoke(manager, displayId); Class cls = displayInfo.getClass(); // width and height already take the rotation into account int width = cls.getDeclaredField("logicalWidth").getInt(displayInfo); int height = cls.getDeclaredField("logicalHeight").getInt(displayInfo); int rotation = cls.getDeclaredField("rotation").getInt(displayInfo); - return new DisplayInfo(new Size(width, height), rotation); + int layerStack = cls.getDeclaredField("layerStack").getInt(displayInfo); + return new DisplayInfo(new Size(width, height), rotation, layerStack); } catch (Exception e) { throw new AssertionError(e); }