Compare commits

..

4 Commits

Author SHA1 Message Date
Romain Vimont
56cda01afc fix2 2024-04-28 16:01:21 +02:00
Romain Vimont
35a38c1090 fix 2024-04-28 15:41:31 +02:00
Romain Vimont
64f7c72d59 Update documentation for --no-window
PR #4868 <https://github.com/Genymobile/scrcpy/pull/4868>
2024-04-28 15:00:45 +02:00
Romain Vimont
ec1cfdf432 Add scrcpy window without video playback
Add the possibility to only control the device with any keyboard and
mouse mode without screen mirroring:

    scrcpy -KM --no-video --no-audio

This is different from OTG mode, which does not require USB debugging at
all. Here, the standard mode is used but with the possibility to disable
video playback.

By default, always open a window (even without video playback), and add
an option --no-window.

Fixes #4727 <https://github.com/Genymobile/scrcpy/issues/4727>
Fixes #4793 <https://github.com/Genymobile/scrcpy/issues/4793>
PR #4868 <https://github.com/Genymobile/scrcpy/pull/4868>
2024-04-28 15:00:00 +02:00
54 changed files with 404 additions and 801 deletions

View File

@@ -2,7 +2,7 @@
source for the project. Do not download releases from random websites, even if
their name contains `scrcpy`.**
# scrcpy (v2.5)
# scrcpy (v2.4)
<img src="app/data/icon.svg" width="128" height="128" alt="scrcpy" align="right" />

View File

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

View File

@@ -32,9 +32,10 @@ arguments=(
{-e,--select-tcpip}'[Use TCP/IP device]'
{-f,--fullscreen}'[Start in fullscreen]'
'--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]'
'-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]'
'--legacy-paste[Inject computer clipboard text as a sequence of key events on Ctrl+v]'
'--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[Use UHID mouse (same as --mouse=uhid)]'
'--max-fps=[Limit the frame rate of screen capture]'
'--mouse=[Set the mouse input mode]:mode:(disabled sdk uhid aoa)'
'--mouse-bind=[Configure bindings of secondary clicks]'
'--mouse[Set the mouse input mode]:mode:(disabled sdk uhid aoa)'
{-n,--no-control}'[Disable device control \(mirror the device in read only\)]'
{-N,--no-playback}'[Disable video and audio playback]'
'--no-audio[Disable audio forwarding]'
@@ -56,7 +56,6 @@ arguments=(
'--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-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-video[Disable video forwarding]'
'--no-video-playback[Disable video playback]'

View File

@@ -4,10 +4,10 @@ DEPS_DIR=$(dirname ${BASH_SOURCE[0]})
cd "$DEPS_DIR"
. common
VERSION=35.0.0
VERSION=34.0.5
FILENAME=platform-tools_r$VERSION-windows.zip
PROJECT_DIR=platform-tools-$VERSION
SHA256SUM=7ab78a8f8b305ae4d0de647d99c43599744de61a0838d3a47bda0cdffefee87e
SHA256SUM=3f8320152704377de150418a3c4c9d07d16d80a6c0d0d8f7289c22c499e33571
cd "$SOURCES_DIR"

View File

@@ -4,10 +4,10 @@ DEPS_DIR=$(dirname ${BASH_SOURCE[0]})
cd "$DEPS_DIR"
. common
VERSION=7.0.1
VERSION=6.1.1
FILENAME=ffmpeg-$VERSION.tar.xz
PROJECT_DIR=ffmpeg-$VERSION
SHA256SUM=bce9eeb0f17ef8982390b1f37711a61b4290dc8c2a0c1a37b5857e85bfb0e4ff
SHA256SUM=8684f4b00f94b85461884c3719382f1261f0d9eb3d59640a1f4ac0873616f968
cd "$SOURCES_DIR"
@@ -17,6 +17,7 @@ then
else
get_file "https://ffmpeg.org/releases/$FILENAME" "$FILENAME" "$SHA256SUM"
tar xf "$FILENAME" # First level directory is "$PROJECT_DIR"
patch -d "$PROJECT_DIR" -p1 < "$PATCHES_DIR"/ffmpeg-6.1-fix-build.patch
fi
mkdir -p "$BUILD_DIR/$PROJECT_DIR"

View File

@@ -0,0 +1,27 @@
From 03c80197afb324da38c9b70254231e3fdcfa68fc Mon Sep 17 00:00:00 2001
From: Romain Vimont <rom@rom1v.com>
Date: Sun, 12 Nov 2023 17:58:50 +0100
Subject: [PATCH] Fix FFmpeg 6.1 build
Build failed on tag n6.1 With --enable-decoder=av1 but without
--enable-muxer=av1.
---
libavcodec/Makefile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 580a8d6b54..aff19b670c 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -249,7 +249,7 @@ OBJS-$(CONFIG_ATRAC3PAL_DECODER) += atrac3plusdec.o atrac3plus.o \
OBJS-$(CONFIG_ATRAC9_DECODER) += atrac9dec.o
OBJS-$(CONFIG_AURA_DECODER) += cyuv.o
OBJS-$(CONFIG_AURA2_DECODER) += aura.o
-OBJS-$(CONFIG_AV1_DECODER) += av1dec.o
+OBJS-$(CONFIG_AV1_DECODER) += av1dec.o av1_parse.o
OBJS-$(CONFIG_AV1_CUVID_DECODER) += cuviddec.o
OBJS-$(CONFIG_AV1_MEDIACODEC_DECODER) += mediacodecdec.o
OBJS-$(CONFIG_AV1_MEDIACODEC_ENCODER) += mediacodecenc.o
--
2.42.0

View File

@@ -4,10 +4,10 @@ DEPS_DIR=$(dirname ${BASH_SOURCE[0]})
cd "$DEPS_DIR"
. common
VERSION=2.30.4
VERSION=2.28.5
FILENAME=SDL-$VERSION.tar.gz
PROJECT_DIR=SDL-release-$VERSION
SHA256SUM=dcc2c8c9c3e9e1a7c8d61d9522f1cba4e9b740feb560dcb15234030984610ee2
SHA256SUM=9f0556e4a24ef5b267010038ad9e9948b62f236d5bcc4b22179f95ef62d84023
cd "$SOURCES_DIR"

View File

@@ -13,7 +13,7 @@ BEGIN
VALUE "LegalCopyright", "Romain Vimont, Genymobile"
VALUE "OriginalFilename", "scrcpy.exe"
VALUE "ProductName", "scrcpy"
VALUE "ProductVersion", "2.5"
VALUE "ProductVersion", "2.4"
END
END
BLOCK "VarFileInfo"

View File

@@ -163,6 +163,10 @@ Start in fullscreen.
.B \-\-force\-adb\-forward
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
.B \-h, \-\-help
Print this help.
@@ -257,27 +261,6 @@ 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[:xxxx]
Configure bindings of secondary clicks.
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:
- '+': 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 '++++:bhsn' for AOA and UHID.
.TP
.B \-n, \-\-no\-control
@@ -321,10 +304,6 @@ Do not forward repeated key events when a key is held down.
.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.
.TP
.B \-\-no\-mouse\-hover
Do not forward mouse hover (mouse motion without any clicks) events.
.TP
.B \-\-no\-power\-on
Do not power on the device on start.
@@ -445,9 +424,9 @@ Turn the device screen off immediately.
.BI "\-\-shortcut\-mod " key\fR[+...]][,...]
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).

View File

@@ -194,11 +194,7 @@ sc_audio_player_frame_sink_push(struct sc_frame_sink *sink,
// Still insufficient, drop old samples to make space
skipped_samples = sc_audiobuf_read(&ap->buf, NULL, remaining);
assert(skipped_samples == remaining);
}
SDL_UnlockAudioDevice(ap->device);
if (written < samples) {
// Now there is enough space
uint32_t w = sc_audiobuf_write(&ap->buf,
swr_buf + TO_BYTES(written),
@@ -206,6 +202,8 @@ sc_audio_player_frame_sink_push(struct sc_frame_sink *sink,
assert(w == remaining);
(void) w;
}
SDL_UnlockAudioDevice(ap->device);
}
uint32_t underflow = 0;

View File

@@ -98,8 +98,6 @@ enum {
OPT_HID_KEYBOARD_DEPRECATED,
OPT_HID_MOUSE_DEPRECATED,
OPT_NO_WINDOW,
OPT_MOUSE_BIND,
OPT_NO_MOUSE_HOVER,
};
struct sc_option {
@@ -354,9 +352,11 @@ static const struct sc_option options[] = {
"device.",
},
{
// deprecated
.longopt_id = OPT_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',
@@ -490,30 +490,6 @@ static const struct sc_option options[] = {
"control of the mouse back to the computer.\n"
"Also see --keyboard.",
},
{
.longopt_id = OPT_MOUSE_BIND,
.longopt = "mouse-bind",
.argdesc = "xxxx[:xxxx]",
.text = "Configure bindings of secondary clicks.\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 '++++:bhsn' for AOA "
"and UHID.",
},
{
.shortopt = 'n',
.longopt = "no-control",
@@ -576,12 +552,6 @@ static const struct sc_option options[] = {
"mipmaps are automatically generated to improve downscaling "
"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 = "no-power-on",
@@ -746,10 +716,10 @@ static const struct sc_option options[] = {
.text = "Specify the modifiers to use for scrcpy shortcuts.\n"
"Possible keys are \"lctrl\", \"rctrl\", \"lalt\", \"ralt\", "
"\"lsuper\" and \"rsuper\".\n"
"Several shortcut modifiers can be specified, separated by "
"','.\n"
"For example, to use either LCtrl or LSuper for scrcpy "
"shortcuts, pass \"lctrl,lsuper\".\n"
"A shortcut can consist in several keys, separated by '+'. "
"Several shortcuts can be specified, separated by ','.\n"
"For example, to use either LCtrl+LAlt or LSuper for scrcpy "
"shortcuts, pass \"lctrl+lalt,lsuper\".\n"
"Default is \"lalt,lsuper\" (left-Alt or left-Super).",
},
{
@@ -1717,62 +1687,82 @@ parse_log_level(const char *s, enum sc_log_level *log_level) {
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) {
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) \
((sizeof(literal)-1 == len) && !memcmp(literal, s, len))
if (STREQ("lctrl", item, len)) {
return SC_SHORTCUT_MOD_LCTRL;
}
if (STREQ("rctrl", item, len)) {
return SC_SHORTCUT_MOD_RCTRL;
}
if (STREQ("lalt", item, len)) {
return SC_SHORTCUT_MOD_LALT;
}
if (STREQ("ralt", item, len)) {
return SC_SHORTCUT_MOD_RALT;
}
if (STREQ("lsuper", item, len)) {
return SC_SHORTCUT_MOD_LSUPER;
}
if (STREQ("rsuper", item, len)) {
return SC_SHORTCUT_MOD_RSUPER;
}
if (STREQ("lctrl", item, key_len)) {
mod |= SC_SHORTCUT_MOD_LCTRL;
} else if (STREQ("rctrl", item, key_len)) {
mod |= SC_SHORTCUT_MOD_RCTRL;
} else if (STREQ("lalt", item, key_len)) {
mod |= SC_SHORTCUT_MOD_LALT;
} else if (STREQ("ralt", item, key_len)) {
mod |= SC_SHORTCUT_MOD_RALT;
} else if (STREQ("lsuper", item, key_len)) {
mod |= SC_SHORTCUT_MOD_LSUPER;
} else if (STREQ("rsuper", item, key_len)) {
mod |= SC_SHORTCUT_MOD_RSUPER;
} else {
LOGE("Unknown modifier key: %.*s "
"(must be one of: lctrl, rctrl, lalt, ralt, lsuper, rsuper)",
(int) key_len, item);
return 0;
}
#undef STREQ
bool has_plus = strchr(item, '+');
if (has_plus) {
LOGE("Shortcut mod combination with '+' is not supported anymore: "
"'%.*s' (see #4741)", (int) len, item);
return 0;
if (!has_plus) {
break;
}
item = plus + 1;
assert(len >= key_len + 1);
len -= key_len + 1;
}
LOGE("Unknown modifier key: %.*s "
"(must be one of: lctrl, rctrl, lalt, ralt, lsuper, rsuper)",
(int) len, item);
return 0;
return mod;
}
static bool
parse_shortcut_mods(const char *s, uint8_t *shortcut_mods) {
uint8_t mods = 0;
parse_shortcut_mods(const char *s, struct sc_shortcut_mods *mods) {
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 (;;) {
char *comma = strchr(s, ',');
assert(!comma || comma > s);
size_t limit = comma ? (size_t) (comma - s) : strlen(s);
enum sc_shortcut_mod mod = parse_shortcut_mods_item(s, limit);
if (!mod) {
if (comma && count == SC_MAX_SHORTCUT_MODS - 1) {
assert(count < SC_MAX_SHORTCUT_MODS);
LOGW("Too many shortcut modifiers alternatives");
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) {
break;
@@ -1781,7 +1771,7 @@ parse_shortcut_mods(const char *s, uint8_t *shortcut_mods) {
s = comma + 1;
}
*shortcut_mods = mods;
mods->count = count;
return true;
}
@@ -1789,7 +1779,7 @@ parse_shortcut_mods(const char *s, uint8_t *shortcut_mods) {
#ifdef SC_TEST
// expose the function to unit-tests
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);
}
#endif
@@ -2068,85 +2058,11 @@ parse_pause_on_exit(const char *s, enum sc_pause_on_exit *pause_on_exit) {
}
LOGE("Unsupported pause on exit mode: %s "
"(expected true, false or if-error)", s);
"(expected true, false or if-error)", optarg);
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_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;
}
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;
}
static bool
parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
const char *optstring, const struct option *longopts) {
@@ -2229,14 +2145,6 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
return false;
}
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:
LOGE("--hid-mouse has been removed, use --mouse=aoa or "
"--mouse=uhid instead.");
@@ -2434,22 +2342,7 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
}
break;
case OPT_FORWARD_ALL_CLICKS:
LOGW("--forward-all-clicks is deprecated, "
"use --mouse-bind=++++ instead.");
opts->mouse_bindings = (struct sc_mouse_bindings) {
.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,
},
};
opts->forward_all_clicks = true;
break;
case OPT_LEGACY_PASTE:
opts->legacy_paste = true;
@@ -2692,11 +2585,6 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
#ifdef HAVE_V4L2
if (v4l2) {
if (!opts->video) {
LOGE("V4L2 sink requires video capture, but --no-video was set.");
return false;
}
if (opts->lock_video_orientation ==
SC_LOCK_VIDEO_ORIENTATION_UNLOCKED) {
LOGI("Video orientation is locked for v4l2 sink. "
@@ -2725,8 +2613,9 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
if (otg) {
opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_AOA;
} else if (!opts->video_playback) {
LOGI("No video mirroring, mouse mode switched to UHID");
opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_UHID;
LOGI("No video mirroring, SDK mouse disabled (you might want "
"--mouse=uhid)");
opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_DISABLED;
} else {
opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_SDK;
}
@@ -2737,40 +2626,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.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.pri = default_shortcuts;
opts->mouse_bindings.sec = forward;
} else {
opts->mouse_bindings.pri = forward;
opts->mouse_bindings.sec = default_shortcuts;
}
}
if (otg) {
if (!opts->control) {
LOGE("--no-control is not allowed in OTG mode");
@@ -2815,12 +2670,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) {
LOGI("Tunnel host/port is set, "
"--force-adb-forward automatically enabled.");
@@ -2885,11 +2734,6 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
}
if (opts->record_filename) {
if (!opts->video && !opts->audio) {
LOGE("Video and audio disabled, nothing to record");
return false;
}
if (!opts->record_format) {
opts->record_format = guess_record_format(opts->record_filename);
if (!opts->record_format) {

View File

@@ -28,7 +28,7 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]);
#ifdef SC_TEST
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

View File

@@ -64,11 +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 SC_POINTER_ID_MOUSE:
case POINTER_ID_MOUSE:
return "mouse";
case SC_POINTER_ID_GENERIC_FINGER:
case POINTER_ID_GENERIC_FINGER:
return "finger";
case SC_POINTER_ID_VIRTUAL_FINGER:
case POINTER_ID_VIRTUAL_MOUSE:
return "vmouse";
case POINTER_ID_VIRTUAL_FINGER:
return "vfinger";
default:
return NULL;

View File

@@ -18,11 +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 SC_POINTER_ID_MOUSE UINT64_C(-1)
#define SC_POINTER_ID_GENERIC_FINGER UINT64_C(-2)
#define POINTER_ID_MOUSE UINT64_C(-1)
#define POINTER_ID_GENERIC_FINGER UINT64_C(-2)
// Used for injecting an additional virtual pointer for pinch-to-zoom
#define SC_POINTER_ID_VIRTUAL_FINGER UINT64_C(-3)
#define POINTER_ID_VIRTUAL_MOUSE UINT64_C(-3)
#define POINTER_ID_VIRTUAL_FINGER UINT64_C(-4)
enum sc_control_msg_type {
SC_CONTROL_MSG_TYPE_INJECT_KEYCODE,

View File

@@ -6,20 +6,8 @@
#define SC_CONTROL_MSG_QUEUE_MAX 64
static void
sc_controller_receiver_on_ended(struct sc_receiver *receiver, bool error,
void *userdata) {
(void) receiver;
struct sc_controller *controller = userdata;
// Forward the event to the controller listener
controller->cbs->on_ended(controller, error, controller->cbs_userdata);
}
bool
sc_controller_init(struct sc_controller *controller, sc_socket control_socket,
const struct sc_controller_callbacks *cbs,
void *cbs_userdata) {
sc_controller_init(struct sc_controller *controller, sc_socket control_socket) {
sc_vecdeque_init(&controller->queue);
bool ok = sc_vecdeque_reserve(&controller->queue, SC_CONTROL_MSG_QUEUE_MAX);
@@ -27,12 +15,7 @@ sc_controller_init(struct sc_controller *controller, sc_socket control_socket,
return false;
}
static const struct sc_receiver_callbacks receiver_cbs = {
.on_ended = sc_controller_receiver_on_ended,
};
ok = sc_receiver_init(&controller->receiver, control_socket, &receiver_cbs,
controller);
ok = sc_receiver_init(&controller->receiver, control_socket);
if (!ok) {
sc_vecdeque_destroy(&controller->queue);
return false;
@@ -56,10 +39,6 @@ sc_controller_init(struct sc_controller *controller, sc_socket control_socket,
controller->control_socket = control_socket;
controller->stopped = false;
assert(cbs && cbs->on_ended);
controller->cbs = cbs;
controller->cbs_userdata = cbs_userdata;
return true;
}
@@ -111,30 +90,21 @@ sc_controller_push_msg(struct sc_controller *controller,
static bool
process_msg(struct sc_controller *controller,
const struct sc_control_msg *msg, bool *eos) {
const struct sc_control_msg *msg) {
static uint8_t serialized_msg[SC_CONTROL_MSG_MAX_SIZE];
size_t length = sc_control_msg_serialize(msg, serialized_msg);
if (!length) {
*eos = false;
return false;
}
ssize_t w =
net_send_all(controller->control_socket, serialized_msg, length);
if ((size_t) w != length) {
*eos = true;
return false;
}
return true;
return (size_t) w == length;
}
static int
run_controller(void *data) {
struct sc_controller *controller = data;
bool error = false;
for (;;) {
sc_mutex_lock(&controller->mutex);
while (!controller->stopped
@@ -144,7 +114,6 @@ run_controller(void *data) {
if (controller->stopped) {
// stop immediately, do not process further msgs
sc_mutex_unlock(&controller->mutex);
LOGD("Controller stopped");
break;
}
@@ -152,20 +121,13 @@ run_controller(void *data) {
struct sc_control_msg msg = sc_vecdeque_pop(&controller->queue);
sc_mutex_unlock(&controller->mutex);
bool eos;
bool ok = process_msg(controller, &msg, &eos);
bool ok = process_msg(controller, &msg);
sc_control_msg_destroy(&msg);
if (!ok) {
if (eos) {
LOGD("Controller stopped (socket closed)");
} // else error already logged
error = !eos;
LOGD("Could not write msg to socket");
break;
}
}
controller->cbs->on_ended(controller, error, controller->cbs_userdata);
return 0;
}

View File

@@ -22,20 +22,10 @@ struct sc_controller {
bool stopped;
struct sc_control_msg_queue queue;
struct sc_receiver receiver;
const struct sc_controller_callbacks *cbs;
void *cbs_userdata;
};
struct sc_controller_callbacks {
void (*on_ended)(struct sc_controller *controller, bool error,
void *userdata);
};
bool
sc_controller_init(struct sc_controller *controller, sc_socket control_socket,
const struct sc_controller_callbacks *cbs,
void *cbs_userdata);
sc_controller_init(struct sc_controller *controller, sc_socket control_socket);
void
sc_controller_configure(struct sc_controller *controller,

View File

@@ -278,6 +278,7 @@ run_demuxer(void *data) {
finally_close_sinks:
sc_packet_source_sinks_close(&demuxer->packet_source);
finally_free_context:
// This also calls avcodec_close() internally
avcodec_free_context(&codec_ctx);
end:
demuxer->cbs->on_ended(demuxer, status, demuxer->cbs_userdata);

View File

@@ -23,6 +23,12 @@ sc_display_init_novideo_icon(struct sc_display *display,
return false;
}
SDL_RenderClear(display->renderer);
if (display->texture) {
SDL_RenderCopy(display->renderer, display->texture, NULL, NULL);
}
SDL_RenderPresent(display->renderer);
return true;
}
@@ -43,10 +49,6 @@ 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

@@ -7,4 +7,3 @@
#define SC_EVENT_RECORDER_ERROR (SDL_USEREVENT + 6)
#define SC_EVENT_SCREEN_INIT_SIZE (SDL_USEREVENT + 7)
#define SC_EVENT_TIME_LIMIT_REACHED (SDL_USEREVENT + 8)
#define SC_EVENT_CONTROLLER_ERROR (SDL_USEREVENT + 9)

View File

@@ -78,19 +78,7 @@ decode_image(const char *path) {
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;
#else
AVCodec *codec;
#endif
int stream =
av_find_best_stream(ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &codec, 0);
int stream = av_find_best_stream(ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
if (stream < 0 ) {
LOGE("Could not find best image stream");
goto close_input;
@@ -98,6 +86,12 @@ decode_image(const char *path) {
AVCodecParameters *params = ctx->streams[stream]->codecpar;
const AVCodec *codec = avcodec_find_decoder(params->codec_id);
if (!codec) {
LOGE("Could not find image decoder");
goto close_input;
}
AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);
if (!codec_ctx) {
LOG_OOM();
@@ -117,21 +111,21 @@ decode_image(const char *path) {
AVFrame *frame = av_frame_alloc();
if (!frame) {
LOG_OOM();
goto free_codec_ctx;
goto close_codec;
}
AVPacket *packet = av_packet_alloc();
if (!packet) {
LOG_OOM();
av_frame_free(&frame);
goto free_codec_ctx;
goto close_codec;
}
if (av_read_frame(ctx, packet) < 0) {
LOGE("Could not read frame");
av_packet_free(&packet);
av_frame_free(&frame);
goto free_codec_ctx;
goto close_codec;
}
int ret;
@@ -139,20 +133,22 @@ decode_image(const char *path) {
LOGE("Could not send icon packet: %d", ret);
av_packet_free(&packet);
av_frame_free(&frame);
goto free_codec_ctx;
goto close_codec;
}
if ((ret = avcodec_receive_frame(codec_ctx, frame)) != 0) {
LOGE("Could not receive icon frame: %d", ret);
av_packet_free(&packet);
av_frame_free(&frame);
goto free_codec_ctx;
goto close_codec;
}
av_packet_free(&packet);
result = frame;
close_codec:
avcodec_close(codec_ctx);
free_codec_ctx:
avcodec_free_context(&codec_ctx);
close_input:

View File

@@ -9,7 +9,6 @@
#include <SDL2/SDL_events.h>
#include "coords.h"
#include "options.h"
/* The representation of input events in scrcpy is very close to the SDL API,
* for simplicity.
@@ -437,11 +436,19 @@ sc_mouse_button_from_sdl(uint8_t button) {
}
static inline uint8_t
sc_mouse_buttons_state_from_sdl(uint32_t buttons_state) {
sc_mouse_buttons_state_from_sdl(uint32_t buttons_state,
bool forward_all_clicks) {
assert(buttons_state < 0x100); // fits in uint8_t
// SC_MOUSE_BUTTON_* constants are initialized from SDL_BUTTON(index)
return buttons_state;
uint8_t mask = SC_MOUSE_BUTTON_LEFT;
if (forward_all_clicks) {
mask |= SC_MOUSE_BUTTON_RIGHT
| SC_MOUSE_BUTTON_MIDDLE
| SC_MOUSE_BUTTON_X1
| SC_MOUSE_BUTTON_X2;
}
return buttons_state & mask;
}
#endif

View File

@@ -10,7 +10,7 @@
#define SC_SDL_SHORTCUT_MODS_MASK (KMOD_CTRL | KMOD_ALT | KMOD_GUI)
static inline uint16_t
to_sdl_mod(uint8_t shortcut_mod) {
to_sdl_mod(unsigned shortcut_mod) {
uint16_t sdl_mod = 0;
if (shortcut_mod & SC_SHORTCUT_MOD_LCTRL) {
sdl_mod |= KMOD_LCTRL;
@@ -38,18 +38,15 @@ is_shortcut_mod(struct sc_input_manager *im, uint16_t sdl_mod) {
// keep only the relevant modifier keys
sdl_mod &= SC_SDL_SHORTCUT_MODS_MASK;
// at least one shortcut mod pressed?
return sdl_mod & im->sdl_shortcut_mods;
}
assert(im->sdl_shortcut_mods.count);
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
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);
return false;
}
void
@@ -67,18 +64,24 @@ sc_input_manager_init(struct sc_input_manager *im,
im->kp = params->kp;
im->mp = params->mp;
im->mouse_bindings = params->mouse_bindings;
im->forward_all_clicks = params->forward_all_clicks;
im->legacy_paste = params->legacy_paste;
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_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;
@@ -367,7 +370,9 @@ 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_FINGER;
msg.inject_touch_event.pointer_id =
im->forward_all_clicks ? POINTER_ID_VIRTUAL_MOUSE
: 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;
@@ -407,12 +412,7 @@ sc_input_manager_process_key(struct sc_input_manager *im,
bool shift = event->keysym.mod & KMOD_SHIFT;
bool repeat = event->repeat;
// Either the modifier includes a shortcut modifier, or the key
// 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);
bool smod = is_shortcut_mod(im, mod);
if (down && !repeat) {
if (keycode == im->last_keycode && mod == im->last_mod) {
@@ -424,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;
switch (keycode) {
case SDLK_h:
@@ -652,11 +653,13 @@ 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->vfinger_down ? SC_POINTER_ID_GENERIC_FINGER
: SC_POINTER_ID_MOUSE,
.pointer_id = im->forward_all_clicks ? POINTER_ID_MOUSE
: POINTER_ID_GENERIC_FINGER,
.xrel = event->xrel,
.yrel = event->yrel,
.buttons_state = im->mouse_buttons_state,
.buttons_state =
sc_mouse_buttons_state_from_sdl(event->state,
im->forward_all_clicks),
};
assert(im->mp->ops->process_mouse_motion);
@@ -707,25 +710,6 @@ sc_input_manager_process_touch(struct sc_input_manager *im,
im->mp->ops->process_touch(im->mp, &evt);
}
static enum sc_mouse_binding
sc_input_manager_get_binding(const struct sc_mouse_binding_set *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
sc_input_manager_process_mouse_button(struct sc_input_manager *im,
const SDL_MouseButtonEvent *event) {
@@ -737,100 +721,65 @@ 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;
if (!im->forward_all_clicks) {
if (control && !paused) {
enum sc_action action = down ? SC_ACTION_DOWN : SC_ACTION_UP;
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(bindings, event->button);
assert(binding != SC_MOUSE_BINDING_AUTO);
switch (binding) {
case SC_MOUSE_BINDING_DISABLED:
// ignore click
if (im->kp && event->button == SDL_BUTTON_X1) {
action_app_switch(im, action);
return;
}
if (event->button == SDL_BUTTON_X2 && down) {
if (event->clicks < 2) {
expand_notification_panel(im);
} else {
expand_settings_panel(im);
}
return;
}
if (im->kp && event->button == SDL_BUTTON_RIGHT) {
press_back_or_turn_screen_on(im, action);
return;
}
if (im->kp && event->button == SDL_BUTTON_MIDDLE) {
action_home(im, action);
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);
}
return;
case SC_MOUSE_BINDING_EXPAND_NOTIFICATION_PANEL:
if (down) {
if (event->clicks < 2) {
expand_notification_panel(im);
} else {
expand_settings_panel(im);
}
}
return;
default:
assert(binding == SC_MOUSE_BINDING_CLICK);
break;
}
}
// double-click on black borders resizes to fit the device screen
bool video = im->screen->video;
bool mouse_relative_mode = im->mp && im->mp->relative_mode;
if (video && !mouse_relative_mode && event->button == SDL_BUTTON_LEFT
&& event->clicks == 2) {
int32_t x = event->x;
int32_t y = event->y;
sc_screen_hidpi_scale_coords(im->screen, &x, &y);
SDL_Rect *r = &im->screen->rect;
bool outside = x < r->x || x >= r->x + r->w
|| y < r->y || y >= r->y + r->h;
if (outside) {
if (down) {
sc_screen_resize_to_fit(im->screen);
}
return;
}
// double-click on black borders resize to fit the device screen
if (event->button == SDL_BUTTON_LEFT && event->clicks == 2) {
int32_t x = event->x;
int32_t y = event->y;
sc_screen_hidpi_scale_coords(im->screen, &x, &y);
SDL_Rect *r = &im->screen->rect;
bool outside = x < r->x || x >= r->x + r->w
|| y < r->y || y >= r->y + r->h;
if (outside) {
if (down) {
sc_screen_resize_to_fit(im->screen);
}
return;
}
}
// otherwise, send the click event to the device
}
if (!im->mp || paused) {
return;
}
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;
uint32_t sdl_buttons_state = SDL_GetMouseState(NULL, NULL);
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 = use_finger ? SC_POINTER_ID_GENERIC_FINGER
: SC_POINTER_ID_MOUSE,
.buttons_state = im->mouse_buttons_state,
.pointer_id = im->forward_all_clicks ? POINTER_ID_MOUSE
: POINTER_ID_GENERIC_FINGER,
.buttons_state =
sc_mouse_buttons_state_from_sdl(sdl_buttons_state,
im->forward_all_clicks),
};
assert(im->mp->ops->process_mouse_click);
@@ -856,7 +805,14 @@ 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.
if (change_vfinger) {
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))) {
struct sc_point mouse =
sc_screen_convert_window_to_frame_coords(im->screen, event->x,
event->y);
@@ -889,7 +845,6 @@ 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),
@@ -900,7 +855,8 @@ 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 = im->mouse_buttons_state,
.buttons_state =
sc_mouse_buttons_state_from_sdl(buttons, im->forward_all_clicks),
};
im->mp->ops->process_mouse_scroll(im->mp, &evt);

View File

@@ -22,18 +22,19 @@ struct sc_input_manager {
struct sc_key_processor *kp;
struct sc_mouse_processor *mp;
struct sc_mouse_bindings mouse_bindings;
bool forward_all_clicks;
bool legacy_paste;
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_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.
@@ -51,10 +52,10 @@ struct sc_input_manager_params {
struct sc_key_processor *kp;
struct sc_mouse_processor *mp;
struct sc_mouse_bindings mouse_bindings;
bool forward_all_clicks;
bool legacy_paste;
bool clipboard_autosync;
uint8_t shortcut_mods; // OR of enum sc_shortcut_mod values
const struct sc_shortcut_mods *shortcut_mods;
};
void

View File

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

View File

@@ -23,20 +23,6 @@ const struct scrcpy_options scrcpy_options_default = {
.record_format = SC_RECORD_FORMAT_AUTO,
.keyboard_input_mode = SC_KEYBOARD_INPUT_MODE_AUTO,
.mouse_input_mode = SC_MOUSE_INPUT_MODE_AUTO,
.mouse_bindings = {
.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 = {
.first = DEFAULT_LOCAL_PORT_RANGE_FIRST,
@@ -44,7 +30,10 @@ const struct scrcpy_options scrcpy_options_default = {
},
.tunnel_host = 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,
.video_bit_rate = 0,
.audio_bit_rate = 0,
@@ -82,6 +71,7 @@ const struct scrcpy_options scrcpy_options_default = {
.force_adb_forward = false,
.disable_screensaver = false,
.forward_key_repeat = true,
.forward_all_clicks = false,
.legacy_paste = false,
.power_off_on_close = false,
.clipboard_autosync = true,
@@ -100,7 +90,6 @@ const struct scrcpy_options scrcpy_options_default = {
.camera_high_speed = false,
.list = 0,
.window = true,
.mouse_hover = true,
};
enum sc_orientation

View File

@@ -155,28 +155,6 @@ enum sc_mouse_input_mode {
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_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.
@@ -191,6 +169,8 @@ enum sc_key_inject_mode {
SC_KEY_INJECT_MODE_RAW,
};
#define SC_MAX_SHORTCUT_MODS 8
enum sc_shortcut_mod {
SC_SHORTCUT_MOD_LCTRL = 1 << 0,
SC_SHORTCUT_MOD_RCTRL = 1 << 1,
@@ -200,6 +180,11 @@ enum sc_shortcut_mod {
SC_SHORTCUT_MOD_RSUPER = 1 << 5,
};
struct sc_shortcut_mods {
unsigned data[SC_MAX_SHORTCUT_MODS];
unsigned count;
};
struct sc_port_range {
uint16_t first;
uint16_t last;
@@ -230,12 +215,11 @@ struct scrcpy_options {
enum sc_record_format record_format;
enum sc_keyboard_input_mode keyboard_input_mode;
enum sc_mouse_input_mode mouse_input_mode;
struct sc_mouse_bindings mouse_bindings;
enum sc_camera_facing camera_facing;
struct sc_port_range port_range;
uint32_t tunnel_host;
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;
uint32_t video_bit_rate;
uint32_t audio_bit_rate;
@@ -273,6 +257,7 @@ struct scrcpy_options {
bool force_adb_forward;
bool disable_screensaver;
bool forward_key_repeat;
bool forward_all_clicks;
bool legacy_paste;
bool power_off_on_close;
bool clipboard_autosync;
@@ -295,7 +280,6 @@ struct scrcpy_options {
#define SC_OPTION_LIST_CAMERA_SIZES 0x8
uint8_t list;
bool window;
bool mouse_hover;
};
extern const struct scrcpy_options scrcpy_options_default;

View File

@@ -10,8 +10,7 @@
#include "util/str.h"
bool
sc_receiver_init(struct sc_receiver *receiver, sc_socket control_socket,
const struct sc_receiver_callbacks *cbs, void *cbs_userdata) {
sc_receiver_init(struct sc_receiver *receiver, sc_socket control_socket) {
bool ok = sc_mutex_init(&receiver->mutex);
if (!ok) {
return false;
@@ -21,10 +20,6 @@ sc_receiver_init(struct sc_receiver *receiver, sc_socket control_socket,
receiver->acksync = NULL;
receiver->uhid_devices = NULL;
assert(cbs && cbs->on_ended);
receiver->cbs = cbs;
receiver->cbs_userdata = cbs_userdata;
return true;
}
@@ -134,15 +129,12 @@ run_receiver(void *data) {
static uint8_t buf[DEVICE_MSG_MAX_SIZE];
size_t head = 0;
bool error = false;
for (;;) {
assert(head < DEVICE_MSG_MAX_SIZE);
ssize_t r = net_recv(receiver->control_socket, buf + head,
DEVICE_MSG_MAX_SIZE - head);
if (r <= 0) {
LOGD("Receiver stopped");
// device disconnected: keep error=false
break;
}
@@ -150,7 +142,6 @@ run_receiver(void *data) {
ssize_t consumed = process_msgs(receiver, buf, head);
if (consumed == -1) {
// an error occurred
error = true;
break;
}
@@ -161,8 +152,6 @@ run_receiver(void *data) {
}
}
receiver->cbs->on_ended(receiver, error, receiver->cbs_userdata);
return 0;
}

View File

@@ -19,18 +19,10 @@ struct sc_receiver {
struct sc_acksync *acksync;
struct sc_uhid_devices *uhid_devices;
const struct sc_receiver_callbacks *cbs;
void *cbs_userdata;
};
struct sc_receiver_callbacks {
void (*on_ended)(struct sc_receiver *receiver, bool error, void *userdata);
};
bool
sc_receiver_init(struct sc_receiver *receiver, sc_socket control_socket,
const struct sc_receiver_callbacks *cbs, void *cbs_userdata);
sc_receiver_init(struct sc_receiver *receiver, sc_socket control_socket);
void
sc_receiver_destroy(struct sc_receiver *receiver);

View File

@@ -174,9 +174,6 @@ event_loop(struct scrcpy *s) {
case SC_EVENT_DEMUXER_ERROR:
LOGE("Demuxer error");
return SCRCPY_EXIT_FAILURE;
case SC_EVENT_CONTROLLER_ERROR:
LOGE("Controller error");
return SCRCPY_EXIT_FAILURE;
case SC_EVENT_RECORDER_ERROR:
LOGE("Recorder error");
return SCRCPY_EXIT_FAILURE;
@@ -268,21 +265,6 @@ sc_audio_demuxer_on_ended(struct sc_demuxer *demuxer,
}
}
static void
sc_controller_on_ended(struct sc_controller *controller, bool error,
void *userdata) {
// Note: this function may be called twice, once from the controller thread
// and once from the receiver thread
(void) controller;
(void) userdata;
if (error) {
PUSH_EVENT(SC_EVENT_CONTROLLER_ERROR);
} else {
PUSH_EVENT(SC_EVENT_DEVICE_DISCONNECTED);
}
}
static void
sc_server_on_connection_failed(struct sc_server *server, void *userdata) {
(void) server;
@@ -426,7 +408,7 @@ scrcpy(struct scrcpy_options *options) {
return SCRCPY_EXIT_FAILURE;
}
if (options->window) {
if (options->video_playback) {
// Set hints before starting the server thread to avoid race conditions
// in SDL
sdl_set_hints(options->render_driver);
@@ -571,12 +553,7 @@ scrcpy(struct scrcpy_options *options) {
struct sc_mouse_processor *mp = NULL;
if (options->control) {
static const struct sc_controller_callbacks controller_cbs = {
.on_ended = sc_controller_on_ended,
};
if (!sc_controller_init(&s->controller, s->server.control_socket,
&controller_cbs, NULL)) {
if (!sc_controller_init(&s->controller, s->server.control_socket)) {
goto end;
}
controller_initialized = true;
@@ -686,8 +663,7 @@ scrcpy(struct scrcpy_options *options) {
}
if (options->mouse_input_mode == SC_MOUSE_INPUT_MODE_SDK) {
sc_mouse_sdk_init(&s->mouse_sdk, &s->controller,
options->mouse_hover);
sc_mouse_sdk_init(&s->mouse_sdk, &s->controller);
mp = &s->mouse_sdk.mouse_processor;
} else if (options->mouse_input_mode == SC_MOUSE_INPUT_MODE_UHID) {
bool ok = sc_mouse_uhid_init(&s->mouse_uhid, &s->controller);
@@ -718,10 +694,10 @@ scrcpy(struct scrcpy_options *options) {
.fp = fp,
.kp = kp,
.mp = mp,
.mouse_bindings = options->mouse_bindings,
.forward_all_clicks = options->forward_all_clicks,
.legacy_paste = options->legacy_paste,
.clipboard_autosync = options->clipboard_autosync,
.shortcut_mods = options->shortcut_mods,
.shortcut_mods = &options->shortcut_mods,
.window_title = window_title,
.always_on_top = options->always_on_top,
.window_x = options->window_x,
@@ -735,20 +711,23 @@ scrcpy(struct scrcpy_options *options) {
.start_fps_counter = options->start_fps_counter,
};
if (!sc_screen_init(&s->screen, &screen_params)) {
goto end;
}
screen_initialized = true;
struct sc_frame_source *src;
if (options->video_playback) {
struct sc_frame_source *src = &s->video_decoder.frame_source;
src = &s->video_decoder.frame_source;
if (options->display_buffer) {
sc_delay_buffer_init(&s->display_buffer,
options->display_buffer, true);
sc_frame_source_add_sink(src, &s->display_buffer.frame_sink);
src = &s->display_buffer.frame_source;
}
}
if (!sc_screen_init(&s->screen, &screen_params)) {
goto end;
}
screen_initialized = true;
if (options->video_playback) {
sc_frame_source_add_sink(src, &s->screen.frame_sink);
}
}

View File

@@ -259,13 +259,6 @@ sc_screen_render(struct sc_screen *screen, bool update_content_rect) {
(void) res; // any error already logged
}
static void
sc_screen_render_novideo(struct sc_screen *screen) {
enum sc_display_result res =
sc_display_render(&screen->display, NULL, SC_ORIENTATION_0);
(void) res; // any error already logged
}
#if defined(__APPLE__) || defined(__WINDOWS__)
# define CONTINUOUS_RESIZING_WORKAROUND
#endif
@@ -378,7 +371,6 @@ sc_screen_init(struct sc_screen *screen,
screen->mouse_capture_key_pressed = 0;
screen->paused = false;
screen->resume_frame = NULL;
screen->orientation = SC_ORIENTATION_0;
screen->video = params->video;
@@ -398,12 +390,10 @@ sc_screen_init(struct sc_screen *screen,
goto error_destroy_frame_buffer;
}
if (screen->video) {
screen->orientation = params->orientation;
if (screen->orientation != SC_ORIENTATION_0) {
LOGI("Initial display orientation set to %s",
sc_orientation_get_name(screen->orientation));
}
screen->orientation = params->orientation;
if (screen->orientation != SC_ORIENTATION_0) {
LOGI("Initial display orientation set to %s",
sc_orientation_get_name(screen->orientation));
}
uint32_t window_flags = SDL_WINDOW_ALLOW_HIGHDPI;
@@ -413,11 +403,6 @@ sc_screen_init(struct sc_screen *screen,
if (params->window_borderless) {
window_flags |= SDL_WINDOW_BORDERLESS;
}
if (params->video) {
// The window will be shown on first frame
window_flags |= SDL_WINDOW_HIDDEN
| SDL_WINDOW_RESIZABLE;
}
const char *title = params->window_title;
assert(title);
@@ -426,17 +411,22 @@ sc_screen_init(struct sc_screen *screen,
int y = SDL_WINDOWPOS_UNDEFINED;
int width = 256;
int height = 256;
if (params->window_x != SC_WINDOW_POSITION_UNDEFINED) {
x = params->window_x;
}
if (params->window_y != SC_WINDOW_POSITION_UNDEFINED) {
y = params->window_y;
}
if (params->window_width) {
width = params->window_width;
}
if (params->window_height) {
height = params->window_height;
if (params->video) {
// The window will be shown on first frame
window_flags |= SDL_WINDOW_HIDDEN
| SDL_WINDOW_RESIZABLE;
if (params->window_x != SC_WINDOW_POSITION_UNDEFINED) {
x = params->window_x;
}
if (params->window_y != SC_WINDOW_POSITION_UNDEFINED) {
y = params->window_y;
}
if (params->window_width) {
width = params->window_width;
}
if (params->window_height) {
height = params->window_height;
}
}
// The window will be positioned and sized on first video frame
@@ -459,9 +449,8 @@ sc_screen_init(struct sc_screen *screen,
}
SDL_Surface *icon_novideo = params->video ? NULL : icon;
bool mipmaps = params->video && params->mipmaps;
ok = sc_display_init(&screen->display, screen->window, icon_novideo,
mipmaps);
params->mipmaps);
if (icon) {
scrcpy_icon_destroy(icon);
}
@@ -481,7 +470,7 @@ sc_screen_init(struct sc_screen *screen,
.screen = screen,
.kp = params->kp,
.mp = params->mp,
.mouse_bindings = params->mouse_bindings,
.forward_all_clicks = params->forward_all_clicks,
.legacy_paste = params->legacy_paste,
.clipboard_autosync = params->clipboard_autosync,
.shortcut_mods = params->shortcut_mods,
@@ -868,11 +857,6 @@ sc_screen_handle_event(struct sc_screen *screen, const SDL_Event *event) {
return true;
}
case SDL_WINDOWEVENT:
if (!screen->video
&& event->window.event == SDL_WINDOWEVENT_EXPOSED) {
sc_screen_render_novideo(screen);
}
// !video implies !has_frame
assert(screen->video || !screen->has_frame);
if (!screen->has_frame) {

View File

@@ -79,10 +79,10 @@ struct sc_screen_params {
struct sc_key_processor *kp;
struct sc_mouse_processor *mp;
struct sc_mouse_bindings mouse_bindings;
bool forward_all_clicks;
bool legacy_paste;
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;
bool always_on_top;

View File

@@ -176,8 +176,6 @@ sc_process_execute_p(const char *const argv[], HANDLE *handle, unsigned flags,
free(lpAttributeList);
}
CloseHandle(pi.hThread);
// These handles are used by the child process, close them for this process
if (pin) {
CloseHandle(stdin_read_handle);

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),
.buttons_state = sc_mouse_buttons_state_from_sdl(event->state, true),
};
assert(mp->ops->process_mouse_motion);
@@ -188,7 +188,8 @@ 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),
.buttons_state =
sc_mouse_buttons_state_from_sdl(sdl_buttons_state, true),
};
assert(mp->ops->process_mouse_click);
@@ -207,7 +208,8 @@ 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),
.buttons_state =
sc_mouse_buttons_state_from_sdl(sdl_buttons_state, true),
};
assert(mp->ops->process_mouse_scroll);

View File

@@ -46,9 +46,6 @@ sc_audiobuf_read(struct sc_audiobuf *buf, void *to_, uint32_t samples_count) {
uint32_t head = atomic_load_explicit(&buf->head, memory_order_acquire);
uint32_t can_read = (buf->alloc_size + head - tail) % buf->alloc_size;
if (!can_read) {
return 0;
}
if (samples_count > can_read) {
samples_count = can_read;
}
@@ -89,9 +86,6 @@ sc_audiobuf_write(struct sc_audiobuf *buf, const void *from_,
uint32_t tail = atomic_load_explicit(&buf->tail, memory_order_acquire);
uint32_t can_write = (buf->alloc_size + tail - head - 1) % buf->alloc_size;
if (!can_write) {
return 0;
}
if (samples_count > can_write) {
samples_count = can_write;
}

View File

@@ -240,7 +240,7 @@ sc_v4l2_sink_open(struct sc_v4l2_sink *vs, const AVCodecContext *ctx) {
vs->frame = av_frame_alloc();
if (!vs->frame) {
LOG_OOM();
goto error_avcodec_free_context;
goto error_avcodec_close;
}
vs->packet = av_packet_alloc();
@@ -268,6 +268,8 @@ error_av_packet_free:
av_packet_free(&vs->packet);
error_av_frame_free:
av_frame_free(&vs->frame);
error_avcodec_close:
avcodec_close(vs->encoder_ctx);
error_avcodec_free_context:
avcodec_free_context(&vs->encoder_ctx);
error_avio_close:
@@ -295,6 +297,7 @@ sc_v4l2_sink_close(struct sc_v4l2_sink *vs) {
av_packet_free(&vs->packet);
av_frame_free(&vs->frame);
avcodec_close(vs->encoder_ctx);
avcodec_free_context(&vs->encoder_ctx);
avio_close(vs->format_ctx->pb);
avformat_free_context(vs->format_ctx);

View File

@@ -124,22 +124,32 @@ static void test_options2(void) {
}
static void test_parse_shortcut_mods(void) {
uint8_t mods;
struct sc_shortcut_mods mods;
bool ok;
ok = sc_parse_shortcut_mods("lctrl", &mods);
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);
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(mods == (SC_SHORTCUT_MOD_LSUPER
| SC_SHORTCUT_MOD_RSUPER
| SC_SHORTCUT_MOD_LCTRL));
assert(mods.count == 3);
assert(mods.data[0] == SC_SHORTCUT_MOD_LSUPER);
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);
assert(!ok);

View File

@@ -7,7 +7,7 @@ buildscript {
mavenCentral()
}
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
// in the individual module build.gradle files

View File

@@ -42,7 +42,7 @@ scrcpy --no-window
# interrupt with Ctrl+C
```
Without video, the audio latency is typically not critical, so it might be
Without video, the audio latency is typically not criticial, so it might be
interesting to add [buffering](#buffering) to minimize glitches:
```

View File

@@ -94,7 +94,7 @@ This is the preferred method (and the way the release is built).
From _Debian_, install _mingw_:
```bash
sudo apt install mingw-w64 mingw-w64-tools libz-mingw-w64-dev
sudo apt install mingw-w64 mingw-w64-tools
```
You also need the JDK to build the server:
@@ -233,10 +233,10 @@ install` must be run as root)._
#### Option 2: Use prebuilt server
- [`scrcpy-server-v2.5`][direct-scrcpy-server]
<sub>SHA-256: `1488b1105d6aff534873a26bf610cd2aea06ee867dd7a4d9c6bb2c091396eb15`</sub>
- [`scrcpy-server-v2.4`][direct-scrcpy-server]
<sub>SHA-256: `93c272b7438605c055e127f7444064ed78fa9ca49f81156777fd201e79ce7ba3`</sub>
[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v2.5/scrcpy-server-v2.5
[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v2.4/scrcpy-server-v2.4
Download the prebuilt server somewhere, and specify its path during the Meson
configuration:

View File

@@ -17,26 +17,11 @@ Read [keyboard](keyboard.md) and [mouse](mouse.md).
## Control only
To control the device without mirroring:
To control only with UHID mouse and keyboard:
```bash
scrcpy --no-video --no-audio
```
By default, mouse mode is switched to UHID if video mirroring is disabled (a
relative mouse mode is required).
To also use a UHID keyboard, set it explicitly:
```bash
scrcpy --no-video --no-audio --keyboard=uhid
scrcpy --no-video --no-audio -K # short version
```
To use AOA instead (over USB only):
```bash
scrcpy --no-video --no-audio --keyboard=aoa --mouse=aoa
scrcpy --no-video --no-audio --keyboard=uhid --mouse=uhid
scrcpy --no-video --no-audio -KM # short version
```
@@ -106,6 +91,15 @@ only inverts _x_.
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
### Install APK

View File

@@ -6,7 +6,7 @@
Scrcpy is packaged in several distributions and package managers:
- Debian/Ubuntu: ~~`apt install scrcpy`~~ _(obsolete version)_
- Debian/Ubuntu: `apt install scrcpy`
- Arch Linux: `pacman -S scrcpy`
- Fedora: `dnf copr enable zeno/scrcpy && dnf install scrcpy`
- Gentoo: `emerge scrcpy`

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
[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
@@ -76,69 +68,3 @@ debugging disabled (see [OTG](otg.md)).
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
process like the _adb daemon_).
## Mouse bindings
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
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).
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:
```
.---- Shift + right click
SECONDARY |.--- Shift + middle click
BINDINGS ||.-- Shift + 4th click
|||.- Shift + 5th click
||||
vvvv
--mouse-bind=xxxx:xxxx
^^^^
||||
PRIMARY ||| `- 5th click
BINDINGS || `-- 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 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

@@ -39,13 +39,13 @@ It only works if the device is connected over USB.
See [FAQ](/FAQ.md#otg-issues-on-windows).
## Control only
## Control-only
Note that the purpose of OTG is to control the device without USB debugging
(adb).
If you want to solely control the device without mirroring while USB debugging
is enabled, then OTG mode is not necessary.
If you want to only control the device without mirroring while USB debugging is
enabled, then OTG mode is not necessary.
Instead, disable video and audio, and select UHID (or AOA):

View File

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

View File

@@ -4,18 +4,24 @@
Download the [latest release]:
- [`scrcpy-win64-v2.5.zip`][direct-win64] (64-bit)
<sub>SHA-256: `345cf04a66a9144281dce72ca4e82adfd2c3092463196e586051df4c69e1507b`</sub>
- [`scrcpy-win32-v2.5.zip`][direct-win32] (32-bit)
<sub>SHA-256: `d56312a92471565fa4f3a6b94e8eb07717c4c90f2c0f05b03ba444e1001806ec`</sub>
- [`scrcpy-win64-v2.4.zip`][direct-win64] (64-bit)
<sub>SHA-256: `9dc56f21bfa455352ec0c58b40feaf2fb02d67372910a4235e298ece286ff3a9`</sub>
- [`scrcpy-win32-v2.4.zip`][direct-win32] (32-bit)
<sub>SHA-256: `cf92acc45eef37c6ee2db819f92e420ced3bc50f1348dd57f7d6ca1fc80f6116`</sub>
[latest release]: https://github.com/Genymobile/scrcpy/releases/latest
[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v2.5/scrcpy-win64-v2.5.zip
[direct-win32]: https://github.com/Genymobile/scrcpy/releases/download/v2.5/scrcpy-win32-v2.5.zip
[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v2.4/scrcpy-win64-v2.4.zip
[direct-win32]: https://github.com/Genymobile/scrcpy/releases/download/v2.4/scrcpy-win32-v2.4.zip
and extract it.
Alternatively, you could install it from packages manager, like [Chocolatey]:
Alternatively, you could install it from packages manager, like [Winget]:
```bash
winget install scrcpy
```
or [Chocolatey]:
```bash
choco install scrcpy

View File

@@ -2,8 +2,8 @@
set -e
BUILDDIR=build-auto
PREBUILT_SERVER_URL=https://github.com/Genymobile/scrcpy/releases/download/v2.5/scrcpy-server-v2.5
PREBUILT_SERVER_SHA256=1488b1105d6aff534873a26bf610cd2aea06ee867dd7a4d9c6bb2c091396eb15
PREBUILT_SERVER_URL=https://github.com/Genymobile/scrcpy/releases/download/v2.4/scrcpy-server-v2.4
PREBUILT_SERVER_SHA256=93c272b7438605c055e127f7444064ed78fa9ca49f81156777fd201e79ce7ba3
echo "[scrcpy] Downloading prebuilt server..."
wget "$PREBUILT_SERVER_URL" -O scrcpy-server

View File

@@ -1,5 +1,5 @@
project('scrcpy', 'c',
version: '2.5',
version: '2.4',
meson_version: '>= 0.48',
default_options: [
'c_std=c11',

View File

@@ -7,8 +7,8 @@ android {
applicationId "com.genymobile.scrcpy"
minSdkVersion 21
targetSdkVersion 34
versionCode 20500
versionName "2.5"
versionCode 20400
versionName "2.4"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {

View File

@@ -12,7 +12,7 @@
set -e
SCRCPY_DEBUG=false
SCRCPY_VERSION_NAME=2.5
SCRCPY_VERSION_NAME=2.4
PLATFORM=${ANDROID_PLATFORM:-34}
BUILD_TOOLS=${ANDROID_BUILD_TOOLS:-34.0.0}

View File

@@ -22,6 +22,7 @@ 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();
@@ -272,8 +273,8 @@ public class Controller implements AsyncProcessor {
pointer.setPressure(pressure);
int source;
if (pointerId == POINTER_ID_MOUSE) {
// real mouse event
if (pointerId == POINTER_ID_MOUSE || pointerId == POINTER_ID_VIRTUAL_MOUSE) {
// real mouse event (forced by the client when --forward-on-click)
pointerProperties[pointerIndex].toolType = MotionEvent.TOOL_TYPE_MOUSE;
source = InputDevice.SOURCE_MOUSE;
pointer.setUp(buttons == 0);

View File

@@ -45,18 +45,18 @@ public class ScreenCapture extends SurfaceCapture implements Device.RotationList
}
try {
display = createDisplay();
setDisplaySurface(display, surface, videoRotation, contentRect, unlockedVideoRect, layerStack);
Ln.d("Display: using SurfaceControl API");
} catch (Exception surfaceControlException) {
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 {
display = createDisplay();
setDisplaySurface(display, surface, videoRotation, contentRect, unlockedVideoRect, layerStack);
Ln.d("Display: using SurfaceControl API");
} catch (Exception surfaceControlException) {
Ln.e("Could not create display using DisplayManager", displayManagerException);
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 DisplayManager", displayManagerException);
throw new AssertionError("Could not create display");
}
}
@@ -68,11 +68,6 @@ public class ScreenCapture extends SurfaceCapture implements Device.RotationList
device.setFoldListener(null);
if (display != null) {
SurfaceControl.destroyDisplay(display);
display = null;
}
if (virtualDisplay != null) {
virtualDisplay.release();
virtualDisplay = null;
}
}

View File

@@ -48,7 +48,7 @@ public class SurfaceEncoder implements AsyncProcessor {
this.downsizeOnError = downsizeOnError;
}
private void streamCapture() throws IOException, ConfigurationException {
private void streamScreen() throws IOException, ConfigurationException {
Codec codec = streamer.getCodec();
MediaCodec mediaCodec = createMediaCodec(codec, encoderName);
MediaFormat format = createFormat(codec.getMimeType(), videoBitRate, maxFps, codecOptions);
@@ -254,7 +254,7 @@ public class SurfaceEncoder implements AsyncProcessor {
Looper.prepare();
try {
streamCapture();
streamScreen();
} catch (ConfigurationException e) {
// Do not print stack trace, a user-friendly error-message has already been logged
} catch (IOException e) {

View File

@@ -63,11 +63,9 @@ 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") || Build.MANUFACTURER.equalsIgnoreCase("skyworth")) {
} else if (Build.BRAND.equalsIgnoreCase("honor")) {
// 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>