diff --git a/app/scrcpy.1 b/app/scrcpy.1
index cf8dfa7fe4..1c0c0f7aff 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 08a4aa3f8e..9dd4953832 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,26 +2102,48 @@ 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[0], &mb->right_click)) {
+ if (!parse_mouse_binding(s[1], &mbs->middle_click)) {
return false;
}
- if (!parse_mouse_binding(s[1], &mb->middle_click)) {
+ if (!parse_mouse_binding(s[2], &mbs->click4)) {
return false;
}
- if (!parse_mouse_binding(s[2], &mb->click4)) {
+ if (!parse_mouse_binding(s[3], &mbs->click5)) {
return false;
}
- if (!parse_mouse_binding(s[3], &mb->click5)) {
+
+ 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_set(s, &mb->pri)) {
+ 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/control_msg.c b/app/src/control_msg.c
index b3da5fe5ae..9b0fab6773 100644
--- a/app/src/control_msg.c
+++ b/app/src/control_msg.c
@@ -64,13 +64,11 @@ static const char *const copy_key_labels[] = {
static inline const char *
get_well_known_pointer_id_name(uint64_t pointer_id) {
switch (pointer_id) {
- case POINTER_ID_MOUSE:
+ case SC_POINTER_ID_MOUSE:
return "mouse";
- case POINTER_ID_GENERIC_FINGER:
+ case SC_POINTER_ID_GENERIC_FINGER:
return "finger";
- case POINTER_ID_VIRTUAL_MOUSE:
- return "vmouse";
- case POINTER_ID_VIRTUAL_FINGER:
+ case SC_POINTER_ID_VIRTUAL_FINGER:
return "vfinger";
default:
return NULL;
diff --git a/app/src/control_msg.h b/app/src/control_msg.h
index cd1340efa7..80714096b7 100644
--- a/app/src/control_msg.h
+++ b/app/src/control_msg.h
@@ -18,12 +18,11 @@
// type: 1 byte; sequence: 8 bytes; paste flag: 1 byte; length: 4 bytes
#define SC_CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH (SC_CONTROL_MSG_MAX_SIZE - 14)
-#define POINTER_ID_MOUSE UINT64_C(-1)
-#define POINTER_ID_GENERIC_FINGER UINT64_C(-2)
+#define SC_POINTER_ID_MOUSE UINT64_C(-1)
+#define SC_POINTER_ID_GENERIC_FINGER UINT64_C(-2)
// Used for injecting an additional virtual pointer for pinch-to-zoom
-#define POINTER_ID_VIRTUAL_MOUSE UINT64_C(-3)
-#define POINTER_ID_VIRTUAL_FINGER UINT64_C(-4)
+#define SC_POINTER_ID_VIRTUAL_FINGER UINT64_C(-3)
enum sc_control_msg_type {
SC_CONTROL_MSG_TYPE_INJECT_KEYCODE,
diff --git a/app/src/input_events.h b/app/src/input_events.h
index ed77bcb4d8..bbf4372fa2 100644
--- a/app/src/input_events.h
+++ b/app/src/input_events.h
@@ -437,25 +437,11 @@ sc_mouse_button_from_sdl(uint8_t button) {
}
static inline uint8_t
-sc_mouse_buttons_state_from_sdl(uint32_t buttons_state,
- const struct sc_mouse_bindings *mb) {
+sc_mouse_buttons_state_from_sdl(uint32_t buttons_state) {
assert(buttons_state < 0x100); // fits in uint8_t
- uint8_t mask = SC_MOUSE_BUTTON_LEFT;
- if (!mb || mb->right_click == SC_MOUSE_BINDING_CLICK) {
- mask |= SC_MOUSE_BUTTON_RIGHT;
- }
- if (!mb || mb->middle_click == SC_MOUSE_BINDING_CLICK) {
- mask |= SC_MOUSE_BUTTON_MIDDLE;
- }
- if (!mb || mb->click4 == SC_MOUSE_BINDING_CLICK) {
- mask |= SC_MOUSE_BUTTON_X1;
- }
- if (!mb || mb->click5 == SC_MOUSE_BINDING_CLICK) {
- mask |= SC_MOUSE_BUTTON_X2;
- }
-
- return buttons_state & mask;
+ // SC_MOUSE_BUTTON_* constants are initialized from SDL_BUTTON(index)
+ return buttons_state;
}
#endif
diff --git a/app/src/input_manager.c b/app/src/input_manager.c
index 43b10d2de8..d3c94d03bf 100644
--- a/app/src/input_manager.c
+++ b/app/src/input_manager.c
@@ -52,14 +52,6 @@ is_shortcut_key(struct sc_input_manager *im, SDL_Keycode keycode) {
|| (im->sdl_shortcut_mods & KMOD_RGUI && keycode == SDLK_RGUI);
}
-static inline bool
-mouse_bindings_has_secondary_click(const struct sc_mouse_bindings *mb) {
- return mb->right_click == SC_MOUSE_BINDING_CLICK
- || mb->middle_click == SC_MOUSE_BINDING_CLICK
- || mb->click4 == SC_MOUSE_BINDING_CLICK
- || mb->click5 == SC_MOUSE_BINDING_CLICK;
-}
-
void
sc_input_manager_init(struct sc_input_manager *im,
const struct sc_input_manager_params *params) {
@@ -76,8 +68,6 @@ sc_input_manager_init(struct sc_input_manager *im,
im->mp = params->mp;
im->mouse_bindings = params->mouse_bindings;
- im->has_secondary_click =
- mouse_bindings_has_secondary_click(&im->mouse_bindings);
im->legacy_paste = params->legacy_paste;
im->clipboard_autosync = params->clipboard_autosync;
@@ -87,6 +77,8 @@ sc_input_manager_init(struct sc_input_manager *im,
im->vfinger_invert_x = false;
im->vfinger_invert_y = false;
+ im->mouse_buttons_state = 0;
+
im->last_keycode = SDLK_UNKNOWN;
im->last_mod = 0;
im->key_repeat = 0;
@@ -375,9 +367,7 @@ simulate_virtual_finger(struct sc_input_manager *im,
msg.inject_touch_event.action = action;
msg.inject_touch_event.position.screen_size = im->screen->frame_size;
msg.inject_touch_event.position.point = point;
- msg.inject_touch_event.pointer_id =
- im->has_secondary_click ? POINTER_ID_VIRTUAL_MOUSE
- : POINTER_ID_VIRTUAL_FINGER;
+ msg.inject_touch_event.pointer_id = SC_POINTER_ID_VIRTUAL_FINGER;
msg.inject_touch_event.pressure = up ? 0.0f : 1.0f;
msg.inject_touch_event.action_button = 0;
msg.inject_touch_event.buttons = 0;
@@ -662,12 +652,11 @@ sc_input_manager_process_mouse_motion(struct sc_input_manager *im,
struct sc_mouse_motion_event evt = {
.position = sc_input_manager_get_position(im, event->x, event->y),
- .pointer_id = im->has_secondary_click ? POINTER_ID_MOUSE
- : POINTER_ID_GENERIC_FINGER,
+ .pointer_id = im->vfinger_down ? SC_POINTER_ID_GENERIC_FINGER
+ : SC_POINTER_ID_MOUSE,
.xrel = event->xrel,
.yrel = event->yrel,
- .buttons_state =
- sc_mouse_buttons_state_from_sdl(event->state, &im->mouse_bindings),
+ .buttons_state = im->mouse_buttons_state,
};
assert(im->mp->ops->process_mouse_motion);
@@ -719,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:
@@ -748,11 +737,25 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im,
bool control = im->controller;
bool paused = im->screen->paused;
bool down = event->type == SDL_MOUSEBUTTONDOWN;
+
+ enum sc_mouse_button button = sc_mouse_button_from_sdl(event->button);
+ if (!down) {
+ // Mark the button as released
+ 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:
@@ -811,16 +814,23 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im,
return;
}
- uint32_t sdl_buttons_state = SDL_GetMouseState(NULL, NULL);
+ if (down) {
+ // Mark the button as pressed
+ im->mouse_buttons_state |= button;
+ }
+
+ bool change_vfinger = event->button == SDL_BUTTON_LEFT &&
+ ((down && !im->vfinger_down && (ctrl_pressed ^ shift_pressed)) ||
+ (!down && im->vfinger_down));
+ bool use_finger = im->vfinger_down || change_vfinger;
struct sc_mouse_click_event evt = {
.position = sc_input_manager_get_position(im, event->x, event->y),
.action = sc_action_from_sdl_mousebutton_type(event->type),
.button = sc_mouse_button_from_sdl(event->button),
- .pointer_id = im->has_secondary_click ? POINTER_ID_MOUSE
- : POINTER_ID_GENERIC_FINGER,
- .buttons_state = sc_mouse_buttons_state_from_sdl(sdl_buttons_state,
- &im->mouse_bindings),
+ .pointer_id = use_finger ? SC_POINTER_ID_GENERIC_FINGER
+ : SC_POINTER_ID_MOUSE,
+ .buttons_state = im->mouse_buttons_state,
};
assert(im->mp->ops->process_mouse_click);
@@ -846,14 +856,7 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im,
// can be used instead of Ctrl. The "virtual finger" has a position
// inverted with respect to the vertical axis of symmetry in the middle of
// the screen.
- const SDL_Keymod keymod = SDL_GetModState();
- const bool ctrl_pressed = keymod & KMOD_CTRL;
- const bool shift_pressed = keymod & KMOD_SHIFT;
- if (event->button == SDL_BUTTON_LEFT &&
- ((down && !im->vfinger_down &&
- ((ctrl_pressed && !shift_pressed) ||
- (!ctrl_pressed && shift_pressed))) ||
- (!down && im->vfinger_down))) {
+ if (change_vfinger) {
struct sc_point mouse =
sc_screen_convert_window_to_frame_coords(im->screen, event->x,
event->y);
@@ -886,6 +889,7 @@ sc_input_manager_process_mouse_wheel(struct sc_input_manager *im,
int mouse_x;
int mouse_y;
uint32_t buttons = SDL_GetMouseState(&mouse_x, &mouse_y);
+ (void) buttons; // Actual buttons are tracked manually to ignore shortcuts
struct sc_mouse_scroll_event evt = {
.position = sc_input_manager_get_position(im, mouse_x, mouse_y),
@@ -896,8 +900,7 @@ sc_input_manager_process_mouse_wheel(struct sc_input_manager *im,
.hscroll = CLAMP(event->x, -1, 1),
.vscroll = CLAMP(event->y, -1, 1),
#endif
- .buttons_state = sc_mouse_buttons_state_from_sdl(buttons,
- &im->mouse_bindings),
+ .buttons_state = im->mouse_buttons_state,
};
im->mp->ops->process_mouse_scroll(im->mp, &evt);
diff --git a/app/src/input_manager.h b/app/src/input_manager.h
index 03c42fe676..88558549b3 100644
--- a/app/src/input_manager.h
+++ b/app/src/input_manager.h
@@ -23,7 +23,6 @@ struct sc_input_manager {
struct sc_mouse_processor *mp;
struct sc_mouse_bindings mouse_bindings;
- bool has_secondary_click;
bool legacy_paste;
bool clipboard_autosync;
@@ -33,6 +32,8 @@ struct sc_input_manager {
bool vfinger_invert_x;
bool vfinger_invert_y;
+ uint8_t mouse_buttons_state; // OR of enum sc_mouse_button values
+
// Tracks the number of identical consecutive shortcut key down events.
// Not to be confused with event->repeat, which counts the number of
// system-generated repeated key presses.
diff --git a/app/src/options.c b/app/src/options.c
index 5556d1f987..5eec6427f2 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 f840a989d7..5ec809f00c 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/app/src/usb/screen_otg.c b/app/src/usb/screen_otg.c
index 33500e0c23..5c4f97f0f7 100644
--- a/app/src/usb/screen_otg.c
+++ b/app/src/usb/screen_otg.c
@@ -169,7 +169,7 @@ sc_screen_otg_process_mouse_motion(struct sc_screen_otg *screen,
// .position not used for HID events
.xrel = event->xrel,
.yrel = event->yrel,
- .buttons_state = sc_mouse_buttons_state_from_sdl(event->state, NULL),
+ .buttons_state = sc_mouse_buttons_state_from_sdl(event->state),
};
assert(mp->ops->process_mouse_motion);
@@ -188,8 +188,7 @@ sc_screen_otg_process_mouse_button(struct sc_screen_otg *screen,
// .position not used for HID events
.action = sc_action_from_sdl_mousebutton_type(event->type),
.button = sc_mouse_button_from_sdl(event->button),
- .buttons_state =
- sc_mouse_buttons_state_from_sdl(sdl_buttons_state, NULL),
+ .buttons_state = sc_mouse_buttons_state_from_sdl(sdl_buttons_state),
};
assert(mp->ops->process_mouse_click);
@@ -208,8 +207,7 @@ sc_screen_otg_process_mouse_wheel(struct sc_screen_otg *screen,
// .position not used for HID events
.hscroll = event->x,
.vscroll = event->y,
- .buttons_state =
- sc_mouse_buttons_state_from_sdl(sdl_buttons_state, NULL),
+ .buttons_state = sc_mouse_buttons_state_from_sdl(sdl_buttons_state),
};
assert(mp->ops->process_mouse_scroll);
diff --git a/doc/mouse.md b/doc/mouse.md
index 1c62ddd043..ec4aea6389 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
```
diff --git a/server/src/main/java/com/genymobile/scrcpy/Controller.java b/server/src/main/java/com/genymobile/scrcpy/Controller.java
index 87faf8ba29..b7d2f93e8b 100644
--- a/server/src/main/java/com/genymobile/scrcpy/Controller.java
+++ b/server/src/main/java/com/genymobile/scrcpy/Controller.java
@@ -22,7 +22,6 @@ public class Controller implements AsyncProcessor {
// control_msg.h values of the pointerId field in inject_touch_event message
private static final int POINTER_ID_MOUSE = -1;
- private static final int POINTER_ID_VIRTUAL_MOUSE = -3;
private static final ScheduledExecutorService EXECUTOR = Executors.newSingleThreadScheduledExecutor();
@@ -273,8 +272,8 @@ private boolean injectTouch(int action, long pointerId, Position position, float
pointer.setPressure(pressure);
int source;
- if (pointerId == POINTER_ID_MOUSE || pointerId == POINTER_ID_VIRTUAL_MOUSE) {
- // real mouse event (forced by the client when --forward-on-click)
+ if (pointerId == POINTER_ID_MOUSE) {
+ // real mouse event
pointerProperties[pointerIndex].toolType = MotionEvent.TOOL_TYPE_MOUSE;
source = InputDevice.SOURCE_MOUSE;
pointer.setUp(buttons == 0);