From 6469054b15355e55ddfa713fa9cef5b88fa46358 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Thu, 12 Dec 2024 18:07:58 +0100 Subject: [PATCH 01/43] Revert "Remove apt update on GitHub Actions" This reverts commit 678025b31672c230575fe2dbc4a0d487d5010bb1. This avoids spurious errors on the CI: E: Unable to fetch some archives, maybe run apt-get update or try with --fix-missing? --- .github/workflows/release.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a77b7ff1..a5701b0a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -74,6 +74,7 @@ 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 \ @@ -99,6 +100,7 @@ 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 \ @@ -129,6 +131,7 @@ 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 \ @@ -162,6 +165,7 @@ 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 \ From f751274b1762a183d2848a86458b8a459b50250a Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Thu, 12 Dec 2024 17:52:17 +0100 Subject: [PATCH 02/43] Define both pkg-config and pkgconfig for meson In Meson cross-files, "pkgconfig" was deprecated in favor of "pkg-config" in meson 1.3.0. The new name is used since 85a94dd4b563e961304b2d9082932c5c1cc2e582 to avoid a warning, but then it fails with older versions of meson. To avoid the problem, define both pkg-config and pkgconfig. > For backward compatibility it is still allowed to define both with the > same value, in that case no deprecation warning is printed. --- .github/workflows/release.yml | 6 ------ cross_win32.txt | 2 ++ cross_win64.txt | 2 ++ 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a5701b0a..da021c6e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -137,9 +137,6 @@ jobs: 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 @@ -171,9 +168,6 @@ jobs: 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 diff --git a/cross_win32.txt b/cross_win32.txt index 05f9a86b..ddbc65f3 100644 --- a/cross_win32.txt +++ b/cross_win32.txt @@ -7,6 +7,8 @@ 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 86364ad6..a6f16e16 100644 --- a/cross_win64.txt +++ b/cross_win64.txt @@ -7,6 +7,8 @@ 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] From 17e205e54f8c975c18d3466ce2a9a5663bfbaf96 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Thu, 12 Dec 2024 17:59:36 +0100 Subject: [PATCH 03/43] Replace meson join_paths() by '/' A new '/' operator was introduced in Meson 0.49 to replace join_paths(): - - Refs #5658 --- app/meson.build | 10 +++++----- meson.build | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/meson.build b/app/meson.build index be02ebc1..85e88940 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: join_paths(datadir, 'icons/hicolor/256x256/apps')) + install_dir: datadir / 'icons/hicolor/256x256/apps') install_data('data/zsh-completion/_scrcpy', - install_dir: join_paths(datadir, 'zsh/site-functions')) + install_dir: datadir / 'zsh/site-functions') install_data('data/bash-completion/scrcpy', - install_dir: join_paths(datadir, 'bash-completion/completions')) + install_dir: datadir / 'bash-completion/completions') # Desktop entry file for application launchers if host_machine.system() == 'linux' # Install a launcher (ex: /usr/local/share/applications/scrcpy.desktop) install_data('data/scrcpy.desktop', - install_dir: join_paths(datadir, 'applications')) + install_dir: datadir / 'applications') install_data('data/scrcpy-console.desktop', - install_dir: join_paths(datadir, 'applications')) + install_dir: datadir / 'applications') endif diff --git a/meson.build b/meson.build index aa1a3a3b..84784814 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project('scrcpy', 'c', version: '3.1', - meson_version: '>= 0.48', + meson_version: '>= 0.49', default_options: [ 'c_std=c11', 'warning_level=2', From ec4e826976d977870fc35d33591fd6164ee9d792 Mon Sep 17 00:00:00 2001 From: Colin Kinloch Date: Thu, 12 Dec 2024 12:41:22 +0000 Subject: [PATCH 04/43] Set icon and server env paths for meson devenv This allows users to compile and run the project in a dev environment. meson setup x meson compile -C x meson devenv -C x scrcpy This is an alternative to `./run x`. PR #5658 Signed-off-by: Romain Vimont --- app/meson.build | 6 ++++++ server/meson.build | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/app/meson.build b/app/meson.build index 85e88940..f7df69eb 100644 --- a/app/meson.build +++ b/app/meson.build @@ -279,3 +279,9 @@ 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/server/meson.build b/server/meson.build index 42b97981..55828e2d 100644 --- a/server/meson.build +++ b/server/meson.build @@ -23,3 +23,9 @@ 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 From 69264703b11614d022c31096d70eec14870393c7 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sat, 14 Dec 2024 10:25:13 +0100 Subject: [PATCH 05/43] Add missing comments in workarounds The implementation of workarounds uses a lot of reflection code. For better readability, always write the equivalent using direct Java code. --- server/src/main/java/com/genymobile/scrcpy/Workarounds.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/server/src/main/java/com/genymobile/scrcpy/Workarounds.java b/server/src/main/java/com/genymobile/scrcpy/Workarounds.java index eec00a04..a5283a96 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Workarounds.java +++ b/server/src/main/java/com/genymobile/scrcpy/Workarounds.java @@ -132,10 +132,13 @@ 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); From dc2fcc46f516588f4575c1fe8cfeca3e57a1653c Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sat, 14 Dec 2024 10:15:20 +0100 Subject: [PATCH 06/43] Add workaround for Pico 4 Ultra Make ActivityThread.isSystem() return true to avoid a NullPointerException later. Refs #5659 comment Fixes #5659 --- server/src/main/java/com/genymobile/scrcpy/Workarounds.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/server/src/main/java/com/genymobile/scrcpy/Workarounds.java b/server/src/main/java/com/genymobile/scrcpy/Workarounds.java index a5283a96..fb4c1389 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Workarounds.java +++ b/server/src/main/java/com/genymobile/scrcpy/Workarounds.java @@ -42,6 +42,11 @@ 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); } From ea6a94d355b92b103da8931d175f2a4a35e8e301 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Tue, 17 Dec 2024 12:20:50 +0100 Subject: [PATCH 07/43] Fix mouse documentation formatting Make the format consistent with the shortcuts documentation. --- doc/mouse.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/mouse.md b/doc/mouse.md index 3607a92c..0bea4aea 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: From 48fc18e3806e6eb77772f9f89671884b40c61714 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Tue, 17 Dec 2024 12:21:59 +0100 Subject: [PATCH 08/43] Add must-know tips All users should be aware of the main shortcuts and the most important setting to improve performance. --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 09fa12b4..5eb59ba5 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,16 @@ 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. From 1fd57ede1f7caca8d9dad73b0fe778079fad73f1 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Tue, 17 Dec 2024 13:09:24 +0100 Subject: [PATCH 09/43] Move "screen off timeout" section in documentation Place the "screen off timeout" section right after "stay awake", as they serve a similar purpose. --- doc/device.md | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/doc/device.md b/doc/device.md index 42208faa..ab1e6ba4 100644 --- a/doc/device.md +++ b/doc/device.md @@ -34,6 +34,31 @@ 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 @@ -71,31 +96,6 @@ 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 From 5ae01749bf4fad9fb4d8bf7c879dc60f479c1013 Mon Sep 17 00:00:00 2001 From: Markus <65797058+headquarter8302@users.noreply.github.com> Date: Wed, 18 Dec 2024 21:12:58 -0400 Subject: [PATCH 10/43] Reintroduce WinGet install note This semantically reverts c27ab46efbcab0b9558a91e691d799ffef496c97. WinGet package has been fixed by: Refs #4027 PR #5686 Signed-off-by: Romain Vimont --- doc/windows.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/doc/windows.md b/doc/windows.md index ec7b904b..89b80727 100644 --- a/doc/windows.md +++ b/doc/windows.md @@ -20,6 +20,12 @@ 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 @@ -29,12 +35,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 From fb47b87eebbda65fd28e407cd2a6d33fea476fe3 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Fri, 20 Dec 2024 20:57:20 +0100 Subject: [PATCH 11/43] Fix pipe read return value The function incorrectly returned false, whereas its return type is ssize_t. --- app/src/util/process_intr.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/util/process_intr.c b/app/src/util/process_intr.c index d37bd5a5..641440ab 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 false; + return -1; } 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 false; + return -1; } ssize_t ret = sc_pipe_read_all(pipe, data, len); From 95c4f03c1bfd566b383780977b3473ebad6477ee Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Thu, 19 Dec 2024 12:25:20 +0100 Subject: [PATCH 12/43] Build static linux binary on Ubuntu 22.04 On Github Actions, ubuntu-latest now points to ubuntu-24.04, which uses a newer version of glibc (2.39). As a result, the binaries fail to work on systems with older versions of glibc, such as Debian Bookworm. To ensure better compatibility, continue building the static Linux binary on Ubuntu 22.04 (with glibc 2.35). Fixes #5689 --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index da021c6e..c90b7fb0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -84,7 +84,7 @@ jobs: run: release/test_client.sh build-linux-x86_64: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Check architecture run: | From 2f44da76f4767aec2f42e6882a30c62615e0f139 Mon Sep 17 00:00:00 2001 From: Simon Chan <1330321+yume-chan@users.noreply.github.com> Date: Sun, 15 Dec 2024 23:50:58 +0800 Subject: [PATCH 13/43] Filter out non-backward-compatible cameras PR #5669 Signed-off-by: Romain Vimont --- .../com/genymobile/scrcpy/util/LogUtils.java | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) 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 088be7e7..961b8da0 100644 --- a/server/src/main/java/com/genymobile/scrcpy/util/LogUtils.java +++ b/server/src/main/java/com/genymobile/scrcpy/util/LogUtils.java @@ -120,6 +120,21 @@ 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(); @@ -129,9 +144,16 @@ public final class LogUtils { builder.append("\n (none)"); } else { for (String id : cameraIds) { - builder.append("\n --camera-id=").append(id); CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(id); + if (!isCameraBackwardCompatible(characteristics)) { + // Ignore depth cameras as suggested by official documentation + // + continue; + } + + builder.append("\n --camera-id=").append(id); + int facing = characteristics.get(CameraCharacteristics.LENS_FACING); builder.append(" (").append(getCameraFacingName(facing)).append(", "); From 538764416099c803fcb041ecffa2c849829b7222 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sun, 22 Dec 2024 21:17:51 +0100 Subject: [PATCH 14/43] Ignore low-FPS ranges if not available Do not report an error if the returned FPS ranges array is null. Refs #5669 --- .../src/main/java/com/genymobile/scrcpy/util/LogUtils.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 961b8da0..701ae373 100644 --- a/server/src/main/java/com/genymobile/scrcpy/util/LogUtils.java +++ b/server/src/main/java/com/genymobile/scrcpy/util/LogUtils.java @@ -163,8 +163,10 @@ 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); - SortedSet uniqueLowFps = getUniqueSet(lowFpsRanges); - builder.append(", fps=").append(uniqueLowFps); + if (lowFpsRanges != null) { + 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); From e0423653c892a62342bf665473093a9a020e0ffc Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Mon, 23 Dec 2024 10:58:02 +0100 Subject: [PATCH 15/43] Remove useless null check The method CameraManager.getCameraIdList() is annotated with @NonNull. This fixes a warning reported by Android Studio. --- server/src/main/java/com/genymobile/scrcpy/util/LogUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 701ae373..4f8927ec 100644 --- a/server/src/main/java/com/genymobile/scrcpy/util/LogUtils.java +++ b/server/src/main/java/com/genymobile/scrcpy/util/LogUtils.java @@ -140,7 +140,7 @@ public final class LogUtils { CameraManager cameraManager = ServiceManager.getCameraManager(); try { String[] cameraIds = cameraManager.getCameraIdList(); - if (cameraIds == null || cameraIds.length == 0) { + if (cameraIds.length == 0) { builder.append("\n (none)"); } else { for (String id : cameraIds) { From 69858c6f437b1bfece96bc291c607de842837d36 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Mon, 23 Dec 2024 11:01:42 +0100 Subject: [PATCH 16/43] Build static linux binary on Ubuntu 20.04 Use the oldest Ubuntu version currently available in GitHub Actions to ensure maximum compatibility with older systems. Refs 95c4f03c1bfd566b383780977b3473ebad6477ee Refs #5689 --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c90b7fb0..b1fedda9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -84,7 +84,7 @@ jobs: run: release/test_client.sh build-linux-x86_64: - runs-on: ubuntu-22.04 + runs-on: ubuntu-20.04 steps: - name: Check architecture run: | From 5b1229a55f8e89facaeb0d3757e37c49d62e88fb Mon Sep 17 00:00:00 2001 From: Simon Chan <1330321+yume-chan@users.noreply.github.com> Date: Mon, 23 Dec 2024 01:12:54 +0800 Subject: [PATCH 17/43] Support older macOS versions in CI build Fixes #5649 Fixes #5697 Signed-off-by: Romain Vimont --- .github/workflows/release.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b1fedda9..5875c6bf 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -206,6 +206,13 @@ 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 @@ -242,6 +249,13 @@ 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 From af15c72f9caef4f829d337c35701d8bc00a58989 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Fri, 20 Dec 2024 20:58:41 +0100 Subject: [PATCH 18/43] Cleanup includes Improved manually with the help of neovim LSP warnings and iwyu: iwyu -Ibuilddir/app/ -Iapp/src/ app/src/XXX.c --- app/src/adb/adb.c | 5 +++-- app/src/adb/adb.h | 2 +- app/src/adb/adb_device.h | 1 - app/src/adb/adb_parser.c | 1 + app/src/adb/adb_parser.h | 4 ++-- app/src/adb/adb_tunnel.c | 4 ++-- app/src/audio_player.h | 4 +--- app/src/audio_regulator.c | 4 ++++ app/src/audio_regulator.h | 2 ++ app/src/cli.c | 2 ++ app/src/decoder.c | 8 +++----- app/src/decoder.h | 6 ++---- app/src/delay_buffer.c | 4 +--- app/src/delay_buffer.h | 1 + app/src/demuxer.c | 7 ++----- app/src/demuxer.h | 4 ---- app/src/device_msg.h | 4 ++-- app/src/display.c | 2 ++ app/src/display.h | 3 ++- app/src/events.c | 2 ++ app/src/file_pusher.c | 2 +- app/src/fps_counter.c | 1 + app/src/fps_counter.h | 2 +- app/src/frame_buffer.c | 2 -- app/src/frame_buffer.h | 1 + app/src/hid/hid_event.h | 1 + app/src/hid/hid_gamepad.c | 2 ++ app/src/hid/hid_gamepad.h | 1 + app/src/hid/hid_keyboard.c | 1 + app/src/hid/hid_keyboard.h | 1 + app/src/hid/hid_mouse.c | 2 ++ app/src/hid/hid_mouse.h | 2 -- app/src/icon.c | 11 ++++++++--- app/src/icon.h | 4 +--- app/src/input_events.h | 1 - app/src/input_manager.c | 6 +++++- app/src/input_manager.h | 6 +++--- app/src/keyboard_sdk.c | 5 +++++ app/src/main.c | 3 --- app/src/mouse_sdk.c | 2 +- app/src/mouse_sdk.h | 1 - app/src/opengl.c | 3 ++- app/src/options.c | 2 ++ app/src/options.h | 1 - app/src/packet_merger.c | 4 ++++ app/src/packet_merger.h | 2 +- app/src/receiver.c | 1 - app/src/recorder.c | 3 +++ app/src/recorder.h | 3 ++- app/src/scrcpy.c | 9 +++++---- app/src/scrcpy.h | 1 - app/src/screen.h | 10 ++++++---- app/src/server.c | 9 ++++----- app/src/server.h | 8 +++----- app/src/shortcut_mod.h | 1 + app/src/sys/unix/file.c | 3 ++- app/src/sys/unix/process.c | 2 ++ app/src/trait/frame_sink.h | 1 - app/src/trait/frame_source.c | 2 ++ app/src/trait/frame_source.h | 4 +++- app/src/trait/gamepad_processor.h | 3 --- app/src/trait/key_processor.h | 1 - app/src/trait/mouse_processor.h | 1 - app/src/trait/packet_sink.h | 1 - app/src/trait/packet_source.c | 2 ++ app/src/trait/packet_source.h | 4 +++- app/src/uhid/gamepad_uhid.c | 5 +++++ app/src/uhid/gamepad_uhid.h | 2 -- app/src/uhid/keyboard_uhid.c | 6 ++++++ app/src/uhid/mouse_uhid.c | 3 +++ app/src/uhid/uhid_output.c | 1 - app/src/uhid/uhid_output.h | 2 +- app/src/usb/aoa_hid.c | 7 +++++-- app/src/usb/aoa_hid.h | 7 ++----- app/src/usb/gamepad_aoa.c | 2 ++ app/src/usb/gamepad_aoa.h | 4 +--- app/src/usb/keyboard_aoa.h | 2 +- app/src/usb/mouse_aoa.c | 1 + app/src/usb/mouse_aoa.h | 2 +- app/src/usb/scrcpy_otg.c | 13 +++++++++++-- app/src/usb/screen_otg.c | 4 ++++ app/src/usb/screen_otg.h | 7 ++++--- app/src/util/acksync.c | 1 - app/src/util/acksync.h | 5 ++++- app/src/util/audiobuf.h | 1 + app/src/util/average.h | 3 --- app/src/util/binary.h | 1 - app/src/util/env.c | 4 +++- app/src/util/intmap.h | 1 + app/src/util/intr.c | 4 ++-- app/src/util/intr.h | 6 +++--- app/src/util/log.c | 5 ++++- app/src/util/net.c | 13 ++++++------- app/src/util/net.h | 3 ++- app/src/util/net_intr.h | 9 +++++++-- app/src/util/process.c | 2 -- app/src/util/process.h | 2 ++ app/src/util/process_intr.h | 4 ++-- app/src/util/str.c | 4 ++-- app/src/util/str.h | 2 ++ app/src/util/strbuf.c | 3 +-- app/src/util/thread.c | 4 +++- app/src/util/tick.c | 1 + app/src/util/timeout.c | 3 ++- app/src/util/timeout.h | 4 ++-- app/src/util/vecdeque.h | 1 + app/src/util/vector.h | 2 +- app/src/v4l2_sink.c | 4 ++++ app/src/v4l2_sink.h | 6 +++--- app/src/version.c | 2 ++ 110 files changed, 225 insertions(+), 151 deletions(-) diff --git a/app/src/adb/adb.c b/app/src/adb/adb.c index 0cd3c0fd..40e9e968 100644 --- a/app/src/adb/adb.c +++ b/app/src/adb/adb.c @@ -4,9 +4,10 @@ #include #include #include +#include -#include "adb_device.h" -#include "adb_parser.h" +#include "adb/adb_device.h" +#include "adb/adb_parser.h" #include "util/env.h" #include "util/file.h" #include "util/log.h" diff --git a/app/src/adb/adb.h b/app/src/adb/adb.h index 43310fb9..e4903902 100644 --- a/app/src/adb/adb.h +++ b/app/src/adb/adb.h @@ -6,7 +6,7 @@ #include #include -#include "adb_device.h" +#include "adb/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 56393bcf..308663ef 100644 --- a/app/src/adb/adb_device.h +++ b/app/src/adb/adb_device.h @@ -4,7 +4,6 @@ #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 66bb1854..90a1b30b 100644 --- a/app/src/adb/adb_parser.c +++ b/app/src/adb/adb_parser.c @@ -3,6 +3,7 @@ #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 f20349f6..b8738a35 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_device.h" +#include "adb/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 fa936e4b..43e80e13 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.h" +#include "adb/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 9133c24a..5a66d43b 100644 --- a/app/src/audio_player.h +++ b/app/src/audio_player.h @@ -3,9 +3,7 @@ #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 3e4f78ad..f7e9b81e 100644 --- a/app/src/audio_regulator.c +++ b/app/src/audio_regulator.c @@ -1,5 +1,9 @@ #include "audio_regulator.h" +#include +#include +#include +#include #include #include diff --git a/app/src/audio_regulator.h b/app/src/audio_regulator.h index 1c0eeb9f..03cf6325 100644 --- a/app/src/audio_regulator.h +++ b/app/src/audio_regulator.h @@ -5,6 +5,8 @@ #include #include +#include +#include #include #include #include "util/audiobuf.h" diff --git a/app/src/cli.c b/app/src/cli.c index ed1970d4..756934ea 100644 --- a/app/src/cli.c +++ b/app/src/cli.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include "options.h" @@ -13,6 +14,7 @@ #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) diff --git a/app/src/decoder.c b/app/src/decoder.c index 5d42b8b0..4d0a1daf 100644 --- a/app/src/decoder.c +++ b/app/src/decoder.c @@ -1,11 +1,9 @@ #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 ba8903f4..1f525fae 100644 --- a/app/src/decoder.h +++ b/app/src/decoder.h @@ -3,13 +3,11 @@ #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 e89a2092..f75c6f72 100644 --- a/app/src/delay_buffer.c +++ b/app/src/delay_buffer.c @@ -2,9 +2,7 @@ #include #include - -#include -#include +#include #include "util/log.h" diff --git a/app/src/delay_buffer.h b/app/src/delay_buffer.h index 18c1ce94..61cd77e4 100644 --- a/app/src/delay_buffer.h +++ b/app/src/delay_buffer.h @@ -4,6 +4,7 @@ #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 7223b553..885cd6ee 100644 --- a/app/src/demuxer.c +++ b/app/src/demuxer.c @@ -1,14 +1,11 @@ #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 5587d12d..2b7cb703 100644 --- a/app/src/demuxer.h +++ b/app/src/demuxer.h @@ -4,12 +4,8 @@ #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 86b2ccb7..d6c701bb 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 39018834..aee8ef80 100644 --- a/app/src/display.c +++ b/app/src/display.c @@ -1,6 +1,8 @@ #include "display.h" #include +#include +#include #include #include "util/log.h" diff --git a/app/src/display.h b/app/src/display.h index 064bb7bf..4de9b0a9 100644 --- a/app/src/display.h +++ b/app/src/display.h @@ -4,7 +4,8 @@ #include "common.h" #include -#include +#include +#include #include #include "coords.h" diff --git a/app/src/events.c b/app/src/events.c index ce885241..b4322d1b 100644 --- a/app/src/events.c +++ b/app/src/events.c @@ -1,5 +1,7 @@ #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 06911052..681fb5d6 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 dd4ae1da..1daa42ba 100644 --- a/app/src/fps_counter.c +++ b/app/src/fps_counter.c @@ -1,6 +1,7 @@ #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 e7619271..3eab461c 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 5699b58f..9fd4cf6f 100644 --- a/app/src/frame_buffer.c +++ b/app/src/frame_buffer.c @@ -1,8 +1,6 @@ #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 f97261cd..e748adfb 100644 --- a/app/src/frame_buffer.h +++ b/app/src/frame_buffer.h @@ -4,6 +4,7 @@ #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 d6818e30..b0d45ce8 100644 --- a/app/src/hid/hid_event.h +++ b/app/src/hid/hid_event.h @@ -3,6 +3,7 @@ #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 8f4e4527..842eae9e 100644 --- a/app/src/hid/hid_gamepad.c +++ b/app/src/hid/hid_gamepad.c @@ -2,6 +2,8 @@ #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 b532a703..8d939ac7 100644 --- a/app/src/hid/hid_gamepad.h +++ b/app/src/hid/hid_gamepad.h @@ -4,6 +4,7 @@ #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 961ad790..6477396a 100644 --- a/app/src/hid/hid_keyboard.c +++ b/app/src/hid/hid_keyboard.c @@ -1,5 +1,6 @@ #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 cde1ac52..5ecfd8cf 100644 --- a/app/src/hid/hid_keyboard.h +++ b/app/src/hid/hid_keyboard.h @@ -4,6 +4,7 @@ #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 7acc413b..29cfc594 100644 --- a/app/src/hid/hid_mouse.c +++ b/app/src/hid/hid_mouse.c @@ -1,5 +1,7 @@ #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 #define SC_HID_MOUSE_INPUT_SIZE 4 diff --git a/app/src/hid/hid_mouse.h b/app/src/hid/hid_mouse.h index a9a54718..06c61dd1 100644 --- a/app/src/hid/hid_mouse.h +++ b/app/src/hid/hid_mouse.h @@ -3,8 +3,6 @@ #include "common.h" -#include - #include "hid/hid_event.h" #include "input_events.h" diff --git a/app/src/icon.c b/app/src/icon.c index 4f3a9a39..797afc75 100644 --- a/app/src/icon.c +++ b/app/src/icon.c @@ -2,17 +2,22 @@ #include #include +#include +#include +#include #include #include +#include #include #include +#include #include "config.h" -#include "compat.h" #include "util/env.h" -#include "util/file.h" +#ifdef PORTABLE +# include "util/file.h" +#endif #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 3251e48f..6bcf46d2 100644 --- a/app/src/icon.h +++ b/app/src/icon.h @@ -3,9 +3,7 @@ #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 ad3afa81..0c022acc 100644 --- a/app/src/input_events.h +++ b/app/src/input_events.h @@ -9,7 +9,6 @@ #include #include "coords.h" -#include "options.h" /* The representation of input events in scrcpy is very close to the SDL API, * for simplicity. diff --git a/app/src/input_manager.c b/app/src/input_manager.c index 2e4337db..635825c9 100644 --- a/app/src/input_manager.c +++ b/app/src/input_manager.c @@ -1,8 +1,12 @@ #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" diff --git a/app/src/input_manager.h b/app/src/input_manager.h index 8efd0153..af4cbc69 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 2d9ca85b..466a1aeb 100644 --- a/app/src/keyboard_sdk.c +++ b/app/src/keyboard_sdk.c @@ -1,8 +1,13 @@ #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 8bbd074f..c58e0be7 100644 --- a/app/src/main.c +++ b/app/src/main.c @@ -1,9 +1,6 @@ #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 a7998972..7eceffa7 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 142b89bb..fe92a2d7 100644 --- a/app/src/mouse_sdk.h +++ b/app/src/mouse_sdk.h @@ -6,7 +6,6 @@ #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 376690af..0cb83ed7 100644 --- a/app/src/opengl.c +++ b/app/src/opengl.c @@ -2,7 +2,8 @@ #include #include -#include "SDL2/SDL.h" +#include +#include void sc_opengl_init(struct sc_opengl *gl) { diff --git a/app/src/options.c b/app/src/options.c index df8033e9..044aa014 100644 --- a/app/src/options.c +++ b/app/src/options.c @@ -1,5 +1,7 @@ #include "options.h" +#include + const struct scrcpy_options scrcpy_options_default = { .serial = NULL, .crop = NULL, diff --git a/app/src/options.h b/app/src/options.h index 152881d8..c8425808 100644 --- a/app/src/options.h +++ b/app/src/options.h @@ -5,7 +5,6 @@ #include #include -#include #include #include "util/tick.h" diff --git a/app/src/packet_merger.c b/app/src/packet_merger.c index 81b02d2c..dea038b6 100644 --- a/app/src/packet_merger.c +++ b/app/src/packet_merger.c @@ -1,5 +1,9 @@ #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 e1824c2c..3f9972ce 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 b89b0c6e..2ccb8a8b 100644 --- a/app/src/receiver.c +++ b/app/src/receiver.c @@ -2,7 +2,6 @@ #include #include -#include #include #include "device_msg.h" diff --git a/app/src/recorder.c b/app/src/recorder.c index 15f27157..c26f8f2d 100644 --- a/app/src/recorder.c +++ b/app/src/recorder.c @@ -1,6 +1,9 @@ #include "recorder.h" #include +#include +#include +#include #include #include #include diff --git a/app/src/recorder.h b/app/src/recorder.h index d096e79a..70b73836 100644 --- a/app/src/recorder.h +++ b/app/src/recorder.h @@ -4,9 +4,10 @@ #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 f1942e43..641d93f7 100644 --- a/app/src/scrcpy.c +++ b/app/src/scrcpy.c @@ -1,10 +1,11 @@ #include "scrcpy.h" +#include +#include +#include #include +#include #include -#include -#include -#include #include #ifdef _WIN32 @@ -37,9 +38,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 diff --git a/app/src/scrcpy.h b/app/src/scrcpy.h index d4d494a3..7f6a0fb2 100644 --- a/app/src/scrcpy.h +++ b/app/src/scrcpy.h @@ -3,7 +3,6 @@ #include "common.h" -#include #include "options.h" enum scrcpy_exit_code { diff --git a/app/src/screen.h b/app/src/screen.h index c716c399..6621b2d2 100644 --- a/app/src/screen.h +++ b/app/src/screen.h @@ -1,11 +1,14 @@ -#ifndef SCREEN_H -#define SCREEN_H +#ifndef SC_SCREEN_H +#define SC_SCREEN_H #include "common.h" #include +#include #include -#include +#include +#include +#include #include "controller.h" #include "coords.h" @@ -14,7 +17,6 @@ #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 22ddd372..cf181abc 100644 --- a/app/src/server.c +++ b/app/src/server.c @@ -1,19 +1,18 @@ #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_intr.h" +#include "util/process.h" #include "util/str.h" #define SC_SERVER_FILENAME "scrcpy-server" diff --git a/app/src/server.h b/app/src/server.h index 3c78b9ed..a03689ff 100644 --- a/app/src/server.h +++ b/app/src/server.h @@ -1,19 +1,17 @@ -#ifndef SERVER_H -#define SERVER_H +#ifndef SC_SERVER_H +#define SC_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 { diff --git a/app/src/shortcut_mod.h b/app/src/shortcut_mod.h index b685e987..f6c13f03 100644 --- a/app/src/shortcut_mod.h +++ b/app/src/shortcut_mod.h @@ -3,6 +3,7 @@ #include "common.h" +#include #include #include #include diff --git a/app/src/sys/unix/file.c b/app/src/sys/unix/file.c index 6123c788..8f7fb074 100644 --- a/app/src/sys/unix/file.c +++ b/app/src/sys/unix/file.c @@ -1,10 +1,11 @@ #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 8c4a53c3..36d1ff7d 100644 --- a/app/src/sys/unix/process.c +++ b/app/src/sys/unix/process.c @@ -4,6 +4,8 @@ #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 8ef248b6..67be4d46 100644 --- a/app/src/trait/frame_sink.h +++ b/app/src/trait/frame_sink.h @@ -3,7 +3,6 @@ #include "common.h" -#include #include #include diff --git a/app/src/trait/frame_source.c b/app/src/trait/frame_source.c index 416eccd9..56848309 100644 --- a/app/src/trait/frame_source.c +++ b/app/src/trait/frame_source.c @@ -1,5 +1,7 @@ #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 94222af0..cb1ef905 100644 --- a/app/src/trait/frame_source.h +++ b/app/src/trait/frame_source.h @@ -3,7 +3,9 @@ #include "common.h" -#include "frame_sink.h" +#include + +#include "trait/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 19629a9a..5e8dc2a4 100644 --- a/app/src/trait/gamepad_processor.h +++ b/app/src/trait/gamepad_processor.h @@ -3,9 +3,6 @@ #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 96374413..9e9bb86e 100644 --- a/app/src/trait/key_processor.h +++ b/app/src/trait/key_processor.h @@ -3,7 +3,6 @@ #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 6e0b596e..d0a96e7c 100644 --- a/app/src/trait/mouse_processor.h +++ b/app/src/trait/mouse_processor.h @@ -3,7 +3,6 @@ #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 84cfe814..e12dea12 100644 --- a/app/src/trait/packet_sink.h +++ b/app/src/trait/packet_sink.h @@ -3,7 +3,6 @@ #include "common.h" -#include #include #include diff --git a/app/src/trait/packet_source.c b/app/src/trait/packet_source.c index c0836f1d..0a2c6c4d 100644 --- a/app/src/trait/packet_source.c +++ b/app/src/trait/packet_source.c @@ -1,5 +1,7 @@ #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 16d56e86..8788021a 100644 --- a/app/src/trait/packet_source.h +++ b/app/src/trait/packet_source.h @@ -3,7 +3,9 @@ #include "common.h" -#include "packet_sink.h" +#include + +#include "trait/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 a066cf03..c64feb18 100644 --- a/app/src/uhid/gamepad_uhid.c +++ b/app/src/uhid/gamepad_uhid.c @@ -1,5 +1,10 @@ #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 07d03099..ad747604 100644 --- a/app/src/uhid/gamepad_uhid.h +++ b/app/src/uhid/gamepad_uhid.h @@ -3,8 +3,6 @@ #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 76d70cc5..70082990 100644 --- a/app/src/uhid/keyboard_uhid.c +++ b/app/src/uhid/keyboard_uhid.c @@ -1,6 +1,12 @@ #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 471030e7..7fed8383 100644 --- a/app/src/uhid/mouse_uhid.c +++ b/app/src/uhid/mouse_uhid.c @@ -1,5 +1,8 @@ #include "mouse_uhid.h" +#include +#include + #include "hid/hid_mouse.h" #include "input_events.h" #include "util/log.h" diff --git a/app/src/uhid/uhid_output.c b/app/src/uhid/uhid_output.c index 05e691da..e743a73c 100644 --- a/app/src/uhid/uhid_output.c +++ b/app/src/uhid/uhid_output.c @@ -1,6 +1,5 @@ #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 cd6a800f..ed028b58 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 236a78ed..8cb62bfd 100644 --- a/app/src/usb/aoa_hid.c +++ b/app/src/usb/aoa_hid.c @@ -1,13 +1,16 @@ -#include "util/log.h" +#include "aoa_hid.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 9cc6355e..2755c957 100644 --- a/app/src/usb/aoa_hid.h +++ b/app/src/usb/aoa_hid.h @@ -3,16 +3,13 @@ #include "common.h" -#include #include - -#include +#include #include "hid/hid_event.h" -#include "usb.h" +#include "usb/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 4372379f..d29b1a78 100644 --- a/app/src/usb/gamepad_aoa.c +++ b/app/src/usb/gamepad_aoa.c @@ -1,5 +1,7 @@ #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 b2dfbe5e..0297a365 100644 --- a/app/src/usb/gamepad_aoa.h +++ b/app/src/usb/gamepad_aoa.h @@ -3,10 +3,8 @@ #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 565b9177..9e9500a3 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 cb566cc0..b64e9b12 100644 --- a/app/src/usb/mouse_aoa.c +++ b/app/src/usb/mouse_aoa.c @@ -1,6 +1,7 @@ #include "mouse_aoa.h" #include +#include #include "hid/hid_mouse.h" #include "input_events.h" diff --git a/app/src/usb/mouse_aoa.h b/app/src/usb/mouse_aoa.h index afaed761..506286ba 100644 --- a/app/src/usb/mouse_aoa.h +++ b/app/src/usb/mouse_aoa.h @@ -5,7 +5,7 @@ #include -#include "aoa_hid.h" +#include "usb/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 6ef2fc2a..1a9cc46e 100644 --- a/app/src/usb/scrcpy_otg.c +++ b/app/src/usb/scrcpy_otg.c @@ -1,10 +1,19 @@ #include "scrcpy_otg.h" +#include +#include +#include #include -#include "adb/adb.h" +#ifdef _WIN32 +# include "adb/adb.h" +#endif #include "events.h" -#include "screen_otg.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 "util/log.h" struct scrcpy_otg { diff --git a/app/src/usb/screen_otg.c b/app/src/usb/screen_otg.c index 368af125..02edc3a3 100644 --- a/app/src/usb/screen_otg.c +++ b/app/src/usb/screen_otg.c @@ -1,7 +1,11 @@ #include "screen_otg.h" +#include +#include + #include "icon.h" #include "options.h" +#include "util/acksync.h" #include "util/log.h" static void diff --git a/app/src/usb/screen_otg.h b/app/src/usb/screen_otg.h index 427723ad..08b76ae7 100644 --- a/app/src/usb/screen_otg.h +++ b/app/src/usb/screen_otg.h @@ -4,12 +4,13 @@ #include "common.h" #include +#include #include -#include "keyboard_aoa.h" -#include "mouse_aoa.h" #include "mouse_capture.h" -#include "gamepad_aoa.h" +#include "usb/gamepad_aoa.h" +#include "usb/keyboard_aoa.h" +#include "usb/mouse_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 2899cdcb..76ecee0d 100644 --- a/app/src/util/acksync.c +++ b/app/src/util/acksync.c @@ -1,7 +1,6 @@ #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 58ab1b35..3d9c9b2f 100644 --- a/app/src/util/acksync.h +++ b/app/src/util/acksync.h @@ -3,7 +3,10 @@ #include "common.h" -#include "thread.h" +#include +#include +#include "util/thread.h" +#include "util/tick.h" #define SC_SEQUENCE_INVALID 0 diff --git a/app/src/util/audiobuf.h b/app/src/util/audiobuf.h index 5e7dd4a0..5cc51932 100644 --- a/app/src/util/audiobuf.h +++ b/app/src/util/audiobuf.h @@ -6,6 +6,7 @@ #include #include #include +#include #include /** diff --git a/app/src/util/average.h b/app/src/util/average.h index 59fae7d1..eded9987 100644 --- a/app/src/util/average.h +++ b/app/src/util/average.h @@ -3,9 +3,6 @@ #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 7de9b505..b6ce3201 100644 --- a/app/src/util/binary.h +++ b/app/src/util/binary.h @@ -4,7 +4,6 @@ #include "common.h" #include -#include #include static inline void diff --git a/app/src/util/env.c b/app/src/util/env.c index 1128e5ea..127f5a1f 100644 --- a/app/src/util/env.c +++ b/app/src/util/env.c @@ -2,7 +2,9 @@ #include #include -#include "util/str.h" +#ifdef _WIN32 +# include "util/str.h" +#endif char * sc_get_env(const char *varname) { diff --git a/app/src/util/intmap.h b/app/src/util/intmap.h index 2898c461..7ab903ca 100644 --- a/app/src/util/intmap.h +++ b/app/src/util/intmap.h @@ -3,6 +3,7 @@ #include "common.h" +#include #include struct sc_intmap_entry { diff --git a/app/src/util/intr.c b/app/src/util/intr.c index 22bd121a..ddf4839f 100644 --- a/app/src/util/intr.c +++ b/app/src/util/intr.c @@ -1,9 +1,9 @@ #include "intr.h" -#include "util/log.h" - #include +#include "util/log.h" + 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 1c20f6df..35bd3375 100644 --- a/app/src/util/intr.h +++ b/app/src/util/intr.h @@ -6,9 +6,9 @@ #include #include -#include "net.h" -#include "process.h" -#include "thread.h" +#include "util/net.h" +#include "util/process.h" +#include "util/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 8a347c84..9114a258 100644 --- a/app/src/util/log.c +++ b/app/src/util/log.c @@ -4,7 +4,10 @@ # 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 d68b0af6..9562ff6b 100644 --- a/app/src/util/net.c +++ b/app/src/util/net.c @@ -1,28 +1,27 @@ #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 94789954..aa99bbc4 100644 --- a/app/src/util/net.h +++ b/app/src/util/net.h @@ -4,14 +4,15 @@ #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 dbef528d..e2bbee88 100644 --- a/app/src/util/net_intr.h +++ b/app/src/util/net_intr.h @@ -3,8 +3,13 @@ #include "common.h" -#include "intr.h" -#include "net.h" +#include +#include +#include +#include + +#include "util/intr.h" +#include "util/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 9c4dcd9f..29d89a54 100644 --- a/app/src/util/process.c +++ b/app/src/util/process.c @@ -1,8 +1,6 @@ #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 4d9d1684..eec51bcc 100644 --- a/app/src/util/process.h +++ b/app/src/util/process.h @@ -4,7 +4,9 @@ #include "common.h" #include +#include #include "util/thread.h" +#include "util/tick.h" #ifdef _WIN32 diff --git a/app/src/util/process_intr.h b/app/src/util/process_intr.h index 530a9046..020eafa1 100644 --- a/app/src/util/process_intr.h +++ b/app/src/util/process_intr.h @@ -3,8 +3,8 @@ #include "common.h" -#include "intr.h" -#include "process.h" +#include "util/intr.h" +#include "util/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 304cd302..83d19c4d 100644 --- a/app/src/util/str.c +++ b/app/src/util/str.c @@ -12,8 +12,8 @@ # include #endif -#include "log.h" -#include "strbuf.h" +#include "util/log.h" +#include "util/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 d20f1b28..b386b48d 100644 --- a/app/src/util/str.h +++ b/app/src/util/str.h @@ -5,6 +5,8 @@ #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 1892b46b..6196d746 100644 --- a/app/src/util/strbuf.c +++ b/app/src/util/strbuf.c @@ -1,11 +1,10 @@ #include "strbuf.h" #include -#include #include #include -#include "log.h" +#include "util/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 9679dfff..2a5253f7 100644 --- a/app/src/util/thread.c +++ b/app/src/util/thread.c @@ -1,10 +1,12 @@ #include "thread.h" #include +#include +#include #include #include -#include "log.h" +#include "util/log.h" sc_thread_id SC_MAIN_THREAD_ID; diff --git a/app/src/util/tick.c b/app/src/util/tick.c index cc0bab5e..edef1070 100644 --- a/app/src/util/tick.c +++ b/app/src/util/tick.c @@ -1,6 +1,7 @@ #include "tick.h" #include +#include #include #ifdef _WIN32 # include diff --git a/app/src/util/timeout.c b/app/src/util/timeout.c index 159a4681..21bc3a53 100644 --- a/app/src/util/timeout.c +++ b/app/src/util/timeout.c @@ -1,8 +1,9 @@ #include "timeout.h" #include +#include -#include "log.h" +#include "util/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 ae171b86..a45ae2ae 100644 --- a/app/src/util/timeout.h +++ b/app/src/util/timeout.h @@ -5,8 +5,8 @@ #include -#include "thread.h" -#include "tick.h" +#include "util/thread.h" +#include "util/tick.h" struct sc_timeout { sc_thread thread; diff --git a/app/src/util/vecdeque.h b/app/src/util/vecdeque.h index ce559ee9..e31724e2 100644 --- a/app/src/util/vecdeque.h +++ b/app/src/util/vecdeque.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include diff --git a/app/src/util/vector.h b/app/src/util/vector.h index 97d7c389..5b399d56 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 087e9af4..da9e02ef 100644 --- a/app/src/v4l2_sink.c +++ b/app/src/v4l2_sink.c @@ -1,5 +1,9 @@ #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 365a739d..2b7c5b50 100644 --- a/app/src/v4l2_sink.h +++ b/app/src/v4l2_sink.h @@ -3,13 +3,13 @@ #include "common.h" +#include #include #include -#include "coords.h" -#include "trait/frame_sink.h" #include "frame_buffer.h" -#include "util/tick.h" +#include "trait/frame_sink.h" +#include "util/thread.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 90ea3334..f8610714 100644 --- a/app/src/version.c +++ b/app/src/version.c @@ -1,5 +1,6 @@ #include "version.h" +#include #include #include #include @@ -9,6 +10,7 @@ #ifdef HAVE_USB # include #endif +#include void scrcpy_print_version(void) { From eac711ace68da43b09a4da99f7990143ae93f7c1 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Mon, 23 Dec 2024 12:51:27 +0100 Subject: [PATCH 19/43] Remove unused rotation and fold listeners IRotationWatcher and IDisplayFoldListener are no longer used since commit 39d51ff2cc2f3e201ad433d48372b548e5dd11d3. --- server/build_without_gradle.sh | 2 - .../android/view/IDisplayFoldListener.aidl | 26 ---------- .../aidl/android/view/IRotationWatcher.aidl | 25 ---------- .../scrcpy/wrappers/WindowManager.java | 48 ------------------- 4 files changed, 101 deletions(-) delete mode 100644 server/src/main/aidl/android/view/IDisplayFoldListener.aidl delete mode 100644 server/src/main/aidl/android/view/IRotationWatcher.aidl diff --git a/server/build_without_gradle.sh b/server/build_without_gradle.sh index d16592b4..e0b69aee 100755 --- a/server/build_without_gradle.sh +++ b/server/build_without_gradle.sh @@ -47,10 +47,8 @@ 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/src/main/aidl/android/view/IDisplayFoldListener.aidl b/server/src/main/aidl/android/view/IDisplayFoldListener.aidl deleted file mode 100644 index 2c91149d..00000000 --- a/server/src/main/aidl/android/view/IDisplayFoldListener.aidl +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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 deleted file mode 100644 index 2cc5e44a..00000000 --- a/server/src/main/aidl/android/view/IRotationWatcher.aidl +++ /dev/null @@ -1,25 +0,0 @@ -/* //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/wrappers/WindowManager.java b/server/src/main/java/com/genymobile/scrcpy/wrappers/WindowManager.java index 86dd83f2..04f5abd7 100644 --- a/server/src/main/java/com/genymobile/scrcpy/wrappers/WindowManager.java +++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/WindowManager.java @@ -5,9 +5,7 @@ import com.genymobile.scrcpy.util.Ln; import android.annotation.TargetApi; import android.os.IInterface; -import android.view.IDisplayFoldListener; import android.view.IDisplayWindowListener; -import android.view.IRotationWatcher; import java.lang.reflect.Method; @@ -182,52 +180,6 @@ 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 { From c27d116a662c87ee84963820669ee0d2ce60e6f1 Mon Sep 17 00:00:00 2001 From: Simon Chan <1330321+yume-chan@users.noreply.github.com> Date: Mon, 23 Dec 2024 12:04:18 +0100 Subject: [PATCH 20/43] Fix AudioRecord package name for Android 16 Since commit 9f91a5eebb4520b9333576e946b3911d0f946a04 in frameworks/av (AOSP), an AudioRecord can be created only if the declared package name in the AttributionSource is "shell" (for the shell UID): - - Refs Fixes #5698 Signed-off-by: Romain Vimont --- server/src/main/java/com/genymobile/scrcpy/FakeContext.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/java/com/genymobile/scrcpy/FakeContext.java b/server/src/main/java/com/genymobile/scrcpy/FakeContext.java index 2b83e397..22fc6d49 100644 --- a/server/src/main/java/com/genymobile/scrcpy/FakeContext.java +++ b/server/src/main/java/com/genymobile/scrcpy/FakeContext.java @@ -72,7 +72,7 @@ public final class FakeContext extends ContextWrapper { @Override public AttributionSource getAttributionSource() { AttributionSource.Builder builder = new AttributionSource.Builder(Process.SHELL_UID); - builder.setPackageName(PACKAGE_NAME); + builder.setPackageName("shell"); return builder.build(); } From 1c7680f689684fe45b8fa8e1525add5dc3cdfdd2 Mon Sep 17 00:00:00 2001 From: "Jaime J. Denizard" Date: Tue, 31 Dec 2024 16:21:36 -0500 Subject: [PATCH 21/43] Fix some grammatical issues in documentation PR #5722 Signed-off-by: Romain Vimont --- doc/connection.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/connection.md b/doc/connection.md index 2c3d37e1..dcf00147 100644 --- a/doc/connection.md +++ b/doc/connection.md @@ -113,16 +113,17 @@ 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 to bypass -having to physically connect your device directly to your computer. +Since Android 11, a [wireless debugging option][adb-wireless] allows you to +bypass having to physically connect your device 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 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 you to run arbitrary commands +whenever a new Android device is connected: [AutoAdb]. It can be used to start +scrcpy: ```bash autoadb scrcpy -s '{}' From cac8e9c821b9b8314d57e84ccbf685929af08794 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Wed, 1 Jan 2025 15:01:18 +0100 Subject: [PATCH 22/43] Happy new year 2025! --- LICENSE | 2 +- README.md | 2 +- app/scrcpy.1 | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/LICENSE b/LICENSE index d9326a74..1196b3da 100644 --- a/LICENSE +++ b/LICENSE @@ -188,7 +188,7 @@ identification within third-party archives. Copyright (C) 2018 Genymobile - Copyright (C) 2018-2024 Romain Vimont + Copyright (C) 2018-2025 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 5eb59ba5..b5884350 100644 --- a/README.md +++ b/README.md @@ -210,7 +210,7 @@ work][donate]: ## Licence Copyright (C) 2018 Genymobile - Copyright (C) 2018-2024 Romain Vimont + Copyright (C) 2018-2025 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/scrcpy.1 b/app/scrcpy.1 index 924905e4..f8b39112 100644 --- a/app/scrcpy.1 +++ b/app/scrcpy.1 @@ -829,7 +829,7 @@ Report bugs to . .SH COPYRIGHT Copyright \(co 2018 Genymobile -Copyright \(co 2018\-2024 Romain Vimont +Copyright \(co 2018\-2025 Romain Vimont Licensed under the Apache License, Version 2.0. From 0ba9d3570560cb46b52a0696134442aeb7f634e6 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Wed, 15 Jan 2025 10:54:57 +0100 Subject: [PATCH 23/43] Mention virtual display destruction The new virtual display does not persist after scrcpy exits. --- doc/virtual_display.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/virtual_display.md b/doc/virtual_display.md index 5d1673e8..09e6f142 100644 --- a/doc/virtual_display.md +++ b/doc/virtual_display.md @@ -11,6 +11,8 @@ 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. From 986328ff9ea82a709f2c31ad3cdd4e86ac845c24 Mon Sep 17 00:00:00 2001 From: Sam Listopad II Date: Thu, 30 Jan 2025 14:02:12 -0600 Subject: [PATCH 24/43] Allow controls with --no-window Without a window, mouse and keyboard events may not be received, but the control channel is still necessary for other features: * --turn-screen-off * --stay-awake * --show-touches * --power-off-on-close * --start-app Fixes #5803 PR #5804 Signed-off-by: Romain Vimont --- app/scrcpy.1 | 2 +- app/src/cli.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/scrcpy.1 b/app/scrcpy.1 index 924905e4..75bf6088 100644 --- a/app/scrcpy.1 +++ b/app/scrcpy.1 @@ -389,7 +389,7 @@ Disable video playback on the computer. .TP .B \-\-no\-window -Disable scrcpy window. Implies --no-video-playback and --no-control. +Disable scrcpy window. Implies --no-video-playback. .TP .BI "\-\-orientation " value diff --git a/app/src/cli.c b/app/src/cli.c index 756934ea..a2e6ab1a 100644 --- a/app/src/cli.c +++ b/app/src/cli.c @@ -689,8 +689,7 @@ static const struct sc_option options[] = { { .longopt_id = OPT_NO_WINDOW, .longopt = "no-window", - .text = "Disable scrcpy window. Implies --no-video-playback and " - "--no-control.", + .text = "Disable scrcpy window. Implies --no-video-playback.", }, { .longopt_id = OPT_ORIENTATION, @@ -2761,9 +2760,10 @@ 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 or control + // Without window, there cannot be any video playback opts->video_playback = false; - opts->control = false; + // Controls are still possible, allowing for options like + // --turn-screen-off } if (!opts->video) { From fd8bef68b794442d9f3bbf47d85632f9a16e504a Mon Sep 17 00:00:00 2001 From: "chengjian.scj" Date: Wed, 25 Dec 2024 15:30:37 +0800 Subject: [PATCH 25/43] Add --display-ime-policy option Add an option to select where the IME should be displayed. Possible values are "local", "fallback" and "hide". PR #5703 Signed-off-by: Romain Vimont --- app/data/bash-completion/scrcpy | 5 ++ app/data/zsh-completion/_scrcpy | 1 + app/scrcpy.1 | 13 ++++ app/src/cli.c | 51 +++++++++++++++ app/src/options.c | 1 + app/src/options.h | 8 +++ app/src/scrcpy.c | 1 + app/src/server.c | 19 ++++++ app/src/server.h | 1 + doc/virtual_display.md | 12 ++++ .../java/com/genymobile/scrcpy/CleanUp.java | 29 +++++++-- .../java/com/genymobile/scrcpy/Options.java | 22 +++++++ .../java/com/genymobile/scrcpy/Server.java | 12 +++- .../scrcpy/video/NewDisplayCapture.java | 6 ++ .../scrcpy/wrappers/WindowManager.java | 65 +++++++++++++++++++ 15 files changed, 239 insertions(+), 7 deletions(-) diff --git a/app/data/bash-completion/scrcpy b/app/data/bash-completion/scrcpy index 29130892..8d149f97 100644 --- a/app/data/bash-completion/scrcpy +++ b/app/data/bash-completion/scrcpy @@ -23,6 +23,7 @@ _scrcpy() { -d --select-usb --disable-screensaver --display-id= + --display-ime-policy= --display-orientation= -e --select-tcpip -f --fullscreen @@ -148,6 +149,10 @@ _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 diff --git a/app/data/zsh-completion/_scrcpy b/app/data/zsh-completion/_scrcpy index 0897b9cc..cccfcc6a 100644 --- a/app/data/zsh-completion/_scrcpy +++ b/app/data/zsh-completion/_scrcpy @@ -30,6 +30,7 @@ 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]' diff --git a/app/scrcpy.1 b/app/scrcpy.1 index 75bf6088..5eea94f4 100644 --- a/app/scrcpy.1 +++ b/app/scrcpy.1 @@ -161,6 +161,19 @@ 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. diff --git a/app/src/cli.c b/app/src/cli.c index a2e6ab1a..b83fc9ec 100644 --- a/app/src/cli.c +++ b/app/src/cli.c @@ -113,6 +113,7 @@ enum { OPT_ANGLE, OPT_NO_VD_SYSTEM_DECORATIONS, OPT_NO_VD_DESTROY_CONTENT, + OPT_DISPLAY_IME_POLICY, }; struct sc_option { @@ -366,6 +367,19 @@ 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", @@ -1614,6 +1628,25 @@ 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")) { @@ -2722,6 +2755,12 @@ 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; @@ -2978,6 +3017,12 @@ 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; @@ -3019,6 +3064,12 @@ 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/options.c b/app/src/options.c index 044aa014..0fe82d29 100644 --- a/app/src/options.c +++ b/app/src/options.c @@ -56,6 +56,7 @@ 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 c8425808..ef7542e3 100644 --- a/app/src/options.h +++ b/app/src/options.h @@ -89,6 +89,13 @@ 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)); @@ -251,6 +258,7 @@ 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/scrcpy.c b/app/src/scrcpy.c index 641d93f7..b3ff9b36 100644 --- a/app/src/scrcpy.c +++ b/app/src/scrcpy.c @@ -436,6 +436,7 @@ 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, diff --git a/app/src/server.c b/app/src/server.c index cf181abc..6979c09b 100644 --- a/app/src/server.c +++ b/app/src/server.c @@ -155,6 +155,21 @@ sc_server_get_audio_source_name(enum sc_audio_source audio_source) { } } +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 @@ -376,6 +391,10 @@ 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 a03689ff..5f4592de 100644 --- a/app/src/server.h +++ b/app/src/server.h @@ -50,6 +50,7 @@ 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/doc/virtual_display.md b/doc/virtual_display.md index 5d1673e8..f1645169 100644 --- a/doc/virtual_display.md +++ b/doc/virtual_display.md @@ -61,3 +61,15 @@ 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/server/src/main/java/com/genymobile/scrcpy/CleanUp.java b/server/src/main/java/com/genymobile/scrcpy/CleanUp.java index 49b23e81..51db985c 100644 --- a/server/src/main/java/com/genymobile/scrcpy/CleanUp.java +++ b/server/src/main/java/com/genymobile/scrcpy/CleanUp.java @@ -4,6 +4,7 @@ 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.system.ErrnoException; @@ -97,18 +98,31 @@ 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); + run(displayId, restoreStayOn, disableShowTouches, powerOffScreen, restoreScreenOffTimeout, restoreDisplayImePolicy); } catch (IOException e) { Ln.e("Clean up I/O exception", e); } } - private void run(int displayId, int restoreStayOn, boolean disableShowTouches, boolean powerOffScreen, int restoreScreenOffTimeout) - throws IOException { + private void run(int displayId, int restoreStayOn, boolean disableShowTouches, boolean powerOffScreen, int restoreScreenOffTimeout, + int restoreDisplayImePolicy) throws IOException { String[] cmd = { "app_process", "/", @@ -118,6 +132,7 @@ public final class CleanUp { String.valueOf(disableShowTouches), String.valueOf(powerOffScreen), String.valueOf(restoreScreenOffTimeout), + String.valueOf(restoreDisplayImePolicy), }; ProcessBuilder builder = new ProcessBuilder(cmd); @@ -178,6 +193,7 @@ public final class CleanUp { 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; @@ -223,6 +239,11 @@ 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/Options.java b/server/src/main/java/com/genymobile/scrcpy/Options.java index 8a438750..66bb68e8 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Options.java +++ b/server/src/main/java/com/genymobile/scrcpy/Options.java @@ -12,6 +12,7 @@ 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; @@ -48,6 +49,7 @@ public class Options { private boolean showTouches; private boolean stayAwake; private int screenOffTimeout = -1; + private int displayImePolicy = -1; private List videoCodecOptions; private List audioCodecOptions; @@ -186,6 +188,10 @@ public class Options { return screenOffTimeout; } + public int getDisplayImePolicy() { + return displayImePolicy; + } + public List getVideoCodecOptions() { return videoCodecOptions; } @@ -482,6 +488,9 @@ 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; @@ -626,4 +635,17 @@ 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 eb8b533a..09cfd6cf 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Server.java +++ b/server/src/main/java/com/genymobile/scrcpy/Server.java @@ -80,9 +80,15 @@ public final class Server { throw new ConfigurationException("Camera mirroring 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"); + 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"); + } } CleanUp cleanUp = null; 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 033d6b9a..792b3a8a 100644 --- a/server/src/main/java/com/genymobile/scrcpy/video/NewDisplayCapture.java +++ b/server/src/main/java/com/genymobile/scrcpy/video/NewDisplayCapture.java @@ -49,6 +49,7 @@ 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; @@ -68,6 +69,7 @@ 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; @@ -191,6 +193,10 @@ 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/WindowManager.java b/server/src/main/java/com/genymobile/scrcpy/wrappers/WindowManager.java index 04f5abd7..08bab1a9 100644 --- a/server/src/main/java/com/genymobile/scrcpy/wrappers/WindowManager.java +++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/WindowManager.java @@ -4,12 +4,19 @@ 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.IDisplayWindowListener; import java.lang.reflect.Method; public final class WindowManager { + + // + 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; @@ -22,6 +29,9 @@ 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); @@ -198,4 +208,59 @@ 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); + } + } } From d892a9aac58bd35b778ff8a0a933d11ec8092df1 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sat, 22 Feb 2025 12:22:45 +0100 Subject: [PATCH 26/43] Disable checkstyle line length warning Checkstyle reports a warning because the line containing a long URL is more than 150 characters. But we can't split the URL, so disable the warning. --- .../main/java/com/genymobile/scrcpy/wrappers/WindowManager.java | 1 + 1 file changed, 1 insertion(+) 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 08bab1a9..7ba5cc06 100644 --- a/server/src/main/java/com/genymobile/scrcpy/wrappers/WindowManager.java +++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/WindowManager.java @@ -12,6 +12,7 @@ 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; From c63d9e1803c658b29b067d5ea68baa38df7e1359 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Fri, 7 Mar 2025 18:40:28 +0100 Subject: [PATCH 27/43] Work around broken display listener on Android 15 A recent Android 15 upgrade broke the display listener (again). Use the alternative method for Android >= 14. Fixes #5908 --- .../java/com/genymobile/scrcpy/video/DisplaySizeMonitor.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 ff863aa8..3d7cccfe 100644 --- a/server/src/main/java/com/genymobile/scrcpy/video/DisplaySizeMonitor.java +++ b/server/src/main/java/com/genymobile/scrcpy/video/DisplaySizeMonitor.java @@ -23,7 +23,9 @@ 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. - private static final boolean USE_DEFAULT_METHOD = Build.VERSION.SDK_INT != AndroidVersions.API_34_ANDROID_14; + // 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 DisplayManager.DisplayListenerHandle displayListenerHandle; private HandlerThread handlerThread; From 7044122fc59cad404722215f375a711667bc4fa0 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sun, 9 Mar 2025 21:10:21 +0100 Subject: [PATCH 28/43] Simplify wording in README --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b5884350..404359f2 100644 --- a/README.md +++ b/README.md @@ -8,10 +8,10 @@ their name contains `scrcpy`.** _pronounced "**scr**een **c**o**py**"_ -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_. +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. It works on +_Linux_, _Windows_, and _macOS_. ![screenshot](assets/screenshot-debian-600.jpg) From 7998811fa55a4f1610dd07680a387251ead2dd1f Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sun, 9 Mar 2025 21:15:58 +0100 Subject: [PATCH 29/43] Mention that no Android app is required --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 404359f2..16b8bca1 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,8 @@ _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. It works on -_Linux_, _Windows_, and _macOS_. +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_. ![screenshot](assets/screenshot-debian-600.jpg) From 457c7fe5cfa5165c02d4263cd7d2a41598990d0e Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sat, 22 Feb 2025 11:09:16 +0100 Subject: [PATCH 30/43] Disable audio regulator underflow logs Only enable them if SC_AUDIO_REGULATOR_DEBUG is set, as they may spam the output. PR #5870 --- app/src/audio_regulator.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/audio_regulator.c b/app/src/audio_regulator.c index f7e9b81e..f11ed0e7 100644 --- a/app/src/audio_regulator.c +++ b/app/src/audio_regulator.c @@ -76,8 +76,10 @@ 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) { - LOGV("[Audio] Inserting initial buffering silence: %" PRIu32 +#ifdef SC_AUDIO_REGULATOR_DEBUG + LOGD("[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). @@ -98,8 +100,10 @@ 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, From 1d253381198495ae351e2fba377a0b325f4b8bfb Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sat, 22 Feb 2025 11:20:35 +0100 Subject: [PATCH 31/43] Report underflow samples in verbose mode Report the number of silence samples inserted due to underflow every second, along with the other metrics. PR #5870 --- app/src/audio_regulator.c | 6 +++++- app/src/audio_regulator.h | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/src/audio_regulator.c b/app/src/audio_regulator.c index f11ed0e7..66900b51 100644 --- a/app/src/audio_regulator.c +++ b/app/src/audio_regulator.c @@ -213,6 +213,7 @@ 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 */; @@ -315,7 +316,9 @@ 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", ar->target_buffering, avg, can_read, diff); + " compensation=%d (underflow=%" PRIu32 ")", + ar->target_buffering, avg, can_read, diff, ar->underflow_report); + ar->underflow_report = 0; int ret = swr_set_compensation(swr_ctx, diff, distance); if (ret < 0) { @@ -398,6 +401,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; return true; diff --git a/app/src/audio_regulator.h b/app/src/audio_regulator.h index 03cf6325..79238fbe 100644 --- a/app/src/audio_regulator.h +++ b/app/src/audio_regulator.h @@ -46,6 +46,9 @@ 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; From 245981281e99662aa77e53259a0864ead39ff438 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sun, 2 Mar 2025 17:09:43 +0100 Subject: [PATCH 32/43] Fix PTS produced by the default opus/flac encoders The default OPUS and FLAC encoders on Android rewrite the input PTS so that they exactly match the number of samples. As a consequence: - audio clock drift is not compensated - implicit silences (without packets) are ignored To work around this behavior, generate new PTS based on the current time (after encoding) and the packet duration. PR #5870 --- .../genymobile/scrcpy/audio/AudioEncoder.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) 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 267be60a..33177228 100644 --- a/server/src/main/java/com/genymobile/scrcpy/audio/AudioEncoder.java +++ b/server/src/main/java/com/genymobile/scrcpy/audio/AudioEncoder.java @@ -55,6 +55,9 @@ 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); @@ -118,6 +121,9 @@ 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); @@ -125,6 +131,25 @@ 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(() -> { @@ -194,6 +219,12 @@ 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(); From 3a0703f428fe024c0bb226c0e311966be22ef57c Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sun, 16 Feb 2025 17:38:27 +0100 Subject: [PATCH 33/43] Handle audio stream discontinuities The audio regulator assumed a continuous audio stream. But some audio sources (like the "voice call" audio source) do not produce any packets on silence, breaking this assumption. Use PTS to detect such discontinuities. PR #5870 --- app/src/audio_regulator.c | 33 ++++++++++++++++++++++++++++++++- app/src/audio_regulator.h | 3 +++ app/src/util/audiobuf.c | 35 +++++++++++++++++++++++++++++++++++ app/src/util/audiobuf.h | 3 +++ app/tests/test_audiobuf.c | 8 ++++++++ 5 files changed, 81 insertions(+), 1 deletion(-) diff --git a/app/src/audio_regulator.c b/app/src/audio_regulator.c index 66900b51..16fdd08b 100644 --- a/app/src/audio_regulator.c +++ b/app/src/audio_regulator.c @@ -141,6 +141,36 @@ 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. @@ -260,7 +290,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 - frame->nb_samples; + int32_t instant_compensation = (int32_t) written - input_samples; // Inserting silence instantly increases buffering int32_t inserted_silence = (int32_t) underflow; // Dropping input samples instantly decreases buffering @@ -403,6 +433,7 @@ sc_audio_regulator_init(struct sc_audio_regulator *ar, size_t sample_size, 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 79238fbe..4e18fe08 100644 --- a/app/src/audio_regulator.h +++ b/app/src/audio_regulator.h @@ -57,6 +57,9 @@ 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/util/audiobuf.c b/app/src/util/audiobuf.c index 3cc5cad1..eeb27514 100644 --- a/app/src/util/audiobuf.c +++ b/app/src/util/audiobuf.c @@ -116,3 +116,38 @@ 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 5cc51932..b55a5a59 100644 --- a/app/src/util/audiobuf.h +++ b/app/src/util/audiobuf.h @@ -50,6 +50,9 @@ 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/tests/test_audiobuf.c b/app/tests/test_audiobuf.c index 94d0f07a..539ee238 100644 --- a/app/tests/test_audiobuf.c +++ b/app/tests/test_audiobuf.c @@ -113,6 +113,14 @@ 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); } From 609719bde025559067e9d11672178531f8431619 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sat, 22 Feb 2025 11:40:24 +0100 Subject: [PATCH 34/43] Refactor audio sources Store the target audio source integer (one of the constants from android.media.MediaRecorder.AudioSource) in the AudioSource enum (or -1 if not relevant). This will simplify adding new audio sources. PR #5870 --- .../scrcpy/audio/AudioDirectCapture.java | 14 +------------- .../com/genymobile/scrcpy/audio/AudioSource.java | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 17 deletions(-) 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 5c859738..bf870bee 100644 --- a/server/src/main/java/com/genymobile/scrcpy/audio/AudioDirectCapture.java +++ b/server/src/main/java/com/genymobile/scrcpy/audio/AudioDirectCapture.java @@ -12,7 +12,6 @@ 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; @@ -32,18 +31,7 @@ public class AudioDirectCapture implements AudioCapture { private AudioRecordReader reader; public AudioDirectCapture(AudioSource audioSource) { - 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); - } + this.audioSource = audioSource.getDirectAudioSource(); } @TargetApi(AndroidVersions.API_23_ANDROID_6_0) 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 6082f20e..353f2281 100644 --- a/server/src/main/java/com/genymobile/scrcpy/audio/AudioSource.java +++ b/server/src/main/java/com/genymobile/scrcpy/audio/AudioSource.java @@ -1,20 +1,28 @@ package com.genymobile.scrcpy.audio; +import android.media.MediaRecorder; + public enum AudioSource { - OUTPUT("output"), - MIC("mic"), - PLAYBACK("playback"); + OUTPUT("output", MediaRecorder.AudioSource.REMOTE_SUBMIX), + MIC("mic", MediaRecorder.AudioSource.MIC), + PLAYBACK("playback", -1); private final String name; + private final int directAudioSource; - AudioSource(String name) { + AudioSource(String name, int directAudioSource) { 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)) { From bef2d8473b3426b1dd2ea9ed0cae3a243a999218 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sat, 22 Feb 2025 12:18:05 +0100 Subject: [PATCH 35/43] Add more audio sources Expose more audio sources from MediaRecorder.AudioSource. Refs Fixes #5412 Fixes #5670 PR #5870 --- app/data/bash-completion/scrcpy | 2 +- app/data/zsh-completion/_scrcpy | 2 +- app/scrcpy.1 | 18 +++-- app/src/cli.c | 76 +++++++++++++++++-- app/src/options.h | 8 ++ app/src/server.c | 16 ++++ doc/audio.md | 14 ++++ .../genymobile/scrcpy/audio/AudioSource.java | 12 ++- 8 files changed, 131 insertions(+), 17 deletions(-) diff --git a/app/data/bash-completion/scrcpy b/app/data/bash-completion/scrcpy index 8d149f97..9918918c 100644 --- a/app/data/bash-completion/scrcpy +++ b/app/data/bash-completion/scrcpy @@ -122,7 +122,7 @@ _scrcpy() { return ;; --audio-source) - COMPREPLY=($(compgen -W 'output mic playback' -- "$cur")) + 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")) return ;; --camera-facing) diff --git a/app/data/zsh-completion/_scrcpy b/app/data/zsh-completion/_scrcpy index cccfcc6a..450fc8f5 100644 --- a/app/data/zsh-completion/_scrcpy +++ b/app/data/zsh-completion/_scrcpy @@ -16,7 +16,7 @@ arguments=( '--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 mic playback)' + '--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-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]' diff --git a/app/scrcpy.1 b/app/scrcpy.1 index 5eea94f4..ffb66ab9 100644 --- a/app/scrcpy.1 +++ b/app/scrcpy.1 @@ -67,13 +67,19 @@ The available encoders can be listed by \fB\-\-list\-encoders\fR. .TP .BI "\-\-audio\-source " source -Select the audio source (output, mic or playback). +Select the audio source. Possible values are: -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. + - "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. Default is output. diff --git a/app/src/cli.c b/app/src/cli.c index b83fc9ec..b2e3e30a 100644 --- a/app/src/cli.c +++ b/app/src/cli.c @@ -217,13 +217,31 @@ static const struct sc_option options[] = { .longopt_id = OPT_AUDIO_SOURCE, .longopt = "audio-source", .argdesc = "source", - .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 " + .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 " "captured).\n" - "The \"mic\" source captures the microphone.\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" "Default is output.", }, { @@ -2036,8 +2054,50 @@ parse_audio_source(const char *optarg, enum sc_audio_source *source) { return true; } - LOGE("Unsupported audio source: %s (expected output, mic or playback)", - optarg); + 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); return false; } diff --git a/app/src/options.h b/app/src/options.h index ef7542e3..03b42913 100644 --- a/app/src/options.h +++ b/app/src/options.h @@ -59,6 +59,14 @@ 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 { diff --git a/app/src/server.c b/app/src/server.c index 6979c09b..153219c3 100644 --- a/app/src/server.c +++ b/app/src/server.c @@ -149,6 +149,22 @@ 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; diff --git a/doc/audio.md b/doc/audio.md index 85f76ac5..142626f5 100644 --- a/doc/audio.md +++ b/doc/audio.md @@ -66,6 +66,20 @@ 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/server/src/main/java/com/genymobile/scrcpy/audio/AudioSource.java b/server/src/main/java/com/genymobile/scrcpy/audio/AudioSource.java index 353f2281..d16b5e38 100644 --- a/server/src/main/java/com/genymobile/scrcpy/audio/AudioSource.java +++ b/server/src/main/java/com/genymobile/scrcpy/audio/AudioSource.java @@ -1,11 +1,21 @@ 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); + 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); private final String name; private final int directAudioSource; From dd1bfae4e00e5756e5f4f43649ce9d6a824cf028 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sat, 29 Mar 2025 15:02:38 +0100 Subject: [PATCH 36/43] Upgrade libusb (1.0.28) --- app/deps/libusb.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/deps/libusb.sh b/app/deps/libusb.sh index 340b0f70..4be03eb1 100755 --- a/app/deps/libusb.sh +++ b/app/deps/libusb.sh @@ -5,10 +5,10 @@ cd "$DEPS_DIR" . common process_args "$@" -VERSION=1.0.27 +VERSION=1.0.28 FILENAME=libusb-$VERSION.tar.gz PROJECT_DIR=libusb-$VERSION -SHA256SUM=e8f18a7a36ecbb11fb820bd71540350d8f61bcd9db0d2e8c18a6fb80b214a3de +SHA256SUM=378b3709a405065f8f9fb9f35e82d666defde4d342c2a1b181a9ac134d23c6fe cd "$SOURCES_DIR" From b7add421544039c4fd5ba97243578f0fbcc908a7 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sat, 29 Mar 2025 15:03:42 +0100 Subject: [PATCH 37/43] Upgrade SDL (2.32.2) Also apply this additional patch to fix the build: --- ...that-the-correct-struct-is-used-for-.patch | 33 +++++++++++++++++++ app/deps/sdl.sh | 5 +-- 2 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 app/deps/patches/SDL-pipewire-Ensure-that-the-correct-struct-is-used-for-.patch diff --git a/app/deps/patches/SDL-pipewire-Ensure-that-the-correct-struct-is-used-for-.patch b/app/deps/patches/SDL-pipewire-Ensure-that-the-correct-struct-is-used-for-.patch new file mode 100644 index 00000000..cbb516ec --- /dev/null +++ b/app/deps/patches/SDL-pipewire-Ensure-that-the-correct-struct-is-used-for-.patch @@ -0,0 +1,33 @@ +From 6be87ceb33a9aad3bf5204bb13b3a5e8b498fd26 Mon Sep 17 00:00:00 2001 +From: Neal Gompa +Date: Mon, 10 Feb 2025 05:00:56 -0500 +Subject: [PATCH] pipewire: Ensure that the correct struct is used for + enumeration APIs + +PipeWire now requires the correct struct type is used, otherwise +it will fail to compile. + +Reference: https://gitlab.freedesktop.org/pipewire/pipewire/-/commit/188d920733f0791413d3386e5536ee7377f71b2f + +Fixes: https://github.com/libsdl-org/SDL/issues/12224 +(cherry picked from commit d35bef64e913dd7d5dd3153a4b61f10ef837dad6) +--- + src/audio/pipewire/SDL_pipewire.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/audio/pipewire/SDL_pipewire.c b/src/audio/pipewire/SDL_pipewire.c +index 889e05decb..5d1bfc28de 100644 +--- a/src/audio/pipewire/SDL_pipewire.c ++++ b/src/audio/pipewire/SDL_pipewire.c +@@ -590,7 +590,7 @@ static void node_event_info(void *object, const struct pw_node_info *info) + + /* Need to parse the parameters to get the sample rate */ + for (i = 0; i < info->n_params; ++i) { +- pw_node_enum_params(node->proxy, 0, info->params[i].id, 0, 0, NULL); ++ pw_node_enum_params((struct pw_node*)node->proxy, 0, info->params[i].id, 0, 0, NULL); + } + + hotplug_core_sync(node); +-- +2.49.0 + diff --git a/app/deps/sdl.sh b/app/deps/sdl.sh index c098e367..c3edee58 100755 --- a/app/deps/sdl.sh +++ b/app/deps/sdl.sh @@ -5,10 +5,10 @@ cd "$DEPS_DIR" . common process_args "$@" -VERSION=2.30.10 +VERSION=2.32.2 FILENAME=SDL-$VERSION.tar.gz PROJECT_DIR=SDL-release-$VERSION -SHA256SUM=35a8b9c4f3635d85762b904ac60ca4e0806bff89faeb269caafbe80860d67168 +SHA256SUM=f2c7297ae7b3d3910a8b131e1e2a558fdd6d1a4443d5e345374d45cadfcb05a4 cd "$SOURCES_DIR" @@ -18,6 +18,7 @@ then else get_file "https://github.com/libsdl-org/SDL/archive/refs/tags/release-$VERSION.tar.gz" "$FILENAME" "$SHA256SUM" tar xf "$FILENAME" # First level directory is "$PROJECT_DIR" + patch -d "$PROJECT_DIR" -p1 < "$PATCHES_DIR"/SDL-pipewire-Ensure-that-the-correct-struct-is-used-for-.patch fi mkdir -p "$BUILD_DIR/$PROJECT_DIR" From 5d12d9071dad6d9d80197546d820dc90fbd31998 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sat, 29 Mar 2025 15:06:00 +0100 Subject: [PATCH 38/43] Upgrade FFmpeg (7.1.1) --- app/deps/ffmpeg.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/deps/ffmpeg.sh b/app/deps/ffmpeg.sh index d268ca91..fb8b9a25 100755 --- a/app/deps/ffmpeg.sh +++ b/app/deps/ffmpeg.sh @@ -5,10 +5,10 @@ cd "$DEPS_DIR" . common process_args "$@" -VERSION=7.1 +VERSION=7.1.1 FILENAME=ffmpeg-$VERSION.tar.xz PROJECT_DIR=ffmpeg-$VERSION -SHA256SUM=40973D44970DBC83EF302B0609F2E74982BE2D85916DD2EE7472D30678A7ABE6 +SHA256SUM=733984395e0dbbe5c046abda2dc49a5544e7e0e1e2366bba849222ae9e3a03b1 cd "$SOURCES_DIR" From 89b624770c7cc133cd14070a1ba766df3befa85f Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sat, 29 Mar 2025 15:45:28 +0100 Subject: [PATCH 39/43] Bump version to 3.2 --- app/scrcpy-windows.rc | 2 +- meson.build | 2 +- server/build.gradle | 4 ++-- server/build_without_gradle.sh | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/scrcpy-windows.rc b/app/scrcpy-windows.rc index 2c441aa1..19475e0b 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.1" + VALUE "ProductVersion", "3.2" END END BLOCK "VarFileInfo" diff --git a/meson.build b/meson.build index 84784814..b64a6c90 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('scrcpy', 'c', - version: '3.1', + version: '3.2', meson_version: '>= 0.49', default_options: [ 'c_std=c11', diff --git a/server/build.gradle b/server/build.gradle index 9c0543e9..02508001 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -7,8 +7,8 @@ android { applicationId "com.genymobile.scrcpy" minSdkVersion 21 targetSdkVersion 35 - versionCode 30100 - versionName "3.1" + versionCode 30200 + versionName "3.2" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { diff --git a/server/build_without_gradle.sh b/server/build_without_gradle.sh index e0b69aee..8bb8632b 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.1 +SCRCPY_VERSION_NAME=3.2 PLATFORM=${ANDROID_PLATFORM:-35} BUILD_TOOLS=${ANDROID_BUILD_TOOLS:-35.0.0} From e0f37f834bb2e9371c0ca893757b60eb36e759af Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sat, 29 Mar 2025 16:15:14 +0100 Subject: [PATCH 40/43] Update links to 3.2 --- README.md | 2 +- doc/build.md | 6 +++--- doc/linux.md | 6 +++--- doc/macos.md | 12 ++++++------ doc/windows.md | 12 ++++++------ install_release.sh | 4 ++-- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 16b8bca1..a3b0d834 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ source for the project. Do not download releases from random websites, even if their name contains `scrcpy`.** -# scrcpy (v3.1) +# scrcpy (v3.2) scrcpy diff --git a/doc/build.md b/doc/build.md index 2776ed01..afe8b21b 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.1`][direct-scrcpy-server] - SHA-256: `958f0944a62f23b1f33a16e9eb14844c1a04b882ca175a738c16d23cb22b86c0` + - [`scrcpy-server-v3.2`][direct-scrcpy-server] + SHA-256: `b920e0ea01936bf2482f4ba2fa985c22c13c621999e3d33b45baa5acfc1ea3d0` -[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v3.1/scrcpy-server-v3.1 +[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v3.2/scrcpy-server-v3.2 Download the prebuilt server somewhere, and specify its path during the Meson configuration: diff --git a/doc/linux.md b/doc/linux.md index 9beaed1e..52345d1a 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.1.tar.gz`][direct-linux-x86_64] (x86_64) - SHA-256: `37dba54092ed9ec6b2f8f95432f61b8ea124aec9f1e9f2b3d22d4b10bb04c59a` + - [`scrcpy-linux-x86_64-v3.2.tar.gz`][direct-linux-x86_64] (x86_64) + SHA-256: `df6cf000447428fcde322022848d655ff0211d98688d0f17cbbf21be9c1272be` [latest release]: https://github.com/Genymobile/scrcpy/releases/latest -[direct-linux-x86_64]: https://github.com/Genymobile/scrcpy/releases/download/v3.1/scrcpy-linux-x86_64-v3.1.tar.gz +[direct-linux-x86_64]: https://github.com/Genymobile/scrcpy/releases/download/v3.2/scrcpy-linux-x86_64-v3.2.tar.gz and extract it. diff --git a/doc/macos.md b/doc/macos.md index 56d9f168..b0335d18 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.1.tar.gz`][direct-macos-aarch64] (aarch64) - SHA-256: `478618d940421e5f57942f5479d493ecbb38210682937a200f712aee5f235daf` + - [`scrcpy-macos-aarch64-v3.2.tar.gz`][direct-macos-aarch64] (aarch64) + SHA-256: `f6d1f3c5f74d4d46f5080baa5b56b69f5edbf698d47e0cf4e2a1fd5058f9507b` - - [`scrcpy-macos-x86_64-v3.1.tar.gz`][direct-macos-x86_64] (x86_64) - SHA-256: `acde98e29c273710ffa469371dbca4a728a44c41c380381f8a54e5b5301b9e87` + - [`scrcpy-macos-x86_64-v3.2.tar.gz`][direct-macos-x86_64] (x86_64) + SHA-256: `e337d5cf0ba4e1281699c338ce5f104aee96eb7b2893dc851399b6643eb4044e` [latest release]: https://github.com/Genymobile/scrcpy/releases/latest -[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 +[direct-macos-aarch64]: https://github.com/Genymobile/scrcpy/releases/download/v3.2/scrcpy-macos-aarch64-v3.2.tar.gz +[direct-macos-x86_64]: https://github.com/Genymobile/scrcpy/releases/download/v3.2/scrcpy-macos-x86_64-v3.2.tar.gz and extract it. diff --git a/doc/windows.md b/doc/windows.md index 89b80727..fb3e3887 100644 --- a/doc/windows.md +++ b/doc/windows.md @@ -6,14 +6,14 @@ Download the [latest release]: - - [`scrcpy-win64-v3.1.zip`][direct-win64] (64-bit) - SHA-256: `0c05ea395d95cfe36bee974eeb435a3db87ea5594ff738370d5dc3068a9538ca` - - [`scrcpy-win32-v3.1.zip`][direct-win32] (32-bit) - SHA-256: `2b4674ef76719680ac5a9b482d1943bdde3fa25821ad2e98f3c40c347d00d560` + - [`scrcpy-win64-v3.2.zip`][direct-win64] (64-bit) + SHA-256: `eaa27133e0520979873ba57ad651560a4cc2618373bd05450b23a84d32beafd0` + - [`scrcpy-win32-v3.2.zip`][direct-win32] (32-bit) + SHA-256: `4a3407d7f0c2c8a03e22a12cf0b5e1e585a5056fe23c8e5cf3252207c6fa8357` [latest release]: https://github.com/Genymobile/scrcpy/releases/latest -[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 +[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v3.2/scrcpy-win64-v3.2.zip +[direct-win32]: https://github.com/Genymobile/scrcpy/releases/download/v3.2/scrcpy-win32-v3.2.zip and extract it. diff --git a/install_release.sh b/install_release.sh index 3774be86..2d2d2c2f 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.1/scrcpy-server-v3.1 -PREBUILT_SERVER_SHA256=958f0944a62f23b1f33a16e9eb14844c1a04b882ca175a738c16d23cb22b86c0 +PREBUILT_SERVER_URL=https://github.com/Genymobile/scrcpy/releases/download/v3.2/scrcpy-server-v3.2 +PREBUILT_SERVER_SHA256=b920e0ea01936bf2482f4ba2fa985c22c13c621999e3d33b45baa5acfc1ea3d0 echo "[scrcpy] Downloading prebuilt server..." wget "$PREBUILT_SERVER_URL" -O scrcpy-server From db9dc6ae836193dcd7883d001fdddbf54cbe9859 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Tue, 1 Apr 2025 11:04:34 +0200 Subject: [PATCH 41/43] Make the snap version as obsolete The version of scrcpy packaged in snap is currently 1.25. Refs --- doc/linux.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/linux.md b/doc/linux.md index 52345d1a..979ef568 100644 --- a/doc/linux.md +++ b/doc/linux.md @@ -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` + - Snap: ~~`snap install scrcpy`~~ _(obsolete version)_ - … (see [repology](https://repology.org/project/scrcpy/versions)) From 5900e9e39c7496bf8dfc1f246c6bbf0a1e072a69 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Mon, 7 Apr 2025 10:30:56 +0200 Subject: [PATCH 42/43] Remove irrelevant link in FAQ --- FAQ.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/FAQ.md b/FAQ.md index 5f089cd7..24722c74 100644 --- a/FAQ.md +++ b/FAQ.md @@ -166,14 +166,13 @@ 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][text-input]. -A trick allows to also inject some [accented characters][accented-characters], +The default text injection method is limited to ASCII characters. 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 From c5ed2cfc28ee7c7b59b11eb4db1258ac1c633bff Mon Sep 17 00:00:00 2001 From: Nicholas Wilson Date: Fri, 18 Apr 2025 09:54:59 -0500 Subject: [PATCH 43/43] Replace "licence" with "license" in README Although "licence" is correct in British English, the rest of the statement uses "license," so change it for consistency. PR #6017 Signed-off-by: Romain Vimont --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a3b0d834..c1fd9f7f 100644 --- a/README.md +++ b/README.md @@ -207,7 +207,7 @@ work][donate]: [donate]: https://blog.rom1v.com/about/#support-my-open-source-work -## Licence +## License Copyright (C) 2018 Genymobile Copyright (C) 2018-2025 Romain Vimont