Compare commits

...

66 Commits

Author SHA1 Message Date
SeungHoon Han
36debbf353 Add fallback to get DisplayInfo
PR #3416 <https://github.com/Genymobile/scrcpy/pull/3416>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2022-09-25 14:07:19 +02:00
Romain Vimont
e5e210506f Add scrcpy-console.desktop
Add a launcher which opens a terminal, and keep it open in case of
errors (so that the user has time to read error messages).

The behavior is the same as scrcpy-console.bat on Windows.

PR #3351 <https://github.com/Genymobile/scrcpy/pull/3351>
2022-09-09 19:06:29 +02:00
Chih-Hsuan Yen
a2a22f497f Use shell environment to execute launcher
Make Exec= compatible with $PATH configured in .bashrc or .zshrc…

PR #3351 <https://github.com/Genymobile/scrcpy/pull/3351>
Refs #296 <https://github.com/Genymobile/scrcpy/pull/296#discussion_r224987002>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2022-09-09 19:06:29 +02:00
Addison Snelling
51a1762cbd Add desktop entry file for Linux app launchers
Refs <https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html>

PR #3351 <https://github.com/Genymobile/scrcpy/pull/3351>
Replaces PR #296 <https://github.com/Genymobile/scrcpy/pull/296>
Fixes #295 <https://github.com/Genymobile/scrcpy/issues/295>
Fixes #748 <https://github.com/Genymobile/scrcpy/issues/748>
Fixes #1636 <https://github.com/Genymobile/scrcpy/issues/1636>

Co-authored-by: Chih-Hsuan Yen <yan12125@gmail.com>
Signed-off-by: Romain Vimont <rom@rom1v.com>
2022-09-09 19:06:29 +02:00
Romain Vimont
c1ec1d1023 Replace hardcoded 'share/' by datadir variable
Meson defines a variable for the data directory.

PR #3351 <https://github.com/Genymobile/scrcpy/pull/3351>
2022-09-09 19:06:29 +02:00
Romain Vimont
0a0a446ea6 Upgrade Android SDK to 33 2022-09-02 14:42:37 +02:00
Romain Vimont
fccfc43b9e Upgrade gradle build tools to 7.2.2
Plugin version 7.2.2.
Gradle version 7.3.3.

Refs: <https://developer.android.com/studio/releases/gradle-plugin#updating-gradle>
2022-09-02 14:40:16 +02:00
Romain Vimont
121bb71dfe Move from jcenter() to mavenCentral()
Refs <https://developer.android.com/studio/build/jcenter-migration>
Refs <https://jfrog.com/blog/into-the-sunset-bintray-jcenter-gocenter-and-chartcenter/>
2022-09-02 14:31:15 +02:00
Simon Chan
57056d078d Use precise scrolling values
Since SDL 2.0.18, the amount scrolled horizontally or vertically is
exposed as a float (between 0 and 1). Forward a precise value to the
Android device when possible.

Refs <https://wiki.libsdl.org/SDL_MouseWheelEvent>
Fixes #3363 <https://github.com/Genymobile/scrcpy/issues/3363>
PR #3369 <https://github.com/Genymobile/scrcpy/pull/3369>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2022-08-28 15:23:08 +02:00
Romain Vimont
1f138aef41 Add conversion from float to fixed-point i16
To encode float values between -1 and 1.

PR #3369 <https://github.com/Genymobile/scrcpy/pull/3369>
2022-08-28 15:23:08 +02:00
Romain Vimont
1ab6c19486 Add unit test for float encoding
PR #3369 <https://github.com/Genymobile/scrcpy/pull/3369>
2022-08-28 15:23:08 +02:00
Romain Vimont
fd3483c837 Extract conversion from float to u16 fixed-point
PR #3369 <https://github.com/Genymobile/scrcpy/pull/3369>
2022-08-28 15:23:08 +02:00
Romain Vimont
041cdf6cf5 Rename buffer_util.h to binary.h
It will allow to expose more binary util functions not related to
buffers.

PR #3369 <https://github.com/Genymobile/scrcpy/pull/3369>
2022-08-28 15:23:08 +02:00
Romain Vimont
136ab8c199 Add unit test for float decoding
PR #3369 <https://github.com/Genymobile/scrcpy/pull/3369>
2022-08-28 15:23:08 +02:00
Romain Vimont
3848ce86f1 Extract conversion from u16 fixed-point to float
PR #3369 <https://github.com/Genymobile/scrcpy/pull/3369>
2022-08-28 15:23:08 +02:00
Romain Vimont
5b8e9aa0e9 Move toUnsigned() to a Binary util class
PR #3369 <https://github.com/Genymobile/scrcpy/pull/3369>
2022-08-28 15:23:08 +02:00
Romain Vimont
3a66b5fd01 Remove deprecated meson.source_root()
This method is deprecated since Meson 0.56.0:
<https://mesonbuild.com/Release-notes-for-0-56-0.html#mesonbuild_root-and-mesonsource_root-are-deprecated>

We could replace it with meson.project_source_root(), but this would
make Meson 0.56 or above mandatory. Since the path in always computed
from the server/ directory, just add '..' to reference the root project
directory.

Refs c456e38264
2022-08-28 15:16:31 +02:00
Derek Wu
9c1722f428 Use DisplayManagerGlobal instance
Use the client instance to communicate with the DisplayManager server.

Fixes #3446 <https://github.com/Genymobile/scrcpy/issues/3446>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2022-08-19 18:03:38 +02:00
Romain Vimont
d19606eb0c Rename net_listen() parameter
For consistency with net_accept(), which necessarily uses a server
socket, name the net_listen() parameter "server_socket".
2022-08-17 16:40:45 +02:00
Romain Vimont
d23b3e88a4 Replace '%g' by '%f' as printf format
For some reason, '%g' does not work correctly with MinGW.

Refs #3369 <https://github.com/Genymobile/scrcpy/pull/3369>
PR #3399 <https://github.com/Genymobile/scrcpy/pull/3399>
2022-08-03 23:25:09 +02:00
Romain Vimont
a47848f304 Detect Windows using _WIN32 in network util
For consistency, always use _WIN32 instead of a mix of __WINDOWS__ and
_WIN32.
2022-07-27 14:54:27 +02:00
Romain Vimont
db8c1ce8e1 Fix protocol documentation in comments
Flags were in the correct order in the schema, but their description
were reversed.
2022-07-20 11:41:04 +02:00
Romain Vimont
4aeb78ece2 Add missing allocation failure check 2022-07-19 12:17:02 +02:00
Romain Vimont
396e4bd925 Add missing LOG_OOM() on malloc failure 2022-07-19 12:15:06 +02:00
Romain Vimont
7f2f5950f2 Remove useless dependencies reference
There is no libs/ directory with local jar files.
2022-06-20 21:23:05 +02:00
Romain Vimont
af4b7855e1 Remove unused stream.h
The file was not removed by 7dec225ceb.
2022-06-09 15:02:42 +02:00
Romain Vimont
b1d8c72780 Rename function to simplify
For consistency with sc_adb_parse_device(), do not include "from_output"
in the function name.
2022-06-09 15:02:42 +02:00
Romain Vimont
55e65fa270 Add missing return 0 in tests 2022-06-09 15:02:42 +02:00
Romain Vimont
69fb5f6ee1 Fix function declarations
Add missing void in function parameters list.
2022-06-09 15:02:42 +02:00
Romain Vimont
faf4535487 Reduce SHA-256 size in README and BUILD
This avoids breaking the page layout on GitHub.
2022-04-28 21:34:56 +02:00
Romain Vimont
3a99e129e6 Update links to v1.24 2022-04-28 21:03:03 +02:00
Romain Vimont
ef13d394fd Bump version to 1.24 2022-04-28 20:46:28 +02:00
Romain Vimont
0049893e10 Merge branch 'master' into dev 2022-04-28 20:46:19 +02:00
Romain Vimont
2f038c834a Revert "Make OTG window resizable"
On Windows and macOS, resizing blocks the event loop. Handling it
properly would require the same workaround as done in screen.c.

This reverts commit 436b368f9d.
2022-04-28 20:45:46 +02:00
Romain Vimont
76b3fcf986 Fix inverted check
SDL_RenderSetLogicalSize() returns 0 on success.

Refs fc8942aa03
2022-04-28 20:45:46 +02:00
Romain Vimont
05d84084ef Fix release script for platform-tools 33.0.1
These paths were not updated by commit
b8d78743f7.
2022-04-28 20:45:38 +02:00
Romain Vimont
471a360099 Use quotes for commands in documentation 2022-04-28 19:50:35 +02:00
Romain Vimont
349dcd8e7b Update installed files list in BUILD documentation 2022-04-28 19:49:34 +02:00
Romain Vimont
f9e3275d4e Upgrade FFmpeg (5.0.1) for Windows 64-bit
Use the latest version of FFmpeg in Windows 64-bit releases.
2022-04-28 19:32:14 +02:00
Romain Vimont
91706ae3d0 Upgrade SDL (2.0.22) for Windows
Include the latest version of SDL in Windows releases.
2022-04-28 19:23:59 +02:00
Romain Vimont
854a56e588 Enable linear filtering in OTG mode
This improves the icon quality with non-standard window size.

PR #3219 <https://github.com/Genymobile/scrcpy/pull/3219>
2022-04-28 19:18:16 +02:00
Romain Vimont
436b368f9d Make OTG window resizable
PR #3219 <https://github.com/Genymobile/scrcpy/pull/3219>
2022-04-28 19:18:12 +02:00
Romain Vimont
fc8942aa03 Apply requested window size in OTG mode
Fixes #3099 <https://github.com/Genymobile/scrcpy/issues/3099>
PR #3219 <https://github.com/Genymobile/scrcpy/pull/3219>
2022-04-28 19:18:09 +02:00
Romain Vimont
c6d9711109 Create OTG window with HIGHDPI flag
This will avoid poor quality with HiDPI displays.

PR #3219 <https://github.com/Genymobile/scrcpy/pull/3219>
2022-04-28 19:17:49 +02:00
Romain Vimont
0fca2ad830 Add option to not power on on start
By default, on start, the device is powered on. To prevent this
behavior, add a new option --no-power-on.

Fixes #3148 <https://github.com/Genymobile/scrcpy/issues/3148>
PR #3210 <https://github.com/Genymobile/scrcpy/pull/3210>
2022-04-28 19:12:52 +02:00
Romain Vimont
326897a0d4 Add missing mouse shortcuts in --help
Document 4th-click and 5th-click shortcuts.

Fixes #3122 <https://github.com/Genymobile/scrcpy/issues/3122>
2022-04-25 18:35:06 +02:00
Sean Wei
a90dfb46bc Fix GitHub case in BUILD
Replace "Github" with "GitHub".

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

Signed-off-by: Romain Vimont <rom@rom1v.com>
2022-04-25 18:25:59 +02:00
Sean Wei
85512b1467 Fix typo in German README
Replace "Wifi" with "Wi-Fi", as in English and other translations.

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

Signed-off-by: Romain Vimont <rom@rom1v.com>
2022-04-25 18:25:59 +02:00
Sean Wei
c05981587f Fix typos in Indonesian README
Fix `code` blocks.

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

Signed-off-by: Romain Vimont <rom@rom1v.com>
2022-04-25 18:25:47 +02:00
Romain Vimont
4db97531e8 Upgrade libusb (1.0.26) for Windows
Upgrade and enable libusb support for Windows 32-bit builds.

Refs #3011 <https://github.com/Genymobile/scrcpy/pull/3011>
Fixes #3204 <https://github.com/Genymobile/scrcpy/issues/3204>
PR #3206 <https://github.com/Genymobile/scrcpy/pull/3206>
2022-04-22 13:34:58 +02:00
Romain Vimont
b8d78743f7 Upgrade platform-tools (33.0.1) for Windows
PR #3206 <https://github.com/Genymobile/scrcpy/pull/3206>
2022-04-22 13:34:11 +02:00
Romain Vimont
6a4a4a283d Remove obsolete alternative injection method
The previous commit replaced the IInterface instance (the "input"
service) by the InputManager instance (retrieved by
InputManager.getInstance()).

Both define an "injectInputEvent" method, but the alternate version
(probably) does not concern the InputManager.

This reverts commit b7a06278fe.

PR #3190 <https://github.com/Genymobile/scrcpy/pull/3190>
2022-04-22 09:52:41 +02:00
Romain Vimont
7d8b72d4a6 Adapt event injection to Android 13
Using the "input" service results in a permission error in Android 13.

Use the InputManager instance (retrieved by InputManager.getInstance())
instead.

Fixes #3186 <https://github.com/Genymobile/scrcpy/issues/3186>
PR #3190 <https://github.com/Genymobile/scrcpy/pull/3190>
2022-04-22 09:52:41 +02:00
Romain Vimont
fa5b2a29e9 Add missing SC_ prefix to header guards 2022-04-12 23:59:01 +02:00
Romain Vimont
0c94887075 Add missing include
Refs c3d45c8397
2022-04-12 23:51:05 +02:00
Romain Vimont
88543cb545 Fix icon path in ./run
The data/ directory has been moved to app/data/.

Refs 36c75e15b8
2022-03-30 14:00:05 +02:00
Romain Vimont
aaf3869a54 Fix OpenGL ES prefix skip 2022-03-30 12:01:01 +02:00
Romain Vimont
c3d45c8397 Consider emulators as TCP/IP devices
Emulators were wrongly considered as USB devices, so they were selected
using -d instead of -e (which was inconsistent with the adb behavior).

Refs #3005 <https://github.com/Genymobile/scrcpy/issues/3005>
Refs #3137 <https://github.com/Genymobile/scrcpy/issues/3137>
2022-03-22 21:08:08 +01:00
Romain Vimont
e56f2ac7a9 Log an error on unexpected device state
Refs #3129 <https://github.com/Genymobile/scrcpy/issues/3129>
2022-03-20 14:56:52 +01:00
Romain Vimont
4ce7af42c6 Use $ANDROID_SERIAL if no selector is specified
Like adb, read the ANDROID_SERIAL environment variable to select a
device by serial if no explicit selection (-s, -d, -e or --tcpip=<addr>)
is provided via the command line.

Fixes #3111 <https://github.com/Genymobile/scrcpy/issues/3111>
PR #3113 <https://github.com/Genymobile/scrcpy/pull/3113>
2022-03-15 08:32:34 +01:00
Romain Vimont
b1dbc30072 Document exit status in --help
Refs #3085 <https://github.com/Genymobile/scrcpy/pull/3085>
2022-03-10 09:13:21 +01:00
martin f. krafft
b3f5dfe1de Add specific exit code for device disconnection
Modify the return logic such that exit code 1 is used when the initial
connection fails, but if a session is established, and then the device
disconnects, exit code 2 is emitted.

Fixes #3083 <https://github.com/Genymobile/scrcpy/issues/3083>
PR #3085 <https://github.com/Genymobile/scrcpy/pull/3085>
Signed-off-by: martin f. krafft <madduck@madduck.net>
Signed-off-by: Romain Vimont <rom@rom1v.com>
2022-03-06 22:19:46 +01:00
Romain Vimont
1f4c801f3c Report server connection state
We must distinguish 3 cases for await_for_server():
 - an error occurred
 - no error occurred, the device is connected
 - no error occurred, the device is not connected (user requested to
   quit)

For this purpose, use an additional output parameter to indicate if the
device is connected (only set when no error occurs).

Refs #3085 <https://github.com/Genymobile/scrcpy/pull/3085>
2022-03-06 22:16:13 +01:00
Romain Vimont
8d91cda4f6 Improve HID event push error message
On HID event push failure, add the event type in the error message.
2022-02-24 23:28:20 +01:00
Romain Vimont
59656fe649 Fix typo in error message 2022-02-24 23:26:12 +01:00
Romain Vimont
e4bb2b8728 Add libusb error log
Log libusb_get_string_descriptor_ascii() errors.

Refs #3050 <https://github.com/Genymobile/scrcpy/issues/3050>
2022-02-24 23:25:02 +01:00
88 changed files with 965 additions and 437 deletions

View File

@@ -46,7 +46,7 @@ sudo ninja -Cbuild-auto uninstall
### `master`
The `master` branch concerns the latest release, and is the home page of the
project on Github.
project on GitHub.
### `dev`
@@ -272,10 +272,10 @@ install` must be run as root)._
#### Option 2: Use prebuilt server
- [`scrcpy-server-v1.23`][direct-scrcpy-server]
_(SHA-256: 2a913fd47478c0b306fca507cb0beb625e49a19ff9fc7ab904e36ef5b9fe7e68)_
- [`scrcpy-server-v1.24`][direct-scrcpy-server]
<sub>SHA-256: `ae74a81ea79c0dc7250e586627c278c0a9a8c5de46c9fb5c38c167fb1a36f056`</sub>
[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v1.23/scrcpy-server-v1.23
[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v1.24/scrcpy-server-v1.24
Download the prebuilt server somewhere, and specify its path during the Meson
configuration:
@@ -305,13 +305,16 @@ After a successful build, you can install _scrcpy_ on the system:
sudo ninja -Cx install # without sudo on Windows
```
This installs three files:
This installs several files:
- `/usr/local/bin/scrcpy`
- `/usr/local/share/scrcpy/scrcpy-server`
- `/usr/local/share/man/man1/scrcpy.1`
- `/usr/local/bin/scrcpy` (main app)
- `/usr/local/share/scrcpy/scrcpy-server` (server to push to the device)
- `/usr/local/share/man/man1/scrcpy.1` (manpage)
- `/usr/local/share/icons/hicolor/256x256/apps/icon.png` (app icon)
- `/usr/local/share/zsh/site-functions/_scrcpy` (zsh completion)
- `/usr/local/share/bash-completion/completions/scrcpy` (bash completion)
You can then [run](README.md#run) _scrcpy_.
You can then [run](README.md#run) `scrcpy`.
### Uninstall

View File

@@ -375,7 +375,7 @@ Dies finden automatisch das Gerät und aktiviert den TCP/IP-Modus. Anschließend
Alternativ kann die TCP/IP-Verbindung auch manuell per `adb` aktiviert werden:
1. Gerät mit demselben Wifi wie den Computer verbinden.
1. Gerät mit demselben Wi-Fi wie den Computer verbinden.
2. IP-Adresse des Gerätes herausfinden, entweder über Einstellungen → Über das Telefon → Status, oder indem dieser Befehl ausgeführt wird:
```bash

View File

@@ -218,7 +218,7 @@ variation] does not impact the recorded file.
#### Wireless
_Scrcpy_ menggunakan `adb` untuk berkomunikasi dengan perangkat, dan` adb` dapat [terhubung] ke perangkat melalui TCP / IP:
_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).
@@ -281,7 +281,7 @@ Dari terminal lain:
scrcpy
```
Untuk menghindari mengaktifkan penerusan port jarak jauh, Anda dapat memaksa sambungan maju sebagai gantinya (perhatikan `-L`, bukan` -R`):
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
@@ -579,7 +579,7 @@ Lihat juga [Masalah #14].
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:
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

View File

@@ -1,4 +1,4 @@
# scrcpy (v1.23)
# scrcpy (v1.24)
<img src="app/data/icon.svg" width="128" height="128" alt="scrcpy" align="right" />
@@ -106,10 +106,10 @@ process][BUILD_simple]).
For Windows, a prebuilt archive with all the dependencies (including `adb`) is
available:
- [`scrcpy-win64-v1.23.zip`][direct-win64]
_(SHA-256: d2f601b1d0157faf65153d8a093d827fd65aec5d5842d677ac86fb2b5b7704cc)_
- [`scrcpy-win64-v1.24.zip`][direct-win64]
<sub>SHA-256: `6ccb64cba0a3e75715e85a188daeb4f306a1985f8ce123eba92ba74fc9b27367`</sub>
[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v1.23/scrcpy-win64-v1.23.zip
[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v1.24/scrcpy-win64-v1.24.zip
It is also available in [Chocolatey]:
@@ -447,6 +447,9 @@ scrcpy --serial 0123456789abcdef
scrcpy -s 0123456789abcdef # short version
```
The serial may also be provided via the environment variable `ANDROID_SERIAL`
(also used by `adb`).
If the device is connected over TCP/IP:
```bash
@@ -719,6 +722,16 @@ To turn the device screen off when closing _scrcpy_:
scrcpy --power-off-on-close
```
#### Power on on start
By default, on start, the device is powered on.
To prevent this behavior:
```bash
scrcpy --no-power-on
```
#### Show touches

View File

@@ -29,6 +29,7 @@ _scrcpy() {
-N --no-display
--no-key-repeat
--no-mipmaps
--no-power-on
--otg
-p --port=
--power-off-on-close

View File

@@ -0,0 +1,13 @@
[Desktop Entry]
Name=scrcpy (console)
GenericName=Android Remote Control
Comment=Display and control your Android device
# For some users, the PATH or ADB environment variables are set from the shell
# startup file, like .bashrc or .zshrc… Run an interactive shell to get
# environment correctly initialized.
Exec=/bin/bash --norc --noprofile -i -c '"$SHELL" -i -c scrcpy || read -p "Press any key to quit..."'
Icon=scrcpy
Terminal=true
Type=Application
Categories=Utility;RemoteAccess;
StartupNotify=false

13
app/data/scrcpy.desktop Normal file
View File

@@ -0,0 +1,13 @@
[Desktop Entry]
Name=scrcpy
GenericName=Android Remote Control
Comment=Display and control your Android device
# For some users, the PATH or ADB environment variables are set from the shell
# startup file, like .bashrc or .zshrc… Run an interactive shell to get
# environment correctly initialized.
Exec=/bin/sh -c '"$SHELL" -i -c scrcpy'
Icon=scrcpy
Terminal=false
Type=Application
Categories=Utility;RemoteAccess;
StartupNotify=false

View File

@@ -35,6 +35,7 @@ arguments=(
{-N,--no-display}'[Do not display device \(during screen recording or when V4L2 sink is enabled\)]'
'--no-key-repeat[Do not forward repeated key events when a key is held down]'
'--no-mipmaps[Disable the generation of mipmaps]'
'--no-power-on[Do not power on the device on start]'
'--otg[Run in OTG mode \(simulating physical keyboard and mouse\)]'
{-p,--port=}'[\[port\[\:port\]\] Set the TCP port \(range\) used by the client to listen]'
'--power-off-on-close[Turn the device screen off when closing scrcpy]'

View File

@@ -143,12 +143,12 @@ else
prebuilt_libusb = meson.get_cross_property('prebuilt_libusb')
prebuilt_libusb_root = meson.get_cross_property('prebuilt_libusb_root')
libusb_bin_dir = meson.current_source_dir() + '/prebuilt-deps/data/' + prebuilt_libusb + '/dll'
libusb_bin_dir = meson.current_source_dir() + '/prebuilt-deps/data/' + prebuilt_libusb
libusb_include_dir = 'prebuilt-deps/data/' + prebuilt_libusb_root + '/include'
libusb = declare_dependency(
dependencies: [
cc.find_library('libusb-1.0', dirs: libusb_bin_dir),
cc.find_library('msys-usb-1.0', dirs: libusb_bin_dir),
],
include_directories: include_directories(libusb_include_dir)
)
@@ -223,14 +223,26 @@ executable('scrcpy', src,
install: true,
c_args: [])
# <https://mesonbuild.com/Builtin-options.html#directories>
datadir = get_option('datadir') # by default 'share'
install_man('scrcpy.1')
install_data('data/icon.png',
rename: 'scrcpy.png',
install_dir: 'share/icons/hicolor/256x256/apps')
install_dir: join_paths(datadir, 'icons/hicolor/256x256/apps'))
install_data('data/zsh-completion/_scrcpy',
install_dir: 'share/zsh/site-functions')
install_dir: join_paths(datadir, 'zsh/site-functions'))
install_data('data/bash-completion/scrcpy',
install_dir: 'share/bash-completion/completions')
install_dir: join_paths(datadir, 'bash-completion/completions'))
# Desktop entry file for application launchers
if host_machine.system() == 'linux'
# Install a launcher (ex: /usr/local/share/applications/scrcpy.desktop)
install_data('data/scrcpy.desktop',
install_dir: join_paths(datadir, 'applications'))
install_data('data/scrcpy-console.desktop',
install_dir: join_paths(datadir, 'applications'))
endif
### TESTS
@@ -245,8 +257,8 @@ if get_option('buildtype') == 'debug'
'src/util/str.c',
'src/util/strbuf.c',
]],
['test_buffer_util', [
'tests/test_buffer_util.c',
['test_binary', [
'tests/test_binary.c',
]],
['test_cbuf', [
'tests/test_cbuf.c',

View File

@@ -6,10 +6,10 @@ cd "$DIR"
mkdir -p "$PREBUILT_DATA_DIR"
cd "$PREBUILT_DATA_DIR"
DEP_DIR=platform-tools-31.0.3
DEP_DIR=platform-tools-33.0.1
FILENAME=platform-tools_r31.0.3-windows.zip
SHA256SUM=0f4b8fdd26af2c3733539d6eebb3c2ed499ea1d4bb1f4e0ecc2d6016961a6e24
FILENAME=platform-tools_r33.0.1-windows.zip
SHA256SUM=c1f02d42ea24ef4ff2a405ae7370e764ef4546f9b3e4520f5571a00ed5012c42
if [[ -d "$DEP_DIR" ]]
then

View File

@@ -6,10 +6,11 @@ cd "$DIR"
mkdir -p "$PREBUILT_DATA_DIR"
cd "$PREBUILT_DATA_DIR"
DEP_DIR=ffmpeg-win64-5.0
VERSION=5.0.1
DEP_DIR=ffmpeg-win64-$VERSION
FILENAME=ffmpeg-5.0-full_build-shared.7z
SHA256SUM=e5900f6cecd4c438d398bd2fc308736c10b857cd8dd61c11bcfb05bff5d1211a
FILENAME=ffmpeg-$VERSION-full_build-shared.7z
SHA256SUM=ded28435b6f04b74f5ef5a6a13761233bce9e8e9f8ecb0eabe936fd36a778b0c
if [[ -d "$DEP_DIR" ]]
then
@@ -17,13 +18,13 @@ then
exit 0
fi
get_file "https://github.com/GyanD/codexffmpeg/releases/download/5.0/$FILENAME" \
get_file "https://github.com/GyanD/codexffmpeg/releases/download/$VERSION/$FILENAME" \
"$FILENAME" "$SHA256SUM"
mkdir "$DEP_DIR"
cd "$DEP_DIR"
ZIP_PREFIX=ffmpeg-5.0-full_build-shared
ZIP_PREFIX=ffmpeg-$VERSION-full_build-shared
7z x "../$FILENAME" \
"$ZIP_PREFIX"/bin/avutil-57.dll \
"$ZIP_PREFIX"/bin/avcodec-59.dll \

View File

@@ -6,10 +6,10 @@ cd "$DIR"
mkdir -p "$PREBUILT_DATA_DIR"
cd "$PREBUILT_DATA_DIR"
DEP_DIR=libusb-1.0.25
DEP_DIR=libusb-1.0.26
FILENAME=libusb-1.0.25.7z
SHA256SUM=3d1c98416f454026034b2b5d67f8a294053898cb70a8b489874e75b136c6674d
FILENAME=libusb-1.0.26-binaries.7z
SHA256SUM=9c242696342dbde9cdc47239391f71833939bf9f7aa2bbb28cdaabe890465ec5
if [[ -d "$DEP_DIR" ]]
then
@@ -17,12 +17,18 @@ then
exit 0
fi
get_file "https://github.com/libusb/libusb/releases/download/v1.0.25/$FILENAME" "$FILENAME" "$SHA256SUM"
get_file "https://github.com/libusb/libusb/releases/download/v1.0.26/$FILENAME" "$FILENAME" "$SHA256SUM"
mkdir "$DEP_DIR"
cd "$DEP_DIR"
# include/ is the same in all folders of the archive
7z x "../$FILENAME" \
MinGW32/dll/libusb-1.0.dll \
MinGW64/dll/libusb-1.0.dll \
include /
libusb-1.0.26-binaries/libusb-MinGW-Win32/bin/msys-usb-1.0.dll \
libusb-1.0.26-binaries/libusb-MinGW-x64/bin/msys-usb-1.0.dll \
libusb-1.0.26-binaries/libusb-MinGW-x64/include/
mv libusb-1.0.26-binaries/libusb-MinGW-Win32/bin MinGW-Win32
mv libusb-1.0.26-binaries/libusb-MinGW-x64/bin MinGW-x64
mv libusb-1.0.26-binaries/libusb-MinGW-x64/include .
rm -rf libusb-1.0.26-binaries

View File

@@ -6,10 +6,10 @@ cd "$DIR"
mkdir -p "$PREBUILT_DATA_DIR"
cd "$PREBUILT_DATA_DIR"
DEP_DIR=SDL2-2.0.20
DEP_DIR=SDL2-2.0.22
FILENAME=SDL2-devel-2.0.20-mingw.tar.gz
SHA256SUM=38094d82a857d6c62352e5c5cdec74948c5b4d25c59cbd298d6d233568976bd1
FILENAME=SDL2-devel-2.0.22-mingw.tar.gz
SHA256SUM=0e91e35973366aa1e6f81ee368924d9b4f93f9da4d2f2a89ec80b06eadcf23d1
if [[ -d "$DEP_DIR" ]]
then

View File

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

View File

@@ -180,6 +180,10 @@ Do not forward repeated key events when a key is held down.
.B \-\-no\-mipmaps
If the renderer is OpenGL 3.0+ or OpenGL ES 2.0+, then mipmaps are automatically generated to improve downscaling quality. This option disables the generation of mipmaps.
.TP
.B \-\-no\-power\-on
Do not power on the device on start.
.TP
.B \-\-otg
Run in OTG mode: simulate physical keyboard and mouse, as if the computer keyboard and mouse were plugged directly to the device via an OTG cable.
@@ -355,6 +359,12 @@ Set the initial window height.
Default is 0 (automatic).
.SH EXIT STATUS
.B scrcpy
will exit with code 0 on normal program termination. If an initial
connection cannot be established, the exit code 1 will be returned. If the
device disconnects while a session is active, exit code 2 will be returned.
.SH SHORTCUTS
In the following list, MOD is the shortcut modifier. By default, it's (left)
@@ -471,6 +481,10 @@ Push file to device (see \fB\-\-push\-target\fR)
.B ADB
Path to adb.
.TP
.B ANDROID_SERIAL
Device serial to use if no selector (-s, -d, -e or --tcpip=<addr>) is specified.
.TP
.B SCRCPY_ICON_PATH
Path to the program icon.

View File

@@ -401,6 +401,7 @@ sc_adb_list_devices(struct sc_intr *intr, unsigned flags,
#define BUFSIZE 65536
char *buf = malloc(BUFSIZE);
if (!buf) {
LOG_OOM();
return false;
}
@@ -473,9 +474,12 @@ sc_adb_accept_device(const struct sc_adb_device *device,
}
return !strcmp(selector->serial, device->serial);
case SC_ADB_DEVICE_SELECT_USB:
return !sc_adb_is_serial_tcpip(device->serial);
return sc_adb_device_get_type(device->serial) ==
SC_ADB_DEVICE_TYPE_USB;
case SC_ADB_DEVICE_SELECT_TCPIP:
return sc_adb_is_serial_tcpip(device->serial);
// Both emulators and TCP/IP devices are selected via -e
return sc_adb_device_get_type(device->serial) !=
SC_ADB_DEVICE_TYPE_USB;
default:
assert(!"Missing SC_ADB_DEVICE_SELECT_* handling");
break;
@@ -509,8 +513,10 @@ sc_adb_devices_log(enum sc_log_level level, struct sc_adb_device *devices,
for (size_t i = 0; i < count; ++i) {
struct sc_adb_device *d = &devices[i];
const char *selection = d->selected ? "-->" : " ";
const char *type = sc_adb_is_serial_tcpip(d->serial) ? "(tcpip)"
: " (usb)";
bool is_usb =
sc_adb_device_get_type(d->serial) == SC_ADB_DEVICE_TYPE_USB;
const char *type = is_usb ? " (usb)"
: "(tcpip)";
LOG(level, " %s %s %-20s %16s %s",
selection, type, d->serial, d->state, d->model ? d->model : "");
}
@@ -531,6 +537,8 @@ sc_adb_device_check_state(struct sc_adb_device *device,
LOGE("A popup should open on the device to request authorization.");
LOGE("Check the FAQ: "
"<https://github.com/Genymobile/scrcpy/blob/master/FAQ.md>");
} else {
LOGE("Device could not be connected (state=%s)", state);
}
return false;
@@ -703,10 +711,5 @@ sc_adb_get_device_ip(struct sc_intr *intr, const char *serial, unsigned flags) {
// It is parsed as a NUL-terminated string
buf[r] = '\0';
return sc_adb_parse_device_ip_from_output(buf);
}
bool
sc_adb_is_serial_tcpip(const char *serial) {
return strchr(serial, ':');
return sc_adb_parse_device_ip(buf);
}

View File

@@ -114,13 +114,4 @@ sc_adb_getprop(struct sc_intr *intr, const char *serial, const char *prop,
char *
sc_adb_get_device_ip(struct sc_intr *intr, const char *serial, unsigned flags);
/**
* Indicate if the serial represents an IP address
*
* In practice, it just returns true if and only if it contains a ':', which is
* sufficient to distinguish an ip:port from a real USB serial.
*/
bool
sc_adb_is_serial_tcpip(const char *serial);
#endif

View File

@@ -1,6 +1,7 @@
#include "adb_device.h"
#include <stdlib.h>
#include <string.h>
void
sc_adb_device_destroy(struct sc_adb_device *device) {
@@ -25,3 +26,18 @@ sc_adb_devices_destroy(struct sc_vec_adb_devices *devices) {
sc_vector_destroy(devices);
}
enum sc_adb_device_type
sc_adb_device_get_type(const char *serial) {
// Starts with "emulator-"
if (!strncmp(serial, "emulator-", sizeof("emulator-") - 1)) {
return SC_ADB_DEVICE_TYPE_EMULATOR;
}
// If the serial contains a ':', then it is a TCP/IP device (it is
// sufficient to distinguish an ip:port from a real USB serial)
if (strchr(serial, ':')) {
return SC_ADB_DEVICE_TYPE_TCPIP;
}
return SC_ADB_DEVICE_TYPE_USB;
}

View File

@@ -15,6 +15,12 @@ struct sc_adb_device {
bool selected;
};
enum sc_adb_device_type {
SC_ADB_DEVICE_TYPE_USB,
SC_ADB_DEVICE_TYPE_TCPIP,
SC_ADB_DEVICE_TYPE_EMULATOR,
};
struct sc_vec_adb_devices SC_VECTOR(struct sc_adb_device);
void
@@ -35,4 +41,10 @@ sc_adb_device_move(struct sc_adb_device *dst, struct sc_adb_device *src);
void
sc_adb_devices_destroy(struct sc_vec_adb_devices *devices);
/**
* Deduce the device type from the serial
*/
enum sc_adb_device_type
sc_adb_device_get_type(const char *serial);
#endif

View File

@@ -199,7 +199,7 @@ sc_adb_parse_device_ip_from_line(char *line) {
}
char *
sc_adb_parse_device_ip_from_output(char *str) {
sc_adb_parse_device_ip(char *str) {
size_t idx_line = 0;
while (str[idx_line] != '\0') {
char *line = &str[idx_line];

View File

@@ -25,6 +25,6 @@ sc_adb_parse_devices(char *str, struct sc_vec_adb_devices *out_vec);
* Warning: this function modifies the buffer for optimization purposes.
*/
char *
sc_adb_parse_device_ip_from_output(char *str);
sc_adb_parse_device_ip(char *str);
#endif

View File

@@ -56,6 +56,7 @@
#define OPT_OTG 1036
#define OPT_NO_CLEANUP 1037
#define OPT_PRINT_FPS 1038
#define OPT_NO_POWER_ON 1039
struct sc_option {
char shortopt;
@@ -80,6 +81,11 @@ struct sc_envvar {
const char *text;
};
struct sc_exit_status {
unsigned value;
const char *text;
};
struct sc_getopt_adapter {
char *optstring;
struct option *longopts;
@@ -297,6 +303,11 @@ static const struct sc_option options[] = {
"mipmaps are automatically generated to improve downscaling "
"quality. This option disables the generation of mipmaps.",
},
{
.longopt_id = OPT_NO_POWER_ON,
.longopt = "no-power-on",
.text = "Do not power on the device on start.",
},
{
.longopt_id = OPT_OTG,
.longopt = "otg",
@@ -572,7 +583,7 @@ static const struct sc_shortcut shortcuts[] = {
.text = "Click on BACK",
},
{
.shortcuts = { "MOD+s" },
.shortcuts = { "MOD+s", "4th-click" },
.text = "Click on APP_SWITCH",
},
{
@@ -608,7 +619,7 @@ static const struct sc_shortcut shortcuts[] = {
.text = "Rotate device screen",
},
{
.shortcuts = { "MOD+n" },
.shortcuts = { "MOD+n", "5th-click" },
.text = "Expand notification panel",
},
{
@@ -655,6 +666,11 @@ static const struct sc_envvar envvars[] = {
.name = "ADB",
.text = "Path to adb executable",
},
{
.name = "ANDROID_SERIAL",
.text = "Device serial to use if no selector (-s, -d, -e or "
"--tcpip=<addr>) is specified",
},
{
.name = "SCRCPY_ICON_PATH",
.text = "Path to the program icon",
@@ -662,7 +678,22 @@ static const struct sc_envvar envvars[] = {
{
.name = "SCRCPY_SERVER_PATH",
.text = "Path to the server binary",
}
},
};
static const struct sc_exit_status exit_statuses[] = {
{
.value = 0,
.text = "Normal program termination",
},
{
.value = 1,
.text = "Start failure",
},
{
.value = 2,
.text = "Device disconnected while running",
},
};
static char *
@@ -901,6 +932,25 @@ print_envvar(const struct sc_envvar *envvar, unsigned cols) {
free(text);
}
static void
print_exit_status(const struct sc_exit_status *status, unsigned cols) {
assert(cols > 8); // sc_str_wrap_lines() requires indent < columns
assert(status->text);
// The text starts at 9: 4 ident spaces, 3 chars for numeric value, 2 spaces
char *text = sc_str_wrap_lines(status->text, cols, 9);
if (!text) {
printf("<ERROR>\n");
return;
}
assert(strlen(text) >= 9); // Contains at least the initial identation
// text + 9 to remove the initial indentation
printf(" %3d %s\n", status->value, text + 9);
free(text);
}
void
scrcpy_print_usage(const char *arg0) {
#define SC_TERM_COLS_DEFAULT 80
@@ -939,6 +989,11 @@ scrcpy_print_usage(const char *arg0) {
for (size_t i = 0; i < ARRAY_LEN(envvars); ++i) {
print_envvar(&envvars[i], cols);
}
printf("\nExit status:\n\n");
for (size_t i = 0; i < ARRAY_LEN(exit_statuses); ++i) {
print_exit_status(&exit_statuses[i], cols);
}
}
static bool
@@ -1549,6 +1604,9 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
case OPT_NO_CLEANUP:
opts->cleanup = false;
break;
case OPT_NO_POWER_ON:
opts->power_on = false;
break;
case OPT_PRINT_FPS:
opts->start_fps_counter = true;
break;

View File

@@ -98,7 +98,7 @@ sc_clock_update(struct sc_clock *clock, sc_tick system, sc_tick stream) {
sc_clock_estimate(clock, &clock->slope, &clock->offset);
#ifndef SC_CLOCK_NDEBUG
LOGD("Clock estimation: %g * pts + %" PRItick,
LOGD("Clock estimation: %f * pts + %" PRItick,
clock->slope, clock->offset);
#endif
}

View File

@@ -1,5 +1,5 @@
#ifndef COMMON_H
#define COMMON_H
#ifndef SC_COMMON_H
#define SC_COMMON_H
#include "config.h"
#include "compat.h"

View File

@@ -1,5 +1,5 @@
#ifndef COMPAT_H
#define COMPAT_H
#ifndef SC_COMPAT_H
#define SC_COMPAT_H
#include "config.h"

View File

@@ -5,7 +5,7 @@
#include <stdlib.h>
#include <string.h>
#include "util/buffer_util.h"
#include "util/binary.h"
#include "util/log.h"
#include "util/str.h"
@@ -78,16 +78,6 @@ write_string(const char *utf8, size_t max_len, unsigned char *buf) {
return 4 + len;
}
static uint16_t
to_fixed_point_16(float f) {
assert(f >= 0.0f && f <= 1.0f);
uint32_t u = f * 0x1p16f; // 2^16
if (u >= 0xffff) {
u = 0xffff;
}
return (uint16_t) u;
}
size_t
sc_control_msg_serialize(const struct sc_control_msg *msg, unsigned char *buf) {
buf[0] = msg->type;
@@ -109,18 +99,20 @@ sc_control_msg_serialize(const struct sc_control_msg *msg, unsigned char *buf) {
sc_write64be(&buf[2], msg->inject_touch_event.pointer_id);
write_position(&buf[10], &msg->inject_touch_event.position);
uint16_t pressure =
to_fixed_point_16(msg->inject_touch_event.pressure);
sc_float_to_u16fp(msg->inject_touch_event.pressure);
sc_write16be(&buf[22], pressure);
sc_write32be(&buf[24], msg->inject_touch_event.buttons);
return 28;
case SC_CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT:
write_position(&buf[1], &msg->inject_scroll_event.position);
sc_write32be(&buf[13],
(uint32_t) msg->inject_scroll_event.hscroll);
sc_write32be(&buf[17],
(uint32_t) msg->inject_scroll_event.vscroll);
sc_write32be(&buf[21], msg->inject_scroll_event.buttons);
return 25;
int16_t hscroll =
sc_float_to_i16fp(msg->inject_scroll_event.hscroll);
int16_t vscroll =
sc_float_to_i16fp(msg->inject_scroll_event.vscroll);
sc_write16be(&buf[13], (uint16_t) hscroll);
sc_write16be(&buf[15], (uint16_t) vscroll);
sc_write32be(&buf[17], msg->inject_scroll_event.buttons);
return 21;
case SC_CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON:
buf[1] = msg->inject_keycode.action;
return 2;
@@ -170,7 +162,7 @@ sc_control_msg_log(const struct sc_control_msg *msg) {
if (id == POINTER_ID_MOUSE || id == POINTER_ID_VIRTUAL_FINGER) {
// string pointer id
LOG_CMSG("touch [id=%s] %-4s position=%" PRIi32 ",%" PRIi32
" pressure=%g buttons=%06lx",
" pressure=%f buttons=%06lx",
id == POINTER_ID_MOUSE ? "mouse" : "vfinger",
MOTIONEVENT_ACTION_LABEL(action),
msg->inject_touch_event.position.point.x,
@@ -180,7 +172,7 @@ sc_control_msg_log(const struct sc_control_msg *msg) {
} else {
// numeric pointer id
LOG_CMSG("touch [id=%" PRIu64_ "] %-4s position=%" PRIi32 ",%"
PRIi32 " pressure=%g buttons=%06lx",
PRIi32 " pressure=%f buttons=%06lx",
id,
MOTIONEVENT_ACTION_LABEL(action),
msg->inject_touch_event.position.point.x,
@@ -191,8 +183,8 @@ sc_control_msg_log(const struct sc_control_msg *msg) {
break;
}
case SC_CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT:
LOG_CMSG("scroll position=%" PRIi32 ",%" PRIi32 " hscroll=%" PRIi32
" vscroll=%" PRIi32 " buttons=%06lx",
LOG_CMSG("scroll position=%" PRIi32 ",%" PRIi32 " hscroll=%f"
" vscroll=%f buttons=%06lx",
msg->inject_scroll_event.position.point.x,
msg->inject_scroll_event.position.point.y,
msg->inject_scroll_event.hscroll,

View File

@@ -1,5 +1,5 @@
#ifndef CONTROLMSG_H
#define CONTROLMSG_H
#ifndef SC_CONTROLMSG_H
#define SC_CONTROLMSG_H
#include "common.h"
@@ -68,8 +68,8 @@ struct sc_control_msg {
} inject_touch_event;
struct {
struct sc_position position;
int32_t hscroll;
int32_t vscroll;
float hscroll;
float vscroll;
enum android_motionevent_buttons buttons;
} inject_scroll_event;
struct {

View File

@@ -1,5 +1,5 @@
#ifndef CONTROLLER_H
#define CONTROLLER_H
#ifndef SC_CONTROLLER_H
#define SC_CONTROLLER_H
#include "common.h"

View File

@@ -7,7 +7,7 @@
#include "decoder.h"
#include "events.h"
#include "recorder.h"
#include "util/buffer_util.h"
#include "util/binary.h"
#include "util/log.h"
#define SC_PACKET_HEADER_SIZE 12
@@ -37,8 +37,8 @@ sc_demuxer_recv_packet(struct sc_demuxer *demuxer, AVPacket *packet) {
// CK...... ........ ........ ........ ........ ........ ........ ........
// ^^<------------------------------------------------------------------->
// || PTS
// | `- config packet
// `-- key frame
// | `- key frame
// `-- config packet
uint8_t header[SC_PACKET_HEADER_SIZE];
ssize_t r = net_recv_all(demuxer->socket, header, SC_PACKET_HEADER_SIZE);

View File

@@ -4,7 +4,7 @@
#include <stdlib.h>
#include <string.h>
#include "util/buffer_util.h"
#include "util/binary.h"
#include "util/log.h"
ssize_t

View File

@@ -1,5 +1,5 @@
#ifndef DEVICEMSG_H
#define DEVICEMSG_H
#ifndef SC_DEVICEMSG_H
#define SC_DEVICEMSG_H
#include "common.h"

View File

@@ -1,5 +1,5 @@
#ifndef FPSCOUNTER_H
#define FPSCOUNTER_H
#ifndef SC_FPSCOUNTER_H
#define SC_FPSCOUNTER_H
#include "common.h"

View File

@@ -1,5 +1,5 @@
#ifndef ICON_H
#define ICON_H
#ifndef SC_ICON_H
#define SC_ICON_H
#include "common.h"

View File

@@ -358,8 +358,8 @@ struct sc_mouse_click_event {
struct sc_mouse_scroll_event {
struct sc_position position;
int32_t hscroll;
int32_t vscroll;
float hscroll;
float vscroll;
uint8_t buttons_state; // bitwise-OR of sc_mouse_button values
};

View File

@@ -747,8 +747,13 @@ sc_input_manager_process_mouse_wheel(struct sc_input_manager *im,
.point = sc_screen_convert_window_to_frame_coords(im->screen,
mouse_x, mouse_y),
},
.hscroll = event->x,
.vscroll = event->y,
#if SDL_VERSION_ATLEAST(2, 0, 18)
.hscroll = CLAMP(event->preciseX, -1.0f, 1.0f),
.vscroll = CLAMP(event->preciseY, -1.0f, 1.0f),
#else
.hscroll = CLAMP(event->x, -1, 1),
.vscroll = CLAMP(event->y, -1, 1),
#endif
.buttons_state =
sc_mouse_buttons_state_from_sdl(buttons, im->forward_all_clicks),
};

View File

@@ -1,5 +1,5 @@
#ifndef INPUTMANAGER_H
#define INPUTMANAGER_H
#ifndef SC_INPUTMANAGER_H
#define SC_INPUTMANAGER_H
#include "common.h"

View File

@@ -40,19 +40,19 @@ main(int argc, char *argv[]) {
#endif
if (!scrcpy_parse_args(&args, argc, argv)) {
return 1;
return SCRCPY_EXIT_FAILURE;
}
sc_set_log_level(args.opts.log_level);
if (args.help) {
scrcpy_print_usage(argv[0]);
return 0;
return SCRCPY_EXIT_SUCCESS;
}
if (args.version) {
scrcpy_print_version();
return 0;
return SCRCPY_EXIT_SUCCESS;
}
#ifdef SCRCPY_LAVF_REQUIRES_REGISTER_ALL
@@ -66,17 +66,17 @@ main(int argc, char *argv[]) {
#endif
if (avformat_network_init()) {
return 1;
return SCRCPY_EXIT_FAILURE;
}
#ifdef HAVE_USB
bool ok = args.opts.otg ? scrcpy_otg(&args.opts)
: scrcpy(&args.opts);
enum scrcpy_exit_code ret = args.opts.otg ? scrcpy_otg(&args.opts)
: scrcpy(&args.opts);
#else
bool ok = scrcpy(&args.opts);
enum scrcpy_exit_code ret = scrcpy(&args.opts);
#endif
avformat_network_deinit(); // ignore failure
return ok ? 0 : 1;
return ret;
}

View File

@@ -28,7 +28,7 @@ sc_opengl_init(struct sc_opengl *gl) {
sizeof(OPENGL_ES_PREFIX) - 1);
if (gl->is_opengles) {
/* skip the prefix */
version += sizeof(PREFIX) - 1;
version += sizeof(OPENGL_ES_PREFIX) - 1;
}
int r = sscanf(version, "%d.%d", &gl->version_major, &gl->version_minor);

View File

@@ -64,4 +64,5 @@ const struct scrcpy_options scrcpy_options_default = {
.select_usb = false,
.cleanup = true,
.start_fps_counter = false,
.power_on = true,
};

View File

@@ -139,6 +139,7 @@ struct scrcpy_options {
bool select_tcpip;
bool cleanup;
bool start_fps_counter;
bool power_on;
};
extern const struct scrcpy_options scrcpy_options_default;

View File

@@ -1,5 +1,5 @@
#ifndef RECEIVER_H
#define RECEIVER_H
#ifndef SC_RECEIVER_H
#define SC_RECEIVER_H
#include "common.h"

View File

@@ -149,38 +149,41 @@ sdl_configure(bool display, bool disable_screensaver) {
}
}
static bool
static enum scrcpy_exit_code
event_loop(struct scrcpy *s) {
SDL_Event event;
while (SDL_WaitEvent(&event)) {
switch (event.type) {
case EVENT_STREAM_STOPPED:
LOGW("Device disconnected");
return false;
return SCRCPY_EXIT_DISCONNECTED;
case SDL_QUIT:
LOGD("User requested to quit");
return true;
return SCRCPY_EXIT_SUCCESS;
default:
sc_screen_handle_event(&s->screen, &event);
break;
}
}
return false;
return SCRCPY_EXIT_FAILURE;
}
// Return true on success, false on error
static bool
await_for_server(void) {
await_for_server(bool *connected) {
SDL_Event event;
while (SDL_WaitEvent(&event)) {
switch (event.type) {
case SDL_QUIT:
LOGD("User requested to quit");
return false;
*connected = false;
return true;
case EVENT_SERVER_CONNECTION_FAILED:
LOGE("Server connection failed");
return false;
case EVENT_SERVER_CONNECTED:
LOGD("Server connected");
*connected = true;
return true;
default:
break;
@@ -262,7 +265,7 @@ sc_server_on_disconnected(struct sc_server *server, void *userdata) {
// event
}
bool
enum scrcpy_exit_code
scrcpy(struct scrcpy_options *options) {
static struct scrcpy scrcpy;
struct scrcpy *s = &scrcpy;
@@ -270,12 +273,12 @@ scrcpy(struct scrcpy_options *options) {
// Minimal SDL initialization
if (SDL_Init(SDL_INIT_EVENTS)) {
LOGE("Could not initialize SDL: %s", SDL_GetError());
return false;
return SCRCPY_EXIT_FAILURE;
}
atexit(SDL_Quit);
bool ret = false;
enum scrcpy_exit_code ret = SCRCPY_EXIT_FAILURE;
bool server_started = false;
bool file_pusher_initialized = false;
@@ -321,6 +324,7 @@ scrcpy(struct scrcpy_options *options) {
.tcpip = options->tcpip,
.tcpip_dst = options->tcpip_dst,
.cleanup = options->cleanup,
.power_on = options->power_on,
};
static const struct sc_server_callbacks cbs = {
@@ -329,7 +333,7 @@ scrcpy(struct scrcpy_options *options) {
.on_disconnected = sc_server_on_disconnected,
};
if (!sc_server_init(&s->server, &params, &cbs, NULL)) {
return false;
return SCRCPY_EXIT_FAILURE;
}
if (!sc_server_start(&s->server)) {
@@ -351,7 +355,14 @@ scrcpy(struct scrcpy_options *options) {
sdl_configure(options->display, options->disable_screensaver);
// Await for server without blocking Ctrl+C handling
if (!await_for_server()) {
bool connected;
if (!await_for_server(&connected)) {
goto end;
}
if (!connected) {
// This is not an error, user requested to quit
ret = SCRCPY_EXIT_SUCCESS;
goto end;
}

View File

@@ -6,7 +6,18 @@
#include <stdbool.h>
#include "options.h"
bool
enum scrcpy_exit_code {
// Normal program termination
SCRCPY_EXIT_SUCCESS,
// No connection could be established
SCRCPY_EXIT_FAILURE,
// Device was disconnected while running
SCRCPY_EXIT_DISCONNECTED,
};
enum scrcpy_exit_code
scrcpy(struct scrcpy_options *options);
#endif

View File

@@ -248,6 +248,10 @@ execute_server(struct sc_server *server,
// By default, cleanup is true
ADD_PARAM("cleanup=false");
}
if (!params->power_on) {
// By default, power_on is true
ADD_PARAM("power_on=false");
}
#undef ADD_PARAM
@@ -649,7 +653,8 @@ sc_server_configure_tcpip_known_address(struct sc_server *server,
static bool
sc_server_configure_tcpip_unknown_address(struct sc_server *server,
const char *serial) {
bool is_already_tcpip = sc_adb_is_serial_tcpip(serial);
bool is_already_tcpip =
sc_adb_device_get_type(serial) == SC_ADB_DEVICE_TYPE_TCPIP;
if (is_already_tcpip) {
// Nothing to do
LOGI("Device already connected via TCP/IP: %s", serial);
@@ -707,7 +712,15 @@ run_server(void *data) {
} else if (params->select_tcpip) {
selector.type = SC_ADB_DEVICE_SELECT_TCPIP;
} else {
selector.type = SC_ADB_DEVICE_SELECT_ALL;
// No explicit selection, check $ANDROID_SERIAL
const char *env_serial = getenv("ANDROID_SERIAL");
if (env_serial) {
LOGI("Using ANDROID_SERIAL: %s", env_serial);
selector.type = SC_ADB_DEVICE_SELECT_SERIAL;
selector.serial = env_serial;
} else {
selector.type = SC_ADB_DEVICE_SELECT_ALL;
}
}
struct sc_adb_device device;
ok = sc_adb_select_device(&server->intr, &selector, 0, &device);

View File

@@ -47,6 +47,7 @@ struct sc_server_params {
bool select_usb;
bool select_tcpip;
bool cleanup;
bool power_on;
};
struct sc_server {

View File

@@ -1,51 +0,0 @@
#ifndef STREAM_H
#define STREAM_H
#include "common.h"
#include <stdbool.h>
#include <stdint.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include "trait/packet_sink.h"
#include "util/net.h"
#include "util/thread.h"
#define STREAM_MAX_SINKS 2
struct stream {
sc_socket socket;
sc_thread thread;
struct sc_packet_sink *sinks[STREAM_MAX_SINKS];
unsigned sink_count;
AVCodecContext *codec_ctx;
AVCodecParserContext *parser;
// successive packets may need to be concatenated, until a non-config
// packet is available
AVPacket *pending;
const struct stream_callbacks *cbs;
void *cbs_userdata;
};
struct stream_callbacks {
void (*on_eos)(struct stream *stream, void *userdata);
};
void
stream_init(struct stream *stream, sc_socket socket,
const struct stream_callbacks *cbs, void *cbs_userdata);
void
stream_add_sink(struct stream *stream, struct sc_packet_sink *sink);
bool
stream_start(struct stream *stream);
void
stream_join(struct stream *stream);
#endif

View File

@@ -340,7 +340,7 @@ push_mod_lock_state(struct sc_hid_keyboard *kb, uint16_t mods_state) {
if (!sc_aoa_push_hid_event(kb->aoa, &hid_event)) {
sc_hid_event_destroy(&hid_event);
LOGW("Could request HID event");
LOGW("Could not request HID event (mod lock state)");
return false;
}
@@ -382,7 +382,7 @@ sc_key_processor_process_key(struct sc_key_processor *kp,
if (!sc_aoa_push_hid_event(kb->aoa, &hid_event)) {
sc_hid_event_destroy(&hid_event);
LOGW("Could request HID event");
LOGW("Could not request HID event (key)");
}
}
}

View File

@@ -181,7 +181,7 @@ sc_mouse_processor_process_mouse_motion(struct sc_mouse_processor *mp,
if (!sc_aoa_push_hid_event(mouse->aoa, &hid_event)) {
sc_hid_event_destroy(&hid_event);
LOGW("Could request HID event");
LOGW("Could not request HID event (mouse motion)");
}
}
@@ -203,7 +203,7 @@ sc_mouse_processor_process_mouse_click(struct sc_mouse_processor *mp,
if (!sc_aoa_push_hid_event(mouse->aoa, &hid_event)) {
sc_hid_event_destroy(&hid_event);
LOGW("Could request HID event");
LOGW("Could not request HID event (mouse click)");
}
}
@@ -228,7 +228,7 @@ sc_mouse_processor_process_mouse_scroll(struct sc_mouse_processor *mp,
if (!sc_aoa_push_hid_event(mouse->aoa, &hid_event)) {
sc_hid_event_destroy(&hid_event);
LOGW("Could request HID event");
LOGW("Could not request HID event (mouse scroll)");
}
}

View File

@@ -29,32 +29,36 @@ sc_usb_on_disconnected(struct sc_usb *usb, void *userdata) {
}
}
static bool
static enum scrcpy_exit_code
event_loop(struct scrcpy_otg *s) {
SDL_Event event;
while (SDL_WaitEvent(&event)) {
switch (event.type) {
case EVENT_USB_DEVICE_DISCONNECTED:
LOGW("Device disconnected");
return false;
return SCRCPY_EXIT_DISCONNECTED;
case SDL_QUIT:
LOGD("User requested to quit");
return true;
return SCRCPY_EXIT_SUCCESS;
default:
sc_screen_otg_handle_event(&s->screen_otg, &event);
break;
}
}
return false;
return SCRCPY_EXIT_FAILURE;
}
bool
enum scrcpy_exit_code
scrcpy_otg(struct scrcpy_options *options) {
static struct scrcpy_otg scrcpy_otg;
struct scrcpy_otg *s = &scrcpy_otg;
const char *serial = options->serial;
if (!SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1")) {
LOGW("Could not enable linear filtering");
}
// Minimal SDL initialization
if (SDL_Init(SDL_INIT_EVENTS)) {
LOGE("Could not initialize SDL: %s", SDL_GetError());
@@ -67,7 +71,7 @@ scrcpy_otg(struct scrcpy_options *options) {
LOGW("Could not enable mouse focus clickthrough");
}
bool ret = false;
enum scrcpy_exit_code ret = SCRCPY_EXIT_FAILURE;
struct sc_hid_keyboard *keyboard = NULL;
struct sc_hid_mouse *mouse = NULL;
@@ -90,7 +94,7 @@ scrcpy_otg(struct scrcpy_options *options) {
};
bool ok = sc_usb_init(&s->usb);
if (!ok) {
return false;
return SCRCPY_EXIT_FAILURE;
}
struct sc_usb_device usb_device;
@@ -162,6 +166,8 @@ scrcpy_otg(struct scrcpy_options *options) {
.always_on_top = options->always_on_top,
.window_x = options->window_x,
.window_y = options->window_y,
.window_width = options->window_width,
.window_height = options->window_height,
.window_borderless = options->window_borderless,
};

View File

@@ -3,10 +3,10 @@
#include "common.h"
#include <stdbool.h>
#include "options.h"
#include "scrcpy.h"
bool
enum scrcpy_exit_code
scrcpy_otg(struct scrcpy_options *options);
#endif

View File

@@ -69,10 +69,10 @@ sc_screen_otg_init(struct sc_screen_otg *screen,
? params->window_x : (int) SDL_WINDOWPOS_UNDEFINED;
int y = params->window_y != SC_WINDOW_POSITION_UNDEFINED
? params->window_y : (int) SDL_WINDOWPOS_UNDEFINED;
int width = 256;
int height = 256;
int width = params->window_width ? params->window_width : 256;
int height = params->window_height ? params->window_height : 256;
uint32_t window_flags = 0;
uint32_t window_flags = SDL_WINDOW_ALLOW_HIGHDPI;
if (params->always_on_top) {
window_flags |= SDL_WINDOW_ALWAYS_ON_TOP;
}
@@ -97,6 +97,11 @@ sc_screen_otg_init(struct sc_screen_otg *screen,
if (icon) {
SDL_SetWindowIcon(screen->window, icon);
if (SDL_RenderSetLogicalSize(screen->renderer, icon->w, icon->h)) {
LOGW("Could not set renderer logical size: %s", SDL_GetError());
// don't fail
}
screen->texture = SDL_CreateTextureFromSurface(screen->renderer, icon);
scrcpy_icon_destroy(icon);
if (!screen->texture) {

View File

@@ -29,6 +29,8 @@ struct sc_screen_otg_params {
bool always_on_top;
int16_t window_x; // accepts SC_WINDOW_POSITION_UNDEFINED
int16_t window_y; // accepts SC_WINDOW_POSITION_UNDEFINED
uint16_t window_width;
uint16_t window_height;
bool window_borderless;
};

View File

@@ -15,6 +15,7 @@ read_string(libusb_device_handle *handle, uint8_t desc_index) {
(unsigned char *) buffer,
sizeof(buffer));
if (result < 0) {
LOGD("Read string: libusb error: %s", libusb_strerror(result));
return NULL;
}
@@ -22,6 +23,11 @@ read_string(libusb_device_handle *handle, uint8_t desc_index) {
// When non-negative, 'result' contains the number of bytes written
char *s = malloc(result + 1);
if (!s) {
LOG_OOM();
return NULL;
}
memcpy(s, buffer, result);
s[result] = '\0';
return s;

View File

@@ -1,8 +1,9 @@
#ifndef SC_BUFFER_UTIL_H
#define SC_BUFFER_UTIL_H
#ifndef SC_BINARY_H
#define SC_BINARY_H
#include "common.h"
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
@@ -43,4 +44,33 @@ sc_read64be(const uint8_t *buf) {
return ((uint64_t) msb << 32) | lsb;
}
/**
* Convert a float between 0 and 1 to an unsigned 16-bit fixed-point value
*/
static inline uint16_t
sc_float_to_u16fp(float f) {
assert(f >= 0.0f && f <= 1.0f);
uint32_t u = f * 0x1p16f; // 2^16
if (u >= 0xffff) {
assert(u == 0x10000); // for f == 1.0f
u = 0xffff;
}
return (uint16_t) u;
}
/**
* Convert a float between -1 and 1 to a signed 16-bit fixed-point value
*/
static inline int16_t
sc_float_to_i16fp(float f) {
assert(f >= -1.0f && f <= 1.0f);
int32_t i = f * 0x1p15f; // 2^15
assert(i >= -0x8000);
if (i >= 0x7fff) {
assert(i == 0x8000); // for f == 1.0f
i = 0x7fff;
}
return (int16_t) i;
}
#endif

View File

@@ -1,6 +1,6 @@
// generic circular buffer (bounded queue) implementation
#ifndef CBUF_H
#define CBUF_H
#ifndef SC_CBUF_H
#define SC_CBUF_H
#include "common.h"

View File

@@ -3,11 +3,10 @@
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <SDL2/SDL_platform.h>
#include "log.h"
#ifdef __WINDOWS__
#ifdef _WIN32
# include <ws2tcpip.h>
typedef int socklen_t;
typedef SOCKET sc_raw_socket;
@@ -29,7 +28,7 @@
bool
net_init(void) {
#ifdef __WINDOWS__
#ifdef _WIN32
WSADATA wsa;
int res = WSAStartup(MAKEWORD(2, 2), &wsa) < 0;
if (res < 0) {
@@ -42,14 +41,14 @@ net_init(void) {
void
net_cleanup(void) {
#ifdef __WINDOWS__
#ifdef _WIN32
WSACleanup();
#endif
}
static inline sc_socket
wrap(sc_raw_socket sock) {
#ifdef __WINDOWS__
#ifdef _WIN32
if (sock == INVALID_SOCKET) {
return SC_SOCKET_NONE;
}
@@ -72,7 +71,7 @@ wrap(sc_raw_socket sock) {
static inline sc_raw_socket
unwrap(sc_socket socket) {
#ifdef __WINDOWS__
#ifdef _WIN32
if (socket == SC_SOCKET_NONE) {
return INVALID_SOCKET;
}
@@ -160,8 +159,8 @@ net_connect(sc_socket socket, uint32_t addr, uint16_t port) {
}
bool
net_listen(sc_socket socket, uint32_t addr, uint16_t port, int backlog) {
sc_raw_socket raw_sock = unwrap(socket);
net_listen(sc_socket server_socket, uint32_t addr, uint16_t port, int backlog) {
sc_raw_socket raw_sock = unwrap(server_socket);
int reuse = 1;
if (setsockopt(raw_sock, SOL_SOCKET, SO_REUSEADDR, (const void *) &reuse,
@@ -248,7 +247,7 @@ net_interrupt(sc_socket socket) {
sc_raw_socket raw_sock = unwrap(socket);
#ifdef __WINDOWS__
#ifdef _WIN32
if (!atomic_flag_test_and_set(&socket->closed)) {
return !closesocket(raw_sock);
}
@@ -262,7 +261,7 @@ bool
net_close(sc_socket socket) {
sc_raw_socket raw_sock = unwrap(socket);
#ifdef __WINDOWS__
#ifdef _WIN32
bool ret = true;
if (!atomic_flag_test_and_set(&socket->closed)) {
ret = !closesocket(raw_sock);

View File

@@ -1,13 +1,12 @@
#ifndef NET_H
#define NET_H
#ifndef SC_NET_H
#define SC_NET_H
#include "common.h"
#include <stdbool.h>
#include <stdint.h>
#include <SDL2/SDL_platform.h>
#ifdef __WINDOWS__
#ifdef _WIN32
# include <winsock2.h>
# include <stdatomic.h>
@@ -17,7 +16,7 @@
atomic_flag closed;
} *sc_socket;
#else // not __WINDOWS__
#else // not _WIN32
# include <sys/socket.h>
# define SC_SOCKET_NONE -1
@@ -40,7 +39,7 @@ bool
net_connect(sc_socket socket, uint32_t addr, uint16_t port);
bool
net_listen(sc_socket socket, uint32_t addr, uint16_t port, int backlog);
net_listen(sc_socket server_socket, uint32_t addr, uint16_t port, int backlog);
sc_socket
net_accept(sc_socket server_socket);

View File

@@ -15,14 +15,14 @@ net_connect_intr(struct sc_intr *intr, sc_socket socket, uint32_t addr,
}
bool
net_listen_intr(struct sc_intr *intr, sc_socket socket, uint32_t addr,
net_listen_intr(struct sc_intr *intr, sc_socket server_socket, uint32_t addr,
uint16_t port, int backlog) {
if (!sc_intr_set_socket(intr, socket)) {
if (!sc_intr_set_socket(intr, server_socket)) {
// Already interrupted
return false;
}
bool ret = net_listen(socket, addr, port, backlog);
bool ret = net_listen(server_socket, addr, port, backlog);
sc_intr_set_socket(intr, SC_SOCKET_NONE);
return ret;

View File

@@ -11,7 +11,7 @@ net_connect_intr(struct sc_intr *intr, sc_socket socket, uint32_t addr,
uint16_t port);
bool
net_listen_intr(struct sc_intr *intr, sc_socket socket, uint32_t addr,
net_listen_intr(struct sc_intr *intr, sc_socket server_socket, uint32_t addr,
uint16_t port, int backlog);
sc_socket

View File

@@ -5,7 +5,7 @@
#include "adb/adb_device.h"
#include "adb/adb_parser.h"
static void test_adb_devices() {
static void test_adb_devices(void) {
char output[] =
"List of devices attached\n"
"0123456789abcdef device usb:2-1 product:MyProduct model:MyModel "
@@ -31,7 +31,7 @@ static void test_adb_devices() {
sc_adb_devices_destroy(&vec);
}
static void test_adb_devices_cr() {
static void test_adb_devices_cr(void) {
char output[] =
"List of devices attached\r\n"
"0123456789abcdef device usb:2-1 product:MyProduct model:MyModel "
@@ -57,7 +57,7 @@ static void test_adb_devices_cr() {
sc_adb_devices_destroy(&vec);
}
static void test_adb_devices_daemon_start() {
static void test_adb_devices_daemon_start(void) {
char output[] =
"* daemon not running; starting now at tcp:5037\n"
"* daemon started successfully\n"
@@ -78,7 +78,7 @@ static void test_adb_devices_daemon_start() {
sc_adb_devices_destroy(&vec);
}
static void test_adb_devices_daemon_start_mixed() {
static void test_adb_devices_daemon_start_mixed(void) {
char output[] =
"List of devices attached\n"
"adb server version (41) doesn't match this client (39); killing...\n"
@@ -105,7 +105,7 @@ static void test_adb_devices_daemon_start_mixed() {
sc_adb_devices_destroy(&vec);
}
static void test_adb_devices_without_eol() {
static void test_adb_devices_without_eol(void) {
char output[] =
"List of devices attached\n"
"0123456789abcdef device usb:2-1 product:MyProduct model:MyModel "
@@ -124,7 +124,7 @@ static void test_adb_devices_without_eol() {
sc_adb_devices_destroy(&vec);
}
static void test_adb_devices_without_header() {
static void test_adb_devices_without_header(void) {
char output[] =
"0123456789abcdef device usb:2-1 product:MyProduct model:MyModel "
"device:MyDevice transport_id:1\n";
@@ -134,7 +134,7 @@ static void test_adb_devices_without_header() {
assert(!ok);
}
static void test_adb_devices_corrupted() {
static void test_adb_devices_corrupted(void) {
char output[] =
"List of devices attached\n"
"corrupted_garbage\n";
@@ -145,7 +145,7 @@ static void test_adb_devices_corrupted() {
assert(vec.size == 0);
}
static void test_adb_devices_spaces() {
static void test_adb_devices_spaces(void) {
char output[] =
"List of devices attached\n"
"0123456789abcdef unauthorized usb:1-4 transport_id:3\n";
@@ -163,81 +163,81 @@ static void test_adb_devices_spaces() {
sc_adb_devices_destroy(&vec);
}
static void test_get_ip_single_line() {
static void test_get_ip_single_line(void) {
char ip_route[] = "192.168.1.0/24 dev wlan0 proto kernel scope link src "
"192.168.12.34\r\r\n";
char *ip = sc_adb_parse_device_ip_from_output(ip_route);
char *ip = sc_adb_parse_device_ip(ip_route);
assert(ip);
assert(!strcmp(ip, "192.168.12.34"));
free(ip);
}
static void test_get_ip_single_line_without_eol() {
static void test_get_ip_single_line_without_eol(void) {
char ip_route[] = "192.168.1.0/24 dev wlan0 proto kernel scope link src "
"192.168.12.34";
char *ip = sc_adb_parse_device_ip_from_output(ip_route);
char *ip = sc_adb_parse_device_ip(ip_route);
assert(ip);
assert(!strcmp(ip, "192.168.12.34"));
free(ip);
}
static void test_get_ip_single_line_with_trailing_space() {
static void test_get_ip_single_line_with_trailing_space(void) {
char ip_route[] = "192.168.1.0/24 dev wlan0 proto kernel scope link src "
"192.168.12.34 \n";
char *ip = sc_adb_parse_device_ip_from_output(ip_route);
char *ip = sc_adb_parse_device_ip(ip_route);
assert(ip);
assert(!strcmp(ip, "192.168.12.34"));
free(ip);
}
static void test_get_ip_multiline_first_ok() {
static void test_get_ip_multiline_first_ok(void) {
char ip_route[] = "192.168.1.0/24 dev wlan0 proto kernel scope link src "
"192.168.1.2\r\n"
"10.0.0.0/24 dev rmnet proto kernel scope link src "
"10.0.0.2\r\n";
char *ip = sc_adb_parse_device_ip_from_output(ip_route);
char *ip = sc_adb_parse_device_ip(ip_route);
assert(ip);
assert(!strcmp(ip, "192.168.1.2"));
free(ip);
}
static void test_get_ip_multiline_second_ok() {
static void test_get_ip_multiline_second_ok(void) {
char ip_route[] = "10.0.0.0/24 dev rmnet proto kernel scope link src "
"10.0.0.3\r\n"
"192.168.1.0/24 dev wlan0 proto kernel scope link src "
"192.168.1.3\r\n";
char *ip = sc_adb_parse_device_ip_from_output(ip_route);
char *ip = sc_adb_parse_device_ip(ip_route);
assert(ip);
assert(!strcmp(ip, "192.168.1.3"));
free(ip);
}
static void test_get_ip_no_wlan() {
static void test_get_ip_no_wlan(void) {
char ip_route[] = "192.168.1.0/24 dev rmnet proto kernel scope link src "
"192.168.12.34\r\r\n";
char *ip = sc_adb_parse_device_ip_from_output(ip_route);
char *ip = sc_adb_parse_device_ip(ip_route);
assert(!ip);
}
static void test_get_ip_no_wlan_without_eol() {
static void test_get_ip_no_wlan_without_eol(void) {
char ip_route[] = "192.168.1.0/24 dev rmnet proto kernel scope link src "
"192.168.12.34";
char *ip = sc_adb_parse_device_ip_from_output(ip_route);
char *ip = sc_adb_parse_device_ip(ip_route);
assert(!ip);
}
static void test_get_ip_truncated() {
static void test_get_ip_truncated(void) {
char ip_route[] = "192.168.1.0/24 dev rmnet proto kernel scope link src "
"\n";
char *ip = sc_adb_parse_device_ip_from_output(ip_route);
char *ip = sc_adb_parse_device_ip(ip_route);
assert(!ip);
}
@@ -262,4 +262,6 @@ int main(int argc, char *argv[]) {
test_get_ip_no_wlan();
test_get_ip_no_wlan_without_eol();
test_get_ip_truncated();
return 0;
}

114
app/tests/test_binary.c Normal file
View File

@@ -0,0 +1,114 @@
#include "common.h"
#include <assert.h>
#include "util/binary.h"
static void test_write16be(void) {
uint16_t val = 0xABCD;
uint8_t buf[2];
sc_write16be(buf, val);
assert(buf[0] == 0xAB);
assert(buf[1] == 0xCD);
}
static void test_write32be(void) {
uint32_t val = 0xABCD1234;
uint8_t buf[4];
sc_write32be(buf, val);
assert(buf[0] == 0xAB);
assert(buf[1] == 0xCD);
assert(buf[2] == 0x12);
assert(buf[3] == 0x34);
}
static void test_write64be(void) {
uint64_t val = 0xABCD1234567890EF;
uint8_t buf[8];
sc_write64be(buf, val);
assert(buf[0] == 0xAB);
assert(buf[1] == 0xCD);
assert(buf[2] == 0x12);
assert(buf[3] == 0x34);
assert(buf[4] == 0x56);
assert(buf[5] == 0x78);
assert(buf[6] == 0x90);
assert(buf[7] == 0xEF);
}
static void test_read16be(void) {
uint8_t buf[2] = {0xAB, 0xCD};
uint16_t val = sc_read16be(buf);
assert(val == 0xABCD);
}
static void test_read32be(void) {
uint8_t buf[4] = {0xAB, 0xCD, 0x12, 0x34};
uint32_t val = sc_read32be(buf);
assert(val == 0xABCD1234);
}
static void test_read64be(void) {
uint8_t buf[8] = {0xAB, 0xCD, 0x12, 0x34,
0x56, 0x78, 0x90, 0xEF};
uint64_t val = sc_read64be(buf);
assert(val == 0xABCD1234567890EF);
}
static void test_float_to_u16fp(void) {
assert(sc_float_to_u16fp(0.0f) == 0);
assert(sc_float_to_u16fp(0.03125f) == 0x800);
assert(sc_float_to_u16fp(0.0625f) == 0x1000);
assert(sc_float_to_u16fp(0.125f) == 0x2000);
assert(sc_float_to_u16fp(0.25f) == 0x4000);
assert(sc_float_to_u16fp(0.5f) == 0x8000);
assert(sc_float_to_u16fp(0.75f) == 0xc000);
assert(sc_float_to_u16fp(1.0f) == 0xffff);
}
static void test_float_to_i16fp(void) {
assert(sc_float_to_i16fp(0.0f) == 0);
assert(sc_float_to_i16fp(0.03125f) == 0x400);
assert(sc_float_to_i16fp(0.0625f) == 0x800);
assert(sc_float_to_i16fp(0.125f) == 0x1000);
assert(sc_float_to_i16fp(0.25f) == 0x2000);
assert(sc_float_to_i16fp(0.5f) == 0x4000);
assert(sc_float_to_i16fp(0.75f) == 0x6000);
assert(sc_float_to_i16fp(1.0f) == 0x7fff);
assert(sc_float_to_i16fp(-0.03125f) == -0x400);
assert(sc_float_to_i16fp(-0.0625f) == -0x800);
assert(sc_float_to_i16fp(-0.125f) == -0x1000);
assert(sc_float_to_i16fp(-0.25f) == -0x2000);
assert(sc_float_to_i16fp(-0.5f) == -0x4000);
assert(sc_float_to_i16fp(-0.75f) == -0x6000);
assert(sc_float_to_i16fp(-1.0f) == -0x8000);
}
int main(int argc, char *argv[]) {
(void) argc;
(void) argv;
test_write16be();
test_write32be();
test_write64be();
test_read16be();
test_read32be();
test_read64be();
test_float_to_u16fp();
test_float_to_i16fp();
return 0;
}

View File

@@ -1,81 +0,0 @@
#include "common.h"
#include <assert.h>
#include "util/buffer_util.h"
static void test_buffer_write16be(void) {
uint16_t val = 0xABCD;
uint8_t buf[2];
sc_write16be(buf, val);
assert(buf[0] == 0xAB);
assert(buf[1] == 0xCD);
}
static void test_buffer_write32be(void) {
uint32_t val = 0xABCD1234;
uint8_t buf[4];
sc_write32be(buf, val);
assert(buf[0] == 0xAB);
assert(buf[1] == 0xCD);
assert(buf[2] == 0x12);
assert(buf[3] == 0x34);
}
static void test_buffer_write64be(void) {
uint64_t val = 0xABCD1234567890EF;
uint8_t buf[8];
sc_write64be(buf, val);
assert(buf[0] == 0xAB);
assert(buf[1] == 0xCD);
assert(buf[2] == 0x12);
assert(buf[3] == 0x34);
assert(buf[4] == 0x56);
assert(buf[5] == 0x78);
assert(buf[6] == 0x90);
assert(buf[7] == 0xEF);
}
static void test_buffer_read16be(void) {
uint8_t buf[2] = {0xAB, 0xCD};
uint16_t val = sc_read16be(buf);
assert(val == 0xABCD);
}
static void test_buffer_read32be(void) {
uint8_t buf[4] = {0xAB, 0xCD, 0x12, 0x34};
uint32_t val = sc_read32be(buf);
assert(val == 0xABCD1234);
}
static void test_buffer_read64be(void) {
uint8_t buf[8] = {0xAB, 0xCD, 0x12, 0x34,
0x56, 0x78, 0x90, 0xEF};
uint64_t val = sc_read64be(buf);
assert(val == 0xABCD1234567890EF);
}
int main(int argc, char *argv[]) {
(void) argc;
(void) argv;
test_buffer_write16be();
test_buffer_write32be();
test_buffer_write64be();
test_buffer_read16be();
test_buffer_read32be();
test_buffer_read64be();
return 0;
}

View File

@@ -132,14 +132,14 @@ static void test_serialize_inject_scroll_event(void) {
unsigned char buf[SC_CONTROL_MSG_MAX_SIZE];
size_t size = sc_control_msg_serialize(&msg, buf);
assert(size == 25);
assert(size == 21);
const unsigned char expected[] = {
SC_CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT,
0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x04, 0x02, // 260 1026
0x04, 0x38, 0x07, 0x80, // 1080 1920
0x00, 0x00, 0x00, 0x01, // 1
0xFF, 0xFF, 0xFF, 0xFF, // -1
0x7F, 0xFF, // 1 (float encoded as i16)
0x80, 0x00, // -1 (float encoded as i16)
0x00, 0x00, 0x00, 0x01, // 1
};
assert(!memcmp(buf, expected, sizeof(expected)));

View File

@@ -4,10 +4,10 @@ buildscript {
repositories {
google()
jcenter()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.0.3'
classpath 'com.android.tools.build:gradle:7.2.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@@ -17,7 +17,7 @@ buildscript {
allprojects {
repositories {
google()
jcenter()
mavenCentral()
}
tasks.withType(JavaCompile) {
options.compilerArgs << "-Xlint:deprecation"

View File

@@ -20,6 +20,6 @@ ffmpeg_avcodec = 'avcodec-58'
ffmpeg_avformat = 'avformat-58'
ffmpeg_avutil = 'avutil-56'
prebuilt_ffmpeg = 'ffmpeg-win32-4.3.1'
prebuilt_sdl2 = 'SDL2-2.0.20/i686-w64-mingw32'
prebuilt_libusb_root = 'libusb-1.0.25'
prebuilt_libusb = prebuilt_libusb_root + '/MinGW32'
prebuilt_sdl2 = 'SDL2-2.0.22/i686-w64-mingw32'
prebuilt_libusb_root = 'libusb-1.0.26'
prebuilt_libusb = prebuilt_libusb_root + '/MinGW-Win32'

View File

@@ -19,7 +19,7 @@ endian = 'little'
ffmpeg_avcodec = 'avcodec-59'
ffmpeg_avformat = 'avformat-59'
ffmpeg_avutil = 'avutil-57'
prebuilt_ffmpeg = 'ffmpeg-win64-5.0'
prebuilt_sdl2 = 'SDL2-2.0.20/x86_64-w64-mingw32'
prebuilt_libusb_root = 'libusb-1.0.25'
prebuilt_libusb = prebuilt_libusb_root + '/MinGW64'
prebuilt_ffmpeg = 'ffmpeg-win64-5.0.1'
prebuilt_sdl2 = 'SDL2-2.0.22/x86_64-w64-mingw32'
prebuilt_libusb_root = 'libusb-1.0.26'
prebuilt_libusb = prebuilt_libusb_root + '/MinGW-x64'

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@@ -2,8 +2,8 @@
set -e
BUILDDIR=build-auto
PREBUILT_SERVER_URL=https://github.com/Genymobile/scrcpy/releases/download/v1.23/scrcpy-server-v1.23
PREBUILT_SERVER_SHA256=2a913fd47478c0b306fca507cb0beb625e49a19ff9fc7ab904e36ef5b9fe7e68
PREBUILT_SERVER_URL=https://github.com/Genymobile/scrcpy/releases/download/v1.24/scrcpy-server-v1.24
PREBUILT_SERVER_SHA256=ae74a81ea79c0dc7250e586627c278c0a9a8c5de46c9fb5c38c167fb1a36f056
echo "[scrcpy] Downloading prebuilt server..."
wget "$PREBUILT_SERVER_URL" -O scrcpy-server

View File

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

View File

@@ -75,12 +75,10 @@ prepare-deps-win64:
@app/prebuilt-deps/prepare-libusb.sh
build-win32: prepare-deps-win32
# -Dusb=false because of libusb-win32 build issue, cf #3011
[ -d "$(WIN32_BUILD_DIR)" ] || ( mkdir "$(WIN32_BUILD_DIR)" && \
meson "$(WIN32_BUILD_DIR)" \
--cross-file cross_win32.txt \
--buildtype release --strip -Db_lto=true \
-Dusb=false \
-Dcompile_server=false \
-Dportable=true )
ninja -C "$(WIN32_BUILD_DIR)"
@@ -107,11 +105,11 @@ dist-win32: build-server build-win32
cp app/prebuilt-deps/data/ffmpeg-win32-4.3.1/bin/avformat-58.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp app/prebuilt-deps/data/ffmpeg-win32-4.3.1/bin/swresample-3.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp app/prebuilt-deps/data/ffmpeg-win32-4.3.1/bin/swscale-5.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp app/prebuilt-deps/data/platform-tools-31.0.3/adb.exe "$(DIST)/$(WIN32_TARGET_DIR)/"
cp app/prebuilt-deps/data/platform-tools-31.0.3/AdbWinApi.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp app/prebuilt-deps/data/platform-tools-31.0.3/AdbWinUsbApi.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp app/prebuilt-deps/data/SDL2-2.0.20/i686-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
#cp app/prebuilt-deps/data/libusb-1.0.25/MinGW32/dll/libusb-1.0.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp app/prebuilt-deps/data/platform-tools-33.0.1/adb.exe "$(DIST)/$(WIN32_TARGET_DIR)/"
cp app/prebuilt-deps/data/platform-tools-33.0.1/AdbWinApi.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp app/prebuilt-deps/data/platform-tools-33.0.1/AdbWinUsbApi.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp app/prebuilt-deps/data/SDL2-2.0.22/i686-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp app/prebuilt-deps/data/libusb-1.0.26/MinGW-Win32/msys-usb-1.0.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
dist-win64: build-server build-win64
mkdir -p "$(DIST)/$(WIN64_TARGET_DIR)"
@@ -121,16 +119,16 @@ dist-win64: build-server build-win64
cp app/data/scrcpy-noconsole.vbs "$(DIST)/$(WIN64_TARGET_DIR)"
cp app/data/icon.png "$(DIST)/$(WIN64_TARGET_DIR)"
cp app/data/open_a_terminal_here.bat "$(DIST)/$(WIN64_TARGET_DIR)"
cp app/prebuilt-deps/data/ffmpeg-win64-5.0/bin/avutil-57.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp app/prebuilt-deps/data/ffmpeg-win64-5.0/bin/avcodec-59.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp app/prebuilt-deps/data/ffmpeg-win64-5.0/bin/avformat-59.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp app/prebuilt-deps/data/ffmpeg-win64-5.0/bin/swresample-4.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp app/prebuilt-deps/data/ffmpeg-win64-5.0/bin/swscale-6.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp app/prebuilt-deps/data/platform-tools-31.0.3/adb.exe "$(DIST)/$(WIN64_TARGET_DIR)/"
cp app/prebuilt-deps/data/platform-tools-31.0.3/AdbWinApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp app/prebuilt-deps/data/platform-tools-31.0.3/AdbWinUsbApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp app/prebuilt-deps/data/SDL2-2.0.20/x86_64-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp app/prebuilt-deps/data/libusb-1.0.25/MinGW64/dll/libusb-1.0.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp app/prebuilt-deps/data/ffmpeg-win64-5.0.1/bin/avutil-57.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp app/prebuilt-deps/data/ffmpeg-win64-5.0.1/bin/avcodec-59.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp app/prebuilt-deps/data/ffmpeg-win64-5.0.1/bin/avformat-59.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp app/prebuilt-deps/data/ffmpeg-win64-5.0.1/bin/swresample-4.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp app/prebuilt-deps/data/ffmpeg-win64-5.0.1/bin/swscale-6.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp app/prebuilt-deps/data/platform-tools-33.0.1/adb.exe "$(DIST)/$(WIN64_TARGET_DIR)/"
cp app/prebuilt-deps/data/platform-tools-33.0.1/AdbWinApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp app/prebuilt-deps/data/platform-tools-33.0.1/AdbWinUsbApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp app/prebuilt-deps/data/SDL2-2.0.22/x86_64-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp app/prebuilt-deps/data/libusb-1.0.26/MinGW-x64/msys-usb-1.0.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
zip-win32: dist-win32
cd "$(DIST)/$(WIN32_TARGET_DIR)"; \

2
run
View File

@@ -20,6 +20,6 @@ then
exit 1
fi
SCRCPY_ICON_PATH="data/icon.png" \
SCRCPY_ICON_PATH="app/data/icon.png" \
SCRCPY_SERVER_PATH="$BUILDDIR/server/scrcpy-server" \
"$BUILDDIR/app/scrcpy" "$@"

View File

@@ -1,13 +1,13 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 31
compileSdkVersion 33
defaultConfig {
applicationId "com.genymobile.scrcpy"
minSdkVersion 21
targetSdkVersion 31
versionCode 12300
versionName "1.23"
targetSdkVersion 33
versionCode 12400
versionName "1.24"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
@@ -19,7 +19,6 @@ android {
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
testImplementation 'junit:junit:4.13.1'
}

View File

@@ -12,10 +12,10 @@
set -e
SCRCPY_DEBUG=false
SCRCPY_VERSION_NAME=1.23
SCRCPY_VERSION_NAME=1.24
PLATFORM=${ANDROID_PLATFORM:-31}
BUILD_TOOLS=${ANDROID_BUILD_TOOLS:-31.0.0}
PLATFORM=${ANDROID_PLATFORM:-33}
BUILD_TOOLS=${ANDROID_BUILD_TOOLS:-33.0.0}
BUILD_DIR="$(realpath ${BUILD_DIR:-build_manual})"
CLASSES_DIR="$BUILD_DIR/classes"

View File

@@ -13,8 +13,8 @@ if prebuilt_server == ''
install_dir: 'share/scrcpy')
else
if not prebuilt_server.startswith('/')
# relative path needs some trick
prebuilt_server = meson.source_root() + '/' + prebuilt_server
# prebuilt server path is relative to the root scrcpy directory
prebuilt_server = '../' + prebuilt_server
endif
custom_target('scrcpy-server-prebuilt',
input: prebuilt_server,

View File

@@ -0,0 +1,38 @@
package com.genymobile.scrcpy;
public final class Binary {
private Binary() {
// not instantiable
}
public static int toUnsigned(short value) {
return value & 0xffff;
}
public static int toUnsigned(byte value) {
return value & 0xff;
}
/**
* Convert unsigned 16-bit fixed-point to a float between 0 and 1
*
* @param value encoded value
* @return Float value between 0 and 1
*/
public static float u16FixedPointToFloat(short value) {
int unsignedShort = Binary.toUnsigned(value);
// 0x1p16f is 2^16 as float
return unsignedShort == 0xffff ? 1f : (unsignedShort / 0x1p16f);
}
/**
* Convert signed 16-bit fixed-point to a float between -1 and 1
*
* @param value encoded value
* @return Float value between -1 and 1
*/
public static float i16FixedPointToFloat(short value) {
// 0x1p15f is 2^15 as float
return value == 0x7fff ? 1f : (value / 0x1p15f);
}
}

View File

@@ -30,4 +30,14 @@ public final class Command {
}
return result;
}
public static String execReadOutput(String... cmd) throws IOException, InterruptedException {
Process process = Runtime.getRuntime().exec(cmd);
String output = IO.toString(process.getInputStream());
int exitCode = process.waitFor();
if (exitCode != 0) {
throw new IOException("Command " + Arrays.toString(cmd) + " returned with value " + exitCode);
}
return output;
}
}

View File

@@ -33,8 +33,8 @@ public final class ControlMessage {
private long pointerId;
private float pressure;
private Position position;
private int hScroll;
private int vScroll;
private float hScroll;
private float vScroll;
private int copyKey;
private boolean paste;
private int repeat;
@@ -71,7 +71,7 @@ public final class ControlMessage {
return msg;
}
public static ControlMessage createInjectScrollEvent(Position position, int hScroll, int vScroll, int buttons) {
public static ControlMessage createInjectScrollEvent(Position position, float hScroll, float vScroll, int buttons) {
ControlMessage msg = new ControlMessage();
msg.type = TYPE_INJECT_SCROLL_EVENT;
msg.position = position;
@@ -156,11 +156,11 @@ public final class ControlMessage {
return position;
}
public int getHScroll() {
public float getHScroll() {
return hScroll;
}
public int getVScroll() {
public float getVScroll() {
return vScroll;
}

View File

@@ -10,7 +10,7 @@ public class ControlMessageReader {
static final int INJECT_KEYCODE_PAYLOAD_LENGTH = 13;
static final int INJECT_TOUCH_EVENT_PAYLOAD_LENGTH = 27;
static final int INJECT_SCROLL_EVENT_PAYLOAD_LENGTH = 24;
static final int INJECT_SCROLL_EVENT_PAYLOAD_LENGTH = 20;
static final int BACK_OR_SCREEN_ON_LENGTH = 1;
static final int SET_SCREEN_POWER_MODE_PAYLOAD_LENGTH = 1;
static final int GET_CLIPBOARD_LENGTH = 1;
@@ -103,7 +103,7 @@ public class ControlMessageReader {
if (buffer.remaining() < INJECT_KEYCODE_PAYLOAD_LENGTH) {
return null;
}
int action = toUnsigned(buffer.get());
int action = Binary.toUnsigned(buffer.get());
int keycode = buffer.getInt();
int repeat = buffer.getInt();
int metaState = buffer.getInt();
@@ -136,13 +136,10 @@ public class ControlMessageReader {
if (buffer.remaining() < INJECT_TOUCH_EVENT_PAYLOAD_LENGTH) {
return null;
}
int action = toUnsigned(buffer.get());
int action = Binary.toUnsigned(buffer.get());
long pointerId = buffer.getLong();
Position position = readPosition(buffer);
// 16 bits fixed-point
int pressureInt = toUnsigned(buffer.getShort());
// convert it to a float between 0 and 1 (0x1p16f is 2^16 as float)
float pressure = pressureInt == 0xffff ? 1f : (pressureInt / 0x1p16f);
float pressure = Binary.u16FixedPointToFloat(buffer.getShort());
int buttons = buffer.getInt();
return ControlMessage.createInjectTouchEvent(action, pointerId, position, pressure, buttons);
}
@@ -152,8 +149,8 @@ public class ControlMessageReader {
return null;
}
Position position = readPosition(buffer);
int hScroll = buffer.getInt();
int vScroll = buffer.getInt();
float hScroll = Binary.i16FixedPointToFloat(buffer.getShort());
float vScroll = Binary.i16FixedPointToFloat(buffer.getShort());
int buttons = buffer.getInt();
return ControlMessage.createInjectScrollEvent(position, hScroll, vScroll, buttons);
}
@@ -162,7 +159,7 @@ public class ControlMessageReader {
if (buffer.remaining() < BACK_OR_SCREEN_ON_LENGTH) {
return null;
}
int action = toUnsigned(buffer.get());
int action = Binary.toUnsigned(buffer.get());
return ControlMessage.createBackOrScreenOn(action);
}
@@ -170,7 +167,7 @@ public class ControlMessageReader {
if (buffer.remaining() < GET_CLIPBOARD_LENGTH) {
return null;
}
int copyKey = toUnsigned(buffer.get());
int copyKey = Binary.toUnsigned(buffer.get());
return ControlMessage.createGetClipboard(copyKey);
}
@@ -198,16 +195,8 @@ public class ControlMessageReader {
private static Position readPosition(ByteBuffer buffer) {
int x = buffer.getInt();
int y = buffer.getInt();
int screenWidth = toUnsigned(buffer.getShort());
int screenHeight = toUnsigned(buffer.getShort());
int screenWidth = Binary.toUnsigned(buffer.getShort());
int screenHeight = Binary.toUnsigned(buffer.getShort());
return new Position(x, y, screenWidth, screenHeight);
}
private static int toUnsigned(short value) {
return value & 0xffff;
}
private static int toUnsigned(byte value) {
return value & 0xff;
}
}

View File

@@ -22,6 +22,7 @@ public class Controller {
private final DesktopConnection connection;
private final DeviceMessageSender sender;
private final boolean clipboardAutosync;
private final boolean powerOn;
private final KeyCharacterMap charMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
@@ -32,10 +33,11 @@ public class Controller {
private boolean keepPowerModeOff;
public Controller(Device device, DesktopConnection connection, boolean clipboardAutosync) {
public Controller(Device device, DesktopConnection connection, boolean clipboardAutosync, boolean powerOn) {
this.device = device;
this.connection = connection;
this.clipboardAutosync = clipboardAutosync;
this.powerOn = powerOn;
initPointers();
sender = new DeviceMessageSender(connection);
}
@@ -56,7 +58,7 @@ public class Controller {
public void control() throws IOException {
// on start, power on the device
if (!Device.isScreenOn()) {
if (powerOn && !Device.isScreenOn()) {
device.pressReleaseKeycode(KeyEvent.KEYCODE_POWER, Device.INJECT_MODE_ASYNC);
// dirty hack
@@ -221,7 +223,7 @@ public class Controller {
return device.injectEvent(event, Device.INJECT_MODE_ASYNC);
}
private boolean injectScroll(Position position, int hScroll, int vScroll, int buttons) {
private boolean injectScroll(Position position, float hScroll, float vScroll, int buttons) {
long now = SystemClock.uptimeMillis();
Point point = device.getPhysicalPoint(position);
if (point == null) {

View File

@@ -6,7 +6,9 @@ import android.system.OsConstants;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.Scanner;
public final class IO {
private IO() {
@@ -37,4 +39,13 @@ public final class IO {
public static void writeFully(FileDescriptor fd, byte[] buffer, int offset, int len) throws IOException {
writeFully(fd, ByteBuffer.wrap(buffer, offset, len));
}
public static String toString(InputStream inputStream) {
StringBuilder builder = new StringBuilder();
Scanner scanner = new Scanner(inputStream);
while (scanner.hasNextLine()) {
builder.append(scanner.nextLine()).append('\n');
}
return builder.toString();
}
}

View File

@@ -22,6 +22,7 @@ public class Options {
private boolean clipboardAutosync = true;
private boolean downsizeOnError = true;
private boolean cleanup = true;
private boolean powerOn = true;
// Options not used by the scrcpy client, but useful to use scrcpy-server directly
private boolean sendDeviceMeta = true; // send device name and size
@@ -164,6 +165,14 @@ public class Options {
this.cleanup = cleanup;
}
public boolean getPowerOn() {
return powerOn;
}
public void setPowerOn(boolean powerOn) {
this.powerOn = powerOn;
}
public boolean getSendDeviceMeta() {
return sendDeviceMeta;
}

View File

@@ -82,7 +82,7 @@ public final class Server {
Thread controllerThread = null;
Thread deviceMessageSenderThread = null;
if (control) {
final Controller controller = new Controller(device, connection, options.getClipboardAutosync());
final Controller controller = new Controller(device, connection, options.getClipboardAutosync(), options.getPowerOn());
// asynchronous
controllerThread = startController(controller);
@@ -248,6 +248,10 @@ public final class Server {
boolean cleanup = Boolean.parseBoolean(value);
options.setCleanup(cleanup);
break;
case "power_on":
boolean powerOn = Boolean.parseBoolean(value);
options.setPowerOn(powerOn);
break;
case "send_device_meta":
boolean sendDeviceMeta = Boolean.parseBoolean(value);
options.setSendDeviceMeta(sendDeviceMeta);

View File

@@ -1,22 +1,78 @@
package com.genymobile.scrcpy.wrappers;
import com.genymobile.scrcpy.Command;
import com.genymobile.scrcpy.DisplayInfo;
import com.genymobile.scrcpy.Ln;
import com.genymobile.scrcpy.Size;
import android.os.IInterface;
import android.view.Display;
import java.lang.reflect.Field;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public final class DisplayManager {
private final IInterface manager;
private final Object manager; // instance of hidden class android.hardware.display.DisplayManagerGlobal
public DisplayManager(IInterface manager) {
public DisplayManager(Object manager) {
this.manager = manager;
}
// public to call it from unit tests
public static DisplayInfo parseDisplayInfo(String dumpsysDisplayOutput, int displayId) {
Pattern regex = Pattern.compile(
"^ mBaseDisplayInfo=DisplayInfo\\{\".*?\", displayId " + displayId + ".*?(, FLAG_.*)?, real ([0-9]+) x ([0-9]+).*?, rotation "
+ "([0-9]+).*?, layerStack ([0-9]+)",
Pattern.MULTILINE);
Matcher m = regex.matcher(dumpsysDisplayOutput);
if (!m.find()) {
return null;
}
int flags = parseDisplayFlags(m.group(1));
int width = Integer.parseInt(m.group(2));
int height = Integer.parseInt(m.group(3));
int rotation = Integer.parseInt(m.group(4));
int layerStack = Integer.parseInt(m.group(5));
return new DisplayInfo(displayId, new Size(width, height), rotation, layerStack, flags);
}
private static DisplayInfo getDisplayInfoFromDumpsysDisplay(int displayId) {
try {
String dumpsysDisplayOutput = Command.execReadOutput("dumpsys", "display");
return parseDisplayInfo(dumpsysDisplayOutput, displayId);
} catch (Exception e) {
Ln.e("Could not get display info from \"dumpsys display\" output", e);
return null;
}
}
private static int parseDisplayFlags(String text) {
Pattern regex = Pattern.compile("FLAG_[A-Z_]+");
if (text == null) {
return 0;
}
int flags = 0;
Matcher m = regex.matcher(text);
while (m.find()) {
String flagString = m.group();
try {
Field filed = Display.class.getDeclaredField(flagString);
flags |= filed.getInt(null);
} catch (NoSuchFieldException | IllegalAccessException e) {
// Silently ignore, some flags reported by "dumpsys display" are @TestApi
}
}
return flags;
}
public DisplayInfo getDisplayInfo(int displayId) {
try {
Object displayInfo = manager.getClass().getMethod("getDisplayInfo", int.class).invoke(manager, displayId);
if (displayInfo == null) {
return null;
// fallback when displayInfo is null
return getDisplayInfoFromDumpsysDisplay(displayId);
}
Class<?> cls = displayInfo.getClass();
// width and height already take the rotation into account

View File

@@ -2,7 +2,6 @@ package com.genymobile.scrcpy.wrappers;
import com.genymobile.scrcpy.Ln;
import android.os.IInterface;
import android.view.InputEvent;
import java.lang.reflect.InvocationTargetException;
@@ -14,24 +13,18 @@ public final class InputManager {
public static final int INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT = 1;
public static final int INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH = 2;
private final IInterface manager;
private final android.hardware.input.InputManager manager;
private Method injectInputEventMethod;
private boolean alternativeInjectInputEventMethod;
private static Method setDisplayIdMethod;
public InputManager(IInterface manager) {
public InputManager(android.hardware.input.InputManager manager) {
this.manager = manager;
}
private Method getInjectInputEventMethod() throws NoSuchMethodException {
if (injectInputEventMethod == null) {
try {
injectInputEventMethod = manager.getClass().getMethod("injectInputEvent", InputEvent.class, int.class);
} catch (NoSuchMethodException e) {
injectInputEventMethod = manager.getClass().getMethod("injectInputEvent", InputEvent.class, int.class, int.class);
alternativeInjectInputEventMethod = true;
}
injectInputEventMethod = manager.getClass().getMethod("injectInputEvent", InputEvent.class, int.class);
}
return injectInputEventMethod;
}
@@ -39,10 +32,6 @@ public final class InputManager {
public boolean injectInputEvent(InputEvent inputEvent, int mode) {
try {
Method method = getInjectInputEventMethod();
if (alternativeInjectInputEventMethod) {
// See <https://github.com/Genymobile/scrcpy/issues/2250>
return (boolean) method.invoke(manager, inputEvent, mode, 0);
}
return (boolean) method.invoke(manager, inputEvent, mode);
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
Ln.e("Could not invoke method", e);

View File

@@ -4,6 +4,7 @@ import android.annotation.SuppressLint;
import android.os.IBinder;
import android.os.IInterface;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@SuppressLint("PrivateApi,DiscouragedPrivateApi")
@@ -49,14 +50,27 @@ public final class ServiceManager {
public DisplayManager getDisplayManager() {
if (displayManager == null) {
displayManager = new DisplayManager(getService("display", "android.hardware.display.IDisplayManager"));
try {
Class<?> clazz = Class.forName("android.hardware.display.DisplayManagerGlobal");
Method getInstanceMethod = clazz.getDeclaredMethod("getInstance");
Object dmg = getInstanceMethod.invoke(null);
displayManager = new DisplayManager(dmg);
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
throw new AssertionError(e);
}
}
return displayManager;
}
public InputManager getInputManager() {
if (inputManager == null) {
inputManager = new InputManager(getService("input", "android.hardware.input.IInputManager"));
try {
Method getInstanceMethod = android.hardware.input.InputManager.class.getDeclaredMethod("getInstance");
android.hardware.input.InputManager im = (android.hardware.input.InputManager) getInstanceMethod.invoke(null);
inputManager = new InputManager(im);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
throw new AssertionError(e);
}
}
return inputManager;
}

View File

@@ -0,0 +1,42 @@
package com.genymobile.scrcpy;
import org.junit.Assert;
import org.junit.Test;
public class BinaryTest {
@Test
public void testU16FixedPointToFloat() {
final float delta = 0.0f; // on these values, there MUST be no rounding error
Assert.assertEquals(0.0f, Binary.u16FixedPointToFloat((short) 0), delta);
Assert.assertEquals(0.03125f, Binary.u16FixedPointToFloat((short) 0x800), delta);
Assert.assertEquals(0.0625f, Binary.u16FixedPointToFloat((short) 0x1000), delta);
Assert.assertEquals(0.125f, Binary.u16FixedPointToFloat((short) 0x2000), delta);
Assert.assertEquals(0.25f, Binary.u16FixedPointToFloat((short) 0x4000), delta);
Assert.assertEquals(0.5f, Binary.u16FixedPointToFloat((short) 0x8000), delta);
Assert.assertEquals(0.75f, Binary.u16FixedPointToFloat((short) 0xc000), delta);
Assert.assertEquals(1.0f, Binary.u16FixedPointToFloat((short) 0xffff), delta);
}
@Test
public void testI16FixedPointToFloat() {
final float delta = 0.0f; // on these values, there MUST be no rounding error
Assert.assertEquals(0.0f, Binary.i16FixedPointToFloat((short) 0), delta);
Assert.assertEquals(0.03125f, Binary.i16FixedPointToFloat((short) 0x400), delta);
Assert.assertEquals(0.0625f, Binary.i16FixedPointToFloat((short) 0x800), delta);
Assert.assertEquals(0.125f, Binary.i16FixedPointToFloat((short) 0x1000), delta);
Assert.assertEquals(0.25f, Binary.i16FixedPointToFloat((short) 0x2000), delta);
Assert.assertEquals(0.5f, Binary.i16FixedPointToFloat((short) 0x4000), delta);
Assert.assertEquals(0.75f, Binary.i16FixedPointToFloat((short) 0x6000), delta);
Assert.assertEquals(1.0f, Binary.i16FixedPointToFloat((short) 0x7fff), delta);
Assert.assertEquals(-0.03125f, Binary.i16FixedPointToFloat((short) -0x400), delta);
Assert.assertEquals(-0.0625f, Binary.i16FixedPointToFloat((short) -0x800), delta);
Assert.assertEquals(-0.125f, Binary.i16FixedPointToFloat((short) -0x1000), delta);
Assert.assertEquals(-0.25f, Binary.i16FixedPointToFloat((short) -0x2000), delta);
Assert.assertEquals(-0.5f, Binary.i16FixedPointToFloat((short) -0x4000), delta);
Assert.assertEquals(-0.75f, Binary.i16FixedPointToFloat((short) -0x6000), delta);
Assert.assertEquals(-1.0f, Binary.i16FixedPointToFloat((short) -0x8000), delta);
}
}

View File

@@ -0,0 +1,144 @@
package com.genymobile.scrcpy;
import com.genymobile.scrcpy.wrappers.DisplayManager;
import android.view.Display;
import org.junit.Assert;
import org.junit.Test;
public class CommandParserTest {
@Test
public void testParseDisplayInfoFromDumpsysDisplay() {
/* @formatter:off */
String partialOutput = "Logical Displays: size=1\n"
+ " Display 0:\n"
+ "mDisplayId=0\n"
+ " mLayerStack=0\n"
+ " mHasContent=true\n"
+ " mDesiredDisplayModeSpecs={baseModeId=2 primaryRefreshRateRange=[90 90] appRequestRefreshRateRange=[90 90]}\n"
+ " mRequestedColorMode=0\n"
+ " mDisplayOffset=(0, 0)\n"
+ " mDisplayScalingDisabled=false\n"
+ " mPrimaryDisplayDevice=Built-in Screen\n"
+ " mBaseDisplayInfo=DisplayInfo{\"Built-in Screen\", displayId 0, FLAG_SECURE, FLAG_SUPPORTS_PROTECTED_BUFFERS, FLAG_TRUSTED, "
+ "real 1440 x 3120, largest app 1440 x 3120, smallest app 1440 x 3120, appVsyncOff 2000000, presDeadline 11111111, mode 2, "
+ "defaultMode 1, modes [{id=1, width=1440, height=3120, fps=60.0}, {id=2, width=1440, height=3120, fps=90.0}, {id=3, width=1080, "
+ "height=2340, fps=90.0}, {id=4, width=1080, height=2340, fps=60.0}], hdrCapabilities HdrCapabilities{mSupportedHdrTypes=[2, 3, 4], "
+ "mMaxLuminance=540.0, mMaxAverageLuminance=270.1, mMinLuminance=0.2}, minimalPostProcessingSupported false, rotation 0, state OFF, "
+ "type INTERNAL, uniqueId \"local:0\", app 1440 x 3120, density 600 (515.154 x 514.597) dpi, layerStack 0, colorMode 0, "
+ "supportedColorModes [0, 7, 9], address {port=129, model=0}, deviceProductInfo DeviceProductInfo{name=, manufacturerPnpId=QCM, "
+ "productId=1, modelYear=null, manufactureDate=ManufactureDate{week=27, year=2006}, relativeAddress=null}, removeMode 0}\n"
+ " mOverrideDisplayInfo=DisplayInfo{\"Built-in Screen\", displayId 0, FLAG_SECURE, FLAG_SUPPORTS_PROTECTED_BUFFERS, "
+ "FLAG_TRUSTED, real 1440 x 3120, largest app 3120 x 2983, smallest app 1440 x 1303, appVsyncOff 2000000, presDeadline 11111111, "
+ "mode 2, defaultMode 1, modes [{id=1, width=1440, height=3120, fps=60.0}, {id=2, width=1440, height=3120, fps=90.0}, {id=3, "
+ "width=1080, height=2340, fps=90.0}, {id=4, width=1080, height=2340, fps=60.0}], hdrCapabilities "
+ "HdrCapabilities{mSupportedHdrTypes=[2, 3, 4], mMaxLuminance=540.0, mMaxAverageLuminance=270.1, mMinLuminance=0.2}, "
+ "minimalPostProcessingSupported false, rotation 0, state ON, type INTERNAL, uniqueId \"local:0\", app 1440 x 3120, density 600 "
+ "(515.154 x 514.597) dpi, layerStack 0, colorMode 0, supportedColorModes [0, 7, 9], address {port=129, model=0}, deviceProductInfo "
+ "DeviceProductInfo{name=, manufacturerPnpId=QCM, productId=1, modelYear=null, manufactureDate=ManufactureDate{week=27, year=2006}, "
+ "relativeAddress=null}, removeMode 0}\n"
+ " mRequestedMinimalPostProcessing=false\n";
DisplayInfo displayInfo = DisplayManager.parseDisplayInfo(partialOutput, 0);
Assert.assertNotNull(displayInfo);
Assert.assertEquals(0, displayInfo.getDisplayId());
Assert.assertEquals(0, displayInfo.getRotation());
Assert.assertEquals(0, displayInfo.getLayerStack());
// FLAG_TRUSTED does not exist in Display (@TestApi), so it won't be reported
Assert.assertEquals(Display.FLAG_SECURE | Display.FLAG_SUPPORTS_PROTECTED_BUFFERS, displayInfo.getFlags());
Assert.assertEquals(1440, displayInfo.getSize().getWidth());
Assert.assertEquals(3120, displayInfo.getSize().getHeight());
}
@Test
public void testParseDisplayInfoFromDumpsysDisplayAPI31() {
/* @formatter:off */
String partialOutput = "Logical Displays: size=1\n"
+ " Display 0:\n"
+ " mDisplayId=0\n"
+ " mPhase=1\n"
+ " mLayerStack=0\n"
+ " mHasContent=true\n"
+ " mDesiredDisplayModeSpecs={baseModeId=1 allowGroupSwitching=false primaryRefreshRateRange=[0 60] appRequestRefreshRateRange=[0 "
+ "Infinity]}\n"
+ " mRequestedColorMode=0\n"
+ " mDisplayOffset=(0, 0)\n"
+ " mDisplayScalingDisabled=false\n"
+ " mPrimaryDisplayDevice=Built-in Screen\n"
+ " mBaseDisplayInfo=DisplayInfo{\"Built-in Screen\", displayId 0\", displayGroupId 0, FLAG_SECURE, "
+ "FLAG_SUPPORTS_PROTECTED_BUFFERS, FLAG_TRUSTED, real 1080 x 2280, largest app 1080 x 2280, smallest app 1080 x 2280, appVsyncOff "
+ "1000000, presDeadline 16666666, mode 1, defaultMode 1, modes [{id=1, width=1080, height=2280, fps=60.000004, "
+ "alternativeRefreshRates=[]}], hdrCapabilities HdrCapabilities{mSupportedHdrTypes=[], mMaxLuminance=500.0, "
+ "mMaxAverageLuminance=500.0, mMinLuminance=0.0}, userDisabledHdrTypes [], minimalPostProcessingSupported false, rotation 0, state "
+ "ON, type INTERNAL, uniqueId \"local:0\", app 1080 x 2280, density 440 (440.0 x 440.0) dpi, layerStack 0, colorMode 0, "
+ "supportedColorModes [0], address {port=0, model=0}, deviceProductInfo DeviceProductInfo{name=EMU_display_0, "
+ "manufacturerPnpId=GGL, productId=1, modelYear=null, manufactureDate=ManufactureDate{week=27, year=2006}, connectionToSinkType=0}, "
+ "removeMode 0, refreshRateOverride 0.0, brightnessMinimum 0.0, brightnessMaximum 1.0, brightnessDefault 0.39763778}\n"
+ " mOverrideDisplayInfo=DisplayInfo{\"Built-in Screen\", displayId 0\", displayGroupId 0, FLAG_SECURE, "
+ "FLAG_SUPPORTS_PROTECTED_BUFFERS, FLAG_TRUSTED, real 1080 x 2280, largest app 2148 x 2065, smallest app 1080 x 997, appVsyncOff "
+ "1000000, presDeadline 16666666, mode 1, defaultMode 1, modes [{id=1, width=1080, height=2280, fps=60.000004, "
+ "alternativeRefreshRates=[]}], hdrCapabilities HdrCapabilities{mSupportedHdrTypes=[], mMaxLuminance=500.0, "
+ "mMaxAverageLuminance=500.0, mMinLuminance=0.0}, userDisabledHdrTypes [], minimalPostProcessingSupported false, rotation 0, state "
+ "ON, type INTERNAL, uniqueId \"local:0\", app 1080 x 2148, density 440 (440.0 x 440.0) dpi, layerStack 0, colorMode 0, "
+ "supportedColorModes [0], address {port=0, model=0}, deviceProductInfo DeviceProductInfo{name=EMU_display_0, "
+ "manufacturerPnpId=GGL, productId=1, modelYear=null, manufactureDate=ManufactureDate{week=27, year=2006}, connectionToSinkType=0}, "
+ "removeMode 0, refreshRateOverride 0.0, brightnessMinimum 0.0, brightnessMaximum 1.0, brightnessDefault 0.39763778}\n"
+ " mRequestedMinimalPostProcessing=false\n"
+ " mFrameRateOverrides=[]\n"
+ " mPendingFrameRateOverrideUids={}\n";
DisplayInfo displayInfo = DisplayManager.parseDisplayInfo(partialOutput, 0);
Assert.assertNotNull(displayInfo);
Assert.assertEquals(0, displayInfo.getDisplayId());
Assert.assertEquals(0, displayInfo.getRotation());
Assert.assertEquals(0, displayInfo.getLayerStack());
// FLAG_TRUSTED does not exist in Display (@TestApi), so it won't be reported
Assert.assertEquals(Display.FLAG_SECURE | Display.FLAG_SUPPORTS_PROTECTED_BUFFERS, displayInfo.getFlags());
Assert.assertEquals(1080, displayInfo.getSize().getWidth());
Assert.assertEquals(2280, displayInfo.getSize().getHeight());
}
@Test
public void testParseDisplayInfoFromDumpsysDisplayAPI31NoFlags() {
/* @formatter:off */
String partialOutput = "Logical Displays: size=1\n"
+ " Display 0:\n"
+ " mDisplayId=0\n"
+ " mPhase=1\n"
+ " mLayerStack=0\n"
+ " mHasContent=true\n"
+ " mDesiredDisplayModeSpecs={baseModeId=1 allowGroupSwitching=false primaryRefreshRateRange=[0 60] appRequestRefreshRateRange=[0 "
+ "Infinity]}\n"
+ " mRequestedColorMode=0\n"
+ " mDisplayOffset=(0, 0)\n"
+ " mDisplayScalingDisabled=false\n"
+ " mPrimaryDisplayDevice=Built-in Screen\n"
+ " mBaseDisplayInfo=DisplayInfo{\"Built-in Screen\", displayId 0\", displayGroupId 0, "
+ "real 1080 x 2280, largest app 1080 x 2280, smallest app 1080 x 2280, appVsyncOff 1000000, presDeadline 16666666, "
+ "mode 1, defaultMode 1, modes [{id=1, width=1080, height=2280, fps=60.000004, alternativeRefreshRates=[]}], "
+ "hdrCapabilities HdrCapabilities{mSupportedHdrTypes=[], mMaxLuminance=500.0, mMaxAverageLuminance=500.0, mMinLuminance=0.0}, "
+ "userDisabledHdrTypes [], minimalPostProcessingSupported false, rotation 0, state ON, type INTERNAL, uniqueId \"local:0\", app "
+ "1080 x 2280, density 440 (440.0 x 440.0) dpi, layerStack 0, colorMode 0, supportedColorModes [0], address {port=0, model=0}, "
+ "deviceProductInfo DeviceProductInfo{name=EMU_display_0, manufacturerPnpId=GGL, productId=1, modelYear=null, "
+ "manufactureDate=ManufactureDate{week=27, year=2006}, connectionToSinkType=0}, removeMode 0, refreshRateOverride 0.0, "
+ "brightnessMinimum 0.0, brightnessMaximum 1.0, brightnessDefault 0.39763778}\n"
+ " mOverrideDisplayInfo=DisplayInfo{\"Built-in Screen\", displayId 0\", displayGroupId 0, FLAG_SECURE, "
+ "FLAG_SUPPORTS_PROTECTED_BUFFERS, FLAG_TRUSTED, real 1080 x 2280, largest app 2148 x 2065, smallest app 1080 x 997, appVsyncOff "
+ "1000000, presDeadline 16666666, mode 1, defaultMode 1, modes [{id=1, width=1080, height=2280, fps=60.000004, "
+ "alternativeRefreshRates=[]}], hdrCapabilities HdrCapabilities{mSupportedHdrTypes=[], mMaxLuminance=500.0, "
+ "mMaxAverageLuminance=500.0, mMinLuminance=0.0}, userDisabledHdrTypes [], minimalPostProcessingSupported false, rotation 0, state "
+ "ON, type INTERNAL, uniqueId \"local:0\", app 1080 x 2148, density 440 (440.0 x 440.0) dpi, layerStack 0, colorMode 0, "
+ "supportedColorModes [0], address {port=0, model=0}, deviceProductInfo DeviceProductInfo{name=EMU_display_0, "
+ "manufacturerPnpId=GGL, productId=1, modelYear=null, manufactureDate=ManufactureDate{week=27, year=2006}, connectionToSinkType=0}, "
+ "removeMode 0, refreshRateOverride 0.0, brightnessMinimum 0.0, brightnessMaximum 1.0, brightnessDefault 0.39763778}\n"
+ " mRequestedMinimalPostProcessing=false\n"
+ " mFrameRateOverrides=[]\n"
+ " mPendingFrameRateOverrideUids={}\n";
DisplayInfo displayInfo = DisplayManager.parseDisplayInfo(partialOutput, 0);
Assert.assertNotNull(displayInfo);
Assert.assertEquals(0, displayInfo.getDisplayId());
Assert.assertEquals(0, displayInfo.getRotation());
Assert.assertEquals(0, displayInfo.getLayerStack());
Assert.assertEquals(0, displayInfo.getFlags());
Assert.assertEquals(1080, displayInfo.getSize().getWidth());
Assert.assertEquals(2280, displayInfo.getSize().getHeight());
}
}

View File

@@ -126,8 +126,8 @@ public class ControlMessageReaderTest {
dos.writeInt(1026);
dos.writeShort(1080);
dos.writeShort(1920);
dos.writeInt(1);
dos.writeInt(-1);
dos.writeShort(0); // 0.0f encoded as i16
dos.writeShort(0x8000); // -1.0f encoded as i16
dos.writeInt(1);
byte[] packet = bos.toByteArray();
@@ -143,8 +143,8 @@ public class ControlMessageReaderTest {
Assert.assertEquals(1026, event.getPosition().getPoint().getY());
Assert.assertEquals(1080, event.getPosition().getScreenSize().getWidth());
Assert.assertEquals(1920, event.getPosition().getScreenSize().getHeight());
Assert.assertEquals(1, event.getHScroll());
Assert.assertEquals(-1, event.getVScroll());
Assert.assertEquals(0f, event.getHScroll(), 0f);
Assert.assertEquals(-1f, event.getVScroll(), 0f);
Assert.assertEquals(1, event.getButtons());
}