Add --list-displays

Add an option to list the device displays properly.
This commit is contained in:
Romain Vimont 2023-02-23 23:10:15 +01:00
parent 2596ca02f0
commit b65301f672
14 changed files with 69 additions and 28 deletions

View file

@ -718,7 +718,7 @@ scrcpy --display=1
The list of display ids can be retrieved by: The list of display ids can be retrieved by:
```bash ```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 The secondary display may only be controlled if the device runs at least Android

View file

@ -19,6 +19,7 @@ _scrcpy() {
-K --hid-keyboard -K --hid-keyboard
-h --help -h --help
--legacy-paste --legacy-paste
--list-displays
--list-encoders --list-encoders
--lock-video-orientation --lock-video-orientation
--lock-video-orientation= --lock-video-orientation=

View file

@ -26,6 +26,7 @@ arguments=(
{-K,--hid-keyboard}'[Simulate a physical keyboard by using HID over AOAv2]' {-K,--hid-keyboard}'[Simulate a physical keyboard by using HID over AOAv2]'
{-h,--help}'[Print the help]' {-h,--help}'[Print the help]'
'--legacy-paste[Inject computer clipboard text as a sequence of key events on Ctrl+v]' '--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]' '--list-encoders[List video and audio encoders available on the device]'
'--lock-video-orientation=[Lock video orientation]:orientation:(unlocked initial 0 1 2 3)' '--lock-video-orientation=[Lock video orientation]:orientation:(unlocked initial 0 1 2 3)'
'--max-fps=[Limit the frame rate of screen capture]' '--max-fps=[Limit the frame rate of screen capture]'

View file

@ -73,10 +73,9 @@ Disable screensaver while scrcpy is running.
.TP .TP
.BI "\-\-display " id .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" The available display ids can be listed by \-\-list\-displays.
(search "mDisplayId=" in the output).
Default is 0. Default is 0.
@ -134,6 +133,10 @@ This is a workaround for some devices not behaving as expected when setting the
.B \-\-list\-encoders .B \-\-list\-encoders
List video and audio encoders available on the device. List video and audio encoders available on the device.
.TP
.B \-\-list\-displays
List displays available on the device.
.TP .TP
\fB\-\-lock\-video\-orientation\fR[=\fIvalue\fR] \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. 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.

View file

@ -69,6 +69,7 @@ enum {
OPT_AUDIO_CODEC_OPTIONS, OPT_AUDIO_CODEC_OPTIONS,
OPT_AUDIO_ENCODER, OPT_AUDIO_ENCODER,
OPT_LIST_ENCODERS, OPT_LIST_ENCODERS,
OPT_LIST_DISPLAYS,
}; };
struct sc_option { struct sc_option {
@ -198,10 +199,9 @@ static const struct sc_option options[] = {
.longopt_id = OPT_DISPLAY_ID, .longopt_id = OPT_DISPLAY_ID,
.longopt = "display", .longopt = "display",
.argdesc = "id", .argdesc = "id",
.text = "Specify the display id to mirror.\n" .text = "Specify the device display id to mirror.\n"
"The list of possible display ids can be listed by:\n" "The available display ids can be listed by:\n"
" adb shell dumpsys display\n" " scrcpy --list-displays\n"
"(search \"mDisplayId=\" in the output)\n"
"Default is 0.", "Default is 0.",
}, },
{ {
@ -272,6 +272,11 @@ static const struct sc_option options[] = {
"This is a workaround for some devices not behaving as " "This is a workaround for some devices not behaving as "
"expected when setting the device clipboard programmatically.", "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_id = OPT_LIST_ENCODERS,
.longopt = "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: case OPT_LIST_ENCODERS:
opts->list_encoders = true; opts->list_encoders = true;
break; break;
case OPT_LIST_DISPLAYS:
opts->list_displays = true;
break;
default: default:
// getopt prints the error message on stderr // getopt prints the error message on stderr
return false; return false;

View file

@ -73,4 +73,5 @@ const struct scrcpy_options scrcpy_options_default = {
.power_on = true, .power_on = true,
.audio = true, .audio = true,
.list_encoders = false, .list_encoders = false,
.list_displays = false,
}; };

View file

@ -155,6 +155,7 @@ struct scrcpy_options {
bool power_on; bool power_on;
bool audio; bool audio;
bool list_encoders; bool list_encoders;
bool list_displays;
}; };
extern const struct scrcpy_options scrcpy_options_default; extern const struct scrcpy_options scrcpy_options_default;

View file

@ -353,6 +353,7 @@ scrcpy(struct scrcpy_options *options) {
.cleanup = options->cleanup, .cleanup = options->cleanup,
.power_on = options->power_on, .power_on = options->power_on,
.list_encoders = options->list_encoders, .list_encoders = options->list_encoders,
.list_displays = options->list_displays,
}; };
static const struct sc_server_callbacks cbs = { static const struct sc_server_callbacks cbs = {
@ -370,7 +371,7 @@ scrcpy(struct scrcpy_options *options) {
server_started = true; server_started = true;
if (options->list_encoders) { if (options->list_encoders || options->list_displays) {
bool ok = await_for_server(NULL); bool ok = await_for_server(NULL);
ret = ok ? SCRCPY_EXIT_SUCCESS : SCRCPY_EXIT_FAILURE; ret = ok ? SCRCPY_EXIT_SUCCESS : SCRCPY_EXIT_FAILURE;
goto end; goto end;

View file

@ -303,6 +303,9 @@ execute_server(struct sc_server *server,
if (params->list_encoders) { if (params->list_encoders) {
ADD_PARAM("list_encoders=true"); ADD_PARAM("list_encoders=true");
} }
if (params->list_displays) {
ADD_PARAM("list_displays=true");
}
#undef ADD_PARAM #undef ADD_PARAM
@ -856,9 +859,9 @@ run_server(void *data) {
goto error_connection_failed; 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. // then exits.
if (params->list_encoders) { if (params->list_encoders || params->list_displays) {
sc_pid pid = execute_server(server, params); sc_pid pid = execute_server(server, params);
if (pid == SC_PROCESS_NONE) { if (pid == SC_PROCESS_NONE) {
goto error_connection_failed; goto error_connection_failed;

View file

@ -56,6 +56,7 @@ struct sc_server_params {
bool cleanup; bool cleanup;
bool power_on; bool power_on;
bool list_encoders; bool list_encoders;
bool list_displays;
}; };
struct sc_server { struct sc_server {

View file

@ -65,7 +65,7 @@ public final class Device {
displayId = options.getDisplayId(); displayId = options.getDisplayId();
DisplayInfo displayInfo = ServiceManager.getDisplayManager().getDisplayInfo(displayId); DisplayInfo displayInfo = ServiceManager.getDisplayManager().getDisplayInfo(displayId);
if (displayInfo == null) { if (displayInfo == null) {
Ln.e(buildUnknownDisplayIdMessage(displayId)); Ln.e("Display " + displayId + " not found\n" + LogUtils.buildDisplayListMessage());
throw new ConfigurationException("Unknown display id: " + displayId); 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) { public synchronized void setMaxSize(int newMaxSize) {
maxSize = newMaxSize; maxSize = newMaxSize;
screenInfo = ScreenInfo.computeScreenInfo(screenInfo.getReverseVideoRotation(), deviceSize, crop, newMaxSize, lockVideoOrientation); screenInfo = ScreenInfo.computeScreenInfo(screenInfo.getReverseVideoRotation(), deviceSize, crop, newMaxSize, lockVideoOrientation);

View file

@ -1,5 +1,7 @@
package com.genymobile.scrcpy; package com.genymobile.scrcpy;
import com.genymobile.scrcpy.wrappers.ServiceManager;
import java.util.List; import java.util.List;
public final class LogUtils { public final class LogUtils {
@ -35,4 +37,17 @@ public final class LogUtils {
} }
return builder.toString(); 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();
}
} }

View file

@ -34,6 +34,7 @@ public class Options {
private boolean powerOn = true; private boolean powerOn = true;
private boolean listEncoders; private boolean listEncoders;
private boolean listDisplays;
// Options not used by the scrcpy client, but useful to use scrcpy-server directly // Options not used by the scrcpy client, but useful to use scrcpy-server directly
private boolean sendDeviceMeta = true; // send device name and size private boolean sendDeviceMeta = true; // send device name and size
@ -249,6 +250,14 @@ public class Options {
this.listEncoders = listEncoders; this.listEncoders = listEncoders;
} }
public boolean getListDisplays() {
return listDisplays;
}
public void setListDisplays(boolean listDisplays) {
this.listDisplays = listDisplays;
}
public boolean getSendDeviceMeta() { public boolean getSendDeviceMeta() {
return sendDeviceMeta; return sendDeviceMeta;
} }

View file

@ -295,6 +295,10 @@ public final class Server {
boolean listEncoders = Boolean.parseBoolean(value); boolean listEncoders = Boolean.parseBoolean(value);
options.setListEncoders(listEncoders); options.setListEncoders(listEncoders);
break; break;
case "list_displays":
boolean listDisplays = Boolean.parseBoolean(value);
options.setListDisplays(listDisplays);
break;
case "send_device_meta": case "send_device_meta":
boolean sendDeviceMeta = Boolean.parseBoolean(value); boolean sendDeviceMeta = Boolean.parseBoolean(value);
options.setSendDeviceMeta(sendDeviceMeta); options.setSendDeviceMeta(sendDeviceMeta);
@ -354,14 +358,19 @@ public final class Server {
Ln.initLogLevel(options.getLogLevel()); Ln.initLogLevel(options.getLogLevel());
if (options.getListEncoders()) { if (options.getListEncoders() || options.getListDisplays()) {
if (options.getCleanup()) { if (options.getCleanup()) {
CleanUp.unlinkSelf(); CleanUp.unlinkSelf();
} }
Ln.i(LogUtils.buildVideoEncoderListMessage()); if (options.getListEncoders()) {
Ln.i(LogUtils.buildAudioEncoderListMessage()); Ln.i(LogUtils.buildVideoEncoderListMessage());
// Just print the available encoders, do not mirror Ln.i(LogUtils.buildAudioEncoderListMessage());
}
if (options.getListDisplays()) {
Ln.i(LogUtils.buildDisplayListMessage());
}
// Just print the requested data, do not mirror
return; return;
} }