Compare commits

..

1 Commits

Author SHA1 Message Date
77ebafd96c Retrieve icon decoder directly
The call to av_find_best_stream() gives the decoder directly, there is
no need to retrieve it afterwards in a separate step.
2024-06-11 08:58:19 +02:00
23 changed files with 234 additions and 461 deletions

View File

@ -25,6 +25,7 @@ _scrcpy() {
-e --select-tcpip -e --select-tcpip
-f --fullscreen -f --fullscreen
--force-adb-forward --force-adb-forward
--forward-all-clicks
-h --help -h --help
-K -K
--keyboard= --keyboard=
@ -40,7 +41,6 @@ _scrcpy() {
-M -M
--max-fps= --max-fps=
--mouse= --mouse=
--mouse-bind=
-n --no-control -n --no-control
-N --no-playback -N --no-playback
--no-audio --no-audio
@ -50,7 +50,6 @@ _scrcpy() {
--no-downsize-on-error --no-downsize-on-error
--no-key-repeat --no-key-repeat
--no-mipmaps --no-mipmaps
--no-mouse-hover
--no-power-on --no-power-on
--no-video --no-video
--no-video-playback --no-video-playback

View File

@ -32,9 +32,10 @@ arguments=(
{-e,--select-tcpip}'[Use TCP/IP device]' {-e,--select-tcpip}'[Use TCP/IP device]'
{-f,--fullscreen}'[Start in fullscreen]' {-f,--fullscreen}'[Start in fullscreen]'
'--force-adb-forward[Do not attempt to use \"adb reverse\" to connect to the device]' '--force-adb-forward[Do not attempt to use \"adb reverse\" to connect to the device]'
'--forward-all-clicks[Forward clicks to device]'
{-h,--help}'[Print the help]' {-h,--help}'[Print the help]'
'-K[Use UHID keyboard (same as --keyboard=uhid)]' '-K[Use UHID keyboard (same as --keyboard=uhid)]'
'--keyboard=[Set the keyboard input mode]:mode:(disabled sdk uhid aoa)' '--keyboard[Set the keyboard input mode]:mode:(disabled sdk uhid aoa)'
'--kill-adb-on-close[Kill adb when scrcpy terminates]' '--kill-adb-on-close[Kill adb when scrcpy terminates]'
'--legacy-paste[Inject computer clipboard text as a sequence of key events on Ctrl+v]' '--legacy-paste[Inject computer clipboard text as a sequence of key events on Ctrl+v]'
'--list-camera-sizes[List the valid camera capture sizes]' '--list-camera-sizes[List the valid camera capture sizes]'
@ -45,8 +46,7 @@ arguments=(
{-m,--max-size=}'[Limit both the width and height of the video to value]' {-m,--max-size=}'[Limit both the width and height of the video to value]'
'-M[Use UHID mouse (same as --mouse=uhid)]' '-M[Use UHID mouse (same as --mouse=uhid)]'
'--max-fps=[Limit the frame rate of screen capture]' '--max-fps=[Limit the frame rate of screen capture]'
'--mouse=[Set the mouse input mode]:mode:(disabled sdk uhid aoa)' '--mouse[Set the mouse input mode]:mode:(disabled sdk uhid aoa)'
'--mouse-bind=[Configure bindings of secondary clicks]'
{-n,--no-control}'[Disable device control \(mirror the device in read only\)]' {-n,--no-control}'[Disable device control \(mirror the device in read only\)]'
{-N,--no-playback}'[Disable video and audio playback]' {-N,--no-playback}'[Disable video and audio playback]'
'--no-audio[Disable audio forwarding]' '--no-audio[Disable audio forwarding]'
@ -56,7 +56,6 @@ arguments=(
'--no-downsize-on-error[Disable lowering definition on MediaCodec error]' '--no-downsize-on-error[Disable lowering definition on MediaCodec error]'
'--no-key-repeat[Do not forward repeated key events when a key is held down]' '--no-key-repeat[Do not forward repeated key events when a key is held down]'
'--no-mipmaps[Disable the generation of mipmaps]' '--no-mipmaps[Disable the generation of mipmaps]'
'--no-mouse-hover[Do not forward mouse hover events]'
'--no-power-on[Do not power on the device on start]' '--no-power-on[Do not power on the device on start]'
'--no-video[Disable video forwarding]' '--no-video[Disable video forwarding]'
'--no-video-playback[Disable video playback]' '--no-video-playback[Disable video playback]'

View File

@ -163,6 +163,10 @@ Start in fullscreen.
.B \-\-force\-adb\-forward .B \-\-force\-adb\-forward
Do not attempt to use "adb reverse" to connect to the device. Do not attempt to use "adb reverse" to connect to the device.
.TP
.B \-\-forward\-all\-clicks
By default, right-click triggers BACK (or POWER on) and middle-click triggers HOME. This option disables these shortcuts and forward the clicks to the device instead.
.TP .TP
.B \-h, \-\-help .B \-h, \-\-help
Print this help. Print this help.
@ -257,23 +261,6 @@ LAlt, LSuper or RSuper toggle the capture mode, to give control of the mouse bac
Also see \fB\-\-keyboard\fR. Also see \fB\-\-keyboard\fR.
.TP
.BI "\-\-mouse\-bind " xxxx
Configure bindings of secondary clicks.
The argument must be exactly 4 characters, one for each secondary click (in order: right click, middle click, 4th click, 5th click).
Each character must be one of the following:
- '+': forward the click to the device
- '-': ignore the click
- 'b': trigger shortcut BACK (or turn screen on if off)
- 'h': trigger shortcut HOME
- 's': trigger shortcut APP_SWITCH
- 'n': trigger shortcut "expand notification panel"
Default is 'bhsn' for SDK mouse, and '++++' for AOA and UHID.
.TP .TP
.B \-n, \-\-no\-control .B \-n, \-\-no\-control
@ -317,10 +304,6 @@ Do not forward repeated key events when a key is held down.
.B \-\-no\-mipmaps .B \-\-no\-mipmaps
If the renderer is OpenGL 3.0+ or OpenGL ES 2.0+, then mipmaps are automatically generated to improve downscaling quality. This option disables the generation of mipmaps. If the renderer is OpenGL 3.0+ or OpenGL ES 2.0+, then mipmaps are automatically generated to improve downscaling quality. This option disables the generation of mipmaps.
.TP
.B \-\-no\-mouse\-hover
Do not forward mouse hover (mouse motion without any clicks) events.
.TP .TP
.B \-\-no\-power\-on .B \-\-no\-power\-on
Do not power on the device on start. Do not power on the device on start.
@ -441,9 +424,9 @@ Turn the device screen off immediately.
.BI "\-\-shortcut\-mod " key\fR[+...]][,...] .BI "\-\-shortcut\-mod " key\fR[+...]][,...]
Specify the modifiers to use for scrcpy shortcuts. Possible keys are "lctrl", "rctrl", "lalt", "ralt", "lsuper" and "rsuper". Specify the modifiers to use for scrcpy shortcuts. Possible keys are "lctrl", "rctrl", "lalt", "ralt", "lsuper" and "rsuper".
Several shortcut modifiers can be specified, separated by ','. A shortcut can consist in several keys, separated by '+'. Several shortcuts can be specified, separated by ','.
For example, to use either LCtrl or LSuper for scrcpy shortcuts, pass "lctrl,lsuper". For example, to use either LCtrl+LAlt or LSuper for scrcpy shortcuts, pass "lctrl+lalt,lsuper".
Default is "lalt,lsuper" (left-Alt or left-Super). Default is "lalt,lsuper" (left-Alt or left-Super).

View File

@ -98,8 +98,6 @@ enum {
OPT_HID_KEYBOARD_DEPRECATED, OPT_HID_KEYBOARD_DEPRECATED,
OPT_HID_MOUSE_DEPRECATED, OPT_HID_MOUSE_DEPRECATED,
OPT_NO_WINDOW, OPT_NO_WINDOW,
OPT_MOUSE_BIND,
OPT_NO_MOUSE_HOVER,
}; };
struct sc_option { struct sc_option {
@ -354,9 +352,11 @@ static const struct sc_option options[] = {
"device.", "device.",
}, },
{ {
// deprecated
.longopt_id = OPT_FORWARD_ALL_CLICKS, .longopt_id = OPT_FORWARD_ALL_CLICKS,
.longopt = "forward-all-clicks", .longopt = "forward-all-clicks",
.text = "By default, right-click triggers BACK (or POWER on) and "
"middle-click triggers HOME. This option disables these "
"shortcuts and forwards the clicks to the device instead.",
}, },
{ {
.shortopt = 'h', .shortopt = 'h',
@ -490,23 +490,6 @@ static const struct sc_option options[] = {
"control of the mouse back to the computer.\n" "control of the mouse back to the computer.\n"
"Also see --keyboard.", "Also see --keyboard.",
}, },
{
.longopt_id = OPT_MOUSE_BIND,
.longopt = "mouse-bind",
.argdesc = "xxxx",
.text = "Configure bindings of secondary clicks.\n"
"The argument must be exactly 4 characters, one for each "
"secondary click (in order: right click, middle click, 4th "
"click, 5th click).\n"
"Each character must be one of the following:\n"
" '+': forward the click to the device\n"
" '-': ignore the click\n"
" 'b': trigger shortcut BACK (or turn screen on if off)\n"
" 'h': trigger shortcut HOME\n"
" 's': trigger shortcut APP_SWITCH\n"
" 'n': trigger shortcut \"expand notification panel\"\n"
"Default is 'bhsn' for SDK mouse, and '++++' for AOA and UHID.",
},
{ {
.shortopt = 'n', .shortopt = 'n',
.longopt = "no-control", .longopt = "no-control",
@ -569,12 +552,6 @@ static const struct sc_option options[] = {
"mipmaps are automatically generated to improve downscaling " "mipmaps are automatically generated to improve downscaling "
"quality. This option disables the generation of mipmaps.", "quality. This option disables the generation of mipmaps.",
}, },
{
.longopt_id = OPT_NO_MOUSE_HOVER,
.longopt = "no-mouse-hover",
.text = "Do not forward mouse hover (mouse motion without any clicks) "
"events.",
},
{ {
.longopt_id = OPT_NO_POWER_ON, .longopt_id = OPT_NO_POWER_ON,
.longopt = "no-power-on", .longopt = "no-power-on",
@ -739,10 +716,10 @@ static const struct sc_option options[] = {
.text = "Specify the modifiers to use for scrcpy shortcuts.\n" .text = "Specify the modifiers to use for scrcpy shortcuts.\n"
"Possible keys are \"lctrl\", \"rctrl\", \"lalt\", \"ralt\", " "Possible keys are \"lctrl\", \"rctrl\", \"lalt\", \"ralt\", "
"\"lsuper\" and \"rsuper\".\n" "\"lsuper\" and \"rsuper\".\n"
"Several shortcut modifiers can be specified, separated by " "A shortcut can consist in several keys, separated by '+'. "
"','.\n" "Several shortcuts can be specified, separated by ','.\n"
"For example, to use either LCtrl or LSuper for scrcpy " "For example, to use either LCtrl+LAlt or LSuper for scrcpy "
"shortcuts, pass \"lctrl,lsuper\".\n" "shortcuts, pass \"lctrl+lalt,lsuper\".\n"
"Default is \"lalt,lsuper\" (left-Alt or left-Super).", "Default is \"lalt,lsuper\" (left-Alt or left-Super).",
}, },
{ {
@ -1710,62 +1687,82 @@ parse_log_level(const char *s, enum sc_log_level *log_level) {
return false; return false;
} }
static enum sc_shortcut_mod // item is a list of mod keys separated by '+' (e.g. "lctrl+lalt")
// returns a bitwise-or of SC_SHORTCUT_MOD_* constants (or 0 on error)
static unsigned
parse_shortcut_mods_item(const char *item, size_t len) { parse_shortcut_mods_item(const char *item, size_t len) {
unsigned mod = 0;
for (;;) {
char *plus = strchr(item, '+');
// strchr() does not consider the "len" parameter, to it could find an
// occurrence too far in the string (there is no strnchr())
bool has_plus = plus && plus < item + len;
assert(!has_plus || plus > item);
size_t key_len = has_plus ? (size_t) (plus - item) : len;
#define STREQ(literal, s, len) \ #define STREQ(literal, s, len) \
((sizeof(literal)-1 == len) && !memcmp(literal, s, len)) ((sizeof(literal)-1 == len) && !memcmp(literal, s, len))
if (STREQ("lctrl", item, len)) { if (STREQ("lctrl", item, key_len)) {
return SC_SHORTCUT_MOD_LCTRL; mod |= SC_SHORTCUT_MOD_LCTRL;
} } else if (STREQ("rctrl", item, key_len)) {
if (STREQ("rctrl", item, len)) { mod |= SC_SHORTCUT_MOD_RCTRL;
return SC_SHORTCUT_MOD_RCTRL; } else if (STREQ("lalt", item, key_len)) {
} mod |= SC_SHORTCUT_MOD_LALT;
if (STREQ("lalt", item, len)) { } else if (STREQ("ralt", item, key_len)) {
return SC_SHORTCUT_MOD_LALT; mod |= SC_SHORTCUT_MOD_RALT;
} } else if (STREQ("lsuper", item, key_len)) {
if (STREQ("ralt", item, len)) { mod |= SC_SHORTCUT_MOD_LSUPER;
return SC_SHORTCUT_MOD_RALT; } else if (STREQ("rsuper", item, key_len)) {
} mod |= SC_SHORTCUT_MOD_RSUPER;
if (STREQ("lsuper", item, len)) { } else {
return SC_SHORTCUT_MOD_LSUPER; LOGE("Unknown modifier key: %.*s "
} "(must be one of: lctrl, rctrl, lalt, ralt, lsuper, rsuper)",
if (STREQ("rsuper", item, len)) { (int) key_len, item);
return SC_SHORTCUT_MOD_RSUPER; return 0;
} }
#undef STREQ #undef STREQ
bool has_plus = strchr(item, '+'); if (!has_plus) {
if (has_plus) { break;
LOGE("Shortcut mod combination with '+' is not supported anymore: "
"'%.*s' (see #4741)", (int) len, item);
return 0;
} }
LOGE("Unknown modifier key: %.*s " item = plus + 1;
"(must be one of: lctrl, rctrl, lalt, ralt, lsuper, rsuper)", assert(len >= key_len + 1);
(int) len, item); len -= key_len + 1;
}
return 0; return mod;
} }
static bool static bool
parse_shortcut_mods(const char *s, uint8_t *shortcut_mods) { parse_shortcut_mods(const char *s, struct sc_shortcut_mods *mods) {
uint8_t mods = 0; unsigned count = 0;
unsigned current = 0;
// A list of shortcut modifiers, for example "lctrl,rctrl,rsuper" // LCtrl+LAlt or RCtrl or LCtrl+RSuper: "lctrl+lalt,rctrl,lctrl+rsuper"
for (;;) { for (;;) {
char *comma = strchr(s, ','); char *comma = strchr(s, ',');
assert(!comma || comma > s); if (comma && count == SC_MAX_SHORTCUT_MODS - 1) {
size_t limit = comma ? (size_t) (comma - s) : strlen(s); assert(count < SC_MAX_SHORTCUT_MODS);
LOGW("Too many shortcut modifiers alternatives");
enum sc_shortcut_mod mod = parse_shortcut_mods_item(s, limit);
if (!mod) {
return false; return false;
} }
mods |= mod; assert(!comma || comma > s);
size_t limit = comma ? (size_t) (comma - s) : strlen(s);
unsigned mod = parse_shortcut_mods_item(s, limit);
if (!mod) {
LOGE("Invalid modifier keys: %.*s", (int) limit, s);
return false;
}
mods->data[current++] = mod;
++count;
if (!comma) { if (!comma) {
break; break;
@ -1774,7 +1771,7 @@ parse_shortcut_mods(const char *s, uint8_t *shortcut_mods) {
s = comma + 1; s = comma + 1;
} }
*shortcut_mods = mods; mods->count = count;
return true; return true;
} }
@ -1782,7 +1779,7 @@ parse_shortcut_mods(const char *s, uint8_t *shortcut_mods) {
#ifdef SC_TEST #ifdef SC_TEST
// expose the function to unit-tests // expose the function to unit-tests
bool bool
sc_parse_shortcut_mods(const char *s, uint8_t *mods) { sc_parse_shortcut_mods(const char *s, struct sc_shortcut_mods *mods) {
return parse_shortcut_mods(s, mods); return parse_shortcut_mods(s, mods);
} }
#endif #endif
@ -2061,63 +2058,11 @@ parse_pause_on_exit(const char *s, enum sc_pause_on_exit *pause_on_exit) {
} }
LOGE("Unsupported pause on exit mode: %s " LOGE("Unsupported pause on exit mode: %s "
"(expected true, false or if-error)", s); "(expected true, false or if-error)", optarg);
return false; return false;
} }
static bool
parse_mouse_binding(char c, enum sc_mouse_binding *b) {
switch (c) {
case '+':
*b = SC_MOUSE_BINDING_CLICK;
return true;
case '-':
*b = SC_MOUSE_BINDING_DISABLED;
return true;
case 'b':
*b = SC_MOUSE_BINDING_BACK;
return true;
case 'h':
*b = SC_MOUSE_BINDING_HOME;
return true;
case 's':
*b = SC_MOUSE_BINDING_APP_SWITCH;
return true;
case 'n':
*b = SC_MOUSE_BINDING_EXPAND_NOTIFICATION_PANEL;
return true;
default:
LOGE("Invalid mouse binding: '%c' "
"(expected '+', '-', 'b', 'h', 's' or 'n')", c);
return false;
}
}
static bool
parse_mouse_bindings(const char *s, struct sc_mouse_bindings *mb) {
if (strlen(s) != 4) {
LOGE("Invalid mouse bindings: '%s' (expected exactly 4 characters from "
"{'+', '-', 'b', 'h', 's', 'n'})", s);
return false;
}
if (!parse_mouse_binding(s[0], &mb->right_click)) {
return false;
}
if (!parse_mouse_binding(s[1], &mb->middle_click)) {
return false;
}
if (!parse_mouse_binding(s[2], &mb->click4)) {
return false;
}
if (!parse_mouse_binding(s[3], &mb->click5)) {
return false;
}
return true;
}
static bool static bool
parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[], parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
const char *optstring, const struct option *longopts) { const char *optstring, const struct option *longopts) {
@ -2200,14 +2145,6 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
return false; return false;
} }
break; break;
case OPT_MOUSE_BIND:
if (!parse_mouse_bindings(optarg, &opts->mouse_bindings)) {
return false;
}
break;
case OPT_NO_MOUSE_HOVER:
opts->mouse_hover = false;
break;
case OPT_HID_MOUSE_DEPRECATED: case OPT_HID_MOUSE_DEPRECATED:
LOGE("--hid-mouse has been removed, use --mouse=aoa or " LOGE("--hid-mouse has been removed, use --mouse=aoa or "
"--mouse=uhid instead."); "--mouse=uhid instead.");
@ -2405,14 +2342,7 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
} }
break; break;
case OPT_FORWARD_ALL_CLICKS: case OPT_FORWARD_ALL_CLICKS:
LOGW("--forward-all-clicks is deprecated, " opts->forward_all_clicks = true;
"use --mouse-bind=++++ instead.");
opts->mouse_bindings = (struct sc_mouse_bindings) {
.right_click = SC_MOUSE_BINDING_CLICK,
.middle_click = SC_MOUSE_BINDING_CLICK,
.click4 = SC_MOUSE_BINDING_CLICK,
.click5 = SC_MOUSE_BINDING_CLICK,
};
break; break;
case OPT_LEGACY_PASTE: case OPT_LEGACY_PASTE:
opts->legacy_paste = true; opts->legacy_paste = true;
@ -2700,30 +2630,6 @@ 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);
// 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,
};
} 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,
};
}
}
if (otg) { if (otg) {
if (!opts->control) { if (!opts->control) {
LOGE("--no-control is not allowed in OTG mode"); LOGE("--no-control is not allowed in OTG mode");
@ -2768,12 +2674,6 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
} }
} }
if (opts->mouse_input_mode != SC_MOUSE_INPUT_MODE_SDK
&& !opts->mouse_hover) {
LOGE("--no-mouse-over is specific to --mouse=sdk");
return false;
}
if ((opts->tunnel_host || opts->tunnel_port) && !opts->force_adb_forward) { if ((opts->tunnel_host || opts->tunnel_port) && !opts->force_adb_forward) {
LOGI("Tunnel host/port is set, " LOGI("Tunnel host/port is set, "
"--force-adb-forward automatically enabled."); "--force-adb-forward automatically enabled.");

View File

@ -28,7 +28,7 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]);
#ifdef SC_TEST #ifdef SC_TEST
bool bool
sc_parse_shortcut_mods(const char *s, uint8_t *shortcut_mods); sc_parse_shortcut_mods(const char *s, struct sc_shortcut_mods *mods);
#endif #endif
#endif #endif

View File

@ -78,16 +78,7 @@ decode_image(const char *path) {
goto close_input; goto close_input;
} }
// In ffmpeg/doc/APIchanges:
// 2021-04-27 - 46dac8cf3d - lavf 59.0.100 - avformat.h
// av_find_best_stream now uses a const AVCodec ** parameter
// for the returned decoder.
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(59, 0, 100)
const AVCodec *codec; const AVCodec *codec;
#else
AVCodec *codec;
#endif
int stream = int stream =
av_find_best_stream(ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &codec, 0); av_find_best_stream(ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &codec, 0);

View File

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

View File

@ -10,7 +10,7 @@
#define SC_SDL_SHORTCUT_MODS_MASK (KMOD_CTRL | KMOD_ALT | KMOD_GUI) #define SC_SDL_SHORTCUT_MODS_MASK (KMOD_CTRL | KMOD_ALT | KMOD_GUI)
static inline uint16_t static inline uint16_t
to_sdl_mod(uint8_t shortcut_mod) { to_sdl_mod(unsigned shortcut_mod) {
uint16_t sdl_mod = 0; uint16_t sdl_mod = 0;
if (shortcut_mod & SC_SHORTCUT_MOD_LCTRL) { if (shortcut_mod & SC_SHORTCUT_MOD_LCTRL) {
sdl_mod |= KMOD_LCTRL; sdl_mod |= KMOD_LCTRL;
@ -38,26 +38,15 @@ is_shortcut_mod(struct sc_input_manager *im, uint16_t sdl_mod) {
// keep only the relevant modifier keys // keep only the relevant modifier keys
sdl_mod &= SC_SDL_SHORTCUT_MODS_MASK; sdl_mod &= SC_SDL_SHORTCUT_MODS_MASK;
// at least one shortcut mod pressed? assert(im->sdl_shortcut_mods.count);
return sdl_mod & im->sdl_shortcut_mods; assert(im->sdl_shortcut_mods.count < SC_MAX_SHORTCUT_MODS);
} for (unsigned i = 0; i < im->sdl_shortcut_mods.count; ++i) {
if (im->sdl_shortcut_mods.data[i] == sdl_mod) {
return true;
}
}
static bool return false;
is_shortcut_key(struct sc_input_manager *im, SDL_Keycode keycode) {
return (im->sdl_shortcut_mods & KMOD_LCTRL && keycode == SDLK_LCTRL)
|| (im->sdl_shortcut_mods & KMOD_RCTRL && keycode == SDLK_RCTRL)
|| (im->sdl_shortcut_mods & KMOD_LALT && keycode == SDLK_LALT)
|| (im->sdl_shortcut_mods & KMOD_RALT && keycode == SDLK_RALT)
|| (im->sdl_shortcut_mods & KMOD_LGUI && keycode == SDLK_LGUI)
|| (im->sdl_shortcut_mods & KMOD_RGUI && keycode == SDLK_RGUI);
}
static inline bool
mouse_bindings_has_secondary_click(const struct sc_mouse_bindings *mb) {
return mb->right_click == SC_MOUSE_BINDING_CLICK
|| mb->middle_click == SC_MOUSE_BINDING_CLICK
|| mb->click4 == SC_MOUSE_BINDING_CLICK
|| mb->click5 == SC_MOUSE_BINDING_CLICK;
} }
void void
@ -75,13 +64,19 @@ sc_input_manager_init(struct sc_input_manager *im,
im->kp = params->kp; im->kp = params->kp;
im->mp = params->mp; im->mp = params->mp;
im->mouse_bindings = params->mouse_bindings; im->forward_all_clicks = params->forward_all_clicks;
im->has_secondary_click =
mouse_bindings_has_secondary_click(&im->mouse_bindings);
im->legacy_paste = params->legacy_paste; im->legacy_paste = params->legacy_paste;
im->clipboard_autosync = params->clipboard_autosync; im->clipboard_autosync = params->clipboard_autosync;
im->sdl_shortcut_mods = to_sdl_mod(params->shortcut_mods); const struct sc_shortcut_mods *shortcut_mods = params->shortcut_mods;
assert(shortcut_mods->count);
assert(shortcut_mods->count < SC_MAX_SHORTCUT_MODS);
for (unsigned i = 0; i < shortcut_mods->count; ++i) {
uint16_t sdl_mod = to_sdl_mod(shortcut_mods->data[i]);
assert(sdl_mod);
im->sdl_shortcut_mods.data[i] = sdl_mod;
}
im->sdl_shortcut_mods.count = shortcut_mods->count;
im->vfinger_down = false; im->vfinger_down = false;
im->vfinger_invert_x = false; im->vfinger_invert_x = false;
@ -376,7 +371,7 @@ simulate_virtual_finger(struct sc_input_manager *im,
msg.inject_touch_event.position.screen_size = im->screen->frame_size; msg.inject_touch_event.position.screen_size = im->screen->frame_size;
msg.inject_touch_event.position.point = point; msg.inject_touch_event.position.point = point;
msg.inject_touch_event.pointer_id = msg.inject_touch_event.pointer_id =
im->has_secondary_click ? POINTER_ID_VIRTUAL_MOUSE im->forward_all_clicks ? POINTER_ID_VIRTUAL_MOUSE
: POINTER_ID_VIRTUAL_FINGER; : POINTER_ID_VIRTUAL_FINGER;
msg.inject_touch_event.pressure = up ? 0.0f : 1.0f; msg.inject_touch_event.pressure = up ? 0.0f : 1.0f;
msg.inject_touch_event.action_button = 0; msg.inject_touch_event.action_button = 0;
@ -417,12 +412,7 @@ sc_input_manager_process_key(struct sc_input_manager *im,
bool shift = event->keysym.mod & KMOD_SHIFT; bool shift = event->keysym.mod & KMOD_SHIFT;
bool repeat = event->repeat; bool repeat = event->repeat;
// Either the modifier includes a shortcut modifier, or the key bool smod = is_shortcut_mod(im, mod);
// press/release is a modifier key.
// The second condition is necessary to ignore the release of the modifier
// key (because in this case mod is 0).
bool is_shortcut = is_shortcut_mod(im, mod)
|| is_shortcut_key(im, keycode);
if (down && !repeat) { if (down && !repeat) {
if (keycode == im->last_keycode && mod == im->last_mod) { if (keycode == im->last_keycode && mod == im->last_mod) {
@ -434,7 +424,8 @@ sc_input_manager_process_key(struct sc_input_manager *im,
} }
} }
if (is_shortcut) { // The shortcut modifier is pressed
if (smod) {
enum sc_action action = down ? SC_ACTION_DOWN : SC_ACTION_UP; enum sc_action action = down ? SC_ACTION_DOWN : SC_ACTION_UP;
switch (keycode) { switch (keycode) {
case SDLK_h: case SDLK_h:
@ -662,12 +653,13 @@ sc_input_manager_process_mouse_motion(struct sc_input_manager *im,
struct sc_mouse_motion_event evt = { struct sc_mouse_motion_event evt = {
.position = sc_input_manager_get_position(im, event->x, event->y), .position = sc_input_manager_get_position(im, event->x, event->y),
.pointer_id = im->has_secondary_click ? POINTER_ID_MOUSE .pointer_id = im->forward_all_clicks ? POINTER_ID_MOUSE
: POINTER_ID_GENERIC_FINGER, : POINTER_ID_GENERIC_FINGER,
.xrel = event->xrel, .xrel = event->xrel,
.yrel = event->yrel, .yrel = event->yrel,
.buttons_state = .buttons_state =
sc_mouse_buttons_state_from_sdl(event->state, &im->mouse_bindings), sc_mouse_buttons_state_from_sdl(event->state,
im->forward_all_clicks),
}; };
assert(im->mp->ops->process_mouse_motion); assert(im->mp->ops->process_mouse_motion);
@ -718,25 +710,6 @@ sc_input_manager_process_touch(struct sc_input_manager *im,
im->mp->ops->process_touch(im->mp, &evt); im->mp->ops->process_touch(im->mp, &evt);
} }
static enum sc_mouse_binding
sc_input_manager_get_binding(const struct sc_mouse_bindings *bindings,
uint8_t sdl_button) {
switch (sdl_button) {
case SDL_BUTTON_LEFT:
return SC_MOUSE_BINDING_CLICK;
case SDL_BUTTON_RIGHT:
return bindings->right_click;
case SDL_BUTTON_MIDDLE:
return bindings->middle_click;
case SDL_BUTTON_X1:
return bindings->click4;
case SDL_BUTTON_X2:
return bindings->click5;
default:
return SC_MOUSE_BINDING_DISABLED;
}
}
static void static void
sc_input_manager_process_mouse_button(struct sc_input_manager *im, sc_input_manager_process_mouse_button(struct sc_input_manager *im,
const SDL_MouseButtonEvent *event) { const SDL_MouseButtonEvent *event) {
@ -748,51 +721,35 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im,
bool control = im->controller; bool control = im->controller;
bool paused = im->screen->paused; bool paused = im->screen->paused;
bool down = event->type == SDL_MOUSEBUTTONDOWN; bool down = event->type == SDL_MOUSEBUTTONDOWN;
if (!im->forward_all_clicks) {
if (control && !paused) { if (control && !paused) {
enum sc_action action = down ? SC_ACTION_DOWN : SC_ACTION_UP; enum sc_action action = down ? SC_ACTION_DOWN : SC_ACTION_UP;
enum sc_mouse_binding binding = if (im->kp && event->button == SDL_BUTTON_X1) {
sc_input_manager_get_binding(&im->mouse_bindings, event->button);
assert(binding != SC_MOUSE_BINDING_AUTO);
switch (binding) {
case SC_MOUSE_BINDING_DISABLED:
// ignore click
return;
case SC_MOUSE_BINDING_BACK:
if (im->kp) {
press_back_or_turn_screen_on(im, action);
}
return;
case SC_MOUSE_BINDING_HOME:
if (im->kp) {
action_home(im, action);
}
return;
case SC_MOUSE_BINDING_APP_SWITCH:
if (im->kp) {
action_app_switch(im, action); action_app_switch(im, action);
}
return; return;
case SC_MOUSE_BINDING_EXPAND_NOTIFICATION_PANEL: }
if (down) { if (event->button == SDL_BUTTON_X2 && down) {
if (event->clicks < 2) { if (event->clicks < 2) {
expand_notification_panel(im); expand_notification_panel(im);
} else { } else {
expand_settings_panel(im); expand_settings_panel(im);
} }
}
return; return;
default: }
assert(binding == SC_MOUSE_BINDING_CLICK); if (im->kp && event->button == SDL_BUTTON_RIGHT) {
break; press_back_or_turn_screen_on(im, action);
return;
}
if (im->kp && event->button == SDL_BUTTON_MIDDLE) {
action_home(im, action);
return;
} }
} }
// double-click on black borders resizes to fit the device screen // double-click on black borders resize to fit the device screen
bool video = im->screen->video; bool video = im->screen->video;
bool mouse_relative_mode = im->mp && im->mp->relative_mode; if (video && event->button == SDL_BUTTON_LEFT && event->clicks == 2) {
if (video && !mouse_relative_mode && event->button == SDL_BUTTON_LEFT
&& event->clicks == 2) {
int32_t x = event->x; int32_t x = event->x;
int32_t y = event->y; int32_t y = event->y;
sc_screen_hidpi_scale_coords(im->screen, &x, &y); sc_screen_hidpi_scale_coords(im->screen, &x, &y);
@ -806,6 +763,8 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im,
return; return;
} }
} }
// otherwise, send the click event to the device
}
if (!im->mp || paused) { if (!im->mp || paused) {
return; return;
@ -817,10 +776,11 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im,
.position = sc_input_manager_get_position(im, event->x, event->y), .position = sc_input_manager_get_position(im, event->x, event->y),
.action = sc_action_from_sdl_mousebutton_type(event->type), .action = sc_action_from_sdl_mousebutton_type(event->type),
.button = sc_mouse_button_from_sdl(event->button), .button = sc_mouse_button_from_sdl(event->button),
.pointer_id = im->has_secondary_click ? POINTER_ID_MOUSE .pointer_id = im->forward_all_clicks ? POINTER_ID_MOUSE
: POINTER_ID_GENERIC_FINGER, : POINTER_ID_GENERIC_FINGER,
.buttons_state = sc_mouse_buttons_state_from_sdl(sdl_buttons_state, .buttons_state =
&im->mouse_bindings), sc_mouse_buttons_state_from_sdl(sdl_buttons_state,
im->forward_all_clicks),
}; };
assert(im->mp->ops->process_mouse_click); assert(im->mp->ops->process_mouse_click);
@ -896,8 +856,8 @@ sc_input_manager_process_mouse_wheel(struct sc_input_manager *im,
.hscroll = CLAMP(event->x, -1, 1), .hscroll = CLAMP(event->x, -1, 1),
.vscroll = CLAMP(event->y, -1, 1), .vscroll = CLAMP(event->y, -1, 1),
#endif #endif
.buttons_state = sc_mouse_buttons_state_from_sdl(buttons, .buttons_state =
&im->mouse_bindings), sc_mouse_buttons_state_from_sdl(buttons, im->forward_all_clicks),
}; };
im->mp->ops->process_mouse_scroll(im->mp, &evt); im->mp->ops->process_mouse_scroll(im->mp, &evt);

View File

@ -22,12 +22,14 @@ struct sc_input_manager {
struct sc_key_processor *kp; struct sc_key_processor *kp;
struct sc_mouse_processor *mp; struct sc_mouse_processor *mp;
struct sc_mouse_bindings mouse_bindings; bool forward_all_clicks;
bool has_secondary_click;
bool legacy_paste; bool legacy_paste;
bool clipboard_autosync; bool clipboard_autosync;
uint16_t sdl_shortcut_mods; struct {
unsigned data[SC_MAX_SHORTCUT_MODS];
unsigned count;
} sdl_shortcut_mods;
bool vfinger_down; bool vfinger_down;
bool vfinger_invert_x; bool vfinger_invert_x;
@ -50,10 +52,10 @@ struct sc_input_manager_params {
struct sc_key_processor *kp; struct sc_key_processor *kp;
struct sc_mouse_processor *mp; struct sc_mouse_processor *mp;
struct sc_mouse_bindings mouse_bindings; bool forward_all_clicks;
bool legacy_paste; bool legacy_paste;
bool clipboard_autosync; bool clipboard_autosync;
uint8_t shortcut_mods; // OR of enum sc_shortcut_mod values const struct sc_shortcut_mods *shortcut_mods;
}; };
void void

View File

@ -58,18 +58,17 @@ convert_touch_action(enum sc_touch_action action) {
static void static void
sc_mouse_processor_process_mouse_motion(struct sc_mouse_processor *mp, sc_mouse_processor_process_mouse_motion(struct sc_mouse_processor *mp,
const struct sc_mouse_motion_event *event) { const struct sc_mouse_motion_event *event) {
struct sc_mouse_sdk *m = DOWNCAST(mp); if (!event->buttons_state) {
if (!m->mouse_hover && !event->buttons_state) {
// Do not send motion events when no click is pressed // Do not send motion events when no click is pressed
return; return;
} }
struct sc_mouse_sdk *m = DOWNCAST(mp);
struct sc_control_msg msg = { struct sc_control_msg msg = {
.type = SC_CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT, .type = SC_CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT,
.inject_touch_event = { .inject_touch_event = {
.action = event->buttons_state ? AMOTION_EVENT_ACTION_MOVE .action = AMOTION_EVENT_ACTION_MOVE,
: AMOTION_EVENT_ACTION_HOVER_MOVE,
.pointer_id = event->pointer_id, .pointer_id = event->pointer_id,
.position = event->position, .position = event->position,
.pressure = 1.f, .pressure = 1.f,
@ -146,10 +145,8 @@ sc_mouse_processor_process_touch(struct sc_mouse_processor *mp,
} }
void void
sc_mouse_sdk_init(struct sc_mouse_sdk *m, struct sc_controller *controller, sc_mouse_sdk_init(struct sc_mouse_sdk *m, struct sc_controller *controller) {
bool mouse_hover) {
m->controller = controller; m->controller = controller;
m->mouse_hover = mouse_hover;
static const struct sc_mouse_processor_ops ops = { static const struct sc_mouse_processor_ops ops = {
.process_mouse_motion = sc_mouse_processor_process_mouse_motion, .process_mouse_motion = sc_mouse_processor_process_mouse_motion,

View File

@ -13,11 +13,9 @@ struct sc_mouse_sdk {
struct sc_mouse_processor mouse_processor; // mouse processor trait struct sc_mouse_processor mouse_processor; // mouse processor trait
struct sc_controller *controller; struct sc_controller *controller;
bool mouse_hover;
}; };
void void
sc_mouse_sdk_init(struct sc_mouse_sdk *m, struct sc_controller *controller, sc_mouse_sdk_init(struct sc_mouse_sdk *m, struct sc_controller *controller);
bool mouse_hover);
#endif #endif

View File

@ -23,12 +23,6 @@ const struct scrcpy_options scrcpy_options_default = {
.record_format = SC_RECORD_FORMAT_AUTO, .record_format = SC_RECORD_FORMAT_AUTO,
.keyboard_input_mode = SC_KEYBOARD_INPUT_MODE_AUTO, .keyboard_input_mode = SC_KEYBOARD_INPUT_MODE_AUTO,
.mouse_input_mode = SC_MOUSE_INPUT_MODE_AUTO, .mouse_input_mode = SC_MOUSE_INPUT_MODE_AUTO,
.mouse_bindings = {
.right_click = SC_MOUSE_BINDING_AUTO,
.middle_click = SC_MOUSE_BINDING_AUTO,
.click4 = SC_MOUSE_BINDING_AUTO,
.click5 = SC_MOUSE_BINDING_AUTO,
},
.camera_facing = SC_CAMERA_FACING_ANY, .camera_facing = SC_CAMERA_FACING_ANY,
.port_range = { .port_range = {
.first = DEFAULT_LOCAL_PORT_RANGE_FIRST, .first = DEFAULT_LOCAL_PORT_RANGE_FIRST,
@ -36,7 +30,10 @@ const struct scrcpy_options scrcpy_options_default = {
}, },
.tunnel_host = 0, .tunnel_host = 0,
.tunnel_port = 0, .tunnel_port = 0,
.shortcut_mods = SC_SHORTCUT_MOD_LALT | SC_SHORTCUT_MOD_LSUPER, .shortcut_mods = {
.data = {SC_SHORTCUT_MOD_LALT, SC_SHORTCUT_MOD_LSUPER},
.count = 2,
},
.max_size = 0, .max_size = 0,
.video_bit_rate = 0, .video_bit_rate = 0,
.audio_bit_rate = 0, .audio_bit_rate = 0,
@ -74,6 +71,7 @@ const struct scrcpy_options scrcpy_options_default = {
.force_adb_forward = false, .force_adb_forward = false,
.disable_screensaver = false, .disable_screensaver = false,
.forward_key_repeat = true, .forward_key_repeat = true,
.forward_all_clicks = false,
.legacy_paste = false, .legacy_paste = false,
.power_off_on_close = false, .power_off_on_close = false,
.clipboard_autosync = true, .clipboard_autosync = true,
@ -92,7 +90,6 @@ const struct scrcpy_options scrcpy_options_default = {
.camera_high_speed = false, .camera_high_speed = false,
.list = 0, .list = 0,
.window = true, .window = true,
.mouse_hover = true,
}; };
enum sc_orientation enum sc_orientation

View File

@ -155,23 +155,6 @@ enum sc_mouse_input_mode {
SC_MOUSE_INPUT_MODE_AOA, SC_MOUSE_INPUT_MODE_AOA,
}; };
enum sc_mouse_binding {
SC_MOUSE_BINDING_AUTO,
SC_MOUSE_BINDING_DISABLED,
SC_MOUSE_BINDING_CLICK,
SC_MOUSE_BINDING_BACK,
SC_MOUSE_BINDING_HOME,
SC_MOUSE_BINDING_APP_SWITCH,
SC_MOUSE_BINDING_EXPAND_NOTIFICATION_PANEL,
};
struct sc_mouse_bindings {
enum sc_mouse_binding right_click;
enum sc_mouse_binding middle_click;
enum sc_mouse_binding click4;
enum sc_mouse_binding click5;
};
enum sc_key_inject_mode { enum sc_key_inject_mode {
// Inject special keys, letters and space as key events. // Inject special keys, letters and space as key events.
// Inject numbers and punctuation as text events. // Inject numbers and punctuation as text events.
@ -186,6 +169,8 @@ enum sc_key_inject_mode {
SC_KEY_INJECT_MODE_RAW, SC_KEY_INJECT_MODE_RAW,
}; };
#define SC_MAX_SHORTCUT_MODS 8
enum sc_shortcut_mod { enum sc_shortcut_mod {
SC_SHORTCUT_MOD_LCTRL = 1 << 0, SC_SHORTCUT_MOD_LCTRL = 1 << 0,
SC_SHORTCUT_MOD_RCTRL = 1 << 1, SC_SHORTCUT_MOD_RCTRL = 1 << 1,
@ -195,6 +180,11 @@ enum sc_shortcut_mod {
SC_SHORTCUT_MOD_RSUPER = 1 << 5, SC_SHORTCUT_MOD_RSUPER = 1 << 5,
}; };
struct sc_shortcut_mods {
unsigned data[SC_MAX_SHORTCUT_MODS];
unsigned count;
};
struct sc_port_range { struct sc_port_range {
uint16_t first; uint16_t first;
uint16_t last; uint16_t last;
@ -225,12 +215,11 @@ struct scrcpy_options {
enum sc_record_format record_format; enum sc_record_format record_format;
enum sc_keyboard_input_mode keyboard_input_mode; enum sc_keyboard_input_mode keyboard_input_mode;
enum sc_mouse_input_mode mouse_input_mode; enum sc_mouse_input_mode mouse_input_mode;
struct sc_mouse_bindings mouse_bindings;
enum sc_camera_facing camera_facing; enum sc_camera_facing camera_facing;
struct sc_port_range port_range; struct sc_port_range port_range;
uint32_t tunnel_host; uint32_t tunnel_host;
uint16_t tunnel_port; uint16_t tunnel_port;
uint8_t shortcut_mods; // OR of enum sc_shortcut_mod values struct sc_shortcut_mods shortcut_mods;
uint16_t max_size; uint16_t max_size;
uint32_t video_bit_rate; uint32_t video_bit_rate;
uint32_t audio_bit_rate; uint32_t audio_bit_rate;
@ -268,6 +257,7 @@ struct scrcpy_options {
bool force_adb_forward; bool force_adb_forward;
bool disable_screensaver; bool disable_screensaver;
bool forward_key_repeat; bool forward_key_repeat;
bool forward_all_clicks;
bool legacy_paste; bool legacy_paste;
bool power_off_on_close; bool power_off_on_close;
bool clipboard_autosync; bool clipboard_autosync;
@ -290,7 +280,6 @@ struct scrcpy_options {
#define SC_OPTION_LIST_CAMERA_SIZES 0x8 #define SC_OPTION_LIST_CAMERA_SIZES 0x8
uint8_t list; uint8_t list;
bool window; bool window;
bool mouse_hover;
}; };
extern const struct scrcpy_options scrcpy_options_default; extern const struct scrcpy_options scrcpy_options_default;

View File

@ -681,8 +681,7 @@ scrcpy(struct scrcpy_options *options) {
} }
if (options->mouse_input_mode == SC_MOUSE_INPUT_MODE_SDK) { if (options->mouse_input_mode == SC_MOUSE_INPUT_MODE_SDK) {
sc_mouse_sdk_init(&s->mouse_sdk, &s->controller, sc_mouse_sdk_init(&s->mouse_sdk, &s->controller);
options->mouse_hover);
mp = &s->mouse_sdk.mouse_processor; mp = &s->mouse_sdk.mouse_processor;
} else if (options->mouse_input_mode == SC_MOUSE_INPUT_MODE_UHID) { } else if (options->mouse_input_mode == SC_MOUSE_INPUT_MODE_UHID) {
bool ok = sc_mouse_uhid_init(&s->mouse_uhid, &s->controller); bool ok = sc_mouse_uhid_init(&s->mouse_uhid, &s->controller);
@ -713,10 +712,10 @@ scrcpy(struct scrcpy_options *options) {
.fp = fp, .fp = fp,
.kp = kp, .kp = kp,
.mp = mp, .mp = mp,
.mouse_bindings = options->mouse_bindings, .forward_all_clicks = options->forward_all_clicks,
.legacy_paste = options->legacy_paste, .legacy_paste = options->legacy_paste,
.clipboard_autosync = options->clipboard_autosync, .clipboard_autosync = options->clipboard_autosync,
.shortcut_mods = options->shortcut_mods, .shortcut_mods = &options->shortcut_mods,
.window_title = window_title, .window_title = window_title,
.always_on_top = options->always_on_top, .always_on_top = options->always_on_top,
.window_x = options->window_x, .window_x = options->window_x,

View File

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

View File

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

View File

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

View File

@ -124,22 +124,32 @@ static void test_options2(void) {
} }
static void test_parse_shortcut_mods(void) { static void test_parse_shortcut_mods(void) {
uint8_t mods; struct sc_shortcut_mods mods;
bool ok; bool ok;
ok = sc_parse_shortcut_mods("lctrl", &mods); ok = sc_parse_shortcut_mods("lctrl", &mods);
assert(ok); assert(ok);
assert(mods == SC_SHORTCUT_MOD_LCTRL); assert(mods.count == 1);
assert(mods.data[0] == SC_SHORTCUT_MOD_LCTRL);
ok = sc_parse_shortcut_mods("lctrl+lalt", &mods);
assert(ok);
assert(mods.count == 1);
assert(mods.data[0] == (SC_SHORTCUT_MOD_LCTRL | SC_SHORTCUT_MOD_LALT));
ok = sc_parse_shortcut_mods("rctrl,lalt", &mods); ok = sc_parse_shortcut_mods("rctrl,lalt", &mods);
assert(ok); assert(ok);
assert(mods == (SC_SHORTCUT_MOD_RCTRL | SC_SHORTCUT_MOD_LALT)); assert(mods.count == 2);
assert(mods.data[0] == SC_SHORTCUT_MOD_RCTRL);
assert(mods.data[1] == SC_SHORTCUT_MOD_LALT);
ok = sc_parse_shortcut_mods("lsuper,rsuper,lctrl", &mods); ok = sc_parse_shortcut_mods("lsuper,rsuper+lalt,lctrl+rctrl+ralt", &mods);
assert(ok); assert(ok);
assert(mods == (SC_SHORTCUT_MOD_LSUPER assert(mods.count == 3);
| SC_SHORTCUT_MOD_RSUPER assert(mods.data[0] == SC_SHORTCUT_MOD_LSUPER);
| SC_SHORTCUT_MOD_LCTRL)); assert(mods.data[1] == (SC_SHORTCUT_MOD_RSUPER | SC_SHORTCUT_MOD_LALT));
assert(mods.data[2] == (SC_SHORTCUT_MOD_LCTRL | SC_SHORTCUT_MOD_RCTRL |
SC_SHORTCUT_MOD_RALT));
ok = sc_parse_shortcut_mods("", &mods); ok = sc_parse_shortcut_mods("", &mods);
assert(!ok); assert(!ok);

View File

@ -7,7 +7,7 @@ buildscript {
mavenCentral() mavenCentral()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:8.3.0' classpath 'com.android.tools.build:gradle:8.1.3'
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files

View File

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

View File

@ -18,14 +18,6 @@ Note that on some devices, an additional option must be enabled in developer
options for this mouse mode to work. See options for this mouse mode to work. See
[prerequisites](/README.md#prerequisites). [prerequisites](/README.md#prerequisites).
### Mouse hover
By default, mouse hover (mouse motion without any clicks) events are forwarded
to the device. This can be disabled with:
```
scrcpy --no-mouse-hover
```
## Physical mouse simulation ## Physical mouse simulation
@ -76,43 +68,3 @@ debugging disabled (see [OTG](otg.md)).
Note: On Windows, it may only work in [OTG mode](otg.md), not while mirroring Note: On Windows, it may only work in [OTG mode](otg.md), not while mirroring
(it is not possible to open a USB device if it is already open by another (it is not possible to open a USB device if it is already open by another
process like the _adb daemon_). process like the _adb daemon_).
## Mouse bindings
By default, 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.
In AOA and UHID mouse modes, all clicks are forwarded by default.
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:
```
--mouse-bind=xxxx
^^^^
||||
||| `- 5th click
|| `-- 4th click
| `--- middle click
`---- right click
```
Each character must be one of the following:
- `+`: forward the click to the device
- `-`: ignore the click
- `b`: trigger shortcut BACK (or turn screen on if off)
- `h`: trigger shortcut HOME
- `s`: trigger shortcut APP_SWITCH
- `n`: trigger shortcut "expand notification panel"
For example:
```bash
scrcpy --mouse-bind=bhsn # the default mode 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
```

View File

@ -13,8 +13,8 @@ It can be changed using `--shortcut-mod`. Possible keys are `lctrl`, `rctrl`,
# use RCtrl for shortcuts # use RCtrl for shortcuts
scrcpy --shortcut-mod=rctrl scrcpy --shortcut-mod=rctrl
# use either LCtrl or LSuper for shortcuts # use either LCtrl+LAlt or LSuper for shortcuts
scrcpy --shortcut-mod=lctrl,lsuper scrcpy --shortcut-mod=lctrl+lalt,lsuper
``` ```
_<kbd>[Super]</kbd> is typically the <kbd>Windows</kbd> or <kbd>Cmd</kbd> key._ _<kbd>[Super]</kbd> is typically the <kbd>Windows</kbd> or <kbd>Cmd</kbd> key._

View File

@ -44,19 +44,19 @@ public class ScreenCapture extends SurfaceCapture implements Device.RotationList
virtualDisplay = null; virtualDisplay = null;
} }
try {
Rect videoRect = screenInfo.getVideoSize().toRect();
virtualDisplay = ServiceManager.getDisplayManager()
.createVirtualDisplay("scrcpy", videoRect.width(), videoRect.height(), device.getDisplayId(), surface);
Ln.d("Display: using DisplayManager API");
} catch (Exception displayManagerException) {
try { try {
display = createDisplay(); display = createDisplay();
setDisplaySurface(display, surface, videoRotation, contentRect, unlockedVideoRect, layerStack); setDisplaySurface(display, surface, videoRotation, contentRect, unlockedVideoRect, layerStack);
Ln.d("Display: using SurfaceControl API"); Ln.d("Display: using SurfaceControl API");
} catch (Exception surfaceControlException) { } catch (Exception surfaceControlException) {
Ln.e("Could not create display using DisplayManager", displayManagerException); Rect videoRect = screenInfo.getVideoSize().toRect();
try {
virtualDisplay = ServiceManager.getDisplayManager()
.createVirtualDisplay("scrcpy", videoRect.width(), videoRect.height(), device.getDisplayId(), surface);
Ln.d("Display: using DisplayManager API");
} catch (Exception displayManagerException) {
Ln.e("Could not create display using SurfaceControl", surfaceControlException); Ln.e("Could not create display using SurfaceControl", surfaceControlException);
Ln.e("Could not create display using DisplayManager", displayManagerException);
throw new AssertionError("Could not create display"); throw new AssertionError("Could not create display");
} }
} }
@ -68,11 +68,6 @@ public class ScreenCapture extends SurfaceCapture implements Device.RotationList
device.setFoldListener(null); device.setFoldListener(null);
if (display != null) { if (display != null) {
SurfaceControl.destroyDisplay(display); SurfaceControl.destroyDisplay(display);
display = null;
}
if (virtualDisplay != null) {
virtualDisplay.release();
virtualDisplay = null;
} }
} }