diff --git a/README.md b/README.md index ea5731a7..09cb27b8 100644 --- a/README.md +++ b/README.md @@ -396,6 +396,18 @@ The list of display ids can be retrieved by: adb shell dumpsys display # search "mDisplayId=" in the output ``` +#### Stay awake + +To prevent the device to sleep after some delay: + +```bash +scrcpy --stay-awake +scrcpy -w +``` + +The initial state is restored when scrcpy is closed. + + #### Turn screen off It is possible to turn the device screen off while mirroring on start with a @@ -410,6 +422,14 @@ Or by pressing `Ctrl`+`o` at any time. To turn it back on, press `POWER` (or `Ctrl`+`p`). +It can be useful to also prevent the device to sleep: + +```bash +scrcpy --turn-screen-off --stay-awake +scrcpy -Sw +``` + + #### Render expired frames By default, to minimize latency, _scrcpy_ always renders the last decoded frame diff --git a/app/scrcpy.1 b/app/scrcpy.1 index 15200460..02389159 100644 --- a/app/scrcpy.1 +++ b/app/scrcpy.1 @@ -144,6 +144,10 @@ It only shows physical touches (not clicks from scrcpy). .B \-v, \-\-version Print the version of scrcpy. +.TP +.B \-w, \-\-stay-awake +Keep the device on while scrcpy is running. + .TP .B \-\-window\-borderless Disable window decorations (display borderless window). diff --git a/app/src/cli.c b/app/src/cli.c index d49301ac..0c68279f 100644 --- a/app/src/cli.c +++ b/app/src/cli.c @@ -137,6 +137,9 @@ scrcpy_print_usage(const char *arg0) { " -v, --version\n" " Print the version of scrcpy.\n" "\n" + " -w, --stay-awake\n" + " Keep the device on while scrcpy is running.\n" + "\n" " --window-borderless\n" " Disable window decorations (display borderless window).\n" "\n" @@ -497,6 +500,7 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) { {"rotation", required_argument, NULL, OPT_ROTATION}, {"serial", required_argument, NULL, 's'}, {"show-touches", no_argument, NULL, 't'}, + {"stay-awake", no_argument, NULL, 'w'}, {"turn-screen-off", no_argument, NULL, 'S'}, {"version", no_argument, NULL, 'v'}, {"window-title", required_argument, NULL, OPT_WINDOW_TITLE}, @@ -514,7 +518,7 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) { optind = 0; // reset to start from the first argument in tests int c; - while ((c = getopt_long(argc, argv, "b:c:fF:hm:nNp:r:s:StTv", long_options, + while ((c = getopt_long(argc, argv, "b:c:fF:hm:nNp:r:s:StTvw", long_options, NULL)) != -1) { switch (c) { case 'b': @@ -594,6 +598,9 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) { case 'v': args->version = true; break; + case 'w': + opts->stay_awake = true; + break; case OPT_RENDER_EXPIRED_FRAMES: opts->render_expired_frames = true; break; @@ -676,5 +683,10 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) { return false; } + if (!opts->control && opts->stay_awake) { + LOGE("Could not request to stay awake if control is disabled"); + return false; + } + return true; } diff --git a/app/src/scrcpy.c b/app/src/scrcpy.c index 015dec08..c085e769 100644 --- a/app/src/scrcpy.c +++ b/app/src/scrcpy.c @@ -278,6 +278,7 @@ scrcpy(const struct scrcpy_options *options) { .control = options->control, .display_id = options->display_id, .show_touches = options->show_touches, + .stay_awake = options->stay_awake, }; if (!server_start(&server, options->serial, ¶ms)) { return false; diff --git a/app/src/scrcpy.h b/app/src/scrcpy.h index d6b0a0f6..99e72ef0 100644 --- a/app/src/scrcpy.h +++ b/app/src/scrcpy.h @@ -38,6 +38,7 @@ struct scrcpy_options { bool prefer_text; bool window_borderless; bool mipmaps; + bool stay_awake; }; #define SCRCPY_OPTIONS_DEFAULT { \ @@ -72,6 +73,7 @@ struct scrcpy_options { .prefer_text = false, \ .window_borderless = false, \ .mipmaps = true, \ + .stay_awake = false, \ } bool diff --git a/app/src/server.c b/app/src/server.c index 5399d269..b98cec2f 100644 --- a/app/src/server.c +++ b/app/src/server.c @@ -269,6 +269,7 @@ execute_server(struct server *server, const struct server_params *params) { params->control ? "true" : "false", display_id_string, params->show_touches ? "true" : "false", + params->stay_awake ? "true" : "false", }; #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 33e99bea..13d0b451 100644 --- a/app/src/server.h +++ b/app/src/server.h @@ -52,6 +52,7 @@ struct server_params { bool control; uint16_t display_id; bool show_touches; + bool stay_awake; }; // init default values diff --git a/server/src/main/java/com/genymobile/scrcpy/CleanUp.java b/server/src/main/java/com/genymobile/scrcpy/CleanUp.java index ccbca275..74555636 100644 --- a/server/src/main/java/com/genymobile/scrcpy/CleanUp.java +++ b/server/src/main/java/com/genymobile/scrcpy/CleanUp.java @@ -19,18 +19,18 @@ public final class CleanUp { // not instantiable } - public static void configure(boolean disableShowTouches) throws IOException { - boolean needProcess = disableShowTouches; + public static void configure(boolean disableShowTouches, int restoreStayOn) throws IOException { + boolean needProcess = disableShowTouches || restoreStayOn != -1; if (needProcess) { - startProcess(disableShowTouches); + startProcess(disableShowTouches, restoreStayOn); } else { // There is no additional clean up to do when scrcpy dies unlinkSelf(); } } - private static void startProcess(boolean disableShowTouches) throws IOException { - String[] cmd = {"app_process", "/", CleanUp.class.getName(), String.valueOf(disableShowTouches)}; + private static void startProcess(boolean disableShowTouches, int restoreStayOn) throws IOException { + String[] cmd = {"app_process", "/", CleanUp.class.getName(), String.valueOf(disableShowTouches), String.valueOf(restoreStayOn)}; ProcessBuilder builder = new ProcessBuilder(cmd); builder.environment().put("CLASSPATH", SERVER_PATH); @@ -58,12 +58,19 @@ public final class CleanUp { Ln.i("Cleaning up"); boolean disableShowTouches = Boolean.parseBoolean(args[0]); + int restoreStayOn = Integer.parseInt(args[1]); - if (disableShowTouches) { + if (disableShowTouches || restoreStayOn != -1) { ServiceManager serviceManager = new ServiceManager(); try (ContentProvider settings = serviceManager.getActivityManager().createSettingsProvider()) { - Ln.i("Disabling \"show touches\""); - settings.putValue(ContentProvider.TABLE_SYSTEM, "show_touches", "0"); + if (disableShowTouches) { + Ln.i("Disabling \"show touches\""); + settings.putValue(ContentProvider.TABLE_SYSTEM, "show_touches", "0"); + } + if (restoreStayOn != -1) { + Ln.i("Restoring \"stay awake\""); + settings.putValue(ContentProvider.TABLE_GLOBAL, "stay_on_while_plugged_in", String.valueOf(restoreStayOn)); + } } } } diff --git a/server/src/main/java/com/genymobile/scrcpy/Options.java b/server/src/main/java/com/genymobile/scrcpy/Options.java index 838416c8..152aa2f7 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Options.java +++ b/server/src/main/java/com/genymobile/scrcpy/Options.java @@ -13,6 +13,7 @@ public class Options { private boolean control; private int displayId; private boolean showTouches; + private boolean stayAwake; public int getMaxSize() { return maxSize; @@ -93,4 +94,12 @@ public class Options { public void setShowTouches(boolean showTouches) { this.showTouches = showTouches; } + + public boolean getStayAwake() { + return stayAwake; + } + + public void setStayAwake(boolean stayAwake) { + this.stayAwake = stayAwake; + } } diff --git a/server/src/main/java/com/genymobile/scrcpy/Server.java b/server/src/main/java/com/genymobile/scrcpy/Server.java index 43ab0f55..73776c3e 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Server.java +++ b/server/src/main/java/com/genymobile/scrcpy/Server.java @@ -4,6 +4,7 @@ import com.genymobile.scrcpy.wrappers.ContentProvider; import android.graphics.Rect; import android.media.MediaCodec; +import android.os.BatteryManager; import android.os.Build; import java.io.IOException; @@ -20,15 +21,32 @@ public final class Server { final Device device = new Device(options); boolean mustDisableShowTouchesOnCleanUp = false; - if (options.getShowTouches()) { + int restoreStayOn = -1; + if (options.getShowTouches() || options.getStayAwake()) { try (ContentProvider settings = device.createSettingsProvider()) { - String oldValue = settings.getAndPutValue(ContentProvider.TABLE_SYSTEM, "show_touches", "1"); - // If "show touches" was disabled, it must be disabled back on clean up - mustDisableShowTouchesOnCleanUp = !"1".equals(oldValue); + if (options.getShowTouches()) { + String oldValue = settings.getAndPutValue(ContentProvider.TABLE_SYSTEM, "show_touches", "1"); + // If "show touches" was disabled, it must be disabled back on clean up + mustDisableShowTouchesOnCleanUp = !"1".equals(oldValue); + } + + if (options.getStayAwake()) { + int stayOn = BatteryManager.BATTERY_PLUGGED_AC | BatteryManager.BATTERY_PLUGGED_USB | BatteryManager.BATTERY_PLUGGED_WIRELESS; + String oldValue = settings.getAndPutValue(ContentProvider.TABLE_GLOBAL, "stay_on_while_plugged_in", String.valueOf(stayOn)); + try { + restoreStayOn = Integer.parseInt(oldValue); + if (restoreStayOn == stayOn) { + // No need to restore + restoreStayOn = -1; + } + } catch (NumberFormatException e) { + restoreStayOn = 0; + } + } } } - CleanUp.configure(mustDisableShowTouchesOnCleanUp); + CleanUp.configure(mustDisableShowTouchesOnCleanUp, restoreStayOn); boolean tunnelForward = options.isTunnelForward(); try (DesktopConnection connection = DesktopConnection.open(device, tunnelForward)) { @@ -91,7 +109,7 @@ public final class Server { "The server version (" + BuildConfig.VERSION_NAME + ") does not match the client " + "(" + clientVersion + ")"); } - final int expectedParameters = 11; + final int expectedParameters = 12; if (args.length != expectedParameters) { throw new IllegalArgumentException("Expecting " + expectedParameters + " parameters"); } @@ -129,6 +147,9 @@ public final class Server { boolean showTouches = Boolean.parseBoolean(args[10]); options.setShowTouches(showTouches); + boolean stayAwake = Boolean.parseBoolean(args[11]); + options.setStayAwake(stayAwake); + return options; }