From be3d357a6dcdf0f3fccabd795b869a30e1e5bee1 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sat, 30 Mar 2024 11:23:41 +0100 Subject: [PATCH 1/4] Use source repo tarball for libusb Legitimate or not, we should not use sources that do not match the repository. Refs Refs Refs #4713 --- app/deps/libusb.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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" \ From 1c3801a0b1624258e5004e651c9dbcff7eafe324 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sun, 10 Mar 2024 22:04:17 +0100 Subject: [PATCH 2/4] Add a shortcut to pause/unpause display Pause/unpause display on MOD+z and MOD+Shift+z. It only impacts rendering, the device is still captured, the video stream continues to be transmitted to the device and recorded (if recording is enabled). Fixes #1632 PR #4748 --- app/scrcpy.1 | 8 ++++++ app/src/cli.c | 8 ++++++ app/src/input_manager.c | 54 +++++++++++++++++++++----------------- app/src/screen.c | 57 +++++++++++++++++++++++++++++++++++++---- app/src/screen.h | 7 +++++ doc/shortcuts.md | 2 ++ 6 files changed, 107 insertions(+), 29 deletions(-) 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_ From c49e294acf41f859eb0719b533cdad8c8662f472 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Thu, 7 Mar 2024 23:07:01 +0100 Subject: [PATCH 3/4] Simplify shortcut modifiers Restrict shortcut modifiers to be composed of only one item each. Before, it was possible to select a list of multiple combinations of modifier keys, like --shortcut-mod='lctrl+lalt,rctrl+rsuper', meaning that shortcuts would be triggered either by LCtrl+LAlt+key or RCtrl+RSuper+key. This was overly generic, probably not used very much, and it prevents to solve inconsistencies between UP and DOWN events of modifier keys sent to the device. Refs #4732 --- app/scrcpy.1 | 4 +- app/src/cli.c | 99 +++++++++++++++++------------------------ app/src/cli.h | 2 +- app/src/input_manager.c | 23 ++-------- app/src/input_manager.h | 7 +-- app/src/options.c | 5 +-- app/src/options.h | 9 +--- app/src/scrcpy.c | 2 +- app/src/screen.h | 2 +- app/tests/test_cli.c | 24 +++------- doc/shortcuts.md | 4 +- 11 files changed, 62 insertions(+), 119 deletions(-) diff --git a/app/scrcpy.1 b/app/scrcpy.1 index eb09f530..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). diff --git a/app/src/cli.c b/app/src/cli.c index 92807947..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).", }, { @@ -1680,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; @@ -1764,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; } @@ -1772,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 c7a758f4..8a5b9da6 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,8 @@ 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; - } - } - - return false; + // at least one shortcut mod pressed? + return sdl_mod & im->sdl_shortcut_mods; } void @@ -68,15 +61,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; 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.h b/app/src/screen.h index 361ce455..2079d262 100644 --- a/app/src/screen.h +++ b/app/src/screen.h @@ -78,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; 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 d0f6ebec..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._ From 21ae1ca1a77e7a2a0b7ac409acfe5d32a52248cc Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Thu, 7 Mar 2024 23:08:27 +0100 Subject: [PATCH 4/4] Ignore shortcut keycodes Never inject keycodes used as shortcut modifiers. Refs #4732 --- app/src/input_manager.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/app/src/input_manager.c b/app/src/input_manager.c index 8a5b9da6..8d078523 100644 --- a/app/src/input_manager.c +++ b/app/src/input_manager.c @@ -42,6 +42,16 @@ is_shortcut_mod(struct sc_input_manager *im, uint16_t sdl_mod) { return sdl_mod & im->sdl_shortcut_mods; } +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 sc_input_manager_init(struct sc_input_manager *im, const struct sc_input_manager_params *params) { @@ -396,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) { @@ -408,8 +423,7 @@ 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: