diff --git a/README.md b/README.md index 5575fc4d..a2e275f9 100644 --- a/README.md +++ b/README.md @@ -718,7 +718,7 @@ scrcpy --display=1 The list of display ids can be retrieved by: ```bash -adb shell dumpsys display # search "mDisplayId=" in the output +scrcpy --list-displays ``` The secondary display may only be controlled if the device runs at least Android diff --git a/app/data/bash-completion/scrcpy b/app/data/bash-completion/scrcpy index 70695019..fa95ce6e 100644 --- a/app/data/bash-completion/scrcpy +++ b/app/data/bash-completion/scrcpy @@ -19,6 +19,7 @@ _scrcpy() { -K --hid-keyboard -h --help --legacy-paste + --list-displays --list-encoders --lock-video-orientation --lock-video-orientation= diff --git a/app/data/zsh-completion/_scrcpy b/app/data/zsh-completion/_scrcpy index 268aa626..231405ce 100644 --- a/app/data/zsh-completion/_scrcpy +++ b/app/data/zsh-completion/_scrcpy @@ -26,6 +26,7 @@ arguments=( {-K,--hid-keyboard}'[Simulate a physical keyboard by using HID over AOAv2]' {-h,--help}'[Print the help]' '--legacy-paste[Inject computer clipboard text as a sequence of key events on Ctrl+v]' + '--list-displays[List displays available on the device]' '--list-encoders[List video and audio encoders available on the device]' '--lock-video-orientation=[Lock video orientation]:orientation:(unlocked initial 0 1 2 3)' '--max-fps=[Limit the frame rate of screen capture]' diff --git a/app/scrcpy.1 b/app/scrcpy.1 index add263c2..40b8158c 100644 --- a/app/scrcpy.1 +++ b/app/scrcpy.1 @@ -73,10 +73,9 @@ Disable screensaver while scrcpy is running. .TP .BI "\-\-display " id -Specify the display id to mirror. +Specify the device display id to mirror. -The list of possible display ids can be listed by "adb shell dumpsys display" -(search "mDisplayId=" in the output). +The available display ids can be listed by \-\-list\-displays. Default is 0. @@ -134,6 +133,10 @@ This is a workaround for some devices not behaving as expected when setting the .B \-\-list\-encoders List video and audio encoders available on the device. +.TP +.B \-\-list\-displays +List displays available on the device. + .TP \fB\-\-lock\-video\-orientation\fR[=\fIvalue\fR] Lock video orientation to \fIvalue\fR. Possible values are "unlocked", "initial" (locked to the initial orientation), 0, 1, 2 and 3. Natural device orientation is 0, and each increment adds a 90 degrees rotation counterclockwise. diff --git a/app/src/cli.c b/app/src/cli.c index edb694a6..8dfcdc79 100644 --- a/app/src/cli.c +++ b/app/src/cli.c @@ -69,6 +69,7 @@ enum { OPT_AUDIO_CODEC_OPTIONS, OPT_AUDIO_ENCODER, OPT_LIST_ENCODERS, + OPT_LIST_DISPLAYS, }; struct sc_option { @@ -198,10 +199,9 @@ static const struct sc_option options[] = { .longopt_id = OPT_DISPLAY_ID, .longopt = "display", .argdesc = "id", - .text = "Specify the display id to mirror.\n" - "The list of possible display ids can be listed by:\n" - " adb shell dumpsys display\n" - "(search \"mDisplayId=\" in the output)\n" + .text = "Specify the device display id to mirror.\n" + "The available display ids can be listed by:\n" + " scrcpy --list-displays\n" "Default is 0.", }, { @@ -272,6 +272,11 @@ static const struct sc_option options[] = { "This is a workaround for some devices not behaving as " "expected when setting the device clipboard programmatically.", }, + { + .longopt_id = OPT_LIST_DISPLAYS, + .longopt = "list-displays", + .text = "List device displays.", + }, { .longopt_id = OPT_LIST_ENCODERS, .longopt = "list-encoders", @@ -1803,6 +1808,9 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[], case OPT_LIST_ENCODERS: opts->list_encoders = true; break; + case OPT_LIST_DISPLAYS: + opts->list_displays = true; + break; default: // getopt prints the error message on stderr return false; diff --git a/app/src/options.c b/app/src/options.c index 1839df6e..8560b37b 100644 --- a/app/src/options.c +++ b/app/src/options.c @@ -73,4 +73,5 @@ const struct scrcpy_options scrcpy_options_default = { .power_on = true, .audio = true, .list_encoders = false, + .list_displays = false, }; diff --git a/app/src/options.h b/app/src/options.h index 568b8155..a15d51f8 100644 --- a/app/src/options.h +++ b/app/src/options.h @@ -155,6 +155,7 @@ struct scrcpy_options { bool power_on; bool audio; bool list_encoders; + bool list_displays; }; extern const struct scrcpy_options scrcpy_options_default; diff --git a/app/src/scrcpy.c b/app/src/scrcpy.c index 5739c3b2..4d68fb29 100644 --- a/app/src/scrcpy.c +++ b/app/src/scrcpy.c @@ -353,6 +353,7 @@ scrcpy(struct scrcpy_options *options) { .cleanup = options->cleanup, .power_on = options->power_on, .list_encoders = options->list_encoders, + .list_displays = options->list_displays, }; static const struct sc_server_callbacks cbs = { @@ -370,7 +371,7 @@ scrcpy(struct scrcpy_options *options) { server_started = true; - if (options->list_encoders) { + if (options->list_encoders || options->list_displays) { bool ok = await_for_server(NULL); ret = ok ? SCRCPY_EXIT_SUCCESS : SCRCPY_EXIT_FAILURE; goto end; diff --git a/app/src/server.c b/app/src/server.c index 077614a8..9d4fb098 100644 --- a/app/src/server.c +++ b/app/src/server.c @@ -303,6 +303,9 @@ execute_server(struct sc_server *server, if (params->list_encoders) { ADD_PARAM("list_encoders=true"); } + if (params->list_displays) { + ADD_PARAM("list_displays=true"); + } #undef ADD_PARAM @@ -856,9 +859,9 @@ run_server(void *data) { goto error_connection_failed; } - // If --list-encoders is passed, then the server just prints the encoders + // If --list-* is passed, then the server just prints the requested data // then exits. - if (params->list_encoders) { + if (params->list_encoders || params->list_displays) { sc_pid pid = execute_server(server, params); if (pid == SC_PROCESS_NONE) { goto error_connection_failed; diff --git a/app/src/server.h b/app/src/server.h index ada04baa..8edf2666 100644 --- a/app/src/server.h +++ b/app/src/server.h @@ -56,6 +56,7 @@ struct sc_server_params { bool cleanup; bool power_on; bool list_encoders; + bool list_displays; }; struct sc_server { diff --git a/server/src/main/java/com/genymobile/scrcpy/Device.java b/server/src/main/java/com/genymobile/scrcpy/Device.java index c7f7c1f8..b66474b7 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Device.java +++ b/server/src/main/java/com/genymobile/scrcpy/Device.java @@ -65,7 +65,7 @@ public final class Device { displayId = options.getDisplayId(); DisplayInfo displayInfo = ServiceManager.getDisplayManager().getDisplayInfo(displayId); if (displayInfo == null) { - Ln.e(buildUnknownDisplayIdMessage(displayId)); + Ln.e("Display " + displayId + " not found\n" + LogUtils.buildDisplayListMessage()); throw new ConfigurationException("Unknown display id: " + displayId); } @@ -130,18 +130,6 @@ public final class Device { } } - private static String buildUnknownDisplayIdMessage(int displayId) { - StringBuilder msg = new StringBuilder("Display ").append(displayId).append(" not found"); - int[] displayIds = ServiceManager.getDisplayManager().getDisplayIds(); - if (displayIds != null && displayIds.length > 0) { - msg.append("\nTry to use one of the available display ids:"); - for (int id : displayIds) { - msg.append("\n scrcpy --display=").append(id); - } - } - return msg.toString(); - } - public synchronized void setMaxSize(int newMaxSize) { maxSize = newMaxSize; screenInfo = ScreenInfo.computeScreenInfo(screenInfo.getReverseVideoRotation(), deviceSize, crop, newMaxSize, lockVideoOrientation); diff --git a/server/src/main/java/com/genymobile/scrcpy/LogUtils.java b/server/src/main/java/com/genymobile/scrcpy/LogUtils.java index e74b7e97..c073336d 100644 --- a/server/src/main/java/com/genymobile/scrcpy/LogUtils.java +++ b/server/src/main/java/com/genymobile/scrcpy/LogUtils.java @@ -1,5 +1,7 @@ package com.genymobile.scrcpy; +import com.genymobile.scrcpy.wrappers.ServiceManager; + import java.util.List; public final class LogUtils { @@ -35,4 +37,17 @@ public final class LogUtils { } return builder.toString(); } + + public static String buildDisplayListMessage() { + StringBuilder builder = new StringBuilder("List of displays:"); + int[] displayIds = ServiceManager.getDisplayManager().getDisplayIds(); + if (displayIds == null || displayIds.length == 0) { + builder.append("\n (none)"); + } else { + for (int id : displayIds) { + builder.append("\n --display=").append(id); + } + } + return builder.toString(); + } } diff --git a/server/src/main/java/com/genymobile/scrcpy/Options.java b/server/src/main/java/com/genymobile/scrcpy/Options.java index 8cac5e2c..bcf235ed 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Options.java +++ b/server/src/main/java/com/genymobile/scrcpy/Options.java @@ -34,6 +34,7 @@ public class Options { private boolean powerOn = true; private boolean listEncoders; + private boolean listDisplays; // Options not used by the scrcpy client, but useful to use scrcpy-server directly private boolean sendDeviceMeta = true; // send device name and size @@ -249,6 +250,14 @@ public class Options { this.listEncoders = listEncoders; } + public boolean getListDisplays() { + return listDisplays; + } + + public void setListDisplays(boolean listDisplays) { + this.listDisplays = listDisplays; + } + public boolean getSendDeviceMeta() { return sendDeviceMeta; } diff --git a/server/src/main/java/com/genymobile/scrcpy/Server.java b/server/src/main/java/com/genymobile/scrcpy/Server.java index f46cf308..35da6965 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Server.java +++ b/server/src/main/java/com/genymobile/scrcpy/Server.java @@ -295,6 +295,10 @@ public final class Server { boolean listEncoders = Boolean.parseBoolean(value); options.setListEncoders(listEncoders); break; + case "list_displays": + boolean listDisplays = Boolean.parseBoolean(value); + options.setListDisplays(listDisplays); + break; case "send_device_meta": boolean sendDeviceMeta = Boolean.parseBoolean(value); options.setSendDeviceMeta(sendDeviceMeta); @@ -354,14 +358,19 @@ public final class Server { Ln.initLogLevel(options.getLogLevel()); - if (options.getListEncoders()) { + if (options.getListEncoders() || options.getListDisplays()) { if (options.getCleanup()) { CleanUp.unlinkSelf(); } - Ln.i(LogUtils.buildVideoEncoderListMessage()); - Ln.i(LogUtils.buildAudioEncoderListMessage()); - // Just print the available encoders, do not mirror + if (options.getListEncoders()) { + Ln.i(LogUtils.buildVideoEncoderListMessage()); + Ln.i(LogUtils.buildAudioEncoderListMessage()); + } + if (options.getListDisplays()) { + Ln.i(LogUtils.buildDisplayListMessage()); + } + // Just print the requested data, do not mirror return; }