Compare commits

..

2 Commits

Author SHA1 Message Date
Romain Vimont
185802fbe2 longlong 2019-12-10 09:22:30 +01:00
Romain Vimont
78f566bd6d Move platorm-specific defines to common.h
They could be useful anywhere.
2019-12-10 09:11:09 +01:00
16 changed files with 108 additions and 235 deletions

View File

@@ -233,10 +233,10 @@ You can then [run](README.md#run) _scrcpy_.
## Prebuilt server ## Prebuilt server
- [`scrcpy-server-v1.12.1`][direct-scrcpy-server] - [`scrcpy-server-v1.12`][direct-scrcpy-server]
_(SHA-256: 63e569c8a1d0c1df31d48c4214871c479a601782945fed50c1e61167d78266ea)_ _(SHA-256: b6595262c230e9773fdb817257abcc8c6e6e00f15b1c32b6a850ccfd8176dc10)_
[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v1.12.1/scrcpy-server-v1.12.1 [direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v1.12/scrcpy-server-v1.12
Download the prebuilt server somewhere, and specify its path during the Meson Download the prebuilt server somewhere, and specify its path during the Meson
configuration: configuration:

View File

@@ -189,7 +189,7 @@ The client uses 4 threads:
recording, recording,
- the **controller** thread, sending _control messages_ to the server, - the **controller** thread, sending _control messages_ to the server,
- the **receiver** thread (managed by the controller), receiving _device - the **receiver** thread (managed by the controller), receiving _device
messages_ from the server. messages_ from the client.
In addition, another thread can be started if necessary to handle APK In addition, another thread can be started if necessary to handle APK
installation or file push requests (via drag&drop on the main window) or to installation or file push requests (via drag&drop on the main window) or to
@@ -214,7 +214,7 @@ When a new decoded frame is available, the decoder _swaps_ the decoding and
rendering frame (with proper synchronization). Thus, it immediatly starts rendering frame (with proper synchronization). Thus, it immediatly starts
to decode a new frame while the main thread renders the last one. to decode a new frame while the main thread renders the last one.
If a [recorder] is present (i.e. `--record` is enabled), then it muxes the raw If a [recorder] is present (i.e. `--record` is enabled), then its muxes the raw
H.264 packet to the output video file. H.264 packet to the output video file.
[stream]: https://github.com/Genymobile/scrcpy/blob/ffe0417228fb78ab45b7ee4e202fc06fc8875bf3/app/src/stream.h [stream]: https://github.com/Genymobile/scrcpy/blob/ffe0417228fb78ab45b7ee4e202fc06fc8875bf3/app/src/stream.h

149
FAQ.md
View File

@@ -3,102 +3,19 @@
Here are the common reported problems and their status. Here are the common reported problems and their status.
## `adb` issues ### On Windows, my device is not detected
`scrcpy` execute `adb` commands to initialize the connection with the device. If The most common is your device not being detected by `adb`, or is unauthorized.
`adb` fails, then scrcpy will not work. Check everything is ok by calling:
In that case, it will print this error: adb devices
> ERROR: "adb push" returned with value 1 Windows may need some [drivers] to detect your device.
This is typically not a bug in _scrcpy_, but a problem in your environment.
To find out the cause, execute:
```bash
adb devices
```
### `adb` not found
You need `adb` accessible from your `PATH`.
On Windows, the current directory is in your `PATH`, and `adb.exe` is included
in the release, so it should work out-of-the-box.
### Device unauthorized
Check [stackoverflow][device-unauthorized].
[device-unauthorized]: https://stackoverflow.com/questions/23081263/adb-android-device-unauthorized
### Device not detected
If your device is not detected, you may need some [drivers] (on Windows).
[drivers]: https://developer.android.com/studio/run/oem-usb.html [drivers]: https://developer.android.com/studio/run/oem-usb.html
### Several devices connected ### I can only mirror, I cannot interact with the device
If several devices are connected, you will encounter this error:
> adb: error: failed to get feature set: more than one device/emulator
the identifier of the device you want to mirror must be provided:
```bash
scrcpy -s 01234567890abcdef
```
Note that if your device is connected over TCP/IP, you'll get this message:
> adb: error: more than one device/emulator
> ERROR: "adb reverse" returned with value 1
> WARN: 'adb reverse' failed, fallback to 'adb forward'
This is expected (due to a bug on old Android versions, see [#5]), but in that
case, scrcpy fallbacks to a different method, which should work.
[#5]: https://github.com/Genymobile/scrcpy/issues/5
### Conflicts between adb versions
> adb server version (41) doesn't match this client (39); killing...
This error occurs when you use several `adb` versions simultaneously. You must
find the program using a different `adb` version, and use the same `adb` version
everywhere.
You could overwrite the `adb` binary in the other program, or ask _scrcpy_ to
use a specific `adb` binary, by setting the `ADB` environment variable:
```bash
set ADB=/path/to/your/adb
scrcpy
```
### Device disconnected
If _scrcpy_ stops itself with the warning "Device disconnected", then the
`adb` connection has been closed.
Try with another USB cable or plug it into another USB port. See [#281] and
[#283].
[#281]: https://github.com/Genymobile/scrcpy/issues/281
[#283]: https://github.com/Genymobile/scrcpy/issues/283
## Control issues
### Mouse and keyboard do not work
On some devices, you may need to enable an option to allow [simulating input]. On some devices, you may need to enable an option to allow [simulating input].
In developer options, enable: In developer options, enable:
@@ -112,25 +29,18 @@ In developer options, enable:
### Mouse clicks at wrong location ### Mouse clicks at wrong location
On MacOS, with HiDPI support and multiple screens, input location are wrongly On MacOS, with HiDPI support and multiple screens, input location are wrongly
scaled. See [#15]. scaled. See [issue 15].
[#15]: https://github.com/Genymobile/scrcpy/issues/15 [issue 15]: https://github.com/Genymobile/scrcpy/issues/15
Open _scrcpy_ directly on the monitor you use it. A workaround is to build with HiDPI support disabled:
```bash
meson x --buildtype release -Dhidpi_support=false
```
### Special characters do not work However, the video will be displayed at lower resolution.
Injecting text input is [limited to ASCII characters][text-input]. A trick
allows to also inject some [accented characters][accented-characters], but
that's all. See [#37].
[text-input]: https://github.com/Genymobile/scrcpy/issues?q=is%3Aopen+is%3Aissue+label%3Aunicode
[accented-characters]: https://blog.rom1v.com/2018/03/introducing-scrcpy/#handle-accented-characters
[#37]: https://github.com/Genymobile/scrcpy/issues/37
## Client issues
### The quality is low on HiDPI display ### The quality is low on HiDPI display
@@ -141,11 +51,6 @@ On Windows, you may need to configure the [scaling behavior].
[scaling behavior]: https://github.com/Genymobile/scrcpy/issues/40#issuecomment-424466723 [scaling behavior]: https://github.com/Genymobile/scrcpy/issues/40#issuecomment-424466723
If your computer definition is far smaller than your screen, then you'll get
poor quality. See [#40].
[#40]: https://github.com/Genymobile/scrcpy/issues/40
### KWin compositor crashes ### KWin compositor crashes
@@ -156,29 +61,19 @@ As a workaround, [disable "Block compositing"][kwin].
[kwin]: https://github.com/Genymobile/scrcpy/issues/114#issuecomment-378778613 [kwin]: https://github.com/Genymobile/scrcpy/issues/114#issuecomment-378778613
## Crashes ### I get an error "Could not open video stream"
### Exception
There may be many reasons. One common cause is that the hardware encoder of your There may be many reasons. One common cause is that the hardware encoder of your
device is not able to encode at the given definition: device is not able to encode at the given definition:
> ``` ```
> ERROR: Exception on thread Thread[main,5,main] ERROR: Exception on thread Thread[main,5,main]
> android.media.MediaCodec$CodecException: Error 0xfffffc0e android.media.MediaCodec$CodecException: Error 0xfffffc0e
> ... ...
> Exit due to uncaughtException in main thread: Exit due to uncaughtException in main thread:
> ERROR: Could not open video stream ERROR: Could not open video stream
> INFO: Initial texture: 1080x2336 INFO: Initial texture: 1080x2336
> ``` ```
or
> ```
> ERROR: Exception on thread Thread[main,5,main]
> java.lang.IllegalStateException
> at android.media.MediaCodec.native_dequeueOutputBuffer(Native Method)
> ```
Just try with a lower definition: Just try with a lower definition:

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
# scrcpy (v1.12.1) # scrcpy (v1.12)
This application provides display and control of Android devices connected on This application provides display and control of Android devices connected on
USB (or [over TCP/IP][article-tcpip]). It does not require any _root_ access. USB (or [over TCP/IP][article-tcpip]). It does not require any _root_ access.
@@ -37,11 +37,8 @@ control it using keyboard and mouse.
### Linux ### Linux
In Debian (_testing_ and _sid_ for now): On Linux, you typically need to [build the app manually][BUILD]. Don't worry,
it's not that hard.
```
apt install scrcpy
```
A [Snap] package is available: [`scrcpy`][snap-link]. A [Snap] package is available: [`scrcpy`][snap-link].
@@ -59,38 +56,19 @@ For Gentoo, an [Ebuild] is available: [`scrcpy/`][ebuild-link].
[Ebuild]: https://wiki.gentoo.org/wiki/Ebuild [Ebuild]: https://wiki.gentoo.org/wiki/Ebuild
[ebuild-link]: https://github.com/maggu2810/maggu2810-overlay/tree/master/app-mobilephone/scrcpy [ebuild-link]: https://github.com/maggu2810/maggu2810-overlay/tree/master/app-mobilephone/scrcpy
You could also [build the app manually][BUILD] (don't worry, it's not that
hard).
### Windows ### Windows
For Windows, for simplicity, a prebuilt archive with all the dependencies For Windows, for simplicity, prebuilt archives with all the dependencies
(including `adb`) is available: (including `adb`) are available:
- [`scrcpy-win64-v1.12.1.zip`][direct-win64] - [`scrcpy-win32-v1.12.zip`][direct-win32]
_(SHA-256: 57d34b6d16cfd9fe169bc37c4df58ebd256d05c1ea3febc63d9cb0a027ab47c9)_ _(SHA-256: b2c8c4a3899c037cf448a2102906775114826ba646ce1b847826925103fa801d)_
- [`scrcpy-win64-v1.12.zip`][direct-win64]
_(SHA-256: 7d47983b426f7287de0230b88975dc17c1d9c343fa61a93ff2af78b6e9ef5c8c)_
[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v1.12.1/scrcpy-win64-v1.12.1.zip [direct-win32]: https://github.com/Genymobile/scrcpy/releases/download/v1.12/scrcpy-win32-v1.12.zip
[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v1.12/scrcpy-win64-v1.12.zip
It is also available in [Chocolatey]:
[Chocolatey]: https://chocolatey.org/
```bash
choco install scrcpy
choco install adb # if you don't have it yet
```
And in [Scoop]:
```bash
scoop install scrcpy
scoop install adb # if you don't have it yet
```
[Scoop]: https://scoop.sh
You can also [build the app manually][BUILD]. You can also [build the app manually][BUILD].
@@ -236,13 +214,6 @@ scrcpy --serial 0123456789abcdef
scrcpy -s 0123456789abcdef # short version scrcpy -s 0123456789abcdef # short version
``` ```
If the device is connected over TCP/IP:
```bash
scrcpy --serial 192.168.0.1:5555
scrcpy -s 192.168.0.1:5555 # short version
```
You can start several instances of _scrcpy_ for several devices. You can start several instances of _scrcpy_ for several devices.
#### SSH tunnel #### SSH tunnel
@@ -518,7 +489,7 @@ Read the [developers page].
## Licence ## Licence
Copyright (C) 2018 Genymobile Copyright (C) 2018 Genymobile
Copyright (C) 2018-2020 Romain Vimont Copyright (C) 2018-2019 Romain Vimont
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@@ -261,7 +261,7 @@ Copyright \(co 2018 Genymobile
Genymobile Genymobile
.UE .UE
Copyright \(co 2018\-2020 Copyright \(co 2018\-2019
.MT rom@rom1v.com .MT rom@rom1v.com
Romain Vimont Romain Vimont
.ME .ME

View File

@@ -2,6 +2,7 @@
#include <getopt.h> #include <getopt.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include "config.h" #include "config.h"
@@ -197,9 +198,9 @@ scrcpy_print_usage(const char *arg0) {
} }
static bool static bool
parse_integer_arg(const char *s, long *out, bool accept_suffix, long min, parse_integer_arg(const char *s, long long *out, bool accept_suffix,
long max, const char *name) { long long min, long long max, const char *name) {
long value; long long value;
bool ok; bool ok;
if (accept_suffix) { if (accept_suffix) {
ok = parse_integer_with_suffix(s, &value); ok = parse_integer_with_suffix(s, &value);
@@ -212,8 +213,8 @@ parse_integer_arg(const char *s, long *out, bool accept_suffix, long min,
} }
if (value < min || value > max) { if (value < min || value > max) {
LOGE("Could not parse %s: value (%ld) out-of-range (%ld; %ld)", LOGE("Could not parse %s: value (%" PRIlld ") out-of-range (%"
name, value, min, max); PRIlld "; %" PRIlld ")", name, value, min, max);
return false; return false;
} }
@@ -223,10 +224,8 @@ parse_integer_arg(const char *s, long *out, bool accept_suffix, long min,
static bool static bool
parse_bit_rate(const char *s, uint32_t *bit_rate) { parse_bit_rate(const char *s, uint32_t *bit_rate) {
long value; long long value;
// long may be 32 bits (it is the case on mingw), so do not use more than bool ok = parse_integer_arg(s, &value, true, 0, 0xFFFFFFFFLL, "bit-rate");
// 31 bits (long is signed)
bool ok = parse_integer_arg(s, &value, true, 0, 0x7FFFFFFF, "bit-rate");
if (!ok) { if (!ok) {
return false; return false;
} }
@@ -237,7 +236,7 @@ parse_bit_rate(const char *s, uint32_t *bit_rate) {
static bool static bool
parse_max_size(const char *s, uint16_t *max_size) { parse_max_size(const char *s, uint16_t *max_size) {
long value; long long value;
bool ok = parse_integer_arg(s, &value, false, 0, 0xFFFF, "max size"); bool ok = parse_integer_arg(s, &value, false, 0, 0xFFFF, "max size");
if (!ok) { if (!ok) {
return false; return false;
@@ -249,7 +248,7 @@ parse_max_size(const char *s, uint16_t *max_size) {
static bool static bool
parse_max_fps(const char *s, uint16_t *max_fps) { parse_max_fps(const char *s, uint16_t *max_fps) {
long value; long long value;
bool ok = parse_integer_arg(s, &value, false, 0, 1000, "max fps"); bool ok = parse_integer_arg(s, &value, false, 0, 1000, "max fps");
if (!ok) { if (!ok) {
return false; return false;
@@ -261,7 +260,7 @@ parse_max_fps(const char *s, uint16_t *max_fps) {
static bool static bool
parse_window_position(const char *s, int16_t *position) { parse_window_position(const char *s, int16_t *position) {
long value; long long value;
bool ok = parse_integer_arg(s, &value, false, -1, 0x7FFF, bool ok = parse_integer_arg(s, &value, false, -1, 0x7FFF,
"window position"); "window position");
if (!ok) { if (!ok) {
@@ -274,7 +273,7 @@ parse_window_position(const char *s, int16_t *position) {
static bool static bool
parse_window_dimension(const char *s, uint16_t *dimension) { parse_window_dimension(const char *s, uint16_t *dimension) {
long value; long long value;
bool ok = parse_integer_arg(s, &value, false, 0, 0xFFFF, bool ok = parse_integer_arg(s, &value, false, 0, 0xFFFF,
"window dimension"); "window dimension");
if (!ok) { if (!ok) {
@@ -287,7 +286,7 @@ parse_window_dimension(const char *s, uint16_t *dimension) {
static bool static bool
parse_port(const char *s, uint16_t *port) { parse_port(const char *s, uint16_t *port) {
long value; long long value;
bool ok = parse_integer_arg(s, &value, false, 0, 0xFFFF, "port"); bool ok = parse_integer_arg(s, &value, false, 0, 0xFFFF, "port");
if (!ok) { if (!ok) {
return false; return false;

View File

@@ -4,38 +4,8 @@
#include <stdbool.h> #include <stdbool.h>
#include <inttypes.h> #include <inttypes.h>
#ifdef _WIN32
// not needed here, but winsock2.h must never be included AFTER windows.h
# include <winsock2.h>
# include <windows.h>
# define PATH_SEPARATOR '\\'
# define PRIexitcode "lu"
// <https://stackoverflow.com/a/44383330/1987178>
# ifdef _WIN64
# define PRIsizet PRIu64
# else
# define PRIsizet PRIu32
# endif
# define PROCESS_NONE NULL
# define NO_EXIT_CODE -1u // max value as unsigned
typedef HANDLE process_t;
typedef DWORD exit_code_t;
#else
# include <sys/types.h>
# define PATH_SEPARATOR '/'
# define PRIsizet "zu"
# define PRIexitcode "d"
# define PROCESS_NONE -1
# define NO_EXIT_CODE -1
typedef pid_t process_t;
typedef int exit_code_t;
#endif
#include "config.h" #include "config.h"
#include "common.h"
enum process_result { enum process_result {
PROCESS_SUCCESS, PROCESS_SUCCESS,

View File

@@ -5,6 +5,44 @@
#include "config.h" #include "config.h"
#ifdef _WIN32
// not needed here, but winsock2.h must never be included AFTER windows.h
# include <winsock2.h>
# include <windows.h>
# define PATH_SEPARATOR '\\'
# define PRIexitcode "lu"
// <https://stackoverflow.com/a/44383330/1987178>
# ifdef _WIN64
# define PRIsizet PRIu64
# else
# define PRIsizet PRIu32
# endif
# define PROCESS_NONE NULL
# define NO_EXIT_CODE -1u // max value as unsigned
typedef HANDLE process_t;
typedef DWORD exit_code_t;
#else
# include <sys/types.h>
# define PATH_SEPARATOR '/'
# define PRIsizet "zu"
# define PRIexitcode "d"
# define PROCESS_NONE -1
# define NO_EXIT_CODE -1
typedef pid_t process_t;
typedef int exit_code_t;
#endif
// in MinGW, "%ll" is not supported as printf format for long long
# ifdef __MINGW32__
# define PRIlld "I64d"
# else
# define PRIlld "lld"
# endif
#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0])) #define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))
#define MIN(X,Y) (X) < (Y) ? (X) : (Y) #define MIN(X,Y) (X) < (Y) ? (X) : (Y)
#define MAX(X,Y) (X) > (Y) ? (X) : (Y) #define MAX(X,Y) (X) > (Y) ? (X) : (Y)

View File

@@ -63,13 +63,13 @@ strquote(const char *src) {
} }
bool bool
parse_integer(const char *s, long *out) { parse_integer(const char *s, long long *out) {
char *endptr; char *endptr;
if (*s == '\0') { if (*s == '\0') {
return false; return false;
} }
errno = 0; errno = 0;
long value = strtol(s, &endptr, 0); long long value = strtol(s, &endptr, 0);
if (errno == ERANGE) { if (errno == ERANGE) {
return false; return false;
} }
@@ -82,13 +82,13 @@ parse_integer(const char *s, long *out) {
} }
bool bool
parse_integer_with_suffix(const char *s, long *out) { parse_integer_with_suffix(const char *s, long long *out) {
char *endptr; char *endptr;
if (*s == '\0') { if (*s == '\0') {
return false; return false;
} }
errno = 0; errno = 0;
long value = strtol(s, &endptr, 0); long long value = strtoll(s, &endptr, 0);
if (errno == ERANGE) { if (errno == ERANGE) {
return false; return false;
} }
@@ -106,8 +106,8 @@ parse_integer_with_suffix(const char *s, long *out) {
} }
} }
if ((value < 0 && LONG_MIN / mul > value) || if ((value < 0 && LLONG_MIN / mul > value) ||
(value > 0 && LONG_MAX / mul < value)) { (value > 0 && LLONG_MAX / mul < value)) {
return false; return false;
} }

View File

@@ -29,14 +29,14 @@ strquote(const char *src);
// parse s as an integer into value // parse s as an integer into value
// returns true if the conversion succeeded, false otherwise // returns true if the conversion succeeded, false otherwise
bool bool
parse_integer(const char *s, long *out); parse_integer(const char *s, long long *out);
// parse s as an integer into value // parse s as an integer into value
// like parse_integer(), but accept 'k'/'K' (x1000) and 'm'/'M' (x1000000) as // like parse_integer(), but accept 'k'/'K' (x1000) and 'm'/'M' (x1000000) as
// suffix // suffix
// returns true if the conversion succeeded, false otherwise // returns true if the conversion succeeded, false otherwise
bool bool
parse_integer_with_suffix(const char *s, long *out); parse_integer_with_suffix(const char *s, long long *out);
// return the index to truncate a UTF-8 string at a valid position // return the index to truncate a UTF-8 string at a valid position
size_t size_t

View File

@@ -171,7 +171,7 @@ static void test_utf8_truncate(void) {
} }
static void test_parse_integer(void) { static void test_parse_integer(void) {
long value; long long value;
bool ok = parse_integer("1234", &value); bool ok = parse_integer("1234", &value);
assert(ok); assert(ok);
assert(value == 1234); assert(value == 1234);
@@ -188,7 +188,7 @@ static void test_parse_integer(void) {
} }
static void test_parse_integer_with_suffix(void) { static void test_parse_integer_with_suffix(void) {
long value; long long value;
bool ok = parse_integer_with_suffix("1234", &value); bool ok = parse_integer_with_suffix("1234", &value);
assert(ok); assert(ok);
assert(value == 1234); assert(value == 1234);

View File

@@ -1,5 +1,5 @@
project('scrcpy', 'c', project('scrcpy', 'c',
version: '1.12.1', version: '1.12',
meson_version: '>= 0.37', meson_version: '>= 0.37',
default_options: [ default_options: [
'c_std=c11', 'c_std=c11',

View File

@@ -6,8 +6,8 @@ android {
applicationId "com.genymobile.scrcpy" applicationId "com.genymobile.scrcpy"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 29 targetSdkVersion 29
versionCode 14 versionCode 13
versionName "1.12.1" versionName "1.12"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
} }
buildTypes { buildTypes {

View File

@@ -12,7 +12,7 @@
set -e set -e
SCRCPY_DEBUG=false SCRCPY_DEBUG=false
SCRCPY_VERSION_NAME=1.12.1 SCRCPY_VERSION_NAME=1.12
PLATFORM=${ANDROID_PLATFORM:-29} PLATFORM=${ANDROID_PLATFORM:-29}
BUILD_TOOLS=${ANDROID_BUILD_TOOLS:-29.0.2} BUILD_TOOLS=${ANDROID_BUILD_TOOLS:-29.0.2}