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 <https://github.com/Genymobile/scrcpy/issues/4732>
simplify_shortcut_mods
Romain Vimont 3 months ago
parent 1c3801a0b1
commit c49e294acf

@ -420,9 +420,9 @@ Turn the device screen off immediately.
.BI "\-\-shortcut\-mod " key\fR[+...]][,...] .BI "\-\-shortcut\-mod " key\fR[+...]][,...]
Specify the modifiers to use for scrcpy shortcuts. Possible keys are "lctrl", "rctrl", "lalt", "ralt", "lsuper" and "rsuper". 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). Default is "lalt,lsuper" (left-Alt or left-Super).

@ -709,10 +709,10 @@ static const struct sc_option options[] = {
.text = "Specify the modifiers to use for scrcpy shortcuts.\n" .text = "Specify the modifiers to use for scrcpy shortcuts.\n"
"Possible keys are \"lctrl\", \"rctrl\", \"lalt\", \"ralt\", " "Possible keys are \"lctrl\", \"rctrl\", \"lalt\", \"ralt\", "
"\"lsuper\" and \"rsuper\".\n" "\"lsuper\" and \"rsuper\".\n"
"A shortcut can consist in several keys, separated by '+'. " "Several shortcut modifiers can be specified, separated by "
"Several shortcuts can be specified, separated by ','.\n" "','.\n"
"For example, to use either LCtrl+LAlt or LSuper for scrcpy " "For example, to use either LCtrl or LSuper for scrcpy "
"shortcuts, pass \"lctrl+lalt,lsuper\".\n" "shortcuts, pass \"lctrl,lsuper\".\n"
"Default is \"lalt,lsuper\" (left-Alt or left-Super).", "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; return false;
} }
// item is a list of mod keys separated by '+' (e.g. "lctrl+lalt") static enum sc_shortcut_mod
// returns a bitwise-or of SC_SHORTCUT_MOD_* constants (or 0 on error)
static unsigned
parse_shortcut_mods_item(const char *item, size_t len) { 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) \ #define STREQ(literal, s, len) \
((sizeof(literal)-1 == len) && !memcmp(literal, s, len)) ((sizeof(literal)-1 == len) && !memcmp(literal, s, len))
if (STREQ("lctrl", item, key_len)) { if (STREQ("lctrl", item, len)) {
mod |= SC_SHORTCUT_MOD_LCTRL; return SC_SHORTCUT_MOD_LCTRL;
} else if (STREQ("rctrl", item, key_len)) { }
mod |= SC_SHORTCUT_MOD_RCTRL; if (STREQ("rctrl", item, len)) {
} else if (STREQ("lalt", item, key_len)) { return SC_SHORTCUT_MOD_RCTRL;
mod |= SC_SHORTCUT_MOD_LALT; }
} else if (STREQ("ralt", item, key_len)) { if (STREQ("lalt", item, len)) {
mod |= SC_SHORTCUT_MOD_RALT; return SC_SHORTCUT_MOD_LALT;
} else if (STREQ("lsuper", item, key_len)) { }
mod |= SC_SHORTCUT_MOD_LSUPER; if (STREQ("ralt", item, len)) {
} else if (STREQ("rsuper", item, key_len)) { return SC_SHORTCUT_MOD_RALT;
mod |= SC_SHORTCUT_MOD_RSUPER; }
} else { if (STREQ("lsuper", item, len)) {
LOGE("Unknown modifier key: %.*s " return SC_SHORTCUT_MOD_LSUPER;
"(must be one of: lctrl, rctrl, lalt, ralt, lsuper, rsuper)", }
(int) key_len, item); if (STREQ("rsuper", item, len)) {
return 0; return SC_SHORTCUT_MOD_RSUPER;
} }
#undef STREQ #undef STREQ
if (!has_plus) { bool has_plus = strchr(item, '+');
break; if (has_plus) {
} LOGE("Shortcut mod combination with '+' is not supported anymore: "
"'%.*s' (see #4741)", (int) len, item);
item = plus + 1; return 0;
assert(len >= key_len + 1);
len -= key_len + 1;
} }
return mod; LOGE("Unknown modifier key: %.*s "
"(must be one of: lctrl, rctrl, lalt, ralt, lsuper, rsuper)",
(int) len, item);
return 0;
} }
static bool static bool
parse_shortcut_mods(const char *s, struct sc_shortcut_mods *mods) { parse_shortcut_mods(const char *s, uint8_t *shortcut_mods) {
unsigned count = 0; uint8_t mods = 0;
unsigned current = 0;
// LCtrl+LAlt or RCtrl or LCtrl+RSuper: "lctrl+lalt,rctrl,lctrl+rsuper" // A list of shortcut modifiers, for example "lctrl,rctrl,rsuper"
for (;;) { for (;;) {
char *comma = strchr(s, ','); 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); assert(!comma || comma > s);
size_t limit = comma ? (size_t) (comma - s) : strlen(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) { if (!mod) {
LOGE("Invalid modifier keys: %.*s", (int) limit, s); LOGE("Invalid modifier keys: %.*s", (int) limit, s);
return false; return false;
} }
mods->data[current++] = mod; mods |= mod;
++count;
if (!comma) { if (!comma) {
break; break;
@ -1764,7 +1745,7 @@ parse_shortcut_mods(const char *s, struct sc_shortcut_mods *mods) {
s = comma + 1; s = comma + 1;
} }
mods->count = count; *shortcut_mods = mods;
return true; return true;
} }
@ -1772,7 +1753,7 @@ parse_shortcut_mods(const char *s, struct sc_shortcut_mods *mods) {
#ifdef SC_TEST #ifdef SC_TEST
// expose the function to unit-tests // expose the function to unit-tests
bool 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); return parse_shortcut_mods(s, mods);
} }
#endif #endif

@ -28,7 +28,7 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]);
#ifdef SC_TEST #ifdef SC_TEST
bool 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
#endif #endif

@ -10,7 +10,7 @@
#define SC_SDL_SHORTCUT_MODS_MASK (KMOD_CTRL | KMOD_ALT | KMOD_GUI) #define SC_SDL_SHORTCUT_MODS_MASK (KMOD_CTRL | KMOD_ALT | KMOD_GUI)
static inline uint16_t static inline uint16_t
to_sdl_mod(unsigned shortcut_mod) { to_sdl_mod(uint8_t shortcut_mod) {
uint16_t sdl_mod = 0; uint16_t sdl_mod = 0;
if (shortcut_mod & SC_SHORTCUT_MOD_LCTRL) { if (shortcut_mod & SC_SHORTCUT_MOD_LCTRL) {
sdl_mod |= KMOD_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 // keep only the relevant modifier keys
sdl_mod &= SC_SDL_SHORTCUT_MODS_MASK; sdl_mod &= SC_SDL_SHORTCUT_MODS_MASK;
assert(im->sdl_shortcut_mods.count); // at least one shortcut mod pressed?
assert(im->sdl_shortcut_mods.count < SC_MAX_SHORTCUT_MODS); return sdl_mod & im->sdl_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;
} }
void void
@ -68,15 +61,7 @@ sc_input_manager_init(struct sc_input_manager *im,
im->legacy_paste = params->legacy_paste; im->legacy_paste = params->legacy_paste;
im->clipboard_autosync = params->clipboard_autosync; im->clipboard_autosync = params->clipboard_autosync;
const struct sc_shortcut_mods *shortcut_mods = params->shortcut_mods; im->sdl_shortcut_mods = to_sdl_mod(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->vfinger_down = false; im->vfinger_down = false;
im->vfinger_invert_x = false; im->vfinger_invert_x = false;

@ -26,10 +26,7 @@ struct sc_input_manager {
bool legacy_paste; bool legacy_paste;
bool clipboard_autosync; bool clipboard_autosync;
struct { uint16_t sdl_shortcut_mods;
unsigned data[SC_MAX_SHORTCUT_MODS];
unsigned count;
} sdl_shortcut_mods;
bool vfinger_down; bool vfinger_down;
bool vfinger_invert_x; bool vfinger_invert_x;
@ -55,7 +52,7 @@ struct sc_input_manager_params {
bool forward_all_clicks; bool forward_all_clicks;
bool legacy_paste; bool legacy_paste;
bool clipboard_autosync; bool clipboard_autosync;
const struct sc_shortcut_mods *shortcut_mods; uint8_t shortcut_mods; // OR of enum sc_shortcut_mod values
}; };
void void

@ -30,10 +30,7 @@ const struct scrcpy_options scrcpy_options_default = {
}, },
.tunnel_host = 0, .tunnel_host = 0,
.tunnel_port = 0, .tunnel_port = 0,
.shortcut_mods = { .shortcut_mods = SC_SHORTCUT_MOD_LALT | SC_SHORTCUT_MOD_LSUPER,
.data = {SC_SHORTCUT_MOD_LALT, SC_SHORTCUT_MOD_LSUPER},
.count = 2,
},
.max_size = 0, .max_size = 0,
.video_bit_rate = 0, .video_bit_rate = 0,
.audio_bit_rate = 0, .audio_bit_rate = 0,

@ -169,8 +169,6 @@ enum sc_key_inject_mode {
SC_KEY_INJECT_MODE_RAW, SC_KEY_INJECT_MODE_RAW,
}; };
#define SC_MAX_SHORTCUT_MODS 8
enum sc_shortcut_mod { enum sc_shortcut_mod {
SC_SHORTCUT_MOD_LCTRL = 1 << 0, SC_SHORTCUT_MOD_LCTRL = 1 << 0,
SC_SHORTCUT_MOD_RCTRL = 1 << 1, SC_SHORTCUT_MOD_RCTRL = 1 << 1,
@ -180,11 +178,6 @@ enum sc_shortcut_mod {
SC_SHORTCUT_MOD_RSUPER = 1 << 5, SC_SHORTCUT_MOD_RSUPER = 1 << 5,
}; };
struct sc_shortcut_mods {
unsigned data[SC_MAX_SHORTCUT_MODS];
unsigned count;
};
struct sc_port_range { struct sc_port_range {
uint16_t first; uint16_t first;
uint16_t last; uint16_t last;
@ -219,7 +212,7 @@ struct scrcpy_options {
struct sc_port_range port_range; struct sc_port_range port_range;
uint32_t tunnel_host; uint32_t tunnel_host;
uint16_t tunnel_port; 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; uint16_t max_size;
uint32_t video_bit_rate; uint32_t video_bit_rate;
uint32_t audio_bit_rate; uint32_t audio_bit_rate;

@ -696,7 +696,7 @@ scrcpy(struct scrcpy_options *options) {
.forward_all_clicks = options->forward_all_clicks, .forward_all_clicks = options->forward_all_clicks,
.legacy_paste = options->legacy_paste, .legacy_paste = options->legacy_paste,
.clipboard_autosync = options->clipboard_autosync, .clipboard_autosync = options->clipboard_autosync,
.shortcut_mods = &options->shortcut_mods, .shortcut_mods = options->shortcut_mods,
.window_title = window_title, .window_title = window_title,
.always_on_top = options->always_on_top, .always_on_top = options->always_on_top,
.window_x = options->window_x, .window_x = options->window_x,

@ -78,7 +78,7 @@ struct sc_screen_params {
bool forward_all_clicks; bool forward_all_clicks;
bool legacy_paste; bool legacy_paste;
bool clipboard_autosync; 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; const char *window_title;
bool always_on_top; bool always_on_top;

@ -124,32 +124,22 @@ static void test_options2(void) {
} }
static void test_parse_shortcut_mods(void) { static void test_parse_shortcut_mods(void) {
struct sc_shortcut_mods mods; uint8_t mods;
bool ok; bool ok;
ok = sc_parse_shortcut_mods("lctrl", &mods); ok = sc_parse_shortcut_mods("lctrl", &mods);
assert(ok); assert(ok);
assert(mods.count == 1); assert(mods == SC_SHORTCUT_MOD_LCTRL);
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));
ok = sc_parse_shortcut_mods("rctrl,lalt", &mods); ok = sc_parse_shortcut_mods("rctrl,lalt", &mods);
assert(ok); assert(ok);
assert(mods.count == 2); assert(mods == (SC_SHORTCUT_MOD_RCTRL | SC_SHORTCUT_MOD_LALT));
assert(mods.data[0] == SC_SHORTCUT_MOD_RCTRL);
assert(mods.data[1] == 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(ok);
assert(mods.count == 3); assert(mods == (SC_SHORTCUT_MOD_LSUPER
assert(mods.data[0] == SC_SHORTCUT_MOD_LSUPER); | SC_SHORTCUT_MOD_RSUPER
assert(mods.data[1] == (SC_SHORTCUT_MOD_RSUPER | SC_SHORTCUT_MOD_LALT)); | SC_SHORTCUT_MOD_LCTRL));
assert(mods.data[2] == (SC_SHORTCUT_MOD_LCTRL | SC_SHORTCUT_MOD_RCTRL |
SC_SHORTCUT_MOD_RALT));
ok = sc_parse_shortcut_mods("", &mods); ok = sc_parse_shortcut_mods("", &mods);
assert(!ok); assert(!ok);

@ -13,8 +13,8 @@ It can be changed using `--shortcut-mod`. Possible keys are `lctrl`, `rctrl`,
# use RCtrl for shortcuts # use RCtrl for shortcuts
scrcpy --shortcut-mod=rctrl scrcpy --shortcut-mod=rctrl
# use either LCtrl+LAlt or LSuper for shortcuts # use either LCtrl or LSuper for shortcuts
scrcpy --shortcut-mod=lctrl+lalt,lsuper scrcpy --shortcut-mod=lctrl,lsuper
``` ```
_<kbd>[Super]</kbd> is typically the <kbd>Windows</kbd> or <kbd>Cmd</kbd> key._ _<kbd>[Super]</kbd> is typically the <kbd>Windows</kbd> or <kbd>Cmd</kbd> key._

Loading…
Cancel
Save