From 487a6b9cf4a2ddc777a336d4b4c747ed33fa87a6 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Mon, 8 Jul 2024 16:17:11 +0200 Subject: [PATCH 1/8] Remove top-level const For consistency, never use top-level const for local variables. PR #5076 --- app/src/input_manager.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/input_manager.c b/app/src/input_manager.c index 43b10d2de8..9da3a72799 100644 --- a/app/src/input_manager.c +++ b/app/src/input_manager.c @@ -846,9 +846,9 @@ 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; + SDL_Keymod keymod = SDL_GetModState(); + bool ctrl_pressed = keymod & KMOD_CTRL; + bool shift_pressed = keymod & KMOD_SHIFT; if (event->button == SDL_BUTTON_LEFT && ((down && !im->vfinger_down && ((ctrl_pressed && !shift_pressed) || From 6d98766cd5cf93804fd14d1eb9765ab9e1357cb1 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Mon, 8 Jul 2024 16:18:06 +0200 Subject: [PATCH 2/8] Simplify boolean condition using XOR (A && !B) || (!A && B) <==> A ^ B PR #5076 --- app/src/input_manager.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/src/input_manager.c b/app/src/input_manager.c index 9da3a72799..96075f84dd 100644 --- a/app/src/input_manager.c +++ b/app/src/input_manager.c @@ -850,9 +850,7 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im, bool ctrl_pressed = keymod & KMOD_CTRL; 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 && (ctrl_pressed ^ shift_pressed)) || (!down && im->vfinger_down))) { struct sc_point mouse = sc_screen_convert_window_to_frame_coords(im->screen, event->x, From 0bce4d7f56a4d109d450c4e41e4387a086c820ef Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Mon, 8 Jul 2024 16:29:47 +0200 Subject: [PATCH 3/8] Add missing SC_ prefix for pointer id constants PR #5076 --- app/src/control_msg.c | 8 ++++---- app/src/control_msg.h | 8 ++++---- app/src/input_manager.c | 12 ++++++------ 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/app/src/control_msg.c b/app/src/control_msg.c index b3da5fe5ae..5a80004032 100644 --- a/app/src/control_msg.c +++ b/app/src/control_msg.c @@ -64,13 +64,13 @@ 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: + case SC_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..2ec7b5be25 100644 --- a/app/src/control_msg.h +++ b/app/src/control_msg.h @@ -18,12 +18,12 @@ // 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_MOUSE UINT64_C(-3) +#define SC_POINTER_ID_VIRTUAL_FINGER UINT64_C(-4) enum sc_control_msg_type { SC_CONTROL_MSG_TYPE_INJECT_KEYCODE, diff --git a/app/src/input_manager.c b/app/src/input_manager.c index 96075f84dd..415c12936d 100644 --- a/app/src/input_manager.c +++ b/app/src/input_manager.c @@ -376,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.point = point; msg.inject_touch_event.pointer_id = - im->has_secondary_click ? POINTER_ID_VIRTUAL_MOUSE - : POINTER_ID_VIRTUAL_FINGER; + im->has_secondary_click ? SC_POINTER_ID_VIRTUAL_MOUSE + : 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,8 +662,8 @@ 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->has_secondary_click ? SC_POINTER_ID_MOUSE + : SC_POINTER_ID_GENERIC_FINGER, .xrel = event->xrel, .yrel = event->yrel, .buttons_state = @@ -817,8 +817,8 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im, .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, + .pointer_id = im->has_secondary_click ? SC_POINTER_ID_MOUSE + : SC_POINTER_ID_GENERIC_FINGER, .buttons_state = sc_mouse_buttons_state_from_sdl(sdl_buttons_state, &im->mouse_bindings), }; From 6808288823239b0f3a76f9be377e4de82e91b35a Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Mon, 8 Jul 2024 16:38:15 +0200 Subject: [PATCH 4/8] Make pointer id independent of mouse bindings The device source (MOUSE or FINGER) to use depended on whether a secondary click was possible via mouse bindings. As a first step, always use a mouse source to break this dependency. Note that this change might cause regressions in some (unknown) cases (refs f70359f14fb13f277c65b96f43ec83aba4722457), but hopefully not. Further commits will restore a finger source in some specific use cases, but independent of secondary clicks. Refs #5055 Fixes #5067 PR #5076 --- app/src/input_manager.c | 20 +++----------------- app/src/input_manager.h | 1 - 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/app/src/input_manager.c b/app/src/input_manager.c index 415c12936d..71c4434bc6 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; @@ -375,9 +365,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 ? SC_POINTER_ID_VIRTUAL_MOUSE - : SC_POINTER_ID_VIRTUAL_FINGER; + msg.inject_touch_event.pointer_id = SC_POINTER_ID_VIRTUAL_MOUSE; msg.inject_touch_event.pressure = up ? 0.0f : 1.0f; msg.inject_touch_event.action_button = 0; msg.inject_touch_event.buttons = 0; @@ -662,8 +650,7 @@ 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 ? SC_POINTER_ID_MOUSE - : SC_POINTER_ID_GENERIC_FINGER, + .pointer_id = SC_POINTER_ID_MOUSE, .xrel = event->xrel, .yrel = event->yrel, .buttons_state = @@ -817,8 +804,7 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im, .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 ? SC_POINTER_ID_MOUSE - : SC_POINTER_ID_GENERIC_FINGER, + .pointer_id = SC_POINTER_ID_MOUSE, .buttons_state = sc_mouse_buttons_state_from_sdl(sdl_buttons_state, &im->mouse_bindings), }; diff --git a/app/src/input_manager.h b/app/src/input_manager.h index 03c42fe676..d5a5a64d3f 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; From 51fee79bf50b124223523eb51c437c1267c2724a Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Mon, 8 Jul 2024 16:27:59 +0200 Subject: [PATCH 5/8] Use finger source when a pointer is simulated For pinch-to-zoom, rotation and tilt simulation, always use a finger source (instead of a mouse) for both pointers (the real one and the simulated one). A "virtual" mouse does not work on all devices (e.g. on Pixel 8). PR #5076 --- app/src/input_manager.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/app/src/input_manager.c b/app/src/input_manager.c index 71c4434bc6..6fbd801c95 100644 --- a/app/src/input_manager.c +++ b/app/src/input_manager.c @@ -365,7 +365,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 = SC_POINTER_ID_VIRTUAL_MOUSE; + 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; @@ -650,7 +650,8 @@ 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 = SC_POINTER_ID_MOUSE, + .pointer_id = im->vfinger_down ? SC_POINTER_ID_GENERIC_FINGER + : SC_POINTER_ID_MOUSE, .xrel = event->xrel, .yrel = event->yrel, .buttons_state = @@ -800,11 +801,20 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im, uint32_t sdl_buttons_state = SDL_GetMouseState(NULL, NULL); + 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)); + 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 = SC_POINTER_ID_MOUSE, + .pointer_id = use_finger ? SC_POINTER_ID_GENERIC_FINGER + : SC_POINTER_ID_MOUSE, .buttons_state = sc_mouse_buttons_state_from_sdl(sdl_buttons_state, &im->mouse_bindings), }; @@ -832,12 +842,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. - SDL_Keymod keymod = SDL_GetModState(); - bool ctrl_pressed = keymod & KMOD_CTRL; - bool shift_pressed = keymod & KMOD_SHIFT; - if (event->button == SDL_BUTTON_LEFT && - ((down && !im->vfinger_down && (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); From 86b8286217b0909bf409e68ac1885ee59cc4cefc Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Mon, 8 Jul 2024 16:33:10 +0200 Subject: [PATCH 6/8] Remove unused virtual mouse PR #5076 --- app/src/control_msg.c | 2 -- app/src/control_msg.h | 3 +-- server/src/main/java/com/genymobile/scrcpy/Controller.java | 5 ++--- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/app/src/control_msg.c b/app/src/control_msg.c index 5a80004032..9b0fab6773 100644 --- a/app/src/control_msg.c +++ b/app/src/control_msg.c @@ -68,8 +68,6 @@ get_well_known_pointer_id_name(uint64_t pointer_id) { return "mouse"; case SC_POINTER_ID_GENERIC_FINGER: return "finger"; - case SC_POINTER_ID_VIRTUAL_MOUSE: - return "vmouse"; case SC_POINTER_ID_VIRTUAL_FINGER: return "vfinger"; default: diff --git a/app/src/control_msg.h b/app/src/control_msg.h index 2ec7b5be25..80714096b7 100644 --- a/app/src/control_msg.h +++ b/app/src/control_msg.h @@ -22,8 +22,7 @@ #define SC_POINTER_ID_GENERIC_FINGER UINT64_C(-2) // Used for injecting an additional virtual pointer for pinch-to-zoom -#define SC_POINTER_ID_VIRTUAL_MOUSE UINT64_C(-3) -#define SC_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/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); From 6baea57987a1867f2157f5c1001e77ca3cb1c6c5 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Tue, 9 Jul 2024 18:37:53 +0200 Subject: [PATCH 7/8] Track mouse buttons state manually The buttons state was tracked by SDL_GetMouseState(), and scrcpy applied a mask to ignore buttons used for shortcuts. Instead, track the buttons actually pressed (ignoring shortcuts) manually, to prepare the introduction of more dynamic mouse shortcuts. PR #5076 --- app/src/input_events.h | 20 +++----------------- app/src/input_manager.c | 24 +++++++++++++++++------- app/src/input_manager.h | 2 ++ app/src/usb/screen_otg.c | 8 +++----- 4 files changed, 25 insertions(+), 29 deletions(-) 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 6fbd801c95..2cc34afac9 100644 --- a/app/src/input_manager.c +++ b/app/src/input_manager.c @@ -77,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; @@ -654,8 +656,7 @@ sc_input_manager_process_mouse_motion(struct sc_input_manager *im, : 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); @@ -736,6 +737,13 @@ 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; + } + if (control && !paused) { enum sc_action action = down ? SC_ACTION_DOWN : SC_ACTION_UP; @@ -799,7 +807,10 @@ 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; + } SDL_Keymod keymod = SDL_GetModState(); bool ctrl_pressed = keymod & KMOD_CTRL; @@ -815,8 +826,7 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im, .button = sc_mouse_button_from_sdl(event->button), .pointer_id = use_finger ? SC_POINTER_ID_GENERIC_FINGER : SC_POINTER_ID_MOUSE, - .buttons_state = sc_mouse_buttons_state_from_sdl(sdl_buttons_state, - &im->mouse_bindings), + .buttons_state = im->mouse_buttons_state, }; assert(im->mp->ops->process_mouse_click); @@ -875,6 +885,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), @@ -885,8 +896,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 d5a5a64d3f..88558549b3 100644 --- a/app/src/input_manager.h +++ b/app/src/input_manager.h @@ -32,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/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); From 9989668226f100534452e0af812807562ff5212f Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Tue, 9 Jul 2024 20:45:49 +0200 Subject: [PATCH 8/8] Add mouse secondary bindings Add secondary bindings (Shift+click) for mouse buttons. In addition to: --mouse-bind=xxxx It is now possible to pass a sequence of secondary bindings: --mouse-bind=xxxx:xxxx <--> <--> primary secondary bindings bindings If the second sequence is omitted, then it is the same as the first one. By default, for SDK mouse, primary bindings trigger shortcuts and secondary bindings forward all clicks. For AOA and UHID, the default bindings are reversed: all clicks are forwarded by default, whereas pressing Shift+click trigger shortcuts. --mouse-bind=bhsn:++++ # default for SDK --mouse-bind=++++:bhsn # default for AOA and UHID Refs 035d60cf5d3f4c83d48735b4cb4cd108a5b5f413 Refs f5e6b8092afd82bab402e7c2c3d00b1719f9bb57 Fixes #5055 PR #5076 --- app/scrcpy.1 | 10 ++-- app/src/cli.c | 115 ++++++++++++++++++++++++++++------------ app/src/input_manager.c | 14 +++-- app/src/options.c | 16 ++++-- app/src/options.h | 7 ++- doc/mouse.md | 52 +++++++++++++----- 6 files changed, 154 insertions(+), 60 deletions(-) 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/input_manager.c b/app/src/input_manager.c index 2cc34afac9..d3c94d03bf 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 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/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 ```