From 44aa7f2c88808f08f45d20c5def26408467f1cca Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sat, 13 Feb 2021 14:40:00 +0100 Subject: [PATCH 1/8] Improve error handling in screen initialization After the struct screen is initialized, the window and the renderer are necessarily valid, so there is no need o check in screen_destroy(). --- app/src/screen.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/app/src/screen.c b/app/src/screen.c index 1136d5478e..fae09102db 100644 --- a/app/src/screen.c +++ b/app/src/screen.c @@ -270,7 +270,7 @@ screen_init_rendering(struct screen *screen, const char *window_title, SDL_RENDERER_ACCELERATED); if (!screen->renderer) { LOGC("Could not create renderer: %s", SDL_GetError()); - screen_destroy(screen); + SDL_DestroyWindow(screen->window); return false; } @@ -318,6 +318,8 @@ screen_init_rendering(struct screen *screen, const char *window_title, screen->texture = create_texture(screen); if (!screen->texture) { LOGC("Could not create texture: %s", SDL_GetError()); + SDL_DestroyRenderer(screen->renderer); + SDL_DestroyWindow(screen->window); screen_destroy(screen); return false; } @@ -342,12 +344,8 @@ screen_destroy(struct screen *screen) { if (screen->texture) { SDL_DestroyTexture(screen->texture); } - if (screen->renderer) { - SDL_DestroyRenderer(screen->renderer); - } - if (screen->window) { - SDL_DestroyWindow(screen->window); - } + SDL_DestroyRenderer(screen->renderer); + SDL_DestroyWindow(screen->window); } static void From b1734ab7371b775cbcf0b3f1699e92c85ef52931 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Mon, 15 Feb 2021 18:28:41 +0100 Subject: [PATCH 2/8] Reference video buffer from screen This paves the way to handle EVENT_NEW_FRAME from screen.c, by allowing to call screen_update_frame() without an explicit video_buffer instance. --- app/src/scrcpy.c | 6 ++++-- app/src/screen.c | 7 ++++--- app/src/screen.h | 6 ++++-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/app/src/scrcpy.c b/app/src/scrcpy.c index 505b5eaf1d..f669ef4093 100644 --- a/app/src/scrcpy.c +++ b/app/src/scrcpy.c @@ -30,7 +30,7 @@ #include "util/net.h" static struct server server; -static struct screen screen = SCREEN_INITIALIZER; +static struct screen screen; static struct fps_counter fps_counter; static struct video_buffer video_buffer; static struct stream stream; @@ -179,7 +179,7 @@ handle_event(SDL_Event *event, const struct scrcpy_options *options) { // this is the very first frame, show the window screen_show_window(&screen); } - if (!screen_update_frame(&screen, &video_buffer)) { + if (!screen_update_frame(&screen)) { return EVENT_RESULT_CONTINUE; } break; @@ -429,6 +429,8 @@ scrcpy(const struct scrcpy_options *options) { const char *window_title = options->window_title ? options->window_title : device_name; + screen_init(&screen, &video_buffer); + if (!screen_init_rendering(&screen, window_title, frame_size, options->always_on_top, options->window_x, options->window_y, options->window_width, diff --git a/app/src/screen.c b/app/src/screen.c index fae09102db..7698e3cecc 100644 --- a/app/src/screen.c +++ b/app/src/screen.c @@ -191,8 +191,9 @@ screen_update_content_rect(struct screen *screen) { } void -screen_init(struct screen *screen) { +screen_init(struct screen *screen, struct video_buffer *vb) { *screen = (struct screen) SCREEN_INITIALIZER; + screen->vb = vb; } static inline SDL_Texture * @@ -449,8 +450,8 @@ update_texture(struct screen *screen, const AVFrame *frame) { } bool -screen_update_frame(struct screen *screen, struct video_buffer *vb) { - const AVFrame *frame = video_buffer_take_rendering_frame(vb); +screen_update_frame(struct screen *screen) { + const AVFrame *frame = video_buffer_take_rendering_frame(screen->vb); struct size new_frame_size = {frame->width, frame->height}; if (!prepare_for_frame(screen, new_frame_size)) { return false; diff --git a/app/src/screen.h b/app/src/screen.h index 35d5df5055..6db52dec51 100644 --- a/app/src/screen.h +++ b/app/src/screen.h @@ -13,6 +13,7 @@ struct video_buffer; struct screen { + struct video_buffer *vb; SDL_Window *window; SDL_Renderer *renderer; SDL_Texture *texture; @@ -37,6 +38,7 @@ struct screen { }; #define SCREEN_INITIALIZER { \ + .vb = NULL, \ .window = NULL, \ .renderer = NULL, \ .texture = NULL, \ @@ -70,7 +72,7 @@ struct screen { // initialize default values void -screen_init(struct screen *screen); +screen_init(struct screen *screen, struct video_buffer *vb); // initialize screen, create window, renderer and texture (window is hidden) // window_x and window_y accept SC_WINDOW_POSITION_UNDEFINED @@ -91,7 +93,7 @@ screen_destroy(struct screen *screen); // resize if necessary and write the rendered frame into the texture bool -screen_update_frame(struct screen *screen, struct video_buffer *vb); +screen_update_frame(struct screen *screen); // render the texture to the renderer // From e1dbe4f71c402a7d56dcdcc8caf5df09bb65c193 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Mon, 15 Feb 2021 18:44:53 +0100 Subject: [PATCH 3/8] Handle screen-related events from screen.c --- app/src/scrcpy.c | 19 ++++--------------- app/src/screen.c | 32 ++++++++++++++++++++++++++++++-- app/src/screen.h | 10 +++------- 3 files changed, 37 insertions(+), 24 deletions(-) diff --git a/app/src/scrcpy.c b/app/src/scrcpy.c index f669ef4093..a150c3ddf0 100644 --- a/app/src/scrcpy.c +++ b/app/src/scrcpy.c @@ -173,21 +173,6 @@ handle_event(SDL_Event *event, const struct scrcpy_options *options) { case SDL_QUIT: LOGD("User requested to quit"); return EVENT_RESULT_STOPPED_BY_USER; - case EVENT_NEW_FRAME: - if (!screen.has_frame) { - screen.has_frame = true; - // this is the very first frame, show the window - screen_show_window(&screen); - } - if (!screen_update_frame(&screen)) { - return EVENT_RESULT_CONTINUE; - } - break; - case SDL_WINDOWEVENT: - if (screen.has_frame) { - screen_handle_window_event(&screen, &event->window); - } - break; case SDL_TEXTINPUT: if (!options->control) { break; @@ -244,6 +229,10 @@ handle_event(SDL_Event *event, const struct scrcpy_options *options) { break; } } + + bool consumed = screen_handle_event(&screen, event); + (void) consumed; + return EVENT_RESULT_CONTINUE; } diff --git a/app/src/screen.c b/app/src/screen.c index 7698e3cecc..65c6180d16 100644 --- a/app/src/screen.c +++ b/app/src/screen.c @@ -4,6 +4,7 @@ #include #include +#include "events.h" #include "icon.xpm" #include "scrcpy.h" #include "tiny_xpm.h" @@ -449,7 +450,7 @@ update_texture(struct screen *screen, const AVFrame *frame) { } } -bool +static bool screen_update_frame(struct screen *screen) { const AVFrame *frame = video_buffer_take_rendering_frame(screen->vb); struct size new_frame_size = {frame->width, frame->height}; @@ -543,7 +544,7 @@ screen_resize_to_pixel_perfect(struct screen *screen) { content_size.height); } -void +static void screen_handle_window_event(struct screen *screen, const SDL_WindowEvent *event) { switch (event->event) { @@ -570,6 +571,33 @@ screen_handle_window_event(struct screen *screen, } } + +bool +screen_handle_event(struct screen *screen, SDL_Event *event) { + switch (event->type) { + case EVENT_NEW_FRAME: + if (!screen->has_frame) { + screen->has_frame = true; + // this is the very first frame, show the window + screen_show_window(screen); + } + bool ok = screen_update_frame(screen); + if (!ok) { + LOGW("Frame update failed\n"); + } + return true; + case SDL_WINDOWEVENT: + if (!screen->has_frame) { + // Do nothing + return true; + } + screen_handle_window_event(screen, &event->window); + return true; + } + + return false; +} + struct point screen_convert_drawable_to_frame_coords(struct screen *screen, int32_t x, int32_t y) { diff --git a/app/src/screen.h b/app/src/screen.h index 6db52dec51..171717abae 100644 --- a/app/src/screen.h +++ b/app/src/screen.h @@ -91,10 +91,6 @@ screen_show_window(struct screen *screen); void screen_destroy(struct screen *screen); -// resize if necessary and write the rendered frame into the texture -bool -screen_update_frame(struct screen *screen); - // render the texture to the renderer // // Set the update_content_rect flag if the window or content size may have @@ -118,9 +114,9 @@ screen_resize_to_pixel_perfect(struct screen *screen); void screen_set_rotation(struct screen *screen, unsigned rotation); -// react to window events -void -screen_handle_window_event(struct screen *screen, const SDL_WindowEvent *event); +// react to SDL events +bool +screen_handle_event(struct screen *screen, SDL_Event *event); // convert point from window coordinates to frame coordinates // x and y are expressed in pixels From 98628f25b5def57a89362d77ecbdebacfec94f62 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Mon, 15 Feb 2021 18:47:17 +0100 Subject: [PATCH 4/8] Inline window events handling Now that all screen-related events are handled from screen.c, there is no need for a separate method for window events. --- app/src/screen.c | 52 +++++++++++++++++++++--------------------------- 1 file changed, 23 insertions(+), 29 deletions(-) diff --git a/app/src/screen.c b/app/src/screen.c index 65c6180d16..1174593dfa 100644 --- a/app/src/screen.c +++ b/app/src/screen.c @@ -544,34 +544,6 @@ screen_resize_to_pixel_perfect(struct screen *screen) { content_size.height); } -static void -screen_handle_window_event(struct screen *screen, - const SDL_WindowEvent *event) { - switch (event->event) { - case SDL_WINDOWEVENT_EXPOSED: - screen_render(screen, true); - break; - case SDL_WINDOWEVENT_SIZE_CHANGED: - screen_render(screen, true); - break; - case SDL_WINDOWEVENT_MAXIMIZED: - screen->maximized = true; - break; - case SDL_WINDOWEVENT_RESTORED: - if (screen->fullscreen) { - // On Windows, in maximized+fullscreen, disabling fullscreen - // mode unexpectedly triggers the "restored" then "maximized" - // events, leaving the window in a weird state (maximized - // according to the events, but not maximized visually). - break; - } - screen->maximized = false; - apply_pending_resize(screen); - break; - } -} - - bool screen_handle_event(struct screen *screen, SDL_Event *event) { switch (event->type) { @@ -591,7 +563,29 @@ screen_handle_event(struct screen *screen, SDL_Event *event) { // Do nothing return true; } - screen_handle_window_event(screen, &event->window); + switch (event->window.event) { + case SDL_WINDOWEVENT_EXPOSED: + screen_render(screen, true); + break; + case SDL_WINDOWEVENT_SIZE_CHANGED: + screen_render(screen, true); + break; + case SDL_WINDOWEVENT_MAXIMIZED: + screen->maximized = true; + break; + case SDL_WINDOWEVENT_RESTORED: + if (screen->fullscreen) { + // On Windows, in maximized+fullscreen, disabling + // fullscreen mode unexpectedly triggers the "restored" + // then "maximized" events, leaving the window in a + // weird state (maximized according to the events, but + // not maximized visually). + break; + } + screen->maximized = false; + apply_pending_resize(screen); + break; + } return true; } From 5c4c28c9736a142b5e8b355901eda562779c88d8 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Mon, 15 Feb 2021 18:53:23 +0100 Subject: [PATCH 5/8] Handle im-related events from input_manager.c --- app/src/input_manager.c | 55 ++++++++++++++++++++++++++++++++++++----- app/src/input_manager.h | 25 ++----------------- app/src/scrcpy.c | 41 +++++------------------------- 3 files changed, 57 insertions(+), 64 deletions(-) diff --git a/app/src/input_manager.c b/app/src/input_manager.c index fd780ae6c3..20ea05c589 100644 --- a/app/src/input_manager.c +++ b/app/src/input_manager.c @@ -286,7 +286,7 @@ rotate_client_right(struct screen *screen) { screen_set_rotation(screen, new_rotation); } -void +static void input_manager_process_text_input(struct input_manager *im, const SDL_TextInputEvent *event) { if (is_shortcut_mod(im, SDL_GetModState())) { @@ -366,7 +366,7 @@ convert_input_key(const SDL_KeyboardEvent *from, struct control_msg *to, return true; } -void +static void input_manager_process_key(struct input_manager *im, const SDL_KeyboardEvent *event) { // control: indicates the state of the command-line option --no-control @@ -551,7 +551,7 @@ convert_mouse_motion(const SDL_MouseMotionEvent *from, struct screen *screen, return true; } -void +static void input_manager_process_mouse_motion(struct input_manager *im, const SDL_MouseMotionEvent *event) { if (!event->state) { @@ -605,7 +605,7 @@ convert_touch(const SDL_TouchFingerEvent *from, struct screen *screen, return true; } -void +static void input_manager_process_touch(struct input_manager *im, const SDL_TouchFingerEvent *event) { struct control_msg msg; @@ -637,7 +637,7 @@ convert_mouse_button(const SDL_MouseButtonEvent *from, struct screen *screen, return true; } -void +static void input_manager_process_mouse_button(struct input_manager *im, const SDL_MouseButtonEvent *event) { bool control = im->control; @@ -736,7 +736,7 @@ convert_mouse_wheel(const SDL_MouseWheelEvent *from, struct screen *screen, return true; } -void +static void input_manager_process_mouse_wheel(struct input_manager *im, const SDL_MouseWheelEvent *event) { struct control_msg msg; @@ -746,3 +746,46 @@ input_manager_process_mouse_wheel(struct input_manager *im, } } } + +bool +input_manager_handle_event(struct input_manager *im, SDL_Event *event) { + switch (event->type) { + case SDL_TEXTINPUT: + if (!im->control) { + return true; + } + input_manager_process_text_input(im, &event->text); + return true; + case SDL_KEYDOWN: + case SDL_KEYUP: + // some key events do not interact with the device, so process the + // event even if control is disabled + input_manager_process_key(im, &event->key); + return true; + case SDL_MOUSEMOTION: + if (!im->control) { + break; + } + input_manager_process_mouse_motion(im, &event->motion); + return true; + case SDL_MOUSEWHEEL: + if (!im->control) { + break; + } + input_manager_process_mouse_wheel(im, &event->wheel); + return true; + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + // some mouse events do not interact with the device, so process + // the event even if control is disabled + input_manager_process_mouse_button(im, &event->button); + return true; + case SDL_FINGERMOTION: + case SDL_FINGERDOWN: + case SDL_FINGERUP: + input_manager_process_touch(im, &event->tfinger); + return true; + } + + return false; +} diff --git a/app/src/input_manager.h b/app/src/input_manager.h index a23a731d24..6acb354d13 100644 --- a/app/src/input_manager.h +++ b/app/src/input_manager.h @@ -40,28 +40,7 @@ void input_manager_init(struct input_manager *im, const struct scrcpy_options *options); -void -input_manager_process_text_input(struct input_manager *im, - const SDL_TextInputEvent *event); - -void -input_manager_process_key(struct input_manager *im, - const SDL_KeyboardEvent *event); - -void -input_manager_process_mouse_motion(struct input_manager *im, - const SDL_MouseMotionEvent *event); - -void -input_manager_process_touch(struct input_manager *im, - const SDL_TouchFingerEvent *event); - -void -input_manager_process_mouse_button(struct input_manager *im, - const SDL_MouseButtonEvent *event); - -void -input_manager_process_mouse_wheel(struct input_manager *im, - const SDL_MouseWheelEvent *event); +bool +input_manager_handle_event(struct input_manager *im, SDL_Event *event); #endif diff --git a/app/src/scrcpy.c b/app/src/scrcpy.c index a150c3ddf0..d24bba2c70 100644 --- a/app/src/scrcpy.c +++ b/app/src/scrcpy.c @@ -173,41 +173,6 @@ handle_event(SDL_Event *event, const struct scrcpy_options *options) { case SDL_QUIT: LOGD("User requested to quit"); return EVENT_RESULT_STOPPED_BY_USER; - case SDL_TEXTINPUT: - if (!options->control) { - break; - } - input_manager_process_text_input(&input_manager, &event->text); - break; - case SDL_KEYDOWN: - case SDL_KEYUP: - // some key events do not interact with the device, so process the - // event even if control is disabled - input_manager_process_key(&input_manager, &event->key); - break; - case SDL_MOUSEMOTION: - if (!options->control) { - break; - } - input_manager_process_mouse_motion(&input_manager, &event->motion); - break; - case SDL_MOUSEWHEEL: - if (!options->control) { - break; - } - input_manager_process_mouse_wheel(&input_manager, &event->wheel); - break; - case SDL_MOUSEBUTTONDOWN: - case SDL_MOUSEBUTTONUP: - // some mouse events do not interact with the device, so process - // the event even if control is disabled - input_manager_process_mouse_button(&input_manager, &event->button); - break; - case SDL_FINGERMOTION: - case SDL_FINGERDOWN: - case SDL_FINGERUP: - input_manager_process_touch(&input_manager, &event->tfinger); - break; case SDL_DROPFILE: { if (!options->control) { break; @@ -231,8 +196,14 @@ handle_event(SDL_Event *event, const struct scrcpy_options *options) { } bool consumed = screen_handle_event(&screen, event); + if (consumed) { + goto end; + } + + consumed = input_manager_handle_event(&input_manager, event); (void) consumed; +end: return EVENT_RESULT_CONTINUE; } From dce08677375dbb1b65217c6f4080b012284f4afa Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Tue, 16 Feb 2021 23:26:35 +0100 Subject: [PATCH 6/8] Enable NDEBUG via Meson built-in option --- app/meson.build | 3 --- meson.build | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/app/meson.build b/app/meson.build index 1f50209e98..75a2fd7101 100644 --- a/app/meson.build +++ b/app/meson.build @@ -99,9 +99,6 @@ foreach f : check_functions endif endforeach -# expose the build type -conf.set('NDEBUG', get_option('buildtype') != 'debug') - # the version, updated on release conf.set_quoted('SCRCPY_VERSION', meson.project_version()) diff --git a/meson.build b/meson.build index 230f8d21ce..c2989ec75e 100644 --- a/meson.build +++ b/meson.build @@ -4,6 +4,7 @@ project('scrcpy', 'c', default_options: [ 'c_std=c11', 'warning_level=2', + 'b_ndebug=if-release', ]) if get_option('compile_app') From 9cb8c929f0da45283f7bdef46e91ff02ddff6c19 Mon Sep 17 00:00:00 2001 From: Yu-Chen Lin Date: Sun, 21 Feb 2021 08:31:37 +0800 Subject: [PATCH 7/8] Export method to power off screen in Device Signed-off-by: Yu-Chen Lin --- .../java/com/genymobile/scrcpy/Device.java | 40 +++++++++++++++++-- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/server/src/main/java/com/genymobile/scrcpy/Device.java b/server/src/main/java/com/genymobile/scrcpy/Device.java index a8fdf677eb..624c9fa0d3 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Device.java +++ b/server/src/main/java/com/genymobile/scrcpy/Device.java @@ -153,13 +153,17 @@ public static String getDeviceName() { return Build.MODEL; } + public static boolean supportsInputEvents(int displayId) { + return displayId == 0 || Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q; + } + public boolean supportsInputEvents() { return supportsInputEvents; } - public boolean injectEvent(InputEvent inputEvent, int mode) { - if (!supportsInputEvents()) { - throw new AssertionError("Could not inject input event if !supportsInputEvents()"); + public static boolean injectEvent(InputEvent inputEvent, int mode, int displayId) { + if (!supportsInputEvents(displayId)) { + return false; } if (displayId != 0 && !InputManager.setDisplayId(inputEvent, displayId)) { @@ -169,10 +173,29 @@ public boolean injectEvent(InputEvent inputEvent, int mode) { return SERVICE_MANAGER.getInputManager().injectInputEvent(inputEvent, mode); } + public boolean injectEvent(InputEvent inputEvent, int mode) { + if (!supportsInputEvents()) { + throw new AssertionError("Could not inject input event if !supportsInputEvents()"); + } + + return injectEvent(inputEvent, mode, displayId); + } + + public static boolean injectEventOnDisplay(InputEvent event, int displayId) { + return injectEvent(event, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC, displayId); + } + public boolean injectEvent(InputEvent event) { return injectEvent(event, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); } + public static boolean injectKeyEvent(int action, int keyCode, int repeat, int metaState, int displayId) { + long now = SystemClock.uptimeMillis(); + KeyEvent event = new KeyEvent(now, now, action, keyCode, repeat, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, + InputDevice.SOURCE_KEYBOARD); + return injectEventOnDisplay(event, displayId); + } + public boolean injectKeyEvent(int action, int keyCode, int repeat, int metaState) { long now = SystemClock.uptimeMillis(); KeyEvent event = new KeyEvent(now, now, action, keyCode, repeat, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, @@ -180,6 +203,10 @@ public boolean injectKeyEvent(int action, int keyCode, int repeat, int metaState return injectEvent(event); } + public static boolean injectKeycode(int keyCode, int displayId) { + return injectKeyEvent(KeyEvent.ACTION_DOWN, keyCode, 0, 0, displayId) && injectKeyEvent(KeyEvent.ACTION_UP, keyCode, 0, 0, displayId); + } + public boolean injectKeycode(int keyCode) { return injectKeyEvent(KeyEvent.ACTION_DOWN, keyCode, 0, 0) && injectKeyEvent(KeyEvent.ACTION_UP, keyCode, 0, 0); } @@ -249,6 +276,13 @@ public static boolean setScreenPowerMode(int mode) { return SurfaceControl.setDisplayPowerMode(d, mode); } + public static boolean powerOffScreen(int displayId) { + if (!isScreenOn()) { + return true; + } + return injectKeycode(KeyEvent.KEYCODE_POWER, displayId); + } + /** * Disable auto-rotation (if enabled), set the screen rotation and re-enable auto-rotation (if it was enabled). */ From a9bd612cd3937d73b81b159bc34c9eb83230dc2c Mon Sep 17 00:00:00 2001 From: Yu-Chen Lin Date: Sun, 21 Feb 2021 08:42:04 +0800 Subject: [PATCH 8/8] Support power off on close Signed-off-by: Yu-Chen Lin --- app/src/cli.c | 6 +++++ app/src/scrcpy.c | 1 + app/src/scrcpy.h | 2 ++ app/src/server.c | 1 + app/src/server.h | 1 + .../java/com/genymobile/scrcpy/CleanUp.java | 27 +++++++++++++------ .../java/com/genymobile/scrcpy/Options.java | 9 +++++++ .../java/com/genymobile/scrcpy/Server.java | 7 +++-- 8 files changed, 44 insertions(+), 10 deletions(-) diff --git a/app/src/cli.c b/app/src/cli.c index fbdef07fb4..acbd280b67 100644 --- a/app/src/cli.c +++ b/app/src/cli.c @@ -667,6 +667,7 @@ guess_record_format(const char *filename) { #define OPT_FORWARD_ALL_CLICKS 1023 #define OPT_LEGACY_PASTE 1024 #define OPT_ENCODER_NAME 1025 +#define OPT_POWER_OFF_ON_CLOSE 1026 bool scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) { @@ -717,6 +718,8 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) { {"window-height", required_argument, NULL, OPT_WINDOW_HEIGHT}, {"window-borderless", no_argument, NULL, OPT_WINDOW_BORDERLESS}, + {"power-off-on-close", no_argument, NULL, + OPT_POWER_OFF_ON_CLOSE}, {NULL, 0, NULL, 0 }, }; @@ -885,6 +888,9 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) { case OPT_LEGACY_PASTE: opts->legacy_paste = true; break; + case OPT_POWER_OFF_ON_CLOSE: + opts->power_off_on_close = true; + break; default: // getopt prints the error message on stderr return false; diff --git a/app/src/scrcpy.c b/app/src/scrcpy.c index d24bba2c70..3e9095cb5d 100644 --- a/app/src/scrcpy.c +++ b/app/src/scrcpy.c @@ -299,6 +299,7 @@ scrcpy(const struct scrcpy_options *options) { .codec_options = options->codec_options, .encoder_name = options->encoder_name, .force_adb_forward = options->force_adb_forward, + .power_off_on_close = options->power_off_on_close, }; if (!server_start(&server, options->serial, ¶ms)) { goto end; diff --git a/app/src/scrcpy.h b/app/src/scrcpy.h index 2253cc284a..1558cfbb8c 100644 --- a/app/src/scrcpy.h +++ b/app/src/scrcpy.h @@ -82,6 +82,7 @@ struct scrcpy_options { bool forward_key_repeat; bool forward_all_clicks; bool legacy_paste; + bool power_off_on_close; }; #define SCRCPY_OPTIONS_DEFAULT { \ @@ -129,6 +130,7 @@ struct scrcpy_options { .forward_key_repeat = true, \ .forward_all_clicks = false, \ .legacy_paste = false, \ + .power_off_on_close = false, \ } bool diff --git a/app/src/server.c b/app/src/server.c index 096ac18fc3..a0b40f965f 100644 --- a/app/src/server.c +++ b/app/src/server.c @@ -293,6 +293,7 @@ execute_server(struct server *server, const struct server_params *params) { params->stay_awake ? "true" : "false", params->codec_options ? params->codec_options : "-", params->encoder_name ? params->encoder_name : "-", + params->power_off_on_close ? "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 83c528efef..15306e4fde 100644 --- a/app/src/server.h +++ b/app/src/server.h @@ -46,6 +46,7 @@ struct server_params { bool show_touches; bool stay_awake; bool force_adb_forward; + bool power_off_on_close; }; // 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 efaa059aa8..2601240931 100644 --- a/server/src/main/java/com/genymobile/scrcpy/CleanUp.java +++ b/server/src/main/java/com/genymobile/scrcpy/CleanUp.java @@ -19,19 +19,25 @@ private CleanUp() { // not instantiable } - public static void configure(boolean disableShowTouches, int restoreStayOn, boolean restoreNormalPowerMode) throws IOException { - boolean needProcess = disableShowTouches || restoreStayOn != -1 || restoreNormalPowerMode; + public static void configure(boolean disableShowTouches, + int restoreStayOn, + boolean restoreNormalPowerMode, + boolean powerOffScreen, int displayId) throws IOException { + boolean needProcess = disableShowTouches || restoreStayOn != -1 || restoreNormalPowerMode || powerOffScreen; if (needProcess) { - startProcess(disableShowTouches, restoreStayOn, restoreNormalPowerMode); + startProcess(disableShowTouches, restoreStayOn, restoreNormalPowerMode, powerOffScreen, displayId); } else { // There is no additional clean up to do when scrcpy dies unlinkSelf(); } } - private static void startProcess(boolean disableShowTouches, int restoreStayOn, boolean restoreNormalPowerMode) throws IOException { + private static void startProcess(boolean disableShowTouches, + int restoreStayOn, + boolean restoreNormalPowerMode, + boolean powerOffScreen, int displayId) throws IOException { String[] cmd = {"app_process", "/", CleanUp.class.getName(), String.valueOf(disableShowTouches), String.valueOf( - restoreStayOn), String.valueOf(restoreNormalPowerMode)}; + restoreStayOn), String.valueOf(restoreNormalPowerMode), String.valueOf(powerOffScreen), String.valueOf(displayId)}; ProcessBuilder builder = new ProcessBuilder(cmd); builder.environment().put("CLASSPATH", SERVER_PATH); @@ -61,6 +67,8 @@ public static void main(String... args) { boolean disableShowTouches = Boolean.parseBoolean(args[0]); int restoreStayOn = Integer.parseInt(args[1]); boolean restoreNormalPowerMode = Boolean.parseBoolean(args[2]); + boolean powerOffScreen = Boolean.parseBoolean(args[3]); + int displayId = Integer.parseInt(args[4]); if (disableShowTouches || restoreStayOn != -1) { ServiceManager serviceManager = new ServiceManager(); @@ -76,9 +84,12 @@ public static void main(String... args) { } } - if (restoreNormalPowerMode) { - Ln.i("Restoring normal power mode"); - if (Device.isScreenOn()) { + if (Device.isScreenOn()) { + if (powerOffScreen) { + Ln.i("Power off screen"); + Device.powerOffScreen(displayId); + } else if (restoreNormalPowerMode) { + Ln.i("Restoring normal power mode"); Device.setScreenPowerMode(Device.POWER_MODE_NORMAL); } } diff --git a/server/src/main/java/com/genymobile/scrcpy/Options.java b/server/src/main/java/com/genymobile/scrcpy/Options.java index 150d06a83d..cf11df0f19 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Options.java +++ b/server/src/main/java/com/genymobile/scrcpy/Options.java @@ -17,6 +17,7 @@ public class Options { private boolean stayAwake; private String codecOptions; private String encoderName; + private boolean powerOffScreenOnClose; public Ln.Level getLogLevel() { return logLevel; @@ -129,4 +130,12 @@ public String getEncoderName() { public void setEncoderName(String encoderName) { this.encoderName = encoderName; } + + public void setPowerOffScreenOnClose(boolean powerOffScreenOnClose) { + this.powerOffScreenOnClose = powerOffScreenOnClose; + } + + public boolean getPowerOffScreenOnClose() { + return this.powerOffScreenOnClose; + } } diff --git a/server/src/main/java/com/genymobile/scrcpy/Server.java b/server/src/main/java/com/genymobile/scrcpy/Server.java index f58e38674d..289f6a9f71 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Server.java +++ b/server/src/main/java/com/genymobile/scrcpy/Server.java @@ -50,7 +50,7 @@ private static void scrcpy(Options options) throws IOException { } } - CleanUp.configure(mustDisableShowTouchesOnCleanUp, restoreStayOn, true); + CleanUp.configure(mustDisableShowTouchesOnCleanUp, restoreStayOn, true, options.getPowerOffScreenOnClose(), options.getDisplayId()); boolean tunnelForward = options.isTunnelForward(); @@ -135,7 +135,7 @@ private static Options createOptions(String... args) { "The server version (" + BuildConfig.VERSION_NAME + ") does not match the client " + "(" + clientVersion + ")"); } - final int expectedParameters = 15; + final int expectedParameters = 16; if (args.length != expectedParameters) { throw new IllegalArgumentException("Expecting " + expectedParameters + " parameters"); } @@ -185,6 +185,9 @@ private static Options createOptions(String... args) { String encoderName = "-".equals(args[14]) ? null : args[14]; options.setEncoderName(encoderName); + boolean powerOffScreenOnClose = Boolean.parseBoolean(args[15]); + options.setPowerOffScreenOnClose(powerOffScreenOnClose); + return options; }