diff --git a/app/scrcpy.1 b/app/scrcpy.1
index cf8dfa7f..1c0c0f7a 100644
--- a/app/scrcpy.1
+++ b/app/scrcpy.1
@@ -258,10 +258,14 @@ LAlt, LSuper or RSuper toggle the capture mode, to give control of the mouse bac
Also see \fB\-\-keyboard\fR.
.TP
-.BI "\-\-mouse\-bind " xxxx
+.BI "\-\-mouse\-bind " xxxx[:xxxx]
Configure bindings of secondary clicks.
-The argument must be exactly 4 characters, one for each secondary click (in order: right click, middle click, 4th click, 5th click).
+The argument must be one or two sequences (separated by ':') of exactly 4 characters, one for each secondary click (in order: right click, middle click, 4th click, 5th click).
+
+The first sequence defines the primary bindings, used when a mouse button is pressed alone. The second sequence defines the secondary bindings, used when a mouse button is pressed while the Shift key is held.
+
+If the second sequence of bindings is omitted, then it is the same as the first one.
Each character must be one of the following:
@@ -272,7 +276,7 @@ Each character must be one of the following:
- 's': trigger shortcut APP_SWITCH
- 'n': trigger shortcut "expand notification panel"
-Default is 'bhsn' for SDK mouse, and '++++' for AOA and UHID.
+Default is 'bhsn:++++' for SDK mouse, and '++++:bhsn' for AOA and UHID.
.TP
diff --git a/app/src/cli.c b/app/src/cli.c
index 08a4aa3f..9dd49538 100644
--- a/app/src/cli.c
+++ b/app/src/cli.c
@@ -493,11 +493,17 @@ static const struct sc_option options[] = {
{
.longopt_id = OPT_MOUSE_BIND,
.longopt = "mouse-bind",
- .argdesc = "xxxx",
+ .argdesc = "xxxx[:xxxx]",
.text = "Configure bindings of secondary clicks.\n"
- "The argument must be exactly 4 characters, one for each "
- "secondary click (in order: right click, middle click, 4th "
- "click, 5th click).\n"
+ "The argument must be one or two sequences (separated by ':') "
+ "of exactly 4 characters, one for each secondary click (in "
+ "order: right click, middle click, 4th click, 5th click).\n"
+ "The first sequence defines the primary bindings, used when a "
+ "mouse button is pressed alone. The second sequence defines "
+ "the secondary bindings, used when a mouse button is pressed "
+ "while the Shift key is held.\n"
+ "If the second sequence of bindings is omitted, then it is the "
+ "same as the first one.\n"
"Each character must be one of the following:\n"
" '+': forward the click to the device\n"
" '-': ignore the click\n"
@@ -505,7 +511,8 @@ static const struct sc_option options[] = {
" 'h': trigger shortcut HOME\n"
" 's': trigger shortcut APP_SWITCH\n"
" 'n': trigger shortcut \"expand notification panel\"\n"
- "Default is 'bhsn' for SDK mouse, and '++++' for AOA and UHID.",
+ "Default is 'bhsn:++++' for SDK mouse, and '++++:bhsn' for AOA "
+ "and UHID.",
},
{
.shortopt = 'n',
@@ -2095,24 +2102,46 @@ parse_mouse_binding(char c, enum sc_mouse_binding *b) {
}
static bool
-parse_mouse_bindings(const char *s, struct sc_mouse_bindings *mb) {
- if (strlen(s) != 4) {
- LOGE("Invalid mouse bindings: '%s' (expected exactly 4 characters from "
- "{'+', '-', 'b', 'h', 's', 'n'})", s);
+parse_mouse_binding_set(const char *s, struct sc_mouse_binding_set *mbs) {
+ assert(strlen(s) >= 4);
+
+ if (!parse_mouse_binding(s[0], &mbs->right_click)) {
+ return false;
+ }
+ if (!parse_mouse_binding(s[1], &mbs->middle_click)) {
+ return false;
+ }
+ if (!parse_mouse_binding(s[2], &mbs->click4)) {
+ return false;
+ }
+ if (!parse_mouse_binding(s[3], &mbs->click5)) {
return false;
}
- if (!parse_mouse_binding(s[0], &mb->right_click)) {
+ return true;
+}
+
+static bool
+parse_mouse_bindings(const char *s, struct sc_mouse_bindings *mb) {
+ size_t len = strlen(s);
+ // either "xxxx" or "xxxx:xxxx"
+ if (len != 4 && (len != 9 || s[4] != ':')) {
+ LOGE("Invalid mouse bindings: '%s' (expected 'xxxx' or 'xxxx:xxxx', "
+ "with each 'x' being in {'+', '-', 'b', 'h', 's', 'n'})", s);
return false;
}
- if (!parse_mouse_binding(s[1], &mb->middle_click)) {
+
+ if (!parse_mouse_binding_set(s, &mb->pri)) {
return false;
}
- if (!parse_mouse_binding(s[2], &mb->click4)) {
- return false;
- }
- if (!parse_mouse_binding(s[3], &mb->click5)) {
- return false;
+
+ if (len == 9) {
+ if (!parse_mouse_binding_set(s + 5, &mb->sec)) {
+ return false;
+ }
+ } else {
+ // use the same bindings for Shift+click
+ mb->sec = mb->pri;
}
return true;
@@ -2408,10 +2437,18 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
LOGW("--forward-all-clicks is deprecated, "
"use --mouse-bind=++++ instead.");
opts->mouse_bindings = (struct sc_mouse_bindings) {
- .right_click = SC_MOUSE_BINDING_CLICK,
- .middle_click = SC_MOUSE_BINDING_CLICK,
- .click4 = SC_MOUSE_BINDING_CLICK,
- .click5 = SC_MOUSE_BINDING_CLICK,
+ .pri = {
+ .right_click = SC_MOUSE_BINDING_CLICK,
+ .middle_click = SC_MOUSE_BINDING_CLICK,
+ .click4 = SC_MOUSE_BINDING_CLICK,
+ .click5 = SC_MOUSE_BINDING_CLICK,
+ },
+ .sec = {
+ .right_click = SC_MOUSE_BINDING_CLICK,
+ .middle_click = SC_MOUSE_BINDING_CLICK,
+ .click4 = SC_MOUSE_BINDING_CLICK,
+ .click5 = SC_MOUSE_BINDING_CLICK,
+ },
};
break;
case OPT_LEGACY_PASTE:
@@ -2701,26 +2738,36 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
}
// If mouse bindings are not explictly set, configure default bindings
- if (opts->mouse_bindings.right_click == SC_MOUSE_BINDING_AUTO) {
- assert(opts->mouse_bindings.middle_click == SC_MOUSE_BINDING_AUTO);
- assert(opts->mouse_bindings.click4 == SC_MOUSE_BINDING_AUTO);
- assert(opts->mouse_bindings.click5 == SC_MOUSE_BINDING_AUTO);
+ if (opts->mouse_bindings.pri.right_click == SC_MOUSE_BINDING_AUTO) {
+ assert(opts->mouse_bindings.pri.middle_click == SC_MOUSE_BINDING_AUTO);
+ assert(opts->mouse_bindings.pri.click4 == SC_MOUSE_BINDING_AUTO);
+ assert(opts->mouse_bindings.pri.click5 == SC_MOUSE_BINDING_AUTO);
+ assert(opts->mouse_bindings.sec.right_click == SC_MOUSE_BINDING_AUTO);
+ assert(opts->mouse_bindings.sec.middle_click == SC_MOUSE_BINDING_AUTO);
+ assert(opts->mouse_bindings.sec.click4 == SC_MOUSE_BINDING_AUTO);
+ assert(opts->mouse_bindings.sec.click5 == SC_MOUSE_BINDING_AUTO);
+
+ static struct sc_mouse_binding_set default_shortcuts = {
+ .right_click = SC_MOUSE_BINDING_BACK,
+ .middle_click = SC_MOUSE_BINDING_HOME,
+ .click4 = SC_MOUSE_BINDING_APP_SWITCH,
+ .click5 = SC_MOUSE_BINDING_EXPAND_NOTIFICATION_PANEL,
+ };
+
+ static struct sc_mouse_binding_set forward = {
+ .right_click = SC_MOUSE_BINDING_CLICK,
+ .middle_click = SC_MOUSE_BINDING_CLICK,
+ .click4 = SC_MOUSE_BINDING_CLICK,
+ .click5 = SC_MOUSE_BINDING_CLICK,
+ };
// By default, forward all clicks only for UHID and AOA
if (opts->mouse_input_mode == SC_MOUSE_INPUT_MODE_SDK) {
- opts->mouse_bindings = (struct sc_mouse_bindings) {
- .right_click = SC_MOUSE_BINDING_BACK,
- .middle_click = SC_MOUSE_BINDING_HOME,
- .click4 = SC_MOUSE_BINDING_APP_SWITCH,
- .click5 = SC_MOUSE_BINDING_EXPAND_NOTIFICATION_PANEL,
- };
+ opts->mouse_bindings.pri = default_shortcuts;
+ opts->mouse_bindings.sec = forward;
} else {
- opts->mouse_bindings = (struct sc_mouse_bindings) {
- .right_click = SC_MOUSE_BINDING_CLICK,
- .middle_click = SC_MOUSE_BINDING_CLICK,
- .click4 = SC_MOUSE_BINDING_CLICK,
- .click5 = SC_MOUSE_BINDING_CLICK,
- };
+ opts->mouse_bindings.pri = forward;
+ opts->mouse_bindings.sec = default_shortcuts;
}
}
diff --git a/app/src/input_manager.c b/app/src/input_manager.c
index 2cc34afa..d3c94d03 100644
--- a/app/src/input_manager.c
+++ b/app/src/input_manager.c
@@ -708,7 +708,7 @@ sc_input_manager_process_touch(struct sc_input_manager *im,
}
static enum sc_mouse_binding
-sc_input_manager_get_binding(const struct sc_mouse_bindings *bindings,
+sc_input_manager_get_binding(const struct sc_mouse_binding_set *bindings,
uint8_t sdl_button) {
switch (sdl_button) {
case SDL_BUTTON_LEFT:
@@ -744,11 +744,18 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im,
im->mouse_buttons_state &= ~button;
}
+ SDL_Keymod keymod = SDL_GetModState();
+ bool ctrl_pressed = keymod & KMOD_CTRL;
+ bool shift_pressed = keymod & KMOD_SHIFT;
+
if (control && !paused) {
enum sc_action action = down ? SC_ACTION_DOWN : SC_ACTION_UP;
+ struct sc_mouse_binding_set *bindings = !shift_pressed
+ ? &im->mouse_bindings.pri
+ : &im->mouse_bindings.sec;
enum sc_mouse_binding binding =
- sc_input_manager_get_binding(&im->mouse_bindings, event->button);
+ sc_input_manager_get_binding(bindings, event->button);
assert(binding != SC_MOUSE_BINDING_AUTO);
switch (binding) {
case SC_MOUSE_BINDING_DISABLED:
@@ -812,9 +819,6 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im,
im->mouse_buttons_state |= button;
}
- SDL_Keymod keymod = SDL_GetModState();
- bool ctrl_pressed = keymod & KMOD_CTRL;
- bool shift_pressed = keymod & KMOD_SHIFT;
bool change_vfinger = event->button == SDL_BUTTON_LEFT &&
((down && !im->vfinger_down && (ctrl_pressed ^ shift_pressed)) ||
(!down && im->vfinger_down));
diff --git a/app/src/options.c b/app/src/options.c
index 5556d1f9..5eec6427 100644
--- a/app/src/options.c
+++ b/app/src/options.c
@@ -24,10 +24,18 @@ const struct scrcpy_options scrcpy_options_default = {
.keyboard_input_mode = SC_KEYBOARD_INPUT_MODE_AUTO,
.mouse_input_mode = SC_MOUSE_INPUT_MODE_AUTO,
.mouse_bindings = {
- .right_click = SC_MOUSE_BINDING_AUTO,
- .middle_click = SC_MOUSE_BINDING_AUTO,
- .click4 = SC_MOUSE_BINDING_AUTO,
- .click5 = SC_MOUSE_BINDING_AUTO,
+ .pri = {
+ .right_click = SC_MOUSE_BINDING_AUTO,
+ .middle_click = SC_MOUSE_BINDING_AUTO,
+ .click4 = SC_MOUSE_BINDING_AUTO,
+ .click5 = SC_MOUSE_BINDING_AUTO,
+ },
+ .sec = {
+ .right_click = SC_MOUSE_BINDING_AUTO,
+ .middle_click = SC_MOUSE_BINDING_AUTO,
+ .click4 = SC_MOUSE_BINDING_AUTO,
+ .click5 = SC_MOUSE_BINDING_AUTO,
+ },
},
.camera_facing = SC_CAMERA_FACING_ANY,
.port_range = {
diff --git a/app/src/options.h b/app/src/options.h
index f840a989..5ec809f0 100644
--- a/app/src/options.h
+++ b/app/src/options.h
@@ -165,13 +165,18 @@ enum sc_mouse_binding {
SC_MOUSE_BINDING_EXPAND_NOTIFICATION_PANEL,
};
-struct sc_mouse_bindings {
+struct sc_mouse_binding_set {
enum sc_mouse_binding right_click;
enum sc_mouse_binding middle_click;
enum sc_mouse_binding click4;
enum sc_mouse_binding click5;
};
+struct sc_mouse_bindings {
+ struct sc_mouse_binding_set pri;
+ struct sc_mouse_binding_set sec; // When Shift is pressed
+};
+
enum sc_key_inject_mode {
// Inject special keys, letters and space as key events.
// Inject numbers and punctuation as text events.
diff --git a/doc/mouse.md b/doc/mouse.md
index 1c62ddd0..ec4aea63 100644
--- a/doc/mouse.md
+++ b/doc/mouse.md
@@ -80,21 +80,37 @@ process like the _adb daemon_).
## Mouse bindings
-By default, with SDK mouse, right-click triggers BACK (or POWER on) and
-middle-click triggers HOME. In addition, the 4th click triggers APP_SWITCH and
-the 5th click expands the notification panel.
+By default, with SDK mouse:
+ - right-click triggers BACK (or POWER on)
+ - middle-click triggers HOME
+ - the 4th click triggers APP_SWITCH
+ - the 5th click expands the notification panel
-In AOA and UHID mouse modes, all clicks are forwarded by default.
+The secondary clicks may be forwarded to the device instead by pressing the
+Shift key (e.g. Shift+right-click injects a right click to
+the device).
-The shortcuts can be configured using `--mouse-bind=xxxx` for any mouse mode.
-The argument must be exactly 4 characters, one for each secondary click:
+In AOA and UHID mouse modes, the default bindings are reversed: all clicks are
+forwarded by default, and pressing Shift gives access to the
+shortcuts (since the cursor is handled on the device side, it makes more sense
+to forward all mouse buttons by default in these modes).
+
+The shortcuts can be configured using `--mouse-bind=xxxx:xxxx` for any mouse
+mode. The argument must be one or two sequences (separated by `:`) of exactly 4
+characters, one for each secondary click:
```
---mouse-bind=xxxx
+ .---- Shift + right click
+ SECONDARY |.--- Shift + middle click
+ BINDINGS ||.-- Shift + 4th click
+ |||.- Shift + 5th click
+ ||||
+ vvvv
+--mouse-bind=xxxx:xxxx
^^^^
||||
- ||| `- 5th click
- || `-- 4th click
+ PRIMARY ||| `- 5th click
+ BINDINGS || `-- 4th click
| `--- middle click
`---- right click
```
@@ -111,8 +127,18 @@ Each character must be one of the following:
For example:
```bash
-scrcpy --mouse-bind=bhsn # the default mode with SDK mouse
-scrcpy --mouse-bind=++++ # forward all clicks (default for AOA/UHID)
-scrcpy --mouse-bind=++bh # forward right and middle clicks,
- # use 4th and 5th for BACK and HOME
+scrcpy --mouse-bind=bhsn:++++ # the default mode for SDK mouse
+scrcpy --mouse-bind=++++:bhsn # the default mode for AOA and UHID
+scrcpy --mouse-bind=++bh:++sn # forward right and middle clicks,
+ # use 4th and 5th for BACK and HOME,
+ # use Shift+4th and Shift+5th for APP_SWITCH
+ # and expand notification panel
+```
+
+The second sequence of bindings may be omitted. In that case, it is the same as
+the first one:
+
+```bash
+scrcpy --mouse-bind=bhsn
+scrcpy --mouse-bind=bhsn:bhsn # equivalent
```