diff --git a/app/deps/libusb.sh b/app/deps/libusb.sh
index 97fc3c72..26f0140b 100755
--- a/app/deps/libusb.sh
+++ b/app/deps/libusb.sh
@@ -5,9 +5,9 @@ cd "$DEPS_DIR"
. common
VERSION=1.0.27
-FILENAME=libusb-$VERSION.tar.bz2
+FILENAME=libusb-$VERSION.tar.gz
PROJECT_DIR=libusb-$VERSION
-SHA256SUM=ffaa41d741a8a3bee244ac8e54a72ea05bf2879663c098c82fc5757853441575
+SHA256SUM=e8f18a7a36ecbb11fb820bd71540350d8f61bcd9db0d2e8c18a6fb80b214a3de
cd "$SOURCES_DIR"
@@ -15,7 +15,7 @@ if [[ -d "$PROJECT_DIR" ]]
then
echo "$PWD/$PROJECT_DIR" found
else
- get_file "https://github.com/libusb/libusb/releases/download/v$VERSION/libusb-$VERSION.tar.bz2" "$FILENAME" "$SHA256SUM"
+ get_file "https://github.com/libusb/libusb/archive/refs/tags/v$VERSION.tar.gz" "$FILENAME" "$SHA256SUM"
tar xf "$FILENAME" # First level directory is "$PROJECT_DIR"
fi
@@ -33,6 +33,7 @@ else
mkdir "$HOST"
cd "$HOST"
+ "$SOURCES_DIR/$PROJECT_DIR"/bootstrap.sh
"$SOURCES_DIR/$PROJECT_DIR"/configure \
--prefix="$INSTALL_DIR/$HOST" \
--host="$HOST_TRIPLET" \
diff --git a/app/scrcpy.1 b/app/scrcpy.1
index 1e3c91b1..dbdcca28 100644
--- a/app/scrcpy.1
+++ b/app/scrcpy.1
@@ -420,9 +420,9 @@ Turn the device screen off immediately.
.BI "\-\-shortcut\-mod " key\fR[+...]][,...]
Specify the modifiers to use for scrcpy shortcuts. Possible keys are "lctrl", "rctrl", "lalt", "ralt", "lsuper" and "rsuper".
-A shortcut can consist in several keys, separated by '+'. Several shortcuts can be specified, separated by ','.
+Several shortcut modifiers can be specified, separated by ','.
-For example, to use either LCtrl+LAlt or LSuper for scrcpy shortcuts, pass "lctrl+lalt,lsuper".
+For example, to use either LCtrl or LSuper for scrcpy shortcuts, pass "lctrl,lsuper".
Default is "lalt,lsuper" (left-Alt or left-Super).
@@ -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..3115a474 100644
--- a/app/src/cli.c
+++ b/app/src/cli.c
@@ -709,10 +709,10 @@ static const struct sc_option options[] = {
.text = "Specify the modifiers to use for scrcpy shortcuts.\n"
"Possible keys are \"lctrl\", \"rctrl\", \"lalt\", \"ralt\", "
"\"lsuper\" and \"rsuper\".\n"
- "A shortcut can consist in several keys, separated by '+'. "
- "Several shortcuts can be specified, separated by ','.\n"
- "For example, to use either LCtrl+LAlt or LSuper for scrcpy "
- "shortcuts, pass \"lctrl+lalt,lsuper\".\n"
+ "Several shortcut modifiers can be specified, separated by "
+ "','.\n"
+ "For example, to use either LCtrl or LSuper for scrcpy "
+ "shortcuts, pass \"lctrl,lsuper\".\n"
"Default is \"lalt,lsuper\" (left-Alt or left-Super).",
},
{
@@ -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)",
@@ -1672,82 +1680,63 @@ parse_log_level(const char *s, enum sc_log_level *log_level) {
return false;
}
-// item is a list of mod keys separated by '+' (e.g. "lctrl+lalt")
-// returns a bitwise-or of SC_SHORTCUT_MOD_* constants (or 0 on error)
-static unsigned
+static enum sc_shortcut_mod
parse_shortcut_mods_item(const char *item, size_t len) {
- unsigned mod = 0;
-
- for (;;) {
- char *plus = strchr(item, '+');
- // strchr() does not consider the "len" parameter, to it could find an
- // occurrence too far in the string (there is no strnchr())
- bool has_plus = plus && plus < item + len;
-
- assert(!has_plus || plus > item);
- size_t key_len = has_plus ? (size_t) (plus - item) : len;
-
#define STREQ(literal, s, len) \
((sizeof(literal)-1 == len) && !memcmp(literal, s, len))
- if (STREQ("lctrl", item, key_len)) {
- mod |= SC_SHORTCUT_MOD_LCTRL;
- } else if (STREQ("rctrl", item, key_len)) {
- mod |= SC_SHORTCUT_MOD_RCTRL;
- } else if (STREQ("lalt", item, key_len)) {
- mod |= SC_SHORTCUT_MOD_LALT;
- } else if (STREQ("ralt", item, key_len)) {
- mod |= SC_SHORTCUT_MOD_RALT;
- } else if (STREQ("lsuper", item, key_len)) {
- mod |= SC_SHORTCUT_MOD_LSUPER;
- } else if (STREQ("rsuper", item, key_len)) {
- mod |= SC_SHORTCUT_MOD_RSUPER;
- } else {
- LOGE("Unknown modifier key: %.*s "
- "(must be one of: lctrl, rctrl, lalt, ralt, lsuper, rsuper)",
- (int) key_len, item);
- return 0;
- }
+ if (STREQ("lctrl", item, len)) {
+ return SC_SHORTCUT_MOD_LCTRL;
+ }
+ if (STREQ("rctrl", item, len)) {
+ return SC_SHORTCUT_MOD_RCTRL;
+ }
+ if (STREQ("lalt", item, len)) {
+ return SC_SHORTCUT_MOD_LALT;
+ }
+ if (STREQ("ralt", item, len)) {
+ return SC_SHORTCUT_MOD_RALT;
+ }
+ if (STREQ("lsuper", item, len)) {
+ return SC_SHORTCUT_MOD_LSUPER;
+ }
+ if (STREQ("rsuper", item, len)) {
+ return SC_SHORTCUT_MOD_RSUPER;
+ }
#undef STREQ
- if (!has_plus) {
- break;
- }
-
- item = plus + 1;
- assert(len >= key_len + 1);
- len -= key_len + 1;
+ bool has_plus = strchr(item, '+');
+ if (has_plus) {
+ LOGE("Shortcut mod combination with '+' is not supported anymore: "
+ "'%.*s' (see #4741)", (int) len, item);
+ return 0;
}
- return mod;
+ LOGE("Unknown modifier key: %.*s "
+ "(must be one of: lctrl, rctrl, lalt, ralt, lsuper, rsuper)",
+ (int) len, item);
+
+ return 0;
}
static bool
-parse_shortcut_mods(const char *s, struct sc_shortcut_mods *mods) {
- unsigned count = 0;
- unsigned current = 0;
+parse_shortcut_mods(const char *s, uint8_t *shortcut_mods) {
+ uint8_t mods = 0;
- // LCtrl+LAlt or RCtrl or LCtrl+RSuper: "lctrl+lalt,rctrl,lctrl+rsuper"
+ // A list of shortcut modifiers, for example "lctrl,rctrl,rsuper"
for (;;) {
char *comma = strchr(s, ',');
- if (comma && count == SC_MAX_SHORTCUT_MODS - 1) {
- assert(count < SC_MAX_SHORTCUT_MODS);
- LOGW("Too many shortcut modifiers alternatives");
- return false;
- }
-
assert(!comma || comma > s);
size_t limit = comma ? (size_t) (comma - s) : strlen(s);
- unsigned mod = parse_shortcut_mods_item(s, limit);
+ enum sc_shortcut_mod mod = parse_shortcut_mods_item(s, limit);
if (!mod) {
LOGE("Invalid modifier keys: %.*s", (int) limit, s);
return false;
}
- mods->data[current++] = mod;
- ++count;
+ mods |= mod;
if (!comma) {
break;
@@ -1756,7 +1745,7 @@ parse_shortcut_mods(const char *s, struct sc_shortcut_mods *mods) {
s = comma + 1;
}
- mods->count = count;
+ *shortcut_mods = mods;
return true;
}
@@ -1764,7 +1753,7 @@ parse_shortcut_mods(const char *s, struct sc_shortcut_mods *mods) {
#ifdef SC_TEST
// expose the function to unit-tests
bool
-sc_parse_shortcut_mods(const char *s, struct sc_shortcut_mods *mods) {
+sc_parse_shortcut_mods(const char *s, uint8_t *mods) {
return parse_shortcut_mods(s, mods);
}
#endif
diff --git a/app/src/cli.h b/app/src/cli.h
index 23d34fcd..6fd579a4 100644
--- a/app/src/cli.h
+++ b/app/src/cli.h
@@ -28,7 +28,7 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]);
#ifdef SC_TEST
bool
-sc_parse_shortcut_mods(const char *s, struct sc_shortcut_mods *mods);
+sc_parse_shortcut_mods(const char *s, uint8_t *shortcut_mods);
#endif
#endif
diff --git a/app/src/input_manager.c b/app/src/input_manager.c
index f26c4164..8d078523 100644
--- a/app/src/input_manager.c
+++ b/app/src/input_manager.c
@@ -10,7 +10,7 @@
#define SC_SDL_SHORTCUT_MODS_MASK (KMOD_CTRL | KMOD_ALT | KMOD_GUI)
static inline uint16_t
-to_sdl_mod(unsigned shortcut_mod) {
+to_sdl_mod(uint8_t shortcut_mod) {
uint16_t sdl_mod = 0;
if (shortcut_mod & SC_SHORTCUT_MOD_LCTRL) {
sdl_mod |= KMOD_LCTRL;
@@ -38,15 +38,18 @@ is_shortcut_mod(struct sc_input_manager *im, uint16_t sdl_mod) {
// keep only the relevant modifier keys
sdl_mod &= SC_SDL_SHORTCUT_MODS_MASK;
- assert(im->sdl_shortcut_mods.count);
- assert(im->sdl_shortcut_mods.count < SC_MAX_SHORTCUT_MODS);
- for (unsigned i = 0; i < im->sdl_shortcut_mods.count; ++i) {
- if (im->sdl_shortcut_mods.data[i] == sdl_mod) {
- return true;
- }
- }
+ // at least one shortcut mod pressed?
+ return sdl_mod & im->sdl_shortcut_mods;
+}
- return false;
+static bool
+is_shortcut_key(struct sc_input_manager *im, SDL_Keycode keycode) {
+ return (im->sdl_shortcut_mods & KMOD_LCTRL && keycode == SDLK_LCTRL)
+ || (im->sdl_shortcut_mods & KMOD_RCTRL && keycode == SDLK_RCTRL)
+ || (im->sdl_shortcut_mods & KMOD_LALT && keycode == SDLK_LALT)
+ || (im->sdl_shortcut_mods & KMOD_RALT && keycode == SDLK_RALT)
+ || (im->sdl_shortcut_mods & KMOD_LGUI && keycode == SDLK_LGUI)
+ || (im->sdl_shortcut_mods & KMOD_RGUI && keycode == SDLK_RGUI);
}
void
@@ -68,15 +71,7 @@ sc_input_manager_init(struct sc_input_manager *im,
im->legacy_paste = params->legacy_paste;
im->clipboard_autosync = params->clipboard_autosync;
- const struct sc_shortcut_mods *shortcut_mods = params->shortcut_mods;
- assert(shortcut_mods->count);
- assert(shortcut_mods->count < SC_MAX_SHORTCUT_MODS);
- for (unsigned i = 0; i < shortcut_mods->count; ++i) {
- uint16_t sdl_mod = to_sdl_mod(shortcut_mods->data[i]);
- assert(sdl_mod);
- im->sdl_shortcut_mods.data[i] = sdl_mod;
- }
- im->sdl_shortcut_mods.count = shortcut_mods->count;
+ im->sdl_shortcut_mods = to_sdl_mod(params->shortcut_mods);
im->vfinger_down = false;
im->vfinger_invert_x = false;
@@ -402,6 +397,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;
@@ -410,7 +406,12 @@ sc_input_manager_process_key(struct sc_input_manager *im,
bool shift = event->keysym.mod & KMOD_SHIFT;
bool repeat = event->repeat;
- bool smod = is_shortcut_mod(im, mod);
+ // Either the modifier includes a shortcut modifier, or the key
+ // press/release is a modifier key.
+ // The second condition is necessary to ignore the release of the modifier
+ // key (because in this case mod is 0).
+ bool is_shortcut = is_shortcut_mod(im, mod)
+ || is_shortcut_key(im, keycode);
if (down && !repeat) {
if (keycode == im->last_keycode && mod == im->last_mod) {
@@ -422,51 +423,55 @@ sc_input_manager_process_key(struct sc_input_manager *im,
}
}
- // The shortcut modifier is pressed
- if (smod) {
+ if (is_shortcut) {
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 +482,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 +510,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 +552,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 +563,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 +579,7 @@ sc_input_manager_process_key(struct sc_input_manager *im,
return;
}
- if (!im->kp) {
+ if (!im->kp || paused) {
return;
}
@@ -622,7 +627,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 +699,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 +751,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 +889,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 +904,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 +924,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/input_manager.h b/app/src/input_manager.h
index 2ce11b03..8c45c165 100644
--- a/app/src/input_manager.h
+++ b/app/src/input_manager.h
@@ -26,10 +26,7 @@ struct sc_input_manager {
bool legacy_paste;
bool clipboard_autosync;
- struct {
- unsigned data[SC_MAX_SHORTCUT_MODS];
- unsigned count;
- } sdl_shortcut_mods;
+ uint16_t sdl_shortcut_mods;
bool vfinger_down;
bool vfinger_invert_x;
@@ -55,7 +52,7 @@ struct sc_input_manager_params {
bool forward_all_clicks;
bool legacy_paste;
bool clipboard_autosync;
- const struct sc_shortcut_mods *shortcut_mods;
+ uint8_t shortcut_mods; // OR of enum sc_shortcut_mod values
};
void
diff --git a/app/src/options.c b/app/src/options.c
index 7a885aa5..22e8547a 100644
--- a/app/src/options.c
+++ b/app/src/options.c
@@ -30,10 +30,7 @@ const struct scrcpy_options scrcpy_options_default = {
},
.tunnel_host = 0,
.tunnel_port = 0,
- .shortcut_mods = {
- .data = {SC_SHORTCUT_MOD_LALT, SC_SHORTCUT_MOD_LSUPER},
- .count = 2,
- },
+ .shortcut_mods = SC_SHORTCUT_MOD_LALT | SC_SHORTCUT_MOD_LSUPER,
.max_size = 0,
.video_bit_rate = 0,
.audio_bit_rate = 0,
diff --git a/app/src/options.h b/app/src/options.h
index 5445e7c8..45f0c49f 100644
--- a/app/src/options.h
+++ b/app/src/options.h
@@ -169,8 +169,6 @@ enum sc_key_inject_mode {
SC_KEY_INJECT_MODE_RAW,
};
-#define SC_MAX_SHORTCUT_MODS 8
-
enum sc_shortcut_mod {
SC_SHORTCUT_MOD_LCTRL = 1 << 0,
SC_SHORTCUT_MOD_RCTRL = 1 << 1,
@@ -180,11 +178,6 @@ enum sc_shortcut_mod {
SC_SHORTCUT_MOD_RSUPER = 1 << 5,
};
-struct sc_shortcut_mods {
- unsigned data[SC_MAX_SHORTCUT_MODS];
- unsigned count;
-};
-
struct sc_port_range {
uint16_t first;
uint16_t last;
@@ -219,7 +212,7 @@ struct scrcpy_options {
struct sc_port_range port_range;
uint32_t tunnel_host;
uint16_t tunnel_port;
- struct sc_shortcut_mods shortcut_mods;
+ uint8_t shortcut_mods; // OR of enum sc_shortcut_mod values
uint16_t max_size;
uint32_t video_bit_rate;
uint32_t audio_bit_rate;
diff --git a/app/src/scrcpy.c b/app/src/scrcpy.c
index f43af35e..b8d87b60 100644
--- a/app/src/scrcpy.c
+++ b/app/src/scrcpy.c
@@ -696,7 +696,7 @@ scrcpy(struct scrcpy_options *options) {
.forward_all_clicks = options->forward_all_clicks,
.legacy_paste = options->legacy_paste,
.clipboard_autosync = options->clipboard_autosync,
- .shortcut_mods = &options->shortcut_mods,
+ .shortcut_mods = options->shortcut_mods,
.window_title = window_title,
.always_on_top = options->always_on_top,
.window_x = options->window_x,
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..2079d262 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 {
@@ -75,7 +78,7 @@ struct sc_screen_params {
bool forward_all_clicks;
bool legacy_paste;
bool clipboard_autosync;
- const struct sc_shortcut_mods *shortcut_mods;
+ uint8_t shortcut_mods; // OR of enum sc_shortcut_mod values
const char *window_title;
bool always_on_top;
@@ -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/app/tests/test_cli.c b/app/tests/test_cli.c
index f2a17272..cef8df3e 100644
--- a/app/tests/test_cli.c
+++ b/app/tests/test_cli.c
@@ -124,32 +124,22 @@ static void test_options2(void) {
}
static void test_parse_shortcut_mods(void) {
- struct sc_shortcut_mods mods;
+ uint8_t mods;
bool ok;
ok = sc_parse_shortcut_mods("lctrl", &mods);
assert(ok);
- assert(mods.count == 1);
- assert(mods.data[0] == SC_SHORTCUT_MOD_LCTRL);
-
- ok = sc_parse_shortcut_mods("lctrl+lalt", &mods);
- assert(ok);
- assert(mods.count == 1);
- assert(mods.data[0] == (SC_SHORTCUT_MOD_LCTRL | SC_SHORTCUT_MOD_LALT));
+ assert(mods == SC_SHORTCUT_MOD_LCTRL);
ok = sc_parse_shortcut_mods("rctrl,lalt", &mods);
assert(ok);
- assert(mods.count == 2);
- assert(mods.data[0] == SC_SHORTCUT_MOD_RCTRL);
- assert(mods.data[1] == SC_SHORTCUT_MOD_LALT);
+ assert(mods == (SC_SHORTCUT_MOD_RCTRL | SC_SHORTCUT_MOD_LALT));
- ok = sc_parse_shortcut_mods("lsuper,rsuper+lalt,lctrl+rctrl+ralt", &mods);
+ ok = sc_parse_shortcut_mods("lsuper,rsuper,lctrl", &mods);
assert(ok);
- assert(mods.count == 3);
- assert(mods.data[0] == SC_SHORTCUT_MOD_LSUPER);
- assert(mods.data[1] == (SC_SHORTCUT_MOD_RSUPER | SC_SHORTCUT_MOD_LALT));
- assert(mods.data[2] == (SC_SHORTCUT_MOD_LCTRL | SC_SHORTCUT_MOD_RCTRL |
- SC_SHORTCUT_MOD_RALT));
+ assert(mods == (SC_SHORTCUT_MOD_LSUPER
+ | SC_SHORTCUT_MOD_RSUPER
+ | SC_SHORTCUT_MOD_LCTRL));
ok = sc_parse_shortcut_mods("", &mods);
assert(!ok);
diff --git a/doc/shortcuts.md b/doc/shortcuts.md
index 8c402855..841ceaa6 100644
--- a/doc/shortcuts.md
+++ b/doc/shortcuts.md
@@ -13,8 +13,8 @@ It can be changed using `--shortcut-mod`. Possible keys are `lctrl`, `rctrl`,
# use RCtrl for shortcuts
scrcpy --shortcut-mod=rctrl
-# use either LCtrl+LAlt or LSuper for shortcuts
-scrcpy --shortcut-mod=lctrl+lalt,lsuper
+# use either LCtrl or LSuper for shortcuts
+scrcpy --shortcut-mod=lctrl,lsuper
```
_[Super] is typically the Windows or Cmd key._
@@ -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_