Compare commits
44 Commits
adb_device
...
otg-window
Author | SHA1 | Date | |
---|---|---|---|
d9bc5082ab | |||
73a5311ac6 | |||
25296ae167 | |||
3bb24b3926 | |||
6ee75c0cff | |||
6b65cd405a | |||
ff3cb31cb4 | |||
06243e7c3c | |||
b9b2879789 | |||
be1936bb85 | |||
3ee3f8dc02 | |||
9db42341e4 | |||
82a99f69ec | |||
33202491e1 | |||
b4fd882ece | |||
c4ab65eb79 | |||
6edf50d447 | |||
4b018be789 | |||
fa93c8a91b | |||
03705b828b | |||
85edba20e7 | |||
2a872c3865 | |||
ccbe370cc5 | |||
bb991f829c | |||
ca9e1a0514 | |||
cc27771dd1 | |||
d0ab8c0e7b | |||
5c62f3419d | |||
044acc2259 | |||
6fc388f369 | |||
1c02b58412 | |||
67068e4e3d | |||
e3c2398aa2 | |||
29c163959c | |||
4a95c08d56 | |||
7848a387c8 | |||
43ae418752 | |||
8d583d36e2 | |||
8498a2e8a6 | |||
c00a31f1b0 | |||
f86df817f9 | |||
9a546ef1af | |||
9477594f80 | |||
29828aa330 |
12
BUILD.md
12
BUILD.md
@ -161,7 +161,8 @@ install the required packages:
|
||||
```bash
|
||||
# runtime dependencies
|
||||
pacman -S mingw-w64-x86_64-SDL2 \
|
||||
mingw-w64-x86_64-ffmpeg
|
||||
mingw-w64-x86_64-ffmpeg \
|
||||
mingw-w64-x86_64-libusb
|
||||
|
||||
# client build dependencies
|
||||
pacman -S mingw-w64-x86_64-make \
|
||||
@ -175,7 +176,8 @@ For a 32 bits version, replace `x86_64` by `i686`:
|
||||
```bash
|
||||
# runtime dependencies
|
||||
pacman -S mingw-w64-i686-SDL2 \
|
||||
mingw-w64-i686-ffmpeg
|
||||
mingw-w64-i686-ffmpeg \
|
||||
mingw-w64-i686-libusb
|
||||
|
||||
# client build dependencies
|
||||
pacman -S mingw-w64-i686-make \
|
||||
@ -199,7 +201,7 @@ Install the packages with [Homebrew]:
|
||||
|
||||
```bash
|
||||
# runtime dependencies
|
||||
brew install sdl2 ffmpeg
|
||||
brew install sdl2 ffmpeg libusb
|
||||
|
||||
# client build dependencies
|
||||
brew install pkg-config meson
|
||||
@ -258,7 +260,7 @@ set ANDROID_SDK_ROOT=%LOCALAPPDATA%\Android\sdk
|
||||
Then, build:
|
||||
|
||||
```bash
|
||||
meson x --buildtype release --strip -Db_lto=true
|
||||
meson x --buildtype=release --strip -Db_lto=true
|
||||
ninja -Cx # DO NOT RUN AS ROOT
|
||||
```
|
||||
|
||||
@ -279,7 +281,7 @@ Download the prebuilt server somewhere, and specify its path during the Meson
|
||||
configuration:
|
||||
|
||||
```bash
|
||||
meson x --buildtype release --strip -Db_lto=true \
|
||||
meson x --buildtype=release --strip -Db_lto=true \
|
||||
-Dprebuilt_server=/path/to/scrcpy-server
|
||||
ninja -Cx # DO NOT RUN AS ROOT
|
||||
```
|
||||
|
@ -215,6 +215,15 @@ scrcpy --max-fps 15
|
||||
|
||||
This is officially supported since Android 10, but may work on earlier versions.
|
||||
|
||||
The actual capture framerate may be printed to the console:
|
||||
|
||||
```
|
||||
scrcpy --print-fps
|
||||
```
|
||||
|
||||
It may also be enabled or disabled at any time with <kbd>MOD</kbd>+<kbd>i</kbd>.
|
||||
|
||||
|
||||
#### Crop
|
||||
|
||||
The device screen may be cropped to mirror only part of the screen.
|
||||
|
@ -26,6 +26,7 @@ src = [
|
||||
'src/scrcpy.c',
|
||||
'src/screen.c',
|
||||
'src/server.c',
|
||||
'src/version.c',
|
||||
'src/video_buffer.c',
|
||||
'src/util/acksync.c',
|
||||
'src/util/file.c',
|
||||
@ -68,12 +69,12 @@ else
|
||||
endif
|
||||
endif
|
||||
|
||||
v4l2_support = host_machine.system() == 'linux'
|
||||
v4l2_support = get_option('v4l2') and host_machine.system() == 'linux'
|
||||
if v4l2_support
|
||||
src += [ 'src/v4l2_sink.c' ]
|
||||
endif
|
||||
|
||||
usb_support = host_machine.system() == 'linux'
|
||||
usb_support = get_option('usb')
|
||||
if usb_support
|
||||
src += [
|
||||
'src/usb/aoa_hid.c',
|
||||
@ -110,9 +111,9 @@ if not crossbuild_windows
|
||||
else
|
||||
# cross-compile mingw32 build (from Linux to Windows)
|
||||
prebuilt_sdl2 = meson.get_cross_property('prebuilt_sdl2')
|
||||
sdl2_bin_dir = meson.current_source_dir() + '/../prebuilt-deps/data/' + prebuilt_sdl2 + '/bin'
|
||||
sdl2_lib_dir = meson.current_source_dir() + '/../prebuilt-deps/data/' + prebuilt_sdl2 + '/lib'
|
||||
sdl2_include_dir = '../prebuilt-deps/data/' + prebuilt_sdl2 + '/include'
|
||||
sdl2_bin_dir = meson.current_source_dir() + '/prebuilt-deps/data/' + prebuilt_sdl2 + '/bin'
|
||||
sdl2_lib_dir = meson.current_source_dir() + '/prebuilt-deps/data/' + prebuilt_sdl2 + '/lib'
|
||||
sdl2_include_dir = 'prebuilt-deps/data/' + prebuilt_sdl2 + '/include'
|
||||
|
||||
sdl2 = declare_dependency(
|
||||
dependencies: [
|
||||
@ -123,8 +124,8 @@ else
|
||||
)
|
||||
|
||||
prebuilt_ffmpeg = meson.get_cross_property('prebuilt_ffmpeg')
|
||||
ffmpeg_bin_dir = meson.current_source_dir() + '/../prebuilt-deps/data/' + prebuilt_ffmpeg + '/bin'
|
||||
ffmpeg_include_dir = '../prebuilt-deps/data/' + prebuilt_ffmpeg + '/include'
|
||||
ffmpeg_bin_dir = meson.current_source_dir() + '/prebuilt-deps/data/' + prebuilt_ffmpeg + '/bin'
|
||||
ffmpeg_include_dir = 'prebuilt-deps/data/' + prebuilt_ffmpeg + '/include'
|
||||
|
||||
# ffmpeg versions are different for win32 and win64 builds
|
||||
ffmpeg_avcodec = meson.get_cross_property('ffmpeg_avcodec')
|
||||
@ -140,9 +141,22 @@ else
|
||||
include_directories: include_directories(ffmpeg_include_dir)
|
||||
)
|
||||
|
||||
prebuilt_libusb = meson.get_cross_property('prebuilt_libusb')
|
||||
prebuilt_libusb_root = meson.get_cross_property('prebuilt_libusb_root')
|
||||
libusb_bin_dir = meson.current_source_dir() + '/prebuilt-deps/data/' + prebuilt_libusb + '/dll'
|
||||
libusb_include_dir = 'prebuilt-deps/data/' + prebuilt_libusb_root + '/include'
|
||||
|
||||
libusb = declare_dependency(
|
||||
dependencies: [
|
||||
cc.find_library('libusb-1.0', dirs: libusb_bin_dir),
|
||||
],
|
||||
include_directories: include_directories(libusb_include_dir)
|
||||
)
|
||||
|
||||
dependencies = [
|
||||
ffmpeg,
|
||||
sdl2,
|
||||
libusb,
|
||||
cc.find_library('mingw32')
|
||||
]
|
||||
|
||||
|
28
app/prebuilt-deps/prepare-libusb.sh
Executable file
28
app/prebuilt-deps/prepare-libusb.sh
Executable file
@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
DIR=$(dirname ${BASH_SOURCE[0]})
|
||||
cd "$DIR"
|
||||
. common
|
||||
mkdir -p "$PREBUILT_DATA_DIR"
|
||||
cd "$PREBUILT_DATA_DIR"
|
||||
|
||||
DEP_DIR=libusb-1.0.25
|
||||
|
||||
FILENAME=libusb-1.0.25.7z
|
||||
SHA256SUM=3d1c98416f454026034b2b5d67f8a294053898cb70a8b489874e75b136c6674d
|
||||
|
||||
if [[ -d "$DEP_DIR" ]]
|
||||
then
|
||||
echo "$DEP_DIR" found
|
||||
exit 0
|
||||
fi
|
||||
|
||||
get_file "https://github.com/libusb/libusb/releases/download/v1.0.25/$FILENAME" "$FILENAME" "$SHA256SUM"
|
||||
|
||||
mkdir "$DEP_DIR"
|
||||
cd "$DEP_DIR"
|
||||
|
||||
7z x "../$FILENAME" \
|
||||
MinGW32/dll/libusb-1.0.dll \
|
||||
MinGW64/dll/libusb-1.0.dll \
|
||||
include /
|
16
app/scrcpy.1
16
app/scrcpy.1
@ -100,7 +100,7 @@ Simulate a physical keyboard by using HID over AOAv2.
|
||||
|
||||
This provides a better experience for IME users, and allows to generate non-ASCII characters, contrary to the default injection method.
|
||||
|
||||
It may only work over USB, and is currently only supported on Linux.
|
||||
It may only work over USB.
|
||||
|
||||
The keyboard layout must be configured (once and for all) on the device, via Settings -> System -> Languages and input -> Physical keyboard. This settings page can be started directly:
|
||||
|
||||
@ -142,10 +142,16 @@ In this mode, the computer mouse is captured to control the device directly (rel
|
||||
|
||||
LAlt, LSuper or RSuper toggle the capture mode, to give control of the mouse back to the computer.
|
||||
|
||||
It may only work over USB, and is currently only supported on Linux.
|
||||
It may only work over USB.
|
||||
|
||||
Also see \fB\-\-hid\-keyboard\fR.
|
||||
|
||||
.TP
|
||||
.B \-\-no\-cleanup
|
||||
By default, scrcpy removes the server binary from the device and restores the device state (show touches, stay awake and power mode) on exit.
|
||||
|
||||
This option disables this cleanup.
|
||||
|
||||
.TP
|
||||
.B \-\-no\-clipboard\-autosync
|
||||
By default, scrcpy automatically synchronizes the computer clipboard to the device clipboard before injecting Ctrl+v, and the device clipboard to the computer clipboard whenever it changes.
|
||||
@ -184,7 +190,7 @@ LAlt, LSuper or RSuper toggle the mouse capture mode, to give control of the mou
|
||||
|
||||
If any of \fB\-\-hid\-keyboard\fR or \fB\-\-hid\-mouse\fR is set, only enable keyboard or mouse respectively, otherwise enable both.
|
||||
|
||||
It may only work over USB, and is currently only supported on Linux.
|
||||
It may only work over USB.
|
||||
|
||||
See \fB\-\-hid\-keyboard\fR and \fB\-\-hid\-mouse\fR.
|
||||
|
||||
@ -205,6 +211,10 @@ Inject alpha characters and space as text events instead of key events.
|
||||
This avoids issues when combining multiple keys to enter special characters,
|
||||
but breaks the expected behavior of alpha keys in games (typically WASD).
|
||||
|
||||
.TP
|
||||
.B "\-\-print\-fps
|
||||
Start FPS counter, to print framerate logs to the console. It can be started or stopped at any time with MOD+i.
|
||||
|
||||
.TP
|
||||
.BI "\-\-push\-target " path
|
||||
Set the target directory for pushing files to the device by drag & drop. It is passed as\-is to "adb push".
|
||||
|
@ -150,7 +150,7 @@ process_check_success_internal(sc_pid pid, const char *name, bool close,
|
||||
static bool
|
||||
process_check_success_intr(struct sc_intr *intr, sc_pid pid, const char *name,
|
||||
unsigned flags) {
|
||||
if (!sc_intr_set_process(intr, pid)) {
|
||||
if (intr && !sc_intr_set_process(intr, pid)) {
|
||||
// Already interrupted
|
||||
return false;
|
||||
}
|
||||
@ -158,7 +158,9 @@ process_check_success_intr(struct sc_intr *intr, sc_pid pid, const char *name,
|
||||
// Always pass close=false, interrupting would be racy otherwise
|
||||
bool ret = process_check_success_internal(pid, name, false, flags);
|
||||
|
||||
sc_intr_set_process(intr, SC_PROCESS_NONE);
|
||||
if (intr) {
|
||||
sc_intr_set_process(intr, SC_PROCESS_NONE);
|
||||
}
|
||||
|
||||
// Close separately
|
||||
sc_process_close(pid);
|
||||
@ -202,6 +204,14 @@ sc_adb_start_server(struct sc_intr *intr, unsigned flags) {
|
||||
return process_check_success_intr(intr, pid, "adb start-server", flags);
|
||||
}
|
||||
|
||||
bool
|
||||
sc_adb_kill_server(struct sc_intr *intr, unsigned flags) {
|
||||
const char *const argv[] = SC_ADB_COMMAND("kill-server");
|
||||
|
||||
sc_pid pid = sc_adb_execute(argv, flags);
|
||||
return process_check_success_intr(intr, pid, "adb kill-server", flags);
|
||||
}
|
||||
|
||||
bool
|
||||
sc_adb_forward(struct sc_intr *intr, const char *serial, uint16_t local_port,
|
||||
const char *device_socket_name, unsigned flags) {
|
||||
@ -412,7 +422,7 @@ sc_adb_list_devices(struct sc_intr *intr, unsigned flags,
|
||||
// The implementation assumes that the output of "adb devices -l" fits
|
||||
// in the buffer in a single pass
|
||||
LOGW("Result of \"adb devices -l\" does not fit in 4Kb. "
|
||||
"Please report an issue.\n");
|
||||
"Please report an issue.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -676,7 +686,7 @@ sc_adb_get_device_ip(struct sc_intr *intr, const char *serial, unsigned flags) {
|
||||
// The implementation assumes that the output of "ip route" fits in the
|
||||
// buffer in a single pass
|
||||
LOGW("Result of \"ip route\" does not fit in 1Kb. "
|
||||
"Please report an issue.\n");
|
||||
"Please report an issue.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,9 @@ sc_adb_execute(const char *const argv[], unsigned flags);
|
||||
bool
|
||||
sc_adb_start_server(struct sc_intr *intr, unsigned flags);
|
||||
|
||||
bool
|
||||
sc_adb_kill_server(struct sc_intr *intr, unsigned flags);
|
||||
|
||||
bool
|
||||
sc_adb_forward(struct sc_intr *intr, const char *serial, uint16_t local_port,
|
||||
const char *device_socket_name, unsigned flags);
|
||||
|
@ -54,6 +54,8 @@
|
||||
#define OPT_RAW_KEY_EVENTS 1034
|
||||
#define OPT_NO_DOWNSIZE_ON_ERROR 1035
|
||||
#define OPT_OTG 1036
|
||||
#define OPT_NO_CLEANUP 1037
|
||||
#define OPT_PRINT_FPS 1038
|
||||
|
||||
struct sc_option {
|
||||
char shortopt;
|
||||
@ -116,7 +118,7 @@ static const struct sc_option options[] = {
|
||||
.text = "Crop the device screen on the server.\n"
|
||||
"The values are expressed in the device natural orientation "
|
||||
"(typically, portrait for a phone, landscape for a tablet). "
|
||||
"Any --max-size value is cmoputed on the cropped size.",
|
||||
"Any --max-size value is computed on the cropped size.",
|
||||
},
|
||||
{
|
||||
.shortopt = 'd',
|
||||
@ -184,8 +186,7 @@ static const struct sc_option options[] = {
|
||||
"It provides a better experience for IME users, and allows to "
|
||||
"generate non-ASCII characters, contrary to the default "
|
||||
"injection method.\n"
|
||||
"It may only work over USB, and is currently only supported "
|
||||
"on Linux.\n"
|
||||
"It may only work over USB.\n"
|
||||
"The keyboard layout must be configured (once and for all) on "
|
||||
"the device, via Settings -> System -> Languages and input -> "
|
||||
"Physical keyboard. This settings page can be started "
|
||||
@ -237,8 +238,7 @@ static const struct sc_option options[] = {
|
||||
"device directly (relative mouse mode).\n"
|
||||
"LAlt, LSuper or RSuper toggle the capture mode, to give "
|
||||
"control of the mouse back to the computer.\n"
|
||||
"It may only work over USB, and is currently only supported "
|
||||
"on Linux.\n"
|
||||
"It may only work over USB.\n"
|
||||
"Also see --hid-keyboard.",
|
||||
},
|
||||
{
|
||||
@ -251,11 +251,12 @@ static const struct sc_option options[] = {
|
||||
"Default is 0 (unlimited).",
|
||||
},
|
||||
{
|
||||
.longopt_id = OPT_NO_DOWNSIZE_ON_ERROR,
|
||||
.longopt = "no-downsize-on-error",
|
||||
.text = "By default, on MediaCodec error, scrcpy automatically tries "
|
||||
"again with a lower definition.\n"
|
||||
"This option disables this behavior.",
|
||||
.longopt_id = OPT_NO_CLEANUP,
|
||||
.longopt = "no-cleanup",
|
||||
.text = "By default, scrcpy removes the server binary from the device "
|
||||
"and restores the device state (show touches, stay awake and "
|
||||
"power mode) on exit.\n"
|
||||
"This option disables this cleanup."
|
||||
},
|
||||
{
|
||||
.longopt_id = OPT_NO_CLIPBOARD_AUTOSYNC,
|
||||
@ -266,6 +267,13 @@ static const struct sc_option options[] = {
|
||||
"it changes.\n"
|
||||
"This option disables this automatic synchronization."
|
||||
},
|
||||
{
|
||||
.longopt_id = OPT_NO_DOWNSIZE_ON_ERROR,
|
||||
.longopt = "no-downsize-on-error",
|
||||
.text = "By default, on MediaCodec error, scrcpy automatically tries "
|
||||
"again with a lower definition.\n"
|
||||
"This option disables this behavior.",
|
||||
},
|
||||
{
|
||||
.shortopt = 'n',
|
||||
.longopt = "no-control",
|
||||
@ -301,8 +309,7 @@ static const struct sc_option options[] = {
|
||||
"control of the mouse back to the computer.\n"
|
||||
"If any of --hid-keyboard or --hid-mouse is set, only enable "
|
||||
"keyboard or mouse respectively, otherwise enable both.\n"
|
||||
"It may only work over USB, and is currently only supported "
|
||||
"on Linux.\n"
|
||||
"It may only work over USB.\n"
|
||||
"See --hid-keyboard and --hid-mouse.",
|
||||
},
|
||||
{
|
||||
@ -327,6 +334,12 @@ static const struct sc_option options[] = {
|
||||
"special character, but breaks the expected behavior of alpha "
|
||||
"keys in games (typically WASD).",
|
||||
},
|
||||
{
|
||||
.longopt_id = OPT_PRINT_FPS,
|
||||
.longopt = "print-fps",
|
||||
.text = "Start FPS counter, to print framerate logs to the console. "
|
||||
"It can be started or stopped at any time with MOD+i.",
|
||||
},
|
||||
{
|
||||
.longopt_id = OPT_PUSH_TARGET,
|
||||
.longopt = "push-target",
|
||||
@ -1357,8 +1370,7 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
||||
opts->keyboard_input_mode = SC_KEYBOARD_INPUT_MODE_HID;
|
||||
break;
|
||||
#else
|
||||
LOGE("HID over AOA (-K/--hid-keyboard) is not supported on "
|
||||
"this platform. It is only available on Linux.");
|
||||
LOGE("HID over AOA (-K/--hid-keyboard) is disabled.");
|
||||
return false;
|
||||
#endif
|
||||
case OPT_MAX_FPS:
|
||||
@ -1376,8 +1388,7 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
||||
opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_HID;
|
||||
break;
|
||||
#else
|
||||
LOGE("HID over AOA (-M/--hid-mouse) is not supported on this"
|
||||
"platform. It is only available on Linux.");
|
||||
LOGE("HID over AOA (-M/--hid-mouse) is disabled.");
|
||||
return false;
|
||||
#endif
|
||||
case OPT_LOCK_VIDEO_ORIENTATION:
|
||||
@ -1535,13 +1546,18 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
||||
case OPT_NO_DOWNSIZE_ON_ERROR:
|
||||
opts->downsize_on_error = false;
|
||||
break;
|
||||
case OPT_NO_CLEANUP:
|
||||
opts->cleanup = false;
|
||||
break;
|
||||
case OPT_PRINT_FPS:
|
||||
opts->start_fps_counter = true;
|
||||
break;
|
||||
case OPT_OTG:
|
||||
#ifdef HAVE_USB
|
||||
opts->otg = true;
|
||||
break;
|
||||
#else
|
||||
LOGE("OTG mode (--otg) is not supported on this platform. It "
|
||||
"is only available on Linux.");
|
||||
LOGE("OTG mode (--otg) is disabled.");
|
||||
return false;
|
||||
#endif
|
||||
case OPT_V4L2_SINK:
|
||||
@ -1549,7 +1565,8 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
||||
opts->v4l2_device = optarg;
|
||||
break;
|
||||
#else
|
||||
LOGE("V4L2 (--v4l2-sink) is only available on Linux.");
|
||||
LOGE("V4L2 (--v4l2-sink) is disabled (or unsupported on this "
|
||||
"platform).");
|
||||
return false;
|
||||
#endif
|
||||
case OPT_V4L2_BUFFER:
|
||||
@ -1663,6 +1680,18 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
||||
}
|
||||
|
||||
#ifdef HAVE_USB
|
||||
|
||||
# ifdef _WIN32
|
||||
if (!opts->otg && (opts->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_HID
|
||||
|| opts->mouse_input_mode == SC_MOUSE_INPUT_MODE_HID)) {
|
||||
LOGE("On Windows, it is not possible to open a USB device already open "
|
||||
"by another process (like adb).");
|
||||
LOGE("Therefore, -K/--hid-keyboard and -M/--hid-mouse may only work in "
|
||||
"OTG mode (--otg).");
|
||||
return false;
|
||||
}
|
||||
# endif
|
||||
|
||||
if (opts->otg) {
|
||||
// OTG mode is compatible with only very few options.
|
||||
// Only report obvious errors.
|
||||
@ -1690,12 +1719,12 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
||||
LOGE("OTG mode: could not select display");
|
||||
return false;
|
||||
}
|
||||
#ifdef HAVE_V4L2
|
||||
# ifdef HAVE_V4L2
|
||||
if (opts->v4l2_device) {
|
||||
LOGE("OTG mode: could not sink to V4L2 device");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
# endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -63,17 +63,17 @@ static const char *const copy_key_labels[] = {
|
||||
|
||||
static void
|
||||
write_position(uint8_t *buf, const struct sc_position *position) {
|
||||
buffer_write32be(&buf[0], position->point.x);
|
||||
buffer_write32be(&buf[4], position->point.y);
|
||||
buffer_write16be(&buf[8], position->screen_size.width);
|
||||
buffer_write16be(&buf[10], position->screen_size.height);
|
||||
sc_write32be(&buf[0], position->point.x);
|
||||
sc_write32be(&buf[4], position->point.y);
|
||||
sc_write16be(&buf[8], position->screen_size.width);
|
||||
sc_write16be(&buf[10], position->screen_size.height);
|
||||
}
|
||||
|
||||
// write length (4 bytes) + string (non null-terminated)
|
||||
static size_t
|
||||
write_string(const char *utf8, size_t max_len, unsigned char *buf) {
|
||||
size_t len = sc_str_utf8_truncation_index(utf8, max_len);
|
||||
buffer_write32be(buf, len);
|
||||
sc_write32be(buf, len);
|
||||
memcpy(&buf[4], utf8, len);
|
||||
return 4 + len;
|
||||
}
|
||||
@ -94,9 +94,9 @@ sc_control_msg_serialize(const struct sc_control_msg *msg, unsigned char *buf) {
|
||||
switch (msg->type) {
|
||||
case SC_CONTROL_MSG_TYPE_INJECT_KEYCODE:
|
||||
buf[1] = msg->inject_keycode.action;
|
||||
buffer_write32be(&buf[2], msg->inject_keycode.keycode);
|
||||
buffer_write32be(&buf[6], msg->inject_keycode.repeat);
|
||||
buffer_write32be(&buf[10], msg->inject_keycode.metastate);
|
||||
sc_write32be(&buf[2], msg->inject_keycode.keycode);
|
||||
sc_write32be(&buf[6], msg->inject_keycode.repeat);
|
||||
sc_write32be(&buf[10], msg->inject_keycode.metastate);
|
||||
return 14;
|
||||
case SC_CONTROL_MSG_TYPE_INJECT_TEXT: {
|
||||
size_t len =
|
||||
@ -106,20 +106,20 @@ sc_control_msg_serialize(const struct sc_control_msg *msg, unsigned char *buf) {
|
||||
}
|
||||
case SC_CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT:
|
||||
buf[1] = msg->inject_touch_event.action;
|
||||
buffer_write64be(&buf[2], msg->inject_touch_event.pointer_id);
|
||||
sc_write64be(&buf[2], msg->inject_touch_event.pointer_id);
|
||||
write_position(&buf[10], &msg->inject_touch_event.position);
|
||||
uint16_t pressure =
|
||||
to_fixed_point_16(msg->inject_touch_event.pressure);
|
||||
buffer_write16be(&buf[22], pressure);
|
||||
buffer_write32be(&buf[24], msg->inject_touch_event.buttons);
|
||||
sc_write16be(&buf[22], pressure);
|
||||
sc_write32be(&buf[24], msg->inject_touch_event.buttons);
|
||||
return 28;
|
||||
case SC_CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT:
|
||||
write_position(&buf[1], &msg->inject_scroll_event.position);
|
||||
buffer_write32be(&buf[13],
|
||||
sc_write32be(&buf[13],
|
||||
(uint32_t) msg->inject_scroll_event.hscroll);
|
||||
buffer_write32be(&buf[17],
|
||||
sc_write32be(&buf[17],
|
||||
(uint32_t) msg->inject_scroll_event.vscroll);
|
||||
buffer_write32be(&buf[21], msg->inject_scroll_event.buttons);
|
||||
sc_write32be(&buf[21], msg->inject_scroll_event.buttons);
|
||||
return 25;
|
||||
case SC_CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON:
|
||||
buf[1] = msg->inject_keycode.action;
|
||||
@ -128,7 +128,7 @@ sc_control_msg_serialize(const struct sc_control_msg *msg, unsigned char *buf) {
|
||||
buf[1] = msg->get_clipboard.copy_key;
|
||||
return 2;
|
||||
case SC_CONTROL_MSG_TYPE_SET_CLIPBOARD:
|
||||
buffer_write64be(&buf[1], msg->set_clipboard.sequence);
|
||||
sc_write64be(&buf[1], msg->set_clipboard.sequence);
|
||||
buf[9] = !!msg->set_clipboard.paste;
|
||||
size_t len = write_string(msg->set_clipboard.text,
|
||||
SC_CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH,
|
||||
|
@ -10,10 +10,12 @@
|
||||
#include "util/buffer_util.h"
|
||||
#include "util/log.h"
|
||||
|
||||
#define BUFSIZE 0x10000
|
||||
#define SC_PACKET_HEADER_SIZE 12
|
||||
|
||||
#define HEADER_SIZE 12
|
||||
#define NO_PTS UINT64_C(-1)
|
||||
#define SC_PACKET_FLAG_CONFIG (UINT64_C(1) << 63)
|
||||
#define SC_PACKET_FLAG_KEY_FRAME (UINT64_C(1) << 62)
|
||||
|
||||
#define SC_PACKET_PTS_MASK (SC_PACKET_FLAG_KEY_FRAME - 1)
|
||||
|
||||
static bool
|
||||
sc_demuxer_recv_packet(struct sc_demuxer *demuxer, AVPacket *packet) {
|
||||
@ -28,16 +30,24 @@ sc_demuxer_recv_packet(struct sc_demuxer *demuxer, AVPacket *packet) {
|
||||
// size
|
||||
//
|
||||
// It is followed by <packet_size> bytes containing the packet/frame.
|
||||
//
|
||||
// The most significant bits of the PTS are used for packet flags:
|
||||
//
|
||||
// byte 7 byte 6 byte 5 byte 4 byte 3 byte 2 byte 1 byte 0
|
||||
// CK...... ........ ........ ........ ........ ........ ........ ........
|
||||
// ^^<------------------------------------------------------------------->
|
||||
// || PTS
|
||||
// | `- config packet
|
||||
// `-- key frame
|
||||
|
||||
uint8_t header[HEADER_SIZE];
|
||||
ssize_t r = net_recv_all(demuxer->socket, header, HEADER_SIZE);
|
||||
if (r < HEADER_SIZE) {
|
||||
uint8_t header[SC_PACKET_HEADER_SIZE];
|
||||
ssize_t r = net_recv_all(demuxer->socket, header, SC_PACKET_HEADER_SIZE);
|
||||
if (r < SC_PACKET_HEADER_SIZE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t pts = buffer_read64be(header);
|
||||
uint32_t len = buffer_read32be(&header[8]);
|
||||
assert(pts == NO_PTS || (pts & 0x8000000000000000) == 0);
|
||||
uint64_t pts_flags = sc_read64be(header);
|
||||
uint32_t len = sc_read32be(&header[8]);
|
||||
assert(len);
|
||||
|
||||
if (av_new_packet(packet, len)) {
|
||||
@ -51,8 +61,17 @@ sc_demuxer_recv_packet(struct sc_demuxer *demuxer, AVPacket *packet) {
|
||||
return false;
|
||||
}
|
||||
|
||||
packet->pts = pts != NO_PTS ? (int64_t) pts : AV_NOPTS_VALUE;
|
||||
if (pts_flags & SC_PACKET_FLAG_CONFIG) {
|
||||
packet->pts = AV_NOPTS_VALUE;
|
||||
} else {
|
||||
packet->pts = pts_flags & SC_PACKET_PTS_MASK;
|
||||
}
|
||||
|
||||
if (pts_flags & SC_PACKET_FLAG_KEY_FRAME) {
|
||||
packet->flags |= AV_PKT_FLAG_KEY;
|
||||
}
|
||||
|
||||
packet->dts = packet->pts;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -69,28 +88,6 @@ push_packet_to_sinks(struct sc_demuxer *demuxer, const AVPacket *packet) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
sc_demuxer_parse(struct sc_demuxer *demuxer, AVPacket *packet) {
|
||||
uint8_t *in_data = packet->data;
|
||||
int in_len = packet->size;
|
||||
uint8_t *out_data = NULL;
|
||||
int out_len = 0;
|
||||
int r = av_parser_parse2(demuxer->parser, demuxer->codec_ctx,
|
||||
&out_data, &out_len, in_data, in_len,
|
||||
AV_NOPTS_VALUE, AV_NOPTS_VALUE, -1);
|
||||
|
||||
// PARSER_FLAG_COMPLETE_FRAMES is set
|
||||
assert(r == in_len);
|
||||
(void) r;
|
||||
assert(out_len == in_len);
|
||||
|
||||
if (demuxer->parser->key_frame == 1) {
|
||||
packet->flags |= AV_PKT_FLAG_KEY;
|
||||
}
|
||||
|
||||
packet->dts = packet->pts;
|
||||
}
|
||||
|
||||
static bool
|
||||
sc_demuxer_push_packet(struct sc_demuxer *demuxer, AVPacket *packet) {
|
||||
bool is_config = packet->pts == AV_NOPTS_VALUE;
|
||||
@ -130,11 +127,6 @@ sc_demuxer_push_packet(struct sc_demuxer *demuxer, AVPacket *packet) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_config) {
|
||||
// data packet
|
||||
sc_demuxer_parse(demuxer, packet);
|
||||
}
|
||||
|
||||
bool ok = push_packet_to_sinks(demuxer, packet);
|
||||
|
||||
if (!is_config && demuxer->pending) {
|
||||
|
@ -18,7 +18,7 @@ device_msg_deserialize(const unsigned char *buf, size_t len,
|
||||
msg->type = buf[0];
|
||||
switch (msg->type) {
|
||||
case DEVICE_MSG_TYPE_CLIPBOARD: {
|
||||
size_t clipboard_len = buffer_read32be(&buf[1]);
|
||||
size_t clipboard_len = sc_read32be(&buf[1]);
|
||||
if (clipboard_len > len - 5) {
|
||||
return 0; // not available
|
||||
}
|
||||
@ -36,7 +36,7 @@ device_msg_deserialize(const unsigned char *buf, size_t len,
|
||||
return 5 + clipboard_len;
|
||||
}
|
||||
case DEVICE_MSG_TYPE_ACK_CLIPBOARD: {
|
||||
uint64_t sequence = buffer_read64be(&buf[1]);
|
||||
uint64_t sequence = sc_read64be(&buf[1]);
|
||||
msg->ack_clipboard.sequence = sequence;
|
||||
return 9;
|
||||
}
|
||||
|
@ -4,10 +4,10 @@
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
#define FPS_COUNTER_INTERVAL SC_TICK_FROM_SEC(1)
|
||||
#define SC_FPS_COUNTER_INTERVAL SC_TICK_FROM_SEC(1)
|
||||
|
||||
bool
|
||||
fps_counter_init(struct fps_counter *counter) {
|
||||
sc_fps_counter_init(struct sc_fps_counter *counter) {
|
||||
bool ok = sc_mutex_init(&counter->mutex);
|
||||
if (!ok) {
|
||||
return false;
|
||||
@ -27,26 +27,26 @@ fps_counter_init(struct fps_counter *counter) {
|
||||
}
|
||||
|
||||
void
|
||||
fps_counter_destroy(struct fps_counter *counter) {
|
||||
sc_fps_counter_destroy(struct sc_fps_counter *counter) {
|
||||
sc_cond_destroy(&counter->state_cond);
|
||||
sc_mutex_destroy(&counter->mutex);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
is_started(struct fps_counter *counter) {
|
||||
is_started(struct sc_fps_counter *counter) {
|
||||
return atomic_load_explicit(&counter->started, memory_order_acquire);
|
||||
}
|
||||
|
||||
static inline void
|
||||
set_started(struct fps_counter *counter, bool started) {
|
||||
set_started(struct sc_fps_counter *counter, bool started) {
|
||||
atomic_store_explicit(&counter->started, started, memory_order_release);
|
||||
}
|
||||
|
||||
// must be called with mutex locked
|
||||
static void
|
||||
display_fps(struct fps_counter *counter) {
|
||||
display_fps(struct sc_fps_counter *counter) {
|
||||
unsigned rendered_per_second =
|
||||
counter->nr_rendered * SC_TICK_FREQ / FPS_COUNTER_INTERVAL;
|
||||
counter->nr_rendered * SC_TICK_FREQ / SC_FPS_COUNTER_INTERVAL;
|
||||
if (counter->nr_skipped) {
|
||||
LOGI("%u fps (+%u frames skipped)", rendered_per_second,
|
||||
counter->nr_skipped);
|
||||
@ -57,7 +57,7 @@ display_fps(struct fps_counter *counter) {
|
||||
|
||||
// must be called with mutex locked
|
||||
static void
|
||||
check_interval_expired(struct fps_counter *counter, uint32_t now) {
|
||||
check_interval_expired(struct sc_fps_counter *counter, sc_tick now) {
|
||||
if (now < counter->next_timestamp) {
|
||||
return;
|
||||
}
|
||||
@ -67,13 +67,13 @@ check_interval_expired(struct fps_counter *counter, uint32_t now) {
|
||||
counter->nr_skipped = 0;
|
||||
// add a multiple of the interval
|
||||
uint32_t elapsed_slices =
|
||||
(now - counter->next_timestamp) / FPS_COUNTER_INTERVAL + 1;
|
||||
counter->next_timestamp += FPS_COUNTER_INTERVAL * elapsed_slices;
|
||||
(now - counter->next_timestamp) / SC_FPS_COUNTER_INTERVAL + 1;
|
||||
counter->next_timestamp += SC_FPS_COUNTER_INTERVAL * elapsed_slices;
|
||||
}
|
||||
|
||||
static int
|
||||
run_fps_counter(void *data) {
|
||||
struct fps_counter *counter = data;
|
||||
struct sc_fps_counter *counter = data;
|
||||
|
||||
sc_mutex_lock(&counter->mutex);
|
||||
while (!counter->interrupted) {
|
||||
@ -94,9 +94,9 @@ run_fps_counter(void *data) {
|
||||
}
|
||||
|
||||
bool
|
||||
fps_counter_start(struct fps_counter *counter) {
|
||||
sc_fps_counter_start(struct sc_fps_counter *counter) {
|
||||
sc_mutex_lock(&counter->mutex);
|
||||
counter->next_timestamp = sc_tick_now() + FPS_COUNTER_INTERVAL;
|
||||
counter->next_timestamp = sc_tick_now() + SC_FPS_COUNTER_INTERVAL;
|
||||
counter->nr_rendered = 0;
|
||||
counter->nr_skipped = 0;
|
||||
sc_mutex_unlock(&counter->mutex);
|
||||
@ -117,22 +117,24 @@ fps_counter_start(struct fps_counter *counter) {
|
||||
counter->thread_started = true;
|
||||
}
|
||||
|
||||
LOGI("FPS counter started");
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
fps_counter_stop(struct fps_counter *counter) {
|
||||
sc_fps_counter_stop(struct sc_fps_counter *counter) {
|
||||
set_started(counter, false);
|
||||
sc_cond_signal(&counter->state_cond);
|
||||
LOGI("FPS counter stopped");
|
||||
}
|
||||
|
||||
bool
|
||||
fps_counter_is_started(struct fps_counter *counter) {
|
||||
sc_fps_counter_is_started(struct sc_fps_counter *counter) {
|
||||
return is_started(counter);
|
||||
}
|
||||
|
||||
void
|
||||
fps_counter_interrupt(struct fps_counter *counter) {
|
||||
sc_fps_counter_interrupt(struct sc_fps_counter *counter) {
|
||||
if (!counter->thread_started) {
|
||||
return;
|
||||
}
|
||||
@ -145,7 +147,7 @@ fps_counter_interrupt(struct fps_counter *counter) {
|
||||
}
|
||||
|
||||
void
|
||||
fps_counter_join(struct fps_counter *counter) {
|
||||
sc_fps_counter_join(struct sc_fps_counter *counter) {
|
||||
if (counter->thread_started) {
|
||||
// interrupted must be set by the thread calling join(), so no need to
|
||||
// lock for the assertion
|
||||
@ -156,7 +158,7 @@ fps_counter_join(struct fps_counter *counter) {
|
||||
}
|
||||
|
||||
void
|
||||
fps_counter_add_rendered_frame(struct fps_counter *counter) {
|
||||
sc_fps_counter_add_rendered_frame(struct sc_fps_counter *counter) {
|
||||
if (!is_started(counter)) {
|
||||
return;
|
||||
}
|
||||
@ -169,7 +171,7 @@ fps_counter_add_rendered_frame(struct fps_counter *counter) {
|
||||
}
|
||||
|
||||
void
|
||||
fps_counter_add_skipped_frame(struct fps_counter *counter) {
|
||||
sc_fps_counter_add_skipped_frame(struct sc_fps_counter *counter) {
|
||||
if (!is_started(counter)) {
|
||||
return;
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
#include "util/thread.h"
|
||||
|
||||
struct fps_counter {
|
||||
struct sc_fps_counter {
|
||||
sc_thread thread;
|
||||
sc_mutex mutex;
|
||||
sc_cond state_cond;
|
||||
@ -28,32 +28,32 @@ struct fps_counter {
|
||||
};
|
||||
|
||||
bool
|
||||
fps_counter_init(struct fps_counter *counter);
|
||||
sc_fps_counter_init(struct sc_fps_counter *counter);
|
||||
|
||||
void
|
||||
fps_counter_destroy(struct fps_counter *counter);
|
||||
sc_fps_counter_destroy(struct sc_fps_counter *counter);
|
||||
|
||||
bool
|
||||
fps_counter_start(struct fps_counter *counter);
|
||||
sc_fps_counter_start(struct sc_fps_counter *counter);
|
||||
|
||||
void
|
||||
fps_counter_stop(struct fps_counter *counter);
|
||||
sc_fps_counter_stop(struct sc_fps_counter *counter);
|
||||
|
||||
bool
|
||||
fps_counter_is_started(struct fps_counter *counter);
|
||||
sc_fps_counter_is_started(struct sc_fps_counter *counter);
|
||||
|
||||
// request to stop the thread (on quit)
|
||||
// must be called before fps_counter_join()
|
||||
// must be called before sc_fps_counter_join()
|
||||
void
|
||||
fps_counter_interrupt(struct fps_counter *counter);
|
||||
sc_fps_counter_interrupt(struct sc_fps_counter *counter);
|
||||
|
||||
void
|
||||
fps_counter_join(struct fps_counter *counter);
|
||||
sc_fps_counter_join(struct sc_fps_counter *counter);
|
||||
|
||||
void
|
||||
fps_counter_add_rendered_frame(struct fps_counter *counter);
|
||||
sc_fps_counter_add_rendered_frame(struct sc_fps_counter *counter);
|
||||
|
||||
void
|
||||
fps_counter_add_skipped_frame(struct fps_counter *counter);
|
||||
sc_fps_counter_add_skipped_frame(struct sc_fps_counter *counter);
|
||||
|
||||
#endif
|
||||
|
@ -242,18 +242,14 @@ set_screen_power_mode(struct sc_controller *controller,
|
||||
}
|
||||
|
||||
static void
|
||||
switch_fps_counter_state(struct fps_counter *fps_counter) {
|
||||
switch_fps_counter_state(struct sc_fps_counter *fps_counter) {
|
||||
// the started state can only be written from the current thread, so there
|
||||
// is no ToCToU issue
|
||||
if (fps_counter_is_started(fps_counter)) {
|
||||
fps_counter_stop(fps_counter);
|
||||
LOGI("FPS counter stopped");
|
||||
if (sc_fps_counter_is_started(fps_counter)) {
|
||||
sc_fps_counter_stop(fps_counter);
|
||||
} else {
|
||||
if (fps_counter_start(fps_counter)) {
|
||||
LOGI("FPS counter started");
|
||||
} else {
|
||||
LOGE("FPS counter starting failed");
|
||||
}
|
||||
sc_fps_counter_start(fps_counter);
|
||||
// Any error is already logged
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,27 +15,7 @@
|
||||
#include "scrcpy.h"
|
||||
#include "usb/scrcpy_otg.h"
|
||||
#include "util/log.h"
|
||||
|
||||
static void
|
||||
print_version(void) {
|
||||
printf("\ndependencies:\n");
|
||||
printf(" - SDL %d.%d.%d\n", SDL_MAJOR_VERSION, SDL_MINOR_VERSION,
|
||||
SDL_PATCHLEVEL);
|
||||
printf(" - libavcodec %d.%d.%d\n", LIBAVCODEC_VERSION_MAJOR,
|
||||
LIBAVCODEC_VERSION_MINOR,
|
||||
LIBAVCODEC_VERSION_MICRO);
|
||||
printf(" - libavformat %d.%d.%d\n", LIBAVFORMAT_VERSION_MAJOR,
|
||||
LIBAVFORMAT_VERSION_MINOR,
|
||||
LIBAVFORMAT_VERSION_MICRO);
|
||||
printf(" - libavutil %d.%d.%d\n", LIBAVUTIL_VERSION_MAJOR,
|
||||
LIBAVUTIL_VERSION_MINOR,
|
||||
LIBAVUTIL_VERSION_MICRO);
|
||||
#ifdef HAVE_V4L2
|
||||
printf(" - libavdevice %d.%d.%d\n", LIBAVDEVICE_VERSION_MAJOR,
|
||||
LIBAVDEVICE_VERSION_MINOR,
|
||||
LIBAVDEVICE_VERSION_MICRO);
|
||||
#endif
|
||||
}
|
||||
#include "version.h"
|
||||
|
||||
int
|
||||
main(int argc, char *argv[]) {
|
||||
@ -71,7 +51,7 @@ main(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
if (args.version) {
|
||||
print_version();
|
||||
scrcpy_print_version();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -62,4 +62,6 @@ const struct scrcpy_options scrcpy_options_default = {
|
||||
.tcpip_dst = NULL,
|
||||
.select_tcpip = false,
|
||||
.select_usb = false,
|
||||
.cleanup = true,
|
||||
.start_fps_counter = false,
|
||||
};
|
||||
|
@ -137,6 +137,8 @@ struct scrcpy_options {
|
||||
const char *tcpip_dst;
|
||||
bool select_usb;
|
||||
bool select_tcpip;
|
||||
bool cleanup;
|
||||
bool start_fps_counter;
|
||||
};
|
||||
|
||||
extern const struct scrcpy_options scrcpy_options_default;
|
||||
|
@ -320,6 +320,7 @@ scrcpy(struct scrcpy_options *options) {
|
||||
.downsize_on_error = options->downsize_on_error,
|
||||
.tcpip = options->tcpip,
|
||||
.tcpip_dst = options->tcpip_dst,
|
||||
.cleanup = options->cleanup,
|
||||
};
|
||||
|
||||
static const struct sc_server_callbacks cbs = {
|
||||
@ -587,6 +588,7 @@ aoa_hid_end:
|
||||
.rotation = options->rotation,
|
||||
.mipmaps = options->mipmaps,
|
||||
.fullscreen = options->fullscreen,
|
||||
.start_fps_counter = options->start_fps_counter,
|
||||
.buffering_time = options->display_buffer,
|
||||
};
|
||||
|
||||
|
@ -163,14 +163,44 @@ sc_screen_is_relative_mode(struct sc_screen *screen) {
|
||||
}
|
||||
|
||||
static void
|
||||
sc_screen_capture_mouse(struct sc_screen *screen, bool capture) {
|
||||
sc_screen_set_mouse_capture(struct sc_screen *screen, bool capture) {
|
||||
#ifdef __APPLE__
|
||||
// Workaround for SDL bug on macOS:
|
||||
// <https://github.com/libsdl-org/SDL/issues/5340>
|
||||
if (capture) {
|
||||
int mouse_x, mouse_y;
|
||||
SDL_GetGlobalMouseState(&mouse_x, &mouse_y);
|
||||
|
||||
int x, y, w, h;
|
||||
SDL_GetWindowPosition(screen->window, &x, &y);
|
||||
SDL_GetWindowSize(screen->window, &w, &h);
|
||||
|
||||
bool outside_window = mouse_x < x || mouse_x >= x + w
|
||||
|| mouse_y < y || mouse_y >= y + h;
|
||||
if (outside_window) {
|
||||
SDL_WarpMouseInWindow(screen->window, w / 2, h / 2);
|
||||
}
|
||||
}
|
||||
#else
|
||||
(void) screen;
|
||||
#endif
|
||||
if (SDL_SetRelativeMouseMode(capture)) {
|
||||
LOGE("Could not set relative mouse mode to %s: %s",
|
||||
capture ? "true" : "false", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
screen->mouse_captured = capture;
|
||||
static inline bool
|
||||
sc_screen_get_mouse_capture(struct sc_screen *screen) {
|
||||
(void) screen;
|
||||
return SDL_GetRelativeMouseMode();
|
||||
}
|
||||
|
||||
static inline void
|
||||
sc_screen_toggle_mouse_capture(struct sc_screen *screen) {
|
||||
(void) screen;
|
||||
bool new_value = !sc_screen_get_mouse_capture(screen);
|
||||
sc_screen_set_mouse_capture(screen, new_value);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -340,7 +370,7 @@ sc_video_buffer_on_new_frame(struct sc_video_buffer *vb, bool previous_skipped,
|
||||
|
||||
bool need_new_event;
|
||||
if (previous_skipped) {
|
||||
fps_counter_add_skipped_frame(&screen->fps_counter);
|
||||
sc_fps_counter_add_skipped_frame(&screen->fps_counter);
|
||||
// The EVENT_NEW_FRAME triggered for the previous frame will consume
|
||||
// this new frame instead, unless the previous event failed
|
||||
need_new_event = screen->event_failed;
|
||||
@ -372,7 +402,6 @@ sc_screen_init(struct sc_screen *screen,
|
||||
screen->fullscreen = false;
|
||||
screen->maximized = false;
|
||||
screen->event_failed = false;
|
||||
screen->mouse_captured = false;
|
||||
screen->mouse_capture_key_pressed = 0;
|
||||
|
||||
screen->req.x = params->window_x;
|
||||
@ -380,6 +409,7 @@ sc_screen_init(struct sc_screen *screen,
|
||||
screen->req.width = params->window_width;
|
||||
screen->req.height = params->window_height;
|
||||
screen->req.fullscreen = params->fullscreen;
|
||||
screen->req.start_fps_counter = params->start_fps_counter;
|
||||
|
||||
static const struct sc_video_buffer_callbacks cbs = {
|
||||
.on_new_frame = sc_video_buffer_on_new_frame,
|
||||
@ -396,7 +426,7 @@ sc_screen_init(struct sc_screen *screen,
|
||||
goto error_destroy_video_buffer;
|
||||
}
|
||||
|
||||
if (!fps_counter_init(&screen->fps_counter)) {
|
||||
if (!sc_fps_counter_init(&screen->fps_counter)) {
|
||||
goto error_stop_and_join_video_buffer;
|
||||
}
|
||||
|
||||
@ -528,7 +558,7 @@ error_destroy_renderer:
|
||||
error_destroy_window:
|
||||
SDL_DestroyWindow(screen->window);
|
||||
error_destroy_fps_counter:
|
||||
fps_counter_destroy(&screen->fps_counter);
|
||||
sc_fps_counter_destroy(&screen->fps_counter);
|
||||
error_stop_and_join_video_buffer:
|
||||
sc_video_buffer_stop(&screen->vb);
|
||||
sc_video_buffer_join(&screen->vb);
|
||||
@ -556,6 +586,10 @@ sc_screen_show_initial_window(struct sc_screen *screen) {
|
||||
sc_screen_switch_fullscreen(screen);
|
||||
}
|
||||
|
||||
if (screen->req.start_fps_counter) {
|
||||
sc_fps_counter_start(&screen->fps_counter);
|
||||
}
|
||||
|
||||
SDL_ShowWindow(screen->window);
|
||||
}
|
||||
|
||||
@ -567,13 +601,13 @@ sc_screen_hide_window(struct sc_screen *screen) {
|
||||
void
|
||||
sc_screen_interrupt(struct sc_screen *screen) {
|
||||
sc_video_buffer_stop(&screen->vb);
|
||||
fps_counter_interrupt(&screen->fps_counter);
|
||||
sc_fps_counter_interrupt(&screen->fps_counter);
|
||||
}
|
||||
|
||||
void
|
||||
sc_screen_join(struct sc_screen *screen) {
|
||||
sc_video_buffer_join(&screen->vb);
|
||||
fps_counter_join(&screen->fps_counter);
|
||||
sc_fps_counter_join(&screen->fps_counter);
|
||||
}
|
||||
|
||||
void
|
||||
@ -585,7 +619,7 @@ sc_screen_destroy(struct sc_screen *screen) {
|
||||
SDL_DestroyTexture(screen->texture);
|
||||
SDL_DestroyRenderer(screen->renderer);
|
||||
SDL_DestroyWindow(screen->window);
|
||||
fps_counter_destroy(&screen->fps_counter);
|
||||
sc_fps_counter_destroy(&screen->fps_counter);
|
||||
sc_video_buffer_destroy(&screen->vb);
|
||||
}
|
||||
|
||||
@ -695,7 +729,7 @@ sc_screen_update_frame(struct sc_screen *screen) {
|
||||
sc_video_buffer_consume(&screen->vb, screen->frame);
|
||||
AVFrame *frame = screen->frame;
|
||||
|
||||
fps_counter_add_rendered_frame(&screen->fps_counter);
|
||||
sc_fps_counter_add_rendered_frame(&screen->fps_counter);
|
||||
|
||||
struct sc_size new_frame_size = {frame->width, frame->height};
|
||||
if (!prepare_for_frame(screen, new_frame_size)) {
|
||||
@ -710,7 +744,7 @@ sc_screen_update_frame(struct sc_screen *screen) {
|
||||
|
||||
if (sc_screen_is_relative_mode(screen)) {
|
||||
// Capture mouse on start
|
||||
sc_screen_capture_mouse(screen, true);
|
||||
sc_screen_set_mouse_capture(screen, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -823,7 +857,7 @@ sc_screen_handle_event(struct sc_screen *screen, SDL_Event *event) {
|
||||
break;
|
||||
case SDL_WINDOWEVENT_FOCUS_LOST:
|
||||
if (relative_mode) {
|
||||
sc_screen_capture_mouse(screen, false);
|
||||
sc_screen_set_mouse_capture(screen, false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -853,8 +887,7 @@ sc_screen_handle_event(struct sc_screen *screen, SDL_Event *event) {
|
||||
if (key == cap) {
|
||||
// A mouse capture key has been pressed then released:
|
||||
// toggle the capture mouse mode
|
||||
sc_screen_capture_mouse(screen,
|
||||
!screen->mouse_captured);
|
||||
sc_screen_toggle_mouse_capture(screen);
|
||||
}
|
||||
// Mouse capture keys are never forwarded to the device
|
||||
return;
|
||||
@ -864,7 +897,7 @@ sc_screen_handle_event(struct sc_screen *screen, SDL_Event *event) {
|
||||
case SDL_MOUSEWHEEL:
|
||||
case SDL_MOUSEMOTION:
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
if (relative_mode && !screen->mouse_captured) {
|
||||
if (relative_mode && !sc_screen_get_mouse_capture(screen)) {
|
||||
// Do not forward to input manager, the mouse will be captured
|
||||
// on SDL_MOUSEBUTTONUP
|
||||
return;
|
||||
@ -880,8 +913,8 @@ sc_screen_handle_event(struct sc_screen *screen, SDL_Event *event) {
|
||||
}
|
||||
break;
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
if (relative_mode && !screen->mouse_captured) {
|
||||
sc_screen_capture_mouse(screen, true);
|
||||
if (relative_mode && !sc_screen_get_mouse_capture(screen)) {
|
||||
sc_screen_set_mouse_capture(screen, true);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
@ -26,7 +26,7 @@ struct sc_screen {
|
||||
|
||||
struct sc_input_manager im;
|
||||
struct sc_video_buffer vb;
|
||||
struct fps_counter fps_counter;
|
||||
struct sc_fps_counter fps_counter;
|
||||
|
||||
// The initial requested window properties
|
||||
struct {
|
||||
@ -35,6 +35,7 @@ struct sc_screen {
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
bool fullscreen;
|
||||
bool start_fps_counter;
|
||||
} req;
|
||||
|
||||
SDL_Window *window;
|
||||
@ -60,7 +61,6 @@ struct sc_screen {
|
||||
|
||||
bool event_failed; // in case SDL_PushEvent() returned an error
|
||||
|
||||
bool mouse_captured; // only relevant in relative mouse mode
|
||||
// To enable/disable mouse capture, a mouse capture key (LALT, LGUI or
|
||||
// RGUI) must be pressed. This variable tracks the pressed capture key.
|
||||
SDL_Keycode mouse_capture_key_pressed;
|
||||
@ -94,6 +94,7 @@ struct sc_screen_params {
|
||||
bool mipmaps;
|
||||
|
||||
bool fullscreen;
|
||||
bool start_fps_counter;
|
||||
|
||||
sc_tick buffering_time;
|
||||
};
|
||||
|
@ -244,6 +244,10 @@ execute_server(struct sc_server *server,
|
||||
// By default, downsize_on_error is true
|
||||
ADD_PARAM("downsize_on_error=false");
|
||||
}
|
||||
if (!params->cleanup) {
|
||||
// By default, cleanup is true
|
||||
ADD_PARAM("cleanup=false");
|
||||
}
|
||||
|
||||
#undef ADD_PARAM
|
||||
|
||||
@ -482,8 +486,10 @@ fail:
|
||||
}
|
||||
}
|
||||
|
||||
// Always leave this function with tunnel disabled
|
||||
sc_adb_tunnel_close(tunnel, &server->intr, serial);
|
||||
if (tunnel->enabled) {
|
||||
// Always leave this function with tunnel disabled
|
||||
sc_adb_tunnel_close(tunnel, &server->intr, serial);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -46,6 +46,7 @@ struct sc_server_params {
|
||||
const char *tcpip_dst;
|
||||
bool select_usb;
|
||||
bool select_tcpip;
|
||||
bool cleanup;
|
||||
};
|
||||
|
||||
struct sc_server {
|
||||
|
@ -95,6 +95,7 @@ sc_aoa_register_hid(struct sc_aoa *aoa, uint16_t accessory_id,
|
||||
DEFAULT_TIMEOUT);
|
||||
if (result < 0) {
|
||||
LOGE("REGISTER_HID: libusb error: %s", libusb_strerror(result));
|
||||
sc_usb_check_disconnected(aoa->usb, result);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -131,6 +132,7 @@ sc_aoa_set_hid_report_desc(struct sc_aoa *aoa, uint16_t accessory_id,
|
||||
DEFAULT_TIMEOUT);
|
||||
if (result < 0) {
|
||||
LOGE("SET_HID_REPORT_DESC: libusb error: %s", libusb_strerror(result));
|
||||
sc_usb_check_disconnected(aoa->usb, result);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -173,6 +175,7 @@ sc_aoa_send_hid_event(struct sc_aoa *aoa, const struct sc_hid_event *event) {
|
||||
DEFAULT_TIMEOUT);
|
||||
if (result < 0) {
|
||||
LOGE("SEND_HID_EVENT: libusb error: %s", libusb_strerror(result));
|
||||
sc_usb_check_disconnected(aoa->usb, result);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -195,6 +198,7 @@ sc_aoa_unregister_hid(struct sc_aoa *aoa, const uint16_t accessory_id) {
|
||||
DEFAULT_TIMEOUT);
|
||||
if (result < 0) {
|
||||
LOGE("UNREGISTER_HID: libusb error: %s", libusb_strerror(result));
|
||||
sc_usb_check_disconnected(aoa->usb, result);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#include "adb/adb.h"
|
||||
#include "events.h"
|
||||
#include "screen_otg.h"
|
||||
#include "util/log.h"
|
||||
@ -75,6 +76,15 @@ scrcpy_otg(struct scrcpy_options *options) {
|
||||
bool aoa_started = false;
|
||||
bool aoa_initialized = false;
|
||||
|
||||
#ifdef _WIN32
|
||||
// On Windows, only one process could open a USB device
|
||||
// <https://github.com/Genymobile/scrcpy/issues/2773>
|
||||
LOGI("Killing adb daemon (if any)...");
|
||||
unsigned flags = SC_ADB_NO_STDOUT | SC_ADB_NO_STDERR | SC_ADB_NO_LOGERR;
|
||||
// uninterruptible (intr == NULL), but in practice it's very quick
|
||||
sc_adb_kill_server(NULL, flags);
|
||||
#endif
|
||||
|
||||
static const struct sc_usb_callbacks cbs = {
|
||||
.on_disconnected = sc_usb_on_disconnected,
|
||||
};
|
||||
@ -89,8 +99,10 @@ scrcpy_otg(struct scrcpy_options *options) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
LOGI("USB device: %s (%04" PRIx16 ":%04" PRIx16 ") %s %s",
|
||||
usb_device.serial, usb_device.vid, usb_device.pid,
|
||||
usb_device_initialized = true;
|
||||
|
||||
LOGI("USB device: %s (%04x:%04x) %s %s", usb_device.serial,
|
||||
(unsigned) usb_device.vid, (unsigned) usb_device.pid,
|
||||
usb_device.manufacturer, usb_device.product);
|
||||
|
||||
ok = sc_usb_connect(&s->usb, usb_device.device, &cbs, NULL);
|
||||
|
@ -5,15 +5,44 @@
|
||||
#include "util/log.h"
|
||||
|
||||
static void
|
||||
sc_screen_otg_capture_mouse(struct sc_screen_otg *screen, bool capture) {
|
||||
assert(screen->mouse);
|
||||
sc_screen_otg_set_mouse_capture(struct sc_screen_otg *screen, bool capture) {
|
||||
#ifdef __APPLE__
|
||||
// Workaround for SDL bug on macOS:
|
||||
// <https://github.com/libsdl-org/SDL/issues/5340>
|
||||
if (capture) {
|
||||
int mouse_x, mouse_y;
|
||||
SDL_GetGlobalMouseState(&mouse_x, &mouse_y);
|
||||
|
||||
int x, y, w, h;
|
||||
SDL_GetWindowPosition(screen->window, &x, &y);
|
||||
SDL_GetWindowSize(screen->window, &w, &h);
|
||||
|
||||
bool outside_window = mouse_x < x || mouse_x >= x + w
|
||||
|| mouse_y < y || mouse_y >= y + h;
|
||||
if (outside_window) {
|
||||
SDL_WarpMouseInWindow(screen->window, w / 2, h / 2);
|
||||
}
|
||||
}
|
||||
#else
|
||||
(void) screen;
|
||||
#endif
|
||||
if (SDL_SetRelativeMouseMode(capture)) {
|
||||
LOGE("Could not set relative mouse mode to %s: %s",
|
||||
capture ? "true" : "false", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
screen->mouse_captured = capture;
|
||||
static inline bool
|
||||
sc_screen_otg_get_mouse_capture(struct sc_screen_otg *screen) {
|
||||
(void) screen;
|
||||
return SDL_GetRelativeMouseMode();
|
||||
}
|
||||
|
||||
static inline void
|
||||
sc_screen_otg_toggle_mouse_capture(struct sc_screen_otg *screen) {
|
||||
(void) screen;
|
||||
bool new_value = !sc_screen_otg_get_mouse_capture(screen);
|
||||
sc_screen_otg_set_mouse_capture(screen, new_value);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -31,7 +60,6 @@ sc_screen_otg_init(struct sc_screen_otg *screen,
|
||||
screen->keyboard = params->keyboard;
|
||||
screen->mouse = params->mouse;
|
||||
|
||||
screen->mouse_captured = false;
|
||||
screen->mouse_capture_key_pressed = 0;
|
||||
|
||||
const char *title = params->window_title;
|
||||
@ -81,7 +109,7 @@ sc_screen_otg_init(struct sc_screen_otg *screen,
|
||||
|
||||
if (screen->mouse) {
|
||||
// Capture mouse on start
|
||||
sc_screen_otg_capture_mouse(screen, true);
|
||||
sc_screen_otg_set_mouse_capture(screen, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -193,7 +221,7 @@ sc_screen_otg_handle_event(struct sc_screen_otg *screen, SDL_Event *event) {
|
||||
break;
|
||||
case SDL_WINDOWEVENT_FOCUS_LOST:
|
||||
if (screen->mouse) {
|
||||
sc_screen_otg_capture_mouse(screen, false);
|
||||
sc_screen_otg_set_mouse_capture(screen, false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -227,8 +255,7 @@ sc_screen_otg_handle_event(struct sc_screen_otg *screen, SDL_Event *event) {
|
||||
if (key == cap) {
|
||||
// A mouse capture key has been pressed then released:
|
||||
// toggle the capture mouse mode
|
||||
sc_screen_otg_capture_mouse(screen,
|
||||
!screen->mouse_captured);
|
||||
sc_screen_otg_toggle_mouse_capture(screen);
|
||||
}
|
||||
// Mouse capture keys are never forwarded to the device
|
||||
return;
|
||||
@ -240,26 +267,26 @@ sc_screen_otg_handle_event(struct sc_screen_otg *screen, SDL_Event *event) {
|
||||
}
|
||||
break;
|
||||
case SDL_MOUSEMOTION:
|
||||
if (screen->mouse && screen->mouse_captured) {
|
||||
if (screen->mouse && sc_screen_otg_get_mouse_capture(screen)) {
|
||||
sc_screen_otg_process_mouse_motion(screen, &event->motion);
|
||||
}
|
||||
break;
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
if (screen->mouse && screen->mouse_captured) {
|
||||
if (screen->mouse && sc_screen_otg_get_mouse_capture(screen)) {
|
||||
sc_screen_otg_process_mouse_button(screen, &event->button);
|
||||
}
|
||||
break;
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
if (screen->mouse) {
|
||||
if (screen->mouse_captured) {
|
||||
if (sc_screen_otg_get_mouse_capture(screen)) {
|
||||
sc_screen_otg_process_mouse_button(screen, &event->button);
|
||||
} else {
|
||||
sc_screen_otg_capture_mouse(screen, true);
|
||||
sc_screen_otg_set_mouse_capture(screen, true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SDL_MOUSEWHEEL:
|
||||
if (screen->mouse && screen->mouse_captured) {
|
||||
if (screen->mouse && sc_screen_otg_get_mouse_capture(screen)) {
|
||||
sc_screen_otg_process_mouse_wheel(screen, &event->wheel);
|
||||
}
|
||||
break;
|
||||
|
@ -18,7 +18,6 @@ struct sc_screen_otg {
|
||||
SDL_Texture *texture;
|
||||
|
||||
// See equivalent mechanism in screen.h
|
||||
bool mouse_captured;
|
||||
SDL_Keycode mouse_capture_key_pressed;
|
||||
};
|
||||
|
||||
|
@ -38,6 +38,11 @@ sc_usb_read_device(libusb_device *device, struct sc_usb_device *out) {
|
||||
libusb_device_handle *handle;
|
||||
result = libusb_open(device, &handle);
|
||||
if (result < 0) {
|
||||
// Log at debug level because it is expected that some non-Android USB
|
||||
// devices present on the computer require special permissions
|
||||
LOGD("Open USB device %04x:%04x: libusb error: %s",
|
||||
(unsigned) desc.idVendor, (unsigned) desc.idProduct,
|
||||
libusb_strerror(result));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -142,8 +147,10 @@ sc_usb_devices_log(enum sc_log_level level, struct sc_usb_device *devices,
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
struct sc_usb_device *d = &devices[i];
|
||||
const char *selection = d->selected ? "-->" : " ";
|
||||
LOG(level, " %s %-18s (%04" PRIx16 ":%04" PRIx16 ") %s %s",
|
||||
selection, d->serial, d->vid, d->pid, d->manufacturer, d->product);
|
||||
// Convert uint16_t to unsigned because PRIx16 may not exist on Windows
|
||||
LOG(level, " %s %-18s (%04x:%04x) %s %s",
|
||||
selection, d->serial, (unsigned) d->vid, (unsigned) d->pid,
|
||||
d->manufacturer, d->product);
|
||||
}
|
||||
}
|
||||
|
||||
@ -212,7 +219,25 @@ sc_usb_destroy(struct sc_usb *usb) {
|
||||
libusb_exit(usb->context);
|
||||
}
|
||||
|
||||
static int
|
||||
static void
|
||||
sc_usb_report_disconnected(struct sc_usb *usb) {
|
||||
if (usb->cbs && !atomic_flag_test_and_set(&usb->disconnection_notified)) {
|
||||
assert(usb->cbs && usb->cbs->on_disconnected);
|
||||
usb->cbs->on_disconnected(usb, usb->cbs_userdata);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
sc_usb_check_disconnected(struct sc_usb *usb, int result) {
|
||||
if (result == LIBUSB_ERROR_NO_DEVICE || result == LIBUSB_ERROR_NOT_FOUND) {
|
||||
sc_usb_report_disconnected(usb);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static LIBUSB_CALL int
|
||||
sc_usb_libusb_callback(libusb_context *ctx, libusb_device *device,
|
||||
libusb_hotplug_event event, void *userdata) {
|
||||
(void) ctx;
|
||||
@ -228,8 +253,7 @@ sc_usb_libusb_callback(libusb_context *ctx, libusb_device *device,
|
||||
return 0;
|
||||
}
|
||||
|
||||
assert(usb->cbs && usb->cbs->on_disconnected);
|
||||
usb->cbs->on_disconnected(usb, usb->cbs_userdata);
|
||||
sc_usb_report_disconnected(usb);
|
||||
|
||||
// Do not automatically deregister the callback by returning 1. Instead,
|
||||
// manually deregister to interrupt libusb_handle_events() from the libusb
|
||||
@ -250,7 +274,8 @@ run_libusb_event_handler(void *data) {
|
||||
static bool
|
||||
sc_usb_register_callback(struct sc_usb *usb) {
|
||||
if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
|
||||
LOGW("libusb does not have hotplug capability");
|
||||
LOGW("On this platform, libusb does not have hotplug capability; "
|
||||
"device disconnection will not be detected properly");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -302,6 +327,7 @@ sc_usb_connect(struct sc_usb *usb, libusb_device *device,
|
||||
|
||||
if (cbs) {
|
||||
atomic_init(&usb->stopped, false);
|
||||
usb->disconnection_notified = (atomic_flag) ATOMIC_FLAG_INIT;
|
||||
if (sc_usb_register_callback(usb)) {
|
||||
// Create a thread to process libusb events, so that device
|
||||
// disconnection could be detected immediately
|
||||
@ -312,8 +338,6 @@ sc_usb_connect(struct sc_usb *usb, libusb_device *device,
|
||||
LOGW("Libusb event thread handler could not be created, USB "
|
||||
"device disconnection might not be detected immediately");
|
||||
}
|
||||
} else {
|
||||
LOGW("Could not register USB device disconnection callback");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,7 @@ struct sc_usb {
|
||||
sc_thread libusb_event_thread;
|
||||
|
||||
atomic_bool stopped; // only used if cbs != NULL
|
||||
atomic_flag disconnection_notified;
|
||||
};
|
||||
|
||||
struct sc_usb_callbacks {
|
||||
@ -73,6 +74,11 @@ sc_usb_connect(struct sc_usb *usb, libusb_device *device,
|
||||
void
|
||||
sc_usb_disconnect(struct sc_usb *usb);
|
||||
|
||||
// A client should call this function with the return value of a libusb call
|
||||
// to detect disconnection immediately
|
||||
bool
|
||||
sc_usb_check_disconnected(struct sc_usb *usb, int result);
|
||||
|
||||
void
|
||||
sc_usb_stop(struct sc_usb *usb);
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
#ifndef BUFFER_UTIL_H
|
||||
#define BUFFER_UTIL_H
|
||||
#ifndef SC_BUFFER_UTIL_H
|
||||
#define SC_BUFFER_UTIL_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
@ -7,13 +7,13 @@
|
||||
#include <stdint.h>
|
||||
|
||||
static inline void
|
||||
buffer_write16be(uint8_t *buf, uint16_t value) {
|
||||
sc_write16be(uint8_t *buf, uint16_t value) {
|
||||
buf[0] = value >> 8;
|
||||
buf[1] = value;
|
||||
}
|
||||
|
||||
static inline void
|
||||
buffer_write32be(uint8_t *buf, uint32_t value) {
|
||||
sc_write32be(uint8_t *buf, uint32_t value) {
|
||||
buf[0] = value >> 24;
|
||||
buf[1] = value >> 16;
|
||||
buf[2] = value >> 8;
|
||||
@ -21,25 +21,25 @@ buffer_write32be(uint8_t *buf, uint32_t value) {
|
||||
}
|
||||
|
||||
static inline void
|
||||
buffer_write64be(uint8_t *buf, uint64_t value) {
|
||||
buffer_write32be(buf, value >> 32);
|
||||
buffer_write32be(&buf[4], (uint32_t) value);
|
||||
sc_write64be(uint8_t *buf, uint64_t value) {
|
||||
sc_write32be(buf, value >> 32);
|
||||
sc_write32be(&buf[4], (uint32_t) value);
|
||||
}
|
||||
|
||||
static inline uint16_t
|
||||
buffer_read16be(const uint8_t *buf) {
|
||||
sc_read16be(const uint8_t *buf) {
|
||||
return (buf[0] << 8) | buf[1];
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
buffer_read32be(const uint8_t *buf) {
|
||||
sc_read32be(const uint8_t *buf) {
|
||||
return ((uint32_t) buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
|
||||
}
|
||||
|
||||
static inline uint64_t
|
||||
buffer_read64be(const uint8_t *buf) {
|
||||
uint32_t msb = buffer_read32be(buf);
|
||||
uint32_t lsb = buffer_read32be(&buf[4]);
|
||||
sc_read64be(const uint8_t *buf) {
|
||||
uint32_t msb = sc_read32be(buf);
|
||||
uint32_t lsb = sc_read32be(&buf[4]);
|
||||
return ((uint64_t) msb << 32) | lsb;
|
||||
}
|
||||
|
||||
|
@ -3,27 +3,33 @@
|
||||
ssize_t
|
||||
sc_pipe_read_intr(struct sc_intr *intr, sc_pid pid, sc_pipe pipe, char *data,
|
||||
size_t len) {
|
||||
if (!sc_intr_set_process(intr, pid)) {
|
||||
if (intr && !sc_intr_set_process(intr, pid)) {
|
||||
// Already interrupted
|
||||
return false;
|
||||
}
|
||||
|
||||
ssize_t ret = sc_pipe_read(pipe, data, len);
|
||||
|
||||
sc_intr_set_process(intr, SC_PROCESS_NONE);
|
||||
if (intr) {
|
||||
sc_intr_set_process(intr, SC_PROCESS_NONE);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t
|
||||
sc_pipe_read_all_intr(struct sc_intr *intr, sc_pid pid, sc_pipe pipe,
|
||||
char *data, size_t len) {
|
||||
if (!sc_intr_set_process(intr, pid)) {
|
||||
if (intr && !sc_intr_set_process(intr, pid)) {
|
||||
// Already interrupted
|
||||
return false;
|
||||
}
|
||||
|
||||
ssize_t ret = sc_pipe_read_all(pipe, data, len);
|
||||
|
||||
sc_intr_set_process(intr, SC_PROCESS_NONE);
|
||||
if (intr) {
|
||||
sc_intr_set_process(intr, SC_PROCESS_NONE);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -136,7 +136,9 @@ sc_cond_timedwait(sc_cond *cond, sc_mutex *mutex, sc_tick deadline) {
|
||||
return false; // timeout
|
||||
}
|
||||
|
||||
uint32_t ms = SC_TICK_TO_MS(deadline - now);
|
||||
// Round up to the next millisecond to guarantee that the deadline is
|
||||
// reached when returning due to timeout
|
||||
uint32_t ms = SC_TICK_TO_MS(deadline - now + SC_TICK_FROM_MS(1) - 1);
|
||||
int r = SDL_CondWaitTimeout(cond->cond, mutex->mutex, ms);
|
||||
#ifndef NDEBUG
|
||||
if (r < 0) {
|
||||
@ -148,6 +150,8 @@ sc_cond_timedwait(sc_cond *cond, sc_mutex *mutex, sc_tick deadline) {
|
||||
memory_order_relaxed);
|
||||
#endif
|
||||
assert(r == 0 || r == SDL_MUTEX_TIMEDOUT);
|
||||
// The deadline is reached on timeout
|
||||
assert(r != SDL_MUTEX_TIMEDOUT || sc_tick_now() >= deadline);
|
||||
return r == 0;
|
||||
}
|
||||
|
||||
|
67
app/src/version.c
Normal file
67
app/src/version.c
Normal file
@ -0,0 +1,67 @@
|
||||
#include "version.h"
|
||||
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavutil/avutil.h>
|
||||
#ifdef HAVE_V4L2
|
||||
# include <libavdevice/avdevice.h>
|
||||
#endif
|
||||
#ifdef HAVE_USB
|
||||
# include <libusb-1.0/libusb.h>
|
||||
#endif
|
||||
|
||||
void
|
||||
scrcpy_print_version(void) {
|
||||
printf("\nDependencies (compiled / linked):\n");
|
||||
|
||||
SDL_version sdl;
|
||||
SDL_GetVersion(&sdl);
|
||||
printf(" - SDL: %u.%u.%u / %u.%u.%u\n",
|
||||
SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL,
|
||||
(unsigned) sdl.major, (unsigned) sdl.minor, (unsigned) sdl.patch);
|
||||
|
||||
unsigned avcodec = avcodec_version();
|
||||
printf(" - libavcodec: %u.%u.%u / %u.%u.%u\n",
|
||||
LIBAVCODEC_VERSION_MAJOR,
|
||||
LIBAVCODEC_VERSION_MINOR,
|
||||
LIBAVCODEC_VERSION_MICRO,
|
||||
AV_VERSION_MAJOR(avcodec),
|
||||
AV_VERSION_MINOR(avcodec),
|
||||
AV_VERSION_MICRO(avcodec));
|
||||
|
||||
unsigned avformat = avformat_version();
|
||||
printf(" - libavformat: %u.%u.%u / %u.%u.%u\n",
|
||||
LIBAVFORMAT_VERSION_MAJOR,
|
||||
LIBAVFORMAT_VERSION_MINOR,
|
||||
LIBAVFORMAT_VERSION_MICRO,
|
||||
AV_VERSION_MAJOR(avformat),
|
||||
AV_VERSION_MINOR(avformat),
|
||||
AV_VERSION_MICRO(avformat));
|
||||
|
||||
unsigned avutil = avutil_version();
|
||||
printf(" - libavutil: %u.%u.%u / %u.%u.%u\n",
|
||||
LIBAVUTIL_VERSION_MAJOR,
|
||||
LIBAVUTIL_VERSION_MINOR,
|
||||
LIBAVUTIL_VERSION_MICRO,
|
||||
AV_VERSION_MAJOR(avutil),
|
||||
AV_VERSION_MINOR(avutil),
|
||||
AV_VERSION_MICRO(avutil));
|
||||
|
||||
#ifdef HAVE_V4L2
|
||||
unsigned avdevice = avdevice_version();
|
||||
printf(" - libavdevice: %u.%u.%u / %u.%u.%u\n",
|
||||
LIBAVDEVICE_VERSION_MAJOR,
|
||||
LIBAVDEVICE_VERSION_MINOR,
|
||||
LIBAVDEVICE_VERSION_MICRO,
|
||||
AV_VERSION_MAJOR(avdevice),
|
||||
AV_VERSION_MINOR(avdevice),
|
||||
AV_VERSION_MICRO(avdevice));
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_USB
|
||||
const struct libusb_version *usb = libusb_get_version();
|
||||
// The compiled version may not be known
|
||||
printf(" - libusb: - / %u.%u.%u\n",
|
||||
(unsigned) usb->major, (unsigned) usb->minor, (unsigned) usb->micro);
|
||||
#endif
|
||||
}
|
9
app/src/version.h
Normal file
9
app/src/version.h
Normal file
@ -0,0 +1,9 @@
|
||||
#ifndef SC_VERSION_H
|
||||
#define SC_VERSION_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
void
|
||||
scrcpy_print_version(void);
|
||||
|
||||
#endif
|
@ -91,7 +91,6 @@ static void test_adb_devices_daemon_start_mixed() {
|
||||
struct sc_adb_device *device = &devices[0];
|
||||
assert(!strcmp("0123456789abcdef", device->serial));
|
||||
assert(!strcmp("unauthorized", device->state));
|
||||
fprintf(stderr, "==== [%s]\n", device->model);
|
||||
assert(!device->model);
|
||||
|
||||
device = &devices[1];
|
||||
|
@ -8,7 +8,7 @@ static void test_buffer_write16be(void) {
|
||||
uint16_t val = 0xABCD;
|
||||
uint8_t buf[2];
|
||||
|
||||
buffer_write16be(buf, val);
|
||||
sc_write16be(buf, val);
|
||||
|
||||
assert(buf[0] == 0xAB);
|
||||
assert(buf[1] == 0xCD);
|
||||
@ -18,7 +18,7 @@ static void test_buffer_write32be(void) {
|
||||
uint32_t val = 0xABCD1234;
|
||||
uint8_t buf[4];
|
||||
|
||||
buffer_write32be(buf, val);
|
||||
sc_write32be(buf, val);
|
||||
|
||||
assert(buf[0] == 0xAB);
|
||||
assert(buf[1] == 0xCD);
|
||||
@ -30,7 +30,7 @@ static void test_buffer_write64be(void) {
|
||||
uint64_t val = 0xABCD1234567890EF;
|
||||
uint8_t buf[8];
|
||||
|
||||
buffer_write64be(buf, val);
|
||||
sc_write64be(buf, val);
|
||||
|
||||
assert(buf[0] == 0xAB);
|
||||
assert(buf[1] == 0xCD);
|
||||
@ -45,7 +45,7 @@ static void test_buffer_write64be(void) {
|
||||
static void test_buffer_read16be(void) {
|
||||
uint8_t buf[2] = {0xAB, 0xCD};
|
||||
|
||||
uint16_t val = buffer_read16be(buf);
|
||||
uint16_t val = sc_read16be(buf);
|
||||
|
||||
assert(val == 0xABCD);
|
||||
}
|
||||
@ -53,7 +53,7 @@ static void test_buffer_read16be(void) {
|
||||
static void test_buffer_read32be(void) {
|
||||
uint8_t buf[4] = {0xAB, 0xCD, 0x12, 0x34};
|
||||
|
||||
uint32_t val = buffer_read32be(buf);
|
||||
uint32_t val = sc_read32be(buf);
|
||||
|
||||
assert(val == 0xABCD1234);
|
||||
}
|
||||
@ -62,7 +62,7 @@ static void test_buffer_read64be(void) {
|
||||
uint8_t buf[8] = {0xAB, 0xCD, 0x12, 0x34,
|
||||
0x56, 0x78, 0x90, 0xEF};
|
||||
|
||||
uint64_t val = buffer_read64be(buf);
|
||||
uint64_t val = sc_read64be(buf);
|
||||
|
||||
assert(val == 0xABCD1234567890EF);
|
||||
}
|
||||
|
@ -21,3 +21,5 @@ ffmpeg_avformat = 'avformat-58'
|
||||
ffmpeg_avutil = 'avutil-56'
|
||||
prebuilt_ffmpeg = 'ffmpeg-win32-4.3.1'
|
||||
prebuilt_sdl2 = 'SDL2-2.0.20/i686-w64-mingw32'
|
||||
prebuilt_libusb_root = 'libusb-1.0.25'
|
||||
prebuilt_libusb = prebuilt_libusb_root + '/MinGW32'
|
||||
|
@ -21,3 +21,5 @@ ffmpeg_avformat = 'avformat-59'
|
||||
ffmpeg_avutil = 'avutil-57'
|
||||
prebuilt_ffmpeg = 'ffmpeg-win64-5.0'
|
||||
prebuilt_sdl2 = 'SDL2-2.0.20/x86_64-w64-mingw32'
|
||||
prebuilt_libusb_root = 'libusb-1.0.25'
|
||||
prebuilt_libusb = prebuilt_libusb_root + '/MinGW64'
|
||||
|
@ -12,7 +12,7 @@ echo "$PREBUILT_SERVER_SHA256 scrcpy-server" | sha256sum --check
|
||||
|
||||
echo "[scrcpy] Building client..."
|
||||
rm -rf "$BUILDDIR"
|
||||
meson "$BUILDDIR" --buildtype release --strip -Db_lto=true \
|
||||
meson "$BUILDDIR" --buildtype=release --strip -Db_lto=true \
|
||||
-Dprebuilt_server=scrcpy-server
|
||||
cd "$BUILDDIR"
|
||||
ninja
|
||||
|
@ -4,3 +4,5 @@ option('prebuilt_server', type: 'string', description: 'Path of the prebuilt ser
|
||||
option('portable', type: 'boolean', value: false, description: 'Use scrcpy-server from the same directory as the scrcpy executable')
|
||||
option('server_debugger', type: 'boolean', value: false, description: 'Run a server debugger and wait for a client to be attached')
|
||||
option('server_debugger_method', type: 'combo', choices: ['old', 'new'], value: 'new', description: 'Select the debugger method (Android < 9: "old", Android >= 9: "new")')
|
||||
option('v4l2', type: 'boolean', value: true, description: 'Enable V4L2 feature when supported')
|
||||
option('usb', type: 'boolean', value: true, description: 'Enable HID/OTG features when supported')
|
||||
|
58
release.mk
58
release.mk
@ -63,24 +63,28 @@ build-server:
|
||||
ninja -C "$(SERVER_BUILD_DIR)"
|
||||
|
||||
prepare-deps-win32:
|
||||
@prebuilt-deps/prepare-adb.sh
|
||||
@prebuilt-deps/prepare-sdl.sh
|
||||
@prebuilt-deps/prepare-ffmpeg-win32.sh
|
||||
@app/prebuilt-deps/prepare-adb.sh
|
||||
@app/prebuilt-deps/prepare-sdl.sh
|
||||
@app/prebuilt-deps/prepare-ffmpeg-win32.sh
|
||||
@app/prebuilt-deps/prepare-libusb.sh
|
||||
|
||||
prepare-deps-win64:
|
||||
@app/prebuilt-deps/prepare-adb.sh
|
||||
@app/prebuilt-deps/prepare-sdl.sh
|
||||
@app/prebuilt-deps/prepare-ffmpeg-win64.sh
|
||||
@app/prebuilt-deps/prepare-libusb.sh
|
||||
|
||||
build-win32: prepare-deps-win32
|
||||
# -Dusb=false because of libusb-win32 build issue, cf #3011
|
||||
[ -d "$(WIN32_BUILD_DIR)" ] || ( mkdir "$(WIN32_BUILD_DIR)" && \
|
||||
meson "$(WIN32_BUILD_DIR)" \
|
||||
--cross-file cross_win32.txt \
|
||||
--buildtype release --strip -Db_lto=true \
|
||||
-Dusb=false \
|
||||
-Dcompile_server=false \
|
||||
-Dportable=true )
|
||||
ninja -C "$(WIN32_BUILD_DIR)"
|
||||
|
||||
prepare-deps-win64:
|
||||
@prebuilt-deps/prepare-adb.sh
|
||||
@prebuilt-deps/prepare-sdl.sh
|
||||
@prebuilt-deps/prepare-ffmpeg-win64.sh
|
||||
|
||||
build-win64: prepare-deps-win64
|
||||
[ -d "$(WIN64_BUILD_DIR)" ] || ( mkdir "$(WIN64_BUILD_DIR)" && \
|
||||
meson "$(WIN64_BUILD_DIR)" \
|
||||
@ -98,15 +102,16 @@ dist-win32: build-server build-win32
|
||||
cp data/scrcpy-noconsole.vbs "$(DIST)/$(WIN32_TARGET_DIR)"
|
||||
cp data/icon.png "$(DIST)/$(WIN32_TARGET_DIR)"
|
||||
cp data/open_a_terminal_here.bat "$(DIST)/$(WIN32_TARGET_DIR)"
|
||||
cp prebuilt-deps/data/ffmpeg-win32-4.3.1/bin/avutil-56.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp prebuilt-deps/data/ffmpeg-win32-4.3.1/bin/avcodec-58.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp prebuilt-deps/data/ffmpeg-win32-4.3.1/bin/avformat-58.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp prebuilt-deps/data/ffmpeg-win32-4.3.1/bin/swresample-3.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp prebuilt-deps/data/ffmpeg-win32-4.3.1/bin/swscale-5.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp prebuilt-deps/data/platform-tools-31.0.3/adb.exe "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp prebuilt-deps/data/platform-tools-31.0.3/AdbWinApi.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp prebuilt-deps/data/platform-tools-31.0.3/AdbWinUsbApi.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp prebuilt-deps/data/SDL2-2.0.20/i686-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp app/prebuilt-deps/data/ffmpeg-win32-4.3.1/bin/avutil-56.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp app/prebuilt-deps/data/ffmpeg-win32-4.3.1/bin/avcodec-58.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp app/prebuilt-deps/data/ffmpeg-win32-4.3.1/bin/avformat-58.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp app/prebuilt-deps/data/ffmpeg-win32-4.3.1/bin/swresample-3.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp app/prebuilt-deps/data/ffmpeg-win32-4.3.1/bin/swscale-5.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp app/prebuilt-deps/data/platform-tools-31.0.3/adb.exe "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp app/prebuilt-deps/data/platform-tools-31.0.3/AdbWinApi.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp app/prebuilt-deps/data/platform-tools-31.0.3/AdbWinUsbApi.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp app/prebuilt-deps/data/SDL2-2.0.20/i686-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
#cp app/prebuilt-deps/data/libusb-1.0.25/MinGW32/dll/libusb-1.0.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
|
||||
dist-win64: build-server build-win64
|
||||
mkdir -p "$(DIST)/$(WIN64_TARGET_DIR)"
|
||||
@ -116,15 +121,16 @@ dist-win64: build-server build-win64
|
||||
cp data/scrcpy-noconsole.vbs "$(DIST)/$(WIN64_TARGET_DIR)"
|
||||
cp data/icon.png "$(DIST)/$(WIN64_TARGET_DIR)"
|
||||
cp data/open_a_terminal_here.bat "$(DIST)/$(WIN64_TARGET_DIR)"
|
||||
cp prebuilt-deps/data/ffmpeg-win64-5.0/bin/avutil-57.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp prebuilt-deps/data/ffmpeg-win64-5.0/bin/avcodec-59.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp prebuilt-deps/data/ffmpeg-win64-5.0/bin/avformat-59.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp prebuilt-deps/data/ffmpeg-win64-5.0/bin/swresample-4.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp prebuilt-deps/data/ffmpeg-win64-5.0/bin/swscale-6.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp prebuilt-deps/data/platform-tools-31.0.3/adb.exe "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp prebuilt-deps/data/platform-tools-31.0.3/AdbWinApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp prebuilt-deps/data/platform-tools-31.0.3/AdbWinUsbApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp prebuilt-deps/data/SDL2-2.0.20/x86_64-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp app/prebuilt-deps/data/ffmpeg-win64-5.0/bin/avutil-57.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp app/prebuilt-deps/data/ffmpeg-win64-5.0/bin/avcodec-59.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp app/prebuilt-deps/data/ffmpeg-win64-5.0/bin/avformat-59.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp app/prebuilt-deps/data/ffmpeg-win64-5.0/bin/swresample-4.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp app/prebuilt-deps/data/ffmpeg-win64-5.0/bin/swscale-6.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp app/prebuilt-deps/data/platform-tools-31.0.3/adb.exe "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp app/prebuilt-deps/data/platform-tools-31.0.3/AdbWinApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp app/prebuilt-deps/data/platform-tools-31.0.3/AdbWinUsbApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp app/prebuilt-deps/data/SDL2-2.0.20/x86_64-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp app/prebuilt-deps/data/libusb-1.0.25/MinGW64/dll/libusb-1.0.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
|
||||
zip-win32: dist-win32
|
||||
cd "$(DIST)/$(WIN32_TARGET_DIR)"; \
|
||||
|
@ -21,6 +21,7 @@ public class Options {
|
||||
private boolean powerOffScreenOnClose;
|
||||
private boolean clipboardAutosync = true;
|
||||
private boolean downsizeOnError = true;
|
||||
private boolean cleanup = true;
|
||||
|
||||
// Options not used by the scrcpy client, but useful to use scrcpy-server directly
|
||||
private boolean sendDeviceMeta = true; // send device name and size
|
||||
@ -155,6 +156,14 @@ public class Options {
|
||||
this.downsizeOnError = downsizeOnError;
|
||||
}
|
||||
|
||||
public boolean getCleanup() {
|
||||
return cleanup;
|
||||
}
|
||||
|
||||
public void setCleanup(boolean cleanup) {
|
||||
this.cleanup = cleanup;
|
||||
}
|
||||
|
||||
public boolean getSendDeviceMeta() {
|
||||
return sendDeviceMeta;
|
||||
}
|
||||
|
@ -28,7 +28,8 @@ public class ScreenEncoder implements Device.RotationListener {
|
||||
// Keep the values in descending order
|
||||
private static final int[] MAX_SIZE_FALLBACK = {2560, 1920, 1600, 1280, 1024, 800};
|
||||
|
||||
private static final int NO_PTS = -1;
|
||||
private static final long PACKET_FLAG_CONFIG = 1L << 63;
|
||||
private static final long PACKET_FLAG_KEY_FRAME = 1L << 62;
|
||||
|
||||
private final AtomicBoolean rotationChanged = new AtomicBoolean();
|
||||
private final ByteBuffer headerBuffer = ByteBuffer.allocate(12);
|
||||
@ -183,12 +184,15 @@ public class ScreenEncoder implements Device.RotationListener {
|
||||
|
||||
long pts;
|
||||
if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
|
||||
pts = NO_PTS; // non-media data packet
|
||||
pts = PACKET_FLAG_CONFIG; // non-media data packet
|
||||
} else {
|
||||
if (ptsOrigin == 0) {
|
||||
ptsOrigin = bufferInfo.presentationTimeUs;
|
||||
}
|
||||
pts = bufferInfo.presentationTimeUs - ptsOrigin;
|
||||
if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0) {
|
||||
pts |= PACKET_FLAG_KEY_FRAME;
|
||||
}
|
||||
}
|
||||
|
||||
headerBuffer.putLong(pts);
|
||||
|
@ -51,11 +51,13 @@ public final class Server {
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
CleanUp.configure(options.getDisplayId(), restoreStayOn, mustDisableShowTouchesOnCleanUp, restoreNormalPowerMode,
|
||||
options.getPowerOffScreenOnClose());
|
||||
} catch (IOException e) {
|
||||
Ln.e("Could not configure cleanup", e);
|
||||
if (options.getCleanup()) {
|
||||
try {
|
||||
CleanUp.configure(options.getDisplayId(), restoreStayOn, mustDisableShowTouchesOnCleanUp, restoreNormalPowerMode,
|
||||
options.getPowerOffScreenOnClose());
|
||||
} catch (IOException e) {
|
||||
Ln.e("Could not configure cleanup", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -243,6 +245,10 @@ public final class Server {
|
||||
boolean downsizeOnError = Boolean.parseBoolean(value);
|
||||
options.setDownsizeOnError(downsizeOnError);
|
||||
break;
|
||||
case "cleanup":
|
||||
boolean cleanup = Boolean.parseBoolean(value);
|
||||
options.setCleanup(cleanup);
|
||||
break;
|
||||
case "send_device_meta":
|
||||
boolean sendDeviceMeta = Boolean.parseBoolean(value);
|
||||
options.setSendDeviceMeta(sendDeviceMeta);
|
||||
|
Reference in New Issue
Block a user