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_