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);