Compare commits

..

10 Commits

Author SHA1 Message Date
Romain Vimont
9989668226 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 035d60cf5d
Refs f5e6b8092a
Fixes #5055 <https://github.com/Genymobile/scrcpy/issues/5055>
PR #5076 <https://github.com/Genymobile/scrcpy/pull/5076>
2024-07-11 12:04:09 +02:00
Romain Vimont
6baea57987 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 <https://github.com/Genymobile/scrcpy/pull/5076>
2024-07-11 12:04:09 +02:00
Romain Vimont
86b8286217 Remove unused virtual mouse
PR #5076 <https://github.com/Genymobile/scrcpy/pull/5076>
2024-07-11 12:04:09 +02:00
Romain Vimont
51fee79bf5 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 <https://github.com/Genymobile/scrcpy/pull/5076>
2024-07-11 12:04:09 +02:00
Romain Vimont
6808288823 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 f70359f14f), but hopefully not.

Further commits will restore a finger source in some specific use cases,
but independent of secondary clicks.

Refs #5055 <https://github.com/Genymobile/scrcpy/issues/5055>
Fixes #5067 <https://github.com/Genymobile/scrcpy/issues/5067>
PR #5076 <https://github.com/Genymobile/scrcpy/pull/5076>
2024-07-11 12:04:09 +02:00
Romain Vimont
0bce4d7f56 Add missing SC_ prefix for pointer id constants
PR #5076 <https://github.com/Genymobile/scrcpy/pull/5076>
2024-07-11 12:04:09 +02:00
Romain Vimont
6d98766cd5 Simplify boolean condition using XOR
(A && !B) || (!A && B) <==> A ^ B

PR #5076 <https://github.com/Genymobile/scrcpy/pull/5076>
2024-07-11 12:04:09 +02:00
Romain Vimont
487a6b9cf4 Remove top-level const
For consistency, never use top-level const for local variables.

PR #5076 <https://github.com/Genymobile/scrcpy/pull/5076>
2024-07-11 12:04:09 +02:00
Romain Vimont
b50f9eb41d Add workaround for Skyworth devices
The vendor-modified ROM of Skyworth devices needs a valid app
info/context.

Fixes #4922 <https://github.com/Genymobile/scrcpy/issues/4922>
2024-07-08 17:16:01 +02:00
Yan
46041e0cc0 Always initialize display->gl_context on macOS
Otherwise SDL_GL_DeleteContext() tried to access an uninitialized
pointer upon exit when not using the OpenGL renderer.

SDL_GL_DeleteContext() doesn't try to delete a NULL pointer, so no need
to check for that.

Fixes #5057 <https://github.com/Genymobile/scrcpy/issues/5057>
PR #5058 <https://github.com/Genymobile/scrcpy/pull/5058>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2024-07-06 00:11:48 +02:00
14 changed files with 207 additions and 127 deletions

View File

@@ -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

View File

@@ -493,11 +493,17 @@ static const struct sc_option options[] = {
{
.longopt_id = OPT_MOUSE_BIND,
.longopt = "mouse-bind",
.argdesc = "xxxx",
.argdesc = "xxxx[:xxxx]",
.text = "Configure bindings of secondary clicks.\n"
"The argument must be exactly 4 characters, one for each "
"secondary click (in order: right click, middle click, 4th "
"click, 5th click).\n"
"The argument must be one or two sequences (separated by ':') "
"of exactly 4 characters, one for each secondary click (in "
"order: right click, middle click, 4th click, 5th click).\n"
"The first sequence defines the primary bindings, used when a "
"mouse button is pressed alone. The second sequence defines "
"the secondary bindings, used when a mouse button is pressed "
"while the Shift key is held.\n"
"If the second sequence of bindings is omitted, then it is the "
"same as the first one.\n"
"Each character must be one of the following:\n"
" '+': forward the click to the device\n"
" '-': ignore the click\n"
@@ -505,7 +511,8 @@ static const struct sc_option options[] = {
" 'h': trigger shortcut HOME\n"
" 's': trigger shortcut APP_SWITCH\n"
" 'n': trigger shortcut \"expand notification panel\"\n"
"Default is 'bhsn' for SDK mouse, and '++++' for AOA and UHID.",
"Default is 'bhsn:++++' for SDK mouse, and '++++:bhsn' for AOA "
"and UHID.",
},
{
.shortopt = 'n',
@@ -2095,24 +2102,46 @@ parse_mouse_binding(char c, enum sc_mouse_binding *b) {
}
static bool
parse_mouse_bindings(const char *s, struct sc_mouse_bindings *mb) {
if (strlen(s) != 4) {
LOGE("Invalid mouse bindings: '%s' (expected exactly 4 characters from "
"{'+', '-', 'b', 'h', 's', 'n'})", s);
parse_mouse_binding_set(const char *s, struct sc_mouse_binding_set *mbs) {
assert(strlen(s) >= 4);
if (!parse_mouse_binding(s[0], &mbs->right_click)) {
return false;
}
if (!parse_mouse_binding(s[1], &mbs->middle_click)) {
return false;
}
if (!parse_mouse_binding(s[2], &mbs->click4)) {
return false;
}
if (!parse_mouse_binding(s[3], &mbs->click5)) {
return false;
}
if (!parse_mouse_binding(s[0], &mb->right_click)) {
return true;
}
static bool
parse_mouse_bindings(const char *s, struct sc_mouse_bindings *mb) {
size_t len = strlen(s);
// either "xxxx" or "xxxx:xxxx"
if (len != 4 && (len != 9 || s[4] != ':')) {
LOGE("Invalid mouse bindings: '%s' (expected 'xxxx' or 'xxxx:xxxx', "
"with each 'x' being in {'+', '-', 'b', 'h', 's', 'n'})", s);
return false;
}
if (!parse_mouse_binding(s[1], &mb->middle_click)) {
if (!parse_mouse_binding_set(s, &mb->pri)) {
return false;
}
if (!parse_mouse_binding(s[2], &mb->click4)) {
return false;
}
if (!parse_mouse_binding(s[3], &mb->click5)) {
return false;
if (len == 9) {
if (!parse_mouse_binding_set(s + 5, &mb->sec)) {
return false;
}
} else {
// use the same bindings for Shift+click
mb->sec = mb->pri;
}
return true;
@@ -2408,10 +2437,18 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
LOGW("--forward-all-clicks is deprecated, "
"use --mouse-bind=++++ instead.");
opts->mouse_bindings = (struct sc_mouse_bindings) {
.right_click = SC_MOUSE_BINDING_CLICK,
.middle_click = SC_MOUSE_BINDING_CLICK,
.click4 = SC_MOUSE_BINDING_CLICK,
.click5 = SC_MOUSE_BINDING_CLICK,
.pri = {
.right_click = SC_MOUSE_BINDING_CLICK,
.middle_click = SC_MOUSE_BINDING_CLICK,
.click4 = SC_MOUSE_BINDING_CLICK,
.click5 = SC_MOUSE_BINDING_CLICK,
},
.sec = {
.right_click = SC_MOUSE_BINDING_CLICK,
.middle_click = SC_MOUSE_BINDING_CLICK,
.click4 = SC_MOUSE_BINDING_CLICK,
.click5 = SC_MOUSE_BINDING_CLICK,
},
};
break;
case OPT_LEGACY_PASTE:
@@ -2701,26 +2738,36 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
}
// If mouse bindings are not explictly set, configure default bindings
if (opts->mouse_bindings.right_click == SC_MOUSE_BINDING_AUTO) {
assert(opts->mouse_bindings.middle_click == SC_MOUSE_BINDING_AUTO);
assert(opts->mouse_bindings.click4 == SC_MOUSE_BINDING_AUTO);
assert(opts->mouse_bindings.click5 == SC_MOUSE_BINDING_AUTO);
if (opts->mouse_bindings.pri.right_click == SC_MOUSE_BINDING_AUTO) {
assert(opts->mouse_bindings.pri.middle_click == SC_MOUSE_BINDING_AUTO);
assert(opts->mouse_bindings.pri.click4 == SC_MOUSE_BINDING_AUTO);
assert(opts->mouse_bindings.pri.click5 == SC_MOUSE_BINDING_AUTO);
assert(opts->mouse_bindings.sec.right_click == SC_MOUSE_BINDING_AUTO);
assert(opts->mouse_bindings.sec.middle_click == SC_MOUSE_BINDING_AUTO);
assert(opts->mouse_bindings.sec.click4 == SC_MOUSE_BINDING_AUTO);
assert(opts->mouse_bindings.sec.click5 == SC_MOUSE_BINDING_AUTO);
static struct sc_mouse_binding_set default_shortcuts = {
.right_click = SC_MOUSE_BINDING_BACK,
.middle_click = SC_MOUSE_BINDING_HOME,
.click4 = SC_MOUSE_BINDING_APP_SWITCH,
.click5 = SC_MOUSE_BINDING_EXPAND_NOTIFICATION_PANEL,
};
static struct sc_mouse_binding_set forward = {
.right_click = SC_MOUSE_BINDING_CLICK,
.middle_click = SC_MOUSE_BINDING_CLICK,
.click4 = SC_MOUSE_BINDING_CLICK,
.click5 = SC_MOUSE_BINDING_CLICK,
};
// By default, forward all clicks only for UHID and AOA
if (opts->mouse_input_mode == SC_MOUSE_INPUT_MODE_SDK) {
opts->mouse_bindings = (struct sc_mouse_bindings) {
.right_click = SC_MOUSE_BINDING_BACK,
.middle_click = SC_MOUSE_BINDING_HOME,
.click4 = SC_MOUSE_BINDING_APP_SWITCH,
.click5 = SC_MOUSE_BINDING_EXPAND_NOTIFICATION_PANEL,
};
opts->mouse_bindings.pri = default_shortcuts;
opts->mouse_bindings.sec = forward;
} else {
opts->mouse_bindings = (struct sc_mouse_bindings) {
.right_click = SC_MOUSE_BINDING_CLICK,
.middle_click = SC_MOUSE_BINDING_CLICK,
.click4 = SC_MOUSE_BINDING_CLICK,
.click5 = SC_MOUSE_BINDING_CLICK,
};
opts->mouse_bindings.pri = forward;
opts->mouse_bindings.sec = default_shortcuts;
}
}

View File

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

View File

@@ -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,

View File

@@ -43,6 +43,10 @@ sc_display_init(struct sc_display *display, SDL_Window *window,
display->mipmaps = false;
#ifdef SC_DISPLAY_FORCE_OPENGL_CORE_PROFILE
display->gl_context = NULL;
#endif
// starts with "opengl"
bool use_opengl = renderer_name && !strncmp(renderer_name, "opengl", 6);
if (use_opengl) {

View File

@@ -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

View File

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

View File

@@ -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.

View File

@@ -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 = {

View File

@@ -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.

View File

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

View File

@@ -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
<kbd>Shift</kbd> key (e.g. <kbd>Shift</kbd>+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 <kbd>Shift</kbd> 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
```

View File

@@ -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 @@ public class Controller implements AsyncProcessor {
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);

View File

@@ -63,9 +63,11 @@ public final class Workarounds {
// - <https://github.com/Genymobile/scrcpy/issues/940>
// - <https://github.com/Genymobile/scrcpy/issues/994>
mustFillAppInfo = true;
} else if (Build.BRAND.equalsIgnoreCase("honor")) {
} else if (Build.BRAND.equalsIgnoreCase("honor") || Build.MANUFACTURER.equalsIgnoreCase("skyworth")) {
// More workarounds must be applied for Honor devices:
// - <https://github.com/Genymobile/scrcpy/issues/4015>
// and Skyworth devices:
// - <https://github.com/Genymobile/scrcpy/issues/4922>
//
// The system context must not be set for all devices, because it would cause other problems:
// - <https://github.com/Genymobile/scrcpy/issues/4015#issuecomment-1595382142>