diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 49402a6e..a77b7ff1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -74,7 +74,6 @@ jobs: - name: Install dependencies run: | - sudo apt update sudo apt install -y meson ninja-build nasm ffmpeg libsdl2-2.0-0 \ libsdl2-dev libavcodec-dev libavdevice-dev libavformat-dev \ libavutil-dev libswresample-dev libusb-1.0-0 libusb-1.0-0-dev \ @@ -84,7 +83,7 @@ jobs: run: release/test_client.sh build-linux-x86_64: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest steps: - name: Check architecture run: | @@ -100,7 +99,6 @@ jobs: - name: Install dependencies run: | - sudo apt update sudo apt install -y meson ninja-build nasm ffmpeg libsdl2-2.0-0 \ libsdl2-dev libavcodec-dev libavdevice-dev libavformat-dev \ libavutil-dev libswresample-dev libusb-1.0-0 libusb-1.0-0-dev \ @@ -131,12 +129,14 @@ jobs: - name: Install dependencies run: | - sudo apt update sudo apt install -y meson ninja-build nasm ffmpeg libsdl2-2.0-0 \ libsdl2-dev libavcodec-dev libavdevice-dev libavformat-dev \ libavutil-dev libswresample-dev libusb-1.0-0 libusb-1.0-0-dev \ mingw-w64 mingw-w64-tools libz-mingw-w64-dev + - name: Workaround for old meson version run by Github Actions + run: sed -i 's/^pkg-config/pkgconfig/' cross_win32.txt + - name: Build run: release/build_windows.sh 32 @@ -162,12 +162,14 @@ jobs: - name: Install dependencies run: | - sudo apt update sudo apt install -y meson ninja-build nasm ffmpeg libsdl2-2.0-0 \ libsdl2-dev libavcodec-dev libavdevice-dev libavformat-dev \ libavutil-dev libswresample-dev libusb-1.0-0 libusb-1.0-0-dev \ mingw-w64 mingw-w64-tools libz-mingw-w64-dev + - name: Workaround for old meson version run by Github Actions + run: sed -i 's/^pkg-config/pkgconfig/' cross_win64.txt + - name: Build run: release/build_windows.sh 64 @@ -206,13 +208,6 @@ jobs: libtool - name: Build - env: - # the default Xcode (and macOS SDK) version can be found at - # - # - # then the minimal supported deployment target of that macOS SDK can be found at - # - MACOSX_DEPLOYMENT_TARGET: 10.13 run: release/build_macos.sh aarch64 # upload-artifact does not preserve permissions @@ -249,13 +244,6 @@ jobs: # autoconf and libtool are already installed on macos-13 - name: Build - env: - # the default Xcode (and macOS SDK) version can be found at - # - # - # then the minimal supported deployment target of that macOS SDK can be found at - # - MACOSX_DEPLOYMENT_TARGET: 10.13 run: release/build_macos.sh x86_64 # upload-artifact does not preserve permissions diff --git a/FAQ.md b/FAQ.md index 24722c74..5f089cd7 100644 --- a/FAQ.md +++ b/FAQ.md @@ -166,13 +166,14 @@ Rebooting the device is necessary once this option is set. ### Special characters do not work -The default text injection method is limited to ASCII characters. A trick allows -to also inject some [accented characters][accented-characters], +The default text injection method is [limited to ASCII characters][text-input]. +A trick allows to also inject some [accented characters][accented-characters], but that's all. See [#37]. To avoid the problem, [change the keyboard mode to simulate a physical keyboard][hid]. +[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 [hid]: doc/keyboard.md#physical-keyboard-simulation diff --git a/LICENSE b/LICENSE index 1196b3da..d9326a74 100644 --- a/LICENSE +++ b/LICENSE @@ -188,7 +188,7 @@ identification within third-party archives. Copyright (C) 2018 Genymobile - Copyright (C) 2018-2025 Romain Vimont + Copyright (C) 2018-2024 Romain Vimont Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index d886d23c..09fa12b4 100644 --- a/README.md +++ b/README.md @@ -2,16 +2,16 @@ source for the project. Do not download releases from random websites, even if their name contains `scrcpy`.** -# scrcpy (v3.3.1) +# scrcpy (v3.1) scrcpy _pronounced "**scr**een **c**o**py**"_ -This application mirrors Android devices (video and audio) connected via USB or -[TCP/IP](doc/connection.md#tcpip-wireless) and allows control using the -computer's keyboard and mouse. It does not require _root_ access or an app -installed on the device. It works on _Linux_, _Windows_, and _macOS_. +This application mirrors Android devices (video and audio) connected via +USB or [over TCP/IP](doc/connection.md#tcpip-wireless), and allows to control the +device with the keyboard and the mouse of the computer. It does not require any +_root_ access. It works on _Linux_, _Windows_ and _macOS_. ![screenshot](assets/screenshot-debian-600.jpg) @@ -58,7 +58,7 @@ Make sure you [enabled USB debugging][enable-adb] on your device(s). On some devices (especially Xiaomi), you might get the following error: ``` -Injecting input events requires the caller (or the source of the instrumentation, if any) to have the INJECT_EVENTS permission. +java.lang.SecurityException: Injecting input events requires the caller (or the source of the instrumentation, if any) to have the INJECT_EVENTS permission. ``` In that case, you need to enable [an additional option][control] `USB debugging @@ -78,16 +78,6 @@ Note that USB debugging is not required to run scrcpy in [OTG mode](doc/otg.md). - [macOS](doc/macos.md) -## Must-know tips - - - [Reducing resolution](doc/video.md#size) may greatly improve performance - (`scrcpy -m1024`) - - [_Right-click_](doc/mouse.md#mouse-bindings) triggers `BACK` - - [_Middle-click_](doc/mouse.md#mouse-bindings) triggers `HOME` - - Alt+f toggles [fullscreen](doc/window.md#fullscreen) - - There are many other [shortcuts](doc/shortcuts.md) - - ## Usage examples There are a lot of options, [documented](#user-documentation) in separate pages. @@ -207,10 +197,10 @@ work][donate]: [donate]: https://blog.rom1v.com/about/#support-my-open-source-work -## License +## Licence Copyright (C) 2018 Genymobile - Copyright (C) 2018-2025 Romain Vimont + Copyright (C) 2018-2024 Romain Vimont Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/app/data/bash-completion/scrcpy b/app/data/bash-completion/scrcpy index a49da8ca..29130892 100644 --- a/app/data/bash-completion/scrcpy +++ b/app/data/bash-completion/scrcpy @@ -23,7 +23,6 @@ _scrcpy() { -d --select-usb --disable-screensaver --display-id= - --display-ime-policy= --display-orientation= -e --select-tcpip -f --fullscreen @@ -122,7 +121,7 @@ _scrcpy() { return ;; --audio-source) - COMPREPLY=($(compgen -W 'output playback mic mic-unprocessed mic-camcorder mic-voice-recognition mic-voice-communication voice-call voice-call-uplink voice-call-downlink voice-performance' -- "$cur")) + COMPREPLY=($(compgen -W 'output mic playback' -- "$cur")) return ;; --camera-facing) @@ -149,10 +148,6 @@ _scrcpy() { COMPREPLY=($(compgen -W '0 90 180 270 flip0 flip90 flip180 flip270' -- "$cur")) return ;; - --display-ime-policy) - COMPREPLY=($(compgen -W 'local fallback hide' -- "$cur")) - return - ;; --record-orientation) COMPREPLY=($(compgen -W '0 90 180 270' -- "$cur")) return @@ -205,7 +200,6 @@ _scrcpy() { |-p|--port \ |--push-target \ |--rotation \ - |--screen-off-timeout \ |--tunnel-host \ |--tunnel-port \ |--v4l2-buffer \ diff --git a/app/data/zsh-completion/_scrcpy b/app/data/zsh-completion/_scrcpy index 04ffb8f1..0897b9cc 100644 --- a/app/data/zsh-completion/_scrcpy +++ b/app/data/zsh-completion/_scrcpy @@ -1,4 +1,4 @@ -#compdef scrcpy scrcpy.exe +#compdef -N scrcpy -N scrcpy.exe # # name: scrcpy # auth: hltdev [hltdev8642@gmail.com] @@ -11,12 +11,12 @@ arguments=( '--always-on-top[Make scrcpy window always on top \(above other windows\)]' '--angle=[Rotate the video content by a custom angle, in degrees]' '--audio-bit-rate=[Encode the audio at the given bit-rate]' - '--audio-buffer=[Configure the audio buffering delay \(in milliseconds\)]' + '--audio-buffer=[Configure the audio buffering delay (in milliseconds)]' '--audio-codec=[Select the audio codec]:codec:(opus aac flac raw)' '--audio-codec-options=[Set a list of comma-separated key\:type=value options for the device audio encoder]' '--audio-dup=[Duplicate audio]' '--audio-encoder=[Use a specific MediaCodec audio encoder]' - '--audio-source=[Select the audio source]:source:(output playback mic mic-unprocessed mic-camcorder mic-voice-recognition mic-voice-communication voice-call voice-call-uplink voice-call-downlink voice-performance)' + '--audio-source=[Select the audio source]:source:(output mic playback)' '--audio-output-buffer=[Configure the size of the SDL audio output buffer (in milliseconds)]' {-b,--video-bit-rate=}'[Encode the video at the given bit-rate]' '--camera-ar=[Select the camera size by its aspect ratio]' @@ -30,15 +30,14 @@ arguments=( {-d,--select-usb}'[Use USB device]' '--disable-screensaver[Disable screensaver while scrcpy is running]' '--display-id=[Specify the display id to mirror]' - '--display-ime-policy[Set the policy for selecting where the IME should be displayed]' '--display-orientation=[Set the initial display orientation]:orientation values:(0 90 180 270 flip0 flip90 flip180 flip270)' {-e,--select-tcpip}'[Use TCP/IP device]' {-f,--fullscreen}'[Start in fullscreen]' '--force-adb-forward[Do not attempt to use \"adb reverse\" to connect to the device]' - '-G[Use UHID/AOA gamepad \(same as --gamepad=uhid or --gamepad=aoa, depending on OTG mode\)]' + '-G[Use UHID/AOA gamepad (same as --gamepad=uhid or --gamepad=aoa, depending on OTG mode)]' '--gamepad=[Set the gamepad input mode]:mode:(disabled uhid aoa)' {-h,--help}'[Print the help]' - '-K[Use UHID/AOA keyboard \(same as --keyboard=uhid or --keyboard=aoa, depending on OTG mode\)]' + '-K[Use UHID/AOA keyboard (same as --keyboard=uhid or --keyboard=aoa, depending on OTG mode)]' '--keyboard=[Set the keyboard input mode]:mode:(disabled sdk uhid aoa)' '--kill-adb-on-close[Kill adb when scrcpy terminates]' '--legacy-paste[Inject computer clipboard text as a sequence of key events on Ctrl+v]' @@ -48,7 +47,7 @@ arguments=( '--list-displays[List displays available on the device]' '--list-encoders[List video and audio encoders available on the device]' {-m,--max-size=}'[Limit both the width and height of the video to value]' - '-M[Use UHID/AOA mouse \(same as --mouse=uhid or --mouse=aoa, depending on OTG mode\)]' + '-M[Use UHID/AOA mouse (same as --mouse=uhid or --mouse=aoa, depending on OTG mode)]' '--max-fps=[Limit the frame rate of screen capture]' '--mouse=[Set the mouse input mode]:mode:(disabled sdk uhid aoa)' '--mouse-bind=[Configure bindings of secondary clicks]' diff --git a/app/deps/adb_linux.sh b/app/deps/adb_linux.sh index a3e339ec..17b5641d 100755 --- a/app/deps/adb_linux.sh +++ b/app/deps/adb_linux.sh @@ -4,10 +4,10 @@ DEPS_DIR=$(dirname ${BASH_SOURCE[0]}) cd "$DEPS_DIR" . common -VERSION=36.0.0 +VERSION=35.0.2 FILENAME=platform-tools_r$VERSION-linux.zip PROJECT_DIR=platform-tools-$VERSION-linux -SHA256SUM=0ead642c943ffe79701fccca8f5f1c69c4ce4f43df2eefee553f6ccb27cbfbe8 +SHA256SUM=acfdcccb123a8718c46c46c059b2f621140194e5ec1ac9d81715be3d6ab6cd0a cd "$SOURCES_DIR" diff --git a/app/deps/adb_macos.sh b/app/deps/adb_macos.sh index 36f5df89..8a25915e 100755 --- a/app/deps/adb_macos.sh +++ b/app/deps/adb_macos.sh @@ -4,10 +4,10 @@ DEPS_DIR=$(dirname ${BASH_SOURCE[0]}) cd "$DEPS_DIR" . common -VERSION=36.0.0 +VERSION=35.0.2 FILENAME=platform-tools_r$VERSION-darwin.zip PROJECT_DIR=platform-tools-$VERSION-darwin -SHA256SUM=b241878e6ec20650b041bf715ea05f7d5dc73bd24529464bd9cf68946e3132bd +SHA256SUM=1820078db90bf21628d257ff052528af1c61bb48f754b3555648f5652fa35d78 cd "$SOURCES_DIR" diff --git a/app/deps/adb_windows.sh b/app/deps/adb_windows.sh index de37162c..d36706b0 100755 --- a/app/deps/adb_windows.sh +++ b/app/deps/adb_windows.sh @@ -4,10 +4,10 @@ DEPS_DIR=$(dirname ${BASH_SOURCE[0]}) cd "$DEPS_DIR" . common -VERSION=36.0.0 +VERSION=35.0.2 FILENAME=platform-tools_r$VERSION-win.zip PROJECT_DIR=platform-tools-$VERSION-windows -SHA256SUM=24bd8bebbbb58b9870db202b5c6775c4a49992632021c60750d9d8ec8179d5f0 +SHA256SUM=2975a3eac0b19182748d64195375ad056986561d994fffbdc64332a516300bb9 cd "$SOURCES_DIR" diff --git a/app/deps/ffmpeg.sh b/app/deps/ffmpeg.sh index fb8b9a25..d268ca91 100755 --- a/app/deps/ffmpeg.sh +++ b/app/deps/ffmpeg.sh @@ -5,10 +5,10 @@ cd "$DEPS_DIR" . common process_args "$@" -VERSION=7.1.1 +VERSION=7.1 FILENAME=ffmpeg-$VERSION.tar.xz PROJECT_DIR=ffmpeg-$VERSION -SHA256SUM=733984395e0dbbe5c046abda2dc49a5544e7e0e1e2366bba849222ae9e3a03b1 +SHA256SUM=40973D44970DBC83EF302B0609F2E74982BE2D85916DD2EE7472D30678A7ABE6 cd "$SOURCES_DIR" diff --git a/app/deps/libusb.sh b/app/deps/libusb.sh index 887a2a77..340b0f70 100755 --- a/app/deps/libusb.sh +++ b/app/deps/libusb.sh @@ -5,10 +5,10 @@ cd "$DEPS_DIR" . common process_args "$@" -VERSION=1.0.29 +VERSION=1.0.27 FILENAME=libusb-$VERSION.tar.gz PROJECT_DIR=libusb-$VERSION -SHA256SUM=7c2dd39c0b2589236e48c93247c986ae272e27570942b4163cb00a060fcf1b74 +SHA256SUM=e8f18a7a36ecbb11fb820bd71540350d8f61bcd9db0d2e8c18a6fb80b214a3de cd "$SOURCES_DIR" diff --git a/app/deps/sdl.sh b/app/deps/sdl.sh index 54fee12b..c098e367 100755 --- a/app/deps/sdl.sh +++ b/app/deps/sdl.sh @@ -5,10 +5,10 @@ cd "$DEPS_DIR" . common process_args "$@" -VERSION=2.32.8 +VERSION=2.30.10 FILENAME=SDL-$VERSION.tar.gz PROJECT_DIR=SDL-release-$VERSION -SHA256SUM=dd35e05644ae527848d02433bec24dd0ea65db59faecf1a0e5d1880c533dac2c +SHA256SUM=35a8b9c4f3635d85762b904ac60ca4e0806bff89faeb269caafbe80860d67168 cd "$SOURCES_DIR" diff --git a/app/meson.build b/app/meson.build index f7df69eb..be02ebc1 100644 --- a/app/meson.build +++ b/app/meson.build @@ -192,19 +192,19 @@ datadir = get_option('datadir') # by default 'share' install_man('scrcpy.1') install_data('data/icon.png', rename: 'scrcpy.png', - install_dir: datadir / 'icons/hicolor/256x256/apps') + install_dir: join_paths(datadir, 'icons/hicolor/256x256/apps')) install_data('data/zsh-completion/_scrcpy', - install_dir: datadir / 'zsh/site-functions') + install_dir: join_paths(datadir, 'zsh/site-functions')) install_data('data/bash-completion/scrcpy', - install_dir: datadir / 'bash-completion/completions') + install_dir: join_paths(datadir, 'bash-completion/completions')) # Desktop entry file for application launchers if host_machine.system() == 'linux' # Install a launcher (ex: /usr/local/share/applications/scrcpy.desktop) install_data('data/scrcpy.desktop', - install_dir: datadir / 'applications') + install_dir: join_paths(datadir, 'applications')) install_data('data/scrcpy-console.desktop', - install_dir: datadir / 'applications') + install_dir: join_paths(datadir, 'applications')) endif @@ -279,9 +279,3 @@ if get_option('buildtype') == 'debug' test(t[0], exe) endforeach endif - -if meson.version().version_compare('>= 0.58.0') - devenv = environment() - devenv.set('SCRCPY_ICON_PATH', meson.current_source_dir() / 'data/icon.png') - meson.add_devenv(devenv) -endif diff --git a/app/scrcpy-windows.rc b/app/scrcpy-windows.rc index 9c5374ae..2c441aa1 100644 --- a/app/scrcpy-windows.rc +++ b/app/scrcpy-windows.rc @@ -13,7 +13,7 @@ BEGIN VALUE "LegalCopyright", "Romain Vimont, Genymobile" VALUE "OriginalFilename", "scrcpy.exe" VALUE "ProductName", "scrcpy" - VALUE "ProductVersion", "3.3.1" + VALUE "ProductVersion", "3.1" END END BLOCK "VarFileInfo" diff --git a/app/scrcpy.1 b/app/scrcpy.1 index d72fda13..924905e4 100644 --- a/app/scrcpy.1 +++ b/app/scrcpy.1 @@ -67,19 +67,13 @@ The available encoders can be listed by \fB\-\-list\-encoders\fR. .TP .BI "\-\-audio\-source " source -Select the audio source. Possible values are: +Select the audio source (output, mic or playback). - - "output": forwards the whole audio output, and disables playback on the device. - - "playback": captures the audio playback (Android apps can opt-out, so the whole output is not necessarily captured). - - "mic": captures the microphone. - - "mic-unprocessed": captures the microphone unprocessed (raw) sound. - - "mic-camcorder": captures the microphone tuned for video recording, with the same orientation as the camera if available. - - "mic-voice-recognition": captures the microphone tuned for voice recognition. - - "mic-voice-communication": captures the microphone tuned for voice communications (it will for instance take advantage of echo cancellation or automatic gain control if available). - - "voice-call": captures voice call. - - "voice-call-uplink": captures voice call uplink only. - - "voice-call-downlink": captures voice call downlink only. - - "voice-performance": captures audio meant to be processed for live performance (karaoke), includes both the microphone and the device playback. +The "output" source forwards the whole audio output, and disables playback on the device. + +The "playback" source captures the audio playback (Android apps can opt-out, so the whole output is not necessarily captured). + +The "mic" source captures the microphone. Default is output. @@ -167,19 +161,6 @@ The available display ids can be listed by \fB\-\-list\-displays\fR. Default is 0. -.TP -.BI "\-\-display\-ime\-policy " value -Set the policy for selecting where the IME should be displayed. - -Possible values are "local", "fallback" and "hide": - - - "local" means that the IME should appear on the local display. - - "fallback" means that the IME should appear on a fallback display (the default display). - - "hide" means that the IME should be hidden. - -By default, the IME policy is left unchanged. - - .TP .BI "\-\-display\-orientation " value Set the initial display orientation. @@ -408,7 +389,7 @@ Disable video playback on the computer. .TP .B \-\-no\-window -Disable scrcpy window. Implies --no-video-playback. +Disable scrcpy window. Implies --no-video-playback and --no-control. .TP .BI "\-\-orientation " value @@ -510,10 +491,6 @@ The device serial number. Mandatory only if several devices are connected to adb .B \-S, \-\-turn\-screen\-off Turn the device screen off immediately. -.TP -.B "\-\-screen\-off\-timeout " seconds -Set the screen off timeout while scrcpy is running (restore the initial value on exit). - .TP .BI "\-\-shortcut\-mod " key\fR[+...]][,...] Specify the modifiers to use for scrcpy shortcuts. Possible keys are "lctrl", "rctrl", "lalt", "ralt", "lsuper" and "rsuper". @@ -852,7 +829,7 @@ Report bugs to . .SH COPYRIGHT Copyright \(co 2018 Genymobile -Copyright \(co 2018\-2025 Romain Vimont +Copyright \(co 2018\-2024 Romain Vimont Licensed under the Apache License, Version 2.0. diff --git a/app/src/adb/adb.c b/app/src/adb/adb.c index 9e9cfd6b..0cd3c0fd 100644 --- a/app/src/adb/adb.c +++ b/app/src/adb/adb.c @@ -4,10 +4,9 @@ #include #include #include -#include -#include "adb/adb_device.h" -#include "adb/adb_parser.h" +#include "adb_device.h" +#include "adb_parser.h" #include "util/env.h" #include "util/file.h" #include "util/log.h" @@ -110,7 +109,7 @@ show_adb_installation_msg(void) { } pkg_managers[] = { {"apt", "apt install adb"}, {"apt-get", "apt-get install adb"}, - {"brew", "brew install --cask android-platform-tools"}, + {"brew", "brew cask install android-platform-tools"}, {"dnf", "dnf install android-tools"}, {"emerge", "emerge dev-util/android-tools"}, {"pacman", "pacman -S android-tools"}, diff --git a/app/src/adb/adb.h b/app/src/adb/adb.h index e4903902..43310fb9 100644 --- a/app/src/adb/adb.h +++ b/app/src/adb/adb.h @@ -6,7 +6,7 @@ #include #include -#include "adb/adb_device.h" +#include "adb_device.h" #include "util/intr.h" #define SC_ADB_NO_STDOUT (1 << 0) diff --git a/app/src/adb/adb_device.h b/app/src/adb/adb_device.h index 308663ef..56393bcf 100644 --- a/app/src/adb/adb_device.h +++ b/app/src/adb/adb_device.h @@ -4,6 +4,7 @@ #include "common.h" #include +#include #include "util/vector.h" diff --git a/app/src/adb/adb_parser.c b/app/src/adb/adb_parser.c index 90a1b30b..66bb1854 100644 --- a/app/src/adb/adb_parser.c +++ b/app/src/adb/adb_parser.c @@ -3,7 +3,6 @@ #include #include #include -#include #include "util/log.h" #include "util/str.h" diff --git a/app/src/adb/adb_parser.h b/app/src/adb/adb_parser.h index b8738a35..f20349f6 100644 --- a/app/src/adb/adb_parser.h +++ b/app/src/adb/adb_parser.h @@ -3,9 +3,9 @@ #include "common.h" -#include +#include -#include "adb/adb_device.h" +#include "adb_device.h" /** * Parse the available devices from the output of `adb devices` diff --git a/app/src/adb/adb_tunnel.c b/app/src/adb/adb_tunnel.c index 43e80e13..fa936e4b 100644 --- a/app/src/adb/adb_tunnel.c +++ b/app/src/adb/adb_tunnel.c @@ -1,11 +1,11 @@ #include "adb_tunnel.h" #include -#include -#include "adb/adb.h" +#include "adb.h" #include "util/log.h" #include "util/net_intr.h" +#include "util/process_intr.h" static bool listen_on_port(struct sc_intr *intr, sc_socket socket, uint16_t port) { diff --git a/app/src/audio_player.h b/app/src/audio_player.h index 5a66d43b..9133c24a 100644 --- a/app/src/audio_player.h +++ b/app/src/audio_player.h @@ -3,7 +3,9 @@ #include "common.h" -#include +#include +#include +#include #include "audio_regulator.h" #include "trait/frame_sink.h" diff --git a/app/src/audio_regulator.c b/app/src/audio_regulator.c index 16fdd08b..3e4f78ad 100644 --- a/app/src/audio_regulator.c +++ b/app/src/audio_regulator.c @@ -1,9 +1,5 @@ #include "audio_regulator.h" -#include -#include -#include -#include #include #include @@ -76,10 +72,8 @@ sc_audio_regulator_pull(struct sc_audio_regulator *ar, uint8_t *out, // Wait until the buffer is filled up to at least target_buffering // before playing if (buffered_samples < ar->target_buffering) { -#ifdef SC_AUDIO_REGULATOR_DEBUG - LOGD("[Audio] Inserting initial buffering silence: %" PRIu32 + LOGV("[Audio] Inserting initial buffering silence: %" PRIu32 " samples", out_samples); -#endif // Delay playback starting to reach the target buffering. Fill the // whole buffer with silence (len is small compared to the // arbitrary margin value). @@ -100,10 +94,8 @@ sc_audio_regulator_pull(struct sc_audio_regulator *ar, uint8_t *out, // dropped to keep the latency minimal. However, this would cause very // audible glitches, so let the clock compensation restore the target // latency. -#ifdef SC_AUDIO_REGULATOR_DEBUG LOGD("[Audio] Buffer underflow, inserting silence: %" PRIu32 " samples", silence); -#endif memset(out + TO_BYTES(read), 0, TO_BYTES(silence)); bool received = atomic_load_explicit(&ar->received, @@ -141,36 +133,6 @@ bool sc_audio_regulator_push(struct sc_audio_regulator *ar, const AVFrame *frame) { SwrContext *swr_ctx = ar->swr_ctx; - uint32_t input_samples = frame->nb_samples; - - assert(frame->pts >= 0); - int64_t pts = frame->pts; - if (ar->next_expected_pts && pts - ar->next_expected_pts > 100000) { - LOGV("[Audio] Discontinuity detected: %" PRIi64 "µs", - pts - ar->next_expected_pts); - // More than 100ms: consider it as a discontinuity - // (typically because silence packets were not captured) - uint32_t can_read = sc_audiobuf_can_read(&ar->buf); - if (input_samples + can_read < ar->target_buffering) { - // Adjust buffering to the target value directly - uint32_t silence = ar->target_buffering - can_read - input_samples; - sc_audiobuf_write_silence(&ar->buf, silence); - } - - // Reset state - ar->avg_buffering.avg = ar->target_buffering; - int ret = swr_set_compensation(swr_ctx, 0, 0); - (void) ret; - assert(!ret); // disabling compensation should never fail - ar->compensation_active = false; - ar->samples_since_resync = 0; - atomic_store_explicit(&ar->underflow, 0, memory_order_relaxed); - } - - int64_t packet_duration = input_samples * INT64_C(1000000) - / ar->sample_rate; - ar->next_expected_pts = pts + packet_duration; - int64_t swr_delay = swr_get_delay(swr_ctx, ar->sample_rate); // No need to av_rescale_rnd(), input and output sample rates are the same. // Add more space (256) for clock compensation. @@ -243,7 +205,6 @@ sc_audio_regulator_push(struct sc_audio_regulator *ar, const AVFrame *frame) { if (played) { underflow = atomic_exchange_explicit(&ar->underflow, 0, memory_order_relaxed); - ar->underflow_report += underflow; max_buffered_samples = ar->target_buffering * 11 / 10 + 60 * ar->sample_rate / 1000 /* 60 ms */; @@ -290,7 +251,7 @@ sc_audio_regulator_push(struct sc_audio_regulator *ar, const AVFrame *frame) { } // Number of samples added (or removed, if negative) for compensation - int32_t instant_compensation = (int32_t) written - input_samples; + int32_t instant_compensation = (int32_t) written - frame->nb_samples; // Inserting silence instantly increases buffering int32_t inserted_silence = (int32_t) underflow; // Dropping input samples instantly decreases buffering @@ -346,9 +307,7 @@ sc_audio_regulator_push(struct sc_audio_regulator *ar, const AVFrame *frame) { int abs_max_diff = distance / 50; diff = CLAMP(diff, -abs_max_diff, abs_max_diff); LOGV("[Audio] Buffering: target=%" PRIu32 " avg=%f cur=%" PRIu32 - " compensation=%d (underflow=%" PRIu32 ")", - ar->target_buffering, avg, can_read, diff, ar->underflow_report); - ar->underflow_report = 0; + " compensation=%d", ar->target_buffering, avg, can_read, diff); int ret = swr_set_compensation(swr_ctx, diff, distance); if (ret < 0) { @@ -431,9 +390,7 @@ sc_audio_regulator_init(struct sc_audio_regulator *ar, size_t sample_size, atomic_init(&ar->played, false); atomic_init(&ar->received, false); atomic_init(&ar->underflow, 0); - ar->underflow_report = 0; ar->compensation_active = false; - ar->next_expected_pts = 0; return true; diff --git a/app/src/audio_regulator.h b/app/src/audio_regulator.h index 4e18fe08..1c0eeb9f 100644 --- a/app/src/audio_regulator.h +++ b/app/src/audio_regulator.h @@ -5,8 +5,6 @@ #include #include -#include -#include #include #include #include "util/audiobuf.h" @@ -46,9 +44,6 @@ struct sc_audio_regulator { // Number of silence samples inserted since the last received packet atomic_uint_least32_t underflow; - // Number of silence samples inserted since the last log - uint32_t underflow_report; - // Non-zero compensation applied (only used by the receiver thread) bool compensation_active; @@ -57,9 +52,6 @@ struct sc_audio_regulator { // Set to true the first time samples are pulled by the player atomic_bool played; - - // PTS of the next expected packet (useful to detect discontinuities) - int64_t next_expected_pts; }; bool diff --git a/app/src/cli.c b/app/src/cli.c index b2e3e30a..ed1970d4 100644 --- a/app/src/cli.c +++ b/app/src/cli.c @@ -5,7 +5,6 @@ #include #include #include -#include #include #include "options.h" @@ -14,7 +13,6 @@ #include "util/str.h" #include "util/strbuf.h" #include "util/term.h" -#include "util/tick.h" #define STR_IMPL_(x) #x #define STR(x) STR_IMPL_(x) @@ -113,7 +111,6 @@ enum { OPT_ANGLE, OPT_NO_VD_SYSTEM_DECORATIONS, OPT_NO_VD_DESTROY_CONTENT, - OPT_DISPLAY_IME_POLICY, }; struct sc_option { @@ -217,31 +214,13 @@ static const struct sc_option options[] = { .longopt_id = OPT_AUDIO_SOURCE, .longopt = "audio-source", .argdesc = "source", - .text = "Select the audio source. Possible values are:\n" - " - \"output\": forwards the whole audio output, and disables " - "playback on the device.\n" - " - \"playback\": captures the audio playback (Android apps " - "can opt-out, so the whole output is not necessarily " + .text = "Select the audio source (output, mic or playback).\n" + "The \"output\" source forwards the whole audio output, and " + "disables playback on the device.\n" + "The \"playback\" source captures the audio playback (Android " + "apps can opt-out, so the whole output is not necessarily " "captured).\n" - " - \"mic\": captures the microphone.\n" - " - \"mic-unprocessed\": captures the microphone unprocessed " - "(raw) sound.\n" - " - \"mic-camcorder\": captures the microphone tuned for video " - "recording, with the same orientation as the camera if " - "available.\n" - " - \"mic-voice-recognition\": captures the microphone tuned " - "for voice recognition.\n" - " - \"mic-voice-communication\": captures the microphone tuned " - "for voice communications (it will for instance take advantage " - "of echo cancellation or automatic gain control if " - "available).\n" - " - \"voice-call\": captures voice call.\n" - " - \"voice-call-uplink\": captures voice call uplink only.\n" - " - \"voice-call-downlink\": captures voice call downlink " - "only.\n" - " - \"voice-performance\": captures audio meant to be " - "processed for live performance (karaoke), includes both the " - "microphone and the device playback.\n" + "The \"mic\" source captures the microphone.\n" "Default is output.", }, { @@ -385,19 +364,6 @@ static const struct sc_option options[] = { " scrcpy --list-displays\n" "Default is 0.", }, - { - .longopt_id = OPT_DISPLAY_IME_POLICY, - .longopt = "display-ime-policy", - .argdesc = "value", - .text = "Set the policy for selecting where the IME should be " - "displayed.\n" - "Possible values are \"local\", \"fallback\" and \"hide\".\n" - "\"local\" means that the IME should appear on the local " - "display.\n" - "\"fallback\" means that the IME should appear on a fallback " - "display (the default display).\n" - "\"hide\" means that the IME should be hidden.", - }, { .longopt_id = OPT_DISPLAY_ORIENTATION, .longopt = "display-orientation", @@ -721,7 +687,8 @@ static const struct sc_option options[] = { { .longopt_id = OPT_NO_WINDOW, .longopt = "no-window", - .text = "Disable scrcpy window. Implies --no-video-playback.", + .text = "Disable scrcpy window. Implies --no-video-playback and " + "--no-control.", }, { .longopt_id = OPT_ORIENTATION, @@ -1646,25 +1613,6 @@ parse_audio_output_buffer(const char *s, sc_tick *tick) { return true; } -static bool -parse_display_ime_policy(const char *s, enum sc_display_ime_policy *policy) { - if (!strcmp(s, "local")) { - *policy = SC_DISPLAY_IME_POLICY_LOCAL; - return true; - } - if (!strcmp(s, "fallback")) { - *policy = SC_DISPLAY_IME_POLICY_FALLBACK; - return true; - } - if (!strcmp(s, "hide")) { - *policy = SC_DISPLAY_IME_POLICY_HIDE; - return true; - } - LOGE("Unsupported display IME policy: %s (expected local, fallback or " - "hide)", s); - return false; -} - static bool parse_orientation(const char *s, enum sc_orientation *orientation) { if (!strcmp(s, "0")) { @@ -2054,50 +2002,8 @@ parse_audio_source(const char *optarg, enum sc_audio_source *source) { return true; } - if (!strcmp(optarg, "mic-unprocessed")) { - *source = SC_AUDIO_SOURCE_MIC_UNPROCESSED; - return true; - } - - if (!strcmp(optarg, "mic-camcorder")) { - *source = SC_AUDIO_SOURCE_MIC_CAMCORDER; - return true; - } - - if (!strcmp(optarg, "mic-voice-recognition")) { - *source = SC_AUDIO_SOURCE_MIC_VOICE_RECOGNITION; - return true; - } - - if (!strcmp(optarg, "mic-voice-communication")) { - *source = SC_AUDIO_SOURCE_MIC_VOICE_COMMUNICATION; - return true; - } - - if (!strcmp(optarg, "voice-call")) { - *source = SC_AUDIO_SOURCE_VOICE_CALL; - return true; - } - - if (!strcmp(optarg, "voice-call-uplink")) { - *source = SC_AUDIO_SOURCE_VOICE_CALL_UPLINK; - return true; - } - - if (!strcmp(optarg, "voice-call-downlink")) { - *source = SC_AUDIO_SOURCE_VOICE_CALL_DOWNLINK; - return true; - } - - if (!strcmp(optarg, "voice-performance")) { - *source = SC_AUDIO_SOURCE_VOICE_PERFORMANCE; - return true; - } - - LOGE("Unsupported audio source: %s (expected output, mic, playback, " - "mic-unprocessed, mic-camcorder, mic-voice-recognition, " - "mic-voice-communication, voice-call, voice-call-uplink, " - "voice-call-downlink, voice-performance)", optarg); + LOGE("Unsupported audio source: %s (expected output, mic or playback)", + optarg); return false; } @@ -2815,12 +2721,6 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[], case OPT_NO_VD_SYSTEM_DECORATIONS: opts->vd_system_decorations = false; break; - case OPT_DISPLAY_IME_POLICY: - if (!parse_display_ime_policy(optarg, - &opts->display_ime_policy)) { - return false; - } - break; default: // getopt prints the error message on stderr return false; @@ -2859,10 +2759,9 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[], #endif if (!opts->window) { - // Without window, there cannot be any video playback + // Without window, there cannot be any video playback or control opts->video_playback = false; - // Controls are still possible, allowing for options like - // --turn-screen-off + opts->control = false; } if (!opts->video) { @@ -3077,12 +2976,6 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[], return false; } - if (opts->display_ime_policy != SC_DISPLAY_IME_POLICY_UNDEFINED) { - LOGE("--display-ime-policy is only available with " - "--video-source=display"); - return false; - } - if (opts->camera_id && opts->camera_facing != SC_CAMERA_FACING_ANY) { LOGE("Cannot specify both --camera-id and --camera-facing"); return false; @@ -3124,12 +3017,6 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[], return false; } - if (opts->display_ime_policy != SC_DISPLAY_IME_POLICY_UNDEFINED - && opts->display_id == 0 && !opts->new_display) { - LOGE("--display-ime-policy is only supported on a secondary display"); - return false; - } - if (opts->audio && opts->audio_source == SC_AUDIO_SOURCE_AUTO) { // Select the audio source according to the video source if (opts->video_source == SC_VIDEO_SOURCE_DISPLAY) { diff --git a/app/src/compat.h b/app/src/compat.h index 296d1a9f..1995d384 100644 --- a/app/src/compat.h +++ b/app/src/compat.h @@ -75,14 +75,6 @@ # define SCRCPY_SDL_HAS_THREAD_PRIORITY_TIME_CRITICAL #endif -#if SDL_VERSION_ATLEAST(2, 0, 18) -# define SCRCPY_SDL_HAS_HINT_APP_NAME -#endif - -#if SDL_VERSION_ATLEAST(2, 0, 14) -# define SCRCPY_SDL_HAS_HINT_AUDIO_DEVICE_APP_NAME -#endif - #ifndef HAVE_STRDUP char *strdup(const char *s); #endif diff --git a/app/src/control_msg.c b/app/src/control_msg.c index e46c6165..e78f0c57 100644 --- a/app/src/control_msg.c +++ b/app/src/control_msg.c @@ -127,14 +127,10 @@ sc_control_msg_serialize(const struct sc_control_msg *msg, uint8_t *buf) { return 32; case SC_CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT: write_position(&buf[1], &msg->inject_scroll_event.position); - // Accept values in the range [-16, 16]. - // Normalize to [-1, 1] in order to use sc_float_to_i16fp(). - float hscroll_norm = msg->inject_scroll_event.hscroll / 16; - hscroll_norm = CLAMP(hscroll_norm, -1, 1); - float vscroll_norm = msg->inject_scroll_event.vscroll / 16; - vscroll_norm = CLAMP(vscroll_norm, -1, 1); - int16_t hscroll = sc_float_to_i16fp(hscroll_norm); - int16_t vscroll = sc_float_to_i16fp(vscroll_norm); + int16_t hscroll = + sc_float_to_i16fp(msg->inject_scroll_event.hscroll); + int16_t vscroll = + sc_float_to_i16fp(msg->inject_scroll_event.vscroll); sc_write16be(&buf[13], (uint16_t) hscroll); sc_write16be(&buf[15], (uint16_t) vscroll); sc_write32be(&buf[17], msg->inject_scroll_event.buttons); diff --git a/app/src/decoder.c b/app/src/decoder.c index 4d0a1daf..5d42b8b0 100644 --- a/app/src/decoder.c +++ b/app/src/decoder.c @@ -1,9 +1,11 @@ #include "decoder.h" -#include -#include -#include +#include +#include +#include +#include "events.h" +#include "trait/frame_sink.h" #include "util/log.h" /** Downcast packet_sink to decoder */ diff --git a/app/src/decoder.h b/app/src/decoder.h index 1f525fae..ba8903f4 100644 --- a/app/src/decoder.h +++ b/app/src/decoder.h @@ -3,11 +3,13 @@ #include "common.h" -#include - #include "trait/frame_source.h" #include "trait/packet_sink.h" +#include +#include +#include + struct sc_decoder { struct sc_packet_sink packet_sink; // packet sink trait struct sc_frame_source frame_source; // frame source trait diff --git a/app/src/delay_buffer.c b/app/src/delay_buffer.c index f75c6f72..e89a2092 100644 --- a/app/src/delay_buffer.c +++ b/app/src/delay_buffer.c @@ -2,7 +2,9 @@ #include #include -#include + +#include +#include #include "util/log.h" diff --git a/app/src/delay_buffer.h b/app/src/delay_buffer.h index 61cd77e4..18c1ce94 100644 --- a/app/src/delay_buffer.h +++ b/app/src/delay_buffer.h @@ -4,7 +4,6 @@ #include "common.h" #include -#include #include "clock.h" #include "trait/frame_source.h" diff --git a/app/src/demuxer.c b/app/src/demuxer.c index 885cd6ee..7223b553 100644 --- a/app/src/demuxer.c +++ b/app/src/demuxer.c @@ -1,11 +1,14 @@ #include "demuxer.h" #include -#include -#include #include +#include +#include +#include "decoder.h" +#include "events.h" #include "packet_merger.h" +#include "recorder.h" #include "util/binary.h" #include "util/log.h" diff --git a/app/src/demuxer.h b/app/src/demuxer.h index 2b7cb703..5587d12d 100644 --- a/app/src/demuxer.h +++ b/app/src/demuxer.h @@ -4,8 +4,12 @@ #include "common.h" #include +#include +#include +#include #include "trait/packet_source.h" +#include "trait/packet_sink.h" #include "util/net.h" #include "util/thread.h" diff --git a/app/src/device_msg.h b/app/src/device_msg.h index d6c701bb..86b2ccb7 100644 --- a/app/src/device_msg.h +++ b/app/src/device_msg.h @@ -3,9 +3,9 @@ #include "common.h" -#include +#include #include -#include +#include #define DEVICE_MSG_MAX_SIZE (1 << 18) // 256k // type: 1 byte; length: 4 bytes diff --git a/app/src/display.c b/app/src/display.c index aee8ef80..39018834 100644 --- a/app/src/display.c +++ b/app/src/display.c @@ -1,8 +1,6 @@ #include "display.h" #include -#include -#include #include #include "util/log.h" diff --git a/app/src/display.h b/app/src/display.h index 4de9b0a9..064bb7bf 100644 --- a/app/src/display.h +++ b/app/src/display.h @@ -4,8 +4,7 @@ #include "common.h" #include -#include -#include +#include #include #include "coords.h" diff --git a/app/src/events.c b/app/src/events.c index b4322d1b..ce885241 100644 --- a/app/src/events.c +++ b/app/src/events.c @@ -1,7 +1,5 @@ #include "events.h" -#include - #include "util/log.h" #include "util/thread.h" diff --git a/app/src/file_pusher.c b/app/src/file_pusher.c index 681fb5d6..06911052 100644 --- a/app/src/file_pusher.c +++ b/app/src/file_pusher.c @@ -1,11 +1,11 @@ #include "file_pusher.h" #include -#include #include #include "adb/adb.h" #include "util/log.h" +#include "util/process_intr.h" #define DEFAULT_PUSH_TARGET "/sdcard/Download/" diff --git a/app/src/fps_counter.c b/app/src/fps_counter.c index 1daa42ba..dd4ae1da 100644 --- a/app/src/fps_counter.c +++ b/app/src/fps_counter.c @@ -1,7 +1,6 @@ #include "fps_counter.h" #include -#include #include "util/log.h" diff --git a/app/src/fps_counter.h b/app/src/fps_counter.h index 3eab461c..e7619271 100644 --- a/app/src/fps_counter.h +++ b/app/src/fps_counter.h @@ -5,9 +5,9 @@ #include #include +#include #include "util/thread.h" -#include "util/tick.h" struct sc_fps_counter { sc_thread thread; diff --git a/app/src/frame_buffer.c b/app/src/frame_buffer.c index 9fd4cf6f..5699b58f 100644 --- a/app/src/frame_buffer.c +++ b/app/src/frame_buffer.c @@ -1,6 +1,8 @@ #include "frame_buffer.h" #include +#include +#include #include "util/log.h" diff --git a/app/src/frame_buffer.h b/app/src/frame_buffer.h index e748adfb..f97261cd 100644 --- a/app/src/frame_buffer.h +++ b/app/src/frame_buffer.h @@ -4,7 +4,6 @@ #include "common.h" #include -#include #include "util/thread.h" diff --git a/app/src/hid/hid_event.h b/app/src/hid/hid_event.h index b0d45ce8..d6818e30 100644 --- a/app/src/hid/hid_event.h +++ b/app/src/hid/hid_event.h @@ -3,7 +3,6 @@ #include "common.h" -#include #include #define SC_HID_MAX_SIZE 15 diff --git a/app/src/hid/hid_gamepad.c b/app/src/hid/hid_gamepad.c index 842eae9e..8f4e4527 100644 --- a/app/src/hid/hid_gamepad.c +++ b/app/src/hid/hid_gamepad.c @@ -2,8 +2,6 @@ #include #include -#include -#include #include "util/binary.h" #include "util/log.h" diff --git a/app/src/hid/hid_gamepad.h b/app/src/hid/hid_gamepad.h index 8d939ac7..b532a703 100644 --- a/app/src/hid/hid_gamepad.h +++ b/app/src/hid/hid_gamepad.h @@ -4,7 +4,6 @@ #include "common.h" #include -#include #include "hid/hid_event.h" #include "input_events.h" diff --git a/app/src/hid/hid_keyboard.c b/app/src/hid/hid_keyboard.c index 6477396a..961ad790 100644 --- a/app/src/hid/hid_keyboard.c +++ b/app/src/hid/hid_keyboard.c @@ -1,6 +1,5 @@ #include "hid_keyboard.h" -#include #include #include "util/log.h" diff --git a/app/src/hid/hid_keyboard.h b/app/src/hid/hid_keyboard.h index 5ecfd8cf..cde1ac52 100644 --- a/app/src/hid/hid_keyboard.h +++ b/app/src/hid/hid_keyboard.h @@ -4,7 +4,6 @@ #include "common.h" #include -#include #include "hid/hid_event.h" #include "input_events.h" diff --git a/app/src/hid/hid_mouse.c b/app/src/hid/hid_mouse.c index 33f0807e..7acc413b 100644 --- a/app/src/hid/hid_mouse.c +++ b/app/src/hid/hid_mouse.c @@ -1,10 +1,8 @@ #include "hid_mouse.h" -#include - // 1 byte for buttons + padding, 1 byte for X position, 1 byte for Y position, -// 1 byte for wheel motion, 1 byte for hozizontal scrolling -#define SC_HID_MOUSE_INPUT_SIZE 5 +// 1 byte for wheel motion +#define SC_HID_MOUSE_INPUT_SIZE 4 /** * Mouse descriptor from the specification: @@ -75,21 +73,6 @@ static const uint8_t SC_HID_MOUSE_REPORT_DESC[] = { // Input (Data, Variable, Relative): 3 position bytes (X, Y, Wheel) 0x81, 0x06, - // Usage Page (Consumer Page) - 0x05, 0x0C, - // Usage(AC Pan) - 0x0A, 0x38, 0x02, - // Logical Minimum (-127) - 0x15, 0x81, - // Logical Maximum (127) - 0x25, 0x7F, - // Report Size (8) - 0x75, 0x08, - // Report Count (1) - 0x95, 0x01, - // Input (Data, Variable, Relative): 1 byte (AC Pan) - 0x81, 0x06, - // End Collection 0xC0, @@ -175,8 +158,7 @@ sc_hid_mouse_generate_input_from_motion(struct sc_hid_input *hid_input, data[0] = sc_hid_buttons_from_buttons_state(event->buttons_state); data[1] = CLAMP(event->xrel, -127, 127); data[2] = CLAMP(event->yrel, -127, 127); - data[3] = 0; // no vertical scrolling - data[4] = 0; // no horizontal scrolling + data[3] = 0; // wheel coordinates only used for scrolling } void @@ -188,27 +170,22 @@ sc_hid_mouse_generate_input_from_click(struct sc_hid_input *hid_input, data[0] = sc_hid_buttons_from_buttons_state(event->buttons_state); data[1] = 0; // no x motion data[2] = 0; // no y motion - data[3] = 0; // no vertical scrolling - data[4] = 0; // no horizontal scrolling + data[3] = 0; // wheel coordinates only used for scrolling } -bool +void sc_hid_mouse_generate_input_from_scroll(struct sc_hid_input *hid_input, const struct sc_mouse_scroll_event *event) { - if (!event->vscroll_int && !event->hscroll_int) { - // Need a full integral value for HID - return false; - } - sc_hid_mouse_input_init(hid_input); uint8_t *data = hid_input->data; data[0] = 0; // buttons state irrelevant (and unknown) data[1] = 0; // no x motion data[2] = 0; // no y motion - data[3] = CLAMP(event->vscroll_int, -127, 127); - data[4] = CLAMP(event->hscroll_int, -127, 127); - return true; + // In practice, vscroll is always -1, 0 or 1, but in theory other values + // are possible + data[3] = CLAMP(event->vscroll, -127, 127); + // Horizontal scrolling ignored } void sc_hid_mouse_generate_open(struct sc_hid_open *hid_open) { diff --git a/app/src/hid/hid_mouse.h b/app/src/hid/hid_mouse.h index 4ae4bfd4..a9a54718 100644 --- a/app/src/hid/hid_mouse.h +++ b/app/src/hid/hid_mouse.h @@ -3,6 +3,8 @@ #include "common.h" +#include + #include "hid/hid_event.h" #include "input_events.h" @@ -22,7 +24,7 @@ void sc_hid_mouse_generate_input_from_click(struct sc_hid_input *hid_input, const struct sc_mouse_click_event *event); -bool +void sc_hid_mouse_generate_input_from_scroll(struct sc_hid_input *hid_input, const struct sc_mouse_scroll_event *event); diff --git a/app/src/icon.c b/app/src/icon.c index 797afc75..4f3a9a39 100644 --- a/app/src/icon.c +++ b/app/src/icon.c @@ -2,22 +2,17 @@ #include #include -#include -#include -#include #include #include -#include #include #include -#include #include "config.h" +#include "compat.h" #include "util/env.h" -#ifdef PORTABLE -# include "util/file.h" -#endif +#include "util/file.h" #include "util/log.h" +#include "util/str.h" #define SCRCPY_PORTABLE_ICON_FILENAME "icon.png" #define SCRCPY_DEFAULT_ICON_PATH \ diff --git a/app/src/icon.h b/app/src/icon.h index 6bcf46d2..3251e48f 100644 --- a/app/src/icon.h +++ b/app/src/icon.h @@ -3,7 +3,9 @@ #include "common.h" -#include +#include +#include +#include SDL_Surface * scrcpy_icon_load(void); diff --git a/app/src/input_events.h b/app/src/input_events.h index 1e34b50e..ad3afa81 100644 --- a/app/src/input_events.h +++ b/app/src/input_events.h @@ -9,6 +9,7 @@ #include #include "coords.h" +#include "options.h" /* The representation of input events in scrcpy is very close to the SDL API, * for simplicity. @@ -393,8 +394,6 @@ struct sc_mouse_scroll_event { struct sc_position position; float hscroll; float vscroll; - int32_t hscroll_int; - int32_t vscroll_int; uint8_t buttons_state; // bitwise-OR of sc_mouse_button values }; diff --git a/app/src/input_manager.c b/app/src/input_manager.c index 3e4dd0f3..2e4337db 100644 --- a/app/src/input_manager.c +++ b/app/src/input_manager.c @@ -1,12 +1,8 @@ #include "input_manager.h" #include -#include -#include -#include +#include -#include "android/input.h" -#include "android/keycodes.h" #include "input_events.h" #include "screen.h" #include "shortcut_mod.h" @@ -897,14 +893,12 @@ sc_input_manager_process_mouse_wheel(struct sc_input_manager *im, struct sc_mouse_scroll_event evt = { .position = sc_input_manager_get_position(im, mouse_x, mouse_y), #if SDL_VERSION_ATLEAST(2, 0, 18) - .hscroll = event->preciseX, - .vscroll = event->preciseY, + .hscroll = CLAMP(event->preciseX, -1.0f, 1.0f), + .vscroll = CLAMP(event->preciseY, -1.0f, 1.0f), #else - .hscroll = event->x, - .vscroll = event->y, + .hscroll = CLAMP(event->x, -1, 1), + .vscroll = CLAMP(event->y, -1, 1), #endif - .hscroll_int = event->x, - .vscroll_int = event->y, .buttons_state = im->mouse_buttons_state, }; diff --git a/app/src/input_manager.h b/app/src/input_manager.h index af4cbc69..8efd0153 100644 --- a/app/src/input_manager.h +++ b/app/src/input_manager.h @@ -4,12 +4,12 @@ #include "common.h" #include -#include -#include -#include + +#include #include "controller.h" #include "file_pusher.h" +#include "fps_counter.h" #include "options.h" #include "trait/gamepad_processor.h" #include "trait/key_processor.h" diff --git a/app/src/keyboard_sdk.c b/app/src/keyboard_sdk.c index 466a1aeb..2d9ca85b 100644 --- a/app/src/keyboard_sdk.c +++ b/app/src/keyboard_sdk.c @@ -1,13 +1,8 @@ #include "keyboard_sdk.h" #include -#include -#include -#include -#include #include "android/input.h" -#include "android/keycodes.h" #include "control_msg.h" #include "controller.h" #include "input_events.h" diff --git a/app/src/main.c b/app/src/main.c index c58e0be7..8bbd074f 100644 --- a/app/src/main.c +++ b/app/src/main.c @@ -1,6 +1,9 @@ #include "common.h" +#include #include +#include +#include #ifdef HAVE_V4L2 # include #endif diff --git a/app/src/mouse_sdk.c b/app/src/mouse_sdk.c index 7eceffa7..a7998972 100644 --- a/app/src/mouse_sdk.c +++ b/app/src/mouse_sdk.c @@ -1,12 +1,12 @@ #include "mouse_sdk.h" #include -#include #include "android/input.h" #include "control_msg.h" #include "controller.h" #include "input_events.h" +#include "util/intmap.h" #include "util/log.h" /** Downcast mouse processor to sc_mouse_sdk */ diff --git a/app/src/mouse_sdk.h b/app/src/mouse_sdk.h index fe92a2d7..142b89bb 100644 --- a/app/src/mouse_sdk.h +++ b/app/src/mouse_sdk.h @@ -6,6 +6,7 @@ #include #include "controller.h" +#include "screen.h" #include "trait/mouse_processor.h" struct sc_mouse_sdk { diff --git a/app/src/opengl.c b/app/src/opengl.c index 0cb83ed7..376690af 100644 --- a/app/src/opengl.c +++ b/app/src/opengl.c @@ -2,8 +2,7 @@ #include #include -#include -#include +#include "SDL2/SDL.h" void sc_opengl_init(struct sc_opengl *gl) { diff --git a/app/src/options.c b/app/src/options.c index 0fe82d29..df8033e9 100644 --- a/app/src/options.c +++ b/app/src/options.c @@ -1,7 +1,5 @@ #include "options.h" -#include - const struct scrcpy_options scrcpy_options_default = { .serial = NULL, .crop = NULL, @@ -56,7 +54,6 @@ const struct scrcpy_options scrcpy_options_default = { .capture_orientation_lock = SC_ORIENTATION_UNLOCKED, .display_orientation = SC_ORIENTATION_0, .record_orientation = SC_ORIENTATION_0, - .display_ime_policy = SC_DISPLAY_IME_POLICY_UNDEFINED, .window_x = SC_WINDOW_POSITION_UNDEFINED, .window_y = SC_WINDOW_POSITION_UNDEFINED, .window_width = 0, diff --git a/app/src/options.h b/app/src/options.h index 03b42913..152881d8 100644 --- a/app/src/options.h +++ b/app/src/options.h @@ -5,6 +5,7 @@ #include #include +#include #include #include "util/tick.h" @@ -59,14 +60,6 @@ enum sc_audio_source { SC_AUDIO_SOURCE_OUTPUT, SC_AUDIO_SOURCE_MIC, SC_AUDIO_SOURCE_PLAYBACK, - SC_AUDIO_SOURCE_MIC_UNPROCESSED, - SC_AUDIO_SOURCE_MIC_CAMCORDER, - SC_AUDIO_SOURCE_MIC_VOICE_RECOGNITION, - SC_AUDIO_SOURCE_MIC_VOICE_COMMUNICATION, - SC_AUDIO_SOURCE_VOICE_CALL, - SC_AUDIO_SOURCE_VOICE_CALL_UPLINK, - SC_AUDIO_SOURCE_VOICE_CALL_DOWNLINK, - SC_AUDIO_SOURCE_VOICE_PERFORMANCE, }; enum sc_camera_facing { @@ -97,13 +90,6 @@ enum sc_orientation_lock { SC_ORIENTATION_LOCKED_INITIAL, // lock to initial device orientation }; -enum sc_display_ime_policy { - SC_DISPLAY_IME_POLICY_UNDEFINED, - SC_DISPLAY_IME_POLICY_LOCAL, - SC_DISPLAY_IME_POLICY_FALLBACK, - SC_DISPLAY_IME_POLICY_HIDE, -}; - static inline bool sc_orientation_is_mirror(enum sc_orientation orientation) { assert(!(orientation & ~7)); @@ -266,7 +252,6 @@ struct scrcpy_options { enum sc_orientation_lock capture_orientation_lock; enum sc_orientation display_orientation; enum sc_orientation record_orientation; - enum sc_display_ime_policy display_ime_policy; int16_t window_x; // SC_WINDOW_POSITION_UNDEFINED for "auto" int16_t window_y; // SC_WINDOW_POSITION_UNDEFINED for "auto" uint16_t window_width; diff --git a/app/src/packet_merger.c b/app/src/packet_merger.c index dea038b6..81b02d2c 100644 --- a/app/src/packet_merger.c +++ b/app/src/packet_merger.c @@ -1,9 +1,5 @@ #include "packet_merger.h" -#include -#include -#include - #include "util/log.h" void diff --git a/app/src/packet_merger.h b/app/src/packet_merger.h index 3f9972ce..e1824c2c 100644 --- a/app/src/packet_merger.h +++ b/app/src/packet_merger.h @@ -5,7 +5,7 @@ #include #include -#include +#include /** * Config packets (containing the SPS/PPS) are sent in-band. A new config diff --git a/app/src/receiver.c b/app/src/receiver.c index 2ccb8a8b..b89b0c6e 100644 --- a/app/src/receiver.c +++ b/app/src/receiver.c @@ -2,6 +2,7 @@ #include #include +#include #include #include "device_msg.h" diff --git a/app/src/recorder.c b/app/src/recorder.c index c26f8f2d..15f27157 100644 --- a/app/src/recorder.c +++ b/app/src/recorder.c @@ -1,9 +1,6 @@ #include "recorder.h" #include -#include -#include -#include #include #include #include diff --git a/app/src/recorder.h b/app/src/recorder.h index 70b73836..d096e79a 100644 --- a/app/src/recorder.h +++ b/app/src/recorder.h @@ -4,10 +4,9 @@ #include "common.h" #include -#include -#include #include +#include "coords.h" #include "options.h" #include "trait/packet_sink.h" #include "util/thread.h" diff --git a/app/src/scrcpy.c b/app/src/scrcpy.c index a4c8c340..f1942e43 100644 --- a/app/src/scrcpy.c +++ b/app/src/scrcpy.c @@ -1,11 +1,10 @@ #include "scrcpy.h" -#include -#include -#include #include -#include #include +#include +#include +#include #include #ifdef _WIN32 @@ -38,9 +37,9 @@ #endif #include "util/acksync.h" #include "util/log.h" +#include "util/net.h" #include "util/rand.h" #include "util/timeout.h" -#include "util/tick.h" #ifdef HAVE_V4L2 # include "v4l2_sink.h" #endif @@ -107,17 +106,6 @@ sdl_set_hints(const char *render_driver) { LOGW("Could not set render driver"); } - // App name used in various contexts (such as PulseAudio) -#if defined(SCRCPY_SDL_HAS_HINT_APP_NAME) - if (!SDL_SetHint(SDL_HINT_APP_NAME, "scrcpy")) { - LOGW("Could not set app name"); - } -#elif defined(SCRCPY_SDL_HAS_HINT_AUDIO_DEVICE_APP_NAME) - if (!SDL_SetHint(SDL_HINT_AUDIO_DEVICE_APP_NAME, "scrcpy")) { - LOGW("Could not set audio device app name"); - } -#endif - // Linear filtering if (!SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1")) { LOGW("Could not enable linear filtering"); @@ -176,7 +164,7 @@ sdl_configure(bool video_playback, bool disable_screensaver) { } static enum scrcpy_exit_code -event_loop(struct scrcpy *s, bool has_screen) { +event_loop(struct scrcpy *s) { SDL_Event event; while (SDL_WaitEvent(&event)) { switch (event.type) { @@ -208,7 +196,7 @@ event_loop(struct scrcpy *s, bool has_screen) { break; } default: - if (has_screen && !sc_screen_handle_event(&s->screen, &event)) { + if (!sc_screen_handle_event(&s->screen, &event)) { return SCRCPY_EXIT_FAILURE; } break; @@ -447,7 +435,6 @@ scrcpy(struct scrcpy_options *options) { .control = options->control, .display_id = options->display_id, .new_display = options->new_display, - .display_ime_policy = options->display_ime_policy, .video = options->video, .audio = options->audio, .audio_dup = options->audio_dup, @@ -944,7 +931,7 @@ aoa_complete: } } - ret = event_loop(s, options->window); + ret = event_loop(s); terminate_event_loop(); LOGD("quit..."); diff --git a/app/src/scrcpy.h b/app/src/scrcpy.h index 7f6a0fb2..d4d494a3 100644 --- a/app/src/scrcpy.h +++ b/app/src/scrcpy.h @@ -3,6 +3,7 @@ #include "common.h" +#include #include "options.h" enum scrcpy_exit_code { diff --git a/app/src/screen.h b/app/src/screen.h index 6621b2d2..c716c399 100644 --- a/app/src/screen.h +++ b/app/src/screen.h @@ -1,14 +1,11 @@ -#ifndef SC_SCREEN_H -#define SC_SCREEN_H +#ifndef SCREEN_H +#define SCREEN_H #include "common.h" #include -#include #include -#include -#include -#include +#include #include "controller.h" #include "coords.h" @@ -17,6 +14,7 @@ #include "frame_buffer.h" #include "input_manager.h" #include "mouse_capture.h" +#include "opengl.h" #include "options.h" #include "trait/key_processor.h" #include "trait/frame_sink.h" diff --git a/app/src/server.c b/app/src/server.c index 153219c3..22ddd372 100644 --- a/app/src/server.c +++ b/app/src/server.c @@ -1,18 +1,19 @@ #include "server.h" #include +#include #include #include -#include -#include -#include +#include +#include #include "adb/adb.h" +#include "util/binary.h" #include "util/env.h" #include "util/file.h" #include "util/log.h" #include "util/net_intr.h" -#include "util/process.h" +#include "util/process_intr.h" #include "util/str.h" #define SC_SERVER_FILENAME "scrcpy-server" @@ -149,43 +150,12 @@ sc_server_get_audio_source_name(enum sc_audio_source audio_source) { return "mic"; case SC_AUDIO_SOURCE_PLAYBACK: return "playback"; - case SC_AUDIO_SOURCE_MIC_UNPROCESSED: - return "mic-unprocessed"; - case SC_AUDIO_SOURCE_MIC_CAMCORDER: - return "mic-camcorder"; - case SC_AUDIO_SOURCE_MIC_VOICE_RECOGNITION: - return "mic-voice-recognition"; - case SC_AUDIO_SOURCE_MIC_VOICE_COMMUNICATION: - return "mic-voice-communication"; - case SC_AUDIO_SOURCE_VOICE_CALL: - return "voice-call"; - case SC_AUDIO_SOURCE_VOICE_CALL_UPLINK: - return "voice-call-uplink"; - case SC_AUDIO_SOURCE_VOICE_CALL_DOWNLINK: - return "voice-call-downlink"; - case SC_AUDIO_SOURCE_VOICE_PERFORMANCE: - return "voice-performance"; default: assert(!"unexpected audio source"); return NULL; } } -static const char * -sc_server_get_display_ime_policy_name(enum sc_display_ime_policy policy) { - switch (policy) { - case SC_DISPLAY_IME_POLICY_LOCAL: - return "local"; - case SC_DISPLAY_IME_POLICY_FALLBACK: - return "fallback"; - case SC_DISPLAY_IME_POLICY_HIDE: - return "hide"; - default: - assert(!"unexpected display IME policy"); - return NULL; - } -} - static bool validate_string(const char *s) { // The parameters values are passed as command line arguments to adb, so @@ -407,10 +377,6 @@ execute_server(struct sc_server *server, VALIDATE_STRING(params->new_display); ADD_PARAM("new_display=%s", params->new_display); } - if (params->display_ime_policy != SC_DISPLAY_IME_POLICY_UNDEFINED) { - ADD_PARAM("display_ime_policy=%s", - sc_server_get_display_ime_policy_name(params->display_ime_policy)); - } if (!params->vd_destroy_content) { ADD_PARAM("vd_destroy_content=false"); } diff --git a/app/src/server.h b/app/src/server.h index 5f4592de..3c78b9ed 100644 --- a/app/src/server.h +++ b/app/src/server.h @@ -1,17 +1,19 @@ -#ifndef SC_SERVER_H -#define SC_SERVER_H +#ifndef SERVER_H +#define SERVER_H #include "common.h" +#include #include #include #include "adb/adb_tunnel.h" +#include "coords.h" #include "options.h" #include "util/intr.h" +#include "util/log.h" #include "util/net.h" #include "util/thread.h" -#include "util/tick.h" #define SC_DEVICE_NAME_FIELD_LENGTH 64 struct sc_server_info { @@ -50,7 +52,6 @@ struct sc_server_params { bool control; uint32_t display_id; const char *new_display; - enum sc_display_ime_policy display_ime_policy; bool video; bool audio; bool audio_dup; diff --git a/app/src/shortcut_mod.h b/app/src/shortcut_mod.h index f6c13f03..b685e987 100644 --- a/app/src/shortcut_mod.h +++ b/app/src/shortcut_mod.h @@ -3,7 +3,6 @@ #include "common.h" -#include #include #include #include diff --git a/app/src/sys/unix/file.c b/app/src/sys/unix/file.c index 8f7fb074..6123c788 100644 --- a/app/src/sys/unix/file.c +++ b/app/src/sys/unix/file.c @@ -1,11 +1,10 @@ #include "util/file.h" #include -#include #include +#include #include #include -#include #include #ifdef __APPLE__ # include // for _NSGetExecutablePath() diff --git a/app/src/sys/unix/process.c b/app/src/sys/unix/process.c index 36d1ff7d..8c4a53c3 100644 --- a/app/src/sys/unix/process.c +++ b/app/src/sys/unix/process.c @@ -4,8 +4,6 @@ #include #include #include -#include -#include #include #include #include diff --git a/app/src/trait/frame_sink.h b/app/src/trait/frame_sink.h index 67be4d46..8ef248b6 100644 --- a/app/src/trait/frame_sink.h +++ b/app/src/trait/frame_sink.h @@ -3,6 +3,7 @@ #include "common.h" +#include #include #include diff --git a/app/src/trait/frame_source.c b/app/src/trait/frame_source.c index 56848309..416eccd9 100644 --- a/app/src/trait/frame_source.c +++ b/app/src/trait/frame_source.c @@ -1,7 +1,5 @@ #include "frame_source.h" -#include - void sc_frame_source_init(struct sc_frame_source *source) { source->sink_count = 0; diff --git a/app/src/trait/frame_source.h b/app/src/trait/frame_source.h index cb1ef905..94222af0 100644 --- a/app/src/trait/frame_source.h +++ b/app/src/trait/frame_source.h @@ -3,9 +3,7 @@ #include "common.h" -#include - -#include "trait/frame_sink.h" +#include "frame_sink.h" #define SC_FRAME_SOURCE_MAX_SINKS 2 diff --git a/app/src/trait/gamepad_processor.h b/app/src/trait/gamepad_processor.h index 5e8dc2a4..19629a9a 100644 --- a/app/src/trait/gamepad_processor.h +++ b/app/src/trait/gamepad_processor.h @@ -3,6 +3,9 @@ #include "common.h" +#include +#include + #include "input_events.h" /** diff --git a/app/src/trait/key_processor.h b/app/src/trait/key_processor.h index 9e9bb86e..96374413 100644 --- a/app/src/trait/key_processor.h +++ b/app/src/trait/key_processor.h @@ -3,6 +3,7 @@ #include "common.h" +#include #include #include "input_events.h" diff --git a/app/src/trait/mouse_processor.h b/app/src/trait/mouse_processor.h index d0a96e7c..6e0b596e 100644 --- a/app/src/trait/mouse_processor.h +++ b/app/src/trait/mouse_processor.h @@ -3,6 +3,7 @@ #include "common.h" +#include #include #include "input_events.h" diff --git a/app/src/trait/packet_sink.h b/app/src/trait/packet_sink.h index e12dea12..84cfe814 100644 --- a/app/src/trait/packet_sink.h +++ b/app/src/trait/packet_sink.h @@ -3,6 +3,7 @@ #include "common.h" +#include #include #include diff --git a/app/src/trait/packet_source.c b/app/src/trait/packet_source.c index 0a2c6c4d..c0836f1d 100644 --- a/app/src/trait/packet_source.c +++ b/app/src/trait/packet_source.c @@ -1,7 +1,5 @@ #include "packet_source.h" -#include - void sc_packet_source_init(struct sc_packet_source *source) { source->sink_count = 0; diff --git a/app/src/trait/packet_source.h b/app/src/trait/packet_source.h index 8788021a..16d56e86 100644 --- a/app/src/trait/packet_source.h +++ b/app/src/trait/packet_source.h @@ -3,9 +3,7 @@ #include "common.h" -#include - -#include "trait/packet_sink.h" +#include "packet_sink.h" #define SC_PACKET_SOURCE_MAX_SINKS 2 diff --git a/app/src/uhid/gamepad_uhid.c b/app/src/uhid/gamepad_uhid.c index c64feb18..a066cf03 100644 --- a/app/src/uhid/gamepad_uhid.c +++ b/app/src/uhid/gamepad_uhid.c @@ -1,10 +1,5 @@ #include "gamepad_uhid.h" -#include -#include -#include -#include - #include "hid/hid_gamepad.h" #include "input_events.h" #include "util/log.h" diff --git a/app/src/uhid/gamepad_uhid.h b/app/src/uhid/gamepad_uhid.h index ad747604..07d03099 100644 --- a/app/src/uhid/gamepad_uhid.h +++ b/app/src/uhid/gamepad_uhid.h @@ -3,6 +3,8 @@ #include "common.h" +#include + #include "controller.h" #include "hid/hid_gamepad.h" #include "trait/gamepad_processor.h" diff --git a/app/src/uhid/keyboard_uhid.c b/app/src/uhid/keyboard_uhid.c index 70082990..76d70cc5 100644 --- a/app/src/uhid/keyboard_uhid.c +++ b/app/src/uhid/keyboard_uhid.c @@ -1,12 +1,6 @@ #include "keyboard_uhid.h" -#include -#include -#include -#include - #include "util/log.h" -#include "util/thread.h" /** Downcast key processor to keyboard_uhid */ #define DOWNCAST(KP) container_of(KP, struct sc_keyboard_uhid, key_processor) diff --git a/app/src/uhid/mouse_uhid.c b/app/src/uhid/mouse_uhid.c index 869e48a4..471030e7 100644 --- a/app/src/uhid/mouse_uhid.c +++ b/app/src/uhid/mouse_uhid.c @@ -1,8 +1,5 @@ #include "mouse_uhid.h" -#include -#include - #include "hid/hid_mouse.h" #include "input_events.h" #include "util/log.h" @@ -55,9 +52,7 @@ sc_mouse_processor_process_mouse_scroll(struct sc_mouse_processor *mp, struct sc_mouse_uhid *mouse = DOWNCAST(mp); struct sc_hid_input hid_input; - if (!sc_hid_mouse_generate_input_from_scroll(&hid_input, event)) { - return; - } + sc_hid_mouse_generate_input_from_scroll(&hid_input, event); sc_mouse_uhid_send_input(mouse, &hid_input, "mouse scroll"); } diff --git a/app/src/uhid/uhid_output.c b/app/src/uhid/uhid_output.c index e743a73c..05e691da 100644 --- a/app/src/uhid/uhid_output.c +++ b/app/src/uhid/uhid_output.c @@ -1,5 +1,6 @@ #include "uhid_output.h" +#include #include #include "uhid/keyboard_uhid.h" diff --git a/app/src/uhid/uhid_output.h b/app/src/uhid/uhid_output.h index ed028b58..cd6a800f 100644 --- a/app/src/uhid/uhid_output.h +++ b/app/src/uhid/uhid_output.h @@ -3,7 +3,7 @@ #include "common.h" -#include +#include #include /** diff --git a/app/src/usb/aoa_hid.c b/app/src/usb/aoa_hid.c index 8cb62bfd..236a78ed 100644 --- a/app/src/usb/aoa_hid.c +++ b/app/src/usb/aoa_hid.c @@ -1,16 +1,13 @@ -#include "aoa_hid.h" +#include "util/log.h" #include #include #include -#include -#include -#include +#include "aoa_hid.h" #include "events.h" #include "util/log.h" #include "util/str.h" -#include "util/tick.h" #include "util/vector.h" // See . diff --git a/app/src/usb/aoa_hid.h b/app/src/usb/aoa_hid.h index 2755c957..9cc6355e 100644 --- a/app/src/usb/aoa_hid.h +++ b/app/src/usb/aoa_hid.h @@ -3,13 +3,16 @@ #include "common.h" -#include #include +#include + +#include #include "hid/hid_event.h" -#include "usb/usb.h" +#include "usb.h" #include "util/acksync.h" #include "util/thread.h" +#include "util/tick.h" #include "util/vecdeque.h" enum sc_aoa_event_type { diff --git a/app/src/usb/gamepad_aoa.c b/app/src/usb/gamepad_aoa.c index d29b1a78..4372379f 100644 --- a/app/src/usb/gamepad_aoa.c +++ b/app/src/usb/gamepad_aoa.c @@ -1,7 +1,5 @@ #include "gamepad_aoa.h" -#include - #include "input_events.h" #include "util/log.h" diff --git a/app/src/usb/gamepad_aoa.h b/app/src/usb/gamepad_aoa.h index 0297a365..b2dfbe5e 100644 --- a/app/src/usb/gamepad_aoa.h +++ b/app/src/usb/gamepad_aoa.h @@ -3,8 +3,10 @@ #include "common.h" +#include + +#include "aoa_hid.h" #include "hid/hid_gamepad.h" -#include "usb/aoa_hid.h" #include "trait/gamepad_processor.h" struct sc_gamepad_aoa { diff --git a/app/src/usb/keyboard_aoa.h b/app/src/usb/keyboard_aoa.h index 9e9500a3..565b9177 100644 --- a/app/src/usb/keyboard_aoa.h +++ b/app/src/usb/keyboard_aoa.h @@ -5,8 +5,8 @@ #include +#include "aoa_hid.h" #include "hid/hid_keyboard.h" -#include "usb/aoa_hid.h" #include "trait/key_processor.h" struct sc_keyboard_aoa { diff --git a/app/src/usb/mouse_aoa.c b/app/src/usb/mouse_aoa.c index fd5fa5e0..cb566cc0 100644 --- a/app/src/usb/mouse_aoa.c +++ b/app/src/usb/mouse_aoa.c @@ -1,7 +1,6 @@ #include "mouse_aoa.h" #include -#include #include "hid/hid_mouse.h" #include "input_events.h" @@ -42,9 +41,7 @@ sc_mouse_processor_process_mouse_scroll(struct sc_mouse_processor *mp, struct sc_mouse_aoa *mouse = DOWNCAST(mp); struct sc_hid_input hid_input; - if (!sc_hid_mouse_generate_input_from_scroll(&hid_input, event)) { - return; - } + sc_hid_mouse_generate_input_from_scroll(&hid_input, event); if (!sc_aoa_push_input(mouse->aoa, &hid_input)) { LOGW("Could not push AOA HID input (mouse scroll)"); diff --git a/app/src/usb/mouse_aoa.h b/app/src/usb/mouse_aoa.h index 506286ba..afaed761 100644 --- a/app/src/usb/mouse_aoa.h +++ b/app/src/usb/mouse_aoa.h @@ -5,7 +5,7 @@ #include -#include "usb/aoa_hid.h" +#include "aoa_hid.h" #include "trait/mouse_processor.h" struct sc_mouse_aoa { diff --git a/app/src/usb/scrcpy_otg.c b/app/src/usb/scrcpy_otg.c index 1a9cc46e..6ef2fc2a 100644 --- a/app/src/usb/scrcpy_otg.c +++ b/app/src/usb/scrcpy_otg.c @@ -1,19 +1,10 @@ #include "scrcpy_otg.h" -#include -#include -#include #include -#ifdef _WIN32 -# include "adb/adb.h" -#endif +#include "adb/adb.h" #include "events.h" -#include "usb/screen_otg.h" -#include "usb/aoa_hid.h" -#include "usb/gamepad_aoa.h" -#include "usb/keyboard_aoa.h" -#include "usb/mouse_aoa.h" +#include "screen_otg.h" #include "util/log.h" struct scrcpy_otg { diff --git a/app/src/usb/screen_otg.c b/app/src/usb/screen_otg.c index 5c580df9..368af125 100644 --- a/app/src/usb/screen_otg.c +++ b/app/src/usb/screen_otg.c @@ -1,11 +1,7 @@ #include "screen_otg.h" -#include -#include - #include "icon.h" #include "options.h" -#include "util/acksync.h" #include "util/log.h" static void @@ -164,15 +160,8 @@ sc_screen_otg_process_mouse_wheel(struct sc_screen_otg *screen, struct sc_mouse_scroll_event evt = { // .position not used for HID events -#if SDL_VERSION_ATLEAST(2, 0, 18) - .hscroll = event->preciseX, - .vscroll = event->preciseY, -#else .hscroll = event->x, .vscroll = event->y, -#endif - .hscroll_int = event->x, - .vscroll_int = event->y, .buttons_state = sc_mouse_buttons_state_from_sdl(sdl_buttons_state), }; diff --git a/app/src/usb/screen_otg.h b/app/src/usb/screen_otg.h index 08b76ae7..427723ad 100644 --- a/app/src/usb/screen_otg.h +++ b/app/src/usb/screen_otg.h @@ -4,13 +4,12 @@ #include "common.h" #include -#include #include +#include "keyboard_aoa.h" +#include "mouse_aoa.h" #include "mouse_capture.h" -#include "usb/gamepad_aoa.h" -#include "usb/keyboard_aoa.h" -#include "usb/mouse_aoa.h" +#include "gamepad_aoa.h" struct sc_screen_otg { struct sc_keyboard_aoa *keyboard; diff --git a/app/src/util/acksync.c b/app/src/util/acksync.c index 76ecee0d..2899cdcb 100644 --- a/app/src/util/acksync.c +++ b/app/src/util/acksync.c @@ -1,6 +1,7 @@ #include "acksync.h" #include +#include "util/log.h" bool sc_acksync_init(struct sc_acksync *as) { diff --git a/app/src/util/acksync.h b/app/src/util/acksync.h index 3d9c9b2f..58ab1b35 100644 --- a/app/src/util/acksync.h +++ b/app/src/util/acksync.h @@ -3,10 +3,7 @@ #include "common.h" -#include -#include -#include "util/thread.h" -#include "util/tick.h" +#include "thread.h" #define SC_SEQUENCE_INVALID 0 diff --git a/app/src/util/audiobuf.c b/app/src/util/audiobuf.c index eeb27514..3cc5cad1 100644 --- a/app/src/util/audiobuf.c +++ b/app/src/util/audiobuf.c @@ -116,38 +116,3 @@ sc_audiobuf_write(struct sc_audiobuf *buf, const void *from_, return samples_count; } - -uint32_t -sc_audiobuf_write_silence(struct sc_audiobuf *buf, uint32_t samples_count) { - // Only the writer thread can write head, so memory_order_relaxed is - // sufficient - uint32_t head = atomic_load_explicit(&buf->head, memory_order_relaxed); - - // The tail cursor is updated after the data is consumed by the reader - uint32_t tail = atomic_load_explicit(&buf->tail, memory_order_acquire); - - uint32_t can_write = (buf->alloc_size + tail - head - 1) % buf->alloc_size; - if (!can_write) { - return 0; - } - if (samples_count > can_write) { - samples_count = can_write; - } - - uint32_t right_count = buf->alloc_size - head; - if (right_count > samples_count) { - right_count = samples_count; - } - memset(buf->data + (head * buf->sample_size), 0, - right_count * buf->sample_size); - - if (samples_count > right_count) { - uint32_t left_count = samples_count - right_count; - memset(buf->data, 0, left_count * buf->sample_size); - } - - uint32_t new_head = (head + samples_count) % buf->alloc_size; - atomic_store_explicit(&buf->head, new_head, memory_order_release); - - return samples_count; -} diff --git a/app/src/util/audiobuf.h b/app/src/util/audiobuf.h index b55a5a59..5e7dd4a0 100644 --- a/app/src/util/audiobuf.h +++ b/app/src/util/audiobuf.h @@ -6,7 +6,6 @@ #include #include #include -#include #include /** @@ -50,9 +49,6 @@ uint32_t sc_audiobuf_write(struct sc_audiobuf *buf, const void *from, uint32_t samples_count); -uint32_t -sc_audiobuf_write_silence(struct sc_audiobuf *buf, uint32_t samples); - static inline uint32_t sc_audiobuf_capacity(struct sc_audiobuf *buf) { assert(buf->alloc_size); diff --git a/app/src/util/average.h b/app/src/util/average.h index eded9987..59fae7d1 100644 --- a/app/src/util/average.h +++ b/app/src/util/average.h @@ -3,6 +3,9 @@ #include "common.h" +#include +#include + struct sc_average { // Current average value float avg; diff --git a/app/src/util/binary.h b/app/src/util/binary.h index b6ce3201..7de9b505 100644 --- a/app/src/util/binary.h +++ b/app/src/util/binary.h @@ -4,6 +4,7 @@ #include "common.h" #include +#include #include static inline void diff --git a/app/src/util/env.c b/app/src/util/env.c index 127f5a1f..1128e5ea 100644 --- a/app/src/util/env.c +++ b/app/src/util/env.c @@ -2,9 +2,7 @@ #include #include -#ifdef _WIN32 -# include "util/str.h" -#endif +#include "util/str.h" char * sc_get_env(const char *varname) { diff --git a/app/src/util/intmap.h b/app/src/util/intmap.h index 7ab903ca..2898c461 100644 --- a/app/src/util/intmap.h +++ b/app/src/util/intmap.h @@ -3,7 +3,6 @@ #include "common.h" -#include #include struct sc_intmap_entry { diff --git a/app/src/util/intr.c b/app/src/util/intr.c index ddf4839f..22bd121a 100644 --- a/app/src/util/intr.c +++ b/app/src/util/intr.c @@ -1,9 +1,9 @@ #include "intr.h" -#include - #include "util/log.h" +#include + bool sc_intr_init(struct sc_intr *intr) { bool ok = sc_mutex_init(&intr->mutex); diff --git a/app/src/util/intr.h b/app/src/util/intr.h index 35bd3375..1c20f6df 100644 --- a/app/src/util/intr.h +++ b/app/src/util/intr.h @@ -6,9 +6,9 @@ #include #include -#include "util/net.h" -#include "util/process.h" -#include "util/thread.h" +#include "net.h" +#include "process.h" +#include "thread.h" /** * Interruptor to wake up a blocking call from another thread diff --git a/app/src/util/log.c b/app/src/util/log.c index 9114a258..8a347c84 100644 --- a/app/src/util/log.c +++ b/app/src/util/log.c @@ -4,10 +4,7 @@ # include #endif #include -#include -#include -#include -#include +#include static SDL_LogPriority log_level_sc_to_sdl(enum sc_log_level level) { diff --git a/app/src/util/net.c b/app/src/util/net.c index 9562ff6b..d68b0af6 100644 --- a/app/src/util/net.c +++ b/app/src/util/net.c @@ -1,27 +1,28 @@ #include "net.h" #include +#include #include +#include "log.h" + #ifdef _WIN32 # include typedef int socklen_t; #else -# include -# include +# include +# include # include # include +# include # include -# include -# include +# include # define SOCKET_ERROR -1 typedef struct sockaddr_in SOCKADDR_IN; typedef struct sockaddr SOCKADDR; typedef struct in_addr IN_ADDR; #endif -#include "util/log.h" - bool net_init(void) { #ifdef _WIN32 diff --git a/app/src/util/net.h b/app/src/util/net.h index aa99bbc4..94789954 100644 --- a/app/src/util/net.h +++ b/app/src/util/net.h @@ -4,15 +4,14 @@ #include "common.h" #include -#include #include -#include #ifdef _WIN32 # include typedef SOCKET sc_raw_socket; # define SC_RAW_SOCKET_NONE INVALID_SOCKET #else // not _WIN32 +# include typedef int sc_raw_socket; # define SC_RAW_SOCKET_NONE -1 #endif diff --git a/app/src/util/net_intr.h b/app/src/util/net_intr.h index e2bbee88..dbef528d 100644 --- a/app/src/util/net_intr.h +++ b/app/src/util/net_intr.h @@ -3,13 +3,8 @@ #include "common.h" -#include -#include -#include -#include - -#include "util/intr.h" -#include "util/net.h" +#include "intr.h" +#include "net.h" bool net_connect_intr(struct sc_intr *intr, sc_socket socket, uint32_t addr, diff --git a/app/src/util/process.c b/app/src/util/process.c index 29d89a54..9c4dcd9f 100644 --- a/app/src/util/process.c +++ b/app/src/util/process.c @@ -1,6 +1,8 @@ #include "process.h" #include +#include +#include "log.h" enum sc_process_result sc_process_execute(const char *const argv[], sc_pid *pid, unsigned flags) { diff --git a/app/src/util/process.h b/app/src/util/process.h index eec51bcc..4d9d1684 100644 --- a/app/src/util/process.h +++ b/app/src/util/process.h @@ -4,9 +4,7 @@ #include "common.h" #include -#include #include "util/thread.h" -#include "util/tick.h" #ifdef _WIN32 diff --git a/app/src/util/process_intr.c b/app/src/util/process_intr.c index 641440ab..d37bd5a5 100644 --- a/app/src/util/process_intr.c +++ b/app/src/util/process_intr.c @@ -5,7 +5,7 @@ sc_pipe_read_intr(struct sc_intr *intr, sc_pid pid, sc_pipe pipe, char *data, size_t len) { if (intr && !sc_intr_set_process(intr, pid)) { // Already interrupted - return -1; + return false; } ssize_t ret = sc_pipe_read(pipe, data, len); @@ -22,7 +22,7 @@ sc_pipe_read_all_intr(struct sc_intr *intr, sc_pid pid, sc_pipe pipe, char *data, size_t len) { if (intr && !sc_intr_set_process(intr, pid)) { // Already interrupted - return -1; + return false; } ssize_t ret = sc_pipe_read_all(pipe, data, len); diff --git a/app/src/util/process_intr.h b/app/src/util/process_intr.h index 020eafa1..530a9046 100644 --- a/app/src/util/process_intr.h +++ b/app/src/util/process_intr.h @@ -3,8 +3,8 @@ #include "common.h" -#include "util/intr.h" -#include "util/process.h" +#include "intr.h" +#include "process.h" ssize_t sc_pipe_read_intr(struct sc_intr *intr, sc_pid pid, sc_pipe pipe, char *data, diff --git a/app/src/util/str.c b/app/src/util/str.c index 83d19c4d..304cd302 100644 --- a/app/src/util/str.c +++ b/app/src/util/str.c @@ -12,8 +12,8 @@ # include #endif -#include "util/log.h" -#include "util/strbuf.h" +#include "log.h" +#include "strbuf.h" size_t sc_strncpy(char *dest, const char *src, size_t n) { diff --git a/app/src/util/str.h b/app/src/util/str.h index b386b48d..d20f1b28 100644 --- a/app/src/util/str.h +++ b/app/src/util/str.h @@ -5,8 +5,6 @@ #include #include -#include -#include /* Stringify a numeric value */ #define SC_STR(s) SC_XSTR(s) diff --git a/app/src/util/strbuf.c b/app/src/util/strbuf.c index 6196d746..1892b46b 100644 --- a/app/src/util/strbuf.c +++ b/app/src/util/strbuf.c @@ -1,10 +1,11 @@ #include "strbuf.h" #include +#include #include #include -#include "util/log.h" +#include "log.h" bool sc_strbuf_init(struct sc_strbuf *buf, size_t init_cap) { diff --git a/app/src/util/thread.c b/app/src/util/thread.c index 2a5253f7..9679dfff 100644 --- a/app/src/util/thread.c +++ b/app/src/util/thread.c @@ -1,12 +1,10 @@ #include "thread.h" #include -#include -#include #include #include -#include "util/log.h" +#include "log.h" sc_thread_id SC_MAIN_THREAD_ID; diff --git a/app/src/util/tick.c b/app/src/util/tick.c index edef1070..cc0bab5e 100644 --- a/app/src/util/tick.c +++ b/app/src/util/tick.c @@ -1,7 +1,6 @@ #include "tick.h" #include -#include #include #ifdef _WIN32 # include diff --git a/app/src/util/timeout.c b/app/src/util/timeout.c index 21bc3a53..159a4681 100644 --- a/app/src/util/timeout.c +++ b/app/src/util/timeout.c @@ -1,9 +1,8 @@ #include "timeout.h" #include -#include -#include "util/log.h" +#include "log.h" bool sc_timeout_init(struct sc_timeout *timeout) { diff --git a/app/src/util/timeout.h b/app/src/util/timeout.h index a45ae2ae..ae171b86 100644 --- a/app/src/util/timeout.h +++ b/app/src/util/timeout.h @@ -5,8 +5,8 @@ #include -#include "util/thread.h" -#include "util/tick.h" +#include "thread.h" +#include "tick.h" struct sc_timeout { sc_thread thread; diff --git a/app/src/util/vecdeque.h b/app/src/util/vecdeque.h index e31724e2..ce559ee9 100644 --- a/app/src/util/vecdeque.h +++ b/app/src/util/vecdeque.h @@ -6,7 +6,6 @@ #include #include #include -#include #include #include diff --git a/app/src/util/vector.h b/app/src/util/vector.h index 5b399d56..97d7c389 100644 --- a/app/src/util/vector.h +++ b/app/src/util/vector.h @@ -5,8 +5,8 @@ #include #include -#include #include +#include // Adapted from vlc_vector: // diff --git a/app/src/v4l2_sink.c b/app/src/v4l2_sink.c index da9e02ef..087e9af4 100644 --- a/app/src/v4l2_sink.c +++ b/app/src/v4l2_sink.c @@ -1,9 +1,5 @@ #include "v4l2_sink.h" -#include -#include -#include -#include #include #include "util/log.h" diff --git a/app/src/v4l2_sink.h b/app/src/v4l2_sink.h index 2b7c5b50..365a739d 100644 --- a/app/src/v4l2_sink.h +++ b/app/src/v4l2_sink.h @@ -3,13 +3,13 @@ #include "common.h" -#include #include #include -#include "frame_buffer.h" +#include "coords.h" #include "trait/frame_sink.h" -#include "util/thread.h" +#include "frame_buffer.h" +#include "util/tick.h" struct sc_v4l2_sink { struct sc_frame_sink frame_sink; // frame sink trait diff --git a/app/src/version.c b/app/src/version.c index f8610714..90ea3334 100644 --- a/app/src/version.c +++ b/app/src/version.c @@ -1,6 +1,5 @@ #include "version.h" -#include #include #include #include @@ -10,7 +9,6 @@ #ifdef HAVE_USB # include #endif -#include void scrcpy_print_version(void) { diff --git a/app/tests/test_audiobuf.c b/app/tests/test_audiobuf.c index 539ee238..94d0f07a 100644 --- a/app/tests/test_audiobuf.c +++ b/app/tests/test_audiobuf.c @@ -113,14 +113,6 @@ static void test_audiobuf_partial_read_write(void) { uint32_t expected2[] = {4, 5, 6, 1, 2, 3, 4, 1, 2, 3}; assert(!memcmp(data, expected2, 12)); - w = sc_audiobuf_write_silence(&buf, 4); - assert(w == 4); - - r = sc_audiobuf_read(&buf, data, 4); - assert(r == 4); - uint32_t expected3[] = {0, 0, 0, 0}; - assert(!memcmp(data, expected3, 4)); - sc_audiobuf_destroy(&buf); } diff --git a/app/tests/test_control_msg_serialize.c b/app/tests/test_control_msg_serialize.c index 0d19919e..af97182d 100644 --- a/app/tests/test_control_msg_serialize.c +++ b/app/tests/test_control_msg_serialize.c @@ -127,8 +127,8 @@ static void test_serialize_inject_scroll_event(void) { .height = 1920, }, }, - .hscroll = 16, - .vscroll = -16, + .hscroll = 1, + .vscroll = -1, .buttons = 1, }, }; @@ -141,8 +141,8 @@ static void test_serialize_inject_scroll_event(void) { SC_CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x04, 0x02, // 260 1026 0x04, 0x38, 0x07, 0x80, // 1080 1920 - 0x7F, 0xFF, // 16 (float encoded as i16 in the range [-16, 16]) - 0x80, 0x00, // -16 (float encoded as i16 in the range [-16, 16]) + 0x7F, 0xFF, // 1 (float encoded as i16) + 0x80, 0x00, // -1 (float encoded as i16) 0x00, 0x00, 0x00, 0x01, // 1 }; assert(!memcmp(buf, expected, sizeof(expected))); diff --git a/cross_win32.txt b/cross_win32.txt index ddbc65f3..05f9a86b 100644 --- a/cross_win32.txt +++ b/cross_win32.txt @@ -7,8 +7,6 @@ cpp = 'i686-w64-mingw32-g++' ar = 'i686-w64-mingw32-ar' strip = 'i686-w64-mingw32-strip' pkg-config = 'i686-w64-mingw32-pkg-config' -# backward compatibility -pkgconfig = 'i686-w64-mingw32-pkg-config' windres = 'i686-w64-mingw32-windres' [host_machine] diff --git a/cross_win64.txt b/cross_win64.txt index a6f16e16..86364ad6 100644 --- a/cross_win64.txt +++ b/cross_win64.txt @@ -7,8 +7,6 @@ cpp = 'x86_64-w64-mingw32-g++' ar = 'x86_64-w64-mingw32-ar' strip = 'x86_64-w64-mingw32-strip' pkg-config = 'x86_64-w64-mingw32-pkg-config' -# backward compatibility -pkgconfig = 'x86_64-w64-mingw32-pkg-config' windres = 'x86_64-w64-mingw32-windres' [host_machine] diff --git a/doc/audio.md b/doc/audio.md index 142626f5..85f76ac5 100644 --- a/doc/audio.md +++ b/doc/audio.md @@ -66,20 +66,6 @@ the computer: scrcpy --audio-source=mic --no-video --no-playback --record=file.opus ``` -Many sources are available: - - - `output` (default): forwards the whole audio output, and disables playback on the device (mapped to [`REMOTE_SUBMIX`](https://developer.android.com/reference/android/media/MediaRecorder.AudioSource#REMOTE_SUBMIX)). - - `playback`: captures the audio playback (Android apps can opt-out, so the whole output is not necessarily captured). - - `mic`: captures the microphone (mapped to [`MIC`](https://developer.android.com/reference/android/media/MediaRecorder.AudioSource#MIC)). - - `mic-unprocessed`: captures the microphone unprocessed (raw) sound (mapped to [`UNPROCESSED`](https://developer.android.com/reference/android/media/MediaRecorder.AudioSource#UNPROCESSED)). - - `mic-camcorder`: captures the microphone tuned for video recording, with the same orientation as the camera if available (mapped to [`CAMCORDER`](https://developer.android.com/reference/android/media/MediaRecorder.AudioSource#CAMCORDER)). - - `mic-voice-recognition`: captures the microphone tuned for voice recognition (mapped to [`VOICE_RECOGNITION`](https://developer.android.com/reference/android/media/MediaRecorder.AudioSource#VOICE_RECOGNITION)). - - `mic-voice-communication`: captures the microphone tuned for voice communications (it will for instance take advantage of echo cancellation or automatic gain control if available) (mapped to [`VOICE_COMMUNICATION`](https://developer.android.com/reference/android/media/MediaRecorder.AudioSource#VOICE_COMMUNICATION)). - - `voice-call`: captures voice call (mapped to [`VOICE_CALL`](https://developer.android.com/reference/android/media/MediaRecorder.AudioSource#VOICE_CALL)). - - `voice-call-uplink`: captures voice call uplink only (mapped to [`VOICE_UPLINK`](https://developer.android.com/reference/android/media/MediaRecorder.AudioSource#VOICE_UPLINK)). - - `voice-call-downlink`: captures voice call downlink only (mapped to [`VOICE_DOWNLINK`](https://developer.android.com/reference/android/media/MediaRecorder.AudioSource#VOICE_DOWNLINK)). - - `voice-performance`: captures audio meant to be processed for live performance (karaoke), includes both the microphone and the device playback (mapped to [`VOICE_PERFORMANCE`](https://developer.android.com/reference/android/media/MediaRecorder.AudioSource#VOICE_PERFORMANCE)). - ### Duplication An alternative device audio capture method is also available (only for Android diff --git a/doc/build.md b/doc/build.md index 7f76b4fd..2776ed01 100644 --- a/doc/build.md +++ b/doc/build.md @@ -233,10 +233,10 @@ install` must be run as root)._ #### Option 2: Use prebuilt server - - [`scrcpy-server-v3.3.1`][direct-scrcpy-server] - SHA-256: `a0f70b20aa4998fbf658c94118cd6c8dab6abbb0647a3bdab344d70bc1ebcbb8` + - [`scrcpy-server-v3.1`][direct-scrcpy-server] + SHA-256: `958f0944a62f23b1f33a16e9eb14844c1a04b882ca175a738c16d23cb22b86c0` -[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v3.3.1/scrcpy-server-v3.3.1 +[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v3.1/scrcpy-server-v3.1 Download the prebuilt server somewhere, and specify its path during the Meson configuration: diff --git a/doc/connection.md b/doc/connection.md index dcf00147..2c3d37e1 100644 --- a/doc/connection.md +++ b/doc/connection.md @@ -113,17 +113,16 @@ with the device IP address you found)_. 7. Run `scrcpy` as usual. 8. Run `adb disconnect` once you're done. -Since Android 11, a [wireless debugging option][adb-wireless] allows you to -bypass having to physically connect your device to your computer. +Since Android 11, a [wireless debugging option][adb-wireless] allows to bypass +having to physically connect your device directly to your computer. [adb-wireless]: https://developer.android.com/studio/command-line/adb#wireless-android11-command-line ## Autostart -A small tool (by the scrcpy author) allows you to run arbitrary commands -whenever a new Android device is connected: [AutoAdb]. It can be used to start -scrcpy: +A small tool (by the scrcpy author) allows to run arbitrary commands whenever a +new Android device is connected: [AutoAdb]. It can be used to start scrcpy: ```bash autoadb scrcpy -s '{}' diff --git a/doc/device.md b/doc/device.md index ab1e6ba4..42208faa 100644 --- a/doc/device.md +++ b/doc/device.md @@ -34,31 +34,6 @@ adb shell settings put global stay_on_while_plugged_in 0 ``` -## Screen off timeout - -The Android screen automatically turns off after some delay. - -To change this delay while scrcpy is running: - -```bash -scrcpy --screen-off-timeout=300 # 300 seconds (5 minutes) -``` - -The initial value is restored on exit. - -It is possible to change this setting manually: - -```bash -# get the current screen_off_timeout value -adb shell settings get system screen_off_timeout -# set a new value (in milliseconds) -adb shell settings put system screen_off_timeout 30000 -``` - -Note that the Android value is in milliseconds, but the scrcpy command line -argument is in seconds. - - ## Turn screen off It is possible to turn the device screen off while mirroring on start with a @@ -96,6 +71,31 @@ adb shell cmd display power-on 0 ``` +## Screen off timeout + +The Android screen automatically turns off after some delay. + +To change this delay while scrcpy is running: + +```bash +scrcpy --screen-off-timeout=300 # 300 seconds (5 minutes) +``` + +The initial value is restored on exit. + +It is possible to change this setting manually: + +```bash +# get the current screen_off_timeout value +adb shell settings get system screen_off_timeout +# set a new value (in milliseconds) +adb shell settings put system screen_off_timeout 30000 +``` + +Note that the Android value is in milliseconds, but the scrcpy command line +argument is in seconds. + + ## Show touches For presentations, it may be useful to show physical touches (on the physical diff --git a/doc/linux.md b/doc/linux.md index be433df4..9beaed1e 100644 --- a/doc/linux.md +++ b/doc/linux.md @@ -6,11 +6,11 @@ Download a static build of the [latest release]: - - [`scrcpy-linux-x86_64-v3.3.1.tar.gz`][direct-linux-x86_64] (x86_64) - SHA-256: `bbfe54c6b178adafeaffbbfbbc1548a74486553170c63e63bdd41863ad123422` + - [`scrcpy-linux-x86_64-v3.1.tar.gz`][direct-linux-x86_64] (x86_64) + SHA-256: `37dba54092ed9ec6b2f8f95432f61b8ea124aec9f1e9f2b3d22d4b10bb04c59a` [latest release]: https://github.com/Genymobile/scrcpy/releases/latest -[direct-linux-x86_64]: https://github.com/Genymobile/scrcpy/releases/download/v3.3.1/scrcpy-linux-x86_64-v3.3.1.tar.gz +[direct-linux-x86_64]: https://github.com/Genymobile/scrcpy/releases/download/v3.1/scrcpy-linux-x86_64-v3.1.tar.gz and extract it. @@ -27,7 +27,7 @@ Scrcpy is packaged in several distributions and package managers: - Arch Linux: `pacman -S scrcpy` - Fedora: `dnf copr enable zeno/scrcpy && dnf install scrcpy` - Gentoo: `emerge scrcpy` - - Snap: ~~`snap install scrcpy`~~ _(obsolete version)_ + - Snap: `snap install scrcpy` - … (see [repology](https://repology.org/project/scrcpy/versions)) diff --git a/doc/macos.md b/doc/macos.md index f6b01c30..56d9f168 100644 --- a/doc/macos.md +++ b/doc/macos.md @@ -6,15 +6,15 @@ Download a static build of the [latest release]: - - [`scrcpy-macos-aarch64-v3.3.1.tar.gz`][direct-macos-aarch64] (aarch64) - SHA-256: `907b925900ebd8499c1e47acc9689a95bd3a6f9930eb1d7bdfbca8375ae4f139` + - [`scrcpy-macos-aarch64-v3.1.tar.gz`][direct-macos-aarch64] (aarch64) + SHA-256: `478618d940421e5f57942f5479d493ecbb38210682937a200f712aee5f235daf` - - [`scrcpy-macos-x86_64-v3.3.1.tar.gz`][direct-macos-x86_64] (x86_64) - SHA-256: `69772491dad718eea82fc65c8e89febff7d41c4ce6faff02f4789a588d10fd7d` + - [`scrcpy-macos-x86_64-v3.1.tar.gz`][direct-macos-x86_64] (x86_64) + SHA-256: `acde98e29c273710ffa469371dbca4a728a44c41c380381f8a54e5b5301b9e87` [latest release]: https://github.com/Genymobile/scrcpy/releases/latest -[direct-macos-aarch64]: https://github.com/Genymobile/scrcpy/releases/download/v3.3.1/scrcpy-macos-aarch64-v3.3.1.tar.gz -[direct-macos-x86_64]: https://github.com/Genymobile/scrcpy/releases/download/v3.3.1/scrcpy-macos-x86_64-v3.3.1.tar.gz +[direct-macos-aarch64]: https://github.com/Genymobile/scrcpy/releases/download/v3.1/scrcpy-macos-aarch64-v3.1.tar.gz +[direct-macos-x86_64]: https://github.com/Genymobile/scrcpy/releases/download/v3.1/scrcpy-macos-x86_64-v3.1.tar.gz and extract it. diff --git a/doc/mouse.md b/doc/mouse.md index 0bea4aea..3607a92c 100644 --- a/doc/mouse.md +++ b/doc/mouse.md @@ -83,9 +83,9 @@ process like the _adb daemon_). ## Mouse bindings By default, with SDK mouse: - - right-click triggers `BACK` (or `POWER` on) - - middle-click triggers `HOME` - - the 4th click triggers `APP_SWITCH` + - right-click triggers BACK (or POWER on) + - middle-click triggers HOME + - the 4th click triggers APP_SWITCH - the 5th click expands the notification panel The secondary clicks may be forwarded to the device instead by pressing the @@ -121,9 +121,9 @@ Each character must be one of the following: - `+`: forward the click to the device - `-`: ignore the click - - `b`: trigger shortcut `BACK` (or turn screen on if off) - - `h`: trigger shortcut `HOME` - - `s`: trigger shortcut `APP_SWITCH` + - `b`: trigger shortcut BACK (or turn screen on if off) + - `h`: trigger shortcut HOME + - `s`: trigger shortcut APP_SWITCH - `n`: trigger shortcut "expand notification panel" For example: diff --git a/doc/virtual_display.md b/doc/virtual_display.md index 9f962127..5d1673e8 100644 --- a/doc/virtual_display.md +++ b/doc/virtual_display.md @@ -11,8 +11,6 @@ scrcpy --new-display # use the main display size and density scrcpy --new-display=/240 # use the main display size and 240 dpi ``` -The new virtual display is destroyed on exit. - ## Start app On some devices, a launcher is available in the virtual display. @@ -63,15 +61,3 @@ To move them to the main display instead, use: ``` scrcpy --new-display --no-vd-destroy-content ``` - - -## Display IME policy - -By default, the virtual display IME appears on the default display. - -To make it appear on the local display, use `--display-ime-policy=local`: - -```bash -scrcpy --display-id=1 --display-ime-policy=local -scrcpy --new-display --display-ime-policy=local -``` diff --git a/doc/windows.md b/doc/windows.md index 8fa1921f..ec7b904b 100644 --- a/doc/windows.md +++ b/doc/windows.md @@ -6,26 +6,20 @@ Download the [latest release]: - - [`scrcpy-win64-v3.3.1.zip`][direct-win64] (64-bit) - SHA-256: `4fcad494772a3ae5de9a133149f8856d2fc429b41795f7cf7c754e0c1bb6fbc0` - - [`scrcpy-win32-v3.3.1.zip`][direct-win32] (32-bit) - SHA-256: `ccdf1b4f5d19dfe760446a107e55b0a010a00e097d46533a161499c9333a20a6` + - [`scrcpy-win64-v3.1.zip`][direct-win64] (64-bit) + SHA-256: `0c05ea395d95cfe36bee974eeb435a3db87ea5594ff738370d5dc3068a9538ca` + - [`scrcpy-win32-v3.1.zip`][direct-win32] (32-bit) + SHA-256: `2b4674ef76719680ac5a9b482d1943bdde3fa25821ad2e98f3c40c347d00d560` [latest release]: https://github.com/Genymobile/scrcpy/releases/latest -[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v3.3.1/scrcpy-win64-v3.3.1.zip -[direct-win32]: https://github.com/Genymobile/scrcpy/releases/download/v3.3.1/scrcpy-win32-v3.3.1.zip +[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v3.1/scrcpy-win64-v3.1.zip +[direct-win32]: https://github.com/Genymobile/scrcpy/releases/download/v3.1/scrcpy-win32-v3.1.zip and extract it. ### From a package manager -From [WinGet] (ADB and other dependencies will be installed alongside scrcpy): - -```bash -winget install --exact Genymobile.scrcpy -``` - From [Chocolatey]: ```bash @@ -35,12 +29,12 @@ choco install adb # if you don't have it yet From [Scoop]: + ```bash scoop install scrcpy scoop install adb # if you don't have it yet ``` -[WinGet]: https://github.com/microsoft/winget-cli [Chocolatey]: https://chocolatey.org/ [Scoop]: https://scoop.sh diff --git a/install_release.sh b/install_release.sh index d960932b..3774be86 100755 --- a/install_release.sh +++ b/install_release.sh @@ -2,8 +2,8 @@ set -e BUILDDIR=build-auto -PREBUILT_SERVER_URL=https://github.com/Genymobile/scrcpy/releases/download/v3.3.1/scrcpy-server-v3.3.1 -PREBUILT_SERVER_SHA256=a0f70b20aa4998fbf658c94118cd6c8dab6abbb0647a3bdab344d70bc1ebcbb8 +PREBUILT_SERVER_URL=https://github.com/Genymobile/scrcpy/releases/download/v3.1/scrcpy-server-v3.1 +PREBUILT_SERVER_SHA256=958f0944a62f23b1f33a16e9eb14844c1a04b882ca175a738c16d23cb22b86c0 echo "[scrcpy] Downloading prebuilt server..." wget "$PREBUILT_SERVER_URL" -O scrcpy-server diff --git a/meson.build b/meson.build index d991d672..aa1a3a3b 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project('scrcpy', 'c', - version: '3.3.1', - meson_version: '>= 0.49', + version: '3.1', + meson_version: '>= 0.48', default_options: [ 'c_std=c11', 'warning_level=2', diff --git a/server/build.gradle b/server/build.gradle index 31092b12..9c0543e9 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -7,8 +7,8 @@ android { applicationId "com.genymobile.scrcpy" minSdkVersion 21 targetSdkVersion 35 - versionCode 30301 - versionName "3.3.1" + versionCode 30100 + versionName "3.1" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { diff --git a/server/build_without_gradle.sh b/server/build_without_gradle.sh index 193a9902..d16592b4 100755 --- a/server/build_without_gradle.sh +++ b/server/build_without_gradle.sh @@ -12,7 +12,7 @@ set -e SCRCPY_DEBUG=false -SCRCPY_VERSION_NAME=3.3.1 +SCRCPY_VERSION_NAME=3.1 PLATFORM=${ANDROID_PLATFORM:-35} BUILD_TOOLS=${ANDROID_BUILD_TOOLS:-35.0.0} @@ -47,8 +47,10 @@ EOF echo "Generating java from aidl..." cd "$SERVER_DIR/src/main/aidl" +"$BUILD_TOOLS_DIR/aidl" -o"$GEN_DIR" -I. android/view/IRotationWatcher.aidl "$BUILD_TOOLS_DIR/aidl" -o"$GEN_DIR" -I. \ android/content/IOnPrimaryClipChangedListener.aidl +"$BUILD_TOOLS_DIR/aidl" -o"$GEN_DIR" -I. android/view/IDisplayFoldListener.aidl "$BUILD_TOOLS_DIR/aidl" -o"$GEN_DIR" -I. -p "$ANDROID_AIDL" \ android/view/IDisplayWindowListener.aidl diff --git a/server/meson.build b/server/meson.build index 55828e2d..42b97981 100644 --- a/server/meson.build +++ b/server/meson.build @@ -23,9 +23,3 @@ else install: true, install_dir: 'share/scrcpy') endif - -if meson.version().version_compare('>= 0.58.0') - devenv = environment() - devenv.set('SCRCPY_SERVER_PATH', meson.current_build_dir() / 'scrcpy-server') - meson.add_devenv(devenv) -endif diff --git a/server/src/main/aidl/android/view/IDisplayFoldListener.aidl b/server/src/main/aidl/android/view/IDisplayFoldListener.aidl new file mode 100644 index 00000000..2c91149d --- /dev/null +++ b/server/src/main/aidl/android/view/IDisplayFoldListener.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +/** + * {@hide} + */ +oneway interface IDisplayFoldListener +{ + /** Called when the foldedness of a display changes */ + void onDisplayFoldChanged(int displayId, boolean folded); +} diff --git a/server/src/main/aidl/android/view/IRotationWatcher.aidl b/server/src/main/aidl/android/view/IRotationWatcher.aidl new file mode 100644 index 00000000..2cc5e44a --- /dev/null +++ b/server/src/main/aidl/android/view/IRotationWatcher.aidl @@ -0,0 +1,25 @@ +/* //device/java/android/android/hardware/ISensorListener.aidl +** +** Copyright 2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.view; + +/** + * {@hide} + */ +interface IRotationWatcher { + oneway void onRotationChanged(int rotation); +} diff --git a/server/src/main/java/com/genymobile/scrcpy/CleanUp.java b/server/src/main/java/com/genymobile/scrcpy/CleanUp.java index 77018afa..49b23e81 100644 --- a/server/src/main/java/com/genymobile/scrcpy/CleanUp.java +++ b/server/src/main/java/com/genymobile/scrcpy/CleanUp.java @@ -4,10 +4,8 @@ import com.genymobile.scrcpy.device.Device; import com.genymobile.scrcpy.util.Ln; import com.genymobile.scrcpy.util.Settings; import com.genymobile.scrcpy.util.SettingsException; -import com.genymobile.scrcpy.wrappers.ServiceManager; import android.os.BatteryManager; -import android.os.Looper; import android.system.ErrnoException; import android.system.Os; @@ -99,31 +97,18 @@ public final class CleanUp { } } + boolean powerOffScreen = options.getPowerOffScreenOnClose(); int displayId = options.getDisplayId(); - int restoreDisplayImePolicy = -1; - if (displayId > 0) { - int displayImePolicy = options.getDisplayImePolicy(); - if (displayImePolicy != -1) { - int currentDisplayImePolicy = ServiceManager.getWindowManager().getDisplayImePolicy(displayId); - if (currentDisplayImePolicy != displayImePolicy) { - ServiceManager.getWindowManager().setDisplayImePolicy(displayId, displayImePolicy); - restoreDisplayImePolicy = currentDisplayImePolicy; - } - } - } - - boolean powerOffScreen = options.getPowerOffScreenOnClose(); - try { - run(displayId, restoreStayOn, disableShowTouches, powerOffScreen, restoreScreenOffTimeout, restoreDisplayImePolicy); + run(displayId, restoreStayOn, disableShowTouches, powerOffScreen, restoreScreenOffTimeout); } catch (IOException e) { Ln.e("Clean up I/O exception", e); } } - private void run(int displayId, int restoreStayOn, boolean disableShowTouches, boolean powerOffScreen, int restoreScreenOffTimeout, - int restoreDisplayImePolicy) throws IOException { + private void run(int displayId, int restoreStayOn, boolean disableShowTouches, boolean powerOffScreen, int restoreScreenOffTimeout) + throws IOException { String[] cmd = { "app_process", "/", @@ -133,7 +118,6 @@ public final class CleanUp { String.valueOf(disableShowTouches), String.valueOf(powerOffScreen), String.valueOf(restoreScreenOffTimeout), - String.valueOf(restoreDisplayImePolicy), }; ProcessBuilder builder = new ProcessBuilder(cmd); @@ -180,11 +164,6 @@ public final class CleanUp { } } - @SuppressWarnings("deprecation") - private static void prepareMainLooper() { - Looper.prepareMainLooper(); - } - public static void main(String... args) { try { // Start a new session to avoid being terminated along with the server process on some devices @@ -194,15 +173,11 @@ public final class CleanUp { } unlinkSelf(); - // Needed for workarounds - prepareMainLooper(); - int displayId = Integer.parseInt(args[0]); int restoreStayOn = Integer.parseInt(args[1]); boolean disableShowTouches = Boolean.parseBoolean(args[2]); boolean powerOffScreen = Boolean.parseBoolean(args[3]); int restoreScreenOffTimeout = Integer.parseInt(args[4]); - int restoreDisplayImePolicy = Integer.parseInt(args[5]); // Dynamic option boolean restoreDisplayPower = false; @@ -248,11 +223,6 @@ public final class CleanUp { } } - if (restoreDisplayImePolicy != -1) { - Ln.i("Restoring \"display IME policy\""); - ServiceManager.getWindowManager().setDisplayImePolicy(displayId, restoreDisplayImePolicy); - } - // Change the power of the main display when mirroring a virtual display int targetDisplayId = displayId != Device.DISPLAY_ID_NONE ? displayId : 0; if (Device.isScreenOn(targetDisplayId)) { diff --git a/server/src/main/java/com/genymobile/scrcpy/FakeContext.java b/server/src/main/java/com/genymobile/scrcpy/FakeContext.java index b43e9e1b..2b83e397 100644 --- a/server/src/main/java/com/genymobile/scrcpy/FakeContext.java +++ b/server/src/main/java/com/genymobile/scrcpy/FakeContext.java @@ -2,10 +2,8 @@ package com.genymobile.scrcpy; import com.genymobile.scrcpy.wrappers.ServiceManager; -import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.content.AttributionSource; -import android.content.ClipboardManager; import android.content.ContentResolver; import android.content.Context; import android.content.ContextWrapper; @@ -13,8 +11,6 @@ import android.content.IContentProvider; import android.os.Binder; import android.os.Process; -import java.lang.reflect.Field; - public final class FakeContext extends ContextWrapper { public static final String PACKAGE_NAME = "com.android.shell"; @@ -95,25 +91,4 @@ public final class FakeContext extends ContextWrapper { public ContentResolver getContentResolver() { return contentResolver; } - - @SuppressLint("SoonBlockedPrivateApi") - @Override - public Object getSystemService(String name) { - Object service = super.getSystemService(name); - if (service == null) { - return null; - } - - if (Context.CLIPBOARD_SERVICE.equals(name)) { - try { - Field field = ClipboardManager.class.getDeclaredField("mContext"); - field.setAccessible(true); - field.set(service, this); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - } - - return service; - } } diff --git a/server/src/main/java/com/genymobile/scrcpy/Options.java b/server/src/main/java/com/genymobile/scrcpy/Options.java index 66bb68e8..8a438750 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Options.java +++ b/server/src/main/java/com/genymobile/scrcpy/Options.java @@ -12,7 +12,6 @@ import com.genymobile.scrcpy.video.CameraAspectRatio; import com.genymobile.scrcpy.video.CameraFacing; import com.genymobile.scrcpy.video.VideoCodec; import com.genymobile.scrcpy.video.VideoSource; -import com.genymobile.scrcpy.wrappers.WindowManager; import android.graphics.Rect; import android.util.Pair; @@ -49,7 +48,6 @@ public class Options { private boolean showTouches; private boolean stayAwake; private int screenOffTimeout = -1; - private int displayImePolicy = -1; private List videoCodecOptions; private List audioCodecOptions; @@ -188,10 +186,6 @@ public class Options { return screenOffTimeout; } - public int getDisplayImePolicy() { - return displayImePolicy; - } - public List getVideoCodecOptions() { return videoCodecOptions; } @@ -488,9 +482,6 @@ public class Options { options.captureOrientationLock = pair.first; options.captureOrientation = pair.second; break; - case "display_ime_policy": - options.displayImePolicy = parseDisplayImePolicy(value); - break; case "send_device_meta": options.sendDeviceMeta = Boolean.parseBoolean(value); break; @@ -635,17 +626,4 @@ public class Options { return Pair.create(lock, Orientation.getByName(value)); } - - private static int parseDisplayImePolicy(String value) { - switch (value) { - case "local": - return WindowManager.DISPLAY_IME_POLICY_LOCAL; - case "fallback": - return WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; - case "hide": - return WindowManager.DISPLAY_IME_POLICY_HIDE; - default: - throw new IllegalArgumentException("Invalid display IME policy: " + value); - } - } } diff --git a/server/src/main/java/com/genymobile/scrcpy/Server.java b/server/src/main/java/com/genymobile/scrcpy/Server.java index a08c948c..eb8b533a 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Server.java +++ b/server/src/main/java/com/genymobile/scrcpy/Server.java @@ -24,13 +24,10 @@ import com.genymobile.scrcpy.video.SurfaceCapture; import com.genymobile.scrcpy.video.SurfaceEncoder; import com.genymobile.scrcpy.video.VideoSource; -import android.annotation.SuppressLint; import android.os.Build; -import android.os.Looper; import java.io.File; import java.io.IOException; -import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; @@ -58,7 +55,17 @@ public final class Server { this.fatalError = true; } if (running == 0 || this.fatalError) { - Looper.getMainLooper().quitSafely(); + notify(); + } + } + + synchronized void await() { + try { + while (running > 0 && !fatalError) { + wait(); + } + } catch (InterruptedException e) { + // ignore } } } @@ -73,15 +80,9 @@ public final class Server { throw new ConfigurationException("Camera mirroring is not supported"); } - if (Build.VERSION.SDK_INT < AndroidVersions.API_29_ANDROID_10) { - if (options.getNewDisplay() != null) { - Ln.e("New virtual display is not supported before Android 10"); - throw new ConfigurationException("New virtual display is not supported"); - } - if (options.getDisplayImePolicy() != -1) { - Ln.e("Display IME policy is not supported before Android 10"); - throw new ConfigurationException("Display IME policy is not supported"); - } + if (Build.VERSION.SDK_INT < AndroidVersions.API_29_ANDROID_10 && options.getNewDisplay() != null) { + Ln.e("New virtual display is not supported before Android 10"); + throw new ConfigurationException("New virtual display is not supported"); } CleanUp cleanUp = null; @@ -165,7 +166,7 @@ public final class Server { }); } - Looper.loop(); // interrupted by the Completion implementation + completion.await(); } finally { if (cleanUp != null) { cleanUp.interrupt(); @@ -194,21 +195,6 @@ public final class Server { } } - private static void prepareMainLooper() { - // Like Looper.prepareMainLooper(), but with quitAllowed set to true - Looper.prepare(); - synchronized (Looper.class) { - try { - @SuppressLint("DiscouragedPrivateApi") - Field field = Looper.class.getDeclaredField("sMainLooper"); - field.setAccessible(true); - field.set(null, Looper.myLooper()); - } catch (ReflectiveOperationException e) { - throw new AssertionError(e); - } - } - } - public static void main(String... args) { int status = 0; try { @@ -229,8 +215,6 @@ public final class Server { Ln.e("Exception on thread " + t, e); }); - prepareMainLooper(); - Options options = Options.parse(args); Ln.disableSystemStreams(); diff --git a/server/src/main/java/com/genymobile/scrcpy/Workarounds.java b/server/src/main/java/com/genymobile/scrcpy/Workarounds.java index b89f19ae..eec00a04 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Workarounds.java +++ b/server/src/main/java/com/genymobile/scrcpy/Workarounds.java @@ -29,6 +29,8 @@ public final class Workarounds { private static final Object ACTIVITY_THREAD; static { + prepareMainLooper(); + try { // ActivityThread activityThread = new ActivityThread(); ACTIVITY_THREAD_CLASS = Class.forName("android.app.ActivityThread"); @@ -40,11 +42,6 @@ public final class Workarounds { Field sCurrentActivityThreadField = ACTIVITY_THREAD_CLASS.getDeclaredField("sCurrentActivityThread"); sCurrentActivityThreadField.setAccessible(true); sCurrentActivityThreadField.set(null, ACTIVITY_THREAD); - - // activityThread.mSystemThread = true; - Field mSystemThreadField = ACTIVITY_THREAD_CLASS.getDeclaredField("mSystemThread"); - mSystemThreadField.setAccessible(true); - mSystemThreadField.setBoolean(ACTIVITY_THREAD, true); } catch (Exception e) { throw new AssertionError(e); } @@ -75,6 +72,19 @@ public final class Workarounds { fillAppContext(); } + @SuppressWarnings("deprecation") + private static void prepareMainLooper() { + // Some devices internally create a Handler when creating an input Surface, causing an exception: + // "Can't create handler inside thread that has not called Looper.prepare()" + // + // + // Use Looper.prepareMainLooper() instead of Looper.prepare() to avoid a NullPointerException: + // "Attempt to read from field 'android.os.MessageQueue android.os.Looper.mQueue' + // on a null object reference" + // + Looper.prepareMainLooper(); + } + private static void fillAppInfo() { try { // ActivityThread.AppBindData appBindData = new ActivityThread.AppBindData(); @@ -122,13 +132,10 @@ public final class Workarounds { try { Class configurationControllerClass = Class.forName("android.app.ConfigurationController"); Class activityThreadInternalClass = Class.forName("android.app.ActivityThreadInternal"); - - // configurationController = new ConfigurationController(ACTIVITY_THREAD); Constructor configurationControllerConstructor = configurationControllerClass.getDeclaredConstructor(activityThreadInternalClass); configurationControllerConstructor.setAccessible(true); Object configurationController = configurationControllerConstructor.newInstance(ACTIVITY_THREAD); - // ACTIVITY_THREAD.mConfigurationController = configurationController; Field configurationControllerField = ACTIVITY_THREAD_CLASS.getDeclaredField("mConfigurationController"); configurationControllerField.setAccessible(true); configurationControllerField.set(ACTIVITY_THREAD, configurationController); diff --git a/server/src/main/java/com/genymobile/scrcpy/audio/AudioDirectCapture.java b/server/src/main/java/com/genymobile/scrcpy/audio/AudioDirectCapture.java index bf870bee..5c859738 100644 --- a/server/src/main/java/com/genymobile/scrcpy/audio/AudioDirectCapture.java +++ b/server/src/main/java/com/genymobile/scrcpy/audio/AudioDirectCapture.java @@ -12,6 +12,7 @@ import android.content.ComponentName; import android.content.Intent; import android.media.AudioRecord; import android.media.MediaCodec; +import android.media.MediaRecorder; import android.os.Build; import android.os.SystemClock; @@ -31,7 +32,18 @@ public class AudioDirectCapture implements AudioCapture { private AudioRecordReader reader; public AudioDirectCapture(AudioSource audioSource) { - this.audioSource = audioSource.getDirectAudioSource(); + this.audioSource = getAudioSourceValue(audioSource); + } + + private static int getAudioSourceValue(AudioSource audioSource) { + switch (audioSource) { + case OUTPUT: + return MediaRecorder.AudioSource.REMOTE_SUBMIX; + case MIC: + return MediaRecorder.AudioSource.MIC; + default: + throw new IllegalArgumentException("Unsupported audio source: " + audioSource); + } } @TargetApi(AndroidVersions.API_23_ANDROID_6_0) diff --git a/server/src/main/java/com/genymobile/scrcpy/audio/AudioEncoder.java b/server/src/main/java/com/genymobile/scrcpy/audio/AudioEncoder.java index 33177228..267be60a 100644 --- a/server/src/main/java/com/genymobile/scrcpy/audio/AudioEncoder.java +++ b/server/src/main/java/com/genymobile/scrcpy/audio/AudioEncoder.java @@ -55,9 +55,6 @@ public final class AudioEncoder implements AsyncProcessor { private final List codecOptions; private final String encoderName; - private boolean recreatePts; - private long previousPts; - // Capacity of 64 is in practice "infinite" (it is limited by the number of available MediaCodec buffers, typically 4). // So many pending tasks would lead to an unacceptable delay anyway. private final BlockingQueue inputTasks = new ArrayBlockingQueue<>(64); @@ -121,9 +118,6 @@ public final class AudioEncoder implements AsyncProcessor { OutputTask task = outputTasks.take(); ByteBuffer buffer = mediaCodec.getOutputBuffer(task.index); try { - if (recreatePts) { - fixTimestamp(task.bufferInfo); - } streamer.writePacket(buffer, task.bufferInfo); } finally { mediaCodec.releaseOutputBuffer(task.index, false); @@ -131,25 +125,6 @@ public final class AudioEncoder implements AsyncProcessor { } } - private void fixTimestamp(MediaCodec.BufferInfo bufferInfo) { - assert recreatePts; - - if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) { - // Config packet, nothing to fix - return; - } - - long pts = bufferInfo.presentationTimeUs; - if (previousPts != 0) { - long now = System.nanoTime() / 1000; - // This specific encoder produces PTS matching the exact number of samples - long duration = pts - previousPts; - bufferInfo.presentationTimeUs = now - duration; - } - - previousPts = pts; - } - @Override public void start(TerminationListener listener) { thread = new Thread(() -> { @@ -219,12 +194,6 @@ public final class AudioEncoder implements AsyncProcessor { Codec codec = streamer.getCodec(); mediaCodec = createMediaCodec(codec, encoderName); - // The default OPUS and FLAC encoders overwrite the input PTS with a value that matches the number of samples. This is not the behavior - // we want: it ignores any audio clock drift and hard silences (packets not produced on silence). To work around this behavior, - // regenerate PTS based on the current time and the packet duration. - String codecName = mediaCodec.getCanonicalName(); - recreatePts = "c2.android.opus.encoder".equals(codecName) || "c2.android.flac.encoder".equals(codecName); - mediaCodecThread = new HandlerThread("media-codec"); mediaCodecThread.start(); diff --git a/server/src/main/java/com/genymobile/scrcpy/audio/AudioSource.java b/server/src/main/java/com/genymobile/scrcpy/audio/AudioSource.java index d16b5e38..6082f20e 100644 --- a/server/src/main/java/com/genymobile/scrcpy/audio/AudioSource.java +++ b/server/src/main/java/com/genymobile/scrcpy/audio/AudioSource.java @@ -1,38 +1,20 @@ package com.genymobile.scrcpy.audio; -import android.annotation.SuppressLint; -import android.media.MediaRecorder; - -@SuppressLint("InlinedApi") public enum AudioSource { - OUTPUT("output", MediaRecorder.AudioSource.REMOTE_SUBMIX), - MIC("mic", MediaRecorder.AudioSource.MIC), - PLAYBACK("playback", -1), - MIC_UNPROCESSED("mic-unprocessed", MediaRecorder.AudioSource.UNPROCESSED), - MIC_CAMCORDER("mic-camcorder", MediaRecorder.AudioSource.CAMCORDER), - MIC_VOICE_RECOGNITION("mic-voice-recognition", MediaRecorder.AudioSource.VOICE_RECOGNITION), - MIC_VOICE_COMMUNICATION("mic-voice-communication", MediaRecorder.AudioSource.VOICE_COMMUNICATION), - VOICE_CALL("voice-call", MediaRecorder.AudioSource.VOICE_CALL), - VOICE_CALL_UPLINK("voice-call-uplink", MediaRecorder.AudioSource.VOICE_UPLINK), - VOICE_CALL_DOWNLINK("voice-call-downlink", MediaRecorder.AudioSource.VOICE_DOWNLINK), - VOICE_PERFORMANCE("voice-performance", MediaRecorder.AudioSource.VOICE_PERFORMANCE); + OUTPUT("output"), + MIC("mic"), + PLAYBACK("playback"); private final String name; - private final int directAudioSource; - AudioSource(String name, int directAudioSource) { + AudioSource(String name) { this.name = name; - this.directAudioSource = directAudioSource; } public boolean isDirect() { return this != PLAYBACK; } - public int getDirectAudioSource() { - return directAudioSource; - } - public static AudioSource findByName(String name) { for (AudioSource audioSource : AudioSource.values()) { if (name.equals(audioSource.name)) { diff --git a/server/src/main/java/com/genymobile/scrcpy/control/ControlMessageReader.java b/server/src/main/java/com/genymobile/scrcpy/control/ControlMessageReader.java index 830a7ec7..e503ec61 100644 --- a/server/src/main/java/com/genymobile/scrcpy/control/ControlMessageReader.java +++ b/server/src/main/java/com/genymobile/scrcpy/control/ControlMessageReader.java @@ -112,9 +112,8 @@ public class ControlMessageReader { private ControlMessage parseInjectScrollEvent() throws IOException { Position position = parsePosition(); - // Binary.i16FixedPointToFloat() decodes values assuming the full range is [-1, 1], but the actual range is [-16, 16]. - float hScroll = Binary.i16FixedPointToFloat(dis.readShort()) * 16; - float vScroll = Binary.i16FixedPointToFloat(dis.readShort()) * 16; + float hScroll = Binary.i16FixedPointToFloat(dis.readShort()); + float vScroll = Binary.i16FixedPointToFloat(dis.readShort()); int buttons = dis.readInt(); return ControlMessage.createInjectScrollEvent(position, hScroll, vScroll, buttons); } diff --git a/server/src/main/java/com/genymobile/scrcpy/control/Controller.java b/server/src/main/java/com/genymobile/scrcpy/control/Controller.java index b4a8e3ca..5e64a4c5 100644 --- a/server/src/main/java/com/genymobile/scrcpy/control/Controller.java +++ b/server/src/main/java/com/genymobile/scrcpy/control/Controller.java @@ -6,7 +6,6 @@ import com.genymobile.scrcpy.CleanUp; import com.genymobile.scrcpy.Options; import com.genymobile.scrcpy.device.Device; import com.genymobile.scrcpy.device.DeviceApp; -import com.genymobile.scrcpy.device.DisplayInfo; import com.genymobile.scrcpy.device.Point; import com.genymobile.scrcpy.device.Position; import com.genymobile.scrcpy.device.Size; @@ -18,6 +17,7 @@ import com.genymobile.scrcpy.wrappers.ClipboardManager; import com.genymobile.scrcpy.wrappers.InputManager; import com.genymobile.scrcpy.wrappers.ServiceManager; +import android.content.IOnPrimaryClipChangedListener; import android.content.Intent; import android.os.Build; import android.os.SystemClock; @@ -114,20 +114,22 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener { Ln.w("Input events are not supported for secondary displays before Android 10"); } - // Make sure the clipboard manager is always created from the main thread (even if clipboardAutosync is disabled) - ClipboardManager clipboardManager = ServiceManager.getClipboardManager(); if (clipboardAutosync) { // If control and autosync are enabled, synchronize Android clipboard to the computer automatically + ClipboardManager clipboardManager = ServiceManager.getClipboardManager(); if (clipboardManager != null) { - clipboardManager.addPrimaryClipChangedListener(() -> { - if (isSettingClipboard.get()) { - // This is a notification for the change we are currently applying, ignore it - return; - } - String text = Device.getClipboardText(); - if (text != null) { - DeviceMessage msg = DeviceMessage.createClipboard(text); - sender.send(msg); + clipboardManager.addPrimaryClipChangedListener(new IOnPrimaryClipChangedListener.Stub() { + @Override + public void dispatchPrimaryClipChanged() { + if (isSettingClipboard.get()) { + // This is a notification for the change we are currently applying, ignore it + return; + } + String text = Device.getClipboardText(); + if (text != null) { + DeviceMessage msg = DeviceMessage.createClipboard(text); + sender.send(msg); + } } }); } else { @@ -154,34 +156,8 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener { private UhidManager getUhidManager() { if (uhidManager == null) { - int uhidDisplayId = displayId; - if (Build.VERSION.SDK_INT >= AndroidVersions.API_35_ANDROID_15) { - if (displayId == Device.DISPLAY_ID_NONE) { - // Mirroring a new virtual display id (using --new-display-id feature) on Android >= 15, where the UHID mouse pointer can be - // associated to the virtual display - try { - // Wait for at most 1 second until a virtual display id is known - DisplayData data = waitDisplayData(1000); - if (data != null) { - uhidDisplayId = data.virtualDisplayId; - } - } catch (InterruptedException e) { - // do nothing - } - } - } - - String displayUniqueId = null; - if (uhidDisplayId > 0) { - // Ignore Device.DISPLAY_ID_NONE and 0 (main display) - DisplayInfo displayInfo = ServiceManager.getDisplayManager().getDisplayInfo(uhidDisplayId); - if (displayInfo != null) { - displayUniqueId = displayInfo.getUniqueId(); - } - } - uhidManager = new UhidManager(sender, displayUniqueId); + uhidManager = new UhidManager(sender); } - return uhidManager; } @@ -723,9 +699,7 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener { if (timeout < 0) { return null; } - if (timeout > 0) { - displayDataAvailable.wait(timeout); - } + displayDataAvailable.wait(timeout); data = displayData.get(); } diff --git a/server/src/main/java/com/genymobile/scrcpy/control/UhidManager.java b/server/src/main/java/com/genymobile/scrcpy/control/UhidManager.java index 20532c0b..c4867a3f 100644 --- a/server/src/main/java/com/genymobile/scrcpy/control/UhidManager.java +++ b/server/src/main/java/com/genymobile/scrcpy/control/UhidManager.java @@ -3,7 +3,6 @@ package com.genymobile.scrcpy.control; import com.genymobile.scrcpy.AndroidVersions; import com.genymobile.scrcpy.util.Ln; import com.genymobile.scrcpy.util.StringUtils; -import com.genymobile.scrcpy.wrappers.ServiceManager; import android.os.Build; import android.os.HandlerThread; @@ -32,20 +31,14 @@ public final class UhidManager { private static final int SIZE_OF_UHID_EVENT = 4380; // sizeof(struct uhid_event) - // Must be unique across the system - private static final String INPUT_PORT = "scrcpy:" + Os.getpid(); - - private final String displayUniqueId; - private final ArrayMap fds = new ArrayMap<>(); private final ByteBuffer buffer = ByteBuffer.allocate(SIZE_OF_UHID_EVENT).order(ByteOrder.nativeOrder()); private final DeviceMessageSender sender; private final MessageQueue queue; - public UhidManager(DeviceMessageSender sender, String displayUniqueId) { + public UhidManager(DeviceMessageSender sender) { this.sender = sender; - this.displayUniqueId = displayUniqueId; if (Build.VERSION.SDK_INT >= AndroidVersions.API_23_ANDROID_6_0) { HandlerThread thread = new HandlerThread("UHidManager"); thread.start(); @@ -59,22 +52,15 @@ public final class UhidManager { try { FileDescriptor fd = Os.open("/dev/uhid", OsConstants.O_RDWR, 0); try { - // First UHID device added - boolean firstDevice = fds.isEmpty(); - FileDescriptor old = fds.put(id, fd); if (old != null) { Ln.w("Duplicate UHID id: " + id); close(old); } - String phys = mustUseInputPort() ? INPUT_PORT : null; - byte[] req = buildUhidCreate2Req(vendorId, productId, name, reportDesc, phys); + byte[] req = buildUhidCreate2Req(vendorId, productId, name, reportDesc); Os.write(fd, req, 0, req.length); - if (firstDevice) { - addUniqueIdAssociation(); - } registerUhidListener(id, fd); } catch (Exception e) { close(fd); @@ -162,7 +148,7 @@ public final class UhidManager { } } - private static byte[] buildUhidCreate2Req(int vendorId, int productId, String name, byte[] reportDesc, String phys) { + private static byte[] buildUhidCreate2Req(int vendorId, int productId, String name, byte[] reportDesc) { /* * struct uhid_event { * uint32_t type; @@ -184,23 +170,17 @@ public final class UhidManager { * } __attribute__((__packed__)); */ + byte[] empty = new byte[256]; ByteBuffer buf = ByteBuffer.allocate(280 + reportDesc.length).order(ByteOrder.nativeOrder()); buf.putInt(UHID_CREATE2); String actualName = name.isEmpty() ? "scrcpy" : name; - byte[] nameBytes = actualName.getBytes(StandardCharsets.UTF_8); - int nameLen = StringUtils.getUtf8TruncationIndex(nameBytes, 127); - assert nameLen <= 127; - buf.put(nameBytes, 0, nameLen); + byte[] utf8Name = actualName.getBytes(StandardCharsets.UTF_8); + int len = StringUtils.getUtf8TruncationIndex(utf8Name, 127); + assert len <= 127; + buf.put(utf8Name, 0, len); + buf.put(empty, 0, 256 - len); - if (phys != null) { - buf.position(4 + 128); - byte[] physBytes = phys.getBytes(StandardCharsets.US_ASCII); - assert physBytes.length <= 63; - buf.put(physBytes); - } - - buf.position(4 + 256); buf.putShort((short) reportDesc.length); buf.putShort(BUS_VIRTUAL); buf.putInt(vendorId); @@ -239,26 +219,15 @@ public final class UhidManager { if (fd != null) { unregisterUhidListener(fd); close(fd); - - if (fds.isEmpty()) { - // Last UHID device removed - removeUniqueIdAssociation(); - } } else { Ln.w("Closing unknown UHID device: " + id); } } public void closeAll() { - if (fds.isEmpty()) { - return; - } - for (FileDescriptor fd : fds.values()) { close(fd); } - - removeUniqueIdAssociation(); } private static void close(FileDescriptor fd) { @@ -268,20 +237,4 @@ public final class UhidManager { Ln.e("Failed to close uhid: " + e.getMessage()); } } - - private boolean mustUseInputPort() { - return Build.VERSION.SDK_INT >= AndroidVersions.API_35_ANDROID_15 && displayUniqueId != null; - } - - private void addUniqueIdAssociation() { - if (mustUseInputPort()) { - ServiceManager.getInputManager().addUniqueIdAssociationByPort(INPUT_PORT, displayUniqueId); - } - } - - private void removeUniqueIdAssociation() { - if (mustUseInputPort()) { - ServiceManager.getInputManager().removeUniqueIdAssociationByPort(INPUT_PORT); - } - } } diff --git a/server/src/main/java/com/genymobile/scrcpy/device/DisplayInfo.java b/server/src/main/java/com/genymobile/scrcpy/device/DisplayInfo.java index 8d26b7ce..cdd4bab9 100644 --- a/server/src/main/java/com/genymobile/scrcpy/device/DisplayInfo.java +++ b/server/src/main/java/com/genymobile/scrcpy/device/DisplayInfo.java @@ -7,18 +7,16 @@ public final class DisplayInfo { private final int layerStack; private final int flags; private final int dpi; - private final String uniqueId; public static final int FLAG_SUPPORTS_PROTECTED_BUFFERS = 0x00000001; - public DisplayInfo(int displayId, Size size, int rotation, int layerStack, int flags, int dpi, String uniqueId) { + public DisplayInfo(int displayId, Size size, int rotation, int layerStack, int flags, int dpi) { this.displayId = displayId; this.size = size; this.rotation = rotation; this.layerStack = layerStack; this.flags = flags; this.dpi = dpi; - this.uniqueId = uniqueId; } public int getDisplayId() { @@ -44,8 +42,5 @@ public final class DisplayInfo { public int getDpi() { return dpi; } - - public String getUniqueId() { - return uniqueId; - } } + diff --git a/server/src/main/java/com/genymobile/scrcpy/device/Orientation.java b/server/src/main/java/com/genymobile/scrcpy/device/Orientation.java index 81168aae..c269750e 100644 --- a/server/src/main/java/com/genymobile/scrcpy/device/Orientation.java +++ b/server/src/main/java/com/genymobile/scrcpy/device/Orientation.java @@ -32,11 +32,9 @@ public enum Orientation { throw new IllegalArgumentException("Unknown orientation: " + name); } - public static Orientation fromRotation(int ccwRotation) { - assert ccwRotation >= 0 && ccwRotation < 4; - // Display rotation is expressed counter-clockwise, orientation is expressed clockwise - int cwRotation = (4 - ccwRotation) % 4; - return values()[cwRotation]; + public static Orientation fromRotation(int rotation) { + assert rotation >= 0 && rotation < 4; + return values()[rotation]; } public boolean isFlipped() { diff --git a/server/src/main/java/com/genymobile/scrcpy/util/LogUtils.java b/server/src/main/java/com/genymobile/scrcpy/util/LogUtils.java index 4f8927ec..088be7e7 100644 --- a/server/src/main/java/com/genymobile/scrcpy/util/LogUtils.java +++ b/server/src/main/java/com/genymobile/scrcpy/util/LogUtils.java @@ -120,39 +120,17 @@ public final class LogUtils { } } - private static boolean isCameraBackwardCompatible(CameraCharacteristics characteristics) { - int[] capabilities = characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES); - if (capabilities == null) { - return false; - } - - for (int capability : capabilities) { - if (capability == CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE) { - return true; - } - } - - return false; - } - public static String buildCameraListMessage(boolean includeSizes) { StringBuilder builder = new StringBuilder("List of cameras:"); CameraManager cameraManager = ServiceManager.getCameraManager(); try { String[] cameraIds = cameraManager.getCameraIdList(); - if (cameraIds.length == 0) { + if (cameraIds == null || cameraIds.length == 0) { builder.append("\n (none)"); } else { for (String id : cameraIds) { - CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(id); - - if (!isCameraBackwardCompatible(characteristics)) { - // Ignore depth cameras as suggested by official documentation - // - continue; - } - builder.append("\n --camera-id=").append(id); + CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(id); int facing = characteristics.get(CameraCharacteristics.LENS_FACING); builder.append(" (").append(getCameraFacingName(facing)).append(", "); @@ -163,10 +141,8 @@ public final class LogUtils { try { // Capture frame rates for low-FPS mode are the same for every resolution Range[] lowFpsRanges = characteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES); - if (lowFpsRanges != null) { - SortedSet uniqueLowFps = getUniqueSet(lowFpsRanges); - builder.append(", fps=").append(uniqueLowFps); - } + SortedSet uniqueLowFps = getUniqueSet(lowFpsRanges); + builder.append(", fps=").append(uniqueLowFps); } catch (Exception e) { // Some devices may provide invalid ranges, causing an IllegalArgumentException "lower must be less than or equal to upper" Ln.w("Could not get available frame rates for camera " + id, e); diff --git a/server/src/main/java/com/genymobile/scrcpy/video/DisplaySizeMonitor.java b/server/src/main/java/com/genymobile/scrcpy/video/DisplaySizeMonitor.java index 3d7cccfe..ff863aa8 100644 --- a/server/src/main/java/com/genymobile/scrcpy/video/DisplaySizeMonitor.java +++ b/server/src/main/java/com/genymobile/scrcpy/video/DisplaySizeMonitor.java @@ -23,9 +23,7 @@ public class DisplaySizeMonitor { // On Android 14, DisplayListener may be broken (it never sends events). This is fixed in recent Android 14 upgrades, but we can't really // detect it directly, so register a DisplayWindowListener (introduced in Android 11) to listen to configuration changes instead. - // It has been broken again after an Android 15 upgrade: - // So use the default method only before Android 14. - private static final boolean USE_DEFAULT_METHOD = Build.VERSION.SDK_INT < AndroidVersions.API_34_ANDROID_14; + private static final boolean USE_DEFAULT_METHOD = Build.VERSION.SDK_INT != AndroidVersions.API_34_ANDROID_14; private DisplayManager.DisplayListenerHandle displayListenerHandle; private HandlerThread handlerThread; diff --git a/server/src/main/java/com/genymobile/scrcpy/video/NewDisplayCapture.java b/server/src/main/java/com/genymobile/scrcpy/video/NewDisplayCapture.java index 792b3a8a..033d6b9a 100644 --- a/server/src/main/java/com/genymobile/scrcpy/video/NewDisplayCapture.java +++ b/server/src/main/java/com/genymobile/scrcpy/video/NewDisplayCapture.java @@ -49,7 +49,6 @@ public class NewDisplayCapture extends SurfaceCapture { private Size mainDisplaySize; private int mainDisplayDpi; private int maxSize; - private int displayImePolicy; private final Rect crop; private final boolean captureOrientationLocked; private final Orientation captureOrientation; @@ -69,7 +68,6 @@ public class NewDisplayCapture extends SurfaceCapture { this.newDisplay = options.getNewDisplay(); assert newDisplay != null; this.maxSize = options.getMaxSize(); - this.displayImePolicy = options.getDisplayImePolicy(); this.crop = options.getCrop(); assert options.getCaptureOrientationLock() != null; this.captureOrientationLocked = options.getCaptureOrientationLock() != Orientation.Lock.Unlocked; @@ -193,10 +191,6 @@ public class NewDisplayCapture extends SurfaceCapture { virtualDisplayId = virtualDisplay.getDisplay().getDisplayId(); Ln.i("New display: " + displaySize.getWidth() + "x" + displaySize.getHeight() + "/" + dpi + " (id=" + virtualDisplayId + ")"); - if (displayImePolicy != -1) { - ServiceManager.getWindowManager().setDisplayImePolicy(virtualDisplayId, displayImePolicy); - } - displaySizeMonitor.start(virtualDisplayId, this::invalidate); } catch (Exception e) { Ln.e("Could not create display", e); diff --git a/server/src/main/java/com/genymobile/scrcpy/wrappers/ClipboardManager.java b/server/src/main/java/com/genymobile/scrcpy/wrappers/ClipboardManager.java index 54936122..791df0f8 100644 --- a/server/src/main/java/com/genymobile/scrcpy/wrappers/ClipboardManager.java +++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/ClipboardManager.java @@ -1,43 +1,270 @@ package com.genymobile.scrcpy.wrappers; +import com.genymobile.scrcpy.AndroidVersions; import com.genymobile.scrcpy.FakeContext; +import com.genymobile.scrcpy.util.Ln; import android.content.ClipData; -import android.content.Context; +import android.content.IOnPrimaryClipChangedListener; +import android.os.Build; +import android.os.IInterface; + +import java.lang.reflect.Method; public final class ClipboardManager { - private final android.content.ClipboardManager manager; + private final IInterface manager; + private Method getPrimaryClipMethod; + private Method setPrimaryClipMethod; + private Method addPrimaryClipChangedListener; + private int getMethodVersion; + private int setMethodVersion; + private int addListenerMethodVersion; static ClipboardManager create() { - android.content.ClipboardManager manager = (android.content.ClipboardManager) FakeContext.get().getSystemService(Context.CLIPBOARD_SERVICE); - if (manager == null) { + IInterface clipboard = ServiceManager.getService("clipboard", "android.content.IClipboard"); + if (clipboard == null) { // Some devices have no clipboard manager // // return null; } - return new ClipboardManager(manager); + return new ClipboardManager(clipboard); } - private ClipboardManager(android.content.ClipboardManager manager) { + private ClipboardManager(IInterface manager) { this.manager = manager; } + private Method getGetPrimaryClipMethod() throws NoSuchMethodException { + if (getPrimaryClipMethod == null) { + if (Build.VERSION.SDK_INT < AndroidVersions.API_29_ANDROID_10) { + getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class); + return getPrimaryClipMethod; + } + + try { + getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class, int.class); + getMethodVersion = 0; + return getPrimaryClipMethod; + } catch (NoSuchMethodException e) { + // fall-through + } + + try { + getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class, String.class, int.class); + getMethodVersion = 1; + return getPrimaryClipMethod; + } catch (NoSuchMethodException e) { + // fall-through + } + + try { + getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class, String.class, int.class, int.class); + getMethodVersion = 2; + return getPrimaryClipMethod; + } catch (NoSuchMethodException e) { + // fall-through + } + + try { + getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class, int.class, String.class); + getMethodVersion = 3; + return getPrimaryClipMethod; + } catch (NoSuchMethodException e) { + // fall-through + } + + try { + getPrimaryClipMethod = manager.getClass() + .getMethod("getPrimaryClip", String.class, String.class, int.class, int.class, boolean.class); + getMethodVersion = 4; + return getPrimaryClipMethod; + } catch (NoSuchMethodException e) { + // fall-through + } + + try { + getPrimaryClipMethod = manager.getClass() + .getMethod("getPrimaryClip", String.class, String.class, String.class, String.class, int.class, int.class, boolean.class); + getMethodVersion = 5; + return getPrimaryClipMethod; + } catch (NoSuchMethodException e) { + // fall-through + } + + getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class, String.class, int.class, int.class, String.class); + getMethodVersion = 6; + } + return getPrimaryClipMethod; + } + + private Method getSetPrimaryClipMethod() throws NoSuchMethodException { + if (setPrimaryClipMethod == null) { + if (Build.VERSION.SDK_INT < AndroidVersions.API_29_ANDROID_10) { + setPrimaryClipMethod = manager.getClass().getMethod("setPrimaryClip", ClipData.class, String.class); + return setPrimaryClipMethod; + } + + try { + setPrimaryClipMethod = manager.getClass().getMethod("setPrimaryClip", ClipData.class, String.class, int.class); + setMethodVersion = 0; + return setPrimaryClipMethod; + } catch (NoSuchMethodException e1) { + // fall-through + } + + try { + setPrimaryClipMethod = manager.getClass().getMethod("setPrimaryClip", ClipData.class, String.class, String.class, int.class); + setMethodVersion = 1; + return setPrimaryClipMethod; + } catch (NoSuchMethodException e2) { + // fall-through + } + + try { + setPrimaryClipMethod = manager.getClass() + .getMethod("setPrimaryClip", ClipData.class, String.class, String.class, int.class, int.class); + setMethodVersion = 2; + return setPrimaryClipMethod; + } catch (NoSuchMethodException e3) { + // fall-through + } + + setPrimaryClipMethod = manager.getClass() + .getMethod("setPrimaryClip", ClipData.class, String.class, String.class, int.class, int.class, boolean.class); + setMethodVersion = 3; + } + return setPrimaryClipMethod; + } + + private static ClipData getPrimaryClip(Method method, int methodVersion, IInterface manager) throws ReflectiveOperationException { + if (Build.VERSION.SDK_INT < AndroidVersions.API_29_ANDROID_10) { + return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME); + } + + switch (methodVersion) { + case 0: + return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, FakeContext.ROOT_UID); + case 1: + return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID); + case 2: + return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID, 0); + case 3: + return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, FakeContext.ROOT_UID, null); + case 4: + // The last boolean parameter is "userOperate" + return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID, 0, true); + case 5: + return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, null, null, null, FakeContext.ROOT_UID, 0, true); + default: + return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID, 0, null); + } + } + + private static void setPrimaryClip(Method method, int methodVersion, IInterface manager, ClipData clipData) throws ReflectiveOperationException { + if (Build.VERSION.SDK_INT < AndroidVersions.API_29_ANDROID_10) { + method.invoke(manager, clipData, FakeContext.PACKAGE_NAME); + return; + } + + switch (methodVersion) { + case 0: + method.invoke(manager, clipData, FakeContext.PACKAGE_NAME, FakeContext.ROOT_UID); + break; + case 1: + method.invoke(manager, clipData, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID); + break; + case 2: + method.invoke(manager, clipData, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID, 0); + break; + default: + // The last boolean parameter is "userOperate" + method.invoke(manager, clipData, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID, 0, true); + } + } + public CharSequence getText() { - ClipData clipData = manager.getPrimaryClip(); - if (clipData == null || clipData.getItemCount() == 0) { + try { + Method method = getGetPrimaryClipMethod(); + ClipData clipData = getPrimaryClip(method, getMethodVersion, manager); + if (clipData == null || clipData.getItemCount() == 0) { + return null; + } + return clipData.getItemAt(0).getText(); + } catch (ReflectiveOperationException e) { + Ln.e("Could not invoke method", e); return null; } - return clipData.getItemAt(0).getText(); } public boolean setText(CharSequence text) { - ClipData clipData = ClipData.newPlainText(null, text); - manager.setPrimaryClip(clipData); - return true; + try { + Method method = getSetPrimaryClipMethod(); + ClipData clipData = ClipData.newPlainText(null, text); + setPrimaryClip(method, setMethodVersion, manager, clipData); + return true; + } catch (ReflectiveOperationException e) { + Ln.e("Could not invoke method", e); + return false; + } } - public void addPrimaryClipChangedListener(android.content.ClipboardManager.OnPrimaryClipChangedListener listener) { - manager.addPrimaryClipChangedListener(listener); + private static void addPrimaryClipChangedListener(Method method, int methodVersion, IInterface manager, IOnPrimaryClipChangedListener listener) + throws ReflectiveOperationException { + if (Build.VERSION.SDK_INT < AndroidVersions.API_29_ANDROID_10) { + method.invoke(manager, listener, FakeContext.PACKAGE_NAME); + return; + } + + switch (methodVersion) { + case 0: + method.invoke(manager, listener, FakeContext.PACKAGE_NAME, FakeContext.ROOT_UID); + break; + case 1: + method.invoke(manager, listener, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID); + break; + default: + method.invoke(manager, listener, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID, 0); + break; + } + } + + private Method getAddPrimaryClipChangedListener() throws NoSuchMethodException { + if (addPrimaryClipChangedListener == null) { + if (Build.VERSION.SDK_INT < AndroidVersions.API_29_ANDROID_10) { + addPrimaryClipChangedListener = manager.getClass() + .getMethod("addPrimaryClipChangedListener", IOnPrimaryClipChangedListener.class, String.class); + } else { + try { + addPrimaryClipChangedListener = manager.getClass() + .getMethod("addPrimaryClipChangedListener", IOnPrimaryClipChangedListener.class, String.class, int.class); + addListenerMethodVersion = 0; + } catch (NoSuchMethodException e1) { + try { + addPrimaryClipChangedListener = manager.getClass() + .getMethod("addPrimaryClipChangedListener", IOnPrimaryClipChangedListener.class, String.class, String.class, + int.class); + addListenerMethodVersion = 1; + } catch (NoSuchMethodException e2) { + addPrimaryClipChangedListener = manager.getClass() + .getMethod("addPrimaryClipChangedListener", IOnPrimaryClipChangedListener.class, String.class, String.class, + int.class, int.class); + addListenerMethodVersion = 2; + } + } + } + } + return addPrimaryClipChangedListener; + } + + public boolean addPrimaryClipChangedListener(IOnPrimaryClipChangedListener listener) { + try { + Method method = getAddPrimaryClipChangedListener(); + addPrimaryClipChangedListener(method, addListenerMethodVersion, manager, listener); + return true; + } catch (ReflectiveOperationException e) { + Ln.e("Could not invoke method", e); + return false; + } } } diff --git a/server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayManager.java b/server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayManager.java index a12470a4..d44ac608 100644 --- a/server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayManager.java +++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayManager.java @@ -46,7 +46,6 @@ public final class DisplayManager { } private final Object manager; // instance of hidden class android.hardware.display.DisplayManagerGlobal - private Method getDisplayInfoMethod; private Method createVirtualDisplayMethod; private Method requestDisplayPowerMethod; @@ -82,7 +81,7 @@ public final class DisplayManager { int density = Integer.parseInt(m.group(5)); int layerStack = Integer.parseInt(m.group(6)); - return new DisplayInfo(displayId, new Size(width, height), rotation, layerStack, flags, density, null); + return new DisplayInfo(displayId, new Size(width, height), rotation, layerStack, flags, density); } private static DisplayInfo getDisplayInfoFromDumpsysDisplay(int displayId) { @@ -96,12 +95,12 @@ public final class DisplayManager { } private static int parseDisplayFlags(String text) { + Pattern regex = Pattern.compile("FLAG_[A-Z_]+"); if (text == null) { return 0; } int flags = 0; - Pattern regex = Pattern.compile("FLAG_[A-Z_]+"); Matcher m = regex.matcher(text); while (m.find()) { String flagString = m.group(); @@ -115,18 +114,9 @@ public final class DisplayManager { return flags; } - // getDisplayInfo() may be used from both the Controller thread and the video (main) thread - private synchronized Method getGetDisplayInfoMethod() throws NoSuchMethodException { - if (getDisplayInfoMethod == null) { - getDisplayInfoMethod = manager.getClass().getMethod("getDisplayInfo", int.class); - } - return getDisplayInfoMethod; - } - public DisplayInfo getDisplayInfo(int displayId) { try { - Method method = getGetDisplayInfoMethod(); - Object displayInfo = method.invoke(manager, displayId); + Object displayInfo = manager.getClass().getMethod("getDisplayInfo", int.class).invoke(manager, displayId); if (displayInfo == null) { // fallback when displayInfo is null return getDisplayInfoFromDumpsysDisplay(displayId); @@ -139,8 +129,7 @@ public final class DisplayManager { int layerStack = cls.getDeclaredField("layerStack").getInt(displayInfo); int flags = cls.getDeclaredField("flags").getInt(displayInfo); int dpi = cls.getDeclaredField("logicalDensityDpi").getInt(displayInfo); - String uniqueId = (String) cls.getDeclaredField("uniqueId").get(displayInfo); - return new DisplayInfo(displayId, new Size(width, height), rotation, layerStack, flags, dpi, uniqueId); + return new DisplayInfo(displayId, new Size(width, height), rotation, layerStack, flags, dpi); } catch (ReflectiveOperationException e) { throw new AssertionError(e); } diff --git a/server/src/main/java/com/genymobile/scrcpy/wrappers/InputManager.java b/server/src/main/java/com/genymobile/scrcpy/wrappers/InputManager.java index f55648d5..5c5ba56c 100644 --- a/server/src/main/java/com/genymobile/scrcpy/wrappers/InputManager.java +++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/InputManager.java @@ -1,15 +1,11 @@ package com.genymobile.scrcpy.wrappers; -import com.genymobile.scrcpy.AndroidVersions; -import com.genymobile.scrcpy.FakeContext; import com.genymobile.scrcpy.util.Ln; import android.annotation.SuppressLint; -import android.annotation.TargetApi; import android.view.InputEvent; import android.view.MotionEvent; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @SuppressLint("PrivateApi,DiscouragedPrivateApi") @@ -19,28 +15,39 @@ public final class InputManager { public static final int INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT = 1; public static final int INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH = 2; - private final android.hardware.input.InputManager manager; - private long lastPermissionLogDate; + private final Object manager; + private Method injectInputEventMethod; - private static Method injectInputEventMethod; private static Method setDisplayIdMethod; private static Method setActionButtonMethod; - private static Method addUniqueIdAssociationByPortMethod; - private static Method removeUniqueIdAssociationByPortMethod; static InputManager create() { - android.hardware.input.InputManager manager = (android.hardware.input.InputManager) FakeContext.get() - .getSystemService(FakeContext.INPUT_SERVICE); - return new InputManager(manager); + try { + Class inputManagerClass = getInputManagerClass(); + Method getInstanceMethod = inputManagerClass.getDeclaredMethod("getInstance"); + Object im = getInstanceMethod.invoke(null); + return new InputManager(im); + } catch (ReflectiveOperationException e) { + throw new AssertionError(e); + } } - private InputManager(android.hardware.input.InputManager manager) { + private static Class getInputManagerClass() { + try { + // Parts of the InputManager class have been moved to a new InputManagerGlobal class in Android 14 preview + return Class.forName("android.hardware.input.InputManagerGlobal"); + } catch (ClassNotFoundException e) { + return android.hardware.input.InputManager.class; + } + } + + private InputManager(Object manager) { this.manager = manager; } - private static Method getInjectInputEventMethod() throws NoSuchMethodException { + private Method getInjectInputEventMethod() throws NoSuchMethodException { if (injectInputEventMethod == null) { - injectInputEventMethod = android.hardware.input.InputManager.class.getMethod("injectInputEvent", InputEvent.class, int.class); + injectInputEventMethod = manager.getClass().getMethod("injectInputEvent", InputEvent.class, int.class); } return injectInputEventMethod; } @@ -50,23 +57,6 @@ public final class InputManager { Method method = getInjectInputEventMethod(); return (boolean) method.invoke(manager, inputEvent, mode); } catch (ReflectiveOperationException e) { - if (e instanceof InvocationTargetException) { - Throwable cause = e.getCause(); - if (cause instanceof SecurityException) { - String message = e.getCause().getMessage(); - if (message != null && message.contains("INJECT_EVENTS permission")) { - // Do not flood the console, limit to one permission error log every 3 seconds - long now = System.currentTimeMillis(); - if (lastPermissionLogDate <= now - 3000) { - Ln.e(message); - Ln.e("Make sure you have enabled \"USB debugging (Security Settings)\" and then rebooted your device."); - lastPermissionLogDate = now; - } - // Do not print the stack trace - return false; - } - } - } Ln.e("Could not invoke method", e); return false; } @@ -107,40 +97,4 @@ public final class InputManager { return false; } } - - private static Method getAddUniqueIdAssociationByPortMethod() throws NoSuchMethodException { - if (addUniqueIdAssociationByPortMethod == null) { - addUniqueIdAssociationByPortMethod = android.hardware.input.InputManager.class.getMethod( - "addUniqueIdAssociationByPort", String.class, String.class); - } - return addUniqueIdAssociationByPortMethod; - } - - @TargetApi(AndroidVersions.API_35_ANDROID_15) - public void addUniqueIdAssociationByPort(String inputPort, String uniqueId) { - try { - Method method = getAddUniqueIdAssociationByPortMethod(); - method.invoke(manager, inputPort, uniqueId); - } catch (ReflectiveOperationException e) { - Ln.e("Cannot add unique id association by port", e); - } - } - - private static Method getRemoveUniqueIdAssociationByPortMethod() throws NoSuchMethodException { - if (removeUniqueIdAssociationByPortMethod == null) { - removeUniqueIdAssociationByPortMethod = android.hardware.input.InputManager.class.getMethod( - "removeUniqueIdAssociationByPort", String.class); - } - return removeUniqueIdAssociationByPortMethod; - } - - @TargetApi(AndroidVersions.API_35_ANDROID_15) - public void removeUniqueIdAssociationByPort(String inputPort) { - try { - Method method = getRemoveUniqueIdAssociationByPortMethod(); - method.invoke(manager, inputPort); - } catch (ReflectiveOperationException e) { - Ln.e("Cannot remove unique id association by port", e); - } - } } diff --git a/server/src/main/java/com/genymobile/scrcpy/wrappers/ServiceManager.java b/server/src/main/java/com/genymobile/scrcpy/wrappers/ServiceManager.java index b1123b55..a8a56dab 100644 --- a/server/src/main/java/com/genymobile/scrcpy/wrappers/ServiceManager.java +++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/ServiceManager.java @@ -54,8 +54,7 @@ public final class ServiceManager { return windowManager; } - // The DisplayManager may be used from both the Controller thread and the video (main) thread - public static synchronized DisplayManager getDisplayManager() { + public static DisplayManager getDisplayManager() { if (displayManager == null) { displayManager = DisplayManager.create(); } diff --git a/server/src/main/java/com/genymobile/scrcpy/wrappers/WindowManager.java b/server/src/main/java/com/genymobile/scrcpy/wrappers/WindowManager.java index 7ba5cc06..86dd83f2 100644 --- a/server/src/main/java/com/genymobile/scrcpy/wrappers/WindowManager.java +++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/WindowManager.java @@ -4,20 +4,14 @@ import com.genymobile.scrcpy.AndroidVersions; import com.genymobile.scrcpy.util.Ln; import android.annotation.TargetApi; -import android.os.Build; import android.os.IInterface; +import android.view.IDisplayFoldListener; import android.view.IDisplayWindowListener; +import android.view.IRotationWatcher; import java.lang.reflect.Method; public final class WindowManager { - - @SuppressWarnings("checkstyle:LineLength") - // - public static final int DISPLAY_IME_POLICY_LOCAL = 0; - public static final int DISPLAY_IME_POLICY_FALLBACK_DISPLAY = 1; - public static final int DISPLAY_IME_POLICY_HIDE = 2; - private final IInterface manager; private Method getRotationMethod; @@ -30,9 +24,6 @@ public final class WindowManager { private Method thawDisplayRotationMethod; private int thawDisplayRotationMethodVersion; - private Method getDisplayImePolicyMethod; - private Method setDisplayImePolicyMethod; - static WindowManager create() { IInterface manager = ServiceManager.getService("window", "android.view.IWindowManager"); return new WindowManager(manager); @@ -191,6 +182,52 @@ public final class WindowManager { } } + public void registerRotationWatcher(IRotationWatcher rotationWatcher, int displayId) { + try { + Class cls = manager.getClass(); + try { + // display parameter added since this commit: + // https://android.googlesource.com/platform/frameworks/base/+/35fa3c26adcb5f6577849fd0df5228b1f67cf2c6%5E%21/#F1 + cls.getMethod("watchRotation", IRotationWatcher.class, int.class).invoke(manager, rotationWatcher, displayId); + } catch (NoSuchMethodException e) { + // old version + if (displayId != 0) { + Ln.e("Secondary display rotation not supported on this device"); + return; + } + cls.getMethod("watchRotation", IRotationWatcher.class).invoke(manager, rotationWatcher); + } + } catch (Exception e) { + Ln.e("Could not register rotation watcher", e); + } + } + + public void unregisterRotationWatcher(IRotationWatcher rotationWatcher) { + try { + manager.getClass().getMethod("removeRotationWatcher", IRotationWatcher.class).invoke(manager, rotationWatcher); + } catch (Exception e) { + Ln.e("Could not unregister rotation watcher", e); + } + } + + @TargetApi(AndroidVersions.API_29_ANDROID_10) + public void registerDisplayFoldListener(IDisplayFoldListener foldListener) { + try { + manager.getClass().getMethod("registerDisplayFoldListener", IDisplayFoldListener.class).invoke(manager, foldListener); + } catch (Exception e) { + Ln.e("Could not register display fold listener", e); + } + } + + @TargetApi(AndroidVersions.API_29_ANDROID_10) + public void unregisterDisplayFoldListener(IDisplayFoldListener foldListener) { + try { + manager.getClass().getMethod("unregisterDisplayFoldListener", IDisplayFoldListener.class).invoke(manager, foldListener); + } catch (Exception e) { + Ln.e("Could not unregister display fold listener", e); + } + } + @TargetApi(AndroidVersions.API_30_ANDROID_11) public int[] registerDisplayWindowListener(IDisplayWindowListener listener) { try { @@ -209,59 +246,4 @@ public final class WindowManager { Ln.e("Could not unregister display window listener", e); } } - - @TargetApi(AndroidVersions.API_29_ANDROID_10) - private Method getGetDisplayImePolicyMethod() throws NoSuchMethodException { - if (getDisplayImePolicyMethod == null) { - if (Build.VERSION.SDK_INT >= AndroidVersions.API_31_ANDROID_12) { - getDisplayImePolicyMethod = manager.getClass().getMethod("getDisplayImePolicy", int.class); - } else { - getDisplayImePolicyMethod = manager.getClass().getMethod("shouldShowIme", int.class); - } - } - return getDisplayImePolicyMethod; - } - - @TargetApi(AndroidVersions.API_29_ANDROID_10) - public int getDisplayImePolicy(int displayId) { - try { - Method method = getGetDisplayImePolicyMethod(); - if (Build.VERSION.SDK_INT >= AndroidVersions.API_31_ANDROID_12) { - return (int) method.invoke(manager, displayId); - } - boolean shouldShowIme = (boolean) method.invoke(manager, displayId); - return shouldShowIme ? DISPLAY_IME_POLICY_LOCAL : DISPLAY_IME_POLICY_FALLBACK_DISPLAY; - } catch (ReflectiveOperationException e) { - Ln.e("Could not invoke method", e); - return -1; - } - } - - @TargetApi(AndroidVersions.API_29_ANDROID_10) - private Method getSetDisplayImePolicyMethod() throws NoSuchMethodException { - if (setDisplayImePolicyMethod == null) { - if (Build.VERSION.SDK_INT >= AndroidVersions.API_31_ANDROID_12) { - setDisplayImePolicyMethod = manager.getClass().getMethod("setDisplayImePolicy", int.class, int.class); - } else { - setDisplayImePolicyMethod = manager.getClass().getMethod("setShouldShowIme", int.class, boolean.class); - } - } - return setDisplayImePolicyMethod; - } - - @TargetApi(AndroidVersions.API_29_ANDROID_10) - public void setDisplayImePolicy(int displayId, int displayImePolicy) { - try { - Method method = getSetDisplayImePolicyMethod(); - if (Build.VERSION.SDK_INT >= AndroidVersions.API_31_ANDROID_12) { - method.invoke(manager, displayId, displayImePolicy); - } else if (displayImePolicy != DISPLAY_IME_POLICY_HIDE) { - method.invoke(manager, displayId, displayImePolicy == DISPLAY_IME_POLICY_LOCAL); - } else { - Ln.w("DISPLAY_IME_POLICY_HIDE is not supported before Android 12"); - } - } catch (ReflectiveOperationException e) { - Ln.e("Could not invoke method", e); - } - } } diff --git a/server/src/test/java/com/genymobile/scrcpy/control/ControlMessageReaderTest.java b/server/src/test/java/com/genymobile/scrcpy/control/ControlMessageReaderTest.java index 0cc0a6b5..74df064f 100644 --- a/server/src/test/java/com/genymobile/scrcpy/control/ControlMessageReaderTest.java +++ b/server/src/test/java/com/genymobile/scrcpy/control/ControlMessageReaderTest.java @@ -125,7 +125,7 @@ public class ControlMessageReaderTest { dos.writeShort(1080); dos.writeShort(1920); dos.writeShort(0); // 0.0f encoded as i16 - dos.writeShort(0x8000); // -16.0f encoded as i16 (the range is [-16, 16]) + dos.writeShort(0x8000); // -1.0f encoded as i16 dos.writeInt(1); byte[] packet = bos.toByteArray(); @@ -139,7 +139,7 @@ public class ControlMessageReaderTest { Assert.assertEquals(1080, event.getPosition().getScreenSize().getWidth()); Assert.assertEquals(1920, event.getPosition().getScreenSize().getHeight()); Assert.assertEquals(0f, event.getHScroll(), 0f); - Assert.assertEquals(-16f, event.getVScroll(), 0f); + Assert.assertEquals(-1f, event.getVScroll(), 0f); Assert.assertEquals(1, event.getButtons()); Assert.assertEquals(-1, bis.read()); // EOS