Compare commits

...

73 Commits

Author SHA1 Message Date
454b9a6c3e Replace SDL_strdup() by strdup()
The functions SDL_malloc(), SDL_free() and SDL_strdup() were used only
because strdup() was not available everywhere.

Now that it is available, use the native version of these functions.
2021-01-24 15:32:44 +01:00
2c8efe1a1d Provide strdup() compat
Make strdup() available on all platforms.
2021-01-24 15:32:44 +01:00
97b001e7c0 Fix undefined left shift
Small unsigned integers promote to signed int. As a consequence, if v is
a uint8_t, then (v << 24) yields an int, so the left shift is undefined
if the MSB is 1.

Cast to uint32_t to yield an unsigned value.

Reported by USAN (meson x -Db_sanitize=undefined):

    runtime error: left shift of 255 by 24 places cannot be represented
    in type 'int'
2021-01-24 15:31:21 +01:00
d8e9ad20b0 Improve file handler error message
Terminating the file handler current process may be either a "push" or
"install" command.
2021-01-24 14:24:24 +01:00
b566700bfd Kill process with SIGKILL signal
An "adb push" command is not terminated by SIGTERM.
2021-01-24 14:24:24 +01:00
7afd149f4b Fix file_handler process race condition
The current process could be waited both by run_file_handler() and
file_handler_stop().

To avoid the race condition, wait the process without closing, then
close with mutex locked.
2021-01-24 14:24:24 +01:00
6a50231698 Expose a single process_wait()
There were two versions: process_wait() and process_wait_noclose().

Expose a single version with a flag (it was already implemented that way
internally).
2021-01-24 14:24:18 +01:00
b8edcf52b0 Simplify process_wait()
The function process_wait() returned a bool (true if the process
terminated successfully) and provided the exit code via an output
parameter exit_code.

But the returned value was always equivalent to exit_code == 0, so just
return the exit code instead.
2021-01-22 18:29:21 +01:00
94eff0a4bb Fix size_t incorrectly assigned to int
The function control_msg_serialize() returns a size_t.
2021-01-17 19:44:23 +01:00
8dbb1676b7 Factorize meson compiler variable initialization 2021-01-17 19:44:23 +01:00
ab912c23e7 Define feature test macros in common.h
This enables necessary functions once for all.

As a consequence, define common.h before any other header.
2021-01-17 14:08:48 +01:00
59feb2a15c Group common includes into common.h
Include config.h and compat.h in common.h, and include common.h from all
source files.
2021-01-08 19:22:10 +01:00
6385b8c162 Move common structs to coords.h
The size, point and position structs were defined in common.h. Move them
to coords.h so that common.h could be used for generic code to be
included in all source files.
2021-01-08 19:22:10 +01:00
037be4af21 Fix compat missing include
The header libavformat/version.h was included, but not
libavcodec/version.h.

As a consequence, the LIBAVCODEC_VERSION_INT definition depended on the
caller includes.
2021-01-08 19:21:54 +01:00
1e215199dc Remove unused struct port_range
It had been replaced by struct sc_port_range in scrcpy.h.
2021-01-08 19:13:53 +01:00
d580ee30f1 Separate process wait and close
On Linux, waitpid() both waits for the process to terminate and reaps it
(closes its handle). On Windows, these actions are separated into
WaitForSingleObject() and CloseHandle().

Expose these actions separately, so that it is possible to send a signal
to a process while waiting for its termination without race condition.

This allows to wait for server termination normally, but kill the
process without race condition if it is not terminated after some delay.
2021-01-08 16:44:21 +01:00
821c175730 Rename process_simple_wait to process_wait
Adding "simple" in the function name brings no benefit.
2021-01-08 16:44:21 +01:00
cc6f5020d8 Move conditional src files in meson.build
Declare all the source files (including the platform-specific ones) at
the beginning.
2021-01-08 16:44:21 +01:00
4bd9da4c93 Split command into process and adb
The process API provides the system-specific implementation, the adb API
uses it to expose adb commands.
2021-01-08 16:44:21 +01:00
aa8b571389 Increase display id range
Some devices use big display id values.

Refs #2009 <https://github.com/Genymobile/scrcpy/issues/2009>
2021-01-04 08:16:32 +01:00
ed130e05d5 Fix possibly uninitialized value
Due to gotos, "ret" may be returned uninitialized.
2021-01-03 22:41:51 +01:00
192fbd8450 Use --cask for latest versions of brew
PR #2004 <https://github.com/Genymobile/scrcpy/pull/2004>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2021-01-02 18:27:38 +01:00
c5c5fc18ae Update links to v1.17 in README and BUILD 2021-01-02 01:26:23 +01:00
f682b87ba5 Bump version to 1.17 2021-01-02 00:53:32 +01:00
10b749e27d Kill the server only after a small delay
Let the server terminate properly once all the sockets are closed.

If it does not terminate (this can happen if the device is asleep), then
kill it.

Note: since the server process termination is detected by a flag set
after waitpid() returns, there is a small chance that the process
terminates (and the PID assigned to a new process) before the flag is
set but before the kill() call. This race condition already existed
before this commit.

Fixes #1992 <https://github.com/Genymobile/scrcpy/issues/1992>
2021-01-01 23:57:01 +01:00
05e8c1a3c5 Call CloseHandle() after wait on Windows
TerminateProcess() is "equivalent" to kill(), while
WaitForSingleObject() is "equivalent" to waitpid(), so the handle must
be closed after WaitForSingleObject().
2021-01-01 23:57:01 +01:00
83910d3b9c Initialize server struct dynamically
This will allow to add mutex/cond fields.
2021-01-01 17:40:52 +01:00
90f8356630 Interrupt device threads on stop
The (non-daemon) threads were not interrupted on video stream stopped,
leaving the server process alive.

Interrupt them to wake up their blocking call so that they terminate
properly.

Refs #1992 <https://github.com/Genymobile/scrcpy/issues/1992>
2021-01-01 17:32:34 +01:00
3ba51211d6 Mention how to add default arguments on Windows
Mention that it is possible to add default arguments by editing the
wrapper scripts.
2021-01-01 17:31:07 +01:00
ea3582d2c3 Merge branch 'master' into dev 2021-01-01 17:18:13 +01:00
112adbba87 Happy new year 2021! 2021-01-01 17:16:44 +01:00
d039a7a39a Upgrade SDL (2.0.14) for Windows
Include the latest version of SDL in Windows releases.
2021-01-01 16:08:58 +01:00
6ab80e4ce8 Rename release.make to release.mk
It's more standard, and benefits from syntax coloration in vi.
2021-01-01 15:51:10 +01:00
230afd8966 Unify release makefile
Before this change, release.sh built some native stuff, and
Makefile.CrossWindows built the Windows releases.

Instead, use a single release.make to build the whole release. It also
avoids to build the server one more time.
2020-12-22 18:52:05 +01:00
a46733906a Replace noconsole binary by a wrapper script
This simplifies the build system.

Refs <https://github.com/Genymobile/scrcpy/issues/1975#issuecomment-745314161>
2020-12-22 18:51:59 +01:00
431c9ee33b Improve rotation documentation 2020-12-22 01:48:01 +01:00
43d3dcbd97 Document Windows command line usage
PR #1973 <https://github.com/Genymobile/scrcpy/pull/1973>

Reviewed-by: Yu-Chen Lin <npes87184@gmail.com>
2020-12-22 01:44:55 +01:00
a5f4f58295 Remove duplicate include 2020-12-22 01:14:30 +01:00
ea12783bbc Upgrade JUnit to 4.13 2020-12-17 10:53:35 +01:00
904d470579 Pause on error from a wrapper script
On Windows, scrcpy paused on error before exiting to give the user a
chance to see the user message.

This was a hack and causes issues when using scrcpy from batch scripts.

Disable this pause from the scrcpy binary, and provide a batch wrapper
(scrcpy-console.bat) to pause on error.

Fixes #1875 <https://github.com/Genymobile/scrcpy/issues/1875>
2020-12-14 09:44:17 +01:00
6d151eaef9 Reference encoder section from FAQ 2020-12-14 09:04:14 +01:00
5dc3285dbf Reference FFmpeg Windows builds from GitHub
Refs #1753 <https://github.com/Genymobile/scrcpy/issues/1753>
2020-12-12 16:26:08 +01:00
c9a4bdb890 Upgrade platform-tools (30.0.5) for Windows
Include the latest version of adb in Windows releases.
2020-12-12 15:56:32 +01:00
d60ac65b32 Merge branch 'master' into dev 2020-12-12 15:55:29 +01:00
d6078cf202 Fix build errors for macOS
PR #1960 <https://github.com/Genymobile/scrcpy/pull/1960>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2020-12-12 15:50:33 +01:00
47c8971267 Document shell command to get the device IP
PR #1944 <https://github.com/Genymobile/scrcpy/pull/1944>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2020-12-10 21:37:23 +01:00
30434afc0a Upgrade gradle build tools to 4.0.1 2020-11-24 09:45:18 +01:00
868e762d71 Fix size_t format specifier for Windows
Use "%Iu" on Windows. This fixes the following warning:

    ../app/src/sys/win/command.c:17:14: warning: unknown conversion type character ‘l’ in format [-Wformat=]
       17 |         LOGE("Command too long (%" PRIsizet " chars)", len - 1);
2020-11-14 22:10:11 +01:00
576814bcec Document --encoder option
Add documentation for the new option --encoder in the manpage and in
README.md.
2020-11-08 21:11:12 +01:00
42ab8fd611 List available encoders on invalid name specified
If an invalid encoder name is given via the --encoder option, list all
the H.264 encoders available on the device.
2020-11-08 21:07:20 +01:00
363eeea19e Log encoder name
When the encoder is selected automatically, log the name of the selected
encoder.
2020-11-08 21:07:20 +01:00
76c2c6e69d Adding new option --encoder
Some devices have more than one encoder, and some encoders may cause
issues or crash. With this option we can specify which encoder we want
the device to use.

PR #1827 <https://github.com/Genymobile/scrcpy/pull/1827>
Fixes #1810 <https://github.com/Genymobile/scrcpy/issues/1810>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2020-11-08 21:07:17 +01:00
d5f059c7cb Add option to force legacy method for pasting
Some devices do not behave as expected when setting the device clipboard
programmatically.

Add an option --legacy-paste to change the behavior of Ctrl+v and MOD+v
so that they inject the computer clipboard text as a sequence of key
events (the same way as MOD+Shift+v).

Fixes #1750 <https://github.com/Genymobile/scrcpy/issues/1750>
Fixes #1771 <https://github.com/Genymobile/scrcpy/issues/1771>
2020-11-07 15:16:51 +01:00
adc547fa6e Add an option to forward all clicks
Add --forward-all-clicks to disable mouse shortcuts and forward middle
and right clicks to the device instead.

Fixes #1302 <https://github.com/Genymobile/scrcpy/issues/1302>
Fixes #1613 <https://github.com/Genymobile/scrcpy/issues/1613>
2020-11-03 17:12:26 +01:00
5dcfc0ebab Add local.properties to gitignore 2020-11-03 17:09:03 +01:00
ad5f567f07 Remove spurious space 2020-11-03 17:08:21 +01:00
25aff00935 Mention version of Indonesian translation 2020-10-05 21:24:59 +02:00
aade92fd10 Add Indonesian translation for README.md
PR #1802 <https://github.com/Genymobile/scrcpy/pull/1802>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2020-10-05 21:17:43 +02:00
83082406d3 Enable Java deprecation warnings details
Without the option, gradle reports a lint issue, but without any
details.
2020-10-05 21:11:50 +02:00
2edf192e3a Remove deprecation warning
As a workaround for some devices, we need to prepare the main looper.
The method is now deprecated, but we still want to call it.
2020-10-05 21:09:47 +02:00
d50ecf40b6 Fix options order 2020-10-01 15:08:18 +02:00
15b81367c9 Fix FAQ.ko.md
PR #1767 <https://github.com/Genymobile/scrcpy/pull/1767>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2020-10-01 14:57:09 +02:00
56d237f152 Fix "press Enter key" message
The message said "Press any key to continue...", whereas only
Enter/Return is accepted.

PR #1783 <https://github.com/Genymobile/scrcpy/pull/1783>
Fixes #1757 <https://github.com/Genymobile/scrcpy/issues/1757>

Reviewed-by: Yu-Chen Lin <npes87184@gmail.com>
Signed-off-by: Romain Vimont <rom@rom1v.com>
2020-10-01 14:52:24 +02:00
acc65f8c9d Remove unused field
Fixes #1775 <https://github.com/Genymobile/scrcpy/issues/1775>

Reported-by: lordnn
2020-09-20 01:11:22 +02:00
a65ebceac1 Add missing mutex unlock on error
Fixes #1770 <https://github.com/Genymobile/scrcpy/issues/1770>

Reported-by: lordnn
2020-09-20 01:11:13 +02:00
c136edf09d Add simplified Chinese translation for README.md
PR #1723 <https://github.com/Genymobile/scrcpy/pull/1723>

Co-authored-by: Shaw Yu <shawyu.nz@gmail.com>
Signed-off-by: Romain Vimont <rom@rom1v.com>
2020-09-17 13:09:43 +02:00
52560faa34 Fix README indentation 2020-09-17 13:03:40 +02:00
d662f73bdc Upgrade Android SDK to 30 2020-09-15 14:54:22 +02:00
1c44dc2259 Use portable shebang for all bash scripts
Refs #426 <https://github.com/Genymobile/scrcpy/pull/426>
Refs #1716 <https://github.com/Genymobile/scrcpy/pull/1716>
2020-09-15 13:54:00 +02:00
02a882a0a2 Use a more portable shebang for bash
This should increase the portability of bash scripts across various *nix
systems such as BSD-like distributions.

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

Signed-off-by: Luís Ferreira <contact@lsferreira.net>
Signed-off-by: Romain Vimont <rom@rom1v.com>
2020-09-15 13:52:50 +02:00
cf7bf3148c Use "/usr/bin/env bash" for build-wrapper.sh
PR #426 <https://github.com/Genymobile/scrcpy/pull/426>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2020-09-15 13:44:02 +02:00
ae758f99d6 Adapt call() on ContentProvider for Android 11
This commit in AOSP framework_base added a parameter "attributionTag" to
the call() method:
12ac3f406f%5E%21/#F17

As a consequence, the method did not exist, so scrcpy used the legacy
call() method (using the authority "unknown") as a fallback, which fails
for security reasons.

Fixes #1468 <https://github.com/Genymobile/scrcpy/issues/1468>
2020-09-15 13:31:10 +02:00
bd9f656933 Fix feature test macro
The expected feature test macro is _POSIX_C_SOURCE having a value
greater or equal to 200809L.

Fixes #1726 <https://github.com/Genymobile/scrcpy/issues/1726>
2020-08-31 14:02:51 +02:00
99 changed files with 2279 additions and 533 deletions

1
.gitignore vendored
View File

@ -6,3 +6,4 @@ build/
.idea/
.gradle/
/x/
local.properties

View File

@ -254,10 +254,10 @@ You can then [run](README.md#run) _scrcpy_.
## Prebuilt server
- [`scrcpy-server-v1.16`][direct-scrcpy-server]
_(SHA-256: 94a79e05b4498d0460ab7bd9d12cbf05156e3a47bf0c5d1420cee1d4493b3832)_
- [`scrcpy-server-v1.17`][direct-scrcpy-server]
_(SHA-256: 11b5ad2d1bc9b9730fb7254a78efd71a8ff46b1938ff468e47a21b653a1b6725_
[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v1.16/scrcpy-server-v1.16
[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v1.17/scrcpy-server-v1.17
Download the prebuilt server somewhere, and specify its path during the Meson
configuration:

View File

@ -3,16 +3,16 @@
다음은 자주 제보되는 문제들과 그들의 현황입니다.
### Window 운영체제에서, 디바이스가 발견되지 않습니다.
### Windows 운영체제에서, 디바이스가 발견되지 않습니다.
가장 흔한 제보는 `adb`에 발견되지 않는 디바이스 혹은 권한 관련 문제입니다.
다음 명령어를 호출하여 모든 것들에 이상이 없는지 확인하세요:
adb devices
Window는 당신의 디바이스를 감지하기 위해 [drivers]가 필요할 수도 있습니다.
Windows는 당신의 디바이스를 감지하기 위해 [드라이버]가 필요할 수도 있습니다.
[drivers]: https://developer.android.com/studio/run/oem-usb.html
[드라이버]: https://developer.android.com/studio/run/oem-usb.html
### 내 디바이스의 미러링만 가능하고, 디바이스와 상호작용을 할 수 없습니다.

38
FAQ.md
View File

@ -199,3 +199,41 @@ scrcpy -m 1920
scrcpy -m 1024
scrcpy -m 800
```
You could also try another [encoder](README.md#encoder).
## Command line on Windows
Some Windows users are not familiar with the command line. Here is how to open a
terminal and run `scrcpy` with arguments:
1. Press <kbd>Windows</kbd>+<kbd>r</kbd>, this opens a dialog box.
2. Type `cmd` and press <kbd>Enter</kbd>, this opens a terminal.
3. Go to your _scrcpy_ directory, by typing (adapt the path):
```bat
cd C:\Users\user\Downloads\scrcpy-win64-xxx
```
and press <kbd>Enter</kbd>
4. Type your command. For example:
```bat
scrcpy --record file.mkv
```
If you plan to always use the same arguments, create a file `myscrcpy.bat`
(enable [show file extensions] to avoid confusion) in the `scrcpy` directory,
containing your command. For example:
```bat
scrcpy --prefer-text --turn-screen-off --stay-awake
```
Then just double-click on that file.
You could also edit (a copy of) `scrcpy-console.bat` or `scrcpy-noconsole.vbs`
to add some arguments.
[show file extensions]: https://www.howtogeek.com/205086/beginner-how-to-make-windows-show-file-extensions/

View File

@ -188,7 +188,7 @@
identification within third-party archives.
Copyright (C) 2018 Genymobile
Copyright (C) 2018-2020 Romain Vimont
Copyright (C) 2018-2021 Romain Vimont
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

699
README.id.md Normal file
View File

@ -0,0 +1,699 @@
_Only the original [README](README.md) is guaranteed to be up-to-date._
# scrcpy (v1.16)
Aplikasi ini menyediakan tampilan dan kontrol perangkat Android yang terhubung pada USB (atau [melalui TCP/IP][article-tcpip]). Ini tidak membutuhkan akses _root_ apa pun. Ini bekerja pada _GNU/Linux_, _Windows_ and _macOS_.
![screenshot](assets/screenshot-debian-600.jpg)
Ini berfokus pada:
- **keringanan** (asli, hanya menampilkan layar perangkat)
- **kinerja** (30~60fps)
- **kualitas** (1920×1080 atau lebih)
- **latensi** rendah ([35~70ms][lowlatency])
- **waktu startup rendah** (~1 detik untuk menampilkan gambar pertama)
- **tidak mengganggu** (tidak ada yang terpasang di perangkat)
[lowlatency]: https://github.com/Genymobile/scrcpy/pull/646
## Persyaratan
Perangkat Android membutuhkan setidaknya API 21 (Android 5.0).
Pastikan Anda [mengaktifkan debugging adb][enable-adb] pada perangkat Anda.
[enable-adb]: https://developer.android.com/studio/command-line/adb.html#Enabling
Di beberapa perangkat, Anda juga perlu mengaktifkan [opsi tambahan][control] untuk mengontrolnya menggunakan keyboard dan mouse.
[control]: https://github.com/Genymobile/scrcpy/issues/70#issuecomment-373286323
## Dapatkan aplikasinya
### Linux
Di Debian (_testing_ dan _sid_ untuk saat ini) dan Ubuntu (20.04):
```
apt install scrcpy
```
Paket [Snap] tersedia: [`scrcpy`][snap-link].
[snap-link]: https://snapstats.org/snaps/scrcpy
[snap]: https://en.wikipedia.org/wiki/Snappy_(package_manager)
Untuk Fedora, paket [COPR] tersedia: [`scrcpy`][copr-link].
[COPR]: https://fedoraproject.org/wiki/Category:Copr
[copr-link]: https://copr.fedorainfracloud.org/coprs/zeno/scrcpy/
Untuk Arch Linux, paket [AUR] tersedia: [`scrcpy`][aur-link].
[AUR]: https://wiki.archlinux.org/index.php/Arch_User_Repository
[aur-link]: https://aur.archlinux.org/packages/scrcpy/
Untuk Gentoo, tersedia [Ebuild]: [`scrcpy/`][ebuild-link].
[Ebuild]: https://wiki.gentoo.org/wiki/Ebuild
[ebuild-link]: https://github.com/maggu2810/maggu2810-overlay/tree/master/app-mobilephone/scrcpy
Anda juga bisa [membangun aplikasi secara manual][BUILD] (jangan khawatir, tidak terlalu sulit).
### Windows
Untuk Windows, untuk kesederhanaan, arsip prebuilt dengan semua dependensi (termasuk `adb`) tersedia :
- [`scrcpy-win64-v1.16.zip`][direct-win64]
_(SHA-256: 3f30dc5db1a2f95c2b40a0f5de91ec1642d9f53799250a8c529bc882bc0918f0)_
[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v1.16/scrcpy-win64-v1.16.zip
Ini juga tersedia di [Chocolatey]:
[Chocolatey]: https://chocolatey.org/
```bash
choco install scrcpy
choco install adb # jika Anda belum memilikinya
```
Dan di [Scoop]:
```bash
scoop install scrcpy
scoop install adb # jika Anda belum memilikinya
```
[Scoop]: https://scoop.sh
Anda juga dapat [membangun aplikasi secara manual][BUILD].
### macOS
Aplikasi ini tersedia di [Homebrew]. Instal saja:
[Homebrew]: https://brew.sh/
```bash
brew install scrcpy
```
Anda membutuhkan `adb`, dapat diakses dari `PATH` Anda. Jika Anda belum memilikinya:
```bash
brew cask install android-platform-tools
```
Anda juga dapat [membangun aplikasi secara manual][BUILD].
## Menjalankan
Pasang perangkat Android, dan jalankan:
```bash
scrcpy
```
Ini menerima argumen baris perintah, didaftarkan oleh:
```bash
scrcpy --help
```
## Fitur
### Menangkap konfigurasi
#### Mengurangi ukuran
Kadang-kadang, berguna untuk mencerminkan perangkat Android dengan definisi yang lebih rendah untuk meningkatkan kinerja.
Untuk membatasi lebar dan tinggi ke beberapa nilai (mis. 1024):
```bash
scrcpy --max-size 1024
scrcpy -m 1024 # versi pendek
```
Dimensi lain dihitung agar rasio aspek perangkat dipertahankan.
Dengan begitu, perangkat 1920×1080 akan dicerminkan pada 1024×576.
#### Ubah kecepatan bit
Kecepatan bit default adalah 8 Mbps. Untuk mengubah bitrate video (mis. Menjadi 2 Mbps):
```bash
scrcpy --bit-rate 2M
scrcpy -b 2M # versi pendek
```
#### Batasi frekuensi gambar
Kecepatan bingkai pengambilan dapat dibatasi:
```bash
scrcpy --max-fps 15
```
Ini secara resmi didukung sejak Android 10, tetapi dapat berfungsi pada versi sebelumnya.
#### Memotong
Layar perangkat dapat dipotong untuk mencerminkan hanya sebagian dari layar.
Ini berguna misalnya untuk mencerminkan hanya satu mata dari Oculus Go:
```bash
scrcpy --crop 1224:1440:0:0 # 1224x1440 Mengimbangi (0,0)
```
Jika `--max-size` juga ditentukan, pengubahan ukuran diterapkan setelah pemotongan.
#### Kunci orientasi video
Untuk mengunci orientasi pencerminan:
```bash
scrcpy --lock-video-orientation 0 # orientasi alami
scrcpy --lock-video-orientation 1 # 90° berlawanan arah jarum jam
scrcpy --lock-video-orientation 2 # 180°
scrcpy --lock-video-orientation 3 # 90° searah jarum jam
```
Ini mempengaruhi orientasi perekaman.
### Rekaman
Anda dapat merekam layar saat melakukan mirroring:
```bash
scrcpy --record file.mp4
scrcpy -r file.mkv
```
Untuk menonaktifkan pencerminan saat merekam:
```bash
scrcpy --no-display --record file.mp4
scrcpy -Nr file.mkv
# berhenti merekam dengan Ctrl+C
```
"Skipped frames" are recorded, even if they are not displayed in real time (for
performance reasons). Frames are _timestamped_ on the device, so [packet delay
variation] does not impact the recorded file.
"Frame yang dilewati" direkam, meskipun tidak ditampilkan secara real time (untuk alasan performa). Bingkai *diberi stempel waktu* pada perangkat, jadi [variasi penundaan paket] tidak memengaruhi file yang direkam.
[variasi penundaan paket]: https://en.wikipedia.org/wiki/Packet_delay_variation
### Koneksi
#### Wireless
_Scrcpy_ menggunakan `adb` untuk berkomunikasi dengan perangkat, dan` adb` dapat [terhubung] ke perangkat melalui TCP / IP:
1. Hubungkan perangkat ke Wi-Fi yang sama dengan komputer Anda.
2. Dapatkan alamat IP perangkat Anda (dalam Pengaturan → Tentang ponsel → Status).
3. Aktifkan adb melalui TCP / IP pada perangkat Anda: `adb tcpip 5555`.
4. Cabut perangkat Anda.
5. Hubungkan ke perangkat Anda: `adb connect DEVICE_IP: 5555` (*ganti* *`DEVICE_IP`*).
6. Jalankan `scrcpy` seperti biasa.
Mungkin berguna untuk menurunkan kecepatan bit dan definisi:
```bash
scrcpy --bit-rate 2M --max-size 800
scrcpy -b2M -m800 # versi pendek
```
[terhubung]: https://developer.android.com/studio/command-line/adb.html#wireless
#### Multi-perangkat
Jika beberapa perangkat dicantumkan di `adb devices`, Anda harus menentukan _serial_:
```bash
scrcpy --serial 0123456789abcdef
scrcpy -s 0123456789abcdef # versi pendek
```
If the device is connected over TCP/IP:
```bash
scrcpy --serial 192.168.0.1:5555
scrcpy -s 192.168.0.1:5555 # versi pendek
```
Anda dapat memulai beberapa contoh _scrcpy_ untuk beberapa perangkat.
#### Mulai otomatis pada koneksi perangkat
Anda bisa menggunakan [AutoAdb]:
```bash
autoadb scrcpy -s '{}'
```
[AutoAdb]: https://github.com/rom1v/autoadb
#### Koneksi via SSH tunnel
Untuk menyambung ke perangkat jarak jauh, dimungkinkan untuk menghubungkan klien `adb` lokal ke server `adb` jarak jauh (asalkan mereka menggunakan versi yang sama dari _adb_ protocol):
```bash
adb kill-server # matikan server adb lokal di 5037
ssh -CN -L5037:localhost:5037 -R27183:localhost:27183 komputer_jarak_jauh_anda
# jaga agar tetap terbuka
```
Dari terminal lain:
```bash
scrcpy
```
Untuk menghindari mengaktifkan penerusan port jarak jauh, Anda dapat memaksa sambungan maju sebagai gantinya (perhatikan `-L`, bukan` -R`):
```bash
adb kill-server # matikan server adb lokal di 5037
ssh -CN -L5037:localhost:5037 -L27183:localhost:27183 komputer_jarak_jauh_anda
# jaga agar tetap terbuka
```
Dari terminal lain:
```bash
scrcpy --force-adb-forward
```
Seperti koneksi nirkabel, mungkin berguna untuk mengurangi kualitas:
```
scrcpy -b2M -m800 --max-fps 15
```
### Konfigurasi Jendela
#### Judul
Secara default, judul jendela adalah model perangkat. Itu bisa diubah:
```bash
scrcpy --window-title 'Perangkat Saya'
```
#### Posisi dan ukuran
Posisi dan ukuran jendela awal dapat ditentukan:
```bash
scrcpy --window-x 100 --window-y 100 --window-width 800 --window-height 600
```
#### Jendela tanpa batas
Untuk menonaktifkan dekorasi jendela:
```bash
scrcpy --window-borderless
```
#### Selalu di atas
Untuk menjaga jendela scrcpy selalu di atas:
```bash
scrcpy --always-on-top
```
#### Layar penuh
Aplikasi dapat dimulai langsung dalam layar penuh::
```bash
scrcpy --fullscreen
scrcpy -f # versi pendek
```
Layar penuh kemudian dapat diubah secara dinamis dengan <kbd>MOD</kbd>+<kbd>f</kbd>.
#### Rotasi
Jendela mungkin diputar:
```bash
scrcpy --rotation 1
```
Nilai yang mungkin adalah:
- `0`: tidak ada rotasi
- `1`: 90 derajat berlawanan arah jarum jam
- `2`: 180 derajat
- `3`: 90 derajat searah jarum jam
Rotasi juga dapat diubah secara dinamis dengan <kbd>MOD</kbd>+<kbd></kbd>
_(kiri)_ and <kbd>MOD</kbd>+<kbd></kbd> _(kanan)_.
Perhatikan bahwa _scrcpy_ mengelola 3 rotasi berbeda::
- <kbd>MOD</kbd>+<kbd>r</kbd> meminta perangkat untuk beralih antara potret dan lanskap (aplikasi yang berjalan saat ini mungkin menolak, jika mendukung orientasi yang diminta).
- `--lock-video-orientation` mengubah orientasi pencerminan (orientasi video yang dikirim dari perangkat ke komputer). Ini mempengaruhi rekaman.
- `--rotation` (atau <kbd>MOD</kbd>+<kbd></kbd>/<kbd>MOD</kbd>+<kbd></kbd>)
memutar hanya konten jendela. Ini hanya mempengaruhi tampilan, bukan rekaman.
### Opsi pencerminan lainnya
#### Hanya-baca
Untuk menonaktifkan kontrol (semua yang dapat berinteraksi dengan perangkat: tombol input, peristiwa mouse, seret & lepas file):
```bash
scrcpy --no-control
scrcpy -n
```
#### Layar
Jika beberapa tampilan tersedia, Anda dapat memilih tampilan untuk cermin:
```bash
scrcpy --display 1
```
Daftar id tampilan dapat diambil dengan::
```
adb shell dumpsys display # cari "mDisplayId=" di keluaran
```
Tampilan sekunder hanya dapat dikontrol jika perangkat menjalankan setidaknya Android 10 (jika tidak maka akan dicerminkan dalam hanya-baca).
#### Tetap terjaga
Untuk mencegah perangkat tidur setelah beberapa penundaan saat perangkat dicolokkan:
```bash
scrcpy --stay-awake
scrcpy -w
```
Keadaan awal dipulihkan ketika scrcpy ditutup.
#### Matikan layar
Dimungkinkan untuk mematikan layar perangkat saat pencerminan mulai dengan opsi baris perintah:
```bash
scrcpy --turn-screen-off
scrcpy -S
```
Atau dengan menekan <kbd>MOD</kbd>+<kbd>o</kbd> kapan saja.
Untuk menyalakannya kembali, tekan <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>o</kbd>.
Di Android, tombol `POWER` selalu menyalakan layar. Untuk kenyamanan, jika `POWER` dikirim melalui scrcpy (melalui klik kanan atau<kbd>MOD</kbd>+<kbd>p</kbd>), itu akan memaksa untuk mematikan layar setelah penundaan kecil (atas dasar upaya terbaik).
Tombol fisik `POWER` masih akan menyebabkan layar dihidupkan.
Ini juga berguna untuk mencegah perangkat tidur:
```bash
scrcpy --turn-screen-off --stay-awake
scrcpy -Sw
```
#### Render frame kedaluwarsa
Secara default, untuk meminimalkan latensi, _scrcpy_ selalu menampilkan frame yang terakhir didekodekan tersedia, dan menghapus frame sebelumnya.
Untuk memaksa rendering semua frame (dengan kemungkinan peningkatan latensi), gunakan:
```bash
scrcpy --render-expired-frames
```
#### Tunjukkan sentuhan
Untuk presentasi, mungkin berguna untuk menunjukkan sentuhan fisik (pada perangkat fisik).
Android menyediakan fitur ini di _Opsi Pengembang_.
_Scrcpy_ menyediakan opsi untuk mengaktifkan fitur ini saat mulai dan mengembalikan nilai awal saat keluar:
```bash
scrcpy --show-touches
scrcpy -t
```
Perhatikan bahwa ini hanya menunjukkan sentuhan _fisik_ (dengan jari di perangkat).
#### Nonaktifkan screensaver
Secara default, scrcpy tidak mencegah screensaver berjalan di komputer.
Untuk menonaktifkannya:
```bash
scrcpy --disable-screensaver
```
### Kontrol masukan
#### Putar layar perangkat
Tekan <kbd>MOD</kbd>+<kbd>r</kbd> untuk beralih antara mode potret dan lanskap.
Perhatikan bahwa itu berputar hanya jika aplikasi di latar depan mendukung orientasi yang diminta.
#### Salin-tempel
Setiap kali papan klip Android berubah, secara otomatis disinkronkan ke papan klip komputer.
Apa saja <kbd>Ctrl</kbd> pintasan diteruskan ke perangkat. Khususnya:
- <kbd>Ctrl</kbd>+<kbd>c</kbd> biasanya salinan
- <kbd>Ctrl</kbd>+<kbd>x</kbd> biasanya memotong
- <kbd>Ctrl</kbd>+<kbd>v</kbd> biasanya menempel (setelah sinkronisasi papan klip komputer-ke-perangkat)
Ini biasanya berfungsi seperti yang Anda harapkan.
Perilaku sebenarnya tergantung pada aplikasi yang aktif. Sebagai contoh,
_Termux_ mengirim SIGINT ke <kbd>Ctrl</kbd>+<kbd>c</kbd> sebagai gantinya, dan _K-9 Mail_ membuat pesan baru.
Untuk menyalin, memotong dan menempel dalam kasus seperti itu (tetapi hanya didukung di Android> = 7):
- <kbd>MOD</kbd>+<kbd>c</kbd> injeksi `COPY` _(salin)_
- <kbd>MOD</kbd>+<kbd>x</kbd> injeksi `CUT` _(potong)_
- <kbd>MOD</kbd>+<kbd>v</kbd> injeksi `PASTE` (setelah sinkronisasi papan klip komputer-ke-perangkat)
Tambahan, <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>v</kbd> memungkinkan untuk memasukkan teks papan klip komputer sebagai urutan peristiwa penting. Ini berguna ketika komponen tidak menerima penempelan teks (misalnya di _Termux_), tetapi dapat merusak konten non-ASCII.
**PERINGATAN:** Menempelkan papan klip komputer ke perangkat (baik melalui
<kbd>Ctrl</kbd>+<kbd>v</kbd> or <kbd>MOD</kbd>+<kbd>v</kbd>) menyalin konten ke clipboard perangkat. Akibatnya, aplikasi Android apa pun dapat membaca kontennya. Anda harus menghindari menempelkan konten sensitif (seperti kata sandi) seperti itu.
#### Cubit untuk memperbesar/memperkecil
Untuk mensimulasikan "cubit-untuk-memperbesar/memperkecil": <kbd>Ctrl</kbd>+_klik-dan-pindah_.
Lebih tepatnya, tahan <kbd>Ctrl</kbd> sambil menekan tombol klik kiri. Hingga tombol klik kiri dilepaskan, semua gerakan mouse berskala dan memutar konten (jika didukung oleh aplikasi) relatif ke tengah layar.
Secara konkret, scrcpy menghasilkan kejadian sentuh tambahan dari "jari virtual" di lokasi yang dibalik melalui bagian tengah layar.
#### Preferensi injeksi teks
Ada dua jenis [peristiwa][textevents] dihasilkan saat mengetik teks:
- _peristiwa penting_, menandakan bahwa tombol ditekan atau dilepaskan;
- _peristiwa teks_, menandakan bahwa teks telah dimasukkan.
Secara default, huruf dimasukkan menggunakan peristiwa kunci, sehingga keyboard berperilaku seperti yang diharapkan dalam game (biasanya untuk tombol WASD).
Tapi ini mungkin [menyebabkan masalah][prefertext]. Jika Anda mengalami masalah seperti itu, Anda dapat menghindarinya dengan:
```bash
scrcpy --prefer-text
```
(tapi ini akan merusak perilaku keyboard dalam game)
[textevents]: https://blog.rom1v.com/2018/03/introducing-scrcpy/#handle-text-input
[prefertext]: https://github.com/Genymobile/scrcpy/issues/650#issuecomment-512945343
#### Ulangi kunci
Secara default, menahan tombol akan menghasilkan peristiwa kunci yang berulang. Ini dapat menyebabkan masalah kinerja di beberapa game, di mana acara ini tidak berguna.
Untuk menghindari penerusan peristiwa penting yang berulang:
```bash
scrcpy --no-key-repeat
```
### Seret/jatuhkan file
#### Pasang APK
Untuk menginstal APK, seret & lepas file APK (diakhiri dengan `.apk`) ke jendela _scrcpy_.
Tidak ada umpan balik visual, log dicetak ke konsol.
#### Dorong file ke perangkat
Untuk mendorong file ke `/sdcard/` di perangkat, seret & jatuhkan file (non-APK) ke jendela _scrcpy_.
Tidak ada umpan balik visual, log dicetak ke konsol.
Direktori target dapat diubah saat mulai:
```bash
scrcpy --push-target /sdcard/foo/bar/
```
### Penerusan audio
Audio tidak diteruskan oleh _scrcpy_. Gunakan [sndcpy].
Lihat juga [Masalah #14].
[sndcpy]: https://github.com/rom1v/sndcpy
[Masalah #14]: https://github.com/Genymobile/scrcpy/issues/14
## Pintasan
Dalam daftar berikut, <kbd>MOD</kbd> adalah pengubah pintasan. Secara default, ini (kiri) <kbd>Alt</kbd> atau (kiri) <kbd>Super</kbd>.
Ini dapat diubah menggunakan `--shortcut-mod`. Kunci yang memungkinkan adalah `lctrl`,`rctrl`, `lalt`,` ralt`, `lsuper` dan` rsuper`. Sebagai contoh:
```bash
# gunakan RCtrl untuk jalan pintas
scrcpy --shortcut-mod=rctrl
# gunakan baik LCtrl+LAlt atau LSuper untuk jalan pintas
scrcpy --shortcut-mod=lctrl+lalt,lsuper
```
_<kbd>[Super]</kbd> biasanya adalah <kbd>Windows</kbd> atau <kbd>Cmd</kbd> key._
[Super]: https://en.wikipedia.org/wiki/Super_key_(keyboard_button)
| Aksi | Pintasan
| ------------------------------------------------------|:-----------------------------
| Alihkan mode layar penuh | <kbd>MOD</kbd>+<kbd>f</kbd>
| Putar layar kiri | <kbd>MOD</kbd>+<kbd></kbd> _(kiri)_
| Putar layar kanan | <kbd>MOD</kbd>+<kbd></kbd> _(kanan)_
| Ubah ukuran jendela menjadi 1:1 (piksel-sempurna) | <kbd>MOD</kbd>+<kbd>g</kbd>
| Ubah ukuran jendela menjadi hapus batas hitam | <kbd>MOD</kbd>+<kbd>w</kbd> \| _klik-dua-kali¹_
| Klik `HOME` | <kbd>MOD</kbd>+<kbd>h</kbd> \| _Klik-tengah_
| Klik `BACK` | <kbd>MOD</kbd>+<kbd>b</kbd> \| _Klik-kanan²_
| Klik `APP_SWITCH` | <kbd>MOD</kbd>+<kbd>s</kbd>
| Klik `MENU` (buka kunci layar) | <kbd>MOD</kbd>+<kbd>m</kbd>
| Klik `VOLUME_UP` | <kbd>MOD</kbd>+<kbd></kbd> _(naik)_
| Klik `VOLUME_DOWN` | <kbd>MOD</kbd>+<kbd></kbd> _(turun)_
| Klik `POWER` | <kbd>MOD</kbd>+<kbd>p</kbd>
| Menyalakan | _Klik-kanan²_
| Matikan layar perangkat (tetap mirroring) | <kbd>MOD</kbd>+<kbd>o</kbd>
| Hidupkan layar perangkat | <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>o</kbd>
| Putar layar perangkat | <kbd>MOD</kbd>+<kbd>r</kbd>
| Luaskan panel notifikasi | <kbd>MOD</kbd>+<kbd>n</kbd>
| Ciutkan panel notifikasi | <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>n</kbd>
| Menyalin ke papan klip³ | <kbd>MOD</kbd>+<kbd>c</kbd>
| Potong ke papan klip³ | <kbd>MOD</kbd>+<kbd>x</kbd>
| Sinkronkan papan klip dan tempel³ | <kbd>MOD</kbd>+<kbd>v</kbd>
| Masukkan teks papan klip komputer | <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>v</kbd>
| Mengaktifkan/menonaktifkan penghitung FPS (di stdout) | <kbd>MOD</kbd>+<kbd>i</kbd>
| Cubit-untuk-memperbesar/memperkecil | <kbd>Ctrl</kbd>+_klik-dan-pindah_
_¹Klik-dua-kali pada batas hitam untuk menghapusnya._
_²Klik-kanan akan menghidupkan layar jika mati, tekan BACK jika tidak._
_³Hanya di Android >= 7._
Semua <kbd>Ctrl</kbd>+_key_ pintasan diteruskan ke perangkat, demikian adanya
ditangani oleh aplikasi aktif.
## Jalur kustom
Untuk menggunakan biner _adb_ tertentu, konfigurasikan jalurnya di variabel lingkungan `ADB`:
ADB=/path/to/adb scrcpy
Untuk mengganti jalur file `scrcpy-server`, konfigurasikan jalurnya di
`SCRCPY_SERVER_PATH`.
[useful]: https://github.com/Genymobile/scrcpy/issues/278#issuecomment-429330345
## Mengapa _scrcpy_?
Seorang kolega menantang saya untuk menemukan nama yang tidak dapat diucapkan seperti [gnirehtet].
[`strcpy`] menyalin sebuah **str**ing; `scrcpy` menyalin sebuah **scr**een.
[gnirehtet]: https://github.com/Genymobile/gnirehtet
[`strcpy`]: http://man7.org/linux/man-pages/man3/strcpy.3.html
## Bagaimana Cara membangun?
Lihat [BUILD].
[BUILD]: BUILD.md
## Masalah umum
Lihat [FAQ](FAQ.md).
## Pengembang
Baca [halaman pengembang].
[halaman pengembang]: DEVELOP.md
## Lisensi
Copyright (C) 2018 Genymobile
Copyright (C) 2018-2021 Romain Vimont
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
## Artikel
- [Introducing scrcpy][article-intro]
- [Scrcpy now works wirelessly][article-tcpip]
[article-intro]: https://blog.rom1v.com/2018/03/introducing-scrcpy/
[article-tcpip]: https://www.genymotion.com/blog/open-source-project-scrcpy-now-works-wirelessly/

View File

@ -477,7 +477,7 @@ _²화면이 꺼진 상태에서 우클릭 시 다시 켜지며, 그 외의 상
## 라이선스
Copyright (C) 2018 Genymobile
Copyright (C) 2018-2020 Romain Vimont
Copyright (C) 2018-2021 Romain Vimont
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -1,4 +1,4 @@
# scrcpy (v1.16)
# scrcpy (v1.17)
[Read in another language](#translations)
@ -77,10 +77,10 @@ hard).
For Windows, for simplicity, a prebuilt archive with all the dependencies
(including `adb`) is available:
- [`scrcpy-win64-v1.16.zip`][direct-win64]
_(SHA-256: 3f30dc5db1a2f95c2b40a0f5de91ec1642d9f53799250a8c529bc882bc0918f0)_
- [`scrcpy-win64-v1.17.zip`][direct-win64]
_(SHA-256: 8b9e57993c707367ed10ebfe0e1ef563c7a29d9af4a355cd8b6a52a317c73eea)_
[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v1.16/scrcpy-win64-v1.16.zip
[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v1.17/scrcpy-win64-v1.17.zip
It is also available in [Chocolatey]:
@ -116,6 +116,10 @@ brew install scrcpy
You need `adb`, accessible from your `PATH`. If you don't have it yet:
```bash
# Homebrew >= 2.6.0
brew install --cask android-platform-tools
# Homebrew < 2.6.0
brew cask install android-platform-tools
```
@ -202,6 +206,24 @@ scrcpy --lock-video-orientation 3 # 90° clockwise
This affects recording orientation.
The [window may also be rotated](#rotation) independently.
#### Encoder
Some devices have more than one encoder, and some of them may cause issues or
crash. It is possible to select a different encoder:
```bash
scrcpy --encoder OMX.qcom.video.encoder.avc
```
To list the available encoders, you could pass an invalid encoder name, the
error will give the available encoders:
```bash
scrcpy --encoder _
```
### Recording
@ -235,7 +257,13 @@ _Scrcpy_ uses `adb` to communicate with the device, and `adb` can [connect] to a
device over TCP/IP:
1. Connect the device to the same Wi-Fi as your computer.
2. Get your device IP address (in Settings → About phone → Status).
2. Get your device IP address, in Settings → About phone → Status, or by
executing this command:
```bash
adb shell ip route | awk '{print $9}'
```
3. Enable adb over TCP/IP on your device: `adb tcpip 5555`.
4. Unplug your device.
5. Connect to your device: `adb connect DEVICE_IP:5555` _(replace `DEVICE_IP`)_.
@ -382,12 +410,12 @@ The rotation can also be changed dynamically with <kbd>MOD</kbd>+<kbd>←</kbd>
_(left)_ and <kbd>MOD</kbd>+<kbd>→</kbd> _(right)_.
Note that _scrcpy_ manages 3 different rotations:
- <kbd>MOD</kbd>+<kbd>r</kbd> requests the device to switch between portrait and
landscape (the current running app may refuse, if it does support the
requested orientation).
- `--lock-video-orientation` changes the mirroring orientation (the orientation
of the video sent from the device to the computer). This affects the
recording.
- <kbd>MOD</kbd>+<kbd>r</kbd> requests the device to switch between portrait
and landscape (the current running app may refuse, if it does support the
requested orientation).
- [`--lock-video-orientation`](#lock-video-orientation) changes the mirroring
orientation (the orientation of the video sent from the device to the
computer). This affects the recording.
- `--rotation` (or <kbd>MOD</kbd>+<kbd>←</kbd>/<kbd>MOD</kbd>+<kbd>→</kbd>)
rotates only the window content. This affects only the display, not the
recording.
@ -548,6 +576,11 @@ into the device clipboard. As a consequence, any Android application could read
its content. You should avoid to paste sensitive content (like passwords) that
way.
Some devices do not behave as expected when setting the device clipboard
programmatically. An option `--legacy-paste` is provided to change the behavior
of <kbd>Ctrl</kbd>+<kbd>v</kbd> and <kbd>MOD</kbd>+<kbd>v</kbd> so that they
also inject the computer clipboard text as a sequence of key events (the same
way as <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>v</kbd>).
#### Pinch-to-zoom
@ -595,6 +628,16 @@ scrcpy --no-key-repeat
```
#### Right-click and middle-click
By default, right-click triggers BACK (or POWER on) and middle-click triggers
HOME. To disable these shortcuts and forward the clicks to the device instead:
```bash
scrcpy --forward-all-clicks
```
### File drop
#### Install APK
@ -729,7 +772,7 @@ Read the [developers page].
## Licence
Copyright (C) 2018 Genymobile
Copyright (C) 2018-2020 Romain Vimont
Copyright (C) 2018-2021 Romain Vimont
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -755,8 +798,10 @@ Read the [developers page].
This README is available in other languages:
- [繁體中文 (Traditional Chinese, `zh-Hant`) - v1.15](README.zh-Hant.md)
- [Indonesian (Indonesia, `id`) - v1.16](README.id.md)
- [한국어 (Korean, `ko`) - v1.11](README.ko.md)
- [português brasileiro (Brazilian Portuguese, `pt-BR`) - v1.12.1](README.pt-br.md)
- [简体中文 (Simplified Chinese, `zh-Hans`) - v1.16](README.zh-Hans.md)
- [繁體中文 (Traditional Chinese, `zh-Hant`) - v1.15](README.zh-Hant.md)
Only this README file is guaranteed to be up-to-date.

View File

@ -508,7 +508,7 @@ Leia a [developers page].
## Licença
Copyright (C) 2018 Genymobile
Copyright (C) 2018-2020 Romain Vimont
Copyright (C) 2018-2021 Romain Vimont
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

726
README.zh-Hans.md Normal file
View File

@ -0,0 +1,726 @@
_Only the original [README](README.md) is guaranteed to be up-to-date._
只有原版的[README](README.md)会保持最新。
本文根据[479d10d]进行翻译。
[479d10d]: https://github.com/Genymobile/scrcpy/commit/479d10dc22b70272187e0963c6ad24d754a669a2#diff-04c6e90faac2675aa89e2176d2eec7d8
# scrcpy (v1.16)
本应用程序可以通过USB或 [TCP/IP][article-tcpip] )连接用于显示或控制安卓设备。这不需要获取 _root_ 权限。
该应用程序可以在 _GNU/Linux_, _Windows__macOS_ 环境下运行。
[article-tcpip]:https://www.genymotion.com/blog/open-source-project-scrcpy-now-works-wirelessly/
![screenshot](assets/screenshot-debian-600.jpg)
它专注于:
- **轻量** (原生,仅显示设备屏幕)
- **性能** 30~60fps
- **质量** 分辨率可达1920x1080或更高
- **低延迟** (35-70ms)
- **快速启动** (数秒内即能开始显示)
- **无侵入性** (不需要在安卓设备上安装任何程序)
## 使用要求
安卓设备系统版本需要在Android 5.0API 21)或以上。
确保您在设备上开启了[adb调试]。
[adb调试]: https://developer.android.com/studio/command-line/adb.html#Enabling
在某些设备上,你还需要开启[额外的选项]以用鼠标和键盘进行控制。
[额外的选项]: https://github.com/Genymobile/scrcpy/issues/70#issuecomment-373286323
## 获取scrcpy
<a href="https://repology.org/project/scrcpy/versions"><img src="https://repology.org/badge/vertical-allrepos/scrcpy.svg" alt="Packaging status" align="right"></a>
### Linux
在Debian目前仅测试版和不稳定版_testing__sid_ 版本和Ubuntu 20.04)上:
```
apt install scrcpy
```
[Snap]包也是可用的: [`scrcpy`][snap-link].
[snap-link]: https://snapstats.org/snaps/scrcpy
[snap]: https://en.wikipedia.org/wiki/Snappy_(package_manager)
对于Fedora用户我们提供[COPR]包: [`scrcpy`][copr-link].
[COPR]: https://fedoraproject.org/wiki/Category:Copr
[copr-link]: https://copr.fedorainfracloud.org/coprs/zeno/scrcpy/
对于Arch Linux用户我们提供[AUR]包: [`scrcpy`][aur-link].
[AUR]: https://wiki.archlinux.org/index.php/Arch_User_Repository
[aur-link]: https://aur.archlinux.org/packages/scrcpy/
对于Gentoo用户我们提供[Ebuild]包:[`scrcpy/`][ebuild-link].
[Ebuild]: https://wiki.gentoo.org/wiki/Ebuild
[ebuild-link]: https://github.com/maggu2810/maggu2810-overlay/tree/master/app-mobilephone/scrcpy
您也可以[自行编译][编译](不必担心,这并不困难)。
### Windows
在Windows上简便起见我们准备了包含所有依赖项包括adb的程序包。
- [`scrcpy-win64-v1.16.zip`][direct-win64]
_(SHA-256: 3f30dc5db1a2f95c2b40a0f5de91ec1642d9f53799250a8c529bc882bc0918f0)_
[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v1.16/scrcpy-win64-v1.16.zip
您也可以在[Chocolatey]下载:
[Chocolatey]: https://chocolatey.org/
```bash
choco install scrcpy
choco install adb # 如果你没有adb
```
也可以使用 [Scoop]:
```bash
scoop install scrcpy
scoop install adb # 如果你没有adb
```
[Scoop]: https://scoop.sh
您也可以[自行编译][编译]。
### macOS
您可以使用[Homebrew]下载scrcpy。直接安装就可以了
[Homebrew]: https://brew.sh/
```bash
brew install scrcpy
```
您需要 `adb`以使用scrcpy并且它需要可以通过 `PATH`被访问。如果您没有:
```bash
brew cask install android-platform-tools
```
您也可以[自行编译][编译]。
## 运行scrcpy
用USB链接电脑和安卓设备并执行
```bash
scrcpy
```
支持带命令行参数执行,查看参数列表:
```bash
scrcpy --help
```
## 功能介绍
### 画面设置
#### 缩小分辨率
有时候,将设备屏幕镜像分辨率降低可以有效地提升性能。
我们可以将高度和宽度都限制在一定大小内(如 1024
```bash
scrcpy --max-size 1024
scrcpy -m 1024 # short version
```
较短的一边会被按比例缩小以保持设备的显示比例。
这样1920x1080 的设备会以 1024x576 的分辨率显示。
#### 修改画面比特率
默认的比特率是8Mbps。如果要改变画面的比特率 (比如说改成2Mbps)
```bash
scrcpy --bit-rate 2M
scrcpy -b 2M # short version
```
#### 限制画面帧率
画面的帧率可以通过下面的命令被限制:
```bash
scrcpy --max-fps 15
```
这个功能仅在Android 10和以后的版本被Android官方支持但也有可能在更早的版本可用。
#### 画面裁剪
设备画面可在裁切后进行镜像,以显示部分屏幕。
这项功能可以用于例如只显示Oculus Go的一只眼睛。
```bash
scrcpy --crop 1224:1440:0:0 # 1224x1440 at offset (0,0)
```
如果`--max-size`在同时被指定,分辨率的改变将在画面裁切后进行。
#### 锁定屏幕朝向
可以使用如下命令锁定屏幕朝向:
```bash
scrcpy --lock-video-orientation 0 # 自然朝向
scrcpy --lock-video-orientation 1 # 90° 逆时针旋转
scrcpy --lock-video-orientation 2 # 180°
scrcpy --lock-video-orientation 3 # 90° 顺时针旋转
```
该设定影响录制。
### 屏幕录制
可以在屏幕镜像的同时录制视频:
```bash
scrcpy --record file.mp4
scrcpy -r file.mkv
```
在不开启屏幕镜像的同时录制:
```bash
scrcpy --no-display --record file.mp4
scrcpy -Nr file.mkv
# 按Ctrl+C以停止录制
```
在显示中“被跳过的帧”会被录制,虽然它们由于性能原因没有实时显示。
在传输中每一帧都有 _时间戳_ ,所以 [包时延变化] 并不影响录制的文件。
[包时延变化]: https://en.wikipedia.org/wiki/Packet_delay_variation
### 连接方式
#### 无线
_Scrcpy_ 使用`adb`来与安卓设备连接。同时,`adb`能够通过TCP/IP[连接]到安卓设备:
1. 将您的安卓设备和电脑连接至同一Wi-Fi。
2. 获取安卓设备的IP地址在设置-关于手机-状态信息)。
3. 打开安卓设备的网络adb功能`adb tcpip 5555`
4. 将您的设备与电脑断开连接。
5. 连接到您的设备:`adb connect DEVICE_IP:5555` _(用设备IP替换 `DEVICE_IP`)_.
6. 运行`scrcpy`
降低比特率和分辨率可能有助于性能:
```bash
scrcpy --bit-rate 2M --max-size 800
scrcpy -b2M -m800 # short version
```
[连接]: https://developer.android.com/studio/command-line/adb.html#wireless
#### 多设备
如果多个设备在执行`adb devices`后被列出,您必须指定设备的 _序列号_
```bash
scrcpy --serial 0123456789abcdef
scrcpy -s 0123456789abcdef # short version
```
如果设备是通过TCP/IP方式连接到电脑的
```bash
scrcpy --serial 192.168.0.1:5555
scrcpy -s 192.168.0.1:5555 # short version
```
您可以同时启动多个 _scrcpy_ 实例以同时显示多个设备的画面。
#### 在设备连接时自动启动
您可以使用 [AutoAdb]:
```bash
autoadb scrcpy -s '{}'
```
[AutoAdb]: https://github.com/rom1v/autoadb
#### SSH 连接
本地的 adb 可以远程连接到另一个 adb 服务器假设两者的adb版本相同来远程连接到设备
```bash
adb kill-server # 关闭本地5037端口上的adb服务器
ssh -CN -L5037:localhost:5037 -R27183:localhost:27183 your_remote_computer
# 保持该窗口开启
```
从另一个终端:
```bash
scrcpy
```
为了避免启动远程端口转发,你可以强制启动一个转发连接(注意`-L``-R`的区别:
```bash
adb kill-server # kill the local adb server on 5037
ssh -CN -L5037:localhost:5037 -L27183:localhost:27183 your_remote_computer
# 保持该窗口开启
```
从另一个终端:
```bash
scrcpy --force-adb-forward
```
和无线网络连接类似,下列设置可能对改善性能有帮助:
```
scrcpy -b2M -m800 --max-fps 15
```
### 窗口设置
#### 标题
窗口的标题默认为设备型号。您可以通过如下命令修改它:
```bash
scrcpy --window-title 'My device'
```
#### 位置和大小
您可以指定初始的窗口位置和大小:
```bash
scrcpy --window-x 100 --window-y 100 --window-width 800 --window-height 600
```
#### 无边框
关闭边框:
```bash
scrcpy --window-borderless
```
#### 保持窗口在最前
您可以通过如下命令保持窗口在最前面:
```bash
scrcpy --always-on-top
```
#### 全屏
您可以通过如下命令直接全屏启动scrcpy
```bash
scrcpy --fullscreen
scrcpy -f # short version
```
全屏状态可以通过<kbd>MOD</kbd>+<kbd>f</kbd>实时改变。
#### 旋转
通过如下命令,窗口可以旋转:
```bash
scrcpy --rotation 1
```
可选的值有:
- `0`: 无旋转
- `1`: 逆时针旋转90°
- `2`: 旋转180°
- `3`: 顺时针旋转90°
这同样可以使用<kbd>MOD</kbd>+<kbd></kbd>
_(左)_<kbd>MOD</kbd>+<kbd></kbd> _(右)_ 的快捷键实时更改。
需要注意的是, _scrcpy_ 控制三个不同的朝向:
- <kbd>MOD</kbd>+<kbd>r</kbd> 请求设备在竖屏和横屏之间切换(如果前台应用程序不支持所请求的朝向,可能会拒绝该请求)。
- `--lock-video-orientation` 改变镜像的朝向(设备镜像到电脑的画面朝向)。这会影响录制。
- `--rotation` (或<kbd>MOD</kbd>+<kbd></kbd>/<kbd>MOD</kbd>+<kbd></kbd>
只旋转窗口的画面。这只影响显示,不影响录制。
### 其他镜像设置
#### 只读
关闭电脑对设备的控制(如键盘输入、鼠标移动和文件传输):
```bash
scrcpy --no-control
scrcpy -n
```
#### 显示屏
如果有多个显示屏可用,您可以选择特定显示屏进行镜像:
```bash
scrcpy --display 1
```
您可以通过如下命令找到显示屏的id
```
adb shell dumpsys display # 在回显中搜索“mDisplayId=”
```
第二显示屏可能只能在设备运行Android 10或以上的情况下被控制它可能会在电脑上显示但无法通过电脑操作
#### 保持常亮
防止设备在已连接的状态下休眠:
```bash
scrcpy --stay-awake
scrcpy -w
```
程序关闭后,设备设置会恢复原样。
#### 关闭设备屏幕
在启动屏幕镜像时,可以通过如下命令关闭设备的屏幕:
```bash
scrcpy --turn-screen-off
scrcpy -S
```
或者在需要的时候按<kbd>MOD</kbd>+<kbd>o</kbd>
要重新打开屏幕的话,需要按<kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>o</kbd>.
在Android上,`电源`按钮始终能把屏幕打开。
为了方便,如果按下`电源`按钮的事件是通过 _scrcpy_ 发出的(通过点按鼠标右键或<kbd>MOD</kbd>+<kbd>p</kbd>),它会在短暂的延迟后将屏幕关闭。
物理的`电源`按钮仍然能打开设备屏幕。
同时,这项功能还能被用于防止设备休眠:
```bash
scrcpy --turn-screen-off --stay-awake
scrcpy -Sw
```
#### 渲染超时帧
为了降低延迟, _scrcpy_ 默认渲染解码成功的最近一帧,并跳过前面任意帧。
强制渲染所有帧(可能导致延迟变高):
```bash
scrcpy --render-expired-frames
```
#### 显示触摸
在展示时,有些时候可能会用到显示触摸点这项功能(在设备上显示)。
Android在 _开发者设置_ 中提供了这项功能。
_Scrcpy_ 提供一个选项可以在启动时开启这项功能并在退出时恢复初始设置:
```bash
scrcpy --show-touches
scrcpy -t
```
请注意这项功能只能显示 _物理_ 触摸(要用手在屏幕上触摸)。
#### 关闭屏保
_Scrcpy_ 不会默认关闭屏幕保护。
关闭屏幕保护:
```bash
scrcpy --disable-screensaver
```
### 输入控制
#### 旋转设备屏幕
使用<kbd>MOD</kbd>+<kbd>r</kbd>以在竖屏和横屏模式之间切换。
需要注意的是,只有在前台应用程序支持所要求的模式时,才会进行切换。
#### 复制黏贴
每次Android的剪贴板变化的时候它都会被自动同步到电脑的剪贴板上。
所有的 <kbd>Ctrl</kbd> 快捷键都会被转发至设备。其中:
- <kbd>Ctrl</kbd>+<kbd>c</kbd> 复制
- <kbd>Ctrl</kbd>+<kbd>x</kbd> 剪切
- <kbd>Ctrl</kbd>+<kbd>v</kbd> 黏贴 (在电脑到设备的剪贴板同步完成之后)
这通常如您所期望的那样运作。
但实际的行为取决于设备上的前台程序。
例如 _Termux_<kbd>Ctrl</kbd>+<kbd>c</kbd>被按下时发送 SIGINT
又如 _K-9 Mail_ 会新建一封新邮件。
在这种情况下剪切复制黏贴仅在Android >= 7时可用
- <kbd>MOD</kbd>+<kbd>c</kbd> 注入 `COPY`(复制)
- <kbd>MOD</kbd>+<kbd>x</kbd> 注入 `CUT`(剪切)
- <kbd>MOD</kbd>+<kbd>v</kbd> 注入 `PASTE`(黏贴)(在电脑到设备的剪贴板同步完成之后)
另外,<kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>v</kbd>可以将电脑的剪贴板内容转换为一串按键事件输入到设备。
在应用程序不接受黏贴时(比如 _Termux_ ),这项功能可以排上一定的用场。
需要注意的是这项功能可能会导致非ASCII编码的内容出现错误。
**警告:** 将电脑剪贴板的内容黏贴至设备(无论是通过<kbd>Ctrl</kbd>+<kbd>v</kbd>还是<kbd>MOD</kbd>+<kbd>v</kbd>
都需要将内容保存至设备的剪贴板。如此,任何一个应用程序都可以读取它。
您应当避免将敏感内容通过这种方式传输(如密码)。
#### 捏拉缩放
模拟 “捏拉缩放”:<kbd>Ctrl</kbd>+_按住并移动鼠标_。
更准确的说,您需要在按住<kbd>Ctrl</kbd>的同时按住并移动鼠标。
在鼠标左键松开之后,光标的任何操作都会相对于屏幕的中央进行。
具体来说, _scrcpy_ 使用“虚拟手指”以在相对于屏幕中央相反的位置产生触摸事件。
#### 文字注入偏好
打字的时候,系统会产生两种[事件][textevents]
- _按键事件_ ,代表一个按键被按下/松开。
- _文本事件_ ,代表一个文本被输入。
程序默认使用按键事件来输入字母。只有这样键盘才会在游戏中正常运作尤其WASD键
但这也有可能[造成问题][prefertext]。如果您遇到了这样的问题,您可以通过下列操作避免它:
```bash
scrcpy --prefer-text
```
(这会导致键盘在游戏中工作不正常)
[textevents]: https://blog.rom1v.com/2018/03/introducing-scrcpy/#handle-text-input
[prefertext]: https://github.com/Genymobile/scrcpy/issues/650#issuecomment-512945343
#### 按键重复
当你一直按着一个按键不放时,程序默认产生多个按键事件。
在某些游戏中这可能会导致性能问题。
避免转发重复按键事件:
```bash
scrcpy --no-key-repeat
```
### 文件传输
#### 安装APK
如果您要要安装APK请拖放APK文件文件名以`.apk`结尾)到 _scrcpy_ 窗口。
该操作在屏幕上不会出现任何变化,而会在控制台输出一条日志。
#### 将文件推送至设备
如果您要推送文件到设备的 `/sdcard/`请拖放文件至不能是APK文件_scrcpy_ 窗口。
该操作没有可见的响应,只会在控制台输出日志。
在启动时可以修改目标目录:
```bash
scrcpy --push-target /sdcard/foo/bar/
```
### 音频转发
_scrcpy_ 不支持音频。请使用 [sndcpy].
另外请阅读 [issue #14]。
[sndcpy]: https://github.com/rom1v/sndcpy
[issue #14]: https://github.com/Genymobile/scrcpy/issues/14
## 热键
在下列表格中, <kbd>MOD</kbd> 是热键的修饰键。
默认是(左)<kbd>Alt</kbd>或者(左)<kbd>Super</kbd>
您可以使用 `--shortcut-mod`后缀来修改它。可选的按键有`lctrl``rctrl`
`lalt``ralt``lsuper``rsuper`。如下例:
```bash
# 使用右侧的Ctrl键
scrcpy --shortcut-mod=rctrl
# 使用左侧的Ctrl键、Alt键或Super键
scrcpy --shortcut-mod=lctrl+lalt,lsuper
```
_一般来说<kbd>[Super]</kbd>就是<kbd>Windows</kbd>或者<kbd>Cmd</kbd>。_
[Super]: https://en.wikipedia.org/wiki/Super_key_(keyboard_button)
| 操作 | 快捷键
| ------------------------------------------- |:-----------------------------
| 全屏 | <kbd>MOD</kbd>+<kbd>f</kbd>
| 向左旋转屏幕 | <kbd>MOD</kbd>+<kbd></kbd> _(左)_
| 向右旋转屏幕 | <kbd>MOD</kbd>+<kbd></kbd> _(右)_
| 将窗口大小重置为1:1 (像素优先) | <kbd>MOD</kbd>+<kbd>g</kbd>
| 将窗口大小重置为消除黑边 | <kbd>MOD</kbd>+<kbd>w</kbd> \| _双击¹_
| 点按 `主屏幕` | <kbd>MOD</kbd>+<kbd>h</kbd> \| _点击鼠标中键_
| 点按 `返回` | <kbd>MOD</kbd>+<kbd>b</kbd> \| _点击鼠标右键²_
| 点按 `切换应用` | <kbd>MOD</kbd>+<kbd>s</kbd>
| 点按 `菜单` (解锁屏幕) | <kbd>MOD</kbd>+<kbd>m</kbd>
| 点按 `音量+` | <kbd>MOD</kbd>+<kbd></kbd> _(up)_
| 点按 `音量-` | <kbd>MOD</kbd>+<kbd></kbd> _(down)_
| 点按 `电源` | <kbd>MOD</kbd>+<kbd>p</kbd>
| 打开屏幕 | _点击鼠标右键²_
| 关闭设备屏幕(但继续在电脑上显示) | <kbd>MOD</kbd>+<kbd>o</kbd>
| 打开设备屏幕 | <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>o</kbd>
| 旋转设备屏幕 | <kbd>MOD</kbd>+<kbd>r</kbd>
| 展开通知面板 | <kbd>MOD</kbd>+<kbd>n</kbd>
| 展开快捷操作 | <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>n</kbd>
| 复制到剪贴板³ | <kbd>MOD</kbd>+<kbd>c</kbd>
| 剪切到剪贴板³ | <kbd>MOD</kbd>+<kbd>x</kbd>
| 同步剪贴板并黏贴³ | <kbd>MOD</kbd>+<kbd>v</kbd>
| 导入电脑剪贴板文本 | <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>v</kbd>
| 打开/关闭FPS显示在 stdout) | <kbd>MOD</kbd>+<kbd>i</kbd>
| 捏拉缩放 | <kbd>Ctrl</kbd>+_点按并移动鼠标_
_¹双击黑色边界以关闭黑色边界_
_²点击鼠标右键将在屏幕熄灭时点亮屏幕其余情况则视为按下 返回键 。_
_³需要安卓版本 Android >= 7。_
所有的 <kbd>Ctrl</kbd>+_按键_ 的热键都是被转发到设备进行处理的,所以实际上会由当前应用程序对其做出响应。
## 自定义路径
为了使用您想使用的 _adb_ ,您可以在环境变量
`ADB`中设置它的路径:
ADB=/path/to/adb scrcpy
如果需要覆盖`scrcpy-server`的路径,您可以在
`SCRCPY_SERVER_PATH`中设置它。
[useful]: https://github.com/Genymobile/scrcpy/issues/278#issuecomment-429330345
## 为什么叫 _scrcpy_
一个同事让我找出一个和[gnirehtet]一样难以发音的名字。
[`strcpy`] 可以复制**str**ing `scrcpy` 可以复制**scr**een。
[gnirehtet]: https://github.com/Genymobile/gnirehtet
[`strcpy`]: http://man7.org/linux/man-pages/man3/strcpy.3.html
## 如何编译?
请查看[编译]。
[编译]: BUILD.md
## 常见问题
请查看[FAQ](FAQ.md).
## 开发者
请查看[开发者页面]。
[开发者页面]: DEVELOP.md
## 许可协议
Copyright (C) 2018 Genymobile
Copyright (C) 2018-2021 Romain Vimont
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
## 相关文章
- [Introducing scrcpy][article-intro]
- [Scrcpy now works wirelessly][article-tcpip]
[article-intro]: https://blog.rom1v.com/2018/03/introducing-scrcpy/
[article-tcpip]: https://www.genymotion.com/blog/open-source-project-scrcpy-now-works-wirelessly/

View File

@ -682,7 +682,7 @@ _³只支援 Android 7+。_
## Licence
Copyright (C) 2018 Genymobile
Copyright (C) 2018-2020 Romain Vimont
Copyright (C) 2018-2021 Romain Vimont
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -1,7 +1,8 @@
src = [
'src/main.c',
'src/adb.c',
'src/cli.c',
'src/command.c',
'src/compat.c',
'src/control_msg.c',
'src/controller.c',
'src/decoder.c',
@ -21,9 +22,22 @@ src = [
'src/tiny_xpm.c',
'src/video_buffer.c',
'src/util/net.c',
'src/util/process.c',
'src/util/str_util.c'
]
if host_machine.system() == 'windows'
src += [ 'src/sys/win/process.c' ]
else
src += [ 'src/sys/unix/process.c' ]
endif
check_functions = [
'strdup'
]
cc = meson.get_compiler('c')
if not get_option('crossbuild_windows')
# native build
@ -37,8 +51,6 @@ if not get_option('crossbuild_windows')
else
# cross-compile mingw32 build (from Linux to Windows)
cc = meson.get_compiler('c')
prebuilt_sdl2 = meson.get_cross_property('prebuilt_sdl2')
sdl2_bin_dir = meson.current_source_dir() + '/../prebuilt-deps/' + prebuilt_sdl2 + '/bin'
sdl2_lib_dir = meson.current_source_dir() + '/../prebuilt-deps/' + prebuilt_sdl2 + '/lib'
@ -73,17 +85,19 @@ else
endif
cc = meson.get_compiler('c')
if host_machine.system() == 'windows'
src += [ 'src/sys/win/command.c' ]
dependencies += cc.find_library('ws2_32')
else
src += [ 'src/sys/unix/command.c' ]
endif
conf = configuration_data()
foreach f : check_functions
if cc.has_function(f)
define = 'HAVE_' + f.underscorify().to_upper()
conf.set(define, true)
endif
endforeach
# expose the build type
conf.set('NDEBUG', get_option('buildtype') != 'debug')
@ -119,9 +133,6 @@ conf.set('DEFAULT_BIT_RATE', '8000000') # 8Mbps
# enable High DPI support
conf.set('HIDPI_SUPPORT', get_option('hidpi_support'))
# disable console on Windows
conf.set('WINDOWS_NOCONSOLE', get_option('windows_noconsole'))
# run a server debugger and wait for a client to be attached
conf.set('SERVER_DEBUGGER', get_option('server_debugger'))
@ -132,18 +143,11 @@ configure_file(configuration: conf, output: 'config.h')
src_dir = include_directories('src')
if get_option('windows_noconsole')
link_args = [ '-Wl,--subsystem,windows' ]
else
link_args = []
endif
executable('scrcpy', src,
dependencies: dependencies,
include_directories: src_dir,
install: true,
c_args: [],
link_args: link_args)
c_args: [])
install_man('scrcpy.1')

View File

@ -56,10 +56,18 @@ The list of possible display ids can be listed by "adb shell dumpsys display"
Default is 0.
.TP
.BI "\-\-encoder " name
Use a specific MediaCodec encoder (must be a H.264 encoder).
.TP
.B \-\-force\-adb\-forward
Do not attempt to use "adb reverse" to connect to the device.
.TP
.B \-\-forward\-all\-clicks
By default, right-click triggers BACK (or POWER on) and middle-click triggers HOME. This option disables these shortcuts and forward the clicks to the device instead.
.TP
.B \-f, \-\-fullscreen
Start in fullscreen.
@ -68,6 +76,12 @@ Start in fullscreen.
.B \-h, \-\-help
Print this help.
.TP
.B \-\-legacy\-paste
Inject computer clipboard text as a sequence of key events on Ctrl+v (like MOD+Shift+v).
This is a workaround for some devices not behaving as expected when setting the device clipboard programmatically.
.TP
.BI "\-\-lock\-video\-orientation " value
Lock video orientation to \fIvalue\fR. Possible values are -1 (unlocked), 0, 1, 2 and 3. Natural device orientation is 0, and each increment adds a 90 degrees otation counterclockwise.
@ -92,14 +106,14 @@ Disable device control (mirror the device in read\-only).
.B \-N, \-\-no\-display
Do not display device (only when screen recording is enabled).
.TP
.B \-\-no\-mipmaps
If the renderer is OpenGL 3.0+ or OpenGL ES 2.0+, then mipmaps are automatically generated to improve downscaling quality. This option disables the generation of mipmaps.
.TP
.B \-\-no\-key\-repeat
Do not forward repeated key events when a key is held down.
.TP
.B \-\-no\-mipmaps
If the renderer is OpenGL 3.0+ or OpenGL ES 2.0+, then mipmaps are automatically generated to improve downscaling quality. This option disables the generation of mipmaps.
.TP
.BI "\-p, \-\-port " port[:port]
Set the TCP port (range) used by the client to listen.

View File

@ -1,12 +1,10 @@
#include "command.h"
#include "adb.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "config.h"
#include "common.h"
#include "util/log.h"
#include "util/str_util.h"
@ -70,7 +68,7 @@ show_adb_installation_msg() {
{"pacman", "pacman -S android-tools"},
};
for (size_t i = 0; i < ARRAY_LEN(pkg_managers); ++i) {
if (cmd_search(pkg_managers[i].binary)) {
if (search_executable(pkg_managers[i].binary)) {
LOGI("You may install 'adb' by \"%s\"", pkg_managers[i].command);
return;
}
@ -118,7 +116,7 @@ adb_execute(const char *serial, const char *const adb_cmd[], size_t len) {
memcpy(&cmd[i], adb_cmd, len * sizeof(const char *));
cmd[len + i] = NULL;
enum process_result r = cmd_execute(cmd, &process);
enum process_result r = process_execute(cmd, &process);
if (r != PROCESS_SUCCESS) {
show_adb_err_msg(r, cmd);
return PROCESS_NONE;
@ -175,7 +173,7 @@ adb_push(const char *serial, const char *local, const char *remote) {
}
remote = strquote(remote);
if (!remote) {
SDL_free((void *) local);
free((void *) local);
return PROCESS_NONE;
}
#endif
@ -184,8 +182,8 @@ adb_push(const char *serial, const char *local, const char *remote) {
process_t proc = adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd));
#ifdef __WINDOWS__
SDL_free((void *) remote);
SDL_free((void *) local);
free((void *) remote);
free((void *) local);
#endif
return proc;
@ -206,26 +204,8 @@ adb_install(const char *serial, const char *local) {
process_t proc = adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd));
#ifdef __WINDOWS__
SDL_free((void *) local);
free((void *) local);
#endif
return proc;
}
bool
process_check_success(process_t proc, const char *name) {
if (proc == PROCESS_NONE) {
LOGE("Could not execute \"%s\"", name);
return false;
}
exit_code_t exit_code;
if (!cmd_simple_wait(proc, &exit_code)) {
if (exit_code != NO_EXIT_CODE) {
LOGE("\"%s\" returned with value %" PRIexitcode, name, exit_code);
} else {
LOGE("\"%s\" exited unexpectedly", name);
}
return false;
}
return true;
}

34
app/src/adb.h Normal file
View File

@ -0,0 +1,34 @@
#ifndef SC_ADB_H
#define SC_ADB_H
#include "common.h"
#include <stdbool.h>
#include <inttypes.h>
#include "util/process.h"
process_t
adb_execute(const char *serial, const char *const adb_cmd[], size_t len);
process_t
adb_forward(const char *serial, uint16_t local_port,
const char *device_socket_name);
process_t
adb_forward_remove(const char *serial, uint16_t local_port);
process_t
adb_reverse(const char *serial, const char *device_socket_name,
uint16_t local_port);
process_t
adb_reverse_remove(const char *serial, const char *device_socket_name);
process_t
adb_push(const char *serial, const char *local, const char *remote);
process_t
adb_install(const char *serial, const char *local);
#endif

View File

@ -6,7 +6,6 @@
#include <stdio.h>
#include <unistd.h>
#include "config.h"
#include "scrcpy.h"
#include "util/log.h"
#include "util/str_util.h"
@ -53,16 +52,30 @@ scrcpy_print_usage(const char *arg0) {
"\n"
" Default is 0.\n"
"\n"
" --encoder name\n"
" Use a specific MediaCodec encoder (must be a H.264 encoder).\n"
"\n"
" --force-adb-forward\n"
" Do not attempt to use \"adb reverse\" to connect to the\n"
" the device.\n"
"\n"
" --forward-all-clicks\n"
" By default, right-click triggers BACK (or POWER on) and\n"
" middle-click triggers HOME. This option disables these\n"
" shortcuts and forward the clicks to the device instead.\n"
"\n"
" -f, --fullscreen\n"
" Start in fullscreen.\n"
"\n"
" -h, --help\n"
" Print this help.\n"
"\n"
" --legacy-paste\n"
" Inject computer clipboard text as a sequence of key events\n"
" on Ctrl+v (like MOD+Shift+v).\n"
" This is a workaround for some devices not behaving as\n"
" expected when setting the device clipboard programmatically.\n"
"\n"
" --lock-video-orientation value\n"
" Lock video orientation to value.\n"
" Possible values are -1 (unlocked), 0, 1, 2 and 3.\n"
@ -87,14 +100,14 @@ scrcpy_print_usage(const char *arg0) {
" Do not display device (only when screen recording is\n"
" enabled).\n"
"\n"
" --no-key-repeat\n"
" Do not forward repeated key events when a key is held down.\n"
"\n"
" --no-mipmaps\n"
" If the renderer is OpenGL 3.0+ or OpenGL ES 2.0+, then\n"
" mipmaps are automatically generated to improve downscaling\n"
" quality. This option disables the generation of mipmaps.\n"
"\n"
" --no-key-repeat\n"
" Do not forward repeated key events when a key is held down.\n"
"\n"
" -p, --port port[:port]\n"
" Set the TCP port (range) used by the client to listen.\n"
" Default is %d:%d.\n"
@ -464,14 +477,14 @@ parse_port_range(const char *s, struct sc_port_range *port_range) {
}
static bool
parse_display_id(const char *s, uint16_t *display_id) {
parse_display_id(const char *s, uint32_t *display_id) {
long value;
bool ok = parse_integer_arg(s, &value, false, 0, 0xFFFF, "display id");
bool ok = parse_integer_arg(s, &value, false, 0, 0x7FFFFFFF, "display id");
if (!ok) {
return false;
}
*display_id = (uint16_t) value;
*display_id = (uint32_t) value;
return true;
}
@ -651,6 +664,9 @@ guess_record_format(const char *filename) {
#define OPT_DISABLE_SCREENSAVER 1020
#define OPT_SHORTCUT_MOD 1021
#define OPT_NO_KEY_REPEAT 1022
#define OPT_FORWARD_ALL_CLICKS 1023
#define OPT_LEGACY_PASTE 1024
#define OPT_ENCODER_NAME 1025
bool
scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
@ -662,18 +678,22 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
{"disable-screensaver", no_argument, NULL,
OPT_DISABLE_SCREENSAVER},
{"display", required_argument, NULL, OPT_DISPLAY_ID},
{"encoder", required_argument, NULL, OPT_ENCODER_NAME},
{"force-adb-forward", no_argument, NULL,
OPT_FORCE_ADB_FORWARD},
{"forward-all-clicks", no_argument, NULL,
OPT_FORWARD_ALL_CLICKS},
{"fullscreen", no_argument, NULL, 'f'},
{"help", no_argument, NULL, 'h'},
{"legacy-paste", no_argument, NULL, OPT_LEGACY_PASTE},
{"lock-video-orientation", required_argument, NULL,
OPT_LOCK_VIDEO_ORIENTATION},
{"max-fps", required_argument, NULL, OPT_MAX_FPS},
{"max-size", required_argument, NULL, 'm'},
{"no-control", no_argument, NULL, 'n'},
{"no-display", no_argument, NULL, 'N'},
{"no-mipmaps", no_argument, NULL, OPT_NO_MIPMAPS},
{"no-key-repeat", no_argument, NULL, OPT_NO_KEY_REPEAT},
{"no-mipmaps", no_argument, NULL, OPT_NO_MIPMAPS},
{"port", required_argument, NULL, 'p'},
{"prefer-text", no_argument, NULL, OPT_PREFER_TEXT},
{"push-target", required_argument, NULL, OPT_PUSH_TARGET},
@ -845,6 +865,9 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
case OPT_CODEC_OPTIONS:
opts->codec_options = optarg;
break;
case OPT_ENCODER_NAME:
opts->encoder_name = optarg;
break;
case OPT_FORCE_ADB_FORWARD:
opts->force_adb_forward = true;
break;
@ -856,6 +879,12 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
return false;
}
break;
case OPT_FORWARD_ALL_CLICKS:
opts->forward_all_clicks = true;
break;
case OPT_LEGACY_PASTE:
opts->legacy_paste = true;
break;
default:
// getopt prints the error message on stderr
return false;

View File

@ -1,9 +1,10 @@
#ifndef SCRCPY_CLI_H
#define SCRCPY_CLI_H
#include "common.h"
#include <stdbool.h>
#include "config.h"
#include "scrcpy.h"
struct scrcpy_cli_args {

View File

@ -1,35 +1,11 @@
#ifndef COMMON_H
#define COMMON_H
#include <stdint.h>
#include "config.h"
#include "compat.h"
#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))
#define MIN(X,Y) (X) < (Y) ? (X) : (Y)
#define MAX(X,Y) (X) > (Y) ? (X) : (Y)
struct size {
uint16_t width;
uint16_t height;
};
struct point {
int32_t x;
int32_t y;
};
struct position {
// The video screen size may be different from the real device screen size,
// so store to which size the absolute position apply, to scale it
// accordingly.
struct size screen_size;
struct point point;
};
struct port_range {
uint16_t first;
uint16_t last;
};
#endif

14
app/src/compat.c Normal file
View File

@ -0,0 +1,14 @@
#include "compat.h"
#include "config.h"
#ifndef HAVE_STRDUP
char *strdup(const char *s) {
size_t size = strlen(s) + 1;
char *dup = malloc(size);
if (dup) {
memcpy(dup, s, size);
}
return dup;
}
#endif

View File

@ -1,6 +1,14 @@
#ifndef COMPAT_H
#define COMPAT_H
#define _POSIX_C_SOURCE 200809L
#define _XOPEN_SOURCE 700
#define _GNU_SOURCE
#ifdef __APPLE__
# define _DARWIN_C_SOURCE
#endif
#include <libavcodec/version.h>
#include <libavformat/version.h>
#include <SDL2/SDL_version.h>
@ -48,4 +56,8 @@
# define SCRCPY_SDL_HAS_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR
#endif
#ifdef HAVE_STRDUP
char *strdup(const char *s);
#endif
#endif

View File

@ -1,9 +1,9 @@
#include "control_msg.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "config.h"
#include "util/buffer_util.h"
#include "util/log.h"
#include "util/str_util.h"
@ -94,10 +94,10 @@ void
control_msg_destroy(struct control_msg *msg) {
switch (msg->type) {
case CONTROL_MSG_TYPE_INJECT_TEXT:
SDL_free(msg->inject_text.text);
free(msg->inject_text.text);
break;
case CONTROL_MSG_TYPE_SET_CLIPBOARD:
SDL_free(msg->set_clipboard.text);
free(msg->set_clipboard.text);
break;
default:
// do nothing

View File

@ -1,14 +1,15 @@
#ifndef CONTROLMSG_H
#define CONTROLMSG_H
#include "common.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "config.h"
#include "android/input.h"
#include "android/keycodes.h"
#include "common.h"
#include "coords.h"
#define CONTROL_MSG_MAX_SIZE (1 << 18) // 256k
@ -49,7 +50,7 @@ struct control_msg {
enum android_metastate metastate;
} inject_keycode;
struct {
char *text; // owned, to be freed by SDL_free()
char *text; // owned, to be freed by free()
} inject_text;
struct {
enum android_motionevent_action action;
@ -64,7 +65,7 @@ struct control_msg {
int32_t vscroll;
} inject_scroll_event;
struct {
char *text; // owned, to be freed by SDL_free()
char *text; // owned, to be freed by free()
bool paste;
} set_clipboard;
struct {

View File

@ -2,7 +2,6 @@
#include <assert.h>
#include "config.h"
#include "util/lock.h"
#include "util/log.h"
@ -61,12 +60,12 @@ static bool
process_msg(struct controller *controller,
const struct control_msg *msg) {
static unsigned char serialized_msg[CONTROL_MSG_MAX_SIZE];
int length = control_msg_serialize(msg, serialized_msg);
size_t length = control_msg_serialize(msg, serialized_msg);
if (!length) {
return false;
}
int w = net_send_all(controller->control_socket, serialized_msg, length);
return w == length;
return (size_t) w == length;
}
static int

View File

@ -1,11 +1,12 @@
#ifndef CONTROLLER_H
#define CONTROLLER_H
#include "common.h"
#include <stdbool.h>
#include <SDL2/SDL_mutex.h>
#include <SDL2/SDL_thread.h>
#include "config.h"
#include "control_msg.h"
#include "receiver.h"
#include "util/cbuf.h"

24
app/src/coords.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef SC_COORDS
#define SC_COORDS
#include <stdint.h>
struct size {
uint16_t width;
uint16_t height;
};
struct point {
int32_t x;
int32_t y;
};
struct position {
// The video screen size may be different from the real device screen size,
// so store to which size the absolute position apply, to scale it
// accordingly.
struct size screen_size;
struct point point;
};
#endif

View File

@ -7,8 +7,6 @@
#include <SDL2/SDL_thread.h>
#include <unistd.h>
#include "config.h"
#include "compat.h"
#include "events.h"
#include "recorder.h"
#include "video_buffer.h"

View File

@ -1,11 +1,11 @@
#ifndef DECODER_H
#define DECODER_H
#include "common.h"
#include <stdbool.h>
#include <libavformat/avformat.h>
#include "config.h"
struct video_buffer;
struct decoder {

View File

@ -1,6 +1,5 @@
#include "device.h"
#include "config.h"
#include "util/log.h"
bool

View File

@ -1,10 +1,11 @@
#ifndef DEVICE_H
#define DEVICE_H
#include "common.h"
#include <stdbool.h>
#include "config.h"
#include "common.h"
#include "coords.h"
#include "util/net.h"
#define DEVICE_NAME_FIELD_LENGTH 64

View File

@ -1,8 +1,8 @@
#include "device_msg.h"
#include <stdlib.h>
#include <string.h>
#include "config.h"
#include "util/buffer_util.h"
#include "util/log.h"
@ -21,7 +21,7 @@ device_msg_deserialize(const unsigned char *buf, size_t len,
if (clipboard_len > len - 5) {
return 0; // not available
}
char *text = SDL_malloc(clipboard_len + 1);
char *text = malloc(clipboard_len + 1);
if (!text) {
LOGW("Could not allocate text for clipboard");
return -1;
@ -43,6 +43,6 @@ device_msg_deserialize(const unsigned char *buf, size_t len,
void
device_msg_destroy(struct device_msg *msg) {
if (msg->type == DEVICE_MSG_TYPE_CLIPBOARD) {
SDL_free(msg->clipboard.text);
free(msg->clipboard.text);
}
}

View File

@ -1,12 +1,12 @@
#ifndef DEVICEMSG_H
#define DEVICEMSG_H
#include "common.h"
#include <stdbool.h>
#include <stdint.h>
#include <unistd.h>
#include "config.h"
#define DEVICE_MSG_MAX_SIZE (1 << 18) // 256k
// type: 1 byte; length: 4 bytes
#define DEVICE_MSG_TEXT_MAX_LENGTH (DEVICE_MSG_MAX_SIZE - 5)
@ -19,7 +19,7 @@ struct device_msg {
enum device_msg_type type;
union {
struct {
char *text; // owned, to be freed by SDL_free()
char *text; // owned, to be freed by free()
} clipboard;
};
};

View File

@ -1,7 +1,5 @@
#include "event_converter.h"
#include "config.h"
#define MAP(FROM, TO) case FROM: *to = TO; return true
#define FAIL default: return false

View File

@ -1,10 +1,11 @@
#ifndef CONVERT_H
#define CONVERT_H
#include "common.h"
#include <stdbool.h>
#include <SDL2/SDL_events.h>
#include "config.h"
#include "control_msg.h"
bool

View File

@ -3,8 +3,7 @@
#include <assert.h>
#include <string.h>
#include "config.h"
#include "command.h"
#include "adb.h"
#include "util/lock.h"
#include "util/log.h"
@ -12,7 +11,7 @@
static void
file_handler_request_destroy(struct file_handler_request *req) {
SDL_free(req->file);
free(req->file);
}
bool
@ -31,7 +30,7 @@ file_handler_init(struct file_handler *file_handler, const char *serial,
}
if (serial) {
file_handler->serial = SDL_strdup(serial);
file_handler->serial = strdup(serial);
if (!file_handler->serial) {
LOGW("Could not strdup serial");
SDL_DestroyCond(file_handler->event_cond);
@ -57,7 +56,7 @@ void
file_handler_destroy(struct file_handler *file_handler) {
SDL_DestroyCond(file_handler->event_cond);
SDL_DestroyMutex(file_handler->mutex);
SDL_free(file_handler->serial);
free(file_handler->serial);
struct file_handler_request req;
while (cbuf_take(&file_handler->queue, &req)) {
@ -136,13 +135,13 @@ run_file_handler(void *data) {
mutex_unlock(file_handler->mutex);
if (req.action == ACTION_INSTALL_APK) {
if (process_check_success(process, "adb install")) {
if (process_check_success(process, "adb install", false)) {
LOGI("%s successfully installed", req.file);
} else {
LOGE("Failed to install %s", req.file);
}
} else {
if (process_check_success(process, "adb push")) {
if (process_check_success(process, "adb push", false)) {
LOGI("%s successfully pushed to %s", req.file,
file_handler->push_target);
} else {
@ -151,6 +150,14 @@ run_file_handler(void *data) {
}
}
mutex_lock(file_handler->mutex);
// Close the process (it is necessary already terminated)
// Execute this call with mutex locked to avoid race conditions with
// file_handler_stop()
process_close(file_handler->current_process);
file_handler->current_process = PROCESS_NONE;
mutex_unlock(file_handler->mutex);
file_handler_request_destroy(&req);
}
return 0;
@ -176,11 +183,9 @@ file_handler_stop(struct file_handler *file_handler) {
file_handler->stopped = true;
cond_signal(file_handler->event_cond);
if (file_handler->current_process != PROCESS_NONE) {
if (!cmd_terminate(file_handler->current_process)) {
LOGW("Could not terminate install process");
if (!process_terminate(file_handler->current_process)) {
LOGW("Could not terminate push/install process");
}
cmd_simple_wait(file_handler->current_process, NULL);
file_handler->current_process = PROCESS_NONE;
}
mutex_unlock(file_handler->mutex);
}

View File

@ -1,12 +1,13 @@
#ifndef FILE_HANDLER_H
#define FILE_HANDLER_H
#include "common.h"
#include <stdbool.h>
#include <SDL2/SDL_mutex.h>
#include <SDL2/SDL_thread.h>
#include "config.h"
#include "command.h"
#include "adb.h"
#include "util/cbuf.h"
typedef enum {
@ -49,7 +50,7 @@ file_handler_stop(struct file_handler *file_handler);
void
file_handler_join(struct file_handler *file_handler);
// take ownership of file, and will SDL_free() it
// take ownership of file, and will free() it
bool
file_handler_request(struct file_handler *file_handler,
file_handler_action_t action,

View File

@ -3,7 +3,6 @@
#include <assert.h>
#include <SDL2/SDL_timer.h>
#include "config.h"
#include "util/lock.h"
#include "util/log.h"

View File

@ -1,14 +1,14 @@
#ifndef FPSCOUNTER_H
#define FPSCOUNTER_H
#include "common.h"
#include <stdatomic.h>
#include <stdbool.h>
#include <stdint.h>
#include <SDL2/SDL_mutex.h>
#include <SDL2/SDL_thread.h>
#include "config.h"
struct fps_counter {
SDL_Thread *thread;
SDL_mutex *mutex;

View File

@ -3,7 +3,6 @@
#include <assert.h>
#include <SDL2/SDL_keycode.h>
#include "config.h"
#include "event_converter.h"
#include "util/lock.h"
#include "util/log.h"
@ -60,6 +59,8 @@ input_manager_init(struct input_manager *im,
im->control = options->control;
im->forward_key_repeat = options->forward_key_repeat;
im->prefer_text = options->prefer_text;
im->forward_all_clicks = options->forward_all_clicks;
im->legacy_paste = options->legacy_paste;
const struct sc_shortcut_mods *shortcut_mods = &options->shortcut_mods;
assert(shortcut_mods->count);
@ -189,13 +190,20 @@ set_device_clipboard(struct controller *controller, bool paste) {
return;
}
char *text_dup = strdup(text);
SDL_free(text);
if (!text_dup) {
LOGW("Could not strdup input text");
return;
}
struct control_msg msg;
msg.type = CONTROL_MSG_TYPE_SET_CLIPBOARD;
msg.set_clipboard.text = text;
msg.set_clipboard.text = text_dup;
msg.set_clipboard.paste = paste;
if (!controller_push_msg(controller, &msg)) {
SDL_free(text);
free(text_dup);
LOGW("Could not request 'set device clipboard'");
}
}
@ -241,11 +249,18 @@ clipboard_paste(struct controller *controller) {
return;
}
char *text_dup = strdup(text);
SDL_free(text);
if (!text_dup) {
LOGW("Could not strdup input text");
return;
}
struct control_msg msg;
msg.type = CONTROL_MSG_TYPE_INJECT_TEXT;
msg.inject_text.text = text;
msg.inject_text.text = text_dup;
if (!controller_push_msg(controller, &msg)) {
SDL_free(text);
free(text_dup);
LOGW("Could not request 'paste clipboard'");
}
}
@ -290,13 +305,13 @@ input_manager_process_text_input(struct input_manager *im,
struct control_msg msg;
msg.type = CONTROL_MSG_TYPE_INJECT_TEXT;
msg.inject_text.text = SDL_strdup(event->text);
msg.inject_text.text = strdup(event->text);
if (!msg.inject_text.text) {
LOGW("Could not strdup input text");
return;
}
if (!controller_push_msg(im->controller, &msg)) {
SDL_free(msg.inject_text.text);
free(msg.inject_text.text);
LOGW("Could not request 'inject text'");
}
}
@ -440,7 +455,7 @@ input_manager_process_key(struct input_manager *im,
return;
case SDLK_v:
if (control && !repeat && down) {
if (shift) {
if (shift || im->legacy_paste) {
// inject the text as input events
clipboard_paste(controller);
} else {
@ -504,6 +519,11 @@ input_manager_process_key(struct input_manager *im,
}
if (ctrl && !shift && keycode == SDLK_v && down && !repeat) {
if (im->legacy_paste) {
// inject the text as input events
clipboard_paste(controller);
return;
}
// Synchronize the computer clipboard to the device clipboard before
// sending Ctrl+v, to allow seamless copy-paste.
set_device_clipboard(controller, false);
@ -629,7 +649,7 @@ input_manager_process_mouse_button(struct input_manager *im,
}
bool down = event->type == SDL_MOUSEBUTTONDOWN;
if (down) {
if (!im->forward_all_clicks && down) {
if (control && event->button == SDL_BUTTON_RIGHT) {
press_back_or_turn_screen_on(im->controller);
return;

View File

@ -1,12 +1,12 @@
#ifndef INPUTMANAGER_H
#define INPUTMANAGER_H
#include "common.h"
#include <stdbool.h>
#include <SDL2/SDL.h>
#include "config.h"
#include "common.h"
#include "controller.h"
#include "fps_counter.h"
#include "scrcpy.h"
@ -25,6 +25,8 @@ struct input_manager {
bool control;
bool forward_key_repeat;
bool prefer_text;
bool forward_all_clicks;
bool legacy_paste;
struct {
unsigned data[SC_MAX_SHORTCUT_MODS];

View File

@ -1,5 +1,7 @@
#include "scrcpy.h"
#include "common.h"
#include <assert.h>
#include <stdbool.h>
#include <unistd.h>
@ -7,9 +9,7 @@
#define SDL_MAIN_HANDLED // avoid link error on Linux Windows Subsystem
#include <SDL2/SDL.h>
#include "config.h"
#include "cli.h"
#include "compat.h"
#include "util/log.h"
static void
@ -98,11 +98,5 @@ main(int argc, char *argv[]) {
avformat_network_deinit(); // ignore failure
#if defined (__WINDOWS__) && ! defined (WINDOWS_NOCONSOLE)
if (res != 0) {
fprintf(stderr, "Press any key to continue...\n");
getchar();
}
#endif
return res;
}

View File

@ -1,11 +1,11 @@
#ifndef SC_OPENGL_H
#define SC_OPENGL_H
#include "common.h"
#include <stdbool.h>
#include <SDL2/SDL_opengl.h>
#include "config.h"
struct sc_opengl {
const char *version;
bool is_opengles;

View File

@ -3,7 +3,6 @@
#include <assert.h>
#include <SDL2/SDL_clipboard.h>
#include "config.h"
#include "device_msg.h"
#include "util/lock.h"
#include "util/log.h"

View File

@ -1,11 +1,12 @@
#ifndef RECEIVER_H
#define RECEIVER_H
#include "common.h"
#include <stdbool.h>
#include <SDL2/SDL_mutex.h>
#include <SDL2/SDL_thread.h>
#include "config.h"
#include "util/net.h"
// receive events from the device

View File

@ -3,8 +3,6 @@
#include <assert.h>
#include <libavutil/time.h>
#include "config.h"
#include "compat.h"
#include "util/lock.h"
#include "util/log.h"
@ -29,7 +27,7 @@ find_muxer(const char *name) {
static struct record_packet *
record_packet_new(const AVPacket *packet) {
struct record_packet *rec = SDL_malloc(sizeof(*rec));
struct record_packet *rec = malloc(sizeof(*rec));
if (!rec) {
return NULL;
}
@ -39,7 +37,7 @@ record_packet_new(const AVPacket *packet) {
av_init_packet(&rec->packet);
if (av_packet_ref(&rec->packet, packet)) {
SDL_free(rec);
free(rec);
return NULL;
}
return rec;
@ -48,7 +46,7 @@ record_packet_new(const AVPacket *packet) {
static void
record_packet_delete(struct record_packet *rec) {
av_packet_unref(&rec->packet);
SDL_free(rec);
free(rec);
}
static void
@ -65,7 +63,7 @@ recorder_init(struct recorder *recorder,
const char *filename,
enum sc_record_format format,
struct size declared_frame_size) {
recorder->filename = SDL_strdup(filename);
recorder->filename = strdup(filename);
if (!recorder->filename) {
LOGE("Could not strdup filename");
return false;
@ -74,7 +72,7 @@ recorder_init(struct recorder *recorder,
recorder->mutex = SDL_CreateMutex();
if (!recorder->mutex) {
LOGC("Could not create mutex");
SDL_free(recorder->filename);
free(recorder->filename);
return false;
}
@ -82,7 +80,7 @@ recorder_init(struct recorder *recorder,
if (!recorder->queue_cond) {
LOGC("Could not create cond");
SDL_DestroyMutex(recorder->mutex);
SDL_free(recorder->filename);
free(recorder->filename);
return false;
}
@ -101,7 +99,7 @@ void
recorder_destroy(struct recorder *recorder) {
SDL_DestroyCond(recorder->queue_cond);
SDL_DestroyMutex(recorder->mutex);
SDL_free(recorder->filename);
free(recorder->filename);
}
static const char *
@ -361,12 +359,14 @@ recorder_push(struct recorder *recorder, const AVPacket *packet) {
if (recorder->failed) {
// reject any new packet (this will stop the stream)
mutex_unlock(recorder->mutex);
return false;
}
struct record_packet *rec = record_packet_new(packet);
if (!rec) {
LOGC("Could not allocate record packet");
mutex_unlock(recorder->mutex);
return false;
}

View File

@ -1,13 +1,14 @@
#ifndef RECORDER_H
#define RECORDER_H
#include "common.h"
#include <stdbool.h>
#include <libavformat/avformat.h>
#include <SDL2/SDL_mutex.h>
#include <SDL2/SDL_thread.h>
#include "config.h"
#include "common.h"
#include "coords.h"
#include "scrcpy.h"
#include "util/queue.h"

View File

@ -13,10 +13,6 @@
# include <windows.h>
#endif
#include "config.h"
#include "command.h"
#include "common.h"
#include "compat.h"
#include "controller.h"
#include "decoder.h"
#include "device.h"
@ -34,7 +30,7 @@
#include "util/log.h"
#include "util/net.h"
static struct server server = SERVER_INITIALIZER;
static struct server server;
static struct screen screen = SCREEN_INITIALIZER;
static struct fps_counter fps_counter;
static struct video_buffer video_buffer;
@ -290,7 +286,7 @@ av_log_callback(void *avcl, int level, const char *fmt, va_list vl) {
if (priority == 0) {
return;
}
char *local_fmt = SDL_malloc(strlen(fmt) + 10);
char *local_fmt = malloc(strlen(fmt) + 10);
if (!local_fmt) {
LOGC("Could not allocate string");
return;
@ -299,11 +295,26 @@ av_log_callback(void *avcl, int level, const char *fmt, va_list vl) {
strcpy(local_fmt, "[FFmpeg] ");
strcpy(local_fmt + 9, fmt);
SDL_LogMessageV(SDL_LOG_CATEGORY_VIDEO, priority, local_fmt, vl);
SDL_free(local_fmt);
free(local_fmt);
}
bool
scrcpy(const struct scrcpy_options *options) {
if (!server_init(&server)) {
return false;
}
bool ret = false;
bool server_started = false;
bool fps_counter_initialized = false;
bool video_buffer_initialized = false;
bool file_handler_initialized = false;
bool recorder_initialized = false;
bool stream_started = false;
bool controller_initialized = false;
bool controller_started = false;
bool record = !!options->record_filename;
struct server_params params = {
.log_level = options->log_level,
@ -318,21 +329,14 @@ scrcpy(const struct scrcpy_options *options) {
.show_touches = options->show_touches,
.stay_awake = options->stay_awake,
.codec_options = options->codec_options,
.encoder_name = options->encoder_name,
.force_adb_forward = options->force_adb_forward,
};
if (!server_start(&server, options->serial, &params)) {
return false;
goto end;
}
bool ret = false;
bool fps_counter_initialized = false;
bool video_buffer_initialized = false;
bool file_handler_initialized = false;
bool recorder_initialized = false;
bool stream_started = false;
bool controller_initialized = false;
bool controller_started = false;
server_started = true;
if (!sdl_init_and_configure(options->display, options->render_driver,
options->disable_screensaver)) {
@ -422,7 +426,7 @@ scrcpy(const struct scrcpy_options *options) {
options->window_y, options->window_width,
options->window_height,
options->window_borderless,
options->rotation, options-> mipmaps)) {
options->rotation, options->mipmaps)) {
goto end;
}
@ -464,8 +468,10 @@ end:
fps_counter_interrupt(&fps_counter);
}
// shutdown the sockets and kill the server
server_stop(&server);
if (server_started) {
// shutdown the sockets and kill the server
server_stop(&server);
}
// now that the sockets are shutdown, the stream and controller are
// interrupted, we can join them

View File

@ -1,12 +1,12 @@
#ifndef SCRCPY_H
#define SCRCPY_H
#include "common.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "config.h"
enum sc_log_level {
SC_LOG_LEVEL_DEBUG,
SC_LOG_LEVEL_INFO,
@ -51,6 +51,7 @@ struct scrcpy_options {
const char *push_target;
const char *render_driver;
const char *codec_options;
const char *encoder_name;
enum sc_log_level log_level;
enum sc_record_format record_format;
struct sc_port_range port_range;
@ -64,7 +65,7 @@ struct scrcpy_options {
int16_t window_y; // SC_WINDOW_POSITION_UNDEFINED for "auto"
uint16_t window_width;
uint16_t window_height;
uint16_t display_id;
uint32_t display_id;
bool show_touches;
bool fullscreen;
bool always_on_top;
@ -79,6 +80,8 @@ struct scrcpy_options {
bool force_adb_forward;
bool disable_screensaver;
bool forward_key_repeat;
bool forward_all_clicks;
bool legacy_paste;
};
#define SCRCPY_OPTIONS_DEFAULT { \
@ -89,6 +92,7 @@ struct scrcpy_options {
.push_target = NULL, \
.render_driver = NULL, \
.codec_options = NULL, \
.encoder_name = NULL, \
.log_level = SC_LOG_LEVEL_INFO, \
.record_format = SC_RECORD_FORMAT_AUTO, \
.port_range = { \
@ -123,6 +127,8 @@ struct scrcpy_options {
.force_adb_forward = false, \
.disable_screensaver = false, \
.forward_key_repeat = true, \
.forward_all_clicks = false, \
.legacy_paste = false, \
}
bool

View File

@ -4,9 +4,6 @@
#include <string.h>
#include <SDL2/SDL.h>
#include "config.h"
#include "common.h"
#include "compat.h"
#include "icon.xpm"
#include "scrcpy.h"
#include "tiny_xpm.h"

View File

@ -1,12 +1,13 @@
#ifndef SCREEN_H
#define SCREEN_H
#include "common.h"
#include <stdbool.h>
#include <SDL2/SDL.h>
#include <libavformat/avformat.h>
#include "config.h"
#include "common.h"
#include "coords.h"
#include "opengl.h"
struct video_buffer;

View File

@ -9,8 +9,8 @@
#include <SDL2/SDL_timer.h>
#include <SDL2/SDL_platform.h>
#include "config.h"
#include "command.h"
#include "adb.h"
#include "util/lock.h"
#include "util/log.h"
#include "util/net.h"
#include "util/str_util.h"
@ -33,7 +33,7 @@ get_server_path(void) {
#ifdef __WINDOWS__
char *server_path = utf8_from_wide_char(server_path_env);
#else
char *server_path = SDL_strdup(server_path_env);
char *server_path = strdup(server_path_env);
#endif
if (!server_path) {
LOGE("Could not allocate memory");
@ -45,7 +45,7 @@ get_server_path(void) {
#ifndef PORTABLE
LOGD("Using server: " DEFAULT_SERVER_PATH);
char *server_path = SDL_strdup(DEFAULT_SERVER_PATH);
char *server_path = strdup(DEFAULT_SERVER_PATH);
if (!server_path) {
LOGE("Could not allocate memory");
return NULL;
@ -67,11 +67,11 @@ get_server_path(void) {
// sizeof(SERVER_FILENAME) gives statically the size including the null byte
size_t len = dirlen + 1 + sizeof(SERVER_FILENAME);
char *server_path = SDL_malloc(len);
char *server_path = malloc(len);
if (!server_path) {
LOGE("Could not alloc server path string, "
"using " SERVER_FILENAME " from current directory");
SDL_free(executable_path);
free(executable_path);
return SERVER_FILENAME;
}
@ -80,7 +80,7 @@ get_server_path(void) {
memcpy(&server_path[dirlen + 1], SERVER_FILENAME, sizeof(SERVER_FILENAME));
// the final null byte has been copied with SERVER_FILENAME
SDL_free(executable_path);
free(executable_path);
LOGD("Using server (portable): %s", server_path);
return server_path;
@ -95,36 +95,36 @@ push_server(const char *serial) {
}
if (!is_regular_file(server_path)) {
LOGE("'%s' does not exist or is not a regular file\n", server_path);
SDL_free(server_path);
free(server_path);
return false;
}
process_t process = adb_push(serial, server_path, DEVICE_SERVER_PATH);
SDL_free(server_path);
return process_check_success(process, "adb push");
free(server_path);
return process_check_success(process, "adb push", true);
}
static bool
enable_tunnel_reverse(const char *serial, uint16_t local_port) {
process_t process = adb_reverse(serial, SOCKET_NAME, local_port);
return process_check_success(process, "adb reverse");
return process_check_success(process, "adb reverse", true);
}
static bool
disable_tunnel_reverse(const char *serial) {
process_t process = adb_reverse_remove(serial, SOCKET_NAME);
return process_check_success(process, "adb reverse --remove");
return process_check_success(process, "adb reverse --remove", true);
}
static bool
enable_tunnel_forward(const char *serial, uint16_t local_port) {
process_t process = adb_forward(serial, local_port, SOCKET_NAME);
return process_check_success(process, "adb forward");
return process_check_success(process, "adb forward", true);
}
static bool
disable_tunnel_forward(const char *serial, uint16_t local_port) {
process_t process = adb_forward_remove(serial, local_port);
return process_check_success(process, "adb forward --remove");
return process_check_success(process, "adb forward --remove", true);
}
static bool
@ -257,12 +257,12 @@ execute_server(struct server *server, const struct server_params *params) {
char bit_rate_string[11];
char max_fps_string[6];
char lock_video_orientation_string[5];
char display_id_string[6];
char display_id_string[11];
sprintf(max_size_string, "%"PRIu16, params->max_size);
sprintf(bit_rate_string, "%"PRIu32, params->bit_rate);
sprintf(max_fps_string, "%"PRIu16, params->max_fps);
sprintf(lock_video_orientation_string, "%"PRIi8, params->lock_video_orientation);
sprintf(display_id_string, "%"PRIu16, params->display_id);
sprintf(display_id_string, "%"PRIu32, params->display_id);
const char *const cmd[] = {
"shell",
"CLASSPATH=" DEVICE_SERVER_PATH,
@ -294,6 +294,7 @@ execute_server(struct server *server, const struct server_params *params) {
params->show_touches ? "true" : "false",
params->stay_awake ? "true" : "false",
params->codec_options ? params->codec_options : "-",
params->encoder_name ? params->encoder_name : "-",
};
#ifdef SERVER_DEBUGGER
LOGI("Server debugger waiting for a client on device port "
@ -352,15 +353,51 @@ close_socket(socket_t socket) {
}
}
void
bool
server_init(struct server *server) {
*server = (struct server) SERVER_INITIALIZER;
server->serial = NULL;
server->process = PROCESS_NONE;
server->wait_server_thread = NULL;
atomic_flag_clear_explicit(&server->server_socket_closed,
memory_order_relaxed);
server->mutex = SDL_CreateMutex();
if (!server->mutex) {
return false;
}
server->process_terminated_cond = SDL_CreateCond();
if (!server->process_terminated_cond) {
SDL_DestroyMutex(server->mutex);
return false;
}
server->process_terminated = false;
server->server_socket = INVALID_SOCKET;
server->video_socket = INVALID_SOCKET;
server->control_socket = INVALID_SOCKET;
server->port_range.first = 0;
server->port_range.last = 0;
server->local_port = 0;
server->tunnel_enabled = false;
server->tunnel_forward = false;
return true;
}
static int
run_wait_server(void *data) {
struct server *server = data;
cmd_simple_wait(server->process, NULL); // ignore exit code
process_wait(server->process, false); // ignore exit code
mutex_lock(server->mutex);
server->process_terminated = true;
cond_signal(server->process_terminated_cond);
mutex_unlock(server->mutex);
// no need for synchronization, server_socket is initialized before this
// thread was created
if (server->server_socket != INVALID_SOCKET
@ -379,7 +416,7 @@ server_start(struct server *server, const char *serial,
server->port_range = params->port_range;
if (serial) {
server->serial = SDL_strdup(serial);
server->serial = strdup(serial);
if (!server->serial) {
return false;
}
@ -409,8 +446,8 @@ server_start(struct server *server, const char *serial,
server->wait_server_thread =
SDL_CreateThread(run_wait_server, "wait-server", server);
if (!server->wait_server_thread) {
cmd_terminate(server->process);
cmd_simple_wait(server->process, NULL); // ignore exit code
process_terminate(server->process);
process_wait(server->process, true); // ignore exit code
goto error2;
}
@ -429,7 +466,7 @@ error2:
}
disable_tunnel(server);
error1:
SDL_free(server->serial);
free(server->serial);
return false;
}
@ -492,17 +529,39 @@ server_stop(struct server *server) {
assert(server->process != PROCESS_NONE);
cmd_terminate(server->process);
if (server->tunnel_enabled) {
// ignore failure
disable_tunnel(server);
}
// Give some delay for the server to terminate properly
mutex_lock(server->mutex);
int r = 0;
if (!server->process_terminated) {
#define WATCHDOG_DELAY_MS 1000
r = cond_wait_timeout(server->process_terminated_cond,
server->mutex,
WATCHDOG_DELAY_MS);
}
mutex_unlock(server->mutex);
// After this delay, kill the server if it's not dead already.
// On some devices, closing the sockets is not sufficient to wake up the
// blocking calls while the device is asleep.
if (r == SDL_MUTEX_TIMEDOUT) {
// The process is terminated, but not reaped (closed) yet, so its PID
// is still valid.
LOGW("Killing the server...");
process_terminate(server->process);
}
SDL_WaitThread(server->wait_server_thread, NULL);
process_close(server->process);
}
void
server_destroy(struct server *server) {
SDL_free(server->serial);
free(server->serial);
SDL_DestroyCond(server->process_terminated_cond);
SDL_DestroyMutex(server->mutex);
}

View File

@ -1,14 +1,14 @@
#ifndef SERVER_H
#define SERVER_H
#include "common.h"
#include <stdatomic.h>
#include <stdbool.h>
#include <stdint.h>
#include <SDL2/SDL_thread.h>
#include "config.h"
#include "command.h"
#include "common.h"
#include "adb.h"
#include "scrcpy.h"
#include "util/log.h"
#include "util/net.h"
@ -18,6 +18,11 @@ struct server {
process_t process;
SDL_Thread *wait_server_thread;
atomic_flag server_socket_closed;
SDL_mutex *mutex;
SDL_cond *process_terminated_cond;
bool process_terminated;
socket_t server_socket; // only used if !tunnel_forward
socket_t video_socket;
socket_t control_socket;
@ -27,41 +32,25 @@ struct server {
bool tunnel_forward; // use "adb forward" instead of "adb reverse"
};
#define SERVER_INITIALIZER { \
.serial = NULL, \
.process = PROCESS_NONE, \
.wait_server_thread = NULL, \
.server_socket_closed = ATOMIC_FLAG_INIT, \
.server_socket = INVALID_SOCKET, \
.video_socket = INVALID_SOCKET, \
.control_socket = INVALID_SOCKET, \
.port_range = { \
.first = 0, \
.last = 0, \
}, \
.local_port = 0, \
.tunnel_enabled = false, \
.tunnel_forward = false, \
}
struct server_params {
enum sc_log_level log_level;
const char *crop;
const char *codec_options;
const char *encoder_name;
struct sc_port_range port_range;
uint16_t max_size;
uint32_t bit_rate;
uint16_t max_fps;
int8_t lock_video_orientation;
bool control;
uint16_t display_id;
uint32_t display_id;
bool show_touches;
bool stay_awake;
bool force_adb_forward;
};
// init default values
void
bool
server_init(struct server *server);
// push, enable tunnel et start the server

View File

@ -8,8 +8,6 @@
#include <SDL2/SDL_thread.h>
#include <unistd.h>
#include "config.h"
#include "compat.h"
#include "decoder.h"
#include "events.h"
#include "recorder.h"

View File

@ -1,20 +1,20 @@
#ifndef STREAM_H
#define STREAM_H
#include "common.h"
#include <stdbool.h>
#include <stdint.h>
#include <libavformat/avformat.h>
#include <SDL2/SDL_atomic.h>
#include <SDL2/SDL_thread.h>
#include "config.h"
#include "util/net.h"
struct video_buffer;
struct stream {
socket_t socket;
struct video_buffer *video_buffer;
SDL_Thread *thread;
struct decoder *decoder;
struct recorder *recorder;

View File

@ -1,13 +1,4 @@
// for portability
#define _POSIX_SOURCE // for kill()
#define _BSD_SOURCE // for readlink()
// modern glibc will complain without this
#define _DEFAULT_SOURCE
#include "command.h"
#include "config.h"
#include "util/process.h"
#include <errno.h>
#include <fcntl.h>
@ -17,14 +8,13 @@
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include "util/log.h"
bool
cmd_search(const char *file) {
search_executable(const char *file) {
char *path = getenv("PATH");
if (!path)
return false;
@ -60,7 +50,7 @@ cmd_search(const char *file) {
}
enum process_result
cmd_execute(const char *const argv[], pid_t *pid) {
process_execute(const char *const argv[], pid_t *pid) {
int fd[2];
if (pipe(fd) == -1) {
@ -122,29 +112,37 @@ end:
}
bool
cmd_terminate(pid_t pid) {
process_terminate(pid_t pid) {
if (pid <= 0) {
LOGC("Requested to kill %d, this is an error. Please report the bug.\n",
(int) pid);
abort();
}
return kill(pid, SIGTERM) != -1;
return kill(pid, SIGKILL) != -1;
}
bool
cmd_simple_wait(pid_t pid, int *exit_code) {
int status;
exit_code_t
process_wait(pid_t pid, bool close) {
int code;
if (waitpid(pid, &status, 0) == -1 || !WIFEXITED(status)) {
int options = WEXITED;
if (!close) {
options |= WNOWAIT;
}
siginfo_t info;
int r = waitid(P_PID, pid, &info, options);
if (r == -1 || info.si_code != CLD_EXITED) {
// could not wait, or exited unexpectedly, probably by a signal
code = -1;
code = NO_EXIT_CODE;
} else {
code = WEXITSTATUS(status);
code = info.si_status;
}
if (exit_code) {
*exit_code = code;
}
return !code;
return code;
}
void
process_close(pid_t pid) {
process_wait(pid, true); // ignore exit code
}
char *
@ -158,7 +156,7 @@ get_executable_path(void) {
return NULL;
}
buf[len] = '\0';
return SDL_strdup(buf);
return strdup(buf);
#else
// in practice, we only need this feature for portable builds, only used on
// Windows, so we don't care implementing it for every platform

View File

@ -1,8 +1,8 @@
#include "command.h"
#include "util/process.h"
#include <assert.h>
#include <sys/stat.h>
#include "config.h"
#include "util/log.h"
#include "util/str_util.h"
@ -21,7 +21,7 @@ build_cmd(char *cmd, size_t len, const char *const argv[]) {
}
enum process_result
cmd_execute(const char *const argv[], HANDLE *handle) {
process_execute(const char *const argv[], HANDLE *handle) {
STARTUPINFOW si;
PROCESS_INFORMATION pi;
memset(&si, 0, sizeof(si));
@ -39,14 +39,9 @@ cmd_execute(const char *const argv[], HANDLE *handle) {
return PROCESS_ERROR_GENERIC;
}
#ifdef WINDOWS_NOCONSOLE
int flags = CREATE_NO_WINDOW;
#else
int flags = 0;
#endif
if (!CreateProcessW(NULL, wide, NULL, NULL, FALSE, flags, NULL, NULL, &si,
if (!CreateProcessW(NULL, wide, NULL, NULL, FALSE, 0, NULL, NULL, &si,
&pi)) {
SDL_free(wide);
free(wide);
*handle = NULL;
if (GetLastError() == ERROR_FILE_NOT_FOUND) {
return PROCESS_ERROR_MISSING_BINARY;
@ -54,28 +49,35 @@ cmd_execute(const char *const argv[], HANDLE *handle) {
return PROCESS_ERROR_GENERIC;
}
SDL_free(wide);
free(wide);
*handle = pi.hProcess;
return PROCESS_SUCCESS;
}
bool
cmd_terminate(HANDLE handle) {
return TerminateProcess(handle, 1) && CloseHandle(handle);
process_terminate(HANDLE handle) {
return TerminateProcess(handle, 1);
}
bool
cmd_simple_wait(HANDLE handle, DWORD *exit_code) {
exit_code_t
process_wait(HANDLE handle, bool close) {
DWORD code;
if (WaitForSingleObject(handle, INFINITE) != WAIT_OBJECT_0
|| !GetExitCodeProcess(handle, &code)) {
// could not wait or retrieve the exit code
code = -1; // max value, it's unsigned
code = NO_EXIT_CODE; // max value, it's unsigned
}
if (exit_code) {
*exit_code = code;
if (close) {
CloseHandle(handle);
}
return !code;
return code;
}
void
process_close(HANDLE handle) {
bool closed = CloseHandle(handle);
assert(closed);
(void) closed;
}
char *
@ -103,7 +105,7 @@ is_regular_file(const char *path) {
struct _stat path_stat;
int r = _wstat(wide_path, &path_stat);
SDL_free(wide_path);
free(wide_path);
if (r) {
perror("stat");

View File

@ -6,7 +6,6 @@
#include <stdio.h>
#include <stdlib.h>
#include "config.h"
#include "util/log.h"
struct index {

View File

@ -1,9 +1,9 @@
#ifndef TINYXPM_H
#define TINYXPM_H
#include <SDL2/SDL.h>
#include "common.h"
#include "config.h"
#include <SDL2/SDL.h>
SDL_Surface *
read_xpm(char *xpm[]);

View File

@ -1,11 +1,11 @@
#ifndef BUFFER_UTIL_H
#define BUFFER_UTIL_H
#include "common.h"
#include <stdbool.h>
#include <stdint.h>
#include "config.h"
static inline void
buffer_write16be(uint8_t *buf, uint16_t value) {
buf[0] = value >> 8;
@ -33,7 +33,7 @@ buffer_read16be(const uint8_t *buf) {
static inline uint32_t
buffer_read32be(const uint8_t *buf) {
return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
return ((uint32_t) buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
}
static inline uint64_t

View File

@ -2,11 +2,11 @@
#ifndef CBUF_H
#define CBUF_H
#include "common.h"
#include <stdbool.h>
#include <unistd.h>
#include "config.h"
// To define a circular buffer type of 20 ints:
// struct cbuf_int CBUF(int, 20);
//

View File

@ -1,10 +1,11 @@
#ifndef LOCK_H
#define LOCK_H
#include "common.h"
#include <stdint.h>
#include <SDL2/SDL_mutex.h>
#include "config.h"
#include "log.h"
static inline void

View File

@ -3,7 +3,6 @@
#include <stdio.h>
#include <SDL2/SDL_platform.h>
#include "config.h"
#include "log.h"
#ifdef __WINDOWS__

View File

@ -1,6 +1,8 @@
#ifndef NET_H
#define NET_H
#include "common.h"
#include <stdbool.h>
#include <stdint.h>
#include <SDL2/SDL_platform.h>
@ -17,8 +19,6 @@
typedef int socket_t;
#endif
#include "config.h"
bool
net_init(void);

21
app/src/util/process.c Normal file
View File

@ -0,0 +1,21 @@
#include "process.h"
#include "log.h"
bool
process_check_success(process_t proc, const char *name, bool close) {
if (proc == PROCESS_NONE) {
LOGE("Could not execute \"%s\"", name);
return false;
}
exit_code_t exit_code = process_wait(proc, close);
if (exit_code) {
if (exit_code != NO_EXIT_CODE) {
LOGE("\"%s\" returned with value %" PRIexitcode, name, exit_code);
} else {
LOGE("\"%s\" exited unexpectedly", name);
}
return false;
}
return true;
}

View File

@ -1,8 +1,9 @@
#ifndef COMMAND_H
#define COMMAND_H
#ifndef SC_PROCESS_H
#define SC_PROCESS_H
#include "common.h"
#include <stdbool.h>
#include <inttypes.h>
#ifdef _WIN32
@ -12,11 +13,7 @@
# define PATH_SEPARATOR '\\'
# define PRIexitcode "lu"
// <https://stackoverflow.com/a/44383330/1987178>
# ifdef _WIN64
# define PRIsizet PRIu64
# else
# define PRIsizet PRIu32
# endif
# define PRIsizet "Iu"
# define PROCESS_NONE NULL
# define NO_EXIT_CODE -1u // max value as unsigned
typedef HANDLE process_t;
@ -35,58 +32,45 @@
#endif
#include "config.h"
enum process_result {
PROCESS_SUCCESS,
PROCESS_ERROR_GENERIC,
PROCESS_ERROR_MISSING_BINARY,
};
#ifndef __WINDOWS__
bool
cmd_search(const char *file);
#endif
// execute the command and write the result to the output parameter "process"
enum process_result
cmd_execute(const char *const argv[], process_t *process);
process_execute(const char *const argv[], process_t *process);
// kill the process
bool
cmd_terminate(process_t pid);
process_terminate(process_t pid);
bool
cmd_simple_wait(process_t pid, exit_code_t *exit_code);
// wait and close the process (like waitpid())
// the "close" flag indicates if the process must be "closed" (reaped)
// (passing false is equivalent to enable WNOWAIT in waitid())
exit_code_t
process_wait(process_t pid, bool close);
process_t
adb_execute(const char *serial, const char *const adb_cmd[], size_t len);
process_t
adb_forward(const char *serial, uint16_t local_port,
const char *device_socket_name);
process_t
adb_forward_remove(const char *serial, uint16_t local_port);
process_t
adb_reverse(const char *serial, const char *device_socket_name,
uint16_t local_port);
process_t
adb_reverse_remove(const char *serial, const char *device_socket_name);
process_t
adb_push(const char *serial, const char *local, const char *remote);
process_t
adb_install(const char *serial, const char *local);
// close the process
//
// Semantically, process_wait(close) = process_wait(noclose) + process_close
void
process_close(process_t pid);
// convenience function to wait for a successful process execution
// automatically log process errors with the provided process name
bool
process_check_success(process_t proc, const char *name);
process_check_success(process_t proc, const char *name, bool close);
#ifndef _WIN32
// only used to find package manager, not implemented for Windows
bool
search_executable(const char *file);
#endif
// return the absolute path of the executable (the scrcpy binary)
// may be NULL on error; to be freed by SDL_free
// may be NULL on error; to be freed by free()
char *
get_executable_path(void);

View File

@ -2,12 +2,12 @@
#ifndef QUEUE_H
#define QUEUE_H
#include "common.h"
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include "config.h"
// To define a queue type of "struct foo":
// struct queue_foo QUEUE(struct foo);
#define QUEUE(TYPE) { \

View File

@ -10,10 +10,6 @@
# include <tchar.h>
#endif
#include <SDL2/SDL_stdinc.h>
#include "config.h"
size_t
xstrncpy(char *dest, const char *src, size_t n) {
size_t i;
@ -51,7 +47,7 @@ truncated:
char *
strquote(const char *src) {
size_t len = strlen(src);
char *quoted = SDL_malloc(len + 3);
char *quoted = malloc(len + 3);
if (!quoted) {
return NULL;
}
@ -169,7 +165,7 @@ utf8_to_wide_char(const char *utf8) {
return NULL;
}
wchar_t *wide = SDL_malloc(len * sizeof(wchar_t));
wchar_t *wide = malloc(len * sizeof(wchar_t));
if (!wide) {
return NULL;
}
@ -185,7 +181,7 @@ utf8_from_wide_char(const wchar_t *ws) {
return NULL;
}
char *utf8 = SDL_malloc(len);
char *utf8 = malloc(len);
if (!utf8) {
return NULL;
}

View File

@ -1,11 +1,11 @@
#ifndef STRUTIL_H
#define STRUTIL_H
#include "common.h"
#include <stdbool.h>
#include <stddef.h>
#include "config.h"
// like strncpy, except:
// - it copies at most n-1 chars
// - the dest string is nul-terminated

View File

@ -5,7 +5,6 @@
#include <libavutil/avutil.h>
#include <libavformat/avformat.h>
#include "config.h"
#include "util/lock.h"
#include "util/log.h"

View File

@ -1,10 +1,11 @@
#ifndef VIDEO_BUFFER_H
#define VIDEO_BUFFER_H
#include "common.h"
#include <stdbool.h>
#include <SDL2/SDL_mutex.h>
#include "config.h"
#include "fps_counter.h"
// forward declarations

View File

@ -1,3 +1,5 @@
#include "common.h"
#include <assert.h>
#include "util/buffer_util.h"

View File

@ -1,3 +1,5 @@
#include "common.h"
#include <assert.h>
#include <string.h>

View File

@ -1,8 +1,9 @@
#include "common.h"
#include <assert.h>
#include <string.h>
#include "cli.h"
#include "common.h"
#include "scrcpy.h"
static void test_flag_version(void) {

View File

@ -1,3 +1,5 @@
#include "common.h"
#include <assert.h>
#include <string.h>
@ -15,7 +17,7 @@ static void test_serialize_inject_keycode(void) {
};
unsigned char buf[CONTROL_MSG_MAX_SIZE];
int size = control_msg_serialize(&msg, buf);
size_t size = control_msg_serialize(&msg, buf);
assert(size == 14);
const unsigned char expected[] = {
@ -37,7 +39,7 @@ static void test_serialize_inject_text(void) {
};
unsigned char buf[CONTROL_MSG_MAX_SIZE];
int size = control_msg_serialize(&msg, buf);
size_t size = control_msg_serialize(&msg, buf);
assert(size == 18);
const unsigned char expected[] = {
@ -57,7 +59,7 @@ static void test_serialize_inject_text_long(void) {
msg.inject_text.text = text;
unsigned char buf[CONTROL_MSG_MAX_SIZE];
int size = control_msg_serialize(&msg, buf);
size_t size = control_msg_serialize(&msg, buf);
assert(size == 5 + CONTROL_MSG_INJECT_TEXT_MAX_LENGTH);
unsigned char expected[5 + CONTROL_MSG_INJECT_TEXT_MAX_LENGTH];
@ -93,7 +95,7 @@ static void test_serialize_inject_touch_event(void) {
};
unsigned char buf[CONTROL_MSG_MAX_SIZE];
int size = control_msg_serialize(&msg, buf);
size_t size = control_msg_serialize(&msg, buf);
assert(size == 28);
const unsigned char expected[] = {
@ -128,7 +130,7 @@ static void test_serialize_inject_scroll_event(void) {
};
unsigned char buf[CONTROL_MSG_MAX_SIZE];
int size = control_msg_serialize(&msg, buf);
size_t size = control_msg_serialize(&msg, buf);
assert(size == 21);
const unsigned char expected[] = {
@ -147,7 +149,7 @@ static void test_serialize_back_or_screen_on(void) {
};
unsigned char buf[CONTROL_MSG_MAX_SIZE];
int size = control_msg_serialize(&msg, buf);
size_t size = control_msg_serialize(&msg, buf);
assert(size == 1);
const unsigned char expected[] = {
@ -162,7 +164,7 @@ static void test_serialize_expand_notification_panel(void) {
};
unsigned char buf[CONTROL_MSG_MAX_SIZE];
int size = control_msg_serialize(&msg, buf);
size_t size = control_msg_serialize(&msg, buf);
assert(size == 1);
const unsigned char expected[] = {
@ -177,7 +179,7 @@ static void test_serialize_collapse_notification_panel(void) {
};
unsigned char buf[CONTROL_MSG_MAX_SIZE];
int size = control_msg_serialize(&msg, buf);
size_t size = control_msg_serialize(&msg, buf);
assert(size == 1);
const unsigned char expected[] = {
@ -192,7 +194,7 @@ static void test_serialize_get_clipboard(void) {
};
unsigned char buf[CONTROL_MSG_MAX_SIZE];
int size = control_msg_serialize(&msg, buf);
size_t size = control_msg_serialize(&msg, buf);
assert(size == 1);
const unsigned char expected[] = {
@ -211,7 +213,7 @@ static void test_serialize_set_clipboard(void) {
};
unsigned char buf[CONTROL_MSG_MAX_SIZE];
int size = control_msg_serialize(&msg, buf);
size_t size = control_msg_serialize(&msg, buf);
assert(size == 19);
const unsigned char expected[] = {
@ -232,7 +234,7 @@ static void test_serialize_set_screen_power_mode(void) {
};
unsigned char buf[CONTROL_MSG_MAX_SIZE];
int size = control_msg_serialize(&msg, buf);
size_t size = control_msg_serialize(&msg, buf);
assert(size == 2);
const unsigned char expected[] = {
@ -248,7 +250,7 @@ static void test_serialize_rotate_device(void) {
};
unsigned char buf[CONTROL_MSG_MAX_SIZE];
int size = control_msg_serialize(&msg, buf);
size_t size = control_msg_serialize(&msg, buf);
assert(size == 1);
const unsigned char expected[] = {

View File

@ -1,3 +1,5 @@
#include "common.h"
#include <assert.h>
#include <string.h>

View File

@ -1,3 +1,5 @@
#include "common.h"
#include <assert.h>
#include "util/queue.h"

View File

@ -1,8 +1,9 @@
#include "common.h"
#include <assert.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <SDL2/SDL.h>
#include "util/str_util.h"
@ -136,7 +137,7 @@ static void test_strquote(void) {
// add '"' at the beginning and the end
assert(!strcmp("\"abcde\"", out));
SDL_free(out);
free(out);
}
static void test_utf8_truncate(void) {

View File

@ -7,7 +7,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.2'
classpath 'com.android.tools.build:gradle:4.0.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@ -19,6 +19,9 @@ allprojects {
google()
jcenter()
}
tasks.withType(JavaCompile) {
options.compilerArgs << "-Xlint:deprecation"
}
}
task clean(type: Delete) {

View File

@ -17,4 +17,4 @@ endian = 'little'
[properties]
prebuilt_ffmpeg_shared = 'ffmpeg-4.3.1-win32-shared'
prebuilt_ffmpeg_dev = 'ffmpeg-4.3.1-win32-dev'
prebuilt_sdl2 = 'SDL2-2.0.12/i686-w64-mingw32'
prebuilt_sdl2 = 'SDL2-2.0.14/i686-w64-mingw32'

View File

@ -17,4 +17,4 @@ endian = 'little'
[properties]
prebuilt_ffmpeg_shared = 'ffmpeg-4.3.1-win64-shared'
prebuilt_ffmpeg_dev = 'ffmpeg-4.3.1-win64-dev'
prebuilt_sdl2 = 'SDL2-2.0.12/x86_64-w64-mingw32'
prebuilt_sdl2 = 'SDL2-2.0.14/x86_64-w64-mingw32'

4
data/scrcpy-console.bat Normal file
View File

@ -0,0 +1,4 @@
@echo off
scrcpy.exe %*
:: if the exit code is >= 1, then pause
if errorlevel 1 pause

View File

@ -0,0 +1 @@
CreateObject("Wscript.Shell").Run "cmd /c scrcpy.exe", 0, false

View File

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

View File

@ -1,7 +1,6 @@
option('compile_app', type: 'boolean', value: true, description: 'Build the client')
option('compile_server', type: 'boolean', value: true, description: 'Build the server')
option('crossbuild_windows', type: 'boolean', value: false, description: 'Build for Windows from Linux')
option('windows_noconsole', type: 'boolean', value: false, description: 'Disable console on Windows (pass -mwindows flag)')
option('prebuilt_server', type: 'string', description: 'Path of the prebuilt server')
option('portable', type: 'boolean', value: false, description: 'Use scrcpy-server from the same directory as the scrcpy executable')
option('hidpi_support', type: 'boolean', value: true, description: 'Enable High DPI support')

View File

@ -10,31 +10,31 @@ prepare-win32: prepare-sdl2 prepare-ffmpeg-shared-win32 prepare-ffmpeg-dev-win32
prepare-win64: prepare-sdl2 prepare-ffmpeg-shared-win64 prepare-ffmpeg-dev-win64 prepare-adb
prepare-ffmpeg-shared-win32:
@./prepare-dep https://ffmpeg.zeranoe.com/builds/win32/shared/ffmpeg-4.3.1-win32-shared.zip \
@./prepare-dep https://github.com/Genymobile/scrcpy/releases/download/v1.16/ffmpeg-4.3.1-win32-shared.zip \
357af9901a456f4dcbacd107e83a934d344c9cb07ddad8aaf80612eeab7d26d2 \
ffmpeg-4.3.1-win32-shared
prepare-ffmpeg-dev-win32:
@./prepare-dep https://ffmpeg.zeranoe.com/builds/win32/dev/ffmpeg-4.3.1-win32-dev.zip \
@./prepare-dep https://github.com/Genymobile/scrcpy/releases/download/v1.16/ffmpeg-4.3.1-win32-dev.zip \
230efb08e9bcf225bd474da29676c70e591fc94d8790a740ca801408fddcb78b \
ffmpeg-4.3.1-win32-dev
prepare-ffmpeg-shared-win64:
@./prepare-dep https://ffmpeg.zeranoe.com/builds/win64/shared/ffmpeg-4.3.1-win64-shared.zip \
@./prepare-dep https://github.com/Genymobile/scrcpy/releases/download/v1.16/ffmpeg-4.3.1-win64-shared.zip \
dd29b7f92f48dead4dd940492c7509138c0f99db445076d0a597007298a79940 \
ffmpeg-4.3.1-win64-shared
prepare-ffmpeg-dev-win64:
@./prepare-dep https://ffmpeg.zeranoe.com/builds/win64/dev/ffmpeg-4.3.1-win64-dev.zip \
@./prepare-dep https://github.com/Genymobile/scrcpy/releases/download/v1.16/ffmpeg-4.3.1-win64-dev.zip \
2e8038242cf8e1bd095c2978f196ff0462b122cc6ef7e74626a6af15459d8b81 \
ffmpeg-4.3.1-win64-dev
prepare-sdl2:
@./prepare-dep https://libsdl.org/release/SDL2-devel-2.0.12-mingw.tar.gz \
e614a60f797e35ef9f3f96aef3dc6a1d786de3cc7ca6216f97e435c0b6aafc46 \
SDL2-2.0.12
@./prepare-dep https://libsdl.org/release/SDL2-devel-2.0.14-mingw.tar.gz \
405eaff3eb18f2e08fe669ef9e63bc9a8710b7d343756f238619761e9b60407d \
SDL2-2.0.14
prepare-adb:
@./prepare-dep https://dl.google.com/android/repository/platform-tools_r30.0.4-windows.zip \
413182fff6c5957911e231b9e97e6be4fc6a539035e3dfb580b5c54bd5950fee \
@./prepare-dep https://dl.google.com/android/repository/platform-tools_r30.0.5-windows.zip \
549ba2bdc31f335eb8a504f005f77606a479cc216d6b64a3e8b64c780003661f \
platform-tools

View File

@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
set -e
url="$1"
sum="$2"

View File

@ -9,21 +9,20 @@
# the server to the device.
.PHONY: default clean \
test \
build-server \
prepare-deps-win32 prepare-deps-win64 \
build-win32 build-win32-noconsole \
build-win64 build-win64-noconsole \
build-win32 build-win64 \
dist-win32 dist-win64 \
zip-win32 zip-win64 \
sums release
release
GRADLE ?= ./gradlew
TEST_BUILD_DIR := build-test
SERVER_BUILD_DIR := build-server
WIN32_BUILD_DIR := build-win32
WIN32_NOCONSOLE_BUILD_DIR := build-win32-noconsole
WIN64_BUILD_DIR := build-win64
WIN64_NOCONSOLE_BUILD_DIR := build-win64-noconsole
DIST := dist
WIN32_TARGET_DIR := scrcpy-win32
@ -33,19 +32,35 @@ VERSION := $(shell git describe --tags --always)
WIN32_TARGET := $(WIN32_TARGET_DIR)-$(VERSION).zip
WIN64_TARGET := $(WIN64_TARGET_DIR)-$(VERSION).zip
release: clean zip-win32 zip-win64 sums
@echo "Windows archives generated in $(DIST)/"
RELEASE_DIR := release-$(VERSION)
release: clean test build-server zip-win32 zip-win64
mkdir -p "$(RELEASE_DIR)"
cp "$(SERVER_BUILD_DIR)/server/scrcpy-server" \
"$(RELEASE_DIR)/scrcpy-server-$(VERSION)"
cp "$(DIST)/$(WIN32_TARGET)" "$(RELEASE_DIR)"
cp "$(DIST)/$(WIN64_TARGET)" "$(RELEASE_DIR)"
cd "$(RELEASE_DIR)" && \
sha256sum "scrcpy-server-$(VERSION)" \
"scrcpy-win32-$(VERSION).zip" \
"scrcpy-win64-$(VERSION).zip" > SHA256SUMS.txt
@echo "Release generated in $(RELEASE_DIR)/"
clean:
$(GRADLE) clean
rm -rf "$(SERVER_BUILD_DIR)" "$(WIN32_BUILD_DIR)" "$(WIN64_BUILD_DIR)" \
"$(WIN32_NOCONSOLE_BUILD_DIR)" "$(WIN64_NOCONSOLE_BUILD_DIR)" "$(DIST)"
rm -rf "$(DIST)" "$(TEST_BUILD_DIR)" "$(SERVER_BUILD_DIR)" \
"$(WIN32_BUILD_DIR)" "$(WIN64_BUILD_DIR)"
test:
[ -d "$(TEST_BUILD_DIR)" ] || ( mkdir "$(TEST_BUILD_DIR)" && \
meson "$(TEST_BUILD_DIR)" -Db_sanitize=address )
ninja -C "$(TEST_BUILD_DIR)"
$(GRADLE) -p server check
build-server:
[ -d "$(SERVER_BUILD_DIR)" ] || ( mkdir "$(SERVER_BUILD_DIR)" && \
meson "$(SERVER_BUILD_DIR)" \
--buildtype release -Dcompile_app=false )
ninja -C "$(SERVER_BUILD_DIR)"
meson "$(SERVER_BUILD_DIR)" --buildtype release -Dcompile_app=false )
ninja -C "$(SERVER_BUILD_DIR)"
prepare-deps-win32:
-$(MAKE) -C prebuilt-deps prepare-win32
@ -60,17 +75,6 @@ build-win32: prepare-deps-win32
-Dportable=true )
ninja -C "$(WIN32_BUILD_DIR)"
build-win32-noconsole: prepare-deps-win32
[ -d "$(WIN32_NOCONSOLE_BUILD_DIR)" ] || ( mkdir "$(WIN32_NOCONSOLE_BUILD_DIR)" && \
meson "$(WIN32_NOCONSOLE_BUILD_DIR)" \
--cross-file cross_win32.txt \
--buildtype release --strip -Db_lto=true \
-Dcrossbuild_windows=true \
-Dcompile_server=false \
-Dwindows_noconsole=true \
-Dportable=true )
ninja -C "$(WIN32_NOCONSOLE_BUILD_DIR)"
prepare-deps-win64:
-$(MAKE) -C prebuilt-deps prepare-win64
@ -84,22 +88,12 @@ build-win64: prepare-deps-win64
-Dportable=true )
ninja -C "$(WIN64_BUILD_DIR)"
build-win64-noconsole: prepare-deps-win64
[ -d "$(WIN64_NOCONSOLE_BUILD_DIR)" ] || ( mkdir "$(WIN64_NOCONSOLE_BUILD_DIR)" && \
meson "$(WIN64_NOCONSOLE_BUILD_DIR)" \
--cross-file cross_win64.txt \
--buildtype release --strip -Db_lto=true \
-Dcrossbuild_windows=true \
-Dcompile_server=false \
-Dwindows_noconsole=true \
-Dportable=true )
ninja -C "$(WIN64_NOCONSOLE_BUILD_DIR)"
dist-win32: build-server build-win32 build-win32-noconsole
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 "$(WIN32_NOCONSOLE_BUILD_DIR)"/app/scrcpy.exe "$(DIST)/$(WIN32_TARGET_DIR)/scrcpy-noconsole.exe"
cp data/scrcpy-console.bat "$(DIST)/$(WIN32_TARGET_DIR)"
cp data/scrcpy-noconsole.vbs "$(DIST)/$(WIN32_TARGET_DIR)"
cp prebuilt-deps/ffmpeg-4.3.1-win32-shared/bin/avutil-56.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp prebuilt-deps/ffmpeg-4.3.1-win32-shared/bin/avcodec-58.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp prebuilt-deps/ffmpeg-4.3.1-win32-shared/bin/avformat-58.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
@ -108,13 +102,14 @@ dist-win32: build-server build-win32 build-win32-noconsole
cp prebuilt-deps/platform-tools/adb.exe "$(DIST)/$(WIN32_TARGET_DIR)/"
cp prebuilt-deps/platform-tools/AdbWinApi.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp prebuilt-deps/platform-tools/AdbWinUsbApi.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp prebuilt-deps/SDL2-2.0.12/i686-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp prebuilt-deps/SDL2-2.0.14/i686-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
dist-win64: build-server build-win64 build-win64-noconsole
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 "$(WIN64_NOCONSOLE_BUILD_DIR)"/app/scrcpy.exe "$(DIST)/$(WIN64_TARGET_DIR)/scrcpy-noconsole.exe"
cp data/scrcpy-console.bat "$(DIST)/$(WIN64_TARGET_DIR)"
cp data/scrcpy-noconsole.vbs "$(DIST)/$(WIN64_TARGET_DIR)"
cp prebuilt-deps/ffmpeg-4.3.1-win64-shared/bin/avutil-56.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp prebuilt-deps/ffmpeg-4.3.1-win64-shared/bin/avcodec-58.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp prebuilt-deps/ffmpeg-4.3.1-win64-shared/bin/avformat-58.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
@ -123,7 +118,7 @@ dist-win64: build-server build-win64 build-win64-noconsole
cp prebuilt-deps/platform-tools/adb.exe "$(DIST)/$(WIN64_TARGET_DIR)/"
cp prebuilt-deps/platform-tools/AdbWinApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp prebuilt-deps/platform-tools/AdbWinUsbApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp prebuilt-deps/SDL2-2.0.12/x86_64-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp prebuilt-deps/SDL2-2.0.14/x86_64-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
zip-win32: dist-win32
cd "$(DIST)/$(WIN32_TARGET_DIR)"; \
@ -132,7 +127,3 @@ zip-win32: dist-win32
zip-win64: dist-win64
cd "$(DIST)/$(WIN64_TARGET_DIR)"; \
zip -r "../$(WIN64_TARGET)" .
sums:
cd "$(DIST)"; \
sha256sum *.zip > SHA256SUMS.txt

View File

@ -1,44 +1,2 @@
#!/bin/bash
set -e
# test locally
TESTDIR=build_test
rm -rf "$TESTDIR"
# run client tests with ASAN enabled
meson "$TESTDIR" -Db_sanitize=address
ninja -C"$TESTDIR" test
# test server
GRADLE=${GRADLE:-./gradlew}
$GRADLE -p server check
BUILDDIR=build_release
rm -rf "$BUILDDIR"
meson "$BUILDDIR" --buildtype release --strip -Db_lto=true
cd "$BUILDDIR"
ninja
cd -
# build Windows releases
make -f Makefile.CrossWindows
# the generated server must be the same everywhere
cmp "$BUILDDIR/server/scrcpy-server" dist/scrcpy-win32/scrcpy-server
cmp "$BUILDDIR/server/scrcpy-server" dist/scrcpy-win64/scrcpy-server
# get version name
TAG=$(git describe --tags --always)
# create release directory
mkdir -p "release-$TAG"
cp "$BUILDDIR/server/scrcpy-server" "release-$TAG/scrcpy-server-$TAG"
cp "dist/scrcpy-win32-$TAG.zip" "release-$TAG/"
cp "dist/scrcpy-win64-$TAG.zip" "release-$TAG/"
# generate checksums
cd "release-$TAG"
sha256sum "scrcpy-server-$TAG" \
"scrcpy-win32-$TAG.zip" \
"scrcpy-win64-$TAG.zip" > SHA256SUMS.txt
echo "Release generated in release-$TAG/"
make -f release.mk

2
run
View File

@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
# Run scrcpy generated in the specified BUILDDIR.
#
# This provides the same feature as "ninja run", except that it is possible to

View File

@ -1,2 +1,2 @@
#!/bin/bash
#!/usr/bin/env bash
SCRCPY_SERVER_PATH="$MESON_BUILD_ROOT/server/scrcpy-server" "$MESON_BUILD_ROOT/app/scrcpy"

View File

@ -1,13 +1,13 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 29
compileSdkVersion 30
defaultConfig {
applicationId "com.genymobile.scrcpy"
minSdkVersion 21
targetSdkVersion 29
versionCode 19
versionName "1.16"
targetSdkVersion 30
versionCode 20
versionName "1.17"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
@ -20,7 +20,7 @@ android {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
testImplementation 'junit:junit:4.12'
testImplementation 'junit:junit:4.13'
}
apply from: "$project.rootDir/config/android-checkstyle.gradle"

View File

@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
#
# This script generates the scrcpy binary "manually" (without gradle).
#
@ -12,10 +12,10 @@
set -e
SCRCPY_DEBUG=false
SCRCPY_VERSION_NAME=1.16
SCRCPY_VERSION_NAME=1.17
PLATFORM=${ANDROID_PLATFORM:-29}
BUILD_TOOLS=${ANDROID_BUILD_TOOLS:-29.0.2}
PLATFORM=${ANDROID_PLATFORM:-30}
BUILD_TOOLS=${ANDROID_BUILD_TOOLS:-30.0.0}
BUILD_DIR="$(realpath ${BUILD_DIR:-build_manual})"
CLASSES_DIR="$BUILD_DIR/classes"

View File

@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
# Wrapper script to invoke gradle from meson
set -e

View File

@ -205,9 +205,13 @@ public class Controller {
}
}
// Right-click and middle-click only work if the source is a mouse
boolean nonPrimaryButtonPressed = (buttons & ~MotionEvent.BUTTON_PRIMARY) != 0;
int source = nonPrimaryButtonPressed ? InputDevice.SOURCE_MOUSE : InputDevice.SOURCE_TOUCHSCREEN;
MotionEvent event = MotionEvent
.obtain(lastTouchDown, now, action, pointerCount, pointerProperties, pointerCoords, 0, buttons, 1f, 1f, DEVICE_ID_VIRTUAL, 0,
InputDevice.SOURCE_TOUCHSCREEN, 0);
.obtain(lastTouchDown, now, action, pointerCount, pointerProperties, pointerCoords, 0, buttons, 1f, 1f, DEVICE_ID_VIRTUAL, 0, source,
0);
return device.injectEvent(event);
}

View File

@ -0,0 +1,23 @@
package com.genymobile.scrcpy;
import android.media.MediaCodecInfo;
public class InvalidEncoderException extends RuntimeException {
private final String name;
private final MediaCodecInfo[] availableEncoders;
public InvalidEncoderException(String name, MediaCodecInfo[] availableEncoders) {
super("There is no encoder having name '" + name + '"');
this.name = name;
this.availableEncoders = availableEncoders;
}
public String getName() {
return name;
}
public MediaCodecInfo[] getAvailableEncoders() {
return availableEncoders;
}
}

View File

@ -16,6 +16,7 @@ public class Options {
private boolean showTouches;
private boolean stayAwake;
private String codecOptions;
private String encoderName;
public Ln.Level getLogLevel() {
return logLevel;
@ -120,4 +121,12 @@ public class Options {
public void setCodecOptions(String codecOptions) {
this.codecOptions = codecOptions;
}
public String getEncoderName() {
return encoderName;
}
public void setEncoderName(String encoderName) {
this.encoderName = encoderName;
}
}

View File

@ -5,6 +5,7 @@ import com.genymobile.scrcpy.wrappers.SurfaceControl;
import android.graphics.Rect;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
import android.media.MediaFormat;
import android.os.IBinder;
import android.view.Surface;
@ -12,6 +13,8 @@ import android.view.Surface;
import java.io.FileDescriptor;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
@ -26,17 +29,19 @@ public class ScreenEncoder implements Device.RotationListener {
private final AtomicBoolean rotationChanged = new AtomicBoolean();
private final ByteBuffer headerBuffer = ByteBuffer.allocate(12);
private String encoderName;
private List<CodecOption> codecOptions;
private int bitRate;
private int maxFps;
private boolean sendFrameMeta;
private long ptsOrigin;
public ScreenEncoder(boolean sendFrameMeta, int bitRate, int maxFps, List<CodecOption> codecOptions) {
public ScreenEncoder(boolean sendFrameMeta, int bitRate, int maxFps, List<CodecOption> codecOptions, String encoderName) {
this.sendFrameMeta = sendFrameMeta;
this.bitRate = bitRate;
this.maxFps = maxFps;
this.codecOptions = codecOptions;
this.encoderName = encoderName;
}
@Override
@ -69,7 +74,7 @@ public class ScreenEncoder implements Device.RotationListener {
boolean alive;
try {
do {
MediaCodec codec = createCodec();
MediaCodec codec = createCodec(encoderName);
IBinder display = createDisplay();
ScreenInfo screenInfo = device.getScreenInfo();
Rect contentRect = screenInfo.getContentRect();
@ -150,8 +155,30 @@ public class ScreenEncoder implements Device.RotationListener {
IO.writeFully(fd, headerBuffer);
}
private static MediaCodec createCodec() throws IOException {
return MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);
private static MediaCodecInfo[] listEncoders() {
List<MediaCodecInfo> result = new ArrayList<>();
MediaCodecList list = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
for (MediaCodecInfo codecInfo : list.getCodecInfos()) {
if (codecInfo.isEncoder() && Arrays.asList(codecInfo.getSupportedTypes()).contains(MediaFormat.MIMETYPE_VIDEO_AVC)) {
result.add(codecInfo);
}
}
return result.toArray(new MediaCodecInfo[result.size()]);
}
private static MediaCodec createCodec(String encoderName) throws IOException {
if (encoderName != null) {
Ln.d("Creating encoder by name: '" + encoderName + "'");
try {
return MediaCodec.createByCodecName(encoderName);
} catch (IllegalArgumentException e) {
MediaCodecInfo[] encoders = listEncoders();
throw new InvalidEncoderException(encoderName, encoders);
}
}
MediaCodec codec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);
Ln.d("Using encoder: '" + codec.getName() + "'");
return codec;
}
private static void setCodecOption(MediaFormat format, CodecOption codecOption) {

View File

@ -4,6 +4,7 @@ import com.genymobile.scrcpy.wrappers.ContentProvider;
import android.graphics.Rect;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.os.BatteryManager;
import android.os.Build;
@ -54,14 +55,17 @@ public final class Server {
boolean tunnelForward = options.isTunnelForward();
try (DesktopConnection connection = DesktopConnection.open(device, tunnelForward)) {
ScreenEncoder screenEncoder = new ScreenEncoder(options.getSendFrameMeta(), options.getBitRate(), options.getMaxFps(), codecOptions);
ScreenEncoder screenEncoder = new ScreenEncoder(options.getSendFrameMeta(), options.getBitRate(), options.getMaxFps(), codecOptions,
options.getEncoderName());
Thread controllerThread = null;
Thread deviceMessageSenderThread = null;
if (options.getControl()) {
final Controller controller = new Controller(device, connection);
// asynchronous
startController(controller);
startDeviceMessageSender(controller.getSender());
controllerThread = startController(controller);
deviceMessageSenderThread = startDeviceMessageSender(controller.getSender());
device.setClipboardListener(new Device.ClipboardListener() {
@Override
@ -77,12 +81,19 @@ public final class Server {
} catch (IOException e) {
// this is expected on close
Ln.d("Screen streaming stopped");
} finally {
if (controllerThread != null) {
controllerThread.interrupt();
}
if (deviceMessageSenderThread != null) {
deviceMessageSenderThread.interrupt();
}
}
}
}
private static void startController(final Controller controller) {
new Thread(new Runnable() {
private static Thread startController(final Controller controller) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
@ -92,11 +103,13 @@ public final class Server {
Ln.d("Controller stopped");
}
}
}).start();
});
thread.start();
return thread;
}
private static void startDeviceMessageSender(final DeviceMessageSender sender) {
new Thread(new Runnable() {
private static Thread startDeviceMessageSender(final DeviceMessageSender sender) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
@ -106,7 +119,9 @@ public final class Server {
Ln.d("Device message sender stopped");
}
}
}).start();
});
thread.start();
return thread;
}
private static Options createOptions(String... args) {
@ -120,7 +135,7 @@ public final class Server {
"The server version (" + BuildConfig.VERSION_NAME + ") does not match the client " + "(" + clientVersion + ")");
}
final int expectedParameters = 14;
final int expectedParameters = 15;
if (args.length != expectedParameters) {
throw new IllegalArgumentException("Expecting " + expectedParameters + " parameters");
}
@ -167,6 +182,9 @@ public final class Server {
String codecOptions = args[13];
options.setCodecOptions(codecOptions);
String encoderName = "-".equals(args[14]) ? null : args[14];
options.setEncoderName(encoderName);
return options;
}
@ -206,6 +224,15 @@ public final class Server {
Ln.e(" scrcpy --display " + id);
}
}
} else if (e instanceof InvalidEncoderException) {
InvalidEncoderException iee = (InvalidEncoderException) e;
MediaCodecInfo[] encoders = iee.getAvailableEncoders();
if (encoders != null && encoders.length > 0) {
Ln.e("Try to use one of the available encoders:");
for (MediaCodecInfo encoder : encoders) {
Ln.e(" scrcpy --encoder-name '" + encoder.getName() + "'");
}
}
}
}

View File

@ -16,6 +16,7 @@ public final class Workarounds {
// not instantiable
}
@SuppressWarnings("deprecation")
public static void prepareMainLooper() {
// Some devices internally create a Handler when creating an input Surface, causing an exception:
// "Can't create handler inside thread that has not called Looper.prepare()"

View File

@ -35,7 +35,7 @@ public class ContentProvider implements Closeable {
private final IBinder token;
private Method callMethod;
private boolean callMethodLegacy;
private int callMethodVersion;
ContentProvider(ActivityManager manager, Object provider, String name, IBinder token) {
this.manager = manager;
@ -46,12 +46,20 @@ public class ContentProvider implements Closeable {
private Method getCallMethod() throws NoSuchMethodException {
if (callMethod == null) {
try {
callMethod = provider.getClass().getMethod("call", String.class, String.class, String.class, String.class, Bundle.class);
callMethod = provider.getClass()
.getMethod("call", String.class, String.class, String.class, String.class, String.class, Bundle.class);
callMethodVersion = 0;
} catch (NoSuchMethodException e) {
// old version
callMethod = provider.getClass().getMethod("call", String.class, String.class, String.class, Bundle.class);
callMethodLegacy = true;
// old versions
try {
callMethod = provider.getClass().getMethod("call", String.class, String.class, String.class, String.class, Bundle.class);
callMethodVersion = 1;
} catch (NoSuchMethodException e2) {
callMethod = provider.getClass().getMethod("call", String.class, String.class, String.class, Bundle.class);
callMethodVersion = 2;
}
}
}
return callMethod;
@ -61,10 +69,16 @@ public class ContentProvider implements Closeable {
try {
Method method = getCallMethod();
Object[] args;
if (!callMethodLegacy) {
args = new Object[]{ServiceManager.PACKAGE_NAME, "settings", callMethod, arg, extras};
} else {
args = new Object[]{ServiceManager.PACKAGE_NAME, callMethod, arg, extras};
switch (callMethodVersion) {
case 0:
args = new Object[]{ServiceManager.PACKAGE_NAME, null, "settings", callMethod, arg, extras};
break;
case 1:
args = new Object[]{ServiceManager.PACKAGE_NAME, "settings", callMethod, arg, extras};
break;
default:
args = new Object[]{ServiceManager.PACKAGE_NAME, callMethod, arg, extras};
break;
}
return (Bundle) method.invoke(provider, args);
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {