diff --git a/app/data/bash-completion/scrcpy b/app/data/bash-completion/scrcpy index 003f9d73..a44ff6e5 100644 --- a/app/data/bash-completion/scrcpy +++ b/app/data/bash-completion/scrcpy @@ -44,6 +44,8 @@ _scrcpy() { --no-video-playback --otg -p --port= + --pause-on-exit + --pause-on-exit= --power-off-on-close --prefer-text --print-fps @@ -97,6 +99,10 @@ _scrcpy() { COMPREPLY=($(compgen -W 'unlocked initial 0 1 2 3' -- "$cur")) return ;; + --pause-on-exit) + COMPREPLY=($(compgen -W 'true false if-error' -- "$cur")) + return + ;; -r|--record) COMPREPLY=($(compgen -f -- "$cur")) return diff --git a/app/data/zsh-completion/_scrcpy b/app/data/zsh-completion/_scrcpy index 81142851..ac40e903 100644 --- a/app/data/zsh-completion/_scrcpy +++ b/app/data/zsh-completion/_scrcpy @@ -50,6 +50,7 @@ arguments=( '--no-video-playback[Disable video playback]' '--otg[Run in OTG mode \(simulating physical keyboard and mouse\)]' {-p,--port=}'[\[port\[\:port\]\] Set the TCP port \(range\) used by the client to listen]' + '--pause-on-exit=[Make scrcpy pause before exiting]:mode:(true false if-error)' '--power-off-on-close[Turn the device screen off when closing scrcpy]' '--prefer-text[Inject alpha characters and space as text events instead of key events]' '--print-fps[Start FPS counter, to print frame logs to the console]' diff --git a/app/scrcpy.1 b/app/scrcpy.1 index c1378d8b..7ae56a27 100644 --- a/app/scrcpy.1 +++ b/app/scrcpy.1 @@ -267,6 +267,16 @@ Set the TCP port (range) used by the client to listen. Default is 27183:27199. +.TP +\fB\-\-pause\-on\-exit\fR[=\fImode\fR] +Configure pause on exit. Possible values are "true" (always pause on exit), "false" (never pause on exit) and "if-error" (pause only if an error occured). + +This is useful to prevent the terminal window from automatically closing, so that error messages can be read. + +Default is "false". + +Passing the option without argument is equivalent to passing "true". + .TP .B \-\-power\-off\-on\-close Turn the device screen off when closing scrcpy. diff --git a/app/src/cli.c b/app/src/cli.c index 09f853f5..86b720fc 100644 --- a/app/src/cli.c +++ b/app/src/cli.c @@ -79,6 +79,7 @@ enum { OPT_AUDIO_SOURCE, OPT_KILL_ADB_ON_CLOSE, OPT_TIME_LIMIT, + OPT_PAUSE_ON_EXIT, }; struct sc_option { @@ -463,6 +464,20 @@ static const struct sc_option options[] = { "Default is " STR(DEFAULT_LOCAL_PORT_RANGE_FIRST) ":" STR(DEFAULT_LOCAL_PORT_RANGE_LAST) ".", }, + { + .longopt_id = OPT_PAUSE_ON_EXIT, + .longopt = "pause-on-exit", + .argdesc = "mode", + .optional_arg = true, + .text = "Configure pause on exit. Possible values are \"true\" (always " + "pause on exit), \"false\" (never pause on exit) and " + "\"if-error\" (pause only if an error occured).\n" + "This is useful to prevent the terminal window from " + "automatically closing, so that error messages can be read.\n" + "Default is \"false\".\n" + "Passing the option without argument is equivalent to passing " + "\"true\".", + }, { .longopt_id = OPT_POWER_OFF_ON_CLOSE, .longopt = "power-off-on-close", @@ -1637,6 +1652,29 @@ parse_time_limit(const char *s, sc_tick *tick) { return true; } +static bool +parse_pause_on_exit(const char *s, enum sc_pause_on_exit *pause_on_exit) { + if (!s || !strcmp(s, "true")) { + *pause_on_exit = SC_PAUSE_ON_EXIT_TRUE; + return true; + } + + if (!strcmp(s, "false")) { + *pause_on_exit = SC_PAUSE_ON_EXIT_FALSE; + return true; + } + + if (!strcmp(s, "if-error")) { + *pause_on_exit = SC_PAUSE_ON_EXIT_IF_ERROR; + return true; + } + + LOGE("Unsupported pause on exit mode: %s " + "(expected true, false or if-error)", optarg); + return false; + +} + static bool parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[], const char *optstring, const struct option *longopts) { @@ -1977,6 +2015,11 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[], return false; } break; + case OPT_PAUSE_ON_EXIT: + if (!parse_pause_on_exit(optarg, &args->pause_on_exit)) { + return false; + } + break; default: // getopt prints the error message on stderr return false; @@ -2190,6 +2233,37 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[], return true; } +static enum sc_pause_on_exit +sc_get_pause_on_exit(int argc, char *argv[]) { + // Read arguments backwards so that the last --pause-on-exit is considered + // (same behavior as getopt()) + for (int i = argc - 1; i >= 1; --i) { + const char *arg = argv[i]; + // Starts with "--pause-on-exit" + if (!strncmp("--pause-on-exit", arg, 15)) { + if (arg[15] == '\0') { + // No argument + return SC_PAUSE_ON_EXIT_TRUE; + } + if (arg[15] != '=') { + // Invalid parameter, ignore + return SC_PAUSE_ON_EXIT_FALSE; + } + const char *value = &arg[16]; + if (!strcmp(value, "true")) { + return SC_PAUSE_ON_EXIT_TRUE; + } + if (!strcmp(value, "if-error")) { + return SC_PAUSE_ON_EXIT_IF_ERROR; + } + // Set to false, inclusing when the value is invalid + return SC_PAUSE_ON_EXIT_FALSE; + } + } + + return false; +} + bool scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) { struct sc_getopt_adapter adapter; @@ -2203,5 +2277,11 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) { sc_getopt_adapter_destroy(&adapter); + if (!ret && args->pause_on_exit == SC_PAUSE_ON_EXIT_FALSE) { + // Check if "--pause-on-exit" is present in the arguments list, because + // it must be taken into account even if command line parsing failed + args->pause_on_exit = sc_get_pause_on_exit(argc, argv); + } + return ret; } diff --git a/app/src/cli.h b/app/src/cli.h index b9361a9c..23d34fcd 100644 --- a/app/src/cli.h +++ b/app/src/cli.h @@ -7,10 +7,17 @@ #include "options.h" +enum sc_pause_on_exit { + SC_PAUSE_ON_EXIT_TRUE, + SC_PAUSE_ON_EXIT_FALSE, + SC_PAUSE_ON_EXIT_IF_ERROR, +}; + struct scrcpy_cli_args { struct scrcpy_options opts; bool help; bool version; + enum sc_pause_on_exit pause_on_exit; }; void diff --git a/app/src/main.c b/app/src/main.c index cc3a85a7..d582c5ae 100644 --- a/app/src/main.c +++ b/app/src/main.c @@ -39,26 +39,32 @@ main_scrcpy(int argc, char *argv[]) { .opts = scrcpy_options_default, .help = false, .version = false, + .pause_on_exit = SC_PAUSE_ON_EXIT_FALSE, }; #ifndef NDEBUG args.opts.log_level = SC_LOG_LEVEL_DEBUG; #endif + enum scrcpy_exit_code ret; + if (!scrcpy_parse_args(&args, argc, argv)) { - return SCRCPY_EXIT_FAILURE; + ret = SCRCPY_EXIT_FAILURE; + goto end; } sc_set_log_level(args.opts.log_level); if (args.help) { scrcpy_print_usage(argv[0]); - return SCRCPY_EXIT_SUCCESS; + ret = SCRCPY_EXIT_SUCCESS; + goto end; } if (args.version) { scrcpy_print_version(); - return SCRCPY_EXIT_SUCCESS; + ret = SCRCPY_EXIT_SUCCESS; + goto end; } #ifdef SCRCPY_LAVF_REQUIRES_REGISTER_ALL @@ -72,18 +78,26 @@ main_scrcpy(int argc, char *argv[]) { #endif if (!net_init()) { - return SCRCPY_EXIT_FAILURE; + ret = SCRCPY_EXIT_FAILURE; + goto end; } sc_log_configure(); #ifdef HAVE_USB - enum scrcpy_exit_code ret = args.opts.otg ? scrcpy_otg(&args.opts) - : scrcpy(&args.opts); + ret = args.opts.otg ? scrcpy_otg(&args.opts) : scrcpy(&args.opts); #else - enum scrcpy_exit_code ret = scrcpy(&args.opts); + ret = scrcpy(&args.opts); #endif +end: + if (args.pause_on_exit == SC_PAUSE_ON_EXIT_TRUE || + (args.pause_on_exit == SC_PAUSE_ON_EXIT_IF_ERROR && + ret != SCRCPY_EXIT_SUCCESS)) { + printf("Press Enter to continue...\n"); + getchar(); + } + return ret; }