diff --git a/app/scrcpy.1 b/app/scrcpy.1 index 1e3c91b1..eb09f530 100644 --- a/app/scrcpy.1 +++ b/app/scrcpy.1 @@ -577,6 +577,14 @@ Flip display horizontally .B MOD+Shift+Up, MOD+Shift+Down Flip display vertically +.TP +.B MOD+z +Pause or re-pause display + +.TP +.B MOD+Shift+z +Unpause display + .TP .B MOD+g Resize window to 1:1 (pixel\-perfect) diff --git a/app/src/cli.c b/app/src/cli.c index daa041cf..92807947 100644 --- a/app/src/cli.c +++ b/app/src/cli.c @@ -900,6 +900,14 @@ static const struct sc_shortcut shortcuts[] = { .shortcuts = { "MOD+Shift+Up", "MOD+Shift+Down" }, .text = "Flip display vertically", }, + { + .shortcuts = { "MOD+z" }, + .text = "Pause or re-pause display", + }, + { + .shortcuts = { "MOD+Shift+z" }, + .text = "Unpause display", + }, { .shortcuts = { "MOD+g" }, .text = "Resize window to 1:1 (pixel-perfect)", diff --git a/app/src/input_manager.c b/app/src/input_manager.c index f26c4164..c7a758f4 100644 --- a/app/src/input_manager.c +++ b/app/src/input_manager.c @@ -402,6 +402,7 @@ sc_input_manager_process_key(struct sc_input_manager *im, const SDL_KeyboardEvent *event) { // controller is NULL if --no-control is requested bool control = im->controller; + bool paused = im->screen->paused; SDL_Keycode keycode = event->keysym.sym; uint16_t mod = event->keysym.mod; @@ -427,46 +428,51 @@ sc_input_manager_process_key(struct sc_input_manager *im, enum sc_action action = down ? SC_ACTION_DOWN : SC_ACTION_UP; switch (keycode) { case SDLK_h: - if (im->kp && !shift && !repeat) { + if (im->kp && !shift && !repeat && !paused) { action_home(im, action); } return; case SDLK_b: // fall-through case SDLK_BACKSPACE: - if (im->kp && !shift && !repeat) { + if (im->kp && !shift && !repeat && !paused) { action_back(im, action); } return; case SDLK_s: - if (im->kp && !shift && !repeat) { + if (im->kp && !shift && !repeat && !paused) { action_app_switch(im, action); } return; case SDLK_m: - if (im->kp && !shift && !repeat) { + if (im->kp && !shift && !repeat && !paused) { action_menu(im, action); } return; case SDLK_p: - if (im->kp && !shift && !repeat) { + if (im->kp && !shift && !repeat && !paused) { action_power(im, action); } return; case SDLK_o: - if (control && !repeat && down) { + if (control && !repeat && down && !paused) { enum sc_screen_power_mode mode = shift ? SC_SCREEN_POWER_MODE_NORMAL : SC_SCREEN_POWER_MODE_OFF; set_screen_power_mode(im, mode); } return; + case SDLK_z: + if (down && !repeat) { + sc_screen_set_paused(im->screen, !shift); + } + return; case SDLK_DOWN: if (shift) { if (!repeat & down) { apply_orientation_transform(im, SC_ORIENTATION_FLIP_180); } - } else if (im->kp) { + } else if (im->kp && !paused) { // forward repeated events action_volume_down(im, action); } @@ -477,7 +483,7 @@ sc_input_manager_process_key(struct sc_input_manager *im, apply_orientation_transform(im, SC_ORIENTATION_FLIP_180); } - } else if (im->kp) { + } else if (im->kp && !paused) { // forward repeated events action_volume_up(im, action); } @@ -505,17 +511,17 @@ sc_input_manager_process_key(struct sc_input_manager *im, } return; case SDLK_c: - if (im->kp && !shift && !repeat && down) { + if (im->kp && !shift && !repeat && down && !paused) { get_device_clipboard(im, SC_COPY_KEY_COPY); } return; case SDLK_x: - if (im->kp && !shift && !repeat && down) { + if (im->kp && !shift && !repeat && down && !paused) { get_device_clipboard(im, SC_COPY_KEY_CUT); } return; case SDLK_v: - if (im->kp && !repeat && down) { + if (im->kp && !repeat && down && !paused) { if (shift || im->legacy_paste) { // inject the text as input events clipboard_paste(im); @@ -547,7 +553,7 @@ sc_input_manager_process_key(struct sc_input_manager *im, } return; case SDLK_n: - if (control && !repeat && down) { + if (control && !repeat && down && !paused) { if (shift) { collapse_panels(im); } else if (im->key_repeat == 0) { @@ -558,12 +564,12 @@ sc_input_manager_process_key(struct sc_input_manager *im, } return; case SDLK_r: - if (control && !shift && !repeat && down) { + if (control && !shift && !repeat && down && !paused) { rotate_device(im); } return; case SDLK_k: - if (control && !shift && !repeat && down + if (control && !shift && !repeat && down && !paused && im->kp && im->kp->hid) { // Only if the current keyboard is hid open_hard_keyboard_settings(im); @@ -574,7 +580,7 @@ sc_input_manager_process_key(struct sc_input_manager *im, return; } - if (!im->kp) { + if (!im->kp || paused) { return; } @@ -622,7 +628,6 @@ sc_input_manager_process_key(struct sc_input_manager *im, static void sc_input_manager_process_mouse_motion(struct sc_input_manager *im, const SDL_MouseMotionEvent *event) { - if (event->which == SDL_TOUCH_MOUSEID) { // simulated from touch events, so it's a duplicate return; @@ -695,16 +700,16 @@ sc_input_manager_process_touch(struct sc_input_manager *im, static void sc_input_manager_process_mouse_button(struct sc_input_manager *im, const SDL_MouseButtonEvent *event) { - bool control = im->controller; - if (event->which == SDL_TOUCH_MOUSEID) { // simulated from touch events, so it's a duplicate return; } + bool control = im->controller; + bool paused = im->screen->paused; bool down = event->type == SDL_MOUSEBUTTONDOWN; if (!im->forward_all_clicks) { - if (control) { + if (control && !paused) { enum sc_action action = down ? SC_ACTION_DOWN : SC_ACTION_UP; if (im->kp && event->button == SDL_BUTTON_X1) { @@ -747,7 +752,7 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im, // otherwise, send the click event to the device } - if (!im->mp) { + if (!im->mp || paused) { return; } @@ -885,9 +890,10 @@ void sc_input_manager_handle_event(struct sc_input_manager *im, const SDL_Event *event) { bool control = im->controller; + bool paused = im->screen->paused; switch (event->type) { case SDL_TEXTINPUT: - if (!im->kp) { + if (!im->kp || paused) { break; } sc_input_manager_process_text_input(im, &event->text); @@ -899,13 +905,13 @@ sc_input_manager_handle_event(struct sc_input_manager *im, sc_input_manager_process_key(im, &event->key); break; case SDL_MOUSEMOTION: - if (!im->mp) { + if (!im->mp || paused) { break; } sc_input_manager_process_mouse_motion(im, &event->motion); break; case SDL_MOUSEWHEEL: - if (!im->mp) { + if (!im->mp || paused) { break; } sc_input_manager_process_mouse_wheel(im, &event->wheel); @@ -919,7 +925,7 @@ sc_input_manager_handle_event(struct sc_input_manager *im, case SDL_FINGERMOTION: case SDL_FINGERDOWN: case SDL_FINGERUP: - if (!im->mp) { + if (!im->mp || paused) { break; } sc_input_manager_process_touch(im, &event->tfinger); diff --git a/app/src/screen.c b/app/src/screen.c index 091001bc..351eb3fb 100644 --- a/app/src/screen.c +++ b/app/src/screen.c @@ -362,6 +362,8 @@ sc_screen_init(struct sc_screen *screen, screen->maximized = false; screen->minimized = false; screen->mouse_capture_key_pressed = 0; + screen->paused = false; + screen->resume_frame = NULL; screen->req.x = params->window_x; screen->req.y = params->window_y; @@ -614,13 +616,10 @@ prepare_for_frame(struct sc_screen *screen, struct sc_size new_frame_size) { } static bool -sc_screen_update_frame(struct sc_screen *screen) { - av_frame_unref(screen->frame); - sc_frame_buffer_consume(&screen->fb, screen->frame); - AVFrame *frame = screen->frame; - +sc_screen_apply_frame(struct sc_screen *screen) { sc_fps_counter_add_rendered_frame(&screen->fps_counter); + AVFrame *frame = screen->frame; struct sc_size new_frame_size = {frame->width, frame->height}; enum sc_display_result res = prepare_for_frame(screen, new_frame_size); if (res == SC_DISPLAY_RESULT_ERROR) { @@ -655,6 +654,54 @@ sc_screen_update_frame(struct sc_screen *screen) { return true; } +static bool +sc_screen_update_frame(struct sc_screen *screen) { + if (screen->paused) { + if (!screen->resume_frame) { + screen->resume_frame = av_frame_alloc(); + if (!screen->resume_frame) { + LOG_OOM(); + return false; + } + } else { + av_frame_unref(screen->resume_frame); + } + sc_frame_buffer_consume(&screen->fb, screen->resume_frame); + return true; + } + + av_frame_unref(screen->frame); + sc_frame_buffer_consume(&screen->fb, screen->frame); + return sc_screen_apply_frame(screen); +} + +void +sc_screen_set_paused(struct sc_screen *screen, bool paused) { + if (!paused && !screen->paused) { + // nothing to do + return; + } + + if (screen->paused && screen->resume_frame) { + // If display screen was paused, refresh the frame immediately, even if + // the new state is also paused. + av_frame_free(&screen->frame); + screen->frame = screen->resume_frame; + screen->resume_frame = NULL; + sc_screen_apply_frame(screen); + } + + if (!paused) { + LOGI("Display screen unpaused"); + } else if (!screen->paused) { + LOGI("Display screen paused"); + } else { + LOGI("Display screen re-paused"); + } + + screen->paused = paused; +} + void sc_screen_switch_fullscreen(struct sc_screen *screen) { uint32_t new_mode = screen->fullscreen ? 0 : SDL_WINDOW_FULLSCREEN_DESKTOP; diff --git a/app/src/screen.h b/app/src/screen.h index 46591be5..361ce455 100644 --- a/app/src/screen.h +++ b/app/src/screen.h @@ -64,6 +64,9 @@ struct sc_screen { SDL_Keycode mouse_capture_key_pressed; AVFrame *frame; + + bool paused; + AVFrame *resume_frame; }; struct sc_screen_params { @@ -135,6 +138,10 @@ void sc_screen_set_orientation(struct sc_screen *screen, enum sc_orientation orientation); +// set the display pause state +void +sc_screen_set_paused(struct sc_screen *screen, bool paused); + // react to SDL events // If this function returns false, scrcpy must exit with an error. bool diff --git a/doc/shortcuts.md b/doc/shortcuts.md index 8c402855..d0f6ebec 100644 --- a/doc/shortcuts.md +++ b/doc/shortcuts.md @@ -28,6 +28,8 @@ _[Super] is typically the Windows or Cmd key._ | Rotate display right | MOD+ _(right)_ | Flip display horizontally | MOD+Shift+ _(left)_ \| MOD+Shift+ _(right)_ | Flip display vertically | MOD+Shift+ _(up)_ \| MOD+Shift+ _(down)_ + | Pause or re-pause display | MOD+z + | Unpause display | MOD+Shift+z | Resize window to 1:1 (pixel-perfect) | MOD+g | Resize window to remove black borders | MOD+w \| _Double-left-click¹_ | Click on `HOME` | MOD+h \| _Middle-click_