Add option to configure mouse bindings

Add a new option --mouse-bind=xxxx.

The argument must be exactly 4 characters, one for each secondary click:

    --mouse-bind=xxxx
                 ^^^^
                 ||||
                 ||| `- 5th click
                 || `-- 4th click
                 | `--- middle click
                  `---- right click

Each character must be one of the following:
 - `+`: forward the click to the device
 - `-`: ignore the click
 - `b`: trigger shortcut BACK (or turn screen on if off)
 - `h`: trigger shortcut HOME
 - `s`: trigger shortcut APP_SWITCH
 - `n`: trigger shortcut "expand notification panel"

This deprecates --forward-all-clicks (use --mouse-bind=++++ instead).

Refs <https://github.com/Genymobile/scrcpy/pull/2258#issuecomment-2182394460>
PR #5022 <https://github.com/Genymobile/scrcpy/pull/5022>
This commit is contained in:
Romain Vimont 2024-06-24 23:07:08 +02:00
parent 40493dff60
commit 035d60cf5d
15 changed files with 261 additions and 69 deletions

View file

@ -25,7 +25,6 @@ _scrcpy() {
-e --select-tcpip -e --select-tcpip
-f --fullscreen -f --fullscreen
--force-adb-forward --force-adb-forward
--forward-all-clicks
-h --help -h --help
-K -K
--keyboard= --keyboard=
@ -41,6 +40,7 @@ _scrcpy() {
-M -M
--max-fps= --max-fps=
--mouse= --mouse=
--mouse-bind=
-n --no-control -n --no-control
-N --no-playback -N --no-playback
--no-audio --no-audio

View file

@ -32,7 +32,6 @@ arguments=(
{-e,--select-tcpip}'[Use TCP/IP device]' {-e,--select-tcpip}'[Use TCP/IP device]'
{-f,--fullscreen}'[Start in fullscreen]' {-f,--fullscreen}'[Start in fullscreen]'
'--force-adb-forward[Do not attempt to use \"adb reverse\" to connect to the device]' '--force-adb-forward[Do not attempt to use \"adb reverse\" to connect to the device]'
'--forward-all-clicks[Forward clicks to device]'
{-h,--help}'[Print the help]' {-h,--help}'[Print the help]'
'-K[Use UHID keyboard (same as --keyboard=uhid)]' '-K[Use UHID keyboard (same as --keyboard=uhid)]'
'--keyboard=[Set the keyboard input mode]:mode:(disabled sdk uhid aoa)' '--keyboard=[Set the keyboard input mode]:mode:(disabled sdk uhid aoa)'
@ -47,6 +46,7 @@ arguments=(
'-M[Use UHID mouse (same as --mouse=uhid)]' '-M[Use UHID mouse (same as --mouse=uhid)]'
'--max-fps=[Limit the frame rate of screen capture]' '--max-fps=[Limit the frame rate of screen capture]'
'--mouse=[Set the mouse input mode]:mode:(disabled sdk uhid aoa)' '--mouse=[Set the mouse input mode]:mode:(disabled sdk uhid aoa)'
'--mouse-bind=[Configure bindings of secondary clicks]'
{-n,--no-control}'[Disable device control \(mirror the device in read only\)]' {-n,--no-control}'[Disable device control \(mirror the device in read only\)]'
{-N,--no-playback}'[Disable video and audio playback]' {-N,--no-playback}'[Disable video and audio playback]'
'--no-audio[Disable audio forwarding]' '--no-audio[Disable audio forwarding]'

View file

@ -163,10 +163,6 @@ Start in fullscreen.
.B \-\-force\-adb\-forward .B \-\-force\-adb\-forward
Do not attempt to use "adb reverse" to connect to the device. Do not attempt to use "adb reverse" to connect to the device.
.TP
.B \-\-forward\-all\-clicks
By default, right-click triggers BACK (or POWER on) and middle-click triggers HOME. This option disables these shortcuts and forward the clicks to the device instead.
.TP .TP
.B \-h, \-\-help .B \-h, \-\-help
Print this help. Print this help.
@ -261,6 +257,23 @@ LAlt, LSuper or RSuper toggle the capture mode, to give control of the mouse bac
Also see \fB\-\-keyboard\fR. Also see \fB\-\-keyboard\fR.
.TP
.BI "\-\-mouse\-bind " 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).
Each character must be one of the following:
- '+': forward the click to the device
- '-': ignore the click
- 'b': trigger shortcut BACK (or turn screen on if off)
- 'h': trigger shortcut HOME
- 's': trigger shortcut APP_SWITCH
- 'n': trigger shortcut "expand notification panel"
Default is 'bhsn'.
.TP .TP
.B \-n, \-\-no\-control .B \-n, \-\-no\-control

View file

@ -98,6 +98,7 @@ enum {
OPT_HID_KEYBOARD_DEPRECATED, OPT_HID_KEYBOARD_DEPRECATED,
OPT_HID_MOUSE_DEPRECATED, OPT_HID_MOUSE_DEPRECATED,
OPT_NO_WINDOW, OPT_NO_WINDOW,
OPT_MOUSE_BIND,
}; };
struct sc_option { struct sc_option {
@ -352,11 +353,9 @@ static const struct sc_option options[] = {
"device.", "device.",
}, },
{ {
// deprecated
.longopt_id = OPT_FORWARD_ALL_CLICKS, .longopt_id = OPT_FORWARD_ALL_CLICKS,
.longopt = "forward-all-clicks", .longopt = "forward-all-clicks",
.text = "By default, right-click triggers BACK (or POWER on) and "
"middle-click triggers HOME. This option disables these "
"shortcuts and forwards the clicks to the device instead.",
}, },
{ {
.shortopt = 'h', .shortopt = 'h',
@ -490,6 +489,23 @@ static const struct sc_option options[] = {
"control of the mouse back to the computer.\n" "control of the mouse back to the computer.\n"
"Also see --keyboard.", "Also see --keyboard.",
}, },
{
.longopt_id = OPT_MOUSE_BIND,
.longopt = "mouse-bind",
.argdesc = "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"
"Each character must be one of the following:\n"
" '+': forward the click to the device\n"
" '-': ignore the click\n"
" 'b': trigger shortcut BACK (or turn screen on if off)\n"
" 'h': trigger shortcut HOME\n"
" 's': trigger shortcut APP_SWITCH\n"
" 'n': trigger shortcut \"expand notification panel\"\n"
"Default is 'bhsn'.",
},
{ {
.shortopt = 'n', .shortopt = 'n',
.longopt = "no-control", .longopt = "no-control",
@ -2043,6 +2059,58 @@ parse_pause_on_exit(const char *s, enum sc_pause_on_exit *pause_on_exit) {
} }
static bool
parse_mouse_binding(char c, enum sc_mouse_binding *b) {
switch (c) {
case '+':
*b = SC_MOUSE_BINDING_CLICK;
return true;
case '-':
*b = SC_MOUSE_BINDING_DISABLED;
return true;
case 'b':
*b = SC_MOUSE_BINDING_BACK;
return true;
case 'h':
*b = SC_MOUSE_BINDING_HOME;
return true;
case 's':
*b = SC_MOUSE_BINDING_APP_SWITCH;
return true;
case 'n':
*b = SC_MOUSE_BINDING_EXPAND_NOTIFICATION_PANEL;
return true;
default:
LOGE("Invalid mouse binding: '%c' "
"(expected '+', '-', 'b', 'h', 's' or 'n')", c);
return false;
}
}
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);
return false;
}
if (!parse_mouse_binding(s[0], &mb->right_click)) {
return false;
}
if (!parse_mouse_binding(s[1], &mb->middle_click)) {
return false;
}
if (!parse_mouse_binding(s[2], &mb->click4)) {
return false;
}
if (!parse_mouse_binding(s[3], &mb->click5)) {
return false;
}
return true;
}
static bool static bool
parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[], parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
const char *optstring, const struct option *longopts) { const char *optstring, const struct option *longopts) {
@ -2125,6 +2193,11 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
return false; return false;
} }
break; break;
case OPT_MOUSE_BIND:
if (!parse_mouse_bindings(optarg, &opts->mouse_bindings)) {
return false;
}
break;
case OPT_HID_MOUSE_DEPRECATED: case OPT_HID_MOUSE_DEPRECATED:
LOGE("--hid-mouse has been removed, use --mouse=aoa or " LOGE("--hid-mouse has been removed, use --mouse=aoa or "
"--mouse=uhid instead."); "--mouse=uhid instead.");
@ -2322,7 +2395,14 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
} }
break; break;
case OPT_FORWARD_ALL_CLICKS: case OPT_FORWARD_ALL_CLICKS:
opts->forward_all_clicks = true; 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,
};
break; break;
case OPT_LEGACY_PASTE: case OPT_LEGACY_PASTE:
opts->legacy_paste = true; opts->legacy_paste = true;

View file

@ -9,6 +9,7 @@
#include <SDL2/SDL_events.h> #include <SDL2/SDL_events.h>
#include "coords.h" #include "coords.h"
#include "options.h"
/* The representation of input events in scrcpy is very close to the SDL API, /* The representation of input events in scrcpy is very close to the SDL API,
* for simplicity. * for simplicity.
@ -437,15 +438,21 @@ sc_mouse_button_from_sdl(uint8_t button) {
static inline uint8_t static inline uint8_t
sc_mouse_buttons_state_from_sdl(uint32_t buttons_state, sc_mouse_buttons_state_from_sdl(uint32_t buttons_state,
bool forward_all_clicks) { const struct sc_mouse_bindings *mb) {
assert(buttons_state < 0x100); // fits in uint8_t assert(buttons_state < 0x100); // fits in uint8_t
uint8_t mask = SC_MOUSE_BUTTON_LEFT; uint8_t mask = SC_MOUSE_BUTTON_LEFT;
if (forward_all_clicks) { if (!mb || mb->right_click == SC_MOUSE_BINDING_CLICK) {
mask |= SC_MOUSE_BUTTON_RIGHT mask |= SC_MOUSE_BUTTON_RIGHT;
| SC_MOUSE_BUTTON_MIDDLE }
| SC_MOUSE_BUTTON_X1 if (!mb || mb->middle_click == SC_MOUSE_BINDING_CLICK) {
| SC_MOUSE_BUTTON_X2; 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; return buttons_state & mask;

View file

@ -52,6 +52,14 @@ is_shortcut_key(struct sc_input_manager *im, SDL_Keycode keycode) {
|| (im->sdl_shortcut_mods & KMOD_RGUI && keycode == SDLK_RGUI); || (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 void
sc_input_manager_init(struct sc_input_manager *im, sc_input_manager_init(struct sc_input_manager *im,
const struct sc_input_manager_params *params) { const struct sc_input_manager_params *params) {
@ -67,7 +75,9 @@ sc_input_manager_init(struct sc_input_manager *im,
im->kp = params->kp; im->kp = params->kp;
im->mp = params->mp; im->mp = params->mp;
im->forward_all_clicks = params->forward_all_clicks; 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->legacy_paste = params->legacy_paste;
im->clipboard_autosync = params->clipboard_autosync; im->clipboard_autosync = params->clipboard_autosync;
@ -366,8 +376,8 @@ simulate_virtual_finger(struct sc_input_manager *im,
msg.inject_touch_event.position.screen_size = im->screen->frame_size; msg.inject_touch_event.position.screen_size = im->screen->frame_size;
msg.inject_touch_event.position.point = point; msg.inject_touch_event.position.point = point;
msg.inject_touch_event.pointer_id = msg.inject_touch_event.pointer_id =
im->forward_all_clicks ? POINTER_ID_VIRTUAL_MOUSE im->has_secondary_click ? POINTER_ID_VIRTUAL_MOUSE
: POINTER_ID_VIRTUAL_FINGER; : POINTER_ID_VIRTUAL_FINGER;
msg.inject_touch_event.pressure = up ? 0.0f : 1.0f; msg.inject_touch_event.pressure = up ? 0.0f : 1.0f;
msg.inject_touch_event.action_button = 0; msg.inject_touch_event.action_button = 0;
msg.inject_touch_event.buttons = 0; msg.inject_touch_event.buttons = 0;
@ -652,13 +662,12 @@ sc_input_manager_process_mouse_motion(struct sc_input_manager *im,
struct sc_mouse_motion_event evt = { struct sc_mouse_motion_event evt = {
.position = sc_input_manager_get_position(im, event->x, event->y), .position = sc_input_manager_get_position(im, event->x, event->y),
.pointer_id = im->forward_all_clicks ? POINTER_ID_MOUSE .pointer_id = im->has_secondary_click ? POINTER_ID_MOUSE
: POINTER_ID_GENERIC_FINGER, : POINTER_ID_GENERIC_FINGER,
.xrel = event->xrel, .xrel = event->xrel,
.yrel = event->yrel, .yrel = event->yrel,
.buttons_state = .buttons_state =
sc_mouse_buttons_state_from_sdl(event->state, sc_mouse_buttons_state_from_sdl(event->state, &im->mouse_bindings),
im->forward_all_clicks),
}; };
assert(im->mp->ops->process_mouse_motion); assert(im->mp->ops->process_mouse_motion);
@ -709,6 +718,25 @@ sc_input_manager_process_touch(struct sc_input_manager *im,
im->mp->ops->process_touch(im->mp, &evt); im->mp->ops->process_touch(im->mp, &evt);
} }
static enum sc_mouse_binding
sc_input_manager_get_binding(const struct sc_mouse_bindings *bindings,
uint8_t sdl_button) {
switch (sdl_button) {
case SDL_BUTTON_LEFT:
return SC_MOUSE_BINDING_CLICK;
case SDL_BUTTON_RIGHT:
return bindings->right_click;
case SDL_BUTTON_MIDDLE:
return bindings->middle_click;
case SDL_BUTTON_X1:
return bindings->click4;
case SDL_BUTTON_X2:
return bindings->click5;
default:
return SC_MOUSE_BINDING_DISABLED;
}
}
static void static void
sc_input_manager_process_mouse_button(struct sc_input_manager *im, sc_input_manager_process_mouse_button(struct sc_input_manager *im,
const SDL_MouseButtonEvent *event) { const SDL_MouseButtonEvent *event) {
@ -720,28 +748,42 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im,
bool control = im->controller; bool control = im->controller;
bool paused = im->screen->paused; bool paused = im->screen->paused;
bool down = event->type == SDL_MOUSEBUTTONDOWN; bool down = event->type == SDL_MOUSEBUTTONDOWN;
if (control && !paused && !im->forward_all_clicks) { if (control && !paused) {
enum sc_action action = down ? SC_ACTION_DOWN : SC_ACTION_UP; enum sc_action action = down ? SC_ACTION_DOWN : SC_ACTION_UP;
if (im->kp && event->button == SDL_BUTTON_X1) { enum sc_mouse_binding binding =
action_app_switch(im, action); sc_input_manager_get_binding(&im->mouse_bindings, event->button);
return; switch (binding) {
} case SC_MOUSE_BINDING_DISABLED:
if (event->button == SDL_BUTTON_X2 && down) { // ignore click
if (event->clicks < 2) { return;
expand_notification_panel(im); case SC_MOUSE_BINDING_BACK:
} else { if (im->kp) {
expand_settings_panel(im); press_back_or_turn_screen_on(im, action);
} }
return; return;
} case SC_MOUSE_BINDING_HOME:
if (im->kp && event->button == SDL_BUTTON_RIGHT) { if (im->kp) {
press_back_or_turn_screen_on(im, action); action_home(im, action);
return; }
} return;
if (im->kp && event->button == SDL_BUTTON_MIDDLE) { case SC_MOUSE_BINDING_APP_SWITCH:
action_home(im, action); if (im->kp) {
return; action_app_switch(im, action);
}
return;
case SC_MOUSE_BINDING_EXPAND_NOTIFICATION_PANEL:
if (down) {
if (event->clicks < 2) {
expand_notification_panel(im);
} else {
expand_settings_panel(im);
}
}
return;
default:
assert(binding == SC_MOUSE_BINDING_CLICK);
break;
} }
} }
@ -774,11 +816,10 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im,
.position = sc_input_manager_get_position(im, event->x, event->y), .position = sc_input_manager_get_position(im, event->x, event->y),
.action = sc_action_from_sdl_mousebutton_type(event->type), .action = sc_action_from_sdl_mousebutton_type(event->type),
.button = sc_mouse_button_from_sdl(event->button), .button = sc_mouse_button_from_sdl(event->button),
.pointer_id = im->forward_all_clicks ? POINTER_ID_MOUSE .pointer_id = im->has_secondary_click ? POINTER_ID_MOUSE
: POINTER_ID_GENERIC_FINGER, : POINTER_ID_GENERIC_FINGER,
.buttons_state = .buttons_state = sc_mouse_buttons_state_from_sdl(sdl_buttons_state,
sc_mouse_buttons_state_from_sdl(sdl_buttons_state, &im->mouse_bindings),
im->forward_all_clicks),
}; };
assert(im->mp->ops->process_mouse_click); assert(im->mp->ops->process_mouse_click);
@ -854,8 +895,8 @@ sc_input_manager_process_mouse_wheel(struct sc_input_manager *im,
.hscroll = CLAMP(event->x, -1, 1), .hscroll = CLAMP(event->x, -1, 1),
.vscroll = CLAMP(event->y, -1, 1), .vscroll = CLAMP(event->y, -1, 1),
#endif #endif
.buttons_state = .buttons_state = sc_mouse_buttons_state_from_sdl(buttons,
sc_mouse_buttons_state_from_sdl(buttons, im->forward_all_clicks), &im->mouse_bindings),
}; };
im->mp->ops->process_mouse_scroll(im->mp, &evt); im->mp->ops->process_mouse_scroll(im->mp, &evt);

View file

@ -22,7 +22,8 @@ struct sc_input_manager {
struct sc_key_processor *kp; struct sc_key_processor *kp;
struct sc_mouse_processor *mp; struct sc_mouse_processor *mp;
bool forward_all_clicks; struct sc_mouse_bindings mouse_bindings;
bool has_secondary_click;
bool legacy_paste; bool legacy_paste;
bool clipboard_autosync; bool clipboard_autosync;
@ -49,7 +50,7 @@ struct sc_input_manager_params {
struct sc_key_processor *kp; struct sc_key_processor *kp;
struct sc_mouse_processor *mp; struct sc_mouse_processor *mp;
bool forward_all_clicks; struct sc_mouse_bindings mouse_bindings;
bool legacy_paste; bool legacy_paste;
bool clipboard_autosync; bool clipboard_autosync;
uint8_t shortcut_mods; // OR of enum sc_shortcut_mod values uint8_t shortcut_mods; // OR of enum sc_shortcut_mod values

View file

@ -23,6 +23,12 @@ const struct scrcpy_options scrcpy_options_default = {
.record_format = SC_RECORD_FORMAT_AUTO, .record_format = SC_RECORD_FORMAT_AUTO,
.keyboard_input_mode = SC_KEYBOARD_INPUT_MODE_AUTO, .keyboard_input_mode = SC_KEYBOARD_INPUT_MODE_AUTO,
.mouse_input_mode = SC_MOUSE_INPUT_MODE_AUTO, .mouse_input_mode = SC_MOUSE_INPUT_MODE_AUTO,
.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,
},
.camera_facing = SC_CAMERA_FACING_ANY, .camera_facing = SC_CAMERA_FACING_ANY,
.port_range = { .port_range = {
.first = DEFAULT_LOCAL_PORT_RANGE_FIRST, .first = DEFAULT_LOCAL_PORT_RANGE_FIRST,
@ -68,7 +74,6 @@ const struct scrcpy_options scrcpy_options_default = {
.force_adb_forward = false, .force_adb_forward = false,
.disable_screensaver = false, .disable_screensaver = false,
.forward_key_repeat = true, .forward_key_repeat = true,
.forward_all_clicks = false,
.legacy_paste = false, .legacy_paste = false,
.power_off_on_close = false, .power_off_on_close = false,
.clipboard_autosync = true, .clipboard_autosync = true,

View file

@ -155,6 +155,22 @@ enum sc_mouse_input_mode {
SC_MOUSE_INPUT_MODE_AOA, SC_MOUSE_INPUT_MODE_AOA,
}; };
enum sc_mouse_binding {
SC_MOUSE_BINDING_DISABLED,
SC_MOUSE_BINDING_CLICK,
SC_MOUSE_BINDING_BACK,
SC_MOUSE_BINDING_HOME,
SC_MOUSE_BINDING_APP_SWITCH,
SC_MOUSE_BINDING_EXPAND_NOTIFICATION_PANEL,
};
struct sc_mouse_bindings {
enum sc_mouse_binding right_click;
enum sc_mouse_binding middle_click;
enum sc_mouse_binding click4;
enum sc_mouse_binding click5;
};
enum sc_key_inject_mode { enum sc_key_inject_mode {
// Inject special keys, letters and space as key events. // Inject special keys, letters and space as key events.
// Inject numbers and punctuation as text events. // Inject numbers and punctuation as text events.
@ -208,6 +224,7 @@ struct scrcpy_options {
enum sc_record_format record_format; enum sc_record_format record_format;
enum sc_keyboard_input_mode keyboard_input_mode; enum sc_keyboard_input_mode keyboard_input_mode;
enum sc_mouse_input_mode mouse_input_mode; enum sc_mouse_input_mode mouse_input_mode;
struct sc_mouse_bindings mouse_bindings;
enum sc_camera_facing camera_facing; enum sc_camera_facing camera_facing;
struct sc_port_range port_range; struct sc_port_range port_range;
uint32_t tunnel_host; uint32_t tunnel_host;
@ -250,7 +267,6 @@ struct scrcpy_options {
bool force_adb_forward; bool force_adb_forward;
bool disable_screensaver; bool disable_screensaver;
bool forward_key_repeat; bool forward_key_repeat;
bool forward_all_clicks;
bool legacy_paste; bool legacy_paste;
bool power_off_on_close; bool power_off_on_close;
bool clipboard_autosync; bool clipboard_autosync;

View file

@ -712,7 +712,7 @@ scrcpy(struct scrcpy_options *options) {
.fp = fp, .fp = fp,
.kp = kp, .kp = kp,
.mp = mp, .mp = mp,
.forward_all_clicks = options->forward_all_clicks, .mouse_bindings = options->mouse_bindings,
.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,

View file

@ -481,7 +481,7 @@ sc_screen_init(struct sc_screen *screen,
.screen = screen, .screen = screen,
.kp = params->kp, .kp = params->kp,
.mp = params->mp, .mp = params->mp,
.forward_all_clicks = params->forward_all_clicks, .mouse_bindings = params->mouse_bindings,
.legacy_paste = params->legacy_paste, .legacy_paste = params->legacy_paste,
.clipboard_autosync = params->clipboard_autosync, .clipboard_autosync = params->clipboard_autosync,
.shortcut_mods = params->shortcut_mods, .shortcut_mods = params->shortcut_mods,

View file

@ -79,7 +79,7 @@ struct sc_screen_params {
struct sc_key_processor *kp; struct sc_key_processor *kp;
struct sc_mouse_processor *mp; struct sc_mouse_processor *mp;
bool forward_all_clicks; struct sc_mouse_bindings mouse_bindings;
bool legacy_paste; bool legacy_paste;
bool clipboard_autosync; bool clipboard_autosync;
uint8_t shortcut_mods; // OR of enum sc_shortcut_mod values uint8_t shortcut_mods; // OR of enum sc_shortcut_mod values

View file

@ -169,7 +169,7 @@ sc_screen_otg_process_mouse_motion(struct sc_screen_otg *screen,
// .position not used for HID events // .position not used for HID events
.xrel = event->xrel, .xrel = event->xrel,
.yrel = event->yrel, .yrel = event->yrel,
.buttons_state = sc_mouse_buttons_state_from_sdl(event->state, true), .buttons_state = sc_mouse_buttons_state_from_sdl(event->state, NULL),
}; };
assert(mp->ops->process_mouse_motion); assert(mp->ops->process_mouse_motion);
@ -189,7 +189,7 @@ sc_screen_otg_process_mouse_button(struct sc_screen_otg *screen,
.action = sc_action_from_sdl_mousebutton_type(event->type), .action = sc_action_from_sdl_mousebutton_type(event->type),
.button = sc_mouse_button_from_sdl(event->button), .button = sc_mouse_button_from_sdl(event->button),
.buttons_state = .buttons_state =
sc_mouse_buttons_state_from_sdl(sdl_buttons_state, true), sc_mouse_buttons_state_from_sdl(sdl_buttons_state, NULL),
}; };
assert(mp->ops->process_mouse_click); assert(mp->ops->process_mouse_click);
@ -209,7 +209,7 @@ sc_screen_otg_process_mouse_wheel(struct sc_screen_otg *screen,
.hscroll = event->x, .hscroll = event->x,
.vscroll = event->y, .vscroll = event->y,
.buttons_state = .buttons_state =
sc_mouse_buttons_state_from_sdl(sdl_buttons_state, true), sc_mouse_buttons_state_from_sdl(sdl_buttons_state, NULL),
}; };
assert(mp->ops->process_mouse_scroll); assert(mp->ops->process_mouse_scroll);

View file

@ -106,15 +106,6 @@ only inverts _x_.
This only works for the default mouse mode (`--mouse=sdk`). This only works for the default mouse mode (`--mouse=sdk`).
## Right-click and middle-click
By default, right-click triggers BACK (or POWER on) and middle-click triggers
HOME. To disable these shortcuts and forward the clicks to the device instead:
```bash
scrcpy --forward-all-clicks
```
## File drop ## File drop
### Install APK ### Install APK

View file

@ -68,3 +68,41 @@ debugging disabled (see [OTG](otg.md)).
Note: On Windows, it may only work in [OTG mode](otg.md), not while mirroring Note: On Windows, it may only work in [OTG mode](otg.md), not while mirroring
(it is not possible to open a USB device if it is already open by another (it is not possible to open a USB device if it is already open by another
process like the _adb daemon_). process like the _adb daemon_).
## Mouse bindings
By default, 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.
The shortcuts can be configured using `--mouse-bind=xxxx`. The argument must be
exactly 4 characters, one for each secondary click:
```
--mouse-bind=xxxx
^^^^
||||
||| `- 5th click
|| `-- 4th click
| `--- middle click
`---- right click
```
Each character must be one of the following:
- `+`: forward the click to the device
- `-`: ignore the click
- `b`: trigger shortcut BACK (or turn screen on if off)
- `h`: trigger shortcut HOME
- `s`: trigger shortcut APP_SWITCH
- `n`: trigger shortcut "expand notification panel"
For example:
```bash
scrcpy --mouse-bind=bhsn # the default mode
scrcpy --mouse-bind=++++ # forward all clicks
scrcpy --mouse-bind=++bh # forward right and middle clicks,
# use 4th and 5th for BACK and HOME
```