Skip to content

Commit

Permalink
Add mouse secondary bindings
Browse files Browse the repository at this point in the history
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 035d60c
Refs f5e6b80
Fixes #5055 <#5055>
PR #5076 <#5076>
  • Loading branch information
rom1v committed Jul 11, 2024
1 parent 6baea57 commit 9989668
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 60 deletions.
10 changes: 7 additions & 3 deletions app/scrcpy.1
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand All @@ -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
Expand Down
115 changes: 81 additions & 34 deletions app/src/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -493,19 +493,26 @@ 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"
" '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' for SDK mouse, and '++++' for AOA and UHID.",
"Default is 'bhsn:++++' for SDK mouse, and '++++:bhsn' for AOA "
"and UHID.",
},
{
.shortopt = 'n',
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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;
}
}

Expand Down
14 changes: 9 additions & 5 deletions app/src/input_manager.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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));
Expand Down
16 changes: 12 additions & 4 deletions app/src/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
7 changes: 6 additions & 1 deletion app/src/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
52 changes: 39 additions & 13 deletions doc/mouse.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```
Expand All @@ -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
```

0 comments on commit 9989668

Please sign in to comment.