diff --git a/app/src/cli.c b/app/src/cli.c index d9e1013a..02bc831b 100644 --- a/app/src/cli.c +++ b/app/src/cli.c @@ -41,6 +41,10 @@ scrcpy_print_usage(const char *arg0) { " -h, --help\n" " Print this help.\n" "\n" + " --layer-stack value\n" + " Specifies the Android layer stack to mirror\n" + " Default is 0\n" + "\n" " --max-fps value\n" " Limit the frame rate of screen capture (only supported on\n" " devices with Android >= 10).\n" @@ -259,6 +263,18 @@ parse_max_fps(const char *s, uint16_t *max_fps) { return true; } +static bool +parse_layer_stack(const char *s, uint16_t *layer_stack) { + long value; + bool ok = parse_integer_arg(s, &value, false, 0, 1000, "layer stack"); + if (!ok) { + return false; + } + + *layer_stack = (uint16_t) value; + return true; +} + static bool parse_window_position(const char *s, int16_t *position) { long value; @@ -340,6 +356,7 @@ guess_record_format(const char *filename) { #define OPT_WINDOW_HEIGHT 1010 #define OPT_WINDOW_BORDERLESS 1011 #define OPT_MAX_FPS 1012 +#define OPT_LAYER_STACK 1013 bool scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) { @@ -350,6 +367,7 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) { {"fullscreen", no_argument, NULL, 'f'}, {"help", no_argument, NULL, 'h'}, {"max-fps", required_argument, NULL, OPT_MAX_FPS}, + {"layer-stack", required_argument, NULL, OPT_LAYER_STACK}, {"max-size", required_argument, NULL, 'm'}, {"no-control", no_argument, NULL, 'n'}, {"no-display", no_argument, NULL, 'N'}, @@ -412,6 +430,11 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) { return false; } break; + case OPT_LAYER_STACK: + if (!parse_layer_stack(optarg, &opts->layer_stack)) { + return false; + } + break; case 'm': if (!parse_max_size(optarg, &opts->max_size)) { return false; diff --git a/app/src/scrcpy.c b/app/src/scrcpy.c index 17be1ed4..707de0ef 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, + .layer_stack = options->layer_stack, }; if (!server_start(&server, options->serial, ¶ms)) { return false; diff --git a/app/src/scrcpy.h b/app/src/scrcpy.h index 75de8717..0710fb0b 100644 --- a/app/src/scrcpy.h +++ b/app/src/scrcpy.h @@ -32,6 +32,7 @@ struct scrcpy_options { bool render_expired_frames; bool prefer_text; bool window_borderless; + uint16_t layer_stack; }; #define SCRCPY_OPTIONS_DEFAULT { \ @@ -58,6 +59,7 @@ struct scrcpy_options { .render_expired_frames = false, \ .prefer_text = false, \ .window_borderless = false, \ + .layer_stack = 0, \ } bool diff --git a/app/src/server.c b/app/src/server.c index ff167aeb..00a9d7de 100644 --- a/app/src/server.c +++ b/app/src/server.c @@ -124,9 +124,12 @@ 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 layer_stack_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(layer_stack_string, "%"PRIu16, params->layer_stack); const char *const cmd[] = { "shell", "CLASSPATH=" DEVICE_SERVER_PATH, @@ -146,6 +149,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", + layer_stack_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..1e467d0f 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 layer_stack; }; // init default values diff --git a/server/src/main/java/com/genymobile/scrcpy/Options.java b/server/src/main/java/com/genymobile/scrcpy/Options.java index 5b993f30..e7a2ee0f 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 layerStack; public int getMaxSize() { return maxSize; @@ -66,4 +67,12 @@ public class Options { public void setControl(boolean control) { this.control = control; } + + public int getLayerStack() { + return layerStack; + } + + public void setLayerStack(int layerStack) { + this.layerStack = layerStack; + } } diff --git a/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java b/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java index c9a37f84..8565cc4b 100644 --- a/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java +++ b/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java @@ -51,7 +51,7 @@ public class ScreenEncoder implements Device.RotationListener { return rotationChanged.getAndSet(false); } - public void streamScreen(Device device, FileDescriptor fd) throws IOException { + public void streamScreen(Device device, FileDescriptor fd, int layerStack) throws IOException { Workarounds.prepareMainLooper(); Workarounds.fillAppInfo(); @@ -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, layerStack); 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/Server.java b/server/src/main/java/com/genymobile/scrcpy/Server.java index 56b738fb..ea291de9 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Server.java +++ b/server/src/main/java/com/genymobile/scrcpy/Server.java @@ -31,7 +31,7 @@ public final class Server { try { // synchronous - screenEncoder.streamScreen(device, connection.getVideoFd()); + screenEncoder.streamScreen(device, connection.getVideoFd(), options.getLayerStack()); } catch (IOException e) { // this is expected on close Ln.d("Screen streaming stopped"); @@ -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 layerStack = Integer.parseInt(args[8]); + options.setLayerStack(layerStack); + return options; }