Compare commits

..

5 Commits

Author SHA1 Message Date
Simon Chan
8314cecd35 Associate UHID devices to virtual displays
This allows the mouse pointer to appear on the correct display (only for
devices running Android 15+).

Fixes #5547 <https://github.com/Genymobile/scrcpy/issues/5547>
PR #6009 <https://github.com/Genymobile/scrcpy/pull/6009>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2025-05-22 20:02:27 +02:00
Romain Vimont
2f97112c27 Synchronize access to DisplayManager
The DisplayManager and its method getDisplayInfo() may be used from both
the Controller thread and the video (main) thread.

PR #6009 <https://github.com/Genymobile/scrcpy/pull/6009>
2025-05-22 20:02:27 +02:00
Romain Vimont
1b946edf46 Cache getDisplayInfo method
Do not use reflection to retrieve the method for every call.

PR #6009 <https://github.com/Genymobile/scrcpy/pull/6009>
2025-05-22 20:02:27 +02:00
Simon Chan
384dcbbe36 Simplify InputManager wrapper
Use the public InputManager API.

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

Signed-off-by: Romain Vimont <rom@rom1v.com>
2025-05-22 20:02:27 +02:00
Romain Vimont
3b36f29be9 Simplify ClipboardManager wrapper
Use the public ClipboardManager API, with the FakeContext as context.

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

Suggested by: Simon Chan <1330321+yume-chan@users.noreply.github.com>
2025-05-22 20:02:27 +02:00
34 changed files with 128 additions and 160 deletions

5
FAQ.md
View File

@@ -166,13 +166,14 @@ Rebooting the device is necessary once this option is set.
### Special characters do not work
The default text injection method is limited to ASCII characters. A trick allows
to also inject some [accented characters][accented-characters],
The default text injection method is [limited to ASCII characters][text-input].
A trick allows to also inject some [accented characters][accented-characters],
but that's all. See [#37].
To avoid the problem, [change the keyboard mode to simulate a physical
keyboard][hid].
[text-input]: https://github.com/Genymobile/scrcpy/issues?q=is%3Aopen+is%3Aissue+label%3Aunicode
[accented-characters]: https://blog.rom1v.com/2018/03/introducing-scrcpy/#handle-accented-characters
[#37]: https://github.com/Genymobile/scrcpy/issues/37
[hid]: doc/keyboard.md#physical-keyboard-simulation

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)
# scrcpy (v3.2)
<img src="app/data/icon.svg" width="128" height="128" alt="scrcpy" align="right" />
@@ -207,7 +207,7 @@ work][donate]:
[donate]: https://blog.rom1v.com/about/#support-my-open-source-work
## License
## Licence
Copyright (C) 2018 Genymobile
Copyright (C) 2018-2025 Romain Vimont

View File

@@ -4,10 +4,10 @@ DEPS_DIR=$(dirname ${BASH_SOURCE[0]})
cd "$DEPS_DIR"
. common
VERSION=36.0.0
VERSION=35.0.2
FILENAME=platform-tools_r$VERSION-linux.zip
PROJECT_DIR=platform-tools-$VERSION-linux
SHA256SUM=0ead642c943ffe79701fccca8f5f1c69c4ce4f43df2eefee553f6ccb27cbfbe8
SHA256SUM=acfdcccb123a8718c46c46c059b2f621140194e5ec1ac9d81715be3d6ab6cd0a
cd "$SOURCES_DIR"

View File

@@ -4,10 +4,10 @@ DEPS_DIR=$(dirname ${BASH_SOURCE[0]})
cd "$DEPS_DIR"
. common
VERSION=36.0.0
VERSION=35.0.2
FILENAME=platform-tools_r$VERSION-darwin.zip
PROJECT_DIR=platform-tools-$VERSION-darwin
SHA256SUM=b241878e6ec20650b041bf715ea05f7d5dc73bd24529464bd9cf68946e3132bd
SHA256SUM=1820078db90bf21628d257ff052528af1c61bb48f754b3555648f5652fa35d78
cd "$SOURCES_DIR"

View File

@@ -4,10 +4,10 @@ DEPS_DIR=$(dirname ${BASH_SOURCE[0]})
cd "$DEPS_DIR"
. common
VERSION=36.0.0
VERSION=35.0.2
FILENAME=platform-tools_r$VERSION-win.zip
PROJECT_DIR=platform-tools-$VERSION-windows
SHA256SUM=24bd8bebbbb58b9870db202b5c6775c4a49992632021c60750d9d8ec8179d5f0
SHA256SUM=2975a3eac0b19182748d64195375ad056986561d994fffbdc64332a516300bb9
cd "$SOURCES_DIR"

View File

@@ -5,10 +5,10 @@ cd "$DEPS_DIR"
. common
process_args "$@"
VERSION=1.0.29
VERSION=1.0.28
FILENAME=libusb-$VERSION.tar.gz
PROJECT_DIR=libusb-$VERSION
SHA256SUM=7c2dd39c0b2589236e48c93247c986ae272e27570942b4163cb00a060fcf1b74
SHA256SUM=378b3709a405065f8f9fb9f35e82d666defde4d342c2a1b181a9ac134d23c6fe
cd "$SOURCES_DIR"

View File

@@ -0,0 +1,33 @@
From 6be87ceb33a9aad3bf5204bb13b3a5e8b498fd26 Mon Sep 17 00:00:00 2001
From: Neal Gompa <neal@gompa.dev>
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

View File

@@ -5,10 +5,10 @@ cd "$DEPS_DIR"
. common
process_args "$@"
VERSION=2.32.8
VERSION=2.32.2
FILENAME=SDL-$VERSION.tar.gz
PROJECT_DIR=SDL-release-$VERSION
SHA256SUM=dd35e05644ae527848d02433bec24dd0ea65db59faecf1a0e5d1880c533dac2c
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"

View File

@@ -13,7 +13,7 @@ BEGIN
VALUE "LegalCopyright", "Romain Vimont, Genymobile"
VALUE "OriginalFilename", "scrcpy.exe"
VALUE "ProductName", "scrcpy"
VALUE "ProductVersion", "3.3"
VALUE "ProductVersion", "3.2"
END
END
BLOCK "VarFileInfo"

View File

@@ -110,7 +110,7 @@ show_adb_installation_msg(void) {
} pkg_managers[] = {
{"apt", "apt install adb"},
{"apt-get", "apt-get install adb"},
{"brew", "brew install --cask android-platform-tools"},
{"brew", "brew cask install android-platform-tools"},
{"dnf", "dnf install android-tools"},
{"emerge", "emerge dev-util/android-tools"},
{"pacman", "pacman -S android-tools"},

View File

@@ -75,14 +75,6 @@
# define SCRCPY_SDL_HAS_THREAD_PRIORITY_TIME_CRITICAL
#endif
#if SDL_VERSION_ATLEAST(2, 0, 18)
# define SCRCPY_SDL_HAS_HINT_APP_NAME
#endif
#if SDL_VERSION_ATLEAST(2, 0, 14)
# define SCRCPY_SDL_HAS_HINT_AUDIO_DEVICE_APP_NAME
#endif
#ifndef HAVE_STRDUP
char *strdup(const char *s);
#endif

View File

@@ -127,14 +127,10 @@ sc_control_msg_serialize(const struct sc_control_msg *msg, uint8_t *buf) {
return 32;
case SC_CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT:
write_position(&buf[1], &msg->inject_scroll_event.position);
// Accept values in the range [-16, 16].
// Normalize to [-1, 1] in order to use sc_float_to_i16fp().
float hscroll_norm = msg->inject_scroll_event.hscroll / 16;
hscroll_norm = CLAMP(hscroll_norm, -1, 1);
float vscroll_norm = msg->inject_scroll_event.vscroll / 16;
vscroll_norm = CLAMP(vscroll_norm, -1, 1);
int16_t hscroll = sc_float_to_i16fp(hscroll_norm);
int16_t vscroll = sc_float_to_i16fp(vscroll_norm);
int16_t hscroll =
sc_float_to_i16fp(msg->inject_scroll_event.hscroll);
int16_t vscroll =
sc_float_to_i16fp(msg->inject_scroll_event.vscroll);
sc_write16be(&buf[13], (uint16_t) hscroll);
sc_write16be(&buf[15], (uint16_t) vscroll);
sc_write32be(&buf[17], msg->inject_scroll_event.buttons);

View File

@@ -3,8 +3,8 @@
#include <stdint.h>
// 1 byte for buttons + padding, 1 byte for X position, 1 byte for Y position,
// 1 byte for wheel motion, 1 byte for hozizontal scrolling
#define SC_HID_MOUSE_INPUT_SIZE 5
// 1 byte for wheel motion
#define SC_HID_MOUSE_INPUT_SIZE 4
/**
* Mouse descriptor from the specification:
@@ -75,21 +75,6 @@ static const uint8_t SC_HID_MOUSE_REPORT_DESC[] = {
// Input (Data, Variable, Relative): 3 position bytes (X, Y, Wheel)
0x81, 0x06,
// Usage Page (Consumer Page)
0x05, 0x0C,
// Usage(AC Pan)
0x0A, 0x38, 0x02,
// Logical Minimum (-127)
0x15, 0x81,
// Logical Maximum (127)
0x25, 0x7F,
// Report Size (8)
0x75, 0x08,
// Report Count (1)
0x95, 0x01,
// Input (Data, Variable, Relative): 1 byte (AC Pan)
0x81, 0x06,
// End Collection
0xC0,
@@ -175,8 +160,7 @@ sc_hid_mouse_generate_input_from_motion(struct sc_hid_input *hid_input,
data[0] = sc_hid_buttons_from_buttons_state(event->buttons_state);
data[1] = CLAMP(event->xrel, -127, 127);
data[2] = CLAMP(event->yrel, -127, 127);
data[3] = 0; // no vertical scrolling
data[4] = 0; // no horizontal scrolling
data[3] = 0; // wheel coordinates only used for scrolling
}
void
@@ -188,27 +172,22 @@ sc_hid_mouse_generate_input_from_click(struct sc_hid_input *hid_input,
data[0] = sc_hid_buttons_from_buttons_state(event->buttons_state);
data[1] = 0; // no x motion
data[2] = 0; // no y motion
data[3] = 0; // no vertical scrolling
data[4] = 0; // no horizontal scrolling
data[3] = 0; // wheel coordinates only used for scrolling
}
bool
void
sc_hid_mouse_generate_input_from_scroll(struct sc_hid_input *hid_input,
const struct sc_mouse_scroll_event *event) {
if (!event->vscroll_int && !event->hscroll_int) {
// Need a full integral value for HID
return false;
}
sc_hid_mouse_input_init(hid_input);
uint8_t *data = hid_input->data;
data[0] = 0; // buttons state irrelevant (and unknown)
data[1] = 0; // no x motion
data[2] = 0; // no y motion
data[3] = CLAMP(event->vscroll_int, -127, 127);
data[4] = CLAMP(event->hscroll_int, -127, 127);
return true;
// In practice, vscroll is always -1, 0 or 1, but in theory other values
// are possible
data[3] = CLAMP(event->vscroll, -127, 127);
// Horizontal scrolling ignored
}
void sc_hid_mouse_generate_open(struct sc_hid_open *hid_open) {

View File

@@ -22,7 +22,7 @@ void
sc_hid_mouse_generate_input_from_click(struct sc_hid_input *hid_input,
const struct sc_mouse_click_event *event);
bool
void
sc_hid_mouse_generate_input_from_scroll(struct sc_hid_input *hid_input,
const struct sc_mouse_scroll_event *event);

View File

@@ -393,8 +393,6 @@ struct sc_mouse_scroll_event {
struct sc_position position;
float hscroll;
float vscroll;
int32_t hscroll_int;
int32_t vscroll_int;
uint8_t buttons_state; // bitwise-OR of sc_mouse_button values
};

View File

@@ -897,14 +897,12 @@ sc_input_manager_process_mouse_wheel(struct sc_input_manager *im,
struct sc_mouse_scroll_event evt = {
.position = sc_input_manager_get_position(im, mouse_x, mouse_y),
#if SDL_VERSION_ATLEAST(2, 0, 18)
.hscroll = event->preciseX,
.vscroll = event->preciseY,
.hscroll = CLAMP(event->preciseX, -1.0f, 1.0f),
.vscroll = CLAMP(event->preciseY, -1.0f, 1.0f),
#else
.hscroll = event->x,
.vscroll = event->y,
.hscroll = CLAMP(event->x, -1, 1),
.vscroll = CLAMP(event->y, -1, 1),
#endif
.hscroll_int = event->x,
.vscroll_int = event->y,
.buttons_state = im->mouse_buttons_state,
};

View File

@@ -107,17 +107,6 @@ sdl_set_hints(const char *render_driver) {
LOGW("Could not set render driver");
}
// App name used in various contexts (such as PulseAudio)
#if defined(SCRCPY_SDL_HAS_HINT_APP_NAME)
if (!SDL_SetHint(SDL_HINT_APP_NAME, "scrcpy")) {
LOGW("Could not set app name");
}
#elif defined(SCRCPY_SDL_HAS_HINT_AUDIO_DEVICE_APP_NAME)
if (!SDL_SetHint(SDL_HINT_AUDIO_DEVICE_APP_NAME, "scrcpy")) {
LOGW("Could not set audio device app name");
}
#endif
// Linear filtering
if (!SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1")) {
LOGW("Could not enable linear filtering");

View File

@@ -55,9 +55,7 @@ sc_mouse_processor_process_mouse_scroll(struct sc_mouse_processor *mp,
struct sc_mouse_uhid *mouse = DOWNCAST(mp);
struct sc_hid_input hid_input;
if (!sc_hid_mouse_generate_input_from_scroll(&hid_input, event)) {
return;
}
sc_hid_mouse_generate_input_from_scroll(&hid_input, event);
sc_mouse_uhid_send_input(mouse, &hid_input, "mouse scroll");
}

View File

@@ -42,9 +42,7 @@ sc_mouse_processor_process_mouse_scroll(struct sc_mouse_processor *mp,
struct sc_mouse_aoa *mouse = DOWNCAST(mp);
struct sc_hid_input hid_input;
if (!sc_hid_mouse_generate_input_from_scroll(&hid_input, event)) {
return;
}
sc_hid_mouse_generate_input_from_scroll(&hid_input, event);
if (!sc_aoa_push_input(mouse->aoa, &hid_input)) {
LOGW("Could not push AOA HID input (mouse scroll)");

View File

@@ -164,15 +164,8 @@ sc_screen_otg_process_mouse_wheel(struct sc_screen_otg *screen,
struct sc_mouse_scroll_event evt = {
// .position not used for HID events
#if SDL_VERSION_ATLEAST(2, 0, 18)
.hscroll = event->preciseX,
.vscroll = event->preciseY,
#else
.hscroll = event->x,
.vscroll = event->y,
#endif
.hscroll_int = event->x,
.vscroll_int = event->y,
.buttons_state = sc_mouse_buttons_state_from_sdl(sdl_buttons_state),
};

View File

@@ -233,10 +233,10 @@ install` must be run as root)._
#### Option 2: Use prebuilt server
- [`scrcpy-server-v3.3`][direct-scrcpy-server]
<sub>SHA-256: `351cb2edc7e4c2c75f09a7933fdabcf137be52e2602df154f24ec02db46e9e51`</sub>
- [`scrcpy-server-v3.2`][direct-scrcpy-server]
<sub>SHA-256: `b920e0ea01936bf2482f4ba2fa985c22c13c621999e3d33b45baa5acfc1ea3d0`</sub>
[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v3.3/scrcpy-server-v3.3
[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:

View File

@@ -6,11 +6,11 @@
Download a static build of the [latest release]:
- [`scrcpy-linux-x86_64-v3.3.tar.gz`][direct-linux-x86_64] (x86_64)
<sub>SHA-256: `a0abf37003c3c47a53c1b2a12420296a2b0ee323cf3610fd6fbf9d9bab9d99f3`</sub>
- [`scrcpy-linux-x86_64-v3.2.tar.gz`][direct-linux-x86_64] (x86_64)
<sub>SHA-256: `df6cf000447428fcde322022848d655ff0211d98688d0f17cbbf21be9c1272be`</sub>
[latest release]: https://github.com/Genymobile/scrcpy/releases/latest
[direct-linux-x86_64]: https://github.com/Genymobile/scrcpy/releases/download/v3.3/scrcpy-linux-x86_64-v3.3.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.

View File

@@ -6,15 +6,15 @@
Download a static build of the [latest release]:
- [`scrcpy-macos-aarch64-v3.3.tar.gz`][direct-macos-aarch64] (aarch64)
<sub>SHA-256: `7a4cdaeb8ba74593edda278c000ddedc8d70a51263a80b16a6345475d42ac21e`</sub>
- [`scrcpy-macos-aarch64-v3.2.tar.gz`][direct-macos-aarch64] (aarch64)
<sub>SHA-256: `f6d1f3c5f74d4d46f5080baa5b56b69f5edbf698d47e0cf4e2a1fd5058f9507b`</sub>
- [`scrcpy-macos-x86_64-v3.3.tar.gz`][direct-macos-x86_64] (x86_64)
<sub>SHA-256: `bb3c13aac166b92539371883a8781aa861a7cd18e3e6077e570ab7a1f562f774`</sub>
- [`scrcpy-macos-x86_64-v3.2.tar.gz`][direct-macos-x86_64] (x86_64)
<sub>SHA-256: `e337d5cf0ba4e1281699c338ce5f104aee96eb7b2893dc851399b6643eb4044e`</sub>
[latest release]: https://github.com/Genymobile/scrcpy/releases/latest
[direct-macos-aarch64]: https://github.com/Genymobile/scrcpy/releases/download/v3.3/scrcpy-macos-aarch64-v3.3.tar.gz
[direct-macos-x86_64]: https://github.com/Genymobile/scrcpy/releases/download/v3.3/scrcpy-macos-x86_64-v3.3.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.

View File

@@ -6,14 +6,14 @@
Download the [latest release]:
- [`scrcpy-win64-v3.3.zip`][direct-win64] (64-bit)
<sub>SHA-256: `a120cb4be7cde2891af38e83d2008173a0b6b6b5e344b2dfe668d0f892999933`</sub>
- [`scrcpy-win32-v3.3.zip`][direct-win32] (32-bit)
<sub>SHA-256: `e409ab83f8c57bd6ac741d652635cab7699fcf3d384e233833872f117b993ca6`</sub>
- [`scrcpy-win64-v3.2.zip`][direct-win64] (64-bit)
<sub>SHA-256: `eaa27133e0520979873ba57ad651560a4cc2618373bd05450b23a84d32beafd0`</sub>
- [`scrcpy-win32-v3.2.zip`][direct-win32] (32-bit)
<sub>SHA-256: `4a3407d7f0c2c8a03e22a12cf0b5e1e585a5056fe23c8e5cf3252207c6fa8357`</sub>
[latest release]: https://github.com/Genymobile/scrcpy/releases/latest
[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v3.3/scrcpy-win64-v3.3.zip
[direct-win32]: https://github.com/Genymobile/scrcpy/releases/download/v3.3/scrcpy-win32-v3.3.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.

View File

@@ -2,8 +2,8 @@
set -e
BUILDDIR=build-auto
PREBUILT_SERVER_URL=https://github.com/Genymobile/scrcpy/releases/download/v3.3/scrcpy-server-v3.3
PREBUILT_SERVER_SHA256=351cb2edc7e4c2c75f09a7933fdabcf137be52e2602df154f24ec02db46e9e51
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

View File

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

View File

@@ -7,8 +7,8 @@ android {
applicationId "com.genymobile.scrcpy"
minSdkVersion 21
targetSdkVersion 35
versionCode 30300
versionName "3.3"
versionCode 30200
versionName "3.2"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {

View File

@@ -12,7 +12,7 @@
set -e
SCRCPY_DEBUG=false
SCRCPY_VERSION_NAME=3.3
SCRCPY_VERSION_NAME=3.2
PLATFORM=${ANDROID_PLATFORM:-35}
BUILD_TOOLS=${ANDROID_BUILD_TOOLS:-35.0.0}

View File

@@ -7,7 +7,6 @@ import com.genymobile.scrcpy.util.SettingsException;
import com.genymobile.scrcpy.wrappers.ServiceManager;
import android.os.BatteryManager;
import android.os.Looper;
import android.system.ErrnoException;
import android.system.Os;
@@ -180,11 +179,6 @@ public final class CleanUp {
}
}
@SuppressWarnings("deprecation")
private static void prepareMainLooper() {
Looper.prepareMainLooper();
}
public static void main(String... args) {
try {
// Start a new session to avoid being terminated along with the server process on some devices
@@ -194,9 +188,6 @@ public final class CleanUp {
}
unlinkSelf();
// Needed for workarounds
prepareMainLooper();
int displayId = Integer.parseInt(args[0]);
int restoreStayOn = Integer.parseInt(args[1]);
boolean disableShowTouches = Boolean.parseBoolean(args[2]);

View File

@@ -24,13 +24,10 @@ import com.genymobile.scrcpy.video.SurfaceCapture;
import com.genymobile.scrcpy.video.SurfaceEncoder;
import com.genymobile.scrcpy.video.VideoSource;
import android.annotation.SuppressLint;
import android.os.Build;
import android.os.Looper;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
@@ -58,7 +55,17 @@ public final class Server {
this.fatalError = true;
}
if (running == 0 || this.fatalError) {
Looper.getMainLooper().quitSafely();
notify();
}
}
synchronized void await() {
try {
while (running > 0 && !fatalError) {
wait();
}
} catch (InterruptedException e) {
// ignore
}
}
}
@@ -165,7 +172,7 @@ public final class Server {
});
}
Looper.loop(); // interrupted by the Completion implementation
completion.await();
} finally {
if (cleanUp != null) {
cleanUp.interrupt();
@@ -194,21 +201,6 @@ public final class Server {
}
}
private static void prepareMainLooper() {
// Like Looper.prepareMainLooper(), but with quitAllowed set to true
Looper.prepare();
synchronized (Looper.class) {
try {
@SuppressLint("DiscouragedPrivateApi")
Field field = Looper.class.getDeclaredField("sMainLooper");
field.setAccessible(true);
field.set(null, Looper.myLooper());
} catch (ReflectiveOperationException e) {
throw new AssertionError(e);
}
}
}
public static void main(String... args) {
int status = 0;
try {
@@ -229,8 +221,6 @@ public final class Server {
Ln.e("Exception on thread " + t, e);
});
prepareMainLooper();
Options options = Options.parse(args);
Ln.disableSystemStreams();

View File

@@ -29,6 +29,8 @@ public final class Workarounds {
private static final Object ACTIVITY_THREAD;
static {
prepareMainLooper();
try {
// ActivityThread activityThread = new ActivityThread();
ACTIVITY_THREAD_CLASS = Class.forName("android.app.ActivityThread");
@@ -75,6 +77,19 @@ public final class Workarounds {
fillAppContext();
}
@SuppressWarnings("deprecation")
private static void prepareMainLooper() {
// Some devices internally create a Handler when creating an input Surface, causing an exception:
// "Can't create handler inside thread that has not called Looper.prepare()"
// <https://github.com/Genymobile/scrcpy/issues/240>
//
// Use Looper.prepareMainLooper() instead of Looper.prepare() to avoid a NullPointerException:
// "Attempt to read from field 'android.os.MessageQueue android.os.Looper.mQueue'
// on a null object reference"
// <https://github.com/Genymobile/scrcpy/issues/921>
Looper.prepareMainLooper();
}
private static void fillAppInfo() {
try {
// ActivityThread.AppBindData appBindData = new ActivityThread.AppBindData();

View File

@@ -112,9 +112,8 @@ public class ControlMessageReader {
private ControlMessage parseInjectScrollEvent() throws IOException {
Position position = parsePosition();
// Binary.i16FixedPointToFloat() decodes values assuming the full range is [-1, 1], but the actual range is [-16, 16].
float hScroll = Binary.i16FixedPointToFloat(dis.readShort()) * 16;
float vScroll = Binary.i16FixedPointToFloat(dis.readShort()) * 16;
float hScroll = Binary.i16FixedPointToFloat(dis.readShort());
float vScroll = Binary.i16FixedPointToFloat(dis.readShort());
int buttons = dis.readInt();
return ControlMessage.createInjectScrollEvent(position, hScroll, vScroll, buttons);
}

View File

@@ -114,10 +114,9 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
Ln.w("Input events are not supported for secondary displays before Android 10");
}
// Make sure the clipboard manager is always created from the main thread (even if clipboardAutosync is disabled)
ClipboardManager clipboardManager = ServiceManager.getClipboardManager();
if (clipboardAutosync) {
// If control and autosync are enabled, synchronize Android clipboard to the computer automatically
ClipboardManager clipboardManager = ServiceManager.getClipboardManager();
if (clipboardManager != null) {
clipboardManager.addPrimaryClipChangedListener(() -> {
if (isSettingClipboard.get()) {
@@ -723,9 +722,7 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
if (timeout < 0) {
return null;
}
if (timeout > 0) {
displayDataAvailable.wait(timeout);
}
displayDataAvailable.wait(timeout);
data = displayData.get();
}

View File

@@ -3,13 +3,13 @@ package com.genymobile.scrcpy.wrappers;
import com.genymobile.scrcpy.FakeContext;
import android.content.ClipData;
import android.content.Context;
public final class ClipboardManager {
private final android.content.ClipboardManager manager;
static ClipboardManager create() {
android.content.ClipboardManager manager = (android.content.ClipboardManager) FakeContext.get().getSystemService(Context.CLIPBOARD_SERVICE);
android.content.ClipboardManager manager = (android.content.ClipboardManager) FakeContext.get()
.getSystemService(FakeContext.CLIPBOARD_SERVICE);
if (manager == null) {
// Some devices have no clipboard manager
// <https://github.com/Genymobile/scrcpy/issues/1440>