Compare commits
18 Commits
shortcutmo
...
fixrepeat
Author | SHA1 | Date | |
---|---|---|---|
8fb01922a3 | |||
521f2fe994 | |||
edc4f7675f | |||
712f1fa6b2 | |||
1ba06037f8 | |||
da63e3774b | |||
cf9d44979c | |||
84f1d9e375 | |||
65d06a3663 | |||
74079ea5e4 | |||
0870d8620f | |||
d49cffb938 | |||
5086e7b744 | |||
e99b896ae2 | |||
8c27f59aa5 | |||
42641d2737 | |||
3c0fc8f54f | |||
1b73eff3c9 |
19
BUILD.md
19
BUILD.md
@ -176,8 +176,8 @@ Additionally, if you want to build the server, install Java 8 from Caskroom, and
|
||||
make it avaliable from the `PATH`:
|
||||
|
||||
```bash
|
||||
brew tap caskroom/versions
|
||||
brew cask install java8
|
||||
brew tap homebrew/cask-versions
|
||||
brew cask install adoptopenjdk/openjdk/adoptopenjdk8
|
||||
export JAVA_HOME="$(/usr/libexec/java_home --version 1.8)"
|
||||
export PATH="$JAVA_HOME/bin:$PATH"
|
||||
```
|
||||
@ -190,12 +190,17 @@ See [pierlon/scrcpy-docker](https://github.com/pierlon/scrcpy-docker).
|
||||
## Common steps
|
||||
|
||||
If you want to build the server, install the [Android SDK] (_Android Studio_),
|
||||
and set `ANDROID_HOME` to its directory. For example:
|
||||
and set `ANDROID_SDK_ROOT` to its directory. For example:
|
||||
|
||||
[Android SDK]: https://developer.android.com/studio/index.html
|
||||
|
||||
```bash
|
||||
export ANDROID_HOME=~/android/sdk
|
||||
# Linux
|
||||
export ANDROID_SDK_ROOT=~/Android/Sdk
|
||||
# Mac
|
||||
export ANDROID_SDK_ROOT=~/Library/Android/sdk
|
||||
# Windows
|
||||
set ANDROID_SDK_ROOT=%LOCALAPPDATA%\Android\sdk
|
||||
```
|
||||
|
||||
If you don't want to build the server, use the [prebuilt server].
|
||||
@ -249,10 +254,10 @@ You can then [run](README.md#run) _scrcpy_.
|
||||
|
||||
## Prebuilt server
|
||||
|
||||
- [`scrcpy-server-v1.14`][direct-scrcpy-server]
|
||||
_(SHA-256: 1d1b18a2b80e956771fd63b99b414d2d028713a8f12ddfa5a369709ad4295620)_
|
||||
- [`scrcpy-server-v1.15`][direct-scrcpy-server]
|
||||
_(SHA-256: e160c46784f30566eae621cad88bfb34e124f945cb8274b8dfc615b613b86dd8)_
|
||||
|
||||
[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v1.14/scrcpy-server-v1.14
|
||||
[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v1.15/scrcpy-server-v1.15
|
||||
|
||||
Download the prebuilt server somewhere, and specify its path during the Meson
|
||||
configuration:
|
||||
|
@ -100,11 +100,11 @@ dist-win32: build-server build-win32 build-win32-noconsole
|
||||
cp "$(SERVER_BUILD_DIR)"/server/scrcpy-server "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp "$(WIN32_BUILD_DIR)"/app/scrcpy.exe "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp "$(WIN32_NOCONSOLE_BUILD_DIR)"/app/scrcpy.exe "$(DIST)/$(WIN32_TARGET_DIR)/scrcpy-noconsole.exe"
|
||||
cp prebuilt-deps/ffmpeg-4.2.2-win32-shared/bin/avutil-56.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp prebuilt-deps/ffmpeg-4.2.2-win32-shared/bin/avcodec-58.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp prebuilt-deps/ffmpeg-4.2.2-win32-shared/bin/avformat-58.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp prebuilt-deps/ffmpeg-4.2.2-win32-shared/bin/swresample-3.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp prebuilt-deps/ffmpeg-4.2.2-win32-shared/bin/swscale-5.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp prebuilt-deps/ffmpeg-4.3.1-win32-shared/bin/avutil-56.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp prebuilt-deps/ffmpeg-4.3.1-win32-shared/bin/avcodec-58.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp prebuilt-deps/ffmpeg-4.3.1-win32-shared/bin/avformat-58.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp prebuilt-deps/ffmpeg-4.3.1-win32-shared/bin/swresample-3.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp prebuilt-deps/ffmpeg-4.3.1-win32-shared/bin/swscale-5.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp prebuilt-deps/platform-tools/adb.exe "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp prebuilt-deps/platform-tools/AdbWinApi.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp prebuilt-deps/platform-tools/AdbWinUsbApi.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
@ -115,11 +115,11 @@ dist-win64: build-server build-win64 build-win64-noconsole
|
||||
cp "$(SERVER_BUILD_DIR)"/server/scrcpy-server "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp "$(WIN64_BUILD_DIR)"/app/scrcpy.exe "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp "$(WIN64_NOCONSOLE_BUILD_DIR)"/app/scrcpy.exe "$(DIST)/$(WIN64_TARGET_DIR)/scrcpy-noconsole.exe"
|
||||
cp prebuilt-deps/ffmpeg-4.2.2-win64-shared/bin/avutil-56.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp prebuilt-deps/ffmpeg-4.2.2-win64-shared/bin/avcodec-58.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp prebuilt-deps/ffmpeg-4.2.2-win64-shared/bin/avformat-58.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp prebuilt-deps/ffmpeg-4.2.2-win64-shared/bin/swresample-3.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp prebuilt-deps/ffmpeg-4.2.2-win64-shared/bin/swscale-5.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp prebuilt-deps/ffmpeg-4.3.1-win64-shared/bin/avutil-56.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp prebuilt-deps/ffmpeg-4.3.1-win64-shared/bin/avcodec-58.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp prebuilt-deps/ffmpeg-4.3.1-win64-shared/bin/avformat-58.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp prebuilt-deps/ffmpeg-4.3.1-win64-shared/bin/swresample-3.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp prebuilt-deps/ffmpeg-4.3.1-win64-shared/bin/swscale-5.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp prebuilt-deps/platform-tools/adb.exe "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp prebuilt-deps/platform-tools/AdbWinApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp prebuilt-deps/platform-tools/AdbWinUsbApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
|
154
README.md
154
README.md
@ -1,4 +1,4 @@
|
||||
# scrcpy (v1.14)
|
||||
# scrcpy (v1.15)
|
||||
|
||||
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.
|
||||
@ -49,6 +49,11 @@ A [Snap] package is available: [`scrcpy`][snap-link].
|
||||
|
||||
[snap]: https://en.wikipedia.org/wiki/Snappy_(package_manager)
|
||||
|
||||
For Fedora, a [COPR] package is available: [`scrcpy`][copr-link].
|
||||
|
||||
[COPR]: https://fedoraproject.org/wiki/Category:Copr
|
||||
[copr-link]: https://copr.fedorainfracloud.org/coprs/zeno/scrcpy/
|
||||
|
||||
For Arch Linux, an [AUR] package is available: [`scrcpy`][aur-link].
|
||||
|
||||
[AUR]: https://wiki.archlinux.org/index.php/Arch_User_Repository
|
||||
@ -69,10 +74,10 @@ hard).
|
||||
For Windows, for simplicity, a prebuilt archive with all the dependencies
|
||||
(including `adb`) is available:
|
||||
|
||||
- [`scrcpy-win64-v1.14.zip`][direct-win64]
|
||||
_(SHA-256: 2be9139e46e29cf2f5f695848bb2b75a543b8f38be1133257dc5068252abc25f)_
|
||||
- [`scrcpy-win64-v1.15.zip`][direct-win64]
|
||||
_(SHA-256: dd514bb591e63ef4cd52a53c30f1153a28f59722d64690eb07bd017849edcba2)_
|
||||
|
||||
[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v1.14/scrcpy-win64-v1.14.zip
|
||||
[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v1.15/scrcpy-win64-v1.15.zip
|
||||
|
||||
It is also available in [Chocolatey]:
|
||||
|
||||
@ -301,7 +306,7 @@ ssh -CN -L5037:localhost:5037 -L27183:localhost:27183 your_remote_computer
|
||||
From another terminal:
|
||||
|
||||
```bash
|
||||
scrcpy --force-adb-forwrad
|
||||
scrcpy --force-adb-forward
|
||||
```
|
||||
|
||||
|
||||
@ -354,7 +359,7 @@ scrcpy --fullscreen
|
||||
scrcpy -f # short version
|
||||
```
|
||||
|
||||
Fullscreen can then be toggled dynamically with `MOD`+`f`.
|
||||
Fullscreen can then be toggled dynamically with <kbd>MOD</kbd>+<kbd>f</kbd>.
|
||||
|
||||
#### Rotation
|
||||
|
||||
@ -370,18 +375,19 @@ Possibles values are:
|
||||
- `2`: 180 degrees
|
||||
- `3`: 90 degrees clockwise
|
||||
|
||||
The rotation can also be changed dynamically with `MOD`+`←` _(left)_ and
|
||||
`MOD`+`→` _(right)_.
|
||||
The rotation can also be changed dynamically with <kbd>MOD</kbd>+<kbd>←</kbd>
|
||||
_(left)_ and <kbd>MOD</kbd>+<kbd>→</kbd> _(right)_.
|
||||
|
||||
Note that _scrcpy_ manages 3 different rotations:
|
||||
- `MOD`+`r` requests the device to switch between portrait and landscape (the
|
||||
current running app may refuse, if it does support the requested
|
||||
orientation).
|
||||
- <kbd>MOD</kbd>+<kbd>r</kbd> requests the device to switch between portrait and
|
||||
landscape (the current running app may refuse, if it does support the
|
||||
requested orientation).
|
||||
- `--lock-video-orientation` changes the mirroring orientation (the orientation
|
||||
of the video sent from the device to the computer). This affects the
|
||||
recording.
|
||||
- `--rotation` (or `MOD`+`←`/`MOD`+`→`) rotates only the window content. This
|
||||
affects only the display, not the recording.
|
||||
- `--rotation` (or <kbd>MOD</kbd>+<kbd>←</kbd>/<kbd>MOD</kbd>+<kbd>→</kbd>)
|
||||
rotates only the window content. This affects only the display, not the
|
||||
recording.
|
||||
|
||||
|
||||
### Other mirroring options
|
||||
@ -437,11 +443,16 @@ scrcpy --turn-screen-off
|
||||
scrcpy -S
|
||||
```
|
||||
|
||||
Or by pressing `MOD`+`o` at any time.
|
||||
Or by pressing <kbd>MOD</kbd>+<kbd>o</kbd> at any time.
|
||||
|
||||
To turn it back on, press `MOD`+`Shift`+`o` (or `POWER`, `MOD`+`p`).
|
||||
To turn it back on, press <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>o</kbd>.
|
||||
|
||||
It can be useful to also prevent the device to sleep:
|
||||
On Android, the `POWER` button always turns the screen on. For convenience, if
|
||||
`POWER` is sent via scrcpy (via right-click or <kbd>Ctrl</kbd>+<kbd>p</kbd>), it
|
||||
will force to turn the screen off after a small delay (on a best effort basis).
|
||||
The physical `POWER` button will still cause the screen to be turned on.
|
||||
|
||||
It can also be useful to prevent the device from sleeping:
|
||||
|
||||
```bash
|
||||
scrcpy --turn-screen-off --stay-awake
|
||||
@ -494,7 +505,8 @@ scrcpy --disable-screensaver
|
||||
|
||||
#### Rotate device screen
|
||||
|
||||
Press `MOD`+`r` to switch between portrait and landscape modes.
|
||||
Press <kbd>MOD</kbd>+<kbd>r</kbd> to switch between portrait and landscape
|
||||
modes.
|
||||
|
||||
Note that it rotates only if the application in foreground supports the
|
||||
requested orientation.
|
||||
@ -504,32 +516,34 @@ requested orientation.
|
||||
Any time the Android clipboard changes, it is automatically synchronized to the
|
||||
computer clipboard.
|
||||
|
||||
Any `Ctrl` shortcut is forwarded to the device. In particular:
|
||||
- `Ctrl`+`c` typically copies
|
||||
- `Ctrl`+`x` typically cuts
|
||||
- `Ctrl`+`v` typically pastes (after computer-to-device clipboard
|
||||
synchronization)
|
||||
Any <kbd>Ctrl</kbd> shortcut is forwarded to the device. In particular:
|
||||
- <kbd>Ctrl</kbd>+<kbd>c</kbd> typically copies
|
||||
- <kbd>Ctrl</kbd>+<kbd>x</kbd> typically cuts
|
||||
- <kbd>Ctrl</kbd>+<kbd>v</kbd> typically pastes (after computer-to-device
|
||||
clipboard synchronization)
|
||||
|
||||
This typically works as you expect.
|
||||
|
||||
The actual behavior depends on the active application though. For example,
|
||||
_Termux_ sends SIGINT on `Ctrl`+`c` instead, and _K-9 Mail_ composes a new
|
||||
message.
|
||||
_Termux_ sends SIGINT on <kbd>Ctrl</kbd>+<kbd>c</kbd> instead, and _K-9 Mail_
|
||||
composes a new message.
|
||||
|
||||
To copy, cut and paste in such cases (but only supported on Android >= 7):
|
||||
- `MOD`+`c` injects `COPY`
|
||||
- `MOD`+`x` injects `CUT`
|
||||
- `MOD`+`v` injects `PASTE` (after computer-to-device clipboard
|
||||
synchronization)
|
||||
- <kbd>MOD</kbd>+<kbd>c</kbd> injects `COPY`
|
||||
- <kbd>MOD</kbd>+<kbd>x</kbd> injects `CUT`
|
||||
- <kbd>MOD</kbd>+<kbd>v</kbd> injects `PASTE` (after computer-to-device
|
||||
clipboard synchronization)
|
||||
|
||||
In addition, `MOD`+`Shift`+`v` allows to inject the computer clipboard text as a
|
||||
sequence of key events. This is useful when the component does not accept text
|
||||
pasting (for example in _Termux_), but it can break non-ASCII content.
|
||||
In addition, <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>v</kbd> allows to inject the
|
||||
computer clipboard text as a sequence of key events. This is useful when the
|
||||
component does not accept text pasting (for example in _Termux_), but it can
|
||||
break non-ASCII content.
|
||||
|
||||
**WARNING:** Pasting the computer clipboard to the device (either via `Ctrl`+`v`
|
||||
or `MOD`+`v`) copies the content into the device clipboard. As a consequence,
|
||||
any Android application could read its content. You should avoid to paste
|
||||
sensitive content (like passwords) that way.
|
||||
**WARNING:** Pasting the computer clipboard to the device (either via
|
||||
<kbd>Ctrl</kbd>+<kbd>v</kbd> or <kbd>MOD</kbd>+<kbd>v</kbd>) copies the content
|
||||
into the device clipboard. As a consequence, any Android application could read
|
||||
its content. You should avoid to paste sensitive content (like passwords) that
|
||||
way.
|
||||
|
||||
#### Text injection preference
|
||||
|
||||
@ -553,6 +567,18 @@ scrcpy --prefer-text
|
||||
[prefertext]: https://github.com/Genymobile/scrcpy/issues/650#issuecomment-512945343
|
||||
|
||||
|
||||
#### Key repeat
|
||||
|
||||
By default, holding a key down generates repeated key events. This can cause
|
||||
performance problems in some games, where these events are useless anyway.
|
||||
|
||||
To avoid forwarding repeated key events:
|
||||
|
||||
```bash
|
||||
scrcpy --no-key-repeat
|
||||
```
|
||||
|
||||
|
||||
### File drop
|
||||
|
||||
#### Install APK
|
||||
@ -579,18 +605,18 @@ scrcpy --push-target /sdcard/foo/bar/
|
||||
|
||||
### Audio forwarding
|
||||
|
||||
Audio is not forwarded by _scrcpy_. Use [USBaudio] (Linux-only).
|
||||
Audio is not forwarded by _scrcpy_. Use [sndcpy].
|
||||
|
||||
Also see [issue #14].
|
||||
|
||||
[USBaudio]: https://github.com/rom1v/usbaudio
|
||||
[sndcpy]: https://github.com/rom1v/sndcpy
|
||||
[issue #14]: https://github.com/Genymobile/scrcpy/issues/14
|
||||
|
||||
|
||||
## Shortcuts
|
||||
|
||||
In the following list, `MOD` is the shortcut modifier. By default, it's (left)
|
||||
`Alt` or (left) `Super`.
|
||||
In the following list, <kbd>MOD</kbd> is the shortcut modifier. By default, it's
|
||||
(left) <kbd>Alt</kbd> or (left) <kbd>Super</kbd>.
|
||||
|
||||
It can be changed using `--shortcut-mod`. Possible keys are `lctrl`, `rctrl`,
|
||||
`lalt`, `ralt`, `lsuper` and `rsuper`. For example:
|
||||
@ -603,42 +629,42 @@ scrcpy --shortcut-mod=rctrl
|
||||
scrcpy --shortcut-mod=lctrl+lalt,lsuper
|
||||
```
|
||||
|
||||
_[Super] is typically the "Windows" or "Cmd" key._
|
||||
_<kbd>[Super]</kbd> is typically the <kbd>Windows</kbd> or <kbd>Cmd</kbd> key._
|
||||
|
||||
[Super]: https://en.wikipedia.org/wiki/Super_key_(keyboard_button)
|
||||
|
||||
| Action | Shortcut
|
||||
| ------------------------------------------- |:-----------------------------
|
||||
| Switch fullscreen mode | `MOD`+`f`
|
||||
| Rotate display left | `MOD`+`←` _(left)_
|
||||
| Rotate display right | `MOD`+`→` _(right)_
|
||||
| Resize window to 1:1 (pixel-perfect) | `MOD`+`g`
|
||||
| Resize window to remove black borders | `MOD`+`w` \| _Double-click¹_
|
||||
| Click on `HOME` | `MOD`+`h` \| _Middle-click_
|
||||
| Click on `BACK` | `MOD`+`b` \| _Right-click²_
|
||||
| Click on `APP_SWITCH` | `MOD`+`s`
|
||||
| Click on `MENU` | `MOD`+`m`
|
||||
| Click on `VOLUME_UP` | `MOD`+`↑` _(up)_
|
||||
| Click on `VOLUME_DOWN` | `MOD`+`↓` _(down)_
|
||||
| Click on `POWER` | `MOD`+`p`
|
||||
| Switch fullscreen mode | <kbd>MOD</kbd>+<kbd>f</kbd>
|
||||
| Rotate display left | <kbd>MOD</kbd>+<kbd>←</kbd> _(left)_
|
||||
| Rotate display right | <kbd>MOD</kbd>+<kbd>→</kbd> _(right)_
|
||||
| Resize window to 1:1 (pixel-perfect) | <kbd>MOD</kbd>+<kbd>g</kbd>
|
||||
| Resize window to remove black borders | <kbd>MOD</kbd>+<kbd>w</kbd> \| _Double-click¹_
|
||||
| Click on `HOME` | <kbd>MOD</kbd>+<kbd>h</kbd> \| _Middle-click_
|
||||
| Click on `BACK` | <kbd>MOD</kbd>+<kbd>b</kbd> \| _Right-click²_
|
||||
| Click on `APP_SWITCH` | <kbd>MOD</kbd>+<kbd>s</kbd>
|
||||
| Click on `MENU` (unlock screen) | <kbd>MOD</kbd>+<kbd>m</kbd>
|
||||
| Click on `VOLUME_UP` | <kbd>MOD</kbd>+<kbd>↑</kbd> _(up)_
|
||||
| Click on `VOLUME_DOWN` | <kbd>MOD</kbd>+<kbd>↓</kbd> _(down)_
|
||||
| Click on `POWER` | <kbd>MOD</kbd>+<kbd>p</kbd>
|
||||
| Power on | _Right-click²_
|
||||
| Turn device screen off (keep mirroring) | `MOD`+`o`
|
||||
| Turn device screen on | `MOD`+`Shift`+`o`
|
||||
| Rotate device screen | `MOD`+`r`
|
||||
| Expand notification panel | `MOD`+`n`
|
||||
| Collapse notification panel | `MOD`+`Shift`+`n`
|
||||
| Copy to clipboard³ | `MOD`+`c`
|
||||
| Cut to clipboard³ | `MOD`+`x`
|
||||
| Synchronize clipboards and paste³ | `MOD`+`v`
|
||||
| Inject computer clipboard text | `MOD`+`Shift`+`v`
|
||||
| Enable/disable FPS counter (on stdout) | `MOD`+`i`
|
||||
| Turn device screen off (keep mirroring) | <kbd>MOD</kbd>+<kbd>o</kbd>
|
||||
| Turn device screen on | <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>o</kbd>
|
||||
| Rotate device screen | <kbd>MOD</kbd>+<kbd>r</kbd>
|
||||
| Expand notification panel | <kbd>MOD</kbd>+<kbd>n</kbd>
|
||||
| Collapse notification panel | <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>n</kbd>
|
||||
| Copy to clipboard³ | <kbd>MOD</kbd>+<kbd>c</kbd>
|
||||
| Cut to clipboard³ | <kbd>MOD</kbd>+<kbd>x</kbd>
|
||||
| Synchronize clipboards and paste³ | <kbd>MOD</kbd>+<kbd>v</kbd>
|
||||
| Inject computer clipboard text | <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>v</kbd>
|
||||
| Enable/disable FPS counter (on stdout) | <kbd>MOD</kbd>+<kbd>i</kbd>
|
||||
|
||||
_¹Double-click on black borders to remove them._
|
||||
_²Right-click turns the screen on if it was off, presses BACK otherwise._
|
||||
_³Only on Android >= 7._
|
||||
|
||||
All `Ctrl`+_key_ shortcuts are forwarded to the device, so they are handled by
|
||||
the active application.
|
||||
All <kbd>Ctrl</kbd>+_key_ shortcuts are forwarded to the device, so they are
|
||||
handled by the active application.
|
||||
|
||||
|
||||
## Custom paths
|
||||
|
@ -96,6 +96,10 @@ Do not display device (only when screen recording is enabled).
|
||||
.B \-\-no\-mipmaps
|
||||
If the renderer is OpenGL 3.0+ or OpenGL ES 2.0+, then mipmaps are automatically generated to improve downscaling quality. This option disables the generation of mipmaps.
|
||||
|
||||
.TP
|
||||
.B \-\-no\-key\-repeat
|
||||
Do not forward repeated key events when a key is held down.
|
||||
|
||||
.TP
|
||||
.BI "\-p, \-\-port " port[:port]
|
||||
Set the TCP port (range) used by the client to listen.
|
||||
|
@ -92,6 +92,9 @@ scrcpy_print_usage(const char *arg0) {
|
||||
" mipmaps are automatically generated to improve downscaling\n"
|
||||
" quality. This option disables the generation of mipmaps.\n"
|
||||
"\n"
|
||||
" --no-key-repeat\n"
|
||||
" Do not forward repeated key events when a key is held down.\n"
|
||||
"\n"
|
||||
" -p, --port port[:port]\n"
|
||||
" Set the TCP port (range) used by the client to listen.\n"
|
||||
" Default is %d:%d.\n"
|
||||
@ -642,6 +645,7 @@ guess_record_format(const char *filename) {
|
||||
#define OPT_FORCE_ADB_FORWARD 1019
|
||||
#define OPT_DISABLE_SCREENSAVER 1020
|
||||
#define OPT_SHORTCUT_MOD 1021
|
||||
#define OPT_NO_KEY_REPEAT 1022
|
||||
|
||||
bool
|
||||
scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
|
||||
@ -664,6 +668,7 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
|
||||
{"no-control", no_argument, NULL, 'n'},
|
||||
{"no-display", no_argument, NULL, 'N'},
|
||||
{"no-mipmaps", no_argument, NULL, OPT_NO_MIPMAPS},
|
||||
{"no-key-repeat", no_argument, NULL, OPT_NO_KEY_REPEAT},
|
||||
{"port", required_argument, NULL, 'p'},
|
||||
{"prefer-text", no_argument, NULL, OPT_PREFER_TEXT},
|
||||
{"push-target", required_argument, NULL, OPT_PUSH_TARGET},
|
||||
@ -829,6 +834,9 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
|
||||
case OPT_NO_MIPMAPS:
|
||||
opts->mipmaps = false;
|
||||
break;
|
||||
case OPT_NO_KEY_REPEAT:
|
||||
opts->forward_key_repeat = false;
|
||||
break;
|
||||
case OPT_CODEC_OPTIONS:
|
||||
opts->codec_options = optarg;
|
||||
break;
|
||||
|
@ -54,11 +54,14 @@ is_shortcut_mod(struct input_manager *im, uint16_t sdl_mod) {
|
||||
}
|
||||
|
||||
void
|
||||
input_manager_init(struct input_manager *im, bool prefer_text,
|
||||
const struct sc_shortcut_mods *shortcut_mods)
|
||||
input_manager_init(struct input_manager *im,
|
||||
const struct scrcpy_options *options)
|
||||
{
|
||||
im->prefer_text = prefer_text;
|
||||
im->control = options->control;
|
||||
im->forward_key_repeat = options->forward_key_repeat;
|
||||
im->prefer_text = options->prefer_text;
|
||||
|
||||
const struct sc_shortcut_mods *shortcut_mods = &options->shortcut_mods;
|
||||
assert(shortcut_mods->count);
|
||||
assert(shortcut_mods->count < SC_MAX_SHORTCUT_MODS);
|
||||
for (unsigned i = 0; i < shortcut_mods->count; ++i) {
|
||||
@ -77,6 +80,7 @@ send_keycode(struct controller *controller, enum android_keycode keycode,
|
||||
msg.type = CONTROL_MSG_TYPE_INJECT_KEYCODE;
|
||||
msg.inject_keycode.keycode = keycode;
|
||||
msg.inject_keycode.metastate = 0;
|
||||
msg.inject_keycode.repeat = 0;
|
||||
|
||||
if (actions & ACTION_DOWN) {
|
||||
msg.inject_keycode.action = AKEY_EVENT_ACTION_DOWN;
|
||||
@ -318,9 +322,9 @@ convert_input_key(const SDL_KeyboardEvent *from, struct control_msg *to,
|
||||
|
||||
void
|
||||
input_manager_process_key(struct input_manager *im,
|
||||
const SDL_KeyboardEvent *event,
|
||||
bool control) {
|
||||
const SDL_KeyboardEvent *event) {
|
||||
// control: indicates the state of the command-line option --no-control
|
||||
bool control = im->control;
|
||||
|
||||
bool smod = is_shortcut_mod(im, event->keysym.mod);
|
||||
|
||||
@ -459,6 +463,9 @@ input_manager_process_key(struct input_manager *im,
|
||||
}
|
||||
|
||||
if (event->repeat) {
|
||||
if (!im->forward_key_repeat) {
|
||||
return;
|
||||
}
|
||||
++im->repeat;
|
||||
} else {
|
||||
im->repeat = 0;
|
||||
@ -573,8 +580,9 @@ convert_mouse_button(const SDL_MouseButtonEvent *from, struct screen *screen,
|
||||
|
||||
void
|
||||
input_manager_process_mouse_button(struct input_manager *im,
|
||||
const SDL_MouseButtonEvent *event,
|
||||
bool control) {
|
||||
const SDL_MouseButtonEvent *event) {
|
||||
bool control = im->control;
|
||||
|
||||
if (event->which == SDL_TOUCH_MOUSEID) {
|
||||
// simulated from touch events, so it's a duplicate
|
||||
return;
|
||||
|
@ -22,6 +22,8 @@ struct input_manager {
|
||||
// number of repetitions. This variable keeps track of the count.
|
||||
unsigned repeat;
|
||||
|
||||
bool control;
|
||||
bool forward_key_repeat;
|
||||
bool prefer_text;
|
||||
|
||||
struct {
|
||||
@ -31,8 +33,8 @@ struct input_manager {
|
||||
};
|
||||
|
||||
void
|
||||
input_manager_init(struct input_manager *im, bool prefer_text,
|
||||
const struct sc_shortcut_mods *shortcut_mods);
|
||||
input_manager_init(struct input_manager *im,
|
||||
const struct scrcpy_options *options);
|
||||
|
||||
void
|
||||
input_manager_process_text_input(struct input_manager *im,
|
||||
@ -40,8 +42,7 @@ input_manager_process_text_input(struct input_manager *im,
|
||||
|
||||
void
|
||||
input_manager_process_key(struct input_manager *im,
|
||||
const SDL_KeyboardEvent *event,
|
||||
bool control);
|
||||
const SDL_KeyboardEvent *event);
|
||||
|
||||
void
|
||||
input_manager_process_mouse_motion(struct input_manager *im,
|
||||
@ -53,8 +54,7 @@ input_manager_process_touch(struct input_manager *im,
|
||||
|
||||
void
|
||||
input_manager_process_mouse_button(struct input_manager *im,
|
||||
const SDL_MouseButtonEvent *event,
|
||||
bool control);
|
||||
const SDL_MouseButtonEvent *event);
|
||||
|
||||
void
|
||||
input_manager_process_mouse_wheel(struct input_manager *im,
|
||||
|
@ -170,7 +170,7 @@ enum event_result {
|
||||
};
|
||||
|
||||
static enum event_result
|
||||
handle_event(SDL_Event *event, bool control) {
|
||||
handle_event(SDL_Event *event, const struct scrcpy_options *options) {
|
||||
switch (event->type) {
|
||||
case EVENT_STREAM_STOPPED:
|
||||
LOGD("Video stream stopped");
|
||||
@ -192,7 +192,7 @@ handle_event(SDL_Event *event, bool control) {
|
||||
screen_handle_window_event(&screen, &event->window);
|
||||
break;
|
||||
case SDL_TEXTINPUT:
|
||||
if (!control) {
|
||||
if (!options->control) {
|
||||
break;
|
||||
}
|
||||
input_manager_process_text_input(&input_manager, &event->text);
|
||||
@ -201,16 +201,16 @@ handle_event(SDL_Event *event, bool control) {
|
||||
case SDL_KEYUP:
|
||||
// some key events do not interact with the device, so process the
|
||||
// event even if control is disabled
|
||||
input_manager_process_key(&input_manager, &event->key, control);
|
||||
input_manager_process_key(&input_manager, &event->key);
|
||||
break;
|
||||
case SDL_MOUSEMOTION:
|
||||
if (!control) {
|
||||
if (!options->control) {
|
||||
break;
|
||||
}
|
||||
input_manager_process_mouse_motion(&input_manager, &event->motion);
|
||||
break;
|
||||
case SDL_MOUSEWHEEL:
|
||||
if (!control) {
|
||||
if (!options->control) {
|
||||
break;
|
||||
}
|
||||
input_manager_process_mouse_wheel(&input_manager, &event->wheel);
|
||||
@ -219,8 +219,7 @@ handle_event(SDL_Event *event, bool control) {
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
// some mouse events do not interact with the device, so process
|
||||
// the event even if control is disabled
|
||||
input_manager_process_mouse_button(&input_manager, &event->button,
|
||||
control);
|
||||
input_manager_process_mouse_button(&input_manager, &event->button);
|
||||
break;
|
||||
case SDL_FINGERMOTION:
|
||||
case SDL_FINGERDOWN:
|
||||
@ -228,7 +227,7 @@ handle_event(SDL_Event *event, bool control) {
|
||||
input_manager_process_touch(&input_manager, &event->tfinger);
|
||||
break;
|
||||
case SDL_DROPFILE: {
|
||||
if (!control) {
|
||||
if (!options->control) {
|
||||
break;
|
||||
}
|
||||
file_handler_action_t action;
|
||||
@ -245,16 +244,15 @@ handle_event(SDL_Event *event, bool control) {
|
||||
}
|
||||
|
||||
static bool
|
||||
event_loop(bool display, bool control) {
|
||||
(void) display;
|
||||
event_loop(const struct scrcpy_options *options) {
|
||||
#ifdef CONTINUOUS_RESIZING_WORKAROUND
|
||||
if (display) {
|
||||
if (options->display) {
|
||||
SDL_AddEventWatch(event_watcher, NULL);
|
||||
}
|
||||
#endif
|
||||
SDL_Event event;
|
||||
while (SDL_WaitEvent(&event)) {
|
||||
enum event_result result = handle_event(&event, control);
|
||||
enum event_result result = handle_event(&event, options);
|
||||
switch (result) {
|
||||
case EVENT_RESULT_STOPPED_BY_USER:
|
||||
return true;
|
||||
@ -443,10 +441,9 @@ scrcpy(const struct scrcpy_options *options) {
|
||||
}
|
||||
}
|
||||
|
||||
input_manager_init(&input_manager, options->prefer_text,
|
||||
&options->shortcut_mods);
|
||||
input_manager_init(&input_manager, options);
|
||||
|
||||
ret = event_loop(options->display, options->control);
|
||||
ret = event_loop(options);
|
||||
LOGD("quit...");
|
||||
|
||||
screen_destroy(&screen);
|
||||
|
@ -78,6 +78,7 @@ struct scrcpy_options {
|
||||
bool stay_awake;
|
||||
bool force_adb_forward;
|
||||
bool disable_screensaver;
|
||||
bool forward_key_repeat;
|
||||
};
|
||||
|
||||
#define SCRCPY_OPTIONS_DEFAULT { \
|
||||
@ -121,6 +122,7 @@ struct scrcpy_options {
|
||||
.stay_awake = false, \
|
||||
.force_adb_forward = false, \
|
||||
.disable_screensaver = false, \
|
||||
.forward_key_repeat = true, \
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -15,6 +15,6 @@ cpu = 'i686'
|
||||
endian = 'little'
|
||||
|
||||
[properties]
|
||||
prebuilt_ffmpeg_shared = 'ffmpeg-4.2.2-win32-shared'
|
||||
prebuilt_ffmpeg_dev = 'ffmpeg-4.2.2-win32-dev'
|
||||
prebuilt_ffmpeg_shared = 'ffmpeg-4.3.1-win32-shared'
|
||||
prebuilt_ffmpeg_dev = 'ffmpeg-4.3.1-win32-dev'
|
||||
prebuilt_sdl2 = 'SDL2-2.0.12/i686-w64-mingw32'
|
||||
|
@ -15,6 +15,6 @@ cpu = 'x86_64'
|
||||
endian = 'little'
|
||||
|
||||
[properties]
|
||||
prebuilt_ffmpeg_shared = 'ffmpeg-4.2.2-win64-shared'
|
||||
prebuilt_ffmpeg_dev = 'ffmpeg-4.2.2-win64-dev'
|
||||
prebuilt_ffmpeg_shared = 'ffmpeg-4.3.1-win64-shared'
|
||||
prebuilt_ffmpeg_dev = 'ffmpeg-4.3.1-win64-dev'
|
||||
prebuilt_sdl2 = 'SDL2-2.0.12/x86_64-w64-mingw32'
|
||||
|
@ -1,5 +1,5 @@
|
||||
project('scrcpy', 'c',
|
||||
version: '1.14',
|
||||
version: '1.15',
|
||||
meson_version: '>= 0.48',
|
||||
default_options: [
|
||||
'c_std=c11',
|
||||
|
@ -10,24 +10,24 @@ prepare-win32: prepare-sdl2 prepare-ffmpeg-shared-win32 prepare-ffmpeg-dev-win32
|
||||
prepare-win64: prepare-sdl2 prepare-ffmpeg-shared-win64 prepare-ffmpeg-dev-win64 prepare-adb
|
||||
|
||||
prepare-ffmpeg-shared-win32:
|
||||
@./prepare-dep https://ffmpeg.zeranoe.com/builds/win32/shared/ffmpeg-4.2.2-win32-shared.zip \
|
||||
ab5d603aaa54de360db2c2ffe378c82376b9343ea1175421dd644639aa07ee31 \
|
||||
ffmpeg-4.2.2-win32-shared
|
||||
@./prepare-dep https://ffmpeg.zeranoe.com/builds/win32/shared/ffmpeg-4.3.1-win32-shared.zip \
|
||||
357af9901a456f4dcbacd107e83a934d344c9cb07ddad8aaf80612eeab7d26d2 \
|
||||
ffmpeg-4.3.1-win32-shared
|
||||
|
||||
prepare-ffmpeg-dev-win32:
|
||||
@./prepare-dep https://ffmpeg.zeranoe.com/builds/win32/dev/ffmpeg-4.2.2-win32-dev.zip \
|
||||
8d224be567a2950cad4be86f4aabdd045bfa52ad758e87c72cedd278613bc6c8 \
|
||||
ffmpeg-4.2.2-win32-dev
|
||||
@./prepare-dep https://ffmpeg.zeranoe.com/builds/win32/dev/ffmpeg-4.3.1-win32-dev.zip \
|
||||
230efb08e9bcf225bd474da29676c70e591fc94d8790a740ca801408fddcb78b \
|
||||
ffmpeg-4.3.1-win32-dev
|
||||
|
||||
prepare-ffmpeg-shared-win64:
|
||||
@./prepare-dep https://ffmpeg.zeranoe.com/builds/win64/shared/ffmpeg-4.2.2-win64-shared.zip \
|
||||
5aedf268952b7d9f6541dbfcb47cd86a7e7881a3b7ba482fd3bc4ca33bda7bf5 \
|
||||
ffmpeg-4.2.2-win64-shared
|
||||
@./prepare-dep https://ffmpeg.zeranoe.com/builds/win64/shared/ffmpeg-4.3.1-win64-shared.zip \
|
||||
dd29b7f92f48dead4dd940492c7509138c0f99db445076d0a597007298a79940 \
|
||||
ffmpeg-4.3.1-win64-shared
|
||||
|
||||
prepare-ffmpeg-dev-win64:
|
||||
@./prepare-dep https://ffmpeg.zeranoe.com/builds/win64/dev/ffmpeg-4.2.2-win64-dev.zip \
|
||||
f4885f859c5b0d6663c2a0a4c1cf035b1c60b146402790b796bd3ad84f4f3ca2 \
|
||||
ffmpeg-4.2.2-win64-dev
|
||||
@./prepare-dep https://ffmpeg.zeranoe.com/builds/win64/dev/ffmpeg-4.3.1-win64-dev.zip \
|
||||
2e8038242cf8e1bd095c2978f196ff0462b122cc6ef7e74626a6af15459d8b81 \
|
||||
ffmpeg-4.3.1-win64-dev
|
||||
|
||||
prepare-sdl2:
|
||||
@./prepare-dep https://libsdl.org/release/SDL2-devel-2.0.12-mingw.tar.gz \
|
||||
@ -35,6 +35,6 @@ prepare-sdl2:
|
||||
SDL2-2.0.12
|
||||
|
||||
prepare-adb:
|
||||
@./prepare-dep https://dl.google.com/android/repository/platform-tools_r30.0.0-windows.zip \
|
||||
854305f9a702f5ea2c3de73edde402bd26afa0ee944c9b0c4380420f5a862e0d \
|
||||
@./prepare-dep https://dl.google.com/android/repository/platform-tools_r30.0.4-windows.zip \
|
||||
413182fff6c5957911e231b9e97e6be4fc6a539035e3dfb580b5c54bd5950fee \
|
||||
platform-tools
|
||||
|
@ -6,8 +6,8 @@ android {
|
||||
applicationId "com.genymobile.scrcpy"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 29
|
||||
versionCode 16
|
||||
versionName "1.14"
|
||||
versionCode 17
|
||||
versionName "1.15"
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
buildTypes {
|
||||
|
@ -12,7 +12,7 @@
|
||||
set -e
|
||||
|
||||
SCRCPY_DEBUG=false
|
||||
SCRCPY_VERSION_NAME=1.14
|
||||
SCRCPY_VERSION_NAME=1.15
|
||||
|
||||
PLATFORM=${ANDROID_PLATFORM:-29}
|
||||
BUILD_TOOLS=${ANDROID_BUILD_TOOLS:-29.0.2}
|
||||
@ -42,6 +42,8 @@ echo "Generating java from aidl..."
|
||||
cd "$SERVER_DIR/src/main/aidl"
|
||||
"$ANDROID_HOME/build-tools/$BUILD_TOOLS/aidl" -o"$CLASSES_DIR" \
|
||||
android/view/IRotationWatcher.aidl
|
||||
"$ANDROID_HOME/build-tools/$BUILD_TOOLS/aidl" -o"$CLASSES_DIR" \
|
||||
android/content/IOnPrimaryClipChangedListener.aidl
|
||||
|
||||
echo "Compiling java sources..."
|
||||
cd ../java
|
||||
@ -55,6 +57,7 @@ cd "$CLASSES_DIR"
|
||||
"$ANDROID_HOME/build-tools/$BUILD_TOOLS/dx" --dex \
|
||||
--output "$BUILD_DIR/classes.dex" \
|
||||
android/view/*.class \
|
||||
android/content/*.class \
|
||||
com/genymobile/scrcpy/*.class \
|
||||
com/genymobile/scrcpy/wrappers/*.class
|
||||
|
||||
|
@ -8,11 +8,16 @@ import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class Controller {
|
||||
|
||||
private static final int DEVICE_ID_VIRTUAL = -1;
|
||||
|
||||
private static final ScheduledExecutorService EXECUTOR = Executors.newSingleThreadScheduledExecutor();
|
||||
|
||||
private final Device device;
|
||||
private final DesktopConnection connection;
|
||||
private final DeviceMessageSender sender;
|
||||
@ -24,6 +29,8 @@ public class Controller {
|
||||
private final MotionEvent.PointerProperties[] pointerProperties = new MotionEvent.PointerProperties[PointersState.MAX_POINTERS];
|
||||
private final MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[PointersState.MAX_POINTERS];
|
||||
|
||||
private boolean keepPowerModeOff;
|
||||
|
||||
public Controller(Device device, DesktopConnection connection) {
|
||||
this.device = device;
|
||||
this.connection = connection;
|
||||
@ -117,6 +124,7 @@ public class Controller {
|
||||
int mode = msg.getAction();
|
||||
boolean setPowerModeOk = Device.setScreenPowerMode(mode);
|
||||
if (setPowerModeOk) {
|
||||
keepPowerModeOff = mode == Device.POWER_MODE_OFF;
|
||||
Ln.i("Device screen turned " + (mode == Device.POWER_MODE_OFF ? "off" : "on"));
|
||||
}
|
||||
}
|
||||
@ -130,6 +138,9 @@ public class Controller {
|
||||
}
|
||||
|
||||
private boolean injectKeycode(int action, int keycode, int repeat, int metaState) {
|
||||
if (keepPowerModeOff && action == KeyEvent.ACTION_UP && (keycode == KeyEvent.KEYCODE_POWER || keycode == KeyEvent.KEYCODE_WAKEUP)) {
|
||||
schedulePowerModeOff();
|
||||
}
|
||||
return device.injectKeyEvent(action, keycode, repeat, metaState);
|
||||
}
|
||||
|
||||
@ -223,8 +234,24 @@ public class Controller {
|
||||
return device.injectEvent(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule a call to set power mode to off after a small delay.
|
||||
*/
|
||||
private static void schedulePowerModeOff() {
|
||||
EXECUTOR.schedule(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Ln.i("Forcing screen off");
|
||||
Device.setScreenPowerMode(Device.POWER_MODE_OFF);
|
||||
}
|
||||
}, 200, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
private boolean pressBackOrTurnScreenOn() {
|
||||
int keycode = device.isScreenOn() ? KeyEvent.KEYCODE_BACK : KeyEvent.KEYCODE_WAKEUP;
|
||||
if (keepPowerModeOff && keycode == KeyEvent.KEYCODE_WAKEUP) {
|
||||
schedulePowerModeOff();
|
||||
}
|
||||
return device.injectKeycode(keycode);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user