Compare commits

...

18 Commits

Author SHA1 Message Date
8fb01922a3 fixrepeat 2020-08-07 09:21:13 +02:00
521f2fe994 Update links to v1.15 in README and BUILD 2020-08-06 21:56:06 +02:00
edc4f7675f Bump version to 1.15 2020-08-06 21:00:48 +02:00
712f1fa6b2 Upgrade FFmpeg (4.3.1) for Windows
Include the latest version of FFmpeg in Windows releases.
2020-08-06 21:00:48 +02:00
1ba06037f8 Upgrade platform-tools (30.0.4) for Windows
Include the latest version of adb in Windows releases.
2020-08-06 21:00:48 +02:00
da63e3774b Merge branch 'master' into dev 2020-08-06 21:00:15 +02:00
cf9d44979c Keep the screen off on powering on
PR #1577 <https://github.com/Genymobile/scrcpy/pull/1577>
Fixes #1573 <https://github.com/Genymobile/scrcpy/issues/1573>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2020-08-03 19:51:58 +02:00
84f1d9e375 Add --no-key-repeat cli option
Add an option to avoid forwarding repeated key events.

PR #1623 <https://github.com/Genymobile/scrcpy/pull/1623>
Refs #1013 <https://github.com/Genymobile/scrcpy/issues/1013>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2020-08-03 19:51:55 +02:00
65d06a3663 Pass full options struct to static functions
This avoids to pass specific options values individually. Since these
function are static (internal to the file), this is not a problem to
make them depend on scrcpy_options.

Refs #1623 <https://github.com/Genymobile/scrcpy/pull/1623>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2020-08-02 18:20:14 +02:00
74079ea5e4 Copy the options used in input manager init
This avoids to pass additional options to some input manager functions.

Refs #1623 <https://github.com/Genymobile/scrcpy/pull/1623>
2020-08-02 18:19:41 +02:00
0870d8620f Mention that MENU unlocks screen
Pressing MENU while in lock screen unlocks (it still asks for the schema
or code if enabled).
2020-08-01 17:10:09 +02:00
d49cffb938 Use <kbd> HTML tag for keys
It's prettyier in a browser.
2020-08-01 17:04:16 +02:00
5086e7b744 Update BUILD.md
Update the macOS section.

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

Signed-off-by: Romain Vimont <rom@rom1v.com>
2020-07-07 20:50:09 +02:00
e99b896ae2 README: Add Fedora install instructions
PR #1549 <https://github.com/Genymobile/scrcpy/pull/1549>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2020-06-27 15:51:44 +02:00
8c27f59aa5 Improve linguistic
PR #1543 <https://github.com/Genymobile/scrcpy/pull/1543>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2020-06-25 22:08:02 +02:00
42641d2737 Fix typo in README.md
PR #1523 <https://github.com/Genymobile/scrcpy/pull/1523>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2020-06-18 21:13:35 +02:00
3c0fc8f54f Mention sndcpy 2020-06-09 22:09:23 +02:00
1b73eff3c9 Add missing file in build_without_gradle.sh
Fixes #1481 <https://github.com/Genymobile/scrcpy/issues/1481>
PR #1482 <https://github.com/Genymobile/scrcpy/pull/1482>

Signed-off-by: Louis Leseur <louis.leseur@gmail.com>
Signed-off-by: Romain Vimont <rom@rom1v.com>
2020-06-07 22:00:35 +02:00
16 changed files with 211 additions and 131 deletions

View File

@ -176,8 +176,8 @@ Additionally, if you want to build the server, install Java 8 from Caskroom, and
make it avaliable from the `PATH`: make it avaliable from the `PATH`:
```bash ```bash
brew tap caskroom/versions brew tap homebrew/cask-versions
brew cask install java8 brew cask install adoptopenjdk/openjdk/adoptopenjdk8
export JAVA_HOME="$(/usr/libexec/java_home --version 1.8)" export JAVA_HOME="$(/usr/libexec/java_home --version 1.8)"
export PATH="$JAVA_HOME/bin:$PATH" export PATH="$JAVA_HOME/bin:$PATH"
``` ```
@ -190,12 +190,17 @@ See [pierlon/scrcpy-docker](https://github.com/pierlon/scrcpy-docker).
## Common steps ## Common steps
If you want to build the server, install the [Android SDK] (_Android Studio_), 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 [Android SDK]: https://developer.android.com/studio/index.html
```bash ```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]. 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 ## Prebuilt server
- [`scrcpy-server-v1.14`][direct-scrcpy-server] - [`scrcpy-server-v1.15`][direct-scrcpy-server]
_(SHA-256: 1d1b18a2b80e956771fd63b99b414d2d028713a8f12ddfa5a369709ad4295620)_ _(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 Download the prebuilt server somewhere, and specify its path during the Meson
configuration: configuration:

View File

@ -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 "$(SERVER_BUILD_DIR)"/server/scrcpy-server "$(DIST)/$(WIN32_TARGET_DIR)/"
cp "$(WIN32_BUILD_DIR)"/app/scrcpy.exe "$(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 "$(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.3.1-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.3.1-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.3.1-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.3.1-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/swscale-5.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp prebuilt-deps/platform-tools/adb.exe "$(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/AdbWinApi.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp prebuilt-deps/platform-tools/AdbWinUsbApi.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 "$(SERVER_BUILD_DIR)"/server/scrcpy-server "$(DIST)/$(WIN64_TARGET_DIR)/"
cp "$(WIN64_BUILD_DIR)"/app/scrcpy.exe "$(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 "$(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.3.1-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.3.1-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.3.1-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.3.1-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/swscale-5.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp prebuilt-deps/platform-tools/adb.exe "$(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/AdbWinApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp prebuilt-deps/platform-tools/AdbWinUsbApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/" cp prebuilt-deps/platform-tools/AdbWinUsbApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/"

154
README.md
View File

@ -1,4 +1,4 @@
# scrcpy (v1.14) # scrcpy (v1.15)
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.
@ -49,6 +49,11 @@ A [Snap] package is available: [`scrcpy`][snap-link].
[snap]: https://en.wikipedia.org/wiki/Snappy_(package_manager) [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]. For Arch Linux, an [AUR] package is available: [`scrcpy`][aur-link].
[AUR]: https://wiki.archlinux.org/index.php/Arch_User_Repository [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 For Windows, for simplicity, a prebuilt archive with all the dependencies
(including `adb`) is available: (including `adb`) is available:
- [`scrcpy-win64-v1.14.zip`][direct-win64] - [`scrcpy-win64-v1.15.zip`][direct-win64]
_(SHA-256: 2be9139e46e29cf2f5f695848bb2b75a543b8f38be1133257dc5068252abc25f)_ _(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]: It is also available in [Chocolatey]:
@ -301,7 +306,7 @@ ssh -CN -L5037:localhost:5037 -L27183:localhost:27183 your_remote_computer
From another terminal: From another terminal:
```bash ```bash
scrcpy --force-adb-forwrad scrcpy --force-adb-forward
``` ```
@ -354,7 +359,7 @@ scrcpy --fullscreen
scrcpy -f # short version 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 #### Rotation
@ -370,18 +375,19 @@ Possibles values are:
- `2`: 180 degrees - `2`: 180 degrees
- `3`: 90 degrees clockwise - `3`: 90 degrees clockwise
The rotation can also be changed dynamically with `MOD`+`←` _(left)_ and The rotation can also be changed dynamically with <kbd>MOD</kbd>+<kbd></kbd>
`MOD`+`→` _(right)_. _(left)_ and <kbd>MOD</kbd>+<kbd></kbd> _(right)_.
Note that _scrcpy_ manages 3 different rotations: Note that _scrcpy_ manages 3 different rotations:
- `MOD`+`r` requests the device to switch between portrait and landscape (the - <kbd>MOD</kbd>+<kbd>r</kbd> requests the device to switch between portrait and
current running app may refuse, if it does support the requested landscape (the current running app may refuse, if it does support the
orientation). requested orientation).
- `--lock-video-orientation` changes the mirroring orientation (the orientation - `--lock-video-orientation` changes the mirroring orientation (the orientation
of the video sent from the device to the computer). This affects the of the video sent from the device to the computer). This affects the
recording. recording.
- `--rotation` (or `MOD`+`←`/`MOD`+`→`) rotates only the window content. This - `--rotation` (or <kbd>MOD</kbd>+<kbd></kbd>/<kbd>MOD</kbd>+<kbd></kbd>)
affects only the display, not the recording. rotates only the window content. This affects only the display, not the
recording.
### Other mirroring options ### Other mirroring options
@ -437,11 +443,16 @@ scrcpy --turn-screen-off
scrcpy -S 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 ```bash
scrcpy --turn-screen-off --stay-awake scrcpy --turn-screen-off --stay-awake
@ -494,7 +505,8 @@ scrcpy --disable-screensaver
#### Rotate device screen #### 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 Note that it rotates only if the application in foreground supports the
requested orientation. requested orientation.
@ -504,32 +516,34 @@ requested orientation.
Any time the Android clipboard changes, it is automatically synchronized to the Any time the Android clipboard changes, it is automatically synchronized to the
computer clipboard. computer clipboard.
Any `Ctrl` shortcut is forwarded to the device. In particular: Any <kbd>Ctrl</kbd> shortcut is forwarded to the device. In particular:
- `Ctrl`+`c` typically copies - <kbd>Ctrl</kbd>+<kbd>c</kbd> typically copies
- `Ctrl`+`x` typically cuts - <kbd>Ctrl</kbd>+<kbd>x</kbd> typically cuts
- `Ctrl`+`v` typically pastes (after computer-to-device clipboard - <kbd>Ctrl</kbd>+<kbd>v</kbd> typically pastes (after computer-to-device
synchronization) clipboard synchronization)
This typically works as you expect. This typically works as you expect.
The actual behavior depends on the active application though. For example, 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 _Termux_ sends SIGINT on <kbd>Ctrl</kbd>+<kbd>c</kbd> instead, and _K-9 Mail_
message. composes a new message.
To copy, cut and paste in such cases (but only supported on Android >= 7): To copy, cut and paste in such cases (but only supported on Android >= 7):
- `MOD`+`c` injects `COPY` - <kbd>MOD</kbd>+<kbd>c</kbd> injects `COPY`
- `MOD`+`x` injects `CUT` - <kbd>MOD</kbd>+<kbd>x</kbd> injects `CUT`
- `MOD`+`v` injects `PASTE` (after computer-to-device clipboard - <kbd>MOD</kbd>+<kbd>v</kbd> injects `PASTE` (after computer-to-device
synchronization) clipboard synchronization)
In addition, `MOD`+`Shift`+`v` allows to inject the computer clipboard text as a In addition, <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>v</kbd> allows to inject the
sequence of key events. This is useful when the component does not accept text computer clipboard text as a sequence of key events. This is useful when the
pasting (for example in _Termux_), but it can break non-ASCII content. 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` **WARNING:** Pasting the computer clipboard to the device (either via
or `MOD`+`v`) copies the content into the device clipboard. As a consequence, <kbd>Ctrl</kbd>+<kbd>v</kbd> or <kbd>MOD</kbd>+<kbd>v</kbd>) copies the content
any Android application could read its content. You should avoid to paste into the device clipboard. As a consequence, any Android application could read
sensitive content (like passwords) that way. its content. You should avoid to paste sensitive content (like passwords) that
way.
#### Text injection preference #### Text injection preference
@ -553,6 +567,18 @@ scrcpy --prefer-text
[prefertext]: https://github.com/Genymobile/scrcpy/issues/650#issuecomment-512945343 [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 ### File drop
#### Install APK #### Install APK
@ -579,18 +605,18 @@ scrcpy --push-target /sdcard/foo/bar/
### Audio forwarding ### Audio forwarding
Audio is not forwarded by _scrcpy_. Use [USBaudio] (Linux-only). Audio is not forwarded by _scrcpy_. Use [sndcpy].
Also see [issue #14]. 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 [issue #14]: https://github.com/Genymobile/scrcpy/issues/14
## Shortcuts ## Shortcuts
In the following list, `MOD` is the shortcut modifier. By default, it's (left) In the following list, <kbd>MOD</kbd> is the shortcut modifier. By default, it's
`Alt` or (left) `Super`. (left) <kbd>Alt</kbd> or (left) <kbd>Super</kbd>.
It can be changed using `--shortcut-mod`. Possible keys are `lctrl`, `rctrl`, It can be changed using `--shortcut-mod`. Possible keys are `lctrl`, `rctrl`,
`lalt`, `ralt`, `lsuper` and `rsuper`. For example: `lalt`, `ralt`, `lsuper` and `rsuper`. For example:
@ -603,42 +629,42 @@ scrcpy --shortcut-mod=rctrl
scrcpy --shortcut-mod=lctrl+lalt,lsuper 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) [Super]: https://en.wikipedia.org/wiki/Super_key_(keyboard_button)
| Action | Shortcut | Action | Shortcut
| ------------------------------------------- |:----------------------------- | ------------------------------------------- |:-----------------------------
| Switch fullscreen mode | `MOD`+`f` | Switch fullscreen mode | <kbd>MOD</kbd>+<kbd>f</kbd>
| Rotate display left | `MOD`+`←` _(left)_ | Rotate display left | <kbd>MOD</kbd>+<kbd></kbd> _(left)_
| Rotate display right | `MOD`+`→` _(right)_ | Rotate display right | <kbd>MOD</kbd>+<kbd></kbd> _(right)_
| Resize window to 1:1 (pixel-perfect) | `MOD`+`g` | Resize window to 1:1 (pixel-perfect) | <kbd>MOD</kbd>+<kbd>g</kbd>
| Resize window to remove black borders | `MOD`+`w` \| _Double-click¹_ | Resize window to remove black borders | <kbd>MOD</kbd>+<kbd>w</kbd> \| _Double-click¹_
| Click on `HOME` | `MOD`+`h` \| _Middle-click_ | Click on `HOME` | <kbd>MOD</kbd>+<kbd>h</kbd> \| _Middle-click_
| Click on `BACK` | `MOD`+`b` \| _Right-click²_ | Click on `BACK` | <kbd>MOD</kbd>+<kbd>b</kbd> \| _Right-click²_
| Click on `APP_SWITCH` | `MOD`+`s` | Click on `APP_SWITCH` | <kbd>MOD</kbd>+<kbd>s</kbd>
| Click on `MENU` | `MOD`+`m` | Click on `MENU` (unlock screen) | <kbd>MOD</kbd>+<kbd>m</kbd>
| Click on `VOLUME_UP` | `MOD`+`↑` _(up)_ | Click on `VOLUME_UP` | <kbd>MOD</kbd>+<kbd></kbd> _(up)_
| Click on `VOLUME_DOWN` | `MOD`+`↓` _(down)_ | Click on `VOLUME_DOWN` | <kbd>MOD</kbd>+<kbd></kbd> _(down)_
| Click on `POWER` | `MOD`+`p` | Click on `POWER` | <kbd>MOD</kbd>+<kbd>p</kbd>
| Power on | _Right-click²_ | Power on | _Right-click²_
| Turn device screen off (keep mirroring) | `MOD`+`o` | Turn device screen off (keep mirroring) | <kbd>MOD</kbd>+<kbd>o</kbd>
| Turn device screen on | `MOD`+`Shift`+`o` | Turn device screen on | <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>o</kbd>
| Rotate device screen | `MOD`+`r` | Rotate device screen | <kbd>MOD</kbd>+<kbd>r</kbd>
| Expand notification panel | `MOD`+`n` | Expand notification panel | <kbd>MOD</kbd>+<kbd>n</kbd>
| Collapse notification panel | `MOD`+`Shift`+`n` | Collapse notification panel | <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>n</kbd>
| Copy to clipboard³ | `MOD`+`c` | Copy to clipboard³ | <kbd>MOD</kbd>+<kbd>c</kbd>
| Cut to clipboard³ | `MOD`+`x` | Cut to clipboard³ | <kbd>MOD</kbd>+<kbd>x</kbd>
| Synchronize clipboards and paste³ | `MOD`+`v` | Synchronize clipboards and paste³ | <kbd>MOD</kbd>+<kbd>v</kbd>
| Inject computer clipboard text | `MOD`+`Shift`+`v` | Inject computer clipboard text | <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>v</kbd>
| Enable/disable FPS counter (on stdout) | `MOD`+`i` | Enable/disable FPS counter (on stdout) | <kbd>MOD</kbd>+<kbd>i</kbd>
_¹Double-click on black borders to remove them._ _¹Double-click on black borders to remove them._
_²Right-click turns the screen on if it was off, presses BACK otherwise._ _²Right-click turns the screen on if it was off, presses BACK otherwise._
_³Only on Android >= 7._ _³Only on Android >= 7._
All `Ctrl`+_key_ shortcuts are forwarded to the device, so they are handled by All <kbd>Ctrl</kbd>+_key_ shortcuts are forwarded to the device, so they are
the active application. handled by the active application.
## Custom paths ## Custom paths

View File

@ -96,6 +96,10 @@ Do not display device (only when screen recording is enabled).
.B \-\-no\-mipmaps .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. 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 .TP
.BI "\-p, \-\-port " port[:port] .BI "\-p, \-\-port " port[:port]
Set the TCP port (range) used by the client to listen. Set the TCP port (range) used by the client to listen.

View File

@ -92,6 +92,9 @@ scrcpy_print_usage(const char *arg0) {
" mipmaps are automatically generated to improve downscaling\n" " mipmaps are automatically generated to improve downscaling\n"
" quality. This option disables the generation of mipmaps.\n" " quality. This option disables the generation of mipmaps.\n"
"\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" " -p, --port port[:port]\n"
" Set the TCP port (range) used by the client to listen.\n" " Set the TCP port (range) used by the client to listen.\n"
" Default is %d:%d.\n" " Default is %d:%d.\n"
@ -642,6 +645,7 @@ guess_record_format(const char *filename) {
#define OPT_FORCE_ADB_FORWARD 1019 #define OPT_FORCE_ADB_FORWARD 1019
#define OPT_DISABLE_SCREENSAVER 1020 #define OPT_DISABLE_SCREENSAVER 1020
#define OPT_SHORTCUT_MOD 1021 #define OPT_SHORTCUT_MOD 1021
#define OPT_NO_KEY_REPEAT 1022
bool bool
scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) { 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-control", no_argument, NULL, 'n'},
{"no-display", no_argument, NULL, 'N'}, {"no-display", no_argument, NULL, 'N'},
{"no-mipmaps", no_argument, NULL, OPT_NO_MIPMAPS}, {"no-mipmaps", no_argument, NULL, OPT_NO_MIPMAPS},
{"no-key-repeat", no_argument, NULL, OPT_NO_KEY_REPEAT},
{"port", required_argument, NULL, 'p'}, {"port", required_argument, NULL, 'p'},
{"prefer-text", no_argument, NULL, OPT_PREFER_TEXT}, {"prefer-text", no_argument, NULL, OPT_PREFER_TEXT},
{"push-target", required_argument, NULL, OPT_PUSH_TARGET}, {"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: case OPT_NO_MIPMAPS:
opts->mipmaps = false; opts->mipmaps = false;
break; break;
case OPT_NO_KEY_REPEAT:
opts->forward_key_repeat = false;
break;
case OPT_CODEC_OPTIONS: case OPT_CODEC_OPTIONS:
opts->codec_options = optarg; opts->codec_options = optarg;
break; break;

View File

@ -54,11 +54,14 @@ is_shortcut_mod(struct input_manager *im, uint16_t sdl_mod) {
} }
void void
input_manager_init(struct input_manager *im, bool prefer_text, input_manager_init(struct input_manager *im,
const struct sc_shortcut_mods *shortcut_mods) 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);
assert(shortcut_mods->count < SC_MAX_SHORTCUT_MODS); assert(shortcut_mods->count < SC_MAX_SHORTCUT_MODS);
for (unsigned i = 0; i < shortcut_mods->count; ++i) { 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.type = CONTROL_MSG_TYPE_INJECT_KEYCODE;
msg.inject_keycode.keycode = keycode; msg.inject_keycode.keycode = keycode;
msg.inject_keycode.metastate = 0; msg.inject_keycode.metastate = 0;
msg.inject_keycode.repeat = 0;
if (actions & ACTION_DOWN) { if (actions & ACTION_DOWN) {
msg.inject_keycode.action = AKEY_EVENT_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 void
input_manager_process_key(struct input_manager *im, input_manager_process_key(struct input_manager *im,
const SDL_KeyboardEvent *event, const SDL_KeyboardEvent *event) {
bool control) {
// control: indicates the state of the command-line option --no-control // control: indicates the state of the command-line option --no-control
bool control = im->control;
bool smod = is_shortcut_mod(im, event->keysym.mod); 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 (event->repeat) {
if (!im->forward_key_repeat) {
return;
}
++im->repeat; ++im->repeat;
} else { } else {
im->repeat = 0; im->repeat = 0;
@ -573,8 +580,9 @@ convert_mouse_button(const SDL_MouseButtonEvent *from, struct screen *screen,
void void
input_manager_process_mouse_button(struct input_manager *im, input_manager_process_mouse_button(struct input_manager *im,
const SDL_MouseButtonEvent *event, const SDL_MouseButtonEvent *event) {
bool control) { bool control = im->control;
if (event->which == SDL_TOUCH_MOUSEID) { if (event->which == SDL_TOUCH_MOUSEID) {
// simulated from touch events, so it's a duplicate // simulated from touch events, so it's a duplicate
return; return;

View File

@ -22,6 +22,8 @@ struct input_manager {
// number of repetitions. This variable keeps track of the count. // number of repetitions. This variable keeps track of the count.
unsigned repeat; unsigned repeat;
bool control;
bool forward_key_repeat;
bool prefer_text; bool prefer_text;
struct { struct {
@ -31,8 +33,8 @@ struct input_manager {
}; };
void void
input_manager_init(struct input_manager *im, bool prefer_text, input_manager_init(struct input_manager *im,
const struct sc_shortcut_mods *shortcut_mods); const struct scrcpy_options *options);
void void
input_manager_process_text_input(struct input_manager *im, input_manager_process_text_input(struct input_manager *im,
@ -40,8 +42,7 @@ input_manager_process_text_input(struct input_manager *im,
void void
input_manager_process_key(struct input_manager *im, input_manager_process_key(struct input_manager *im,
const SDL_KeyboardEvent *event, const SDL_KeyboardEvent *event);
bool control);
void void
input_manager_process_mouse_motion(struct input_manager *im, input_manager_process_mouse_motion(struct input_manager *im,
@ -53,8 +54,7 @@ input_manager_process_touch(struct input_manager *im,
void void
input_manager_process_mouse_button(struct input_manager *im, input_manager_process_mouse_button(struct input_manager *im,
const SDL_MouseButtonEvent *event, const SDL_MouseButtonEvent *event);
bool control);
void void
input_manager_process_mouse_wheel(struct input_manager *im, input_manager_process_mouse_wheel(struct input_manager *im,

View File

@ -170,7 +170,7 @@ enum event_result {
}; };
static 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) { switch (event->type) {
case EVENT_STREAM_STOPPED: case EVENT_STREAM_STOPPED:
LOGD("Video stream stopped"); LOGD("Video stream stopped");
@ -192,7 +192,7 @@ handle_event(SDL_Event *event, bool control) {
screen_handle_window_event(&screen, &event->window); screen_handle_window_event(&screen, &event->window);
break; break;
case SDL_TEXTINPUT: case SDL_TEXTINPUT:
if (!control) { if (!options->control) {
break; break;
} }
input_manager_process_text_input(&input_manager, &event->text); input_manager_process_text_input(&input_manager, &event->text);
@ -201,16 +201,16 @@ handle_event(SDL_Event *event, bool control) {
case SDL_KEYUP: case SDL_KEYUP:
// some key events do not interact with the device, so process the // some key events do not interact with the device, so process the
// event even if control is disabled // event even if control is disabled
input_manager_process_key(&input_manager, &event->key, control); input_manager_process_key(&input_manager, &event->key);
break; break;
case SDL_MOUSEMOTION: case SDL_MOUSEMOTION:
if (!control) { if (!options->control) {
break; break;
} }
input_manager_process_mouse_motion(&input_manager, &event->motion); input_manager_process_mouse_motion(&input_manager, &event->motion);
break; break;
case SDL_MOUSEWHEEL: case SDL_MOUSEWHEEL:
if (!control) { if (!options->control) {
break; break;
} }
input_manager_process_mouse_wheel(&input_manager, &event->wheel); input_manager_process_mouse_wheel(&input_manager, &event->wheel);
@ -219,8 +219,7 @@ handle_event(SDL_Event *event, bool control) {
case SDL_MOUSEBUTTONUP: case SDL_MOUSEBUTTONUP:
// some mouse events do not interact with the device, so process // some mouse events do not interact with the device, so process
// the event even if control is disabled // the event even if control is disabled
input_manager_process_mouse_button(&input_manager, &event->button, input_manager_process_mouse_button(&input_manager, &event->button);
control);
break; break;
case SDL_FINGERMOTION: case SDL_FINGERMOTION:
case SDL_FINGERDOWN: case SDL_FINGERDOWN:
@ -228,7 +227,7 @@ handle_event(SDL_Event *event, bool control) {
input_manager_process_touch(&input_manager, &event->tfinger); input_manager_process_touch(&input_manager, &event->tfinger);
break; break;
case SDL_DROPFILE: { case SDL_DROPFILE: {
if (!control) { if (!options->control) {
break; break;
} }
file_handler_action_t action; file_handler_action_t action;
@ -245,16 +244,15 @@ handle_event(SDL_Event *event, bool control) {
} }
static bool static bool
event_loop(bool display, bool control) { event_loop(const struct scrcpy_options *options) {
(void) display;
#ifdef CONTINUOUS_RESIZING_WORKAROUND #ifdef CONTINUOUS_RESIZING_WORKAROUND
if (display) { if (options->display) {
SDL_AddEventWatch(event_watcher, NULL); SDL_AddEventWatch(event_watcher, NULL);
} }
#endif #endif
SDL_Event event; SDL_Event event;
while (SDL_WaitEvent(&event)) { while (SDL_WaitEvent(&event)) {
enum event_result result = handle_event(&event, control); enum event_result result = handle_event(&event, options);
switch (result) { switch (result) {
case EVENT_RESULT_STOPPED_BY_USER: case EVENT_RESULT_STOPPED_BY_USER:
return true; return true;
@ -443,10 +441,9 @@ scrcpy(const struct scrcpy_options *options) {
} }
} }
input_manager_init(&input_manager, options->prefer_text, input_manager_init(&input_manager, options);
&options->shortcut_mods);
ret = event_loop(options->display, options->control); ret = event_loop(options);
LOGD("quit..."); LOGD("quit...");
screen_destroy(&screen); screen_destroy(&screen);

View File

@ -78,6 +78,7 @@ struct scrcpy_options {
bool stay_awake; bool stay_awake;
bool force_adb_forward; bool force_adb_forward;
bool disable_screensaver; bool disable_screensaver;
bool forward_key_repeat;
}; };
#define SCRCPY_OPTIONS_DEFAULT { \ #define SCRCPY_OPTIONS_DEFAULT { \
@ -121,6 +122,7 @@ struct scrcpy_options {
.stay_awake = false, \ .stay_awake = false, \
.force_adb_forward = false, \ .force_adb_forward = false, \
.disable_screensaver = false, \ .disable_screensaver = false, \
.forward_key_repeat = true, \
} }
bool bool

View File

@ -15,6 +15,6 @@ cpu = 'i686'
endian = 'little' endian = 'little'
[properties] [properties]
prebuilt_ffmpeg_shared = 'ffmpeg-4.2.2-win32-shared' prebuilt_ffmpeg_shared = 'ffmpeg-4.3.1-win32-shared'
prebuilt_ffmpeg_dev = 'ffmpeg-4.2.2-win32-dev' prebuilt_ffmpeg_dev = 'ffmpeg-4.3.1-win32-dev'
prebuilt_sdl2 = 'SDL2-2.0.12/i686-w64-mingw32' prebuilt_sdl2 = 'SDL2-2.0.12/i686-w64-mingw32'

View File

@ -15,6 +15,6 @@ cpu = 'x86_64'
endian = 'little' endian = 'little'
[properties] [properties]
prebuilt_ffmpeg_shared = 'ffmpeg-4.2.2-win64-shared' prebuilt_ffmpeg_shared = 'ffmpeg-4.3.1-win64-shared'
prebuilt_ffmpeg_dev = 'ffmpeg-4.2.2-win64-dev' prebuilt_ffmpeg_dev = 'ffmpeg-4.3.1-win64-dev'
prebuilt_sdl2 = 'SDL2-2.0.12/x86_64-w64-mingw32' prebuilt_sdl2 = 'SDL2-2.0.12/x86_64-w64-mingw32'

View File

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

View File

@ -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-win64: prepare-sdl2 prepare-ffmpeg-shared-win64 prepare-ffmpeg-dev-win64 prepare-adb
prepare-ffmpeg-shared-win32: prepare-ffmpeg-shared-win32:
@./prepare-dep https://ffmpeg.zeranoe.com/builds/win32/shared/ffmpeg-4.2.2-win32-shared.zip \ @./prepare-dep https://ffmpeg.zeranoe.com/builds/win32/shared/ffmpeg-4.3.1-win32-shared.zip \
ab5d603aaa54de360db2c2ffe378c82376b9343ea1175421dd644639aa07ee31 \ 357af9901a456f4dcbacd107e83a934d344c9cb07ddad8aaf80612eeab7d26d2 \
ffmpeg-4.2.2-win32-shared ffmpeg-4.3.1-win32-shared
prepare-ffmpeg-dev-win32: prepare-ffmpeg-dev-win32:
@./prepare-dep https://ffmpeg.zeranoe.com/builds/win32/dev/ffmpeg-4.2.2-win32-dev.zip \ @./prepare-dep https://ffmpeg.zeranoe.com/builds/win32/dev/ffmpeg-4.3.1-win32-dev.zip \
8d224be567a2950cad4be86f4aabdd045bfa52ad758e87c72cedd278613bc6c8 \ 230efb08e9bcf225bd474da29676c70e591fc94d8790a740ca801408fddcb78b \
ffmpeg-4.2.2-win32-dev ffmpeg-4.3.1-win32-dev
prepare-ffmpeg-shared-win64: prepare-ffmpeg-shared-win64:
@./prepare-dep https://ffmpeg.zeranoe.com/builds/win64/shared/ffmpeg-4.2.2-win64-shared.zip \ @./prepare-dep https://ffmpeg.zeranoe.com/builds/win64/shared/ffmpeg-4.3.1-win64-shared.zip \
5aedf268952b7d9f6541dbfcb47cd86a7e7881a3b7ba482fd3bc4ca33bda7bf5 \ dd29b7f92f48dead4dd940492c7509138c0f99db445076d0a597007298a79940 \
ffmpeg-4.2.2-win64-shared ffmpeg-4.3.1-win64-shared
prepare-ffmpeg-dev-win64: prepare-ffmpeg-dev-win64:
@./prepare-dep https://ffmpeg.zeranoe.com/builds/win64/dev/ffmpeg-4.2.2-win64-dev.zip \ @./prepare-dep https://ffmpeg.zeranoe.com/builds/win64/dev/ffmpeg-4.3.1-win64-dev.zip \
f4885f859c5b0d6663c2a0a4c1cf035b1c60b146402790b796bd3ad84f4f3ca2 \ 2e8038242cf8e1bd095c2978f196ff0462b122cc6ef7e74626a6af15459d8b81 \
ffmpeg-4.2.2-win64-dev ffmpeg-4.3.1-win64-dev
prepare-sdl2: prepare-sdl2:
@./prepare-dep https://libsdl.org/release/SDL2-devel-2.0.12-mingw.tar.gz \ @./prepare-dep https://libsdl.org/release/SDL2-devel-2.0.12-mingw.tar.gz \
@ -35,6 +35,6 @@ prepare-sdl2:
SDL2-2.0.12 SDL2-2.0.12
prepare-adb: prepare-adb:
@./prepare-dep https://dl.google.com/android/repository/platform-tools_r30.0.0-windows.zip \ @./prepare-dep https://dl.google.com/android/repository/platform-tools_r30.0.4-windows.zip \
854305f9a702f5ea2c3de73edde402bd26afa0ee944c9b0c4380420f5a862e0d \ 413182fff6c5957911e231b9e97e6be4fc6a539035e3dfb580b5c54bd5950fee \
platform-tools platform-tools

View File

@ -6,8 +6,8 @@ android {
applicationId "com.genymobile.scrcpy" applicationId "com.genymobile.scrcpy"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 29 targetSdkVersion 29
versionCode 16 versionCode 17
versionName "1.14" versionName "1.15"
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.14 SCRCPY_VERSION_NAME=1.15
PLATFORM=${ANDROID_PLATFORM:-29} PLATFORM=${ANDROID_PLATFORM:-29}
BUILD_TOOLS=${ANDROID_BUILD_TOOLS:-29.0.2} BUILD_TOOLS=${ANDROID_BUILD_TOOLS:-29.0.2}
@ -42,6 +42,8 @@ echo "Generating java from aidl..."
cd "$SERVER_DIR/src/main/aidl" cd "$SERVER_DIR/src/main/aidl"
"$ANDROID_HOME/build-tools/$BUILD_TOOLS/aidl" -o"$CLASSES_DIR" \ "$ANDROID_HOME/build-tools/$BUILD_TOOLS/aidl" -o"$CLASSES_DIR" \
android/view/IRotationWatcher.aidl android/view/IRotationWatcher.aidl
"$ANDROID_HOME/build-tools/$BUILD_TOOLS/aidl" -o"$CLASSES_DIR" \
android/content/IOnPrimaryClipChangedListener.aidl
echo "Compiling java sources..." echo "Compiling java sources..."
cd ../java cd ../java
@ -55,6 +57,7 @@ cd "$CLASSES_DIR"
"$ANDROID_HOME/build-tools/$BUILD_TOOLS/dx" --dex \ "$ANDROID_HOME/build-tools/$BUILD_TOOLS/dx" --dex \
--output "$BUILD_DIR/classes.dex" \ --output "$BUILD_DIR/classes.dex" \
android/view/*.class \ android/view/*.class \
android/content/*.class \
com/genymobile/scrcpy/*.class \ com/genymobile/scrcpy/*.class \
com/genymobile/scrcpy/wrappers/*.class com/genymobile/scrcpy/wrappers/*.class

View File

@ -8,11 +8,16 @@ import android.view.KeyEvent;
import android.view.MotionEvent; import android.view.MotionEvent;
import java.io.IOException; import java.io.IOException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class Controller { public class Controller {
private static final int DEVICE_ID_VIRTUAL = -1; private static final int DEVICE_ID_VIRTUAL = -1;
private static final ScheduledExecutorService EXECUTOR = Executors.newSingleThreadScheduledExecutor();
private final Device device; private final Device device;
private final DesktopConnection connection; private final DesktopConnection connection;
private final DeviceMessageSender sender; 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.PointerProperties[] pointerProperties = new MotionEvent.PointerProperties[PointersState.MAX_POINTERS];
private final MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[PointersState.MAX_POINTERS]; private final MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[PointersState.MAX_POINTERS];
private boolean keepPowerModeOff;
public Controller(Device device, DesktopConnection connection) { public Controller(Device device, DesktopConnection connection) {
this.device = device; this.device = device;
this.connection = connection; this.connection = connection;
@ -117,6 +124,7 @@ public class Controller {
int mode = msg.getAction(); int mode = msg.getAction();
boolean setPowerModeOk = Device.setScreenPowerMode(mode); boolean setPowerModeOk = Device.setScreenPowerMode(mode);
if (setPowerModeOk) { if (setPowerModeOk) {
keepPowerModeOff = mode == Device.POWER_MODE_OFF;
Ln.i("Device screen turned " + (mode == Device.POWER_MODE_OFF ? "off" : "on")); 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) { 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); return device.injectKeyEvent(action, keycode, repeat, metaState);
} }
@ -223,8 +234,24 @@ public class Controller {
return device.injectEvent(event); 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() { private boolean pressBackOrTurnScreenOn() {
int keycode = device.isScreenOn() ? KeyEvent.KEYCODE_BACK : KeyEvent.KEYCODE_WAKEUP; int keycode = device.isScreenOn() ? KeyEvent.KEYCODE_BACK : KeyEvent.KEYCODE_WAKEUP;
if (keepPowerModeOff && keycode == KeyEvent.KEYCODE_WAKEUP) {
schedulePowerModeOff();
}
return device.injectKeycode(keycode); return device.injectKeycode(keycode);
} }