Compare commits

...

49 Commits

Author SHA1 Message Date
e2e76c5d48 Increase adb devices -l max output size
For simplicity, the parsing of `adb devices -l` output is performed in a
single pass on the whole output.

This output was limited to 4096 bytes. Since there are about 100 chars
per device line, this limited the number of connected devices to ~40.

Increase to 65536 bytes to avoid a limitation in practice.

PR #3035 <https://github.com/Genymobile/scrcpy/pull/3035>
2022-02-21 00:03:45 +01:00
4b8cb042c4 Use vector for listing ADB devices
This avoids the hardcoded maximum number of ADB devices detected (16).

Refs #3029 <https://github.com/Genymobile/scrcpy/pull/3029>
PR #3035 <https://github.com/Genymobile/scrcpy/pull/3035>

Co-authored-by: Daniel Ansorregui <d.ansorregui@samsung.com>
2022-02-20 23:59:35 +01:00
1790e88278 Use vector for listing USB devices
This avoids the hardcoded maximum number of USB devices detected (16).

Refs #3029 <https://github.com/Genymobile/scrcpy/pull/3029>
PR #3035 <https://github.com/Genymobile/scrcpy/pull/3035>
2022-02-20 23:59:35 +01:00
c070723bc8 Add sc_vector
Adapt vlc_vector [1], that I initially wrote while implementing the VLC
playlist [2].

Change the implementation to use "statement expressions" [3], which are
forbidden in VLC because "non-standard", but:
 - they are supported by gcc and clang;
 - they are already used in the scrcpy codebase;
 - they avoid implementation hacks (VLC_VECTOR_FAILFLAG_);
 - they allow a better API (sc_vector_index_of() may return the result
   without an output parameter).

PR #3035 <https://github.com/Genymobile/scrcpy/pull/3035>

[1]: 0857947aba/include/vlc_vector.h
[2]: https://blog.rom1v.com/2019/05/a-new-core-playlist-for-vlc-4
[3]: https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html
2022-02-20 23:59:35 +01:00
36c75e15b8 Move data/ to app/
The files in data/ are specific to the client app (not the server).

This also avoids to reference the parent directory (../) from
app/meson.build.

Refs 8d583d36e2
2022-02-20 17:56:50 +01:00
d9bc5082ab Disable USB features for win32
Currently, there is an issue with the libusb prebuilt dll.

Refs libusb/#1049 <https://github.com/libusb/libusb/issues/1049>
PR #3011 <https://github.com/Genymobile/scrcpy/pull/3011>
2022-02-20 17:39:52 +01:00
73a5311ac6 Forbid HID input without OTG on Windows
On Windows, if the adb daemon is running, opening the USB device will
necessarily fail, so HID input is not possible.

Refs #2773 <https://github.com/Genymobile/scrcpy/issues/2773>
PR #3011 <https://github.com/Genymobile/scrcpy/pull/3011>
2022-02-20 17:39:42 +01:00
25296ae167 Kill adb daemon in OTG mode on Windows
On Windows, it is not possible to open a USB device from several
process, so HID events may only work if no adb daemon is running.

PR #3011 <https://github.com/Genymobile/scrcpy/pull/3011>
2022-02-20 17:39:39 +01:00
3bb24b3926 Make intr optional for adb commands
All adb commands are executed with an "interruptor", so that they can be
interrupted on Ctrl+C.

Make this interruptor optional, so that we could call "adb kill-server"
in OTG mode. This command always returns almost immediately anyway.

Ideally, we should make all blocking calls interruptible (including
libusb calls, by using the asynchronous API), but it's a lot of work,
and in practice it works well enough.

PR #3011 <https://github.com/Genymobile/scrcpy/pull/3011>
2022-02-20 17:39:36 +01:00
6ee75c0cff Remove obsolete text in error message
The HID/OTG features are now available on all platforms.

PR #3011 <https://github.com/Genymobile/scrcpy/pull/3011>
2022-02-20 17:39:30 +01:00
6b65cd405a Build for Windows with libusb support
Fixes #2773 <https://github.com/Genymobile/scrcpy/issues/2773>
PR #3011 <https://github.com/Genymobile/scrcpy/pull/3011>
2022-02-20 17:39:12 +01:00
ff3cb31cb4 Fix libusb callback for Windows
Add LIBUSB_CALL so that the callback has the correct signature on
Windows (including __attribute__((stdcall))).

PR #3011 <https://github.com/Genymobile/scrcpy/pull/3011>
2022-02-20 17:39:09 +01:00
06243e7c3c Avoid PRIx16 printf format on Windows
Convert uint16_t to unsigned to avoid using PRIx16, which may not exist
on Windows.

PR #3011 <https://github.com/Genymobile/scrcpy/pull/3011>
2022-02-20 17:39:04 +01:00
b9b2879789 Remove USB hotplug callback error log
If it fails, the error is already logged by sc_usb_register_callback().

PR #3011 <https://github.com/Genymobile/scrcpy/pull/3011>
2022-02-20 17:38:51 +01:00
be1936bb85 Report USB device disconnection when detected
USB device disconnection is detected via a hotplug callback when it is
supported.

In addition, report disconnection on libusb calls returning
LIBUSB_ERROR_NO_DEVICE or LIBUSB_ERROR_NOT_FOUND. This allows to detect
disconnection after a libusb call when hotplug is not available.

PR #3011 <https://github.com/Genymobile/scrcpy/pull/3011>
2022-02-20 17:38:14 +01:00
3ee3f8dc02 Work around mouse capture SDL bug on macOS
On macOS, SDL relative mouse mode does not work correctly when the
cursor is outside the window.

As a workaround, move the cursor inside the window before setting the
relative mouse mode.

Refs SDL/#5340 <https://github.com/libsdl-org/SDL/issues/5340>
PR #3031 <https://github.com/Genymobile/scrcpy/pull/3031>
2022-02-20 17:28:02 +01:00
9db42341e4 Pass screen instance to mouse capture functions
Using the screen instance or not in these functions is an implementation
detail. Further changes will require the screen instance.

Refs 7848a387c8
PR #3031 <https://github.com/Genymobile/scrcpy/pull/3031>
2022-02-20 17:27:53 +01:00
82a99f69ec Remove "linux-only" mentions for HID/OTG features
HID/OTG features are not limited to Linux anymore.

PR #3031 <https://github.com/Genymobile/scrcpy/pull/3031>
2022-02-20 17:27:43 +01:00
33202491e1 Build on macOS with libusb support
Fixes #2774 <https://github.com/Genymobile/scrcpy/issues/2774>
PR #3031 <https://github.com/Genymobile/scrcpy/pull/3031>
2022-02-20 17:27:22 +01:00
b4fd882ece Fix typo 2022-02-20 17:21:25 +01:00
c4ab65eb79 Remove useless '\n' in log 2022-02-18 21:18:36 +01:00
6edf50d447 Remove fprintf() in tests
It was committed by mistake.
2022-02-18 19:34:54 +01:00
4b018be789 Add --print-fps to enable FPS counter on start
The FPS counter could be enabled/disabled via MOD+i.

Add a command line option to enable it on start. This is consistent with
other features like --turn-screen-off or --fullscreen.

Fixes #468 <https://github.com/Genymobile/scrcpy/issues/468>
PR #3030 <https://github.com/Genymobile/scrcpy/pull/3030>
2022-02-18 18:16:50 +01:00
fa93c8a91b Move FPS counter start/stop logs
This will allow to print the same logs for every start/stop call.

PR #3030 <https://github.com/Genymobile/scrcpy/pull/3030>
2022-02-18 18:16:12 +01:00
03705b828b Use sc_prefix for fps counter 2022-02-17 19:55:24 +01:00
85edba20e7 Enforce deadline reached on timeout
The value of sc_tick_now() has microsecond precision, but
sc_cond_timedwait() has only millisecond precision.

To guarantee that sc_tick_now() >= deadline when sc_cond_timedwait()
returns due to timeout, round up to the next millisecond.

This avoids to call a non-blocking sc_cond_timedwait() in a loop for no
reason until a target deadline during up to 1 millisecond.

Refs 682a691173
2022-02-16 18:29:30 +01:00
2a872c3865 Fix fps_counter tick type
The type uint32_t is not sufficient to store the result of
sc_tick_now().

As a consequence, the FPS counter entered a live loop and caused a lock
starvation (deadlock in practice).

Refs ec871dd3f5
Refs 682a691173
2022-02-16 18:12:57 +01:00
ccbe370cc5 Add --no-cleanup option
It might be useful not to cleanup on exit, for example to leave the
screen turned off, or keep the server binary on the device (via the
server option "cleanup=false").

Fixes #1764 <https://github.com/Genymobile/scrcpy/issues/1764>
PR #3020 <https://github.com/Genymobile/scrcpy/pull/3020>
2022-02-15 19:25:57 +01:00
bb991f829c Fix order of options
In alphabetic order, "no-clipboard-autosync" is before
"no-downsize-on-error".
2022-02-13 17:26:34 +01:00
ca9e1a0514 Add compilation flag for USB features
This allows to disable HID/OTG features on Linux to build without
libusb.
2022-02-12 14:15:07 +01:00
cc27771dd1 Add compilation flag for V4L2 feature
This allows to disable V4L2 support on Linux to build without
libavdevice.
2022-02-12 14:15:07 +01:00
d0ab8c0e7b Fix double adb tunnel closing
On error, close the adb tunnel only if it has not already been closed
beforehand.
2022-02-12 14:15:02 +01:00
5c62f3419d Rename buffer util functions with sc_ prefix 2022-02-12 09:12:46 +01:00
044acc2259 Rename HEADER_SIZE to SC_PACKET_HEADER_SIZE
Prefix the constant for consistency.
2022-02-11 21:34:58 +01:00
6fc388f369 Remove unused BUFSIZE 2022-02-11 21:34:12 +01:00
1c02b58412 Remove sc_demuxer_parse()
Now that the key frame flag is known, parsing the packet is useless.
2022-02-11 21:33:09 +01:00
67068e4e3d Pass key frame flag from the device
MediaCodec indicates when a packet is a key frame. Transmit it to the
client.
2022-02-11 21:32:55 +01:00
e3c2398aa2 Store packet flags in PTS most significant bits
A special PTS value was used to encode a config packet.

To prepare for adding more flags, use the most significant bits of the
PTS field to store flags.
2022-02-11 21:32:11 +01:00
29c163959c Indent ifdef for clarity
Make it explicit that the ifdef is an inner block.
2022-02-10 09:24:19 +01:00
4a95c08d56 Improve error message for unsupported usb hotplug 2022-02-10 08:54:43 +01:00
7848a387c8 Do not duplicate relative mouse mode state
The relative mouse mode is tracked by SDL, and accessible via
SDL_GetRelativeMouseMode().

This is more robust in case SDL changes the relative mouse mode on its
own.
2022-02-10 08:50:18 +01:00
43ae418752 Fix USB device leak on connection error
If sc_usb_connect() failed, then the sc_usb_device was never destroyed.

The assignment was mistakenly removed by commit
61969aeb80.
2022-02-10 08:47:39 +01:00
8d583d36e2 Move prebuilt-deps/ to app/
The prebuilt dependencies are specific to the client app (not the
server).

This also avoids to reference the parent directory (../) from
app/meson.build.
2022-02-09 23:18:34 +01:00
8498a2e8a6 Reorder release.mk recipes
Group prepare-deps for win32 and win64.
2022-02-09 23:01:49 +01:00
c00a31f1b0 Pass --buildtype=release as a single meson arg
For consistency with the other arguments
2022-02-09 18:16:34 +01:00
f86df817f9 Print libusb version on --version 2022-02-09 10:15:19 +01:00
9a546ef1af Print both compiled and linked versions of libs
On --version, print both the version scrcpy had been compiled against,
and the version linked at runtime.
2022-02-09 10:15:19 +01:00
9477594f80 Move version handling to a separate file
This will avoid to include all dependencies headers from main.c.
2022-02-09 10:15:07 +01:00
29828aa330 Log device opening errors during listing
Without this log, the user would have no way to know that a USB device
is rejected because it could not be opened (typically due to
insufficient permissions).
2022-02-09 10:04:09 +01:00
62 changed files with 1686 additions and 385 deletions

View File

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

View File

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

View File

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -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')
]
@ -210,7 +224,7 @@ executable('scrcpy', src,
c_args: [])
install_man('scrcpy.1')
install_data('../data/icon.png',
install_data('data/icon.png',
rename: 'scrcpy.png',
install_dir: 'share/icons/hicolor/256x256/apps')
@ -268,6 +282,9 @@ if get_option('buildtype') == 'debug'
'src/util/str.c',
'src/util/strbuf.c',
]],
['test_vector', [
'tests/test_vector.c',
]],
]
foreach t : tests

View 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 /

View File

@ -1,6 +1,6 @@
#include <winuser.h>
0 ICON "../data/icon.ico"
0 ICON "data/icon.ico"
1 RT_MANIFEST "scrcpy-windows.manifest"
2 VERSIONINFO
BEGIN

View File

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

View File

@ -5,6 +5,7 @@
#include <stdlib.h>
#include <string.h>
#include "adb_device.h"
#include "adb_parser.h"
#include "util/file.h"
#include "util/log.h"
@ -150,7 +151,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 +159,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);
if (intr) {
sc_intr_set_process(intr, SC_PROCESS_NONE);
}
// Close separately
sc_process_close(pid);
@ -202,6 +205,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) {
@ -382,45 +393,55 @@ sc_adb_disconnect(struct sc_intr *intr, const char *ip_port, unsigned flags) {
return process_check_success_intr(intr, pid, "adb disconnect", flags);
}
static ssize_t
static bool
sc_adb_list_devices(struct sc_intr *intr, unsigned flags,
struct sc_adb_device *devices, size_t len) {
struct sc_vec_adb_devices *out_vec) {
const char *const argv[] = SC_ADB_COMMAND("devices", "-l");
#define BUFSIZE 65536
char *buf = malloc(BUFSIZE);
if (!buf) {
return false;
}
sc_pipe pout;
sc_pid pid = sc_adb_execute_p(argv, flags, &pout);
if (pid == SC_PROCESS_NONE) {
LOGE("Could not execute \"adb devices -l\"");
return -1;
free(buf);
return false;
}
char buf[4096];
ssize_t r = sc_pipe_read_all_intr(intr, pid, pout, buf, sizeof(buf) - 1);
ssize_t r = sc_pipe_read_all_intr(intr, pid, pout, buf, BUFSIZE - 1);
sc_pipe_close(pout);
bool ok = process_check_success_intr(intr, pid, "adb devices -l", flags);
if (!ok) {
return -1;
free(buf);
return false;
}
if (r == -1) {
return -1;
free(buf);
return false;
}
assert((size_t) r < sizeof(buf));
if (r == sizeof(buf) - 1) {
assert((size_t) r < BUFSIZE);
if (r == BUFSIZE - 1) {
// 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");
return -1;
LOGW("Result of \"adb devices -l\" does not fit in 64Kb. "
"Please report an issue.");
return false;
}
// It is parsed as a NUL-terminated string
buf[r] = '\0';
// List all devices to the output list directly
return sc_adb_parse_devices(buf, devices, len);
ok = sc_adb_parse_devices(buf, out_vec);
free(buf);
return ok;
}
static bool
@ -519,22 +540,21 @@ bool
sc_adb_select_device(struct sc_intr *intr,
const struct sc_adb_device_selector *selector,
unsigned flags, struct sc_adb_device *out_device) {
struct sc_adb_device devices[16];
ssize_t count =
sc_adb_list_devices(intr, flags, devices, ARRAY_LEN(devices));
if (count == -1) {
struct sc_vec_adb_devices vec = SC_VECTOR_INITIALIZER;
bool ok = sc_adb_list_devices(intr, flags, &vec);
if (!ok) {
LOGE("Could not list ADB devices");
return false;
}
if (count == 0) {
if (vec.size == 0) {
LOGE("Could not find any ADB device");
return false;
}
size_t sel_idx; // index of the single matching device if sel_count == 1
size_t sel_count =
sc_adb_devices_select(devices, count, selector, &sel_idx);
sc_adb_devices_select(vec.data, vec.size, selector, &sel_idx);
if (sel_count == 0) {
// if count > 0 && sel_count == 0, then necessarily a selection is
@ -557,8 +577,8 @@ sc_adb_select_device(struct sc_intr *intr,
break;
}
sc_adb_devices_log(SC_LOG_LEVEL_ERROR, devices, count);
sc_adb_devices_destroy_all(devices, count);
sc_adb_devices_log(SC_LOG_LEVEL_ERROR, vec.data, vec.size);
sc_adb_devices_destroy(&vec);
return false;
}
@ -584,28 +604,28 @@ sc_adb_select_device(struct sc_intr *intr,
assert(!"Unexpected selector type");
break;
}
sc_adb_devices_log(SC_LOG_LEVEL_ERROR, devices, count);
sc_adb_devices_log(SC_LOG_LEVEL_ERROR, vec.data, vec.size);
LOGE("Select a device via -s (--serial), -d (--select-usb) or -e "
"(--select-tcpip)");
sc_adb_devices_destroy_all(devices, count);
sc_adb_devices_destroy(&vec);
return false;
}
assert(sel_count == 1); // sel_idx is valid only if sel_count == 1
struct sc_adb_device *device = &devices[sel_idx];
struct sc_adb_device *device = &vec.data[sel_idx];
bool ok = sc_adb_device_check_state(device, devices, count);
ok = sc_adb_device_check_state(device, vec.data, vec.size);
if (!ok) {
sc_adb_devices_destroy_all(devices, count);
sc_adb_devices_destroy(&vec);
return false;
}
LOGD("ADB device found:");
sc_adb_devices_log(SC_LOG_LEVEL_DEBUG, devices, count);
sc_adb_devices_log(SC_LOG_LEVEL_DEBUG, vec.data, vec.size);
// Move devics into out_device (do not destroy device)
sc_adb_device_move(out_device, device);
sc_adb_devices_destroy_all(devices, count);
sc_adb_devices_destroy(&vec);
return true;
}
@ -676,7 +696,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;
}

View File

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

View File

@ -18,9 +18,10 @@ sc_adb_device_move(struct sc_adb_device *dst, struct sc_adb_device *src) {
}
void
sc_adb_devices_destroy_all(struct sc_adb_device *devices, size_t count) {
for (size_t i = 0; i < count; ++i) {
sc_adb_device_destroy(&devices[i]);
sc_adb_devices_destroy(struct sc_vec_adb_devices *devices) {
for (size_t i = 0; i < devices->size; ++i) {
sc_adb_device_destroy(&devices->data[i]);
}
sc_vector_destroy(devices);
}

View File

@ -6,6 +6,8 @@
#include <stdbool.h>
#include <stddef.h>
#include "util/vector.h"
struct sc_adb_device {
char *serial;
char *state;
@ -13,6 +15,8 @@ struct sc_adb_device {
bool selected;
};
struct sc_vec_adb_devices SC_VECTOR(struct sc_adb_device);
void
sc_adb_device_destroy(struct sc_adb_device *device);
@ -29,6 +33,6 @@ void
sc_adb_device_move(struct sc_adb_device *dst, struct sc_adb_device *src);
void
sc_adb_devices_destroy_all(struct sc_adb_device *devices, size_t count);
sc_adb_devices_destroy(struct sc_vec_adb_devices *devices);
#endif

View File

@ -109,11 +109,8 @@ sc_adb_parse_device(char *line, struct sc_adb_device *device) {
return true;
}
ssize_t
sc_adb_parse_devices(char *str, struct sc_adb_device *devices,
size_t devices_len) {
size_t dev_count = 0;
bool
sc_adb_parse_devices(char *str, struct sc_vec_adb_devices *out_vec) {
#define HEADER "List of devices attached"
#define HEADER_LEN (sizeof(HEADER) - 1)
bool header_found = false;
@ -144,25 +141,24 @@ sc_adb_parse_devices(char *str, struct sc_adb_device *devices,
size_t line_len = sc_str_remove_trailing_cr(line, len);
line[line_len] = '\0';
bool ok = sc_adb_parse_device(line, &devices[dev_count]);
struct sc_adb_device device;
bool ok = sc_adb_parse_device(line, &device);
if (!ok) {
continue;
}
++dev_count;
assert(dev_count <= devices_len);
if (dev_count == devices_len) {
// Max number of devices reached
break;
ok = sc_vector_push(out_vec, device);
if (!ok) {
LOG_OOM();
LOGE("Could not push adb_device to vector");
sc_adb_device_destroy(&device);
// continue anyway
continue;
}
}
if (!header_found) {
return -1;
}
return dev_count;
assert(header_found || out_vec->size == 0);
return header_found;
}
static char *

View File

@ -14,9 +14,8 @@
*
* Warning: this function modifies the buffer for optimization purposes.
*/
ssize_t
sc_adb_parse_devices(char *str, struct sc_adb_device *devices,
size_t devices_len);
bool
sc_adb_parse_devices(char *str, struct sc_vec_adb_devices *out_vec);
/**
* Parse the ip from the output of `adb shell ip route`

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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:
}
}
if (tunnel->enabled) {
// Always leave this function with tunnel disabled
sc_adb_tunnel_close(tunnel, &server->intr, serial);
}
return false;
}

View File

@ -46,6 +46,7 @@ struct sc_server_params {
const char *tcpip_dst;
bool select_usb;
bool select_tcpip;
bool cleanup;
};
struct sc_server {

View File

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

View File

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

View File

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

View File

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

View File

@ -3,6 +3,9 @@
#include <assert.h>
#include "util/log.h"
#include "util/vector.h"
struct sc_vec_usb_devices SC_VECTOR(struct sc_usb_device);
static char *
read_string(libusb_device_handle *handle, uint8_t desc_index) {
@ -38,6 +41,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;
}
@ -80,33 +88,39 @@ sc_usb_device_move(struct sc_usb_device *dst, struct sc_usb_device *src) {
}
void
sc_usb_devices_destroy_all(struct sc_usb_device *usb_devices, size_t count) {
for (size_t i = 0; i < count; ++i) {
sc_usb_device_destroy(&usb_devices[i]);
sc_usb_devices_destroy(struct sc_vec_usb_devices *usb_devices) {
for (size_t i = 0; i < usb_devices->size; ++i) {
sc_usb_device_destroy(&usb_devices->data[i]);
}
sc_vector_destroy(usb_devices);
}
static ssize_t
sc_usb_list_devices(struct sc_usb *usb, struct sc_usb_device *devices,
size_t len) {
static bool
sc_usb_list_devices(struct sc_usb *usb, struct sc_vec_usb_devices *out_vec) {
libusb_device **list;
ssize_t count = libusb_get_device_list(usb->context, &list);
if (count < 0) {
LOGE("List USB devices: libusb error: %s", libusb_strerror(count));
return -1;
return false;
}
size_t idx = 0;
for (size_t i = 0; i < (size_t) count && idx < len; ++i) {
for (size_t i = 0; i < (size_t) count; ++i) {
libusb_device *device = list[i];
if (sc_usb_read_device(device, &devices[idx])) {
++idx;
struct sc_usb_device usb_device;
if (sc_usb_read_device(device, &usb_device)) {
bool ok = sc_vector_push(out_vec, usb_device);
if (!ok) {
LOG_OOM();
LOGE("Could not push usb_device to vector");
sc_usb_device_destroy(&usb_device);
// continue anyway
}
}
}
libusb_free_device_list(list, 1);
return idx;
return true;
}
static bool
@ -142,37 +156,38 @@ 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);
}
}
bool
sc_usb_select_device(struct sc_usb *usb, const char *serial,
struct sc_usb_device *out_device) {
struct sc_usb_device usb_devices[16];
ssize_t count =
sc_usb_list_devices(usb, usb_devices, ARRAY_LEN(usb_devices));
if (count == -1) {
struct sc_vec_usb_devices vec = SC_VECTOR_INITIALIZER;
bool ok = sc_usb_list_devices(usb, &vec);
if (!ok) {
LOGE("Could not list USB devices");
return false;
}
if (count == 0) {
if (vec.size == 0) {
LOGE("Could not find any USB device");
return false;
}
size_t sel_idx; // index of the single matching device if sel_count == 1
size_t sel_count =
sc_usb_devices_select(usb_devices, count, serial, &sel_idx);
sc_usb_devices_select(vec.data, vec.size, serial, &sel_idx);
if (sel_count == 0) {
// if count > 0 && sel_count == 0, then necessarily a serial is provided
assert(serial);
LOGE("Could not find USB device %s", serial);
sc_usb_devices_log(SC_LOG_LEVEL_ERROR, usb_devices, count);
sc_usb_devices_destroy_all(usb_devices, count);
sc_usb_devices_log(SC_LOG_LEVEL_ERROR, vec.data, vec.size);
sc_usb_devices_destroy(&vec);
return false;
}
@ -183,21 +198,21 @@ sc_usb_select_device(struct sc_usb *usb, const char *serial,
} else {
LOGE("Multiple (%" SC_PRIsizet ") USB devices:", sel_count);
}
sc_usb_devices_log(SC_LOG_LEVEL_ERROR, usb_devices, count);
sc_usb_devices_log(SC_LOG_LEVEL_ERROR, vec.data, vec.size);
LOGE("Select a device via -s (--serial)");
sc_usb_devices_destroy_all(usb_devices, count);
sc_usb_devices_destroy(&vec);
return false;
}
assert(sel_count == 1); // sel_idx is valid only if sel_count == 1
struct sc_usb_device *device = &usb_devices[sel_idx];
struct sc_usb_device *device = &vec.data[sel_idx];
LOGD("USB device found:");
sc_usb_devices_log(SC_LOG_LEVEL_DEBUG, usb_devices, count);
sc_usb_devices_log(SC_LOG_LEVEL_DEBUG, vec.data, vec.size);
// Move device into out_device (do not destroy device)
sc_usb_device_move(out_device, device);
sc_usb_devices_destroy_all(usb_devices, count);
sc_usb_devices_destroy(&vec);
return true;
}
@ -212,7 +227,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 +261,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 +282,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 +335,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 +346,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");
}
}

View File

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

View File

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

View File

@ -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);
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);
if (intr) {
sc_intr_set_process(intr, SC_PROCESS_NONE);
}
return ret;
}

View File

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

539
app/src/util/vector.h Normal file
View File

@ -0,0 +1,539 @@
#ifndef SC_VECTOR_H
#define SC_VECTOR_H
#include "common.h"
#include <stdbool.h>
#include <stddef.h>
// Adapted from vlc_vector:
// <https://code.videolan.org/videolan/vlc/-/blob/0857947abaed9c89810cd96353aaa1b7e6ba3b0d/include/vlc_vector.h>
/**
* Vector struct body
*
* A vector is a dynamic array, managed by the sc_vector_* helpers.
*
* It is generic over the type of its items, so it is implemented as macros.
*
* To use a vector, a new type must be defined:
*
* struct vec_int SC_VECTOR(int);
*
* The struct may be anonymous:
*
* struct SC_VECTOR(const char *) names;
*
* Vector size is accessible via `vec.size`, and items are intended to be
* accessed directly, via `vec.data[i]`.
*
* Functions and macros having name ending with '_' are private.
*/
#define SC_VECTOR(type) \
{ \
size_t cap; \
size_t size; \
type *data; \
}
/**
* Static initializer for a vector
*/
#define SC_VECTOR_INITIALIZER { 0, 0, NULL }
/**
* Initialize an empty vector
*/
#define sc_vector_init(pv) \
({ \
(pv)->cap = 0; \
(pv)->size = 0; \
(pv)->data = NULL; \
})
/**
* Destroy a vector
*
* The vector may not be used anymore unless sc_vector_init() is called.
*/
#define sc_vector_destroy(pv) \
free((pv)->data)
/**
* Clear a vector
*
* Remove all items from the vector.
*/
#define sc_vector_clear(pv) \
({ \
sc_vector_destroy(pv); \
sc_vector_init(pv);\
})
/**
* The minimal allocation size, in number of items
*
* Private.
*/
#define SC_VECTOR_MINCAP_ ((size_t) 10)
static inline size_t
sc_vector_min_(size_t a, size_t b)
{
return a < b ? a : b;
}
static inline size_t
sc_vector_max_(size_t a, size_t b)
{
return a > b ? a : b;
}
static inline size_t
sc_vector_clamp_(size_t x, size_t min, size_t max)
{
return sc_vector_max_(min, sc_vector_min_(max, x));
}
/**
* Realloc data and update vector fields
*
* On reallocation success, update the vector capacity (*pcap) and size
* (*psize), and return the reallocated data.
*
* On reallocation failure, return NULL without any change.
*
* Private.
*
* \param ptr the current `data` field of the vector to realloc
* \param count the requested capacity, in number of items
* \param size the size of one item
* \param pcap a pointer to the `cap` field of the vector [IN/OUT]
* \param psize a pointer to the `size` field of the vector [IN/OUT]
* \return the new ptr on success, NULL on error
*/
static inline void *
sc_vector_reallocdata_(void *ptr, size_t count, size_t size,
size_t *restrict pcap, size_t *restrict psize)
{
void *p = realloc(ptr, count * size);
if (!p) {
return NULL;
}
*pcap = count;
*psize = sc_vector_min_(*psize, count);
return p;
}
#define sc_vector_realloc_(pv, newcap) \
({ \
void *p = sc_vector_reallocdata_((pv)->data, newcap, sizeof(*(pv)->data), \
&(pv)->cap, &(pv)->size); \
if (p) { \
(pv)->data = p; \
} \
(bool) p; \
});
#define sc_vector_resize_(pv, newcap) \
({ \
bool ok; \
if ((pv)->cap == (newcap)) { \
ok = true; \
} else if ((newcap) > 0) { \
ok = sc_vector_realloc_(pv, (newcap)); \
} else { \
sc_vector_clear(pv); \
ok = true; \
} \
ok; \
})
static inline size_t
sc_vector_growsize_(size_t value)
{
/* integer multiplication by 1.5 */
return value + (value >> 1);
}
/* SIZE_MAX/2 to fit in ssize_t, and so that cap*1.5 does not overflow. */
#define sc_vector_max_cap_(pv) (SIZE_MAX / 2 / sizeof(*(pv)->data))
/**
* Increase the capacity of the vector to at least `mincap`
*
* \param pv a pointer to the vector
* \param mincap (size_t) the requested capacity
* \retval true if no allocation failed
* \retval false on allocation failure (the vector is left untouched)
*/
#define sc_vector_reserve(pv, mincap) \
({ \
bool ok; \
/* avoid to allocate tiny arrays (< SC_VECTOR_MINCAP_) */ \
size_t mincap_ = sc_vector_max_(mincap, SC_VECTOR_MINCAP_); \
if (mincap_ <= (pv)->cap) { \
/* nothing to do */ \
ok = true; \
} else if (mincap_ <= sc_vector_max_cap_(pv)) { \
/* not too big */ \
size_t newsize = sc_vector_growsize_((pv)->cap); \
newsize = sc_vector_clamp_(newsize, mincap_, sc_vector_max_cap_(pv)); \
ok = sc_vector_realloc_(pv, newsize); \
} else { \
ok = false; \
} \
ok; \
})
#define sc_vector_shrink_to_fit(pv) \
/* decreasing the size may not fail */ \
(void) sc_vector_resize_(pv, (pv)->size)
/**
* Resize the vector down automatically
*
* Shrink only when necessary (in practice when cap > (size+5)*1.5)
*
* \param pv a pointer to the vector
*/
#define sc_vector_autoshrink(pv) \
({ \
bool must_shrink = \
/* do not shrink to tiny size */ \
(pv)->cap > SC_VECTOR_MINCAP_ && \
/* no need to shrink */ \
(pv)->cap >= sc_vector_growsize_((pv)->size + 5); \
if (must_shrink) { \
size_t newsize = sc_vector_max_((pv)->size + 5, SC_VECTOR_MINCAP_); \
sc_vector_resize_(pv, newsize); \
} \
})
#define sc_vector_check_same_ptr_type_(a, b) \
(void) ((a) == (b)) /* warn on type mismatch */
/**
* Push an item at the end of the vector
*
* The amortized complexity is O(1).
*
* \param pv a pointer to the vector
* \param item the item to append
* \retval true if no allocation failed
* \retval false on allocation failure (the vector is left untouched)
*/
#define sc_vector_push(pv, item) \
({ \
bool ok = sc_vector_reserve(pv, (pv)->size + 1); \
if (ok) { \
(pv)->data[(pv)->size++] = (item); \
} \
ok; \
})
/**
* Append `count` items at the end of the vector
*
* \param pv a pointer to the vector
* \param items the items array to append
* \param count the number of items in the array
* \retval true if no allocation failed
* \retval false on allocation failure (the vector is left untouched)
*/
#define sc_vector_push_all(pv, items, count) \
sc_vector_push_all_(pv, items, (size_t) count)
#define sc_vector_push_all_(pv, items, count) \
({ \
sc_vector_check_same_ptr_type_((pv)->data, items); \
bool ok = sc_vector_reserve(pv, (pv)->size + (count)); \
if (ok) { \
memcpy(&(pv)->data[(pv)->size], items, (count) * sizeof(*(pv)->data)); \
(pv)->size += count; \
} \
ok; \
})
/**
* Insert an hole of size `count` to the given index
*
* The items in range [index; size-1] will be moved. The items in the hole are
* left uninitialized.
*
* \param pv a pointer to the vector
* \param index the index where the hole is to be inserted
* \param count the number of items in the hole
* \retval true if no allocation failed
* \retval false on allocation failure (the vector is left untouched)
*/
#define sc_vector_insert_hole(pv, index, count) \
sc_vector_insert_hole_(pv, (size_t) index, (size_t) count);
#define sc_vector_insert_hole_(pv, index, count) \
({ \
bool ok = sc_vector_reserve(pv, (pv)->size + (count)); \
if (ok) { \
if ((index) < (pv)->size) { \
memmove(&(pv)->data[(index) + (count)], \
&(pv)->data[(index)], \
((pv)->size - (index)) * sizeof(*(pv)->data)); \
} \
(pv)->size += count; \
} \
ok; \
})
/**
* Insert an item at the given index
*
* The items in range [index; size-1] will be moved.
*
* \param pv a pointer to the vector
* \param index the index where the item is to be inserted
* \param item the item to append
* \retval true if no allocation failed
* \retval false on allocation failure (the vector is left untouched)
*/
#define sc_vector_insert(pv, index, item) \
sc_vector_insert_(pv, (size_t) index, (size_t) item);
#define sc_vector_insert_(pv, index, item) \
({ \
bool ok = sc_vector_insert_hole_(pv, index, 1); \
if (ok) { \
(pv)->data[index] = (item); \
} \
ok; \
})
/**
* Insert `count` items at the given index
*
* The items in range [index; size-1] will be moved.
*
* \param pv a pointer to the vector
* \param index the index where the items are to be inserted
* \param items the items array to append
* \param count the number of items in the array
* \retval true if no allocation failed
* \retval false on allocation failure (the vector is left untouched)
*/
#define sc_vector_insert_all(pv, index, items, count) \
sc_vector_insert_all_(pv, (size_t) index, items, (size_t) count)
#define sc_vector_insert_all_(pv, index, items, count) \
({ \
sc_vector_check_same_ptr_type_((pv)->data, items); \
bool ok = sc_vector_insert_hole_(pv, index, count); \
if (ok) { \
memcpy(&(pv)->data[index], items, count * sizeof(*(pv)->data)); \
} \
ok; \
})
/** Reverse a char array in place */
static inline void
sc_char_array_reverse(char *array, size_t len)
{
for (size_t i = 0; i < len / 2; ++i)
{
char c = array[i];
array[i] = array[len - i - 1];
array[len - i - 1] = c;
}
}
/**
* Right-rotate a (char) array in place
*
* For example, left-rotating a char array containing {1, 2, 3, 4, 5, 6} with
* distance 4 will result in {5, 6, 1, 2, 3, 4}.
*
* Private.
*/
static inline void
sc_char_array_rotate_left(char *array, size_t len, size_t distance)
{
sc_char_array_reverse(array, distance);
sc_char_array_reverse(&array[distance], len - distance);
sc_char_array_reverse(array, len);
}
/**
* Right-rotate a (char) array in place
*
* For example, left-rotating a char array containing {1, 2, 3, 4, 5, 6} with
* distance 2 will result in {5, 6, 1, 2, 3, 4}.
*
* Private.
*/
static inline void
sc_char_array_rotate_right(char *array, size_t len, size_t distance)
{
sc_char_array_rotate_left(array, len, len - distance);
}
/**
* Move items in a (char) array in place
*
* Move slice [index, count] to target.
*/
static inline void
sc_char_array_move(char *array, size_t idx, size_t count, size_t target)
{
if (idx < target) {
sc_char_array_rotate_left(&array[idx], target - idx + count, count);
} else {
sc_char_array_rotate_right(&array[target], idx - target + count, count);
}
}
/**
* Move a slice of items to a given target index
*
* The items in range [index; count] will be moved so that the *new* position
* of the first item is `target`.
*
* \param pv a pointer to the vector
* \param index the index of the first item to move
* \param count the number of items to move
* \param target the new index of the moved slice
*/
#define sc_vector_move_slice(pv, index, count, target) \
sc_vector_move_slice_(pv, (size_t) index, count, (size_t) target);
#define sc_vector_move_slice_(pv, index, count, target) \
({ \
sc_char_array_move((char *) (pv)->data, \
(index) * sizeof(*(pv)->data), \
(count) * sizeof(*(pv)->data), \
(target) * sizeof(*(pv)->data)); \
})
/**
* Move an item to a given target index
*
* The items will be moved so that its *new* position is `target`.
*
* \param pv a pointer to the vector
* \param index the index of the item to move
* \param target the new index of the moved item
*/
#define sc_vector_move(pv, index, target) \
sc_vector_move_slice(pv, index, 1, target)
/**
* Remove a slice of items, without shrinking the array
*
* If you have no good reason to use the _noshrink() version, use
* sc_vector_remove_slice() instead.
*
* The items in range [index+count; size-1] will be moved.
*
* \param pv a pointer to the vector
* \param index the index of the first item to remove
* \param count the number of items to remove
*/
#define sc_vector_remove_slice_noshrink(pv, index, count) \
sc_vector_remove_slice_noshrink_(pv, (size_t) index, (size_t) count)
#define sc_vector_remove_slice_noshrink_(pv, index, count) \
({ \
if ((index) + (count) < (pv)->size) { \
memmove(&(pv)->data[index], \
&(pv)->data[(index) + (count)], \
((pv)->size - (index) - (count)) * sizeof(*(pv)->data)); \
} \
(pv)->size -= count; \
})
/**
* Remove a slice of items
*
* The items in range [index+count; size-1] will be moved.
*
* \param pv a pointer to the vector
* \param index the index of the first item to remove
* \param count the number of items to remove
*/
#define sc_vector_remove_slice(pv, index, count) \
({ \
sc_vector_remove_slice_noshrink(pv, index, count); \
sc_vector_autoshrink(pv); \
})
/**
* Remove an item, without shrinking the array
*
* If you have no good reason to use the _noshrink() version, use
* sc_vector_remove() instead.
*
* The items in range [index+1; size-1] will be moved.
*
* \param pv a pointer to the vector
* \param index the index of item to remove
*/
#define sc_vector_remove_noshrink(pv, index) \
sc_vector_remove_slice_noshrink(pv, index, 1)
/**
* Remove an item
*
* The items in range [index+1; size-1] will be moved.
*
* \param pv a pointer to the vector
* \param index the index of item to remove
*/
#define sc_vector_remove(pv, index) \
({ \
sc_vector_remove_noshrink(pv, index); \
sc_vector_autoshrink(pv); \
})
/**
* Remove an item
*
* The removed item is replaced by the last item of the vector.
*
* This does not preserve ordering, but is O(1). This is useful when the order
* of items is not meaningful.
*
* \param pv a pointer to the vector
* \param index the index of item to remove
*/
#define sc_vector_swap_remove(pv, index) \
sc_vector_swap_remove_(pv, (size_t) index);
#define sc_vector_swap_remove_(pv, index) \
({ \
(pv)->data[index] = (pv)->data[(pv)->size-1]; \
(pv)->size--; \
});
/**
* Return the index of an item
*
* Iterate over all items to find a given item.
*
* Use only for vectors of primitive types or pointers.
*
* Return the index, or -1 if not found.
*
* \param pv a pointer to the vector
* \param item the item to find (compared with ==)
*/
#define sc_vector_index_of(pv, item) \
({ \
ssize_t idx = -1; \
for (size_t i = 0; i < (pv)->size; ++i) { \
if ((pv)->data[i] == (item)) { \
idx = (ssize_t) i; \
break; \
} \
} \
idx; \
})
#endif

67
app/src/version.c Normal file
View 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
View File

@ -0,0 +1,9 @@
#ifndef SC_VERSION_H
#define SC_VERSION_H
#include "common.h"
void
scrcpy_print_version(void);
#endif

View File

@ -13,21 +13,22 @@ static void test_adb_devices() {
"192.168.1.1:5555 device product:MyWifiProduct model:MyWifiModel "
"device:MyWifiDevice trandport_id:2\n";
struct sc_adb_device devices[16];
ssize_t count = sc_adb_parse_devices(output, devices, ARRAY_LEN(devices));
assert(count == 2);
struct sc_vec_adb_devices vec = SC_VECTOR_INITIALIZER;
bool ok = sc_adb_parse_devices(output, &vec);
assert(ok);
assert(vec.size == 2);
struct sc_adb_device *device = &devices[0];
struct sc_adb_device *device = &vec.data[0];
assert(!strcmp("0123456789abcdef", device->serial));
assert(!strcmp("device", device->state));
assert(!strcmp("MyModel", device->model));
device = &devices[1];
device = &vec.data[1];
assert(!strcmp("192.168.1.1:5555", device->serial));
assert(!strcmp("device", device->state));
assert(!strcmp("MyWifiModel", device->model));
sc_adb_devices_destroy_all(devices, count);
sc_adb_devices_destroy(&vec);
}
static void test_adb_devices_cr() {
@ -38,21 +39,22 @@ static void test_adb_devices_cr() {
"192.168.1.1:5555 device product:MyWifiProduct model:MyWifiModel "
"device:MyWifiDevice trandport_id:2\r\n";
struct sc_adb_device devices[16];
ssize_t count = sc_adb_parse_devices(output, devices, ARRAY_LEN(devices));
assert(count == 2);
struct sc_vec_adb_devices vec = SC_VECTOR_INITIALIZER;
bool ok = sc_adb_parse_devices(output, &vec);
assert(ok);
assert(vec.size == 2);
struct sc_adb_device *device = &devices[0];
struct sc_adb_device *device = &vec.data[0];
assert(!strcmp("0123456789abcdef", device->serial));
assert(!strcmp("device", device->state));
assert(!strcmp("MyModel", device->model));
device = &devices[1];
device = &vec.data[1];
assert(!strcmp("192.168.1.1:5555", device->serial));
assert(!strcmp("device", device->state));
assert(!strcmp("MyWifiModel", device->model));
sc_adb_devices_destroy_all(devices, count);
sc_adb_devices_destroy(&vec);
}
static void test_adb_devices_daemon_start() {
@ -63,16 +65,17 @@ static void test_adb_devices_daemon_start() {
"0123456789abcdef device usb:2-1 product:MyProduct model:MyModel "
"device:MyDevice transport_id:1\n";
struct sc_adb_device devices[16];
ssize_t count = sc_adb_parse_devices(output, devices, ARRAY_LEN(devices));
assert(count == 1);
struct sc_vec_adb_devices vec = SC_VECTOR_INITIALIZER;
bool ok = sc_adb_parse_devices(output, &vec);
assert(ok);
assert(vec.size == 1);
struct sc_adb_device *device = &devices[0];
struct sc_adb_device *device = &vec.data[0];
assert(!strcmp("0123456789abcdef", device->serial));
assert(!strcmp("device", device->state));
assert(!strcmp("MyModel", device->model));
sc_adb_device_destroy(device);
sc_adb_devices_destroy(&vec);
}
static void test_adb_devices_daemon_start_mixed() {
@ -84,22 +87,22 @@ static void test_adb_devices_daemon_start_mixed() {
"87654321 device usb:2-1 product:MyProduct model:MyModel "
"device:MyDevice\n";
struct sc_adb_device devices[16];
ssize_t count = sc_adb_parse_devices(output, devices, ARRAY_LEN(devices));
assert(count == 2);
struct sc_vec_adb_devices vec = SC_VECTOR_INITIALIZER;
bool ok = sc_adb_parse_devices(output, &vec);
assert(ok);
assert(vec.size == 2);
struct sc_adb_device *device = &devices[0];
struct sc_adb_device *device = &vec.data[0];
assert(!strcmp("0123456789abcdef", device->serial));
assert(!strcmp("unauthorized", device->state));
fprintf(stderr, "==== [%s]\n", device->model);
assert(!device->model);
device = &devices[1];
device = &vec.data[1];
assert(!strcmp("87654321", device->serial));
assert(!strcmp("device", device->state));
assert(!strcmp("MyModel", device->model));
sc_adb_devices_destroy_all(devices, count);
sc_adb_devices_destroy(&vec);
}
static void test_adb_devices_without_eol() {
@ -107,34 +110,39 @@ static void test_adb_devices_without_eol() {
"List of devices attached\n"
"0123456789abcdef device usb:2-1 product:MyProduct model:MyModel "
"device:MyDevice transport_id:1";
struct sc_adb_device devices[16];
ssize_t count = sc_adb_parse_devices(output, devices, ARRAY_LEN(devices));
assert(count == 1);
struct sc_adb_device *device = &devices[0];
struct sc_vec_adb_devices vec = SC_VECTOR_INITIALIZER;
bool ok = sc_adb_parse_devices(output, &vec);
assert(ok);
assert(vec.size == 1);
struct sc_adb_device *device = &vec.data[0];
assert(!strcmp("0123456789abcdef", device->serial));
assert(!strcmp("device", device->state));
assert(!strcmp("MyModel", device->model));
sc_adb_device_destroy(device);
sc_adb_devices_destroy(&vec);
}
static void test_adb_devices_without_header() {
char output[] =
"0123456789abcdef device usb:2-1 product:MyProduct model:MyModel "
"device:MyDevice transport_id:1\n";
struct sc_adb_device devices[16];
ssize_t count = sc_adb_parse_devices(output, devices, ARRAY_LEN(devices));
assert(count == -1);
struct sc_vec_adb_devices vec = SC_VECTOR_INITIALIZER;
bool ok = sc_adb_parse_devices(output, &vec);
assert(!ok);
}
static void test_adb_devices_corrupted() {
char output[] =
"List of devices attached\n"
"corrupted_garbage\n";
struct sc_adb_device devices[16];
ssize_t count = sc_adb_parse_devices(output, devices, ARRAY_LEN(devices));
assert(count == 0);
struct sc_vec_adb_devices vec = SC_VECTOR_INITIALIZER;
bool ok = sc_adb_parse_devices(output, &vec);
assert(ok);
assert(vec.size == 0);
}
static void test_adb_devices_spaces() {
@ -142,16 +150,17 @@ static void test_adb_devices_spaces() {
"List of devices attached\n"
"0123456789abcdef unauthorized usb:1-4 transport_id:3\n";
struct sc_adb_device devices[16];
ssize_t count = sc_adb_parse_devices(output, devices, ARRAY_LEN(devices));
assert(count == 1);
struct sc_vec_adb_devices vec = SC_VECTOR_INITIALIZER;
bool ok = sc_adb_parse_devices(output, &vec);
assert(ok);
assert(vec.size == 1);
struct sc_adb_device *device = &devices[0];
struct sc_adb_device *device = &vec.data[0];
assert(!strcmp("0123456789abcdef", device->serial));
assert(!strcmp("unauthorized", device->state));
assert(!device->model);
sc_adb_device_destroy(device);
sc_adb_devices_destroy(&vec);
}
static void test_get_ip_single_line() {

View File

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

421
app/tests/test_vector.c Normal file
View File

@ -0,0 +1,421 @@
#include "common.h"
#include <assert.h>
#include "util/vector.h"
static void test_vector_insert_remove(void) {
struct SC_VECTOR(int) vec = SC_VECTOR_INITIALIZER;
bool ok;
ok = sc_vector_push(&vec, 42);
assert(ok);
assert(vec.data[0] == 42);
assert(vec.size == 1);
ok = sc_vector_push(&vec, 37);
assert(ok);
assert(vec.size == 2);
assert(vec.data[0] == 42);
assert(vec.data[1] == 37);
ok = sc_vector_insert(&vec, 1, 100);
assert(ok);
assert(vec.size == 3);
assert(vec.data[0] == 42);
assert(vec.data[1] == 100);
assert(vec.data[2] == 37);
ok = sc_vector_push(&vec, 77);
assert(ok);
assert(vec.size == 4);
assert(vec.data[0] == 42);
assert(vec.data[1] == 100);
assert(vec.data[2] == 37);
assert(vec.data[3] == 77);
sc_vector_remove(&vec, 1);
assert(vec.size == 3);
assert(vec.data[0] == 42);
assert(vec.data[1] == 37);
assert(vec.data[2] == 77);
sc_vector_clear(&vec);
assert(vec.size == 0);
sc_vector_destroy(&vec);
}
static void test_vector_push_array(void) {
struct SC_VECTOR(int) vec = SC_VECTOR_INITIALIZER;
bool ok;
ok = sc_vector_push(&vec, 3); assert(ok);
ok = sc_vector_push(&vec, 14); assert(ok);
ok = sc_vector_push(&vec, 15); assert(ok);
ok = sc_vector_push(&vec, 92); assert(ok);
ok = sc_vector_push(&vec, 65); assert(ok);
assert(vec.size == 5);
int items[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
ok = sc_vector_push_all(&vec, items, 8);
assert(ok);
assert(vec.size == 13);
assert(vec.data[0] == 3);
assert(vec.data[1] == 14);
assert(vec.data[2] == 15);
assert(vec.data[3] == 92);
assert(vec.data[4] == 65);
assert(vec.data[5] == 1);
assert(vec.data[6] == 2);
assert(vec.data[7] == 3);
assert(vec.data[8] == 4);
assert(vec.data[9] == 5);
assert(vec.data[10] == 6);
assert(vec.data[11] == 7);
assert(vec.data[12] == 8);
sc_vector_destroy(&vec);
}
static void test_vector_insert_array(void) {
struct SC_VECTOR(int) vec = SC_VECTOR_INITIALIZER;
bool ok;
ok = sc_vector_push(&vec, 3); assert(ok);
ok = sc_vector_push(&vec, 14); assert(ok);
ok = sc_vector_push(&vec, 15); assert(ok);
ok = sc_vector_push(&vec, 92); assert(ok);
ok = sc_vector_push(&vec, 65); assert(ok);
assert(vec.size == 5);
int items[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
ok = sc_vector_insert_all(&vec, 3, items, 8);
assert(ok);
assert(vec.size == 13);
assert(vec.data[0] == 3);
assert(vec.data[1] == 14);
assert(vec.data[2] == 15);
assert(vec.data[3] == 1);
assert(vec.data[4] == 2);
assert(vec.data[5] == 3);
assert(vec.data[6] == 4);
assert(vec.data[7] == 5);
assert(vec.data[8] == 6);
assert(vec.data[9] == 7);
assert(vec.data[10] == 8);
assert(vec.data[11] == 92);
assert(vec.data[12] == 65);
sc_vector_destroy(&vec);
}
static void test_vector_remove_slice(void) {
struct SC_VECTOR(int) vec = SC_VECTOR_INITIALIZER;
bool ok;
for (int i = 0; i < 100; ++i)
{
ok = sc_vector_push(&vec, i);
assert(ok);
}
assert(vec.size == 100);
sc_vector_remove_slice(&vec, 32, 60);
assert(vec.size == 40);
assert(vec.data[31] == 31);
assert(vec.data[32] == 92);
sc_vector_destroy(&vec);
}
static void test_vector_swap_remove(void) {
struct SC_VECTOR(int) vec = SC_VECTOR_INITIALIZER;
bool ok;
ok = sc_vector_push(&vec, 3); assert(ok);
ok = sc_vector_push(&vec, 14); assert(ok);
ok = sc_vector_push(&vec, 15); assert(ok);
ok = sc_vector_push(&vec, 92); assert(ok);
ok = sc_vector_push(&vec, 65); assert(ok);
assert(vec.size == 5);
sc_vector_swap_remove(&vec, 1);
assert(vec.size == 4);
assert(vec.data[0] == 3);
assert(vec.data[1] == 65);
assert(vec.data[2] == 15);
assert(vec.data[3] == 92);
sc_vector_destroy(&vec);
}
static void test_vector_index_of(void) {
struct SC_VECTOR(int) vec;
sc_vector_init(&vec);
bool ok;
for (int i = 0; i < 10; ++i)
{
ok = sc_vector_push(&vec, i);
assert(ok);
}
ssize_t idx;
idx = sc_vector_index_of(&vec, 0);
assert(idx == 0);
idx = sc_vector_index_of(&vec, 1);
assert(idx == 1);
idx = sc_vector_index_of(&vec, 4);
assert(idx == 4);
idx = sc_vector_index_of(&vec, 9);
assert(idx == 9);
idx = sc_vector_index_of(&vec, 12);
assert(idx == -1);
sc_vector_destroy(&vec);
}
static void test_vector_grow() {
struct SC_VECTOR(int) vec = SC_VECTOR_INITIALIZER;
bool ok;
for (int i = 0; i < 50; ++i)
{
ok = sc_vector_push(&vec, i); /* append */
assert(ok);
}
assert(vec.cap >= 50);
assert(vec.size == 50);
for (int i = 0; i < 25; ++i)
{
ok = sc_vector_insert(&vec, 20, i); /* insert in the middle */
assert(ok);
}
assert(vec.cap >= 75);
assert(vec.size == 75);
for (int i = 0; i < 25; ++i)
{
ok = sc_vector_insert(&vec, 0, i); /* prepend */
assert(ok);
}
assert(vec.cap >= 100);
assert(vec.size == 100);
for (int i = 0; i < 50; ++i)
sc_vector_remove(&vec, 20); /* remove from the middle */
assert(vec.cap >= 50);
assert(vec.size == 50);
for (int i = 0; i < 25; ++i)
sc_vector_remove(&vec, 0); /* remove from the head */
assert(vec.cap >= 25);
assert(vec.size == 25);
for (int i = 24; i >=0; --i)
sc_vector_remove(&vec, i); /* remove from the tail */
assert(vec.size == 0);
sc_vector_destroy(&vec);
}
static void test_vector_exp_growth(void) {
struct SC_VECTOR(int) vec = SC_VECTOR_INITIALIZER;
size_t oldcap = vec.cap;
int realloc_count = 0;
bool ok;
for (int i = 0; i < 10000; ++i)
{
ok = sc_vector_push(&vec, i);
assert(ok);
if (vec.cap != oldcap)
{
realloc_count++;
oldcap = vec.cap;
}
}
/* Test speciically for an expected growth factor of 1.5. In practice, the
* result is even lower (19) due to the first alloc of size 10 */
assert(realloc_count <= 23); /* ln(10000) / ln(1.5) ~= 23 */
realloc_count = 0;
for (int i = 9999; i >= 0; --i)
{
sc_vector_remove(&vec, i);
if (vec.cap != oldcap)
{
realloc_count++;
oldcap = vec.cap;
}
}
assert(realloc_count <= 23); /* same expectations for removals */
assert(realloc_count > 0); /* sc_vector_remove() must autoshrink */
sc_vector_destroy(&vec);
}
static void test_vector_reserve(void) {
struct SC_VECTOR(int) vec = SC_VECTOR_INITIALIZER;
bool ok;
ok = sc_vector_reserve(&vec, 800);
assert(ok);
assert(vec.cap >= 800);
assert(vec.size == 0);
size_t initial_cap = vec.cap;
for (int i = 0; i < 800; ++i)
{
ok = sc_vector_push(&vec, i);
assert(ok);
assert(vec.cap == initial_cap); /* no realloc */
}
sc_vector_destroy(&vec);
}
static void test_vector_shrink_to_fit(void) {
struct SC_VECTOR(int) vec = SC_VECTOR_INITIALIZER;
bool ok;
ok = sc_vector_reserve(&vec, 800);
assert(ok);
for (int i = 0; i < 250; ++i)
{
ok = sc_vector_push(&vec, i);
assert(ok);
}
assert(vec.cap >= 800);
assert(vec.size == 250);
sc_vector_shrink_to_fit(&vec);
assert(vec.cap == 250);
assert(vec.size == 250);
sc_vector_destroy(&vec);
}
static void test_vector_move(void) {
struct SC_VECTOR(int) vec = SC_VECTOR_INITIALIZER;
for (int i = 0; i < 7; ++i)
{
bool ok = sc_vector_push(&vec, i);
assert(ok);
}
/* move item at 1 so that its new position is 4 */
sc_vector_move(&vec, 1, 4);
assert(vec.size == 7);
assert(vec.data[0] == 0);
assert(vec.data[1] == 2);
assert(vec.data[2] == 3);
assert(vec.data[3] == 4);
assert(vec.data[4] == 1);
assert(vec.data[5] == 5);
assert(vec.data[6] == 6);
sc_vector_destroy(&vec);
}
static void test_vector_move_slice_forward(void) {
struct SC_VECTOR(int) vec = SC_VECTOR_INITIALIZER;
for (int i = 0; i < 10; ++i)
{
bool ok = sc_vector_push(&vec, i);
assert(ok);
}
/* move slice {2, 3, 4, 5} so that its new position is 5 */
sc_vector_move_slice(&vec, 2, 4, 5);
assert(vec.size == 10);
assert(vec.data[0] == 0);
assert(vec.data[1] == 1);
assert(vec.data[2] == 6);
assert(vec.data[3] == 7);
assert(vec.data[4] == 8);
assert(vec.data[5] == 2);
assert(vec.data[6] == 3);
assert(vec.data[7] == 4);
assert(vec.data[8] == 5);
assert(vec.data[9] == 9);
sc_vector_destroy(&vec);
}
static void test_vector_move_slice_backward(void) {
struct SC_VECTOR(int) vec = SC_VECTOR_INITIALIZER;
for (int i = 0; i < 10; ++i)
{
bool ok = sc_vector_push(&vec, i);
assert(ok);
}
/* move slice {5, 6, 7} so that its new position is 2 */
sc_vector_move_slice(&vec, 5, 3, 2);
assert(vec.size == 10);
assert(vec.data[0] == 0);
assert(vec.data[1] == 1);
assert(vec.data[2] == 5);
assert(vec.data[3] == 6);
assert(vec.data[4] == 7);
assert(vec.data[5] == 2);
assert(vec.data[6] == 3);
assert(vec.data[7] == 4);
assert(vec.data[8] == 8);
assert(vec.data[9] == 9);
sc_vector_destroy(&vec);
}
int main(int argc, char *argv[]) {
(void) argc;
(void) argv;
test_vector_insert_remove();
test_vector_push_array();
test_vector_insert_array();
test_vector_remove_slice();
test_vector_swap_remove();
test_vector_move();
test_vector_move_slice_forward();
test_vector_move_slice_backward();
test_vector_index_of();
test_vector_grow();
test_vector_exp_growth();
test_vector_reserve();
test_vector_shrink_to_fit();
return 0;
}

View File

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

View File

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

View File

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

View File

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

View File

@ -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)" \
@ -94,37 +98,39 @@ dist-win32: build-server build-win32
mkdir -p "$(DIST)/$(WIN32_TARGET_DIR)"
cp "$(SERVER_BUILD_DIR)"/server/scrcpy-server "$(DIST)/$(WIN32_TARGET_DIR)/"
cp "$(WIN32_BUILD_DIR)"/app/scrcpy.exe "$(DIST)/$(WIN32_TARGET_DIR)/"
cp data/scrcpy-console.bat "$(DIST)/$(WIN32_TARGET_DIR)"
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/data/scrcpy-console.bat "$(DIST)/$(WIN32_TARGET_DIR)"
cp app/data/scrcpy-noconsole.vbs "$(DIST)/$(WIN32_TARGET_DIR)"
cp app/data/icon.png "$(DIST)/$(WIN32_TARGET_DIR)"
cp app/data/open_a_terminal_here.bat "$(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)"
cp "$(SERVER_BUILD_DIR)"/server/scrcpy-server "$(DIST)/$(WIN64_TARGET_DIR)/"
cp "$(WIN64_BUILD_DIR)"/app/scrcpy.exe "$(DIST)/$(WIN64_TARGET_DIR)/"
cp data/scrcpy-console.bat "$(DIST)/$(WIN64_TARGET_DIR)"
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/data/scrcpy-console.bat "$(DIST)/$(WIN64_TARGET_DIR)"
cp app/data/scrcpy-noconsole.vbs "$(DIST)/$(WIN64_TARGET_DIR)"
cp app/data/icon.png "$(DIST)/$(WIN64_TARGET_DIR)"
cp app/data/open_a_terminal_here.bat "$(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)"; \

View File

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

View File

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

View File

@ -51,6 +51,7 @@ public final class Server {
}
}
if (options.getCleanup()) {
try {
CleanUp.configure(options.getDisplayId(), restoreStayOn, mustDisableShowTouchesOnCleanUp, restoreNormalPowerMode,
options.getPowerOffScreenOnClose());
@ -58,6 +59,7 @@ public final class Server {
Ln.e("Could not configure cleanup", e);
}
}
}
private static void scrcpy(Options options) throws IOException {
Ln.i("Device: " + Build.MANUFACTURER + " " + Build.MODEL + " (Android " + Build.VERSION.RELEASE + ")");
@ -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);