From 9d56d26d454551d5fae5631fe92c72be0c495abf Mon Sep 17 00:00:00 2001 From: David Griswold Date: Thu, 11 Sep 2025 15:36:14 +0300 Subject: [PATCH 01/31] Make virtual display presentable With this flag, apps with baked in two-screen support can see the virtual display as an external display they can present to. PR #6344 Signed-off-by: Romain Vimont --- .../java/com/genymobile/scrcpy/video/NewDisplayCapture.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/src/main/java/com/genymobile/scrcpy/video/NewDisplayCapture.java b/server/src/main/java/com/genymobile/scrcpy/video/NewDisplayCapture.java index 792b3a8a..e933f60e 100644 --- a/server/src/main/java/com/genymobile/scrcpy/video/NewDisplayCapture.java +++ b/server/src/main/java/com/genymobile/scrcpy/video/NewDisplayCapture.java @@ -25,6 +25,7 @@ public class NewDisplayCapture extends SurfaceCapture { // Internal fields copied from android.hardware.display.DisplayManager private static final int VIRTUAL_DISPLAY_FLAG_PUBLIC = android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC; + private static final int VIRTUAL_DISPLAY_FLAG_PRESENTATION = android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION; private static final int VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY = android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY; private static final int VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH = 1 << 6; private static final int VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT = 1 << 7; @@ -169,6 +170,7 @@ public class NewDisplayCapture extends SurfaceCapture { int virtualDisplayId; try { int flags = VIRTUAL_DISPLAY_FLAG_PUBLIC + | VIRTUAL_DISPLAY_FLAG_PRESENTATION | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY | VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH | VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT; From e11399aff0562a45e397beb9a98743be1c411710 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Thu, 18 Sep 2025 10:01:29 +0200 Subject: [PATCH 02/31] Ignore unknown methods in IDisplayWindowListener New Android versions may add methods to IDisplayWindowListener.aidl. When these methods are called by the system, they result in an AbstractMethodError because they are not implemented on the scrcpy side. To avoid releasing a new version for each newly added method, ignore them at the Binder level. Refs afaca80b375a2fbdb966c15f52645abb578071f4 Fixes #6362 --- .../android/view/IDisplayWindowListener.aidl | 23 ---------- .../wrappers/DisplayWindowListener.java | 45 ++++++------------- 2 files changed, 14 insertions(+), 54 deletions(-) diff --git a/server/src/main/aidl/android/view/IDisplayWindowListener.aidl b/server/src/main/aidl/android/view/IDisplayWindowListener.aidl index 3e664e81..0d1f1979 100644 --- a/server/src/main/aidl/android/view/IDisplayWindowListener.aidl +++ b/server/src/main/aidl/android/view/IDisplayWindowListener.aidl @@ -48,27 +48,4 @@ oneway interface IDisplayWindowListener { * Called when a display is removed from the hierarchy. */ void onDisplayRemoved(int displayId); - - /** - * Called when fixed rotation is started on a display. - */ - void onFixedRotationStarted(int displayId, int newRotation); - - /** - * Called when the previous fixed rotation on a display is finished. - */ - void onFixedRotationFinished(int displayId); - - /** - * Called when the keep clear ares on a display have changed. - */ - void onKeepClearAreasChanged(int displayId, in List restricted, in List unrestricted); - - /** - * Called when the eligibility of the desktop mode for a display have changed. - */ - void onDesktopModeEligibleChanged(int displayId); - - void onDisplayAddSystemDecorations(int displayId); - void onDisplayRemoveSystemDecorations(int displayId); } diff --git a/server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayWindowListener.java b/server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayWindowListener.java index b8b00e44..1573d817 100644 --- a/server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayWindowListener.java +++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayWindowListener.java @@ -1,10 +1,11 @@ package com.genymobile.scrcpy.wrappers; -import android.content.res.Configuration; -import android.graphics.Rect; -import android.view.IDisplayWindowListener; +import com.genymobile.scrcpy.util.Ln; -import java.util.List; +import android.content.res.Configuration; +import android.os.Parcel; +import android.os.RemoteException; +import android.view.IDisplayWindowListener; public class DisplayWindowListener extends IDisplayWindowListener.Stub { @Override @@ -23,32 +24,14 @@ public class DisplayWindowListener extends IDisplayWindowListener.Stub { } @Override - public void onFixedRotationStarted(int displayId, int newRotation) { - // empty default implementation - } - - @Override - public void onFixedRotationFinished(int displayId) { - // empty default implementation - } - - @Override - public void onKeepClearAreasChanged(int displayId, List restricted, List unrestricted) { - // empty default implementation - } - - @Override - public void onDesktopModeEligibleChanged(int displayId) { - // empty default implementation - } - - @Override - public void onDisplayAddSystemDecorations(int displayId) { - // empty default implementation - } - - @Override - public void onDisplayRemoveSystemDecorations(int displayId) { - // empty default implementation + public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { + try { + return super.onTransact(code, data, reply, flags); + } catch (AbstractMethodError e) { + Ln.v("Ignoring AbstractMethodError: " + e.getMessage()); + // Ignore unknown methods, write default response to reply parcel + reply.writeNoException(); + return true; + } } } From bfb0872493ee49aaccb000afaa62187f46b7e990 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Tue, 23 Sep 2025 20:17:27 +0200 Subject: [PATCH 03/31] Avoid resetting pending frame The function update_texture() calls update_texture_internal() and falls back to set_pending_frame() if it fails. When the frame passed is the pending frame, call only the _internal() version instead. This will prevent issues with frame reference counts by ensuring the source and destination frames are never the same. Refs 6298ef095ffa9a2cdd2b4d245e71280743b5a59e Refs #6357 --- app/src/display.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/src/display.c b/app/src/display.c index aee8ef80..200118e6 100644 --- a/app/src/display.c +++ b/app/src/display.c @@ -181,6 +181,11 @@ sc_display_set_pending_frame(struct sc_display *display, const AVFrame *frame) { return true; } +// Forward declaration +static bool +sc_display_update_texture_internal(struct sc_display *display, + const AVFrame *frame); + static bool sc_display_apply_pending(struct sc_display *display) { if (display->pending.flags & SC_DISPLAY_PENDING_FLAG_SIZE) { @@ -196,7 +201,8 @@ sc_display_apply_pending(struct sc_display *display) { if (display->pending.flags & SC_DISPLAY_PENDING_FLAG_FRAME) { assert(display->pending.frame); - bool ok = sc_display_update_texture(display, display->pending.frame); + bool ok = sc_display_update_texture_internal(display, + display->pending.frame); if (!ok) { return false; } From be21e43be585a1595ee4ad41acacc25f3cffaebe Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Tue, 23 Sep 2025 20:18:26 +0200 Subject: [PATCH 04/31] Fix frame leak on pending frame update The previous pending frame was not unreferenced before referencing the new one, causing frames to leak whenever a texture update failed (typically on Windows when the window is minimized with D3D9). Refs 6298ef095ffa9a2cdd2b4d245e71280743b5a59e Fixes #4297 Fixes #6357 --- app/src/display.c | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/display.c b/app/src/display.c index 200118e6..15f9a1f1 100644 --- a/app/src/display.c +++ b/app/src/display.c @@ -170,6 +170,7 @@ sc_display_set_pending_frame(struct sc_display *display, const AVFrame *frame) { } } + av_frame_unref(display->pending.frame); int r = av_frame_ref(display->pending.frame, frame); if (r) { LOGE("Could not ref frame: %d", r); From 10a0974f43cce259ba6c36346e3d962bf06e5259 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Tue, 23 Sep 2025 21:16:29 +0200 Subject: [PATCH 05/31] Bump version to 3.3.3 --- 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 f75a8976..196a547c 100644 --- a/app/scrcpy-windows.rc +++ b/app/scrcpy-windows.rc @@ -13,7 +13,7 @@ BEGIN VALUE "LegalCopyright", "Romain Vimont, Genymobile" VALUE "OriginalFilename", "scrcpy.exe" VALUE "ProductName", "scrcpy" - VALUE "ProductVersion", "3.3.2" + VALUE "ProductVersion", "3.3.3" END END BLOCK "VarFileInfo" diff --git a/meson.build b/meson.build index 4a03d122..7abe6a8c 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('scrcpy', 'c', - version: '3.3.2', + version: '3.3.3', meson_version: '>= 0.49', default_options: [ 'c_std=c11', diff --git a/server/build.gradle b/server/build.gradle index 7afd1e7a..dbe889e0 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -7,8 +7,8 @@ android { applicationId "com.genymobile.scrcpy" minSdkVersion 21 targetSdkVersion 35 - versionCode 30302 - versionName "3.3.2" + versionCode 30303 + versionName "3.3.3" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { diff --git a/server/build_without_gradle.sh b/server/build_without_gradle.sh index dd1fe623..edc4f793 100755 --- a/server/build_without_gradle.sh +++ b/server/build_without_gradle.sh @@ -12,7 +12,7 @@ set -e SCRCPY_DEBUG=false -SCRCPY_VERSION_NAME=3.3.2 +SCRCPY_VERSION_NAME=3.3.3 PLATFORM=${ANDROID_PLATFORM:-35} BUILD_TOOLS=${ANDROID_BUILD_TOOLS:-35.0.0} From e5e58b1b307d92cbe3432431a9e22cd648f8d4d1 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sat, 27 Sep 2025 16:10:10 +0200 Subject: [PATCH 06/31] Upgrade links to 3.3.3 --- 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 a610a8ba..195706f6 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.3.2) +# scrcpy (v3.3.3) scrcpy diff --git a/doc/build.md b/doc/build.md index d6e067d3..1c228fb8 100644 --- a/doc/build.md +++ b/doc/build.md @@ -233,10 +233,10 @@ install` must be run as root)._ #### Option 2: Use prebuilt server - - [`scrcpy-server-v3.3.2`][direct-scrcpy-server] - SHA-256: `2ee5ca0863ef440f5b7c75856bb475c5283d0a8359cb370b1c161314fd29dfd9` + - [`scrcpy-server-v3.3.3`][direct-scrcpy-server] + SHA-256: `7e70323ba7f259649dd4acce97ac4fefbae8102b2c6d91e2e7be613fd5354be0` -[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v3.3.2/scrcpy-server-v3.3.2 +[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v3.3.3/scrcpy-server-v3.3.3 Download the prebuilt server somewhere, and specify its path during the Meson configuration: diff --git a/doc/linux.md b/doc/linux.md index 7498ed98..39e92961 100644 --- a/doc/linux.md +++ b/doc/linux.md @@ -6,11 +6,11 @@ Download a static build of the [latest release]: - - [`scrcpy-linux-x86_64-v3.3.2.tar.gz`][direct-linux-x86_64] (x86_64) - SHA-256: `92bed0fa274b9165eb8740e07cf2e2692ebe09ad6911175b0ee42e08799dc51c` + - [`scrcpy-linux-x86_64-v3.3.3.tar.gz`][direct-linux-x86_64] (x86_64) + SHA-256: `9b30e813e8191329ba8025dc80cb0f198fb0a318960a3b5c15395cf675c9c638` [latest release]: https://github.com/Genymobile/scrcpy/releases/latest -[direct-linux-x86_64]: https://github.com/Genymobile/scrcpy/releases/download/v3.3.2/scrcpy-linux-x86_64-v3.3.2.tar.gz +[direct-linux-x86_64]: https://github.com/Genymobile/scrcpy/releases/download/v3.3.3/scrcpy-linux-x86_64-v3.3.3.tar.gz and extract it. diff --git a/doc/macos.md b/doc/macos.md index 81e0d65d..7ba08c85 100644 --- a/doc/macos.md +++ b/doc/macos.md @@ -6,15 +6,15 @@ Download a static build of the [latest release]: - - [`scrcpy-macos-aarch64-v3.3.2.tar.gz`][direct-macos-aarch64] (aarch64) - SHA-256: `a213eeff8ac95893e69c4bc6a001a402c6680dbfcb74cb353c0124184ed88e8d` + - [`scrcpy-macos-aarch64-v3.3.3.tar.gz`][direct-macos-aarch64] (aarch64) + SHA-256: `b93299468f19ae89ac70f7c1453914c41f1f2bcd31f6ab530038da885c19581f` - - [`scrcpy-macos-x86_64-v3.3.2.tar.gz`][direct-macos-x86_64] (x86_64) - SHA-256: `2a1b27fbb67821a886c7e8dea641899836c0abbe7afd37905584b99bcd21bc04` + - [`scrcpy-macos-x86_64-v3.3.3.tar.gz`][direct-macos-x86_64] (x86_64) + SHA-256: `c767fc1d41e4ae26e40558656570962f474739924fd22ee023d8754889ee4366` [latest release]: https://github.com/Genymobile/scrcpy/releases/latest -[direct-macos-aarch64]: https://github.com/Genymobile/scrcpy/releases/download/v3.3.2/scrcpy-macos-aarch64-v3.3.2.tar.gz -[direct-macos-x86_64]: https://github.com/Genymobile/scrcpy/releases/download/v3.3.2/scrcpy-macos-x86_64-v3.3.2.tar.gz +[direct-macos-aarch64]: https://github.com/Genymobile/scrcpy/releases/download/v3.3.3/scrcpy-macos-aarch64-v3.3.3.tar.gz +[direct-macos-x86_64]: https://github.com/Genymobile/scrcpy/releases/download/v3.3.3/scrcpy-macos-x86_64-v3.3.3.tar.gz and extract it. diff --git a/doc/windows.md b/doc/windows.md index 63fff892..b6dc220f 100644 --- a/doc/windows.md +++ b/doc/windows.md @@ -6,14 +6,14 @@ Download the [latest release]: - - [`scrcpy-win64-v3.3.2.zip`][direct-win64] (64-bit) - SHA-256: `8f7b19371657b872e271e6b02a0c758c61c6e31e032e9df55a83aa3aab960bfa` - - [`scrcpy-win32-v3.3.2.zip`][direct-win32] (32-bit) - SHA-256: `cff2bbebdcfe14a023b77cd601fc4420b5631b19bd4b09ce4dcd4e5bf8e63244` + - [`scrcpy-win64-v3.3.3.zip`][direct-win64] (64-bit) + SHA-256: `4b458d33d0436688c69875cd267cae6fa8be08aa3c17772edf3a940a3dc4b17e` + - [`scrcpy-win32-v3.3.3.zip`][direct-win32] (32-bit) + SHA-256: `e3d43e21c0bd6e070381c390c1e4cccd48a1e71ae73a8c217e6e6b8506598c79` [latest release]: https://github.com/Genymobile/scrcpy/releases/latest -[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v3.3.2/scrcpy-win64-v3.3.2.zip -[direct-win32]: https://github.com/Genymobile/scrcpy/releases/download/v3.3.2/scrcpy-win32-v3.3.2.zip +[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v3.3.3/scrcpy-win64-v3.3.3.zip +[direct-win32]: https://github.com/Genymobile/scrcpy/releases/download/v3.3.3/scrcpy-win32-v3.3.3.zip and extract it. diff --git a/install_release.sh b/install_release.sh index f6e94dae..3419a9bc 100755 --- a/install_release.sh +++ b/install_release.sh @@ -2,8 +2,8 @@ set -e BUILDDIR=build-auto -PREBUILT_SERVER_URL=https://github.com/Genymobile/scrcpy/releases/download/v3.3.2/scrcpy-server-v3.3.2 -PREBUILT_SERVER_SHA256=2ee5ca0863ef440f5b7c75856bb475c5283d0a8359cb370b1c161314fd29dfd9 +PREBUILT_SERVER_URL=https://github.com/Genymobile/scrcpy/releases/download/v3.3.3/scrcpy-server-v3.3.3 +PREBUILT_SERVER_SHA256=7e70323ba7f259649dd4acce97ac4fefbae8102b2c6d91e2e7be613fd5354be0 echo "[scrcpy] Downloading prebuilt server..." wget "$PREBUILT_SERVER_URL" -O scrcpy-server From 9d7a4c88e067dc523bd16565e1592717dcaca360 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sun, 5 Oct 2025 21:34:59 +0200 Subject: [PATCH 07/31] Apply workarounds in the cleanup process Accessing settings may require workarounds on certain devices. Fixes #6405 --- server/src/main/java/com/genymobile/scrcpy/CleanUp.java | 1 + 1 file changed, 1 insertion(+) diff --git a/server/src/main/java/com/genymobile/scrcpy/CleanUp.java b/server/src/main/java/com/genymobile/scrcpy/CleanUp.java index 77018afa..a5816c32 100644 --- a/server/src/main/java/com/genymobile/scrcpy/CleanUp.java +++ b/server/src/main/java/com/genymobile/scrcpy/CleanUp.java @@ -196,6 +196,7 @@ public final class CleanUp { // Needed for workarounds prepareMainLooper(); + Workarounds.apply(); int displayId = Integer.parseInt(args[0]); int restoreStayOn = Integer.parseInt(args[1]); From 3e40b2473772cea3a23d4932088fd0bc4cc0f52c Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Thu, 9 Oct 2025 09:26:47 +0200 Subject: [PATCH 08/31] Fix UHID_OUTPUT message parsing The bounds check was incorrect. Fixes #6415 --- app/src/device_msg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/device_msg.c b/app/src/device_msg.c index 7621c040..2172d59b 100644 --- a/app/src/device_msg.c +++ b/app/src/device_msg.c @@ -53,7 +53,7 @@ sc_device_msg_deserialize(const uint8_t *buf, size_t len, } uint16_t id = sc_read16be(&buf[1]); size_t size = sc_read16be(&buf[3]); - if (size < len - 5) { + if (size > len - 5) { return 0; // not available } uint8_t *data = malloc(size); From eee3f24739dfcff2ebc428e9b2b19eb823641849 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sun, 19 Oct 2025 17:02:35 +0200 Subject: [PATCH 09/31] Upgrade Gradle and use Android SDK 36 --- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 4 ++-- server/build.gradle | 17 ++++++++++------- server/build_without_gradle.sh | 4 ++-- .../main/java/com/genymobile/scrcpy/Server.java | 4 ++++ 5 files changed, 19 insertions(+), 12 deletions(-) diff --git a/build.gradle b/build.gradle index 81c91d37..8c671e6f 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:8.7.1' + classpath 'com.android.tools.build:gradle:8.13.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b34b7096..fbd6c374 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip # https://gradle.org/release-checksums/ -distributionSha256Sum=d725d707bfabd4dfdc958c624003b3c80accc03f7037b5122c4b1d0ef15cecab +distributionSha256Sum=bd71102213493060956ec229d946beee57158dbd89d0e62b91bca0fa2c5f3531 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/server/build.gradle b/server/build.gradle index dbe889e0..3dfc7144 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -1,15 +1,15 @@ apply plugin: 'com.android.application' android { - namespace 'com.genymobile.scrcpy' - compileSdk 35 + namespace = 'com.genymobile.scrcpy' + compileSdk 36 defaultConfig { - applicationId "com.genymobile.scrcpy" + applicationId = "com.genymobile.scrcpy" minSdkVersion 21 - targetSdkVersion 35 + targetSdkVersion 36 versionCode 30303 versionName "3.3.3" - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + testInstrumentationRunner = "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { @@ -18,8 +18,11 @@ android { } } buildFeatures { - buildConfig true - aidl true + buildConfig = true + aidl = true + } + lint { + disable 'UseRequiresApi' } } diff --git a/server/build_without_gradle.sh b/server/build_without_gradle.sh index edc4f793..8c572f62 100755 --- a/server/build_without_gradle.sh +++ b/server/build_without_gradle.sh @@ -14,8 +14,8 @@ set -e SCRCPY_DEBUG=false SCRCPY_VERSION_NAME=3.3.3 -PLATFORM=${ANDROID_PLATFORM:-35} -BUILD_TOOLS=${ANDROID_BUILD_TOOLS:-35.0.0} +PLATFORM=${ANDROID_PLATFORM:-36} +BUILD_TOOLS=${ANDROID_BUILD_TOOLS:-36.0.0} PLATFORM_TOOLS="$ANDROID_HOME/platforms/android-$PLATFORM" BUILD_TOOLS_DIR="$ANDROID_HOME/build-tools/$BUILD_TOOLS" diff --git a/server/src/main/java/com/genymobile/scrcpy/Server.java b/server/src/main/java/com/genymobile/scrcpy/Server.java index a08c948c..a675f1e7 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Server.java +++ b/server/src/main/java/com/genymobile/scrcpy/Server.java @@ -226,7 +226,11 @@ public final class Server { private static void internalMain(String... args) throws Exception { Thread.setDefaultUncaughtExceptionHandler((t, e) -> { + Thread.UncaughtExceptionHandler defaultHandler = Thread.getDefaultUncaughtExceptionHandler(); Ln.e("Exception on thread " + t, e); + if (defaultHandler != null) { + defaultHandler.uncaughtException(t, e); + } }); prepareMainLooper(); From f3d4fde15b51fd85a59c835d0310aac19d429737 Mon Sep 17 00:00:00 2001 From: Yan Date: Sun, 5 Oct 2025 22:53:22 +0200 Subject: [PATCH 10/31] Fix handling of non-integer ANDROID_PLATFORM ANDROID_PLATFORM is not always an integer; it can also be a value like "36.1". Handle such cases properly. This fixes the following error: server/build_without_gradle.sh: line 89: [[: 36.1: syntax error: invalid arithmetic operator (error token is ".1") PR #6408 Signed-off-by: Romain Vimont --- server/build_without_gradle.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/build_without_gradle.sh b/server/build_without_gradle.sh index 8c572f62..bb477a32 100755 --- a/server/build_without_gradle.sh +++ b/server/build_without_gradle.sh @@ -86,7 +86,7 @@ javac -encoding UTF-8 -bootclasspath "$ANDROID_JAR" \ echo "Dexing..." cd "$CLASSES_DIR" -if [[ $PLATFORM -lt 31 ]] +if [[ "${PLATFORM%%.*}" -lt 31 ]] then # use dx "$BUILD_TOOLS_DIR/dx" --dex --output "$BUILD_DIR/classes.dex" \ From 925949d54ad88a595c8470b2e22b9cad10d98dfa Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Thu, 30 Oct 2025 22:26:51 +0100 Subject: [PATCH 11/31] Refactor dependency build scripts initialization Rename "common" to "_init" because it not only exposes common functions but also initializes environment variables. Call _init in a single line in all dependency build scripts. --- app/deps/{common => _init} | 7 +++---- app/deps/adb_linux.sh | 4 +--- app/deps/adb_macos.sh | 4 +--- app/deps/adb_windows.sh | 4 +--- app/deps/dav1d.sh | 4 +--- app/deps/ffmpeg.sh | 4 +--- app/deps/libusb.sh | 4 +--- app/deps/sdl.sh | 4 +--- 8 files changed, 10 insertions(+), 25 deletions(-) rename app/deps/{common => _init} (92%) diff --git a/app/deps/common b/app/deps/_init similarity index 92% rename from app/deps/common rename to app/deps/_init index daaa96c0..4612d9fe 100644 --- a/app/deps/common +++ b/app/deps/_init @@ -1,10 +1,9 @@ -#!/usr/bin/env bash # This file is intended to be sourced by other scripts, not executed process_args() { if [[ $# != 3 ]] then - # : win32 or win64 + # : linux, macos, win32 or win64 # : native or cross # : static or shared echo "Syntax: $0 " >&2 @@ -12,8 +11,8 @@ process_args() { fi HOST="$1" - BUILD_TYPE="$2" # native or cross - LINK_TYPE="$3" # static or shared + BUILD_TYPE="$2" + LINK_TYPE="$3" DIRNAME="$HOST-$BUILD_TYPE-$LINK_TYPE" if [[ "$BUILD_TYPE" != native && "$BUILD_TYPE" != cross ]] diff --git a/app/deps/adb_linux.sh b/app/deps/adb_linux.sh index a3e339ec..c284dd24 100755 --- a/app/deps/adb_linux.sh +++ b/app/deps/adb_linux.sh @@ -1,8 +1,6 @@ #!/usr/bin/env bash set -ex -DEPS_DIR=$(dirname ${BASH_SOURCE[0]}) -cd "$DEPS_DIR" -. common +. $(dirname ${BASH_SOURCE[0]})/_init "$@" VERSION=36.0.0 FILENAME=platform-tools_r$VERSION-linux.zip diff --git a/app/deps/adb_macos.sh b/app/deps/adb_macos.sh index 6880fb93..e580e571 100755 --- a/app/deps/adb_macos.sh +++ b/app/deps/adb_macos.sh @@ -1,8 +1,6 @@ #!/usr/bin/env bash set -ex -DEPS_DIR=$(dirname ${BASH_SOURCE[0]}) -cd "$DEPS_DIR" -. common +. $(dirname ${BASH_SOURCE[0]})/_init "$@" VERSION=36.0.0 FILENAME=platform-tools_r$VERSION-darwin.zip diff --git a/app/deps/adb_windows.sh b/app/deps/adb_windows.sh index b9bd05dc..da95bb81 100755 --- a/app/deps/adb_windows.sh +++ b/app/deps/adb_windows.sh @@ -1,8 +1,6 @@ #!/usr/bin/env bash set -ex -DEPS_DIR=$(dirname ${BASH_SOURCE[0]}) -cd "$DEPS_DIR" -. common +. $(dirname ${BASH_SOURCE[0]})/_init "$@" VERSION=36.0.0 FILENAME=platform-tools_r$VERSION-win.zip diff --git a/app/deps/dav1d.sh b/app/deps/dav1d.sh index 3069b6fe..89956636 100755 --- a/app/deps/dav1d.sh +++ b/app/deps/dav1d.sh @@ -1,8 +1,6 @@ #!/usr/bin/env bash set -ex -DEPS_DIR=$(dirname ${BASH_SOURCE[0]}) -cd "$DEPS_DIR" -. common +. $(dirname ${BASH_SOURCE[0]})/_init process_args "$@" VERSION=1.5.0 diff --git a/app/deps/ffmpeg.sh b/app/deps/ffmpeg.sh index fb8b9a25..a879669d 100755 --- a/app/deps/ffmpeg.sh +++ b/app/deps/ffmpeg.sh @@ -1,8 +1,6 @@ #!/usr/bin/env bash set -ex -DEPS_DIR=$(dirname ${BASH_SOURCE[0]}) -cd "$DEPS_DIR" -. common +. $(dirname ${BASH_SOURCE[0]})/_init process_args "$@" VERSION=7.1.1 diff --git a/app/deps/libusb.sh b/app/deps/libusb.sh index 887a2a77..d1dd4cbf 100755 --- a/app/deps/libusb.sh +++ b/app/deps/libusb.sh @@ -1,8 +1,6 @@ #!/usr/bin/env bash set -ex -DEPS_DIR=$(dirname ${BASH_SOURCE[0]}) -cd "$DEPS_DIR" -. common +. $(dirname ${BASH_SOURCE[0]})/_init process_args "$@" VERSION=1.0.29 diff --git a/app/deps/sdl.sh b/app/deps/sdl.sh index e04deb0d..d382c183 100755 --- a/app/deps/sdl.sh +++ b/app/deps/sdl.sh @@ -1,8 +1,6 @@ #!/usr/bin/env bash set -ex -DEPS_DIR=$(dirname ${BASH_SOURCE[0]}) -cd "$DEPS_DIR" -. common +. $(dirname ${BASH_SOURCE[0]})/_init process_args "$@" VERSION=2.32.8 From 3281fda6ef6189b173a464c845a87511bfaf62e9 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Thu, 30 Oct 2025 22:31:16 +0100 Subject: [PATCH 12/31] Set URL explicitly in dependency build scripts Explicitly set the URL of each dependency at the beginning of its script. PROJECT_DIR and FILENAME are internal details. --- app/deps/adb_linux.sh | 8 +++++--- app/deps/adb_macos.sh | 8 +++++--- app/deps/adb_windows.sh | 8 +++++--- app/deps/dav1d.sh | 8 +++++--- app/deps/ffmpeg.sh | 8 +++++--- app/deps/libusb.sh | 8 +++++--- app/deps/sdl.sh | 11 +++++++---- 7 files changed, 37 insertions(+), 22 deletions(-) diff --git a/app/deps/adb_linux.sh b/app/deps/adb_linux.sh index c284dd24..e47edf88 100755 --- a/app/deps/adb_linux.sh +++ b/app/deps/adb_linux.sh @@ -3,17 +3,19 @@ set -ex . $(dirname ${BASH_SOURCE[0]})/_init "$@" VERSION=36.0.0 -FILENAME=platform-tools_r$VERSION-linux.zip -PROJECT_DIR=platform-tools-$VERSION-linux +URL="https://dl.google.com/android/repository/platform-tools_r$VERSION-linux.zip" SHA256SUM=0ead642c943ffe79701fccca8f5f1c69c4ce4f43df2eefee553f6ccb27cbfbe8 +PROJECT_DIR="platform-tools-$VERSION-linux" +FILENAME="$PROJECT_DIR.zip" + cd "$SOURCES_DIR" if [[ -d "$PROJECT_DIR" ]] then echo "$PWD/$PROJECT_DIR" found else - get_file "https://dl.google.com/android/repository/$FILENAME" "$FILENAME" "$SHA256SUM" + get_file "$URL" "$FILENAME" "$SHA256SUM" mkdir -p "$PROJECT_DIR" cd "$PROJECT_DIR" ZIP_PREFIX=platform-tools diff --git a/app/deps/adb_macos.sh b/app/deps/adb_macos.sh index e580e571..a56117dc 100755 --- a/app/deps/adb_macos.sh +++ b/app/deps/adb_macos.sh @@ -3,17 +3,19 @@ set -ex . $(dirname ${BASH_SOURCE[0]})/_init "$@" VERSION=36.0.0 -FILENAME=platform-tools_r$VERSION-darwin.zip -PROJECT_DIR=platform-tools-$VERSION-darwin +URL="https://dl.google.com/android/repository/platform-tools_r$VERSION-darwin.zip" SHA256SUM=d3e9fa1df3345cf728586908426615a60863d2632f73f1ce14f0f1349ef000fd +PROJECT_DIR="platform-tools-$VERSION-darwin" +FILENAME="$PROJECT_DIR.zip" + cd "$SOURCES_DIR" if [[ -d "$PROJECT_DIR" ]] then echo "$PWD/$PROJECT_DIR" found else - get_file "https://dl.google.com/android/repository/$FILENAME" "$FILENAME" "$SHA256SUM" + get_file "$URL" "$FILENAME" "$SHA256SUM" mkdir -p "$PROJECT_DIR" cd "$PROJECT_DIR" ZIP_PREFIX=platform-tools diff --git a/app/deps/adb_windows.sh b/app/deps/adb_windows.sh index da95bb81..69287b1d 100755 --- a/app/deps/adb_windows.sh +++ b/app/deps/adb_windows.sh @@ -3,17 +3,19 @@ set -ex . $(dirname ${BASH_SOURCE[0]})/_init "$@" VERSION=36.0.0 -FILENAME=platform-tools_r$VERSION-win.zip -PROJECT_DIR=platform-tools-$VERSION-windows +URL="https://dl.google.com/android/repository/platform-tools_r$VERSION-win.zip" SHA256SUM=12c2841f354e92a0eb2fd7bf6f0f9bf8538abce7bd6b060ac8349d6f6a61107c +PROJECT_DIR="platform-tools-$VERSION-windows" +FILENAME="$PROJECT_DIR.zip" + cd "$SOURCES_DIR" if [[ -d "$PROJECT_DIR" ]] then echo "$PWD/$PROJECT_DIR" found else - get_file "https://dl.google.com/android/repository/$FILENAME" "$FILENAME" "$SHA256SUM" + get_file "$URL" "$FILENAME" "$SHA256SUM" mkdir -p "$PROJECT_DIR" cd "$PROJECT_DIR" ZIP_PREFIX=platform-tools diff --git a/app/deps/dav1d.sh b/app/deps/dav1d.sh index 89956636..46fa3954 100755 --- a/app/deps/dav1d.sh +++ b/app/deps/dav1d.sh @@ -4,17 +4,19 @@ set -ex process_args "$@" VERSION=1.5.0 -FILENAME=dav1d-$VERSION.tar.gz -PROJECT_DIR=dav1d-$VERSION +URL="https://code.videolan.org/videolan/dav1d/-/archive/$VERSION/dav1d-$VERSION.tar.gz" SHA256SUM=78b15d9954b513ea92d27f39362535ded2243e1b0924fde39f37a31ebed5f76b +PROJECT_DIR="dav1d-$VERSION" +FILENAME="$PROJECT_DIR.tar.gz" + cd "$SOURCES_DIR" if [[ -d "$PROJECT_DIR" ]] then echo "$PWD/$PROJECT_DIR" found else - get_file "https://code.videolan.org/videolan/dav1d/-/archive/$VERSION/$FILENAME" "$FILENAME" "$SHA256SUM" + get_file "$URL" "$FILENAME" "$SHA256SUM" tar xf "$FILENAME" # First level directory is "$PROJECT_DIR" fi diff --git a/app/deps/ffmpeg.sh b/app/deps/ffmpeg.sh index a879669d..d6cfad54 100755 --- a/app/deps/ffmpeg.sh +++ b/app/deps/ffmpeg.sh @@ -4,17 +4,19 @@ set -ex process_args "$@" VERSION=7.1.1 -FILENAME=ffmpeg-$VERSION.tar.xz -PROJECT_DIR=ffmpeg-$VERSION +URL="https://ffmpeg.org/releases/ffmpeg-$VERSION.tar.xz" SHA256SUM=733984395e0dbbe5c046abda2dc49a5544e7e0e1e2366bba849222ae9e3a03b1 +PROJECT_DIR="ffmpeg-$VERSION" +FILENAME="$PROJECT_DIR.tar.xz" + cd "$SOURCES_DIR" if [[ -d "$PROJECT_DIR" ]] then echo "$PWD/$PROJECT_DIR" found else - get_file "https://ffmpeg.org/releases/$FILENAME" "$FILENAME" "$SHA256SUM" + get_file "$URL" "$FILENAME" "$SHA256SUM" tar xf "$FILENAME" # First level directory is "$PROJECT_DIR" fi diff --git a/app/deps/libusb.sh b/app/deps/libusb.sh index d1dd4cbf..72170b9e 100755 --- a/app/deps/libusb.sh +++ b/app/deps/libusb.sh @@ -4,17 +4,19 @@ set -ex process_args "$@" VERSION=1.0.29 -FILENAME=libusb-$VERSION.tar.gz -PROJECT_DIR=libusb-$VERSION +URL="https://github.com/libusb/libusb/archive/refs/tags/v$VERSION.tar.gz" SHA256SUM=7c2dd39c0b2589236e48c93247c986ae272e27570942b4163cb00a060fcf1b74 +PROJECT_DIR="libusb-$VERSION" +FILENAME="$PROJECT_DIR.tar.gz" + cd "$SOURCES_DIR" if [[ -d "$PROJECT_DIR" ]] then echo "$PWD/$PROJECT_DIR" found else - get_file "https://github.com/libusb/libusb/archive/refs/tags/v$VERSION.tar.gz" "$FILENAME" "$SHA256SUM" + get_file "$URL" "$FILENAME" "$SHA256SUM" tar xf "$FILENAME" # First level directory is "$PROJECT_DIR" fi diff --git a/app/deps/sdl.sh b/app/deps/sdl.sh index d382c183..f74ccf41 100755 --- a/app/deps/sdl.sh +++ b/app/deps/sdl.sh @@ -4,18 +4,21 @@ set -ex process_args "$@" VERSION=2.32.8 -FILENAME=SDL-$VERSION.tar.gz -PROJECT_DIR=SDL-release-$VERSION +URL="https://github.com/libsdl-org/SDL/archive/refs/tags/release-$VERSION.tar.gz" SHA256SUM=dd35e05644ae527848d02433bec24dd0ea65db59faecf1a0e5d1880c533dac2c +PROJECT_DIR="sdl-$VERSION" +FILENAME="$PROJECT_DIR.tar.gz" + cd "$SOURCES_DIR" if [[ -d "$PROJECT_DIR" ]] then echo "$PWD/$PROJECT_DIR" found 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" + get_file "$URL" "$FILENAME" "$SHA256SUM" + tar xf "$FILENAME" # First level directory is "SDL-release-$VERSION" + mv "SDL-release-$VERSION" "$PROJECT_DIR" fi mkdir -p "$BUILD_DIR/$PROJECT_DIR" From d0047b2110e2bd70ac2b27e7cec012747b01dfb7 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sat, 1 Nov 2025 00:34:20 +0100 Subject: [PATCH 13/31] Do not fail when uniqueId field is missing On some devices, DisplayInfo does not have a "uniqueId" field. This field is only used for correct UHID behavior on virtual displays, so its absence should not prevent scrcpy from working. Refs #6009 Fixes #6461 --- .../com/genymobile/scrcpy/wrappers/DisplayManager.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayManager.java b/server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayManager.java index a12470a4..9e84ec00 100644 --- a/server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayManager.java +++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayManager.java @@ -139,7 +139,13 @@ public final class DisplayManager { int layerStack = cls.getDeclaredField("layerStack").getInt(displayInfo); int flags = cls.getDeclaredField("flags").getInt(displayInfo); int dpi = cls.getDeclaredField("logicalDensityDpi").getInt(displayInfo); - String uniqueId = (String) cls.getDeclaredField("uniqueId").get(displayInfo); + String uniqueId; + try { + uniqueId = (String) cls.getDeclaredField("uniqueId").get(displayInfo); + } catch (NoSuchFieldException e) { + // This field might not exist: + uniqueId = null; + } return new DisplayInfo(displayId, new Size(width, height), rotation, layerStack, flags, dpi, uniqueId); } catch (ReflectiveOperationException e) { throw new AssertionError(e); From 35308510712ebf464c58d5aaebd4da9515f9098c Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Tue, 4 Nov 2025 20:35:39 +0100 Subject: [PATCH 14/31] Fix uncaught exception handler The default handler was mistakenly retrieved after our custom handler was set, causing it to reference itself. As a result, this led to infinite recursion. Bug introduced by eee3f24739dfcff2ebc428e9b2b19eb823641849. --- server/src/main/java/com/genymobile/scrcpy/Server.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/java/com/genymobile/scrcpy/Server.java b/server/src/main/java/com/genymobile/scrcpy/Server.java index a675f1e7..04e4a837 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Server.java +++ b/server/src/main/java/com/genymobile/scrcpy/Server.java @@ -225,8 +225,8 @@ public final class Server { } private static void internalMain(String... args) throws Exception { + Thread.UncaughtExceptionHandler defaultHandler = Thread.getDefaultUncaughtExceptionHandler(); Thread.setDefaultUncaughtExceptionHandler((t, e) -> { - Thread.UncaughtExceptionHandler defaultHandler = Thread.getDefaultUncaughtExceptionHandler(); Ln.e("Exception on thread " + t, e); if (defaultHandler != null) { defaultHandler.uncaughtException(t, e); From 7dd9bcaf606eca7f785ab71423aa113c214522a5 Mon Sep 17 00:00:00 2001 From: valord577 Date: Tue, 18 Nov 2025 15:23:07 +0800 Subject: [PATCH 15/31] Prevent error log interleaving Between the calls to CONSOLE_ERR.print() and printStackTrace(CONSOLE_ERR), logs from other threads may be inserted. Synchronizing access to CONSOLE_ERR ensures that logs from different threads do not mix. PR #6487 Signed-off-by: valord577 Signed-off-by: Romain Vimont --- .../main/java/com/genymobile/scrcpy/util/Ln.java | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/server/src/main/java/com/genymobile/scrcpy/util/Ln.java b/server/src/main/java/com/genymobile/scrcpy/util/Ln.java index c0700125..811a4f31 100644 --- a/server/src/main/java/com/genymobile/scrcpy/util/Ln.java +++ b/server/src/main/java/com/genymobile/scrcpy/util/Ln.java @@ -74,9 +74,11 @@ public final class Ln { public static void w(String message, Throwable throwable) { if (isEnabled(Level.WARN)) { Log.w(TAG, message, throwable); - CONSOLE_ERR.print(PREFIX + "WARN: " + message + '\n'); - if (throwable != null) { - throwable.printStackTrace(CONSOLE_ERR); + synchronized (CONSOLE_ERR) { + CONSOLE_ERR.print(PREFIX + "WARN: " + message + '\n'); + if (throwable != null) { + throwable.printStackTrace(CONSOLE_ERR); + } } } } @@ -88,9 +90,11 @@ public final class Ln { public static void e(String message, Throwable throwable) { if (isEnabled(Level.ERROR)) { Log.e(TAG, message, throwable); - CONSOLE_ERR.print(PREFIX + "ERROR: " + message + '\n'); - if (throwable != null) { - throwable.printStackTrace(CONSOLE_ERR); + synchronized (CONSOLE_ERR) { + CONSOLE_ERR.print(PREFIX + "ERROR: " + message + '\n'); + if (throwable != null) { + throwable.printStackTrace(CONSOLE_ERR); + } } } } From b08093d1c09d3fff1238abfa57e3efddeadcf41c Mon Sep 17 00:00:00 2001 From: paradoxskin <1312269430@qq.com> Date: Wed, 19 Nov 2025 23:14:46 +0800 Subject: [PATCH 16/31] Fix incorrect icon filename in build documentation The installed icon was listed as `icon.png`, but the actual filename is `scrcpy.png`. PR #6490 Signed-off-by: Romain Vimont --- doc/build.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/build.md b/doc/build.md index 1c228fb8..fd272fde 100644 --- a/doc/build.md +++ b/doc/build.md @@ -271,7 +271,7 @@ This installs several files: - `/usr/local/bin/scrcpy` (main app) - `/usr/local/share/scrcpy/scrcpy-server` (server to push to the device) - `/usr/local/share/man/man1/scrcpy.1` (manpage) - - `/usr/local/share/icons/hicolor/256x256/apps/icon.png` (app icon) + - `/usr/local/share/icons/hicolor/256x256/apps/scrcpy.png` (app icon) - `/usr/local/share/zsh/site-functions/_scrcpy` (zsh completion) - `/usr/local/share/bash-completion/completions/scrcpy` (bash completion) From 9cfa5b197a1b94acc7cece4b91c4173c5fe1c181 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sun, 23 Nov 2025 14:33:50 +0100 Subject: [PATCH 17/31] Create Application instance via instrumentation This fixes an issue on certain Meizu devices. Fixes #6480 --- .../src/main/java/com/genymobile/scrcpy/Workarounds.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/server/src/main/java/com/genymobile/scrcpy/Workarounds.java b/server/src/main/java/com/genymobile/scrcpy/Workarounds.java index b89f19ae..1e012de6 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Workarounds.java +++ b/server/src/main/java/com/genymobile/scrcpy/Workarounds.java @@ -6,9 +6,9 @@ import com.genymobile.scrcpy.util.Ln; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.Application; +import android.app.Instrumentation; import android.content.AttributionSource; import android.content.Context; -import android.content.ContextWrapper; import android.content.pm.ApplicationInfo; import android.media.AudioAttributes; import android.media.AudioManager; @@ -103,10 +103,7 @@ public final class Workarounds { private static void fillAppContext() { try { - Application app = new Application(); - Field baseField = ContextWrapper.class.getDeclaredField("mBase"); - baseField.setAccessible(true); - baseField.set(app, FakeContext.get()); + Application app = Instrumentation.newApplication(Application.class, FakeContext.get()); // activityThread.mInitialApplication = app; Field mInitialApplicationField = ACTIVITY_THREAD_CLASS.getDeclaredField("mInitialApplication"); From 6f9eb31d529a520bbf90febd62096f84a286ddb2 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Tue, 25 Nov 2025 00:06:10 +0100 Subject: [PATCH 18/31] Add missing test for START_APP serialization A test for Java deserialization of the START_APP control message was already present, but the corresponding C-side serialization test was missing. Refs 13ce277e1f1eec6312350c5a3a3ac3be7b9be6e1 --- app/tests/test_control_msg_serialize.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/app/tests/test_control_msg_serialize.c b/app/tests/test_control_msg_serialize.c index 0d19919e..e43c7ce4 100644 --- a/app/tests/test_control_msg_serialize.c +++ b/app/tests/test_control_msg_serialize.c @@ -411,6 +411,26 @@ static void test_serialize_open_hard_keyboard(void) { assert(!memcmp(buf, expected, sizeof(expected))); } +static void test_serialize_start_app(void) { + struct sc_control_msg msg = { + .type = SC_CONTROL_MSG_TYPE_START_APP, + .start_app = { + .name = "firefox", + }, + }; + + uint8_t buf[SC_CONTROL_MSG_MAX_SIZE]; + size_t size = sc_control_msg_serialize(&msg, buf); + assert(size == 9); + + const uint8_t expected[] = { + SC_CONTROL_MSG_TYPE_START_APP, + 7, // length + 'f', 'i', 'r', 'e', 'f', 'o', 'x', // app name + }; + assert(!memcmp(buf, expected, sizeof(expected))); +} + static void test_serialize_reset_video(void) { struct sc_control_msg msg = { .type = SC_CONTROL_MSG_TYPE_RESET_VIDEO, @@ -448,6 +468,7 @@ int main(int argc, char *argv[]) { test_serialize_uhid_input(); test_serialize_uhid_destroy(); test_serialize_open_hard_keyboard(); + test_serialize_start_app(); test_serialize_reset_video(); return 0; } From 7e66062086aaf085f9315a89f60b170bb105dc72 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Fri, 28 Nov 2025 22:31:26 +0100 Subject: [PATCH 19/31] Extract function to execute code on a handler Extract function to synchronously execute code on a handler, waiting for the execution to complete. --- .../scrcpy/opengl/OpenGLRunner.java | 32 +++++--------- .../com/genymobile/scrcpy/util/Threads.java | 43 +++++++++++++++++++ 2 files changed, 53 insertions(+), 22 deletions(-) create mode 100644 server/src/main/java/com/genymobile/scrcpy/util/Threads.java diff --git a/server/src/main/java/com/genymobile/scrcpy/opengl/OpenGLRunner.java b/server/src/main/java/com/genymobile/scrcpy/opengl/OpenGLRunner.java index 86bd1859..06bcd7eb 100644 --- a/server/src/main/java/com/genymobile/scrcpy/opengl/OpenGLRunner.java +++ b/server/src/main/java/com/genymobile/scrcpy/opengl/OpenGLRunner.java @@ -1,6 +1,7 @@ package com.genymobile.scrcpy.opengl; import com.genymobile.scrcpy.device.Size; +import com.genymobile.scrcpy.util.Threads; import android.graphics.SurfaceTexture; import android.opengl.EGL14; @@ -15,6 +16,7 @@ import android.os.Handler; import android.os.HandlerThread; import android.view.Surface; +import java.util.concurrent.Callable; import java.util.concurrent.Semaphore; public final class OpenGLRunner { @@ -80,31 +82,17 @@ public final class OpenGLRunner { public Surface start(Size inputSize, Size outputSize, Surface outputSurface) throws OpenGLException { initOnce(); - // Simulate CompletableFuture, but working for all Android versions - final Semaphore sem = new Semaphore(0); - Throwable[] throwableRef = new Throwable[1]; - // The whole OpenGL execution must be performed on a Handler, so that SurfaceTexture.setOnFrameAvailableListener() works correctly. // See - handler.post(() -> { - try { - run(inputSize, outputSize, outputSurface); - } catch (Throwable throwable) { - throwableRef[0] = throwable; - } finally { - sem.release(); - } - }); - try { - sem.acquire(); - } catch (InterruptedException e) { - // Behave as if this method call was synchronous - Thread.currentThread().interrupt(); - } - - Throwable throwable = throwableRef[0]; - if (throwable != null) { + Threads.executeSynchronouslyOn(handler, new Callable() { + @Override + public Void call() throws Exception { + run(inputSize, outputSize, outputSurface); + return null; + } + }); + } catch (Throwable throwable) { if (throwable instanceof OpenGLException) { throw (OpenGLException) throwable; } diff --git a/server/src/main/java/com/genymobile/scrcpy/util/Threads.java b/server/src/main/java/com/genymobile/scrcpy/util/Threads.java new file mode 100644 index 00000000..c561d1ef --- /dev/null +++ b/server/src/main/java/com/genymobile/scrcpy/util/Threads.java @@ -0,0 +1,43 @@ +package com.genymobile.scrcpy.util; + +import android.os.Handler; + +import java.util.concurrent.Callable; +import java.util.concurrent.Semaphore; + +public final class Threads { + private Threads() { + // not instantiable + } + + public static T executeSynchronouslyOn(Handler handler, Callable callable) throws Throwable { + // Simulate CompletableFuture, but working for all Android versions + final Semaphore sem = new Semaphore(0); + @SuppressWarnings("unchecked") + T[] resultRef = (T[]) new Object[1]; + Throwable[] throwableRef = new Throwable[1]; + + handler.post(() -> { + try { + resultRef[0] = callable.call(); + } catch (Throwable throwable) { + throwableRef[0] = throwable; + } finally { + sem.release(); + } + }); + + try { + sem.acquire(); + } catch (InterruptedException e) { + // Behave as if this method call was synchronous + Thread.currentThread().interrupt(); + } + + if (throwableRef[0] != null) { + throw throwableRef[0]; + } + + return resultRef[0]; + } +} From 5b51396a8c60f2c0d198569b05d589bd34d9ce9c Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Tue, 9 Dec 2025 20:20:22 +0100 Subject: [PATCH 20/31] Fix permission denial error after Android upgrade Assign the FakeContext instance to ActivityManager.mContext to avoid a permission error: Permission Denial: package=android does not belong to uid=2000 Fixes #6523 --- server/src/main/java/com/genymobile/scrcpy/FakeContext.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/server/src/main/java/com/genymobile/scrcpy/FakeContext.java b/server/src/main/java/com/genymobile/scrcpy/FakeContext.java index 7c0f3645..5d41a8f3 100644 --- a/server/src/main/java/com/genymobile/scrcpy/FakeContext.java +++ b/server/src/main/java/com/genymobile/scrcpy/FakeContext.java @@ -109,8 +109,10 @@ public final class FakeContext extends ContextWrapper { } // "semclipboard" is a Samsung-internal service - // See - if (Context.CLIPBOARD_SERVICE.equals(name) || "semclipboard".equals(name)) { + // See: + // - + // - + if (Context.CLIPBOARD_SERVICE.equals(name) || "semclipboard".equals(name) || Context.ACTIVITY_SERVICE.equals(name)) { try { Field field = service.getClass().getDeclaredField("mContext"); field.setAccessible(true); From 1d1a4103cc7ba6a9bdb458e77011067e3ccb46c1 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Wed, 17 Dec 2025 19:55:27 +0100 Subject: [PATCH 21/31] Replace macos-13 runner with macos-15-intel Refs Refs #5526 --- .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 27e3abf2..c4ca1829 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -229,7 +229,7 @@ jobs: path: release/work/build-macos-aarch64/dist-tar/ build-macos-x86_64: - runs-on: macos-13 + runs-on: macos-15-intel steps: - name: Check architecture run: | From 06fd3b47860374350ef2bd8b62dc8dfac5c28720 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Wed, 17 Dec 2025 19:43:29 +0100 Subject: [PATCH 22/31] Bump version to 3.3.4 --- 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 196a547c..07df992f 100644 --- a/app/scrcpy-windows.rc +++ b/app/scrcpy-windows.rc @@ -13,7 +13,7 @@ BEGIN VALUE "LegalCopyright", "Romain Vimont, Genymobile" VALUE "OriginalFilename", "scrcpy.exe" VALUE "ProductName", "scrcpy" - VALUE "ProductVersion", "3.3.3" + VALUE "ProductVersion", "3.3.4" END END BLOCK "VarFileInfo" diff --git a/meson.build b/meson.build index 7abe6a8c..fc82dee1 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('scrcpy', 'c', - version: '3.3.3', + version: '3.3.4', meson_version: '>= 0.49', default_options: [ 'c_std=c11', diff --git a/server/build.gradle b/server/build.gradle index 3dfc7144..abbbb60a 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -7,8 +7,8 @@ android { applicationId = "com.genymobile.scrcpy" minSdkVersion 21 targetSdkVersion 36 - versionCode 30303 - versionName "3.3.3" + versionCode 30304 + versionName "3.3.4" testInstrumentationRunner = "android.support.test.runner.AndroidJUnitRunner" } buildTypes { diff --git a/server/build_without_gradle.sh b/server/build_without_gradle.sh index bb477a32..435ac8f6 100755 --- a/server/build_without_gradle.sh +++ b/server/build_without_gradle.sh @@ -12,7 +12,7 @@ set -e SCRCPY_DEBUG=false -SCRCPY_VERSION_NAME=3.3.3 +SCRCPY_VERSION_NAME=3.3.4 PLATFORM=${ANDROID_PLATFORM:-36} BUILD_TOOLS=${ANDROID_BUILD_TOOLS:-36.0.0} From fb6381f5b9bb96f3fa823d899f4c32de2ec84ab3 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Wed, 17 Dec 2025 20:18:49 +0100 Subject: [PATCH 23/31] Upgrade links to 3.3.4 --- README.md | 2 +- doc/build.md | 6 +++--- doc/linux.md | 6 +++--- doc/macos.md | 13 ++++++------- doc/windows.md | 12 ++++++------ install_release.sh | 4 ++-- 6 files changed, 21 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 195706f6..07501a2c 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.3.3) +# scrcpy (v3.3.4) scrcpy diff --git a/doc/build.md b/doc/build.md index 1c228fb8..45aaf6bf 100644 --- a/doc/build.md +++ b/doc/build.md @@ -233,10 +233,10 @@ install` must be run as root)._ #### Option 2: Use prebuilt server - - [`scrcpy-server-v3.3.3`][direct-scrcpy-server] - SHA-256: `7e70323ba7f259649dd4acce97ac4fefbae8102b2c6d91e2e7be613fd5354be0` + - [`scrcpy-server-v3.3.4`][direct-scrcpy-server] + SHA-256: `8588238c9a5a00aa542906b6ec7e6d5541d9ffb9b5d0f6e1bc0e365e2303079e` -[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v3.3.3/scrcpy-server-v3.3.3 +[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v3.3.4/scrcpy-server-v3.3.4 Download the prebuilt server somewhere, and specify its path during the Meson configuration: diff --git a/doc/linux.md b/doc/linux.md index 39e92961..fd9eca8c 100644 --- a/doc/linux.md +++ b/doc/linux.md @@ -6,11 +6,11 @@ Download a static build of the [latest release]: - - [`scrcpy-linux-x86_64-v3.3.3.tar.gz`][direct-linux-x86_64] (x86_64) - SHA-256: `9b30e813e8191329ba8025dc80cb0f198fb0a318960a3b5c15395cf675c9c638` + - [`scrcpy-linux-x86_64-v3.3.4.tar.gz`][direct-linux-x86_64] (x86_64) + SHA-256: `0305d98c06178c67e12427bbf340c436d0d58c9e2a39bf9ffbbf8f54d7ef95a5` [latest release]: https://github.com/Genymobile/scrcpy/releases/latest -[direct-linux-x86_64]: https://github.com/Genymobile/scrcpy/releases/download/v3.3.3/scrcpy-linux-x86_64-v3.3.3.tar.gz +[direct-linux-x86_64]: https://github.com/Genymobile/scrcpy/releases/download/v3.3.4/scrcpy-linux-x86_64-v3.3.4.tar.gz and extract it. diff --git a/doc/macos.md b/doc/macos.md index 7ba08c85..5435e613 100644 --- a/doc/macos.md +++ b/doc/macos.md @@ -6,15 +6,14 @@ Download a static build of the [latest release]: - - [`scrcpy-macos-aarch64-v3.3.3.tar.gz`][direct-macos-aarch64] (aarch64) - SHA-256: `b93299468f19ae89ac70f7c1453914c41f1f2bcd31f6ab530038da885c19581f` - - - [`scrcpy-macos-x86_64-v3.3.3.tar.gz`][direct-macos-x86_64] (x86_64) - SHA-256: `c767fc1d41e4ae26e40558656570962f474739924fd22ee023d8754889ee4366` + - [`scrcpy-macos-aarch64-v3.3.4.tar.gz`][direct-macos-aarch64] (aarch64) + SHA-256: `8fef43520405dd523c74e1530ac68febcc5a405ea89712c874936675da8513dd` + - [`scrcpy-macos-x86_64-v3.3.4.tar.gz`][direct-macos-x86_64] (x86_64) + SHA-256: `cf9b3453a33279b6009dfb256b1a84c374bd4c30a71edd74bacab28d72a5d929` [latest release]: https://github.com/Genymobile/scrcpy/releases/latest -[direct-macos-aarch64]: https://github.com/Genymobile/scrcpy/releases/download/v3.3.3/scrcpy-macos-aarch64-v3.3.3.tar.gz -[direct-macos-x86_64]: https://github.com/Genymobile/scrcpy/releases/download/v3.3.3/scrcpy-macos-x86_64-v3.3.3.tar.gz +[direct-macos-aarch64]: https://github.com/Genymobile/scrcpy/releases/download/v3.3.4/scrcpy-macos-aarch64-v3.3.4.tar.gz +[direct-macos-x86_64]: https://github.com/Genymobile/scrcpy/releases/download/v3.3.4/scrcpy-macos-x86_64-v3.3.4.tar.gz and extract it. diff --git a/doc/windows.md b/doc/windows.md index b6dc220f..cdc30bdc 100644 --- a/doc/windows.md +++ b/doc/windows.md @@ -6,14 +6,14 @@ Download the [latest release]: - - [`scrcpy-win64-v3.3.3.zip`][direct-win64] (64-bit) - SHA-256: `4b458d33d0436688c69875cd267cae6fa8be08aa3c17772edf3a940a3dc4b17e` - - [`scrcpy-win32-v3.3.3.zip`][direct-win32] (32-bit) - SHA-256: `e3d43e21c0bd6e070381c390c1e4cccd48a1e71ae73a8c217e6e6b8506598c79` + - [`scrcpy-win64-v3.3.4.zip`][direct-win64] (64-bit) + SHA-256: `d8a155b7c180b7ca4cdadd40712b8750b63f3aab48cb5b8a2a39ac2d0d4c5d38` + - [`scrcpy-win32-v3.3.4.zip`][direct-win32] (32-bit) + SHA-256: `393f7d5379dabd8aacc41184755c3d0df975cd2861353cb7a8d50e0835e2eb72` [latest release]: https://github.com/Genymobile/scrcpy/releases/latest -[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v3.3.3/scrcpy-win64-v3.3.3.zip -[direct-win32]: https://github.com/Genymobile/scrcpy/releases/download/v3.3.3/scrcpy-win32-v3.3.3.zip +[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v3.3.4/scrcpy-win64-v3.3.4.zip +[direct-win32]: https://github.com/Genymobile/scrcpy/releases/download/v3.3.4/scrcpy-win32-v3.3.4.zip and extract it. diff --git a/install_release.sh b/install_release.sh index 3419a9bc..5930e54e 100755 --- a/install_release.sh +++ b/install_release.sh @@ -2,8 +2,8 @@ set -e BUILDDIR=build-auto -PREBUILT_SERVER_URL=https://github.com/Genymobile/scrcpy/releases/download/v3.3.3/scrcpy-server-v3.3.3 -PREBUILT_SERVER_SHA256=7e70323ba7f259649dd4acce97ac4fefbae8102b2c6d91e2e7be613fd5354be0 +PREBUILT_SERVER_URL=https://github.com/Genymobile/scrcpy/releases/download/v3.3.4/scrcpy-server-v3.3.4 +PREBUILT_SERVER_SHA256=8588238c9a5a00aa542906b6ec7e6d5541d9ffb9b5d0f6e1bc0e365e2303079e echo "[scrcpy] Downloading prebuilt server..." wget "$PREBUILT_SERVER_URL" -O scrcpy-server From c8c1316db9adefca6aae054be936049c2f8e02f1 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Thu, 1 Jan 2026 16:43:12 +0100 Subject: [PATCH 24/31] Happy new year 2026! --- LICENSE | 2 +- README.md | 2 +- app/scrcpy.1 | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/LICENSE b/LICENSE index 1196b3da..3325e9f3 100644 --- a/LICENSE +++ b/LICENSE @@ -188,7 +188,7 @@ identification within third-party archives. Copyright (C) 2018 Genymobile - Copyright (C) 2018-2025 Romain Vimont + Copyright (C) 2018-2026 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 07501a2c..2ee76736 100644 --- a/README.md +++ b/README.md @@ -210,7 +210,7 @@ work][donate]: ## License Copyright (C) 2018 Genymobile - Copyright (C) 2018-2025 Romain Vimont + Copyright (C) 2018-2026 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 d72fda13..d6940449 100644 --- a/app/scrcpy.1 +++ b/app/scrcpy.1 @@ -852,7 +852,7 @@ Report bugs to . .SH COPYRIGHT Copyright \(co 2018 Genymobile -Copyright \(co 2018\-2025 Romain Vimont +Copyright \(co 2018\-2026 Romain Vimont Licensed under the Apache License, Version 2.0. From 3fcc177da5b6b4514d0e8e8d90d7d58d6731eac9 Mon Sep 17 00:00:00 2001 From: Almog Kurtser Date: Thu, 22 Jan 2026 17:48:42 +0200 Subject: [PATCH 25/31] Use OpenJDK instead of Adoptium on Mac PR #6621 Signed-off-by: Romain Vimont --- doc/build.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/build.md b/doc/build.md index 07efa6b7..bf246ce2 100644 --- a/doc/build.md +++ b/doc/build.md @@ -172,8 +172,7 @@ Additionally, if you want to build the server, install Java 17 from Caskroom, an make it available from the `PATH`: ```bash -brew tap homebrew/cask-versions -brew install adoptopenjdk/openjdk/adoptopenjdk17 +brew install openjdk@17 export JAVA_HOME="$(/usr/libexec/java_home --version 1.17)" export PATH="$JAVA_HOME/bin:$PATH" ``` From 247a37d57bcf389b06d6e17e1af60ded72704a47 Mon Sep 17 00:00:00 2001 From: Benjamin Loison Date: Mon, 6 Apr 2026 01:55:11 +0200 Subject: [PATCH 26/31] Fix typo in doc/develop.md PR #6752 Signed-off-by: Romain Vimont --- doc/develop.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/develop.md b/doc/develop.md index 21949ea6..61102bf4 100644 --- a/doc/develop.md +++ b/doc/develop.md @@ -409,8 +409,8 @@ with any client which uses the same protocol. For simplicity, some [server-specific options] have been added to produce raw streams easily: - - `send_device_meta=false`: disable the device metata (in practice, the device - name) sent on the _first_ socket + - `send_device_meta=false`: disable the device metadata (in practice, the + device name) sent on the _first_ socket - `send_frame_meta=false`: disable the 12-byte header for each packet - `send_dummy_byte`: disable the dummy byte sent on forward connections - `send_codec_meta`: disable the codec information (and initial device size for From 158cae323787661ac7563e5bf5ce40be3e096445 Mon Sep 17 00:00:00 2001 From: Managor <42655600+Managor@users.noreply.github.com> Date: Sun, 15 Mar 2026 23:26:31 +0200 Subject: [PATCH 27/31] Add instructions for creating v4l2loopback device PR #6725 Signed-off-by: Romain Vimont --- doc/v4l2.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/v4l2.md b/doc/v4l2.md index 54272b2b..ac1e3540 100644 --- a/doc/v4l2.md +++ b/doc/v4l2.md @@ -38,6 +38,13 @@ v4l2-ctl --list-devices ls /dev/video* ``` +If a loopback device was not created automatically, you can make a new one: + +``` +# requires v4l2loopback-utils package +sudo v4l2loopback-ctl add +``` + To start `scrcpy` using a v4l2 sink: ```bash From 0abee2d01e0b89e7fd7aec26b4b9b0f512463f2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C8=98andor=20Sergiu?= Date: Fri, 16 Jan 2026 01:26:07 +0200 Subject: [PATCH 28/31] Reference OTG comment in FAQ.md Add a reference to a comment that can help resolve the "OTG Entity not found" issue on Windows. PR #6609 Signed-off-by: Romain Vimont --- FAQ.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/FAQ.md b/FAQ.md index 24722c74..f9da5d1f 100644 --- a/FAQ.md +++ b/FAQ.md @@ -141,12 +141,13 @@ On Windows, if `scrcpy --otg` (or `--keyboard=aoa`/`--mouse=aoa`) results in: (or if only unrelated USB devices are detected), there might be drivers issues. -Please read [#3654], in particular [this comment][#3654-comment1] and [the next -one][#3654-comment2]. +Please read [#3654], in particular [this comment][#3654-comment1], [the next +one][#3654-comment2] and [this one][#3654-comment3]. [#3654]: https://github.com/Genymobile/scrcpy/issues/3654 [#3654-comment1]: https://github.com/Genymobile/scrcpy/issues/3654#issuecomment-1369278232 [#3654-comment2]: https://github.com/Genymobile/scrcpy/issues/3654#issuecomment-1369295011 +[#3654-comment3]: https://github.com/Genymobile/scrcpy/issues/3654#issuecomment-2613219725 ## Control issues From db013aa7a74d54fa03f5bfe879f81740492d30ba Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Fri, 17 Apr 2026 23:06:34 +0200 Subject: [PATCH 29/31] Fix macOS spelling in documentation --- doc/build.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/build.md b/doc/build.md index bf246ce2..31be38fd 100644 --- a/doc/build.md +++ b/doc/build.md @@ -154,7 +154,7 @@ install it manually and make it available from the `PATH`: export PATH="$JAVA_HOME/bin:$PATH" ``` -### Mac OS +### macOS Install the packages with [Homebrew]: From cbc59170503f35b54c2e5186a78f0cc19bd50bf7 Mon Sep 17 00:00:00 2001 From: Satyam Kashyap Date: Wed, 23 Jul 2025 12:30:20 +0530 Subject: [PATCH 30/31] Clarify MSYS2 MinGW terminal requirement PR #6254 Signed-off-by: Romain Vimont --- doc/build.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/build.md b/doc/build.md index 31be38fd..e1873f93 100644 --- a/doc/build.md +++ b/doc/build.md @@ -154,6 +154,10 @@ install it manually and make it available from the `PATH`: export PATH="$JAVA_HOME/bin:$PATH" ``` +When following the rest of the build instructions below, make sure you use the +MinGW terminal within MSYS2. + + ### macOS Install the packages with [Homebrew]: From 4671927c34ce390e3f3c0044187b31c910f1c11e Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sun, 19 Apr 2026 11:17:53 +0200 Subject: [PATCH 31/31] Add missing language identifier in v4l2.md Refs #6725 comment --- doc/v4l2.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/v4l2.md b/doc/v4l2.md index ac1e3540..b50eb9b8 100644 --- a/doc/v4l2.md +++ b/doc/v4l2.md @@ -24,7 +24,7 @@ to create several devices or devices with specific IDs). If you encounter problems detecting your device with Chrome/WebRTC, you can try `exclusive_caps` mode: -``` +```bash sudo modprobe v4l2loopback exclusive_caps=1 ``` @@ -40,7 +40,7 @@ ls /dev/video* If a loopback device was not created automatically, you can make a new one: -``` +```bash # requires v4l2loopback-utils package sudo v4l2loopback-ctl add ```