From eff5b4b219be6043a3baf51149b1d6752569a173 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sun, 3 Nov 2024 22:46:21 +0100 Subject: [PATCH] Add --screen-off-timeout Change the Android "screen off timeout" (the idle delay before the screen automatically turns off) and restore the initial value on exit. PR #5447 --- app/data/bash-completion/scrcpy | 1 + app/data/zsh-completion/_scrcpy | 1 + app/src/cli.c | 28 +++++++++++++++ app/src/options.c | 1 + app/src/options.h | 1 + app/src/scrcpy.c | 1 + app/src/server.c | 5 +++ app/src/server.h | 1 + doc/device.md | 25 +++++++++++++ .../java/com/genymobile/scrcpy/CleanUp.java | 35 +++++++++++++++++-- .../java/com/genymobile/scrcpy/Options.java | 11 ++++++ 11 files changed, 108 insertions(+), 2 deletions(-) diff --git a/app/data/bash-completion/scrcpy b/app/data/bash-completion/scrcpy index d9a7be60..d9ad4c8d 100644 --- a/app/data/bash-completion/scrcpy +++ b/app/data/bash-completion/scrcpy @@ -77,6 +77,7 @@ _scrcpy() { --rotation= -s --serial= -S --turn-screen-off + --screen-off-timeout= --shortcut-mod= --start-app= -t --show-touches diff --git a/app/data/zsh-completion/_scrcpy b/app/data/zsh-completion/_scrcpy index c4e0e60c..430e8000 100644 --- a/app/data/zsh-completion/_scrcpy +++ b/app/data/zsh-completion/_scrcpy @@ -80,6 +80,7 @@ arguments=( '--require-audio=[Make scrcpy fail if audio is enabled but does not work]' {-s,--serial=}'[The device serial number \(mandatory for multiple devices only\)]:serial:($("${ADB-adb}" devices | awk '\''$2 == "device" {print $1}'\''))' {-S,--turn-screen-off}'[Turn the device screen off immediately]' + '--screen-off-timeout=[Set the screen off timeout in seconds]' '--shortcut-mod=[\[key1,key2+key3,...\] Specify the modifiers to use for scrcpy shortcuts]:shortcut mod:(lctrl rctrl lalt ralt lsuper rsuper)' '--start-app=[Start an Android app]' {-t,--show-touches}'[Show physical touches]' diff --git a/app/src/cli.c b/app/src/cli.c index 7cc68085..ebf0f6f6 100644 --- a/app/src/cli.c +++ b/app/src/cli.c @@ -106,6 +106,7 @@ enum { OPT_NEW_DISPLAY, OPT_LIST_APPS, OPT_START_APP, + OPT_SCREEN_OFF_TIMEOUT, }; struct sc_option { @@ -793,6 +794,13 @@ static const struct sc_option options[] = { .longopt = "turn-screen-off", .text = "Turn the device screen off immediately.", }, + { + .longopt_id = OPT_SCREEN_OFF_TIMEOUT, + .longopt = "screen-off-timeout", + .argdesc = "seconds", + .text = "Set the screen off timeout while scrcpy is running (restore " + "the initial value on exit).", + }, { .longopt_id = OPT_SHORTCUT_MOD, .longopt = "shortcut-mod", @@ -2155,6 +2163,20 @@ parse_time_limit(const char *s, sc_tick *tick) { return true; } +static bool +parse_screen_off_timeout(const char *s, sc_tick *tick) { + long value; + // value in seconds, but must fit in 31 bits in milliseconds + bool ok = parse_integer_arg(s, &value, false, 0, 0x7FFFFFFF / 1000, + "screen off timeout"); + if (!ok) { + return false; + } + + *tick = SC_TICK_FROM_SEC(value); + return true; +} + static bool parse_pause_on_exit(const char *s, enum sc_pause_on_exit *pause_on_exit) { if (!s || !strcmp(s, "true")) { @@ -2730,6 +2752,12 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[], case OPT_START_APP: opts->start_app = optarg; break; + case OPT_SCREEN_OFF_TIMEOUT: + if (!parse_screen_off_timeout(optarg, + &opts->screen_off_timeout)) { + return false; + } + break; default: // getopt prints the error message on stderr return false; diff --git a/app/src/options.c b/app/src/options.c index b1a3b739..3cad9d9f 100644 --- a/app/src/options.c +++ b/app/src/options.c @@ -62,6 +62,7 @@ const struct scrcpy_options scrcpy_options_default = { .audio_buffer = -1, // depends on the audio format, .audio_output_buffer = SC_TICK_FROM_MS(5), .time_limit = 0, + .screen_off_timeout = -1, #ifdef HAVE_V4L2 .v4l2_device = NULL, .v4l2_buffer = 0, diff --git a/app/src/options.h b/app/src/options.h index d37ac0a2..5662719a 100644 --- a/app/src/options.h +++ b/app/src/options.h @@ -265,6 +265,7 @@ struct scrcpy_options { sc_tick audio_buffer; sc_tick audio_output_buffer; sc_tick time_limit; + sc_tick screen_off_timeout; #ifdef HAVE_V4L2 const char *v4l2_device; sc_tick v4l2_buffer; diff --git a/app/src/scrcpy.c b/app/src/scrcpy.c index 8d135394..2721c0d8 100644 --- a/app/src/scrcpy.c +++ b/app/src/scrcpy.c @@ -428,6 +428,7 @@ scrcpy(struct scrcpy_options *options) { .video_bit_rate = options->video_bit_rate, .audio_bit_rate = options->audio_bit_rate, .max_fps = options->max_fps, + .screen_off_timeout = options->screen_off_timeout, .lock_video_orientation = options->lock_video_orientation, .control = options->control, .display_id = options->display_id, diff --git a/app/src/server.c b/app/src/server.c index 167582e4..41f0bf27 100644 --- a/app/src/server.c +++ b/app/src/server.c @@ -320,6 +320,11 @@ execute_server(struct sc_server *server, if (params->stay_awake) { ADD_PARAM("stay_awake=true"); } + if (params->screen_off_timeout != -1) { + assert(params->screen_off_timeout >= 0); + uint64_t ms = SC_TICK_TO_MS(params->screen_off_timeout); + ADD_PARAM("screen_off_timeout=%" PRIu64, ms); + } if (params->video_codec_options) { VALIDATE_STRING(params->video_codec_options); ADD_PARAM("video_codec_options=%s", params->video_codec_options); diff --git a/app/src/server.h b/app/src/server.h index 4ff5539d..7059be7f 100644 --- a/app/src/server.h +++ b/app/src/server.h @@ -45,6 +45,7 @@ struct sc_server_params { uint32_t video_bit_rate; uint32_t audio_bit_rate; const char *max_fps; // float to be parsed by the server + sc_tick screen_off_timeout; int8_t lock_video_orientation; bool control; uint32_t display_id; diff --git a/doc/device.md b/doc/device.md index 06210e18..42208faa 100644 --- a/doc/device.md +++ b/doc/device.md @@ -71,6 +71,31 @@ adb shell cmd display power-on 0 ``` +## Screen off timeout + +The Android screen automatically turns off after some delay. + +To change this delay while scrcpy is running: + +```bash +scrcpy --screen-off-timeout=300 # 300 seconds (5 minutes) +``` + +The initial value is restored on exit. + +It is possible to change this setting manually: + +```bash +# get the current screen_off_timeout value +adb shell settings get system screen_off_timeout +# set a new value (in milliseconds) +adb shell settings put system screen_off_timeout 30000 +``` + +Note that the Android value is in milliseconds, but the scrcpy command line +argument is in seconds. + + ## Show touches For presentations, it may be useful to show physical touches (on the physical diff --git a/server/src/main/java/com/genymobile/scrcpy/CleanUp.java b/server/src/main/java/com/genymobile/scrcpy/CleanUp.java index a8a5784d..90de8c2c 100644 --- a/server/src/main/java/com/genymobile/scrcpy/CleanUp.java +++ b/server/src/main/java/com/genymobile/scrcpy/CleanUp.java @@ -73,10 +73,29 @@ public final class CleanUp { } } + int restoreScreenOffTimeout = -1; + int screenOffTimeout = options.getScreenOffTimeout(); + if (screenOffTimeout != -1) { + try { + String oldValue = Settings.getAndPutValue(Settings.TABLE_SYSTEM, "screen_off_timeout", String.valueOf(screenOffTimeout)); + try { + int currentScreenOffTimeout = Integer.parseInt(oldValue); + // Restore only if the current value is different + if (currentScreenOffTimeout != screenOffTimeout) { + restoreScreenOffTimeout = currentScreenOffTimeout; + } + } catch (NumberFormatException e) { + // ignore + } + } catch (SettingsException e) { + Ln.e("Could not change \"screen_off_timeout\"", e); + } + } + boolean powerOffScreen = options.getPowerOffScreenOnClose(); try { - run(displayId, restoreStayOn, disableShowTouches, powerOffScreen); + run(displayId, restoreStayOn, disableShowTouches, powerOffScreen, restoreScreenOffTimeout); } catch (InterruptedException e) { // ignore } catch (IOException e) { @@ -84,7 +103,8 @@ public final class CleanUp { } } - private void run(int displayId, int restoreStayOn, boolean disableShowTouches, boolean powerOffScreen) throws IOException, InterruptedException { + private void run(int displayId, int restoreStayOn, boolean disableShowTouches, boolean powerOffScreen, int restoreScreenOffTimeout) + throws IOException, InterruptedException { String[] cmd = { "app_process", "/", @@ -93,6 +113,7 @@ public final class CleanUp { String.valueOf(restoreStayOn), String.valueOf(disableShowTouches), String.valueOf(powerOffScreen), + String.valueOf(restoreScreenOffTimeout), }; ProcessBuilder builder = new ProcessBuilder(cmd); @@ -139,6 +160,7 @@ public final class CleanUp { int restoreStayOn = Integer.parseInt(args[1]); boolean disableShowTouches = Boolean.parseBoolean(args[2]); boolean powerOffScreen = Boolean.parseBoolean(args[3]); + int restoreScreenOffTimeout = Integer.parseInt(args[4]); // Dynamic option boolean restoreDisplayPower = false; @@ -175,6 +197,15 @@ public final class CleanUp { } } + if (restoreScreenOffTimeout != -1) { + Ln.i("Restoring \"screen off timeout\""); + try { + Settings.putValue(Settings.TABLE_SYSTEM, "screen_off_timeout", String.valueOf(restoreScreenOffTimeout)); + } catch (SettingsException e) { + Ln.e("Could not restore \"screen_off_timeout\"", e); + } + } + if (displayId != Device.DISPLAY_ID_NONE && Device.isScreenOn(displayId)) { if (powerOffScreen) { Ln.i("Power off screen"); diff --git a/server/src/main/java/com/genymobile/scrcpy/Options.java b/server/src/main/java/com/genymobile/scrcpy/Options.java index 659a6948..e75321e6 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Options.java +++ b/server/src/main/java/com/genymobile/scrcpy/Options.java @@ -45,6 +45,7 @@ public class Options { private boolean cameraHighSpeed; private boolean showTouches; private boolean stayAwake; + private int screenOffTimeout = -1; private List videoCodecOptions; private List audioCodecOptions; @@ -174,6 +175,10 @@ public class Options { return stayAwake; } + public int getScreenOffTimeout() { + return screenOffTimeout; + } + public List getVideoCodecOptions() { return videoCodecOptions; } @@ -363,6 +368,12 @@ public class Options { case "stay_awake": options.stayAwake = Boolean.parseBoolean(value); break; + case "screen_off_timeout": + options.screenOffTimeout = Integer.parseInt(value); + if (options.screenOffTimeout < -1) { + throw new IllegalArgumentException("Invalid screen off timeout: " + options.screenOffTimeout); + } + break; case "video_codec_options": options.videoCodecOptions = CodecOption.parse(value); break;