Compare commits

...

11 Commits

Author SHA1 Message Date
Almog Kurtser
3fcc177da5 Use OpenJDK instead of Adoptium on Mac
PR #6621 <https://github.com/Genymobile/scrcpy/pull/6621>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2026-02-12 17:59:11 +01:00
Romain Vimont
c8c1316db9 Happy new year 2026! 2026-01-01 16:43:12 +01:00
Romain Vimont
db2f72a58a Merge branch 'master' into dev 2025-12-17 20:27:11 +01:00
Romain Vimont
fb6381f5b9 Upgrade links to 3.3.4 2025-12-17 20:21:05 +01:00
Romain Vimont
06fd3b4786 Bump version to 3.3.4 2025-12-17 19:57:50 +01:00
Romain Vimont
1d1a4103cc Replace macos-13 runner with macos-15-intel
Refs <https://github.com/actions/runner-images/issues/13046>
Refs #5526 <https://github.com/Genymobile/scrcpy/pull/5526>
2025-12-17 19:57:50 +01:00
Romain Vimont
5b51396a8c 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 <https://github.com/Genymobile/scrcpy/issues/6523>
2025-12-09 20:24:42 +01:00
Romain Vimont
7e66062086 Extract function to execute code on a handler
Extract function to synchronously execute code on a handler, waiting for
the execution to complete.
2025-11-28 22:42:17 +01:00
Romain Vimont
6f9eb31d52 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 13ce277e1f
2025-11-25 00:13:13 +01:00
Romain Vimont
9cfa5b197a Create Application instance via instrumentation
This fixes an issue on certain Meizu devices.

Fixes #6480 <https://github.com/Genymobile/scrcpy/issues/6480>
2025-11-23 15:56:31 +01:00
paradoxskin
b08093d1c0 Fix incorrect icon filename in build documentation
The installed icon was listed as `icon.png`, but the actual
filename is `scrcpy.png`.

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

Signed-off-by: Romain Vimont <rom@rom1v.com>
2025-11-19 17:13:40 +01:00
18 changed files with 112 additions and 63 deletions

View File

@@ -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: |

View File

@@ -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.

View File

@@ -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)
<img src="app/data/icon.svg" width="128" height="128" alt="scrcpy" align="right" />
@@ -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.

View File

@@ -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"

View File

@@ -852,7 +852,7 @@ Report bugs to <https://github.com/Genymobile/scrcpy/issues>.
.SH COPYRIGHT
Copyright \(co 2018 Genymobile <https://www.genymobile.com>
Copyright \(co 2018\-2025 Romain Vimont <rom@rom1v.com>
Copyright \(co 2018\-2026 Romain Vimont <rom@rom1v.com>
Licensed under the Apache License, Version 2.0.

View File

@@ -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;
}

View File

@@ -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"
```
@@ -233,10 +232,10 @@ install` must be run as root)._
#### Option 2: Use prebuilt server
- [`scrcpy-server-v3.3.3`][direct-scrcpy-server]
<sub>SHA-256: `7e70323ba7f259649dd4acce97ac4fefbae8102b2c6d91e2e7be613fd5354be0`</sub>
- [`scrcpy-server-v3.3.4`][direct-scrcpy-server]
<sub>SHA-256: `8588238c9a5a00aa542906b6ec7e6d5541d9ffb9b5d0f6e1bc0e365e2303079e`</sub>
[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:
@@ -271,7 +270,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)

View File

@@ -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)
<sub>SHA-256: `9b30e813e8191329ba8025dc80cb0f198fb0a318960a3b5c15395cf675c9c638`</sub>
- [`scrcpy-linux-x86_64-v3.3.4.tar.gz`][direct-linux-x86_64] (x86_64)
<sub>SHA-256: `0305d98c06178c67e12427bbf340c436d0d58c9e2a39bf9ffbbf8f54d7ef95a5`</sub>
[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.

View File

@@ -6,15 +6,14 @@
Download a static build of the [latest release]:
- [`scrcpy-macos-aarch64-v3.3.3.tar.gz`][direct-macos-aarch64] (aarch64)
<sub>SHA-256: `b93299468f19ae89ac70f7c1453914c41f1f2bcd31f6ab530038da885c19581f`</sub>
- [`scrcpy-macos-x86_64-v3.3.3.tar.gz`][direct-macos-x86_64] (x86_64)
<sub>SHA-256: `c767fc1d41e4ae26e40558656570962f474739924fd22ee023d8754889ee4366`</sub>
- [`scrcpy-macos-aarch64-v3.3.4.tar.gz`][direct-macos-aarch64] (aarch64)
<sub>SHA-256: `8fef43520405dd523c74e1530ac68febcc5a405ea89712c874936675da8513dd`</sub>
- [`scrcpy-macos-x86_64-v3.3.4.tar.gz`][direct-macos-x86_64] (x86_64)
<sub>SHA-256: `cf9b3453a33279b6009dfb256b1a84c374bd4c30a71edd74bacab28d72a5d929`</sub>
[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.

View File

@@ -6,14 +6,14 @@
Download the [latest release]:
- [`scrcpy-win64-v3.3.3.zip`][direct-win64] (64-bit)
<sub>SHA-256: `4b458d33d0436688c69875cd267cae6fa8be08aa3c17772edf3a940a3dc4b17e`</sub>
- [`scrcpy-win32-v3.3.3.zip`][direct-win32] (32-bit)
<sub>SHA-256: `e3d43e21c0bd6e070381c390c1e4cccd48a1e71ae73a8c217e6e6b8506598c79`</sub>
- [`scrcpy-win64-v3.3.4.zip`][direct-win64] (64-bit)
<sub>SHA-256: `d8a155b7c180b7ca4cdadd40712b8750b63f3aab48cb5b8a2a39ac2d0d4c5d38`</sub>
- [`scrcpy-win32-v3.3.4.zip`][direct-win32] (32-bit)
<sub>SHA-256: `393f7d5379dabd8aacc41184755c3d0df975cd2861353cb7a8d50e0835e2eb72`</sub>
[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.

View File

@@ -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

View File

@@ -1,5 +1,5 @@
project('scrcpy', 'c',
version: '3.3.3',
version: '3.3.4',
meson_version: '>= 0.49',
default_options: [
'c_std=c11',

View File

@@ -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 {

View File

@@ -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}

View File

@@ -109,8 +109,10 @@ public final class FakeContext extends ContextWrapper {
}
// "semclipboard" is a Samsung-internal service
// See <https://github.com/Genymobile/scrcpy/issues/6224>
if (Context.CLIPBOARD_SERVICE.equals(name) || "semclipboard".equals(name)) {
// See:
// - <https://github.com/Genymobile/scrcpy/issues/6224>
// - <https://github.com/Genymobile/scrcpy/issues/6523>
if (Context.CLIPBOARD_SERVICE.equals(name) || "semclipboard".equals(name) || Context.ACTIVITY_SERVICE.equals(name)) {
try {
Field field = service.getClass().getDeclaredField("mContext");
field.setAccessible(true);

View File

@@ -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");

View File

@@ -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 <https://github.com/Genymobile/scrcpy/issues/5444>
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<Void>() {
@Override
public Void call() throws Exception {
run(inputSize, outputSize, outputSurface);
return null;
}
});
} catch (Throwable throwable) {
if (throwable instanceof OpenGLException) {
throw (OpenGLException) throwable;
}

View File

@@ -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> T executeSynchronouslyOn(Handler handler, Callable<T> 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];
}
}