From 0c5e0a4f6d30834e7aa1623636675df5d9bda1f2 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Fri, 21 Aug 2020 12:24:11 +0200 Subject: [PATCH 01/46] Make Device methods static when possible The behavior of some methods do not depend on the user-provided options. These methods can be static. This will allow to call them directly from the cleanup process. --- .../com/genymobile/scrcpy/Controller.java | 12 +++--- .../java/com/genymobile/scrcpy/Device.java | 40 +++++++++---------- .../java/com/genymobile/scrcpy/Server.java | 2 +- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/server/src/main/java/com/genymobile/scrcpy/Controller.java b/server/src/main/java/com/genymobile/scrcpy/Controller.java index 9100a9db61..79feefc118 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Controller.java +++ b/server/src/main/java/com/genymobile/scrcpy/Controller.java @@ -54,7 +54,7 @@ private void initPointers() { public void control() throws IOException { // on start, power on the device - if (!device.isScreenOn()) { + if (!Device.isScreenOn()) { device.injectKeycode(KeyEvent.KEYCODE_POWER); // dirty hack @@ -105,13 +105,13 @@ private void handleEvent() throws IOException { } break; case ControlMessage.TYPE_EXPAND_NOTIFICATION_PANEL: - device.expandNotificationPanel(); + Device.expandNotificationPanel(); break; case ControlMessage.TYPE_COLLAPSE_NOTIFICATION_PANEL: - device.collapsePanels(); + Device.collapsePanels(); break; case ControlMessage.TYPE_GET_CLIPBOARD: - String clipboardText = device.getClipboardText(); + String clipboardText = Device.getClipboardText(); if (clipboardText != null) { sender.pushClipboardText(clipboardText); } @@ -130,7 +130,7 @@ private void handleEvent() throws IOException { } break; case ControlMessage.TYPE_ROTATE_DEVICE: - device.rotateDevice(); + Device.rotateDevice(); break; default: // do nothing @@ -248,7 +248,7 @@ public void run() { } private boolean pressBackOrTurnScreenOn() { - int keycode = device.isScreenOn() ? KeyEvent.KEYCODE_BACK : KeyEvent.KEYCODE_POWER; + int keycode = Device.isScreenOn() ? KeyEvent.KEYCODE_BACK : KeyEvent.KEYCODE_POWER; if (keepPowerModeOff && keycode == KeyEvent.KEYCODE_POWER) { schedulePowerModeOff(); } diff --git a/server/src/main/java/com/genymobile/scrcpy/Device.java b/server/src/main/java/com/genymobile/scrcpy/Device.java index f23dd05680..a8fdf677eb 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Device.java +++ b/server/src/main/java/com/genymobile/scrcpy/Device.java @@ -25,6 +25,8 @@ public final class Device { public static final int POWER_MODE_OFF = SurfaceControl.POWER_MODE_OFF; public static final int POWER_MODE_NORMAL = SurfaceControl.POWER_MODE_NORMAL; + private static final ServiceManager SERVICE_MANAGER = new ServiceManager(); + public interface RotationListener { void onRotationChanged(int rotation); } @@ -33,8 +35,6 @@ public interface ClipboardListener { void onClipboardTextChanged(String text); } - private final ServiceManager serviceManager = new ServiceManager(); - private ScreenInfo screenInfo; private RotationListener rotationListener; private ClipboardListener clipboardListener; @@ -54,9 +54,9 @@ public interface ClipboardListener { public Device(Options options) { displayId = options.getDisplayId(); - DisplayInfo displayInfo = serviceManager.getDisplayManager().getDisplayInfo(displayId); + DisplayInfo displayInfo = SERVICE_MANAGER.getDisplayManager().getDisplayInfo(displayId); if (displayInfo == null) { - int[] displayIds = serviceManager.getDisplayManager().getDisplayIds(); + int[] displayIds = SERVICE_MANAGER.getDisplayManager().getDisplayIds(); throw new InvalidDisplayIdException(displayId, displayIds); } @@ -65,7 +65,7 @@ public Device(Options options) { screenInfo = ScreenInfo.computeScreenInfo(displayInfo, options.getCrop(), options.getMaxSize(), options.getLockedVideoOrientation()); layerStack = displayInfo.getLayerStack(); - serviceManager.getWindowManager().registerRotationWatcher(new IRotationWatcher.Stub() { + SERVICE_MANAGER.getWindowManager().registerRotationWatcher(new IRotationWatcher.Stub() { @Override public void onRotationChanged(int rotation) { synchronized (Device.this) { @@ -81,7 +81,7 @@ public void onRotationChanged(int rotation) { if (options.getControl()) { // If control is enabled, synchronize Android clipboard to the computer automatically - ClipboardManager clipboardManager = serviceManager.getClipboardManager(); + ClipboardManager clipboardManager = SERVICE_MANAGER.getClipboardManager(); if (clipboardManager != null) { clipboardManager.addPrimaryClipChangedListener(new IOnPrimaryClipChangedListener.Stub() { @Override @@ -166,7 +166,7 @@ public boolean injectEvent(InputEvent inputEvent, int mode) { return false; } - return serviceManager.getInputManager().injectInputEvent(inputEvent, mode); + return SERVICE_MANAGER.getInputManager().injectInputEvent(inputEvent, mode); } public boolean injectEvent(InputEvent event) { @@ -184,8 +184,8 @@ public boolean injectKeycode(int keyCode) { return injectKeyEvent(KeyEvent.ACTION_DOWN, keyCode, 0, 0) && injectKeyEvent(KeyEvent.ACTION_UP, keyCode, 0, 0); } - public boolean isScreenOn() { - return serviceManager.getPowerManager().isScreenOn(); + public static boolean isScreenOn() { + return SERVICE_MANAGER.getPowerManager().isScreenOn(); } public synchronized void setRotationListener(RotationListener rotationListener) { @@ -196,16 +196,16 @@ public synchronized void setClipboardListener(ClipboardListener clipboardListene this.clipboardListener = clipboardListener; } - public void expandNotificationPanel() { - serviceManager.getStatusBarManager().expandNotificationsPanel(); + public static void expandNotificationPanel() { + SERVICE_MANAGER.getStatusBarManager().expandNotificationsPanel(); } - public void collapsePanels() { - serviceManager.getStatusBarManager().collapsePanels(); + public static void collapsePanels() { + SERVICE_MANAGER.getStatusBarManager().collapsePanels(); } - public String getClipboardText() { - ClipboardManager clipboardManager = serviceManager.getClipboardManager(); + public static String getClipboardText() { + ClipboardManager clipboardManager = SERVICE_MANAGER.getClipboardManager(); if (clipboardManager == null) { return null; } @@ -217,7 +217,7 @@ public String getClipboardText() { } public boolean setClipboardText(String text) { - ClipboardManager clipboardManager = serviceManager.getClipboardManager(); + ClipboardManager clipboardManager = SERVICE_MANAGER.getClipboardManager(); if (clipboardManager == null) { return false; } @@ -252,8 +252,8 @@ public static boolean setScreenPowerMode(int mode) { /** * Disable auto-rotation (if enabled), set the screen rotation and re-enable auto-rotation (if it was enabled). */ - public void rotateDevice() { - WindowManager wm = serviceManager.getWindowManager(); + public static void rotateDevice() { + WindowManager wm = SERVICE_MANAGER.getWindowManager(); boolean accelerometerRotation = !wm.isRotationFrozen(); @@ -270,7 +270,7 @@ public void rotateDevice() { } } - public ContentProvider createSettingsProvider() { - return serviceManager.getActivityManager().createSettingsProvider(); + public static ContentProvider createSettingsProvider() { + return SERVICE_MANAGER.getActivityManager().createSettingsProvider(); } } diff --git a/server/src/main/java/com/genymobile/scrcpy/Server.java b/server/src/main/java/com/genymobile/scrcpy/Server.java index d257e3196e..9b7f9de8f7 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Server.java +++ b/server/src/main/java/com/genymobile/scrcpy/Server.java @@ -26,7 +26,7 @@ private static void scrcpy(Options options) throws IOException { boolean mustDisableShowTouchesOnCleanUp = false; int restoreStayOn = -1; if (options.getShowTouches() || options.getStayAwake()) { - try (ContentProvider settings = device.createSettingsProvider()) { + try (ContentProvider settings = Device.createSettingsProvider()) { if (options.getShowTouches()) { String oldValue = settings.getAndPutValue(ContentProvider.TABLE_SYSTEM, "show_touches", "1"); // If "show touches" was disabled, it must be disabled back on clean up From 0bf110dd5cc6a9793177c7764fe7fbfb452628f6 Mon Sep 17 00:00:00 2001 From: brunoais Date: Thu, 13 Aug 2020 07:33:36 +0100 Subject: [PATCH 02/46] Reset power mode only if screen is on PR #1670 Signed-off-by: Romain Vimont --- server/src/main/java/com/genymobile/scrcpy/CleanUp.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/src/main/java/com/genymobile/scrcpy/CleanUp.java b/server/src/main/java/com/genymobile/scrcpy/CleanUp.java index d0ea141b19..efaa059aa8 100644 --- a/server/src/main/java/com/genymobile/scrcpy/CleanUp.java +++ b/server/src/main/java/com/genymobile/scrcpy/CleanUp.java @@ -78,7 +78,9 @@ public static void main(String... args) { if (restoreNormalPowerMode) { Ln.i("Restoring normal power mode"); - Device.setScreenPowerMode(Device.POWER_MODE_NORMAL); + if (Device.isScreenOn()) { + Device.setScreenPowerMode(Device.POWER_MODE_NORMAL); + } } } } From c243fd4c3fe91e83bacf89d2d6bbf642e5019f39 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Mon, 31 Aug 2020 13:37:09 +0200 Subject: [PATCH 03/46] Fix more log format warning The expression port + 1 is promoted to int, but printed as uint16_t. This is the same mistake fixed for a different log by 7eb16ce36458011d87972715f08bd9f03ff39807. Refs #1726 --- app/src/server.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/server.c b/app/src/server.c index 05b2cf9113..422bbfa58a 100644 --- a/app/src/server.c +++ b/app/src/server.c @@ -201,7 +201,7 @@ enable_tunnel_forward_any_port(struct server *server, if (port < port_range.last) { LOGW("Could not forward port %" PRIu16", retrying on %" PRIu16, - port, port + 1); + port, (uint16_t) (port + 1)); port++; continue; } From bd9f656933e79f7b21b42993f8a70a761ab47226 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Mon, 31 Aug 2020 13:52:29 +0200 Subject: [PATCH 04/46] Fix feature test macro The expected feature test macro is _POSIX_C_SOURCE having a value greater or equal to 200809L. Fixes #1726 --- app/src/sys/unix/command.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/sys/unix/command.c b/app/src/sys/unix/command.c index 64a54e7105..3c2f587d3b 100644 --- a/app/src/sys/unix/command.c +++ b/app/src/sys/unix/command.c @@ -1,6 +1,6 @@ -// for portability -#define _POSIX_SOURCE // for kill() -#define _BSD_SOURCE // for readlink() +// for portability (kill, readlink, strdup, strtok_r) +#define _POSIX_C_SOURCE 200809L +#define _BSD_SOURCE // modern glibc will complain without this #define _DEFAULT_SOURCE From ae758f99d6af81123bb6b74d307d798c2c17acf3 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sun, 13 Sep 2020 21:35:11 +0200 Subject: [PATCH 05/46] Adapt call() on ContentProvider for Android 11 This commit in AOSP framework_base added a parameter "attributionTag" to the call() method: https://android.googlesource.com/platform/frameworks/base.git/+/12ac3f406fed87cb9cd3a28b9947e7202a2d14bd%5E%21/#F17 As a consequence, the method did not exist, so scrcpy used the legacy call() method (using the authority "unknown") as a fallback, which fails for security reasons. Fixes #1468 --- .../scrcpy/wrappers/ContentProvider.java | 32 +++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/server/src/main/java/com/genymobile/scrcpy/wrappers/ContentProvider.java b/server/src/main/java/com/genymobile/scrcpy/wrappers/ContentProvider.java index b43494c77d..f8393e59a6 100644 --- a/server/src/main/java/com/genymobile/scrcpy/wrappers/ContentProvider.java +++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/ContentProvider.java @@ -35,7 +35,7 @@ public class ContentProvider implements Closeable { private final IBinder token; private Method callMethod; - private boolean callMethodLegacy; + private int callMethodVersion; ContentProvider(ActivityManager manager, Object provider, String name, IBinder token) { this.manager = manager; @@ -46,12 +46,20 @@ public class ContentProvider implements Closeable { private Method getCallMethod() throws NoSuchMethodException { if (callMethod == null) { + try { - callMethod = provider.getClass().getMethod("call", String.class, String.class, String.class, String.class, Bundle.class); + callMethod = provider.getClass() + .getMethod("call", String.class, String.class, String.class, String.class, String.class, Bundle.class); + callMethodVersion = 0; } catch (NoSuchMethodException e) { - // old version - callMethod = provider.getClass().getMethod("call", String.class, String.class, String.class, Bundle.class); - callMethodLegacy = true; + // old versions + try { + callMethod = provider.getClass().getMethod("call", String.class, String.class, String.class, String.class, Bundle.class); + callMethodVersion = 1; + } catch (NoSuchMethodException e2) { + callMethod = provider.getClass().getMethod("call", String.class, String.class, String.class, Bundle.class); + callMethodVersion = 2; + } } } return callMethod; @@ -61,10 +69,16 @@ private Bundle call(String callMethod, String arg, Bundle extras) { try { Method method = getCallMethod(); Object[] args; - if (!callMethodLegacy) { - args = new Object[]{ServiceManager.PACKAGE_NAME, "settings", callMethod, arg, extras}; - } else { - args = new Object[]{ServiceManager.PACKAGE_NAME, callMethod, arg, extras}; + switch (callMethodVersion) { + case 0: + args = new Object[]{ServiceManager.PACKAGE_NAME, null, "settings", callMethod, arg, extras}; + break; + case 1: + args = new Object[]{ServiceManager.PACKAGE_NAME, "settings", callMethod, arg, extras}; + break; + default: + args = new Object[]{ServiceManager.PACKAGE_NAME, callMethod, arg, extras}; + break; } return (Bundle) method.invoke(provider, args); } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) { From cf7bf3148caca8d4428cbd70578696a8230689e0 Mon Sep 17 00:00:00 2001 From: CapsLock Date: Wed, 6 Feb 2019 01:13:31 +0100 Subject: [PATCH 06/46] Use "/usr/bin/env bash" for build-wrapper.sh PR #426 Signed-off-by: Romain Vimont --- server/scripts/build-wrapper.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/scripts/build-wrapper.sh b/server/scripts/build-wrapper.sh index f55e1ea408..7e16dc9464 100755 --- a/server/scripts/build-wrapper.sh +++ b/server/scripts/build-wrapper.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Wrapper script to invoke gradle from meson set -e From 02a882a0a2fbe40504889bf753aa055bfc3c4fdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Ferreira?= Date: Thu, 27 Aug 2020 18:00:40 +0100 Subject: [PATCH 07/46] Use a more portable shebang for bash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This should increase the portability of bash scripts across various *nix systems such as BSD-like distributions. PR #1716 Signed-off-by: Luís Ferreira 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 e757be9a0f..3060b8e1d4 100755 --- a/server/build_without_gradle.sh +++ b/server/build_without_gradle.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # This script generates the scrcpy binary "manually" (without gradle). # From 1c44dc2259f7111f8432fcf1ab8509c68f48d9a0 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Tue, 15 Sep 2020 13:54:00 +0200 Subject: [PATCH 08/46] Use portable shebang for all bash scripts Refs #426 Refs #1716 --- prebuilt-deps/prepare-dep | 2 +- run | 2 +- scripts/run-scrcpy.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/prebuilt-deps/prepare-dep b/prebuilt-deps/prepare-dep index 34ddcbf50a..f152e6cf65 100755 --- a/prebuilt-deps/prepare-dep +++ b/prebuilt-deps/prepare-dep @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -e url="$1" sum="$2" diff --git a/run b/run index bfb499aed1..628c5c7ea6 100755 --- a/run +++ b/run @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Run scrcpy generated in the specified BUILDDIR. # # This provides the same feature as "ninja run", except that it is possible to diff --git a/scripts/run-scrcpy.sh b/scripts/run-scrcpy.sh index f3130ee93a..e93b639fa0 100755 --- a/scripts/run-scrcpy.sh +++ b/scripts/run-scrcpy.sh @@ -1,2 +1,2 @@ -#!/bin/bash +#!/usr/bin/env bash SCRCPY_SERVER_PATH="$MESON_BUILD_ROOT/server/scrcpy-server" "$MESON_BUILD_ROOT/app/scrcpy" From d662f73bdceda1ceb95ea61fe5fd3b556c1ba932 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Tue, 15 Sep 2020 14:53:37 +0200 Subject: [PATCH 09/46] Upgrade Android SDK to 30 --- server/build.gradle | 4 ++-- server/build_without_gradle.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/server/build.gradle b/server/build.gradle index c7f8bc0fda..4225247280 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -1,11 +1,11 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 29 + compileSdkVersion 30 defaultConfig { applicationId "com.genymobile.scrcpy" minSdkVersion 21 - targetSdkVersion 29 + targetSdkVersion 30 versionCode 19 versionName "1.16" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" diff --git a/server/build_without_gradle.sh b/server/build_without_gradle.sh index 3060b8e1d4..e03939a505 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=1.16 -PLATFORM=${ANDROID_PLATFORM:-29} -BUILD_TOOLS=${ANDROID_BUILD_TOOLS:-29.0.2} +PLATFORM=${ANDROID_PLATFORM:-30} +BUILD_TOOLS=${ANDROID_BUILD_TOOLS:-30.0.0} BUILD_DIR="$(realpath ${BUILD_DIR:-build_manual})" CLASSES_DIR="$BUILD_DIR/classes" From a65ebceac1e1f214328b3be776e7dbc724656db6 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sun, 20 Sep 2020 01:07:18 +0200 Subject: [PATCH 10/46] Add missing mutex unlock on error Fixes #1770 Reported-by: lordnn --- app/src/recorder.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/recorder.c b/app/src/recorder.c index 76edbd0308..e31492c0c5 100644 --- a/app/src/recorder.c +++ b/app/src/recorder.c @@ -361,12 +361,14 @@ recorder_push(struct recorder *recorder, const AVPacket *packet) { if (recorder->failed) { // reject any new packet (this will stop the stream) + mutex_unlock(recorder->mutex); return false; } struct record_packet *rec = record_packet_new(packet); if (!rec) { LOGC("Could not allocate record packet"); + mutex_unlock(recorder->mutex); return false; } From acc65f8c9dc477e978d438a2f2a3501e49230211 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sun, 20 Sep 2020 01:09:05 +0200 Subject: [PATCH 11/46] Remove unused field Fixes #1775 Reported-by: lordnn --- app/src/stream.h | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/stream.h b/app/src/stream.h index f7c5e475e2..cd09d95978 100644 --- a/app/src/stream.h +++ b/app/src/stream.h @@ -14,7 +14,6 @@ struct video_buffer; struct stream { socket_t socket; - struct video_buffer *video_buffer; SDL_Thread *thread; struct decoder *decoder; struct recorder *recorder; From 56d237f152fc4d454a65fe594412b4bd9409fd64 Mon Sep 17 00:00:00 2001 From: Brinan Sjostrom Date: Tue, 22 Sep 2020 18:02:18 -0400 Subject: [PATCH 12/46] Fix "press Enter key" message The message said "Press any key to continue...", whereas only Enter/Return is accepted. PR #1783 Fixes #1757 Reviewed-by: Yu-Chen Lin Signed-off-by: Romain Vimont --- app/src/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main.c b/app/src/main.c index 202c217c2b..d32d989616 100644 --- a/app/src/main.c +++ b/app/src/main.c @@ -100,7 +100,7 @@ main(int argc, char *argv[]) { #if defined (__WINDOWS__) && ! defined (WINDOWS_NOCONSOLE) if (res != 0) { - fprintf(stderr, "Press any key to continue...\n"); + fprintf(stderr, "Press Enter to continue...\n"); getchar(); } #endif From d50ecf40b603f8a0e5a73699babce7301c0bd870 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Thu, 1 Oct 2020 15:06:34 +0200 Subject: [PATCH 13/46] Fix options order --- app/scrcpy.1 | 8 ++++---- app/src/cli.c | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/scrcpy.1 b/app/scrcpy.1 index 6a890f14e0..e957a4d012 100644 --- a/app/scrcpy.1 +++ b/app/scrcpy.1 @@ -92,14 +92,14 @@ Disable device control (mirror the device in read\-only). .B \-N, \-\-no\-display Do not display device (only when screen recording is enabled). -.TP -.B \-\-no\-mipmaps -If the renderer is OpenGL 3.0+ or OpenGL ES 2.0+, then mipmaps are automatically generated to improve downscaling quality. This option disables the generation of mipmaps. - .TP .B \-\-no\-key\-repeat Do not forward repeated key events when a key is held down. +.TP +.B \-\-no\-mipmaps +If the renderer is OpenGL 3.0+ or OpenGL ES 2.0+, then mipmaps are automatically generated to improve downscaling quality. This option disables the generation of mipmaps. + .TP .BI "\-p, \-\-port " port[:port] Set the TCP port (range) used by the client to listen. diff --git a/app/src/cli.c b/app/src/cli.c index 9c791fbf43..41de8ca5dd 100644 --- a/app/src/cli.c +++ b/app/src/cli.c @@ -87,14 +87,14 @@ scrcpy_print_usage(const char *arg0) { " Do not display device (only when screen recording is\n" " enabled).\n" "\n" + " --no-key-repeat\n" + " Do not forward repeated key events when a key is held down.\n" + "\n" " --no-mipmaps\n" " If the renderer is OpenGL 3.0+ or OpenGL ES 2.0+, then\n" " mipmaps are automatically generated to improve downscaling\n" " quality. This option disables the generation of mipmaps.\n" "\n" - " --no-key-repeat\n" - " Do not forward repeated key events when a key is held down.\n" - "\n" " -p, --port port[:port]\n" " Set the TCP port (range) used by the client to listen.\n" " Default is %d:%d.\n" @@ -672,8 +672,8 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) { {"max-size", required_argument, NULL, 'm'}, {"no-control", no_argument, NULL, 'n'}, {"no-display", no_argument, NULL, 'N'}, - {"no-mipmaps", no_argument, NULL, OPT_NO_MIPMAPS}, {"no-key-repeat", no_argument, NULL, OPT_NO_KEY_REPEAT}, + {"no-mipmaps", no_argument, NULL, OPT_NO_MIPMAPS}, {"port", required_argument, NULL, 'p'}, {"prefer-text", no_argument, NULL, OPT_PREFER_TEXT}, {"push-target", required_argument, NULL, OPT_PUSH_TARGET}, From 2edf192e3ae80252d136e6d1a614ab07f93fc561 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Mon, 5 Oct 2020 21:09:01 +0200 Subject: [PATCH 14/46] Remove deprecation warning As a workaround for some devices, we need to prepare the main looper. The method is now deprecated, but we still want to call it. --- server/src/main/java/com/genymobile/scrcpy/Workarounds.java | 1 + 1 file changed, 1 insertion(+) diff --git a/server/src/main/java/com/genymobile/scrcpy/Workarounds.java b/server/src/main/java/com/genymobile/scrcpy/Workarounds.java index 351cc574ce..0f473bc1b7 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Workarounds.java +++ b/server/src/main/java/com/genymobile/scrcpy/Workarounds.java @@ -16,6 +16,7 @@ private Workarounds() { // not instantiable } + @SuppressWarnings("deprecation") public 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()" From 83082406d36d177976c54d93e8eda5a5ea4811e5 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Mon, 5 Oct 2020 21:11:50 +0200 Subject: [PATCH 15/46] Enable Java deprecation warnings details Without the option, gradle reports a lint issue, but without any details. --- build.gradle | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build.gradle b/build.gradle index 94862d2ecf..2755fd62fb 100644 --- a/build.gradle +++ b/build.gradle @@ -19,6 +19,9 @@ allprojects { google() jcenter() } + tasks.withType(JavaCompile) { + options.compilerArgs << "-Xlint:deprecation" + } } task clean(type: Delete) { From ad5f567f07627baea09c7bcf596f921923dca4b2 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Tue, 3 Nov 2020 17:08:21 +0100 Subject: [PATCH 16/46] Remove spurious space --- app/src/scrcpy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/scrcpy.c b/app/src/scrcpy.c index 45068cbbbc..eae9a1eb8a 100644 --- a/app/src/scrcpy.c +++ b/app/src/scrcpy.c @@ -422,7 +422,7 @@ scrcpy(const struct scrcpy_options *options) { options->window_y, options->window_width, options->window_height, options->window_borderless, - options->rotation, options-> mipmaps)) { + options->rotation, options->mipmaps)) { goto end; } From 5dcfc0ebab563e7bfdcf4d28025fa1996a79c214 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Tue, 3 Nov 2020 17:09:03 +0100 Subject: [PATCH 17/46] Add local.properties to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 7bc30289ae..2829d835f2 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ build/ .idea/ .gradle/ /x/ +local.properties From adc547fa6e8e6167cd9633a97d98de6665b8c23a Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Mon, 5 Oct 2020 20:45:53 +0200 Subject: [PATCH 18/46] Add an option to forward all clicks Add --forward-all-clicks to disable mouse shortcuts and forward middle and right clicks to the device instead. Fixes #1302 Fixes #1613 --- README.md | 10 ++++++++++ app/scrcpy.1 | 4 ++++ app/src/cli.c | 11 +++++++++++ app/src/input_manager.c | 3 ++- app/src/input_manager.h | 1 + app/src/scrcpy.h | 2 ++ .../main/java/com/genymobile/scrcpy/Controller.java | 8 ++++++-- 7 files changed, 36 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e8b5446545..a61407ac3a 100644 --- a/README.md +++ b/README.md @@ -595,6 +595,16 @@ scrcpy --no-key-repeat ``` +#### Right-click and middle-click + +By default, right-click triggers BACK (or POWER on) and middle-click triggers +HOME. To disable these shortcuts and forward the clicks to the device instead: + +```bash +scrcpy --forward-all-clicks +``` + + ### File drop #### Install APK diff --git a/app/scrcpy.1 b/app/scrcpy.1 index e957a4d012..e2b999deef 100644 --- a/app/scrcpy.1 +++ b/app/scrcpy.1 @@ -60,6 +60,10 @@ Default is 0. .B \-\-force\-adb\-forward Do not attempt to use "adb reverse" to connect to the device. +.TP +.B \-\-forward\-all\-clicks +By default, right-click triggers BACK (or POWER on) and middle-click triggers HOME. This option disables these shortcuts and forward the clicks to the device instead. + .TP .B \-f, \-\-fullscreen Start in fullscreen. diff --git a/app/src/cli.c b/app/src/cli.c index 41de8ca5dd..ffe3145524 100644 --- a/app/src/cli.c +++ b/app/src/cli.c @@ -57,6 +57,11 @@ scrcpy_print_usage(const char *arg0) { " Do not attempt to use \"adb reverse\" to connect to the\n" " the device.\n" "\n" + " --forward-all-clicks\n" + " By default, right-click triggers BACK (or POWER on) and\n" + " middle-click triggers HOME. This option disables these\n" + " shortcuts and forward the clicks to the device instead.\n" + "\n" " -f, --fullscreen\n" " Start in fullscreen.\n" "\n" @@ -651,6 +656,7 @@ guess_record_format(const char *filename) { #define OPT_DISABLE_SCREENSAVER 1020 #define OPT_SHORTCUT_MOD 1021 #define OPT_NO_KEY_REPEAT 1022 +#define OPT_FORWARD_ALL_CLICKS 1023 bool scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) { @@ -664,6 +670,8 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) { {"display", required_argument, NULL, OPT_DISPLAY_ID}, {"force-adb-forward", no_argument, NULL, OPT_FORCE_ADB_FORWARD}, + {"forward-all-clicks", no_argument, NULL, + OPT_FORWARD_ALL_CLICKS}, {"fullscreen", no_argument, NULL, 'f'}, {"help", no_argument, NULL, 'h'}, {"lock-video-orientation", required_argument, NULL, @@ -856,6 +864,9 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) { return false; } break; + case OPT_FORWARD_ALL_CLICKS: + opts->forward_all_clicks = true; + break; default: // getopt prints the error message on stderr return false; diff --git a/app/src/input_manager.c b/app/src/input_manager.c index 962db1d3e7..b03da38349 100644 --- a/app/src/input_manager.c +++ b/app/src/input_manager.c @@ -60,6 +60,7 @@ input_manager_init(struct input_manager *im, im->control = options->control; im->forward_key_repeat = options->forward_key_repeat; im->prefer_text = options->prefer_text; + im->forward_all_clicks = options->forward_all_clicks; const struct sc_shortcut_mods *shortcut_mods = &options->shortcut_mods; assert(shortcut_mods->count); @@ -629,7 +630,7 @@ input_manager_process_mouse_button(struct input_manager *im, } bool down = event->type == SDL_MOUSEBUTTONDOWN; - if (down) { + if (!im->forward_all_clicks && down) { if (control && event->button == SDL_BUTTON_RIGHT) { press_back_or_turn_screen_on(im->controller); return; diff --git a/app/src/input_manager.h b/app/src/input_manager.h index c3756e40cf..157c28329d 100644 --- a/app/src/input_manager.h +++ b/app/src/input_manager.h @@ -25,6 +25,7 @@ struct input_manager { bool control; bool forward_key_repeat; bool prefer_text; + bool forward_all_clicks; struct { unsigned data[SC_MAX_SHORTCUT_MODS]; diff --git a/app/src/scrcpy.h b/app/src/scrcpy.h index 86a2b57bc6..8ab05a9dd6 100644 --- a/app/src/scrcpy.h +++ b/app/src/scrcpy.h @@ -79,6 +79,7 @@ struct scrcpy_options { bool force_adb_forward; bool disable_screensaver; bool forward_key_repeat; + bool forward_all_clicks; }; #define SCRCPY_OPTIONS_DEFAULT { \ @@ -123,6 +124,7 @@ struct scrcpy_options { .force_adb_forward = false, \ .disable_screensaver = false, \ .forward_key_repeat = true, \ + .forward_all_clicks = false, \ } bool diff --git a/server/src/main/java/com/genymobile/scrcpy/Controller.java b/server/src/main/java/com/genymobile/scrcpy/Controller.java index 79feefc118..84780239d7 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Controller.java +++ b/server/src/main/java/com/genymobile/scrcpy/Controller.java @@ -205,9 +205,13 @@ private boolean injectTouch(int action, long pointerId, Position position, float } } + // Right-click and middle-click only work if the source is a mouse + boolean nonPrimaryButtonPressed = (buttons & ~MotionEvent.BUTTON_PRIMARY) != 0; + int source = nonPrimaryButtonPressed ? InputDevice.SOURCE_MOUSE : InputDevice.SOURCE_TOUCHSCREEN; + MotionEvent event = MotionEvent - .obtain(lastTouchDown, now, action, pointerCount, pointerProperties, pointerCoords, 0, buttons, 1f, 1f, DEVICE_ID_VIRTUAL, 0, - InputDevice.SOURCE_TOUCHSCREEN, 0); + .obtain(lastTouchDown, now, action, pointerCount, pointerProperties, pointerCoords, 0, buttons, 1f, 1f, DEVICE_ID_VIRTUAL, 0, source, + 0); return device.injectEvent(event); } From d5f059c7cbef7d13889086670716d9f33f805dc4 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Tue, 6 Oct 2020 21:30:10 +0200 Subject: [PATCH 19/46] Add option to force legacy method for pasting Some devices do not behave as expected when setting the device clipboard programmatically. Add an option --legacy-paste to change the behavior of Ctrl+v and MOD+v so that they inject the computer clipboard text as a sequence of key events (the same way as MOD+Shift+v). Fixes #1750 Fixes #1771 --- README.md | 5 +++++ app/scrcpy.1 | 6 ++++++ app/src/cli.c | 11 +++++++++++ app/src/input_manager.c | 8 +++++++- app/src/input_manager.h | 1 + app/src/scrcpy.h | 2 ++ 6 files changed, 32 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a61407ac3a..95a38409df 100644 --- a/README.md +++ b/README.md @@ -548,6 +548,11 @@ into the device clipboard. As a consequence, any Android application could read its content. You should avoid to paste sensitive content (like passwords) that way. +Some devices do not behave as expected when setting the device clipboard +programmatically. An option `--legacy-paste` is provided to change the behavior +of Ctrl+v and MOD+v so that they +also inject the computer clipboard text as a sequence of key events (the same +way as MOD+Shift+v). #### Pinch-to-zoom diff --git a/app/scrcpy.1 b/app/scrcpy.1 index e2b999deef..b16d1004c3 100644 --- a/app/scrcpy.1 +++ b/app/scrcpy.1 @@ -72,6 +72,12 @@ Start in fullscreen. .B \-h, \-\-help Print this help. +.TP +.B \-\-legacy\-paste +Inject computer clipboard text as a sequence of key events on Ctrl+v (like MOD+Shift+v). + +This is a workaround for some devices not behaving as expected when setting the device clipboard programmatically. + .TP .BI "\-\-lock\-video\-orientation " value Lock video orientation to \fIvalue\fR. Possible values are -1 (unlocked), 0, 1, 2 and 3. Natural device orientation is 0, and each increment adds a 90 degrees otation counterclockwise. diff --git a/app/src/cli.c b/app/src/cli.c index ffe3145524..4f15f2e8d5 100644 --- a/app/src/cli.c +++ b/app/src/cli.c @@ -68,6 +68,12 @@ scrcpy_print_usage(const char *arg0) { " -h, --help\n" " Print this help.\n" "\n" + " --legacy-paste\n" + " Inject computer clipboard text as a sequence of key events\n" + " on Ctrl+v (like MOD+Shift+v).\n" + " This is a workaround for some devices not behaving as\n" + " expected when setting the device clipboard programmatically.\n" + "\n" " --lock-video-orientation value\n" " Lock video orientation to value.\n" " Possible values are -1 (unlocked), 0, 1, 2 and 3.\n" @@ -657,6 +663,7 @@ guess_record_format(const char *filename) { #define OPT_SHORTCUT_MOD 1021 #define OPT_NO_KEY_REPEAT 1022 #define OPT_FORWARD_ALL_CLICKS 1023 +#define OPT_LEGACY_PASTE 1024 bool scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) { @@ -674,6 +681,7 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) { OPT_FORWARD_ALL_CLICKS}, {"fullscreen", no_argument, NULL, 'f'}, {"help", no_argument, NULL, 'h'}, + {"legacy-paste", no_argument, NULL, OPT_LEGACY_PASTE}, {"lock-video-orientation", required_argument, NULL, OPT_LOCK_VIDEO_ORIENTATION}, {"max-fps", required_argument, NULL, OPT_MAX_FPS}, @@ -867,6 +875,9 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) { case OPT_FORWARD_ALL_CLICKS: opts->forward_all_clicks = true; break; + case OPT_LEGACY_PASTE: + opts->legacy_paste = true; + break; default: // getopt prints the error message on stderr return false; diff --git a/app/src/input_manager.c b/app/src/input_manager.c index b03da38349..bab8566021 100644 --- a/app/src/input_manager.c +++ b/app/src/input_manager.c @@ -61,6 +61,7 @@ input_manager_init(struct input_manager *im, im->forward_key_repeat = options->forward_key_repeat; im->prefer_text = options->prefer_text; im->forward_all_clicks = options->forward_all_clicks; + im->legacy_paste = options->legacy_paste; const struct sc_shortcut_mods *shortcut_mods = &options->shortcut_mods; assert(shortcut_mods->count); @@ -441,7 +442,7 @@ input_manager_process_key(struct input_manager *im, return; case SDLK_v: if (control && !repeat && down) { - if (shift) { + if (shift || im->legacy_paste) { // inject the text as input events clipboard_paste(controller); } else { @@ -505,6 +506,11 @@ input_manager_process_key(struct input_manager *im, } if (ctrl && !shift && keycode == SDLK_v && down && !repeat) { + if (im->legacy_paste) { + // inject the text as input events + clipboard_paste(controller); + return; + } // Synchronize the computer clipboard to the device clipboard before // sending Ctrl+v, to allow seamless copy-paste. set_device_clipboard(controller, false); diff --git a/app/src/input_manager.h b/app/src/input_manager.h index 157c28329d..df9b071fe5 100644 --- a/app/src/input_manager.h +++ b/app/src/input_manager.h @@ -26,6 +26,7 @@ struct input_manager { bool forward_key_repeat; bool prefer_text; bool forward_all_clicks; + bool legacy_paste; struct { unsigned data[SC_MAX_SHORTCUT_MODS]; diff --git a/app/src/scrcpy.h b/app/src/scrcpy.h index 8ab05a9dd6..5f761f75e5 100644 --- a/app/src/scrcpy.h +++ b/app/src/scrcpy.h @@ -80,6 +80,7 @@ struct scrcpy_options { bool disable_screensaver; bool forward_key_repeat; bool forward_all_clicks; + bool legacy_paste; }; #define SCRCPY_OPTIONS_DEFAULT { \ @@ -125,6 +126,7 @@ struct scrcpy_options { .disable_screensaver = false, \ .forward_key_repeat = true, \ .forward_all_clicks = false, \ + .legacy_paste = false, \ } bool From 76c2c6e69dc074031a81c4e050347e374cbfb776 Mon Sep 17 00:00:00 2001 From: Tzah Mazuz Date: Mon, 12 Oct 2020 12:23:06 +0300 Subject: [PATCH 20/46] Adding new option --encoder Some devices have more than one encoder, and some encoders may cause issues or crash. With this option we can specify which encoder we want the device to use. PR #1827 Fixes #1810 Signed-off-by: Romain Vimont --- app/src/cli.c | 8 ++++++++ app/src/scrcpy.c | 1 + app/src/scrcpy.h | 2 ++ app/src/server.c | 1 + app/src/server.h | 1 + .../src/main/java/com/genymobile/scrcpy/Options.java | 9 +++++++++ .../java/com/genymobile/scrcpy/ScreenEncoder.java | 12 +++++++++--- .../src/main/java/com/genymobile/scrcpy/Server.java | 8 ++++++-- 8 files changed, 37 insertions(+), 5 deletions(-) diff --git a/app/src/cli.c b/app/src/cli.c index 4f15f2e8d5..f01b7941f8 100644 --- a/app/src/cli.c +++ b/app/src/cli.c @@ -53,6 +53,9 @@ scrcpy_print_usage(const char *arg0) { "\n" " Default is 0.\n" "\n" + " --encoder name\n" + " Use a specific MediaCodec encoder (must be a H.264 encoder).\n" + "\n" " --force-adb-forward\n" " Do not attempt to use \"adb reverse\" to connect to the\n" " the device.\n" @@ -664,6 +667,7 @@ guess_record_format(const char *filename) { #define OPT_NO_KEY_REPEAT 1022 #define OPT_FORWARD_ALL_CLICKS 1023 #define OPT_LEGACY_PASTE 1024 +#define OPT_ENCODER_NAME 1025 bool scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) { @@ -675,6 +679,7 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) { {"disable-screensaver", no_argument, NULL, OPT_DISABLE_SCREENSAVER}, {"display", required_argument, NULL, OPT_DISPLAY_ID}, + {"encoder", required_argument, NULL, OPT_ENCODER_NAME}, {"force-adb-forward", no_argument, NULL, OPT_FORCE_ADB_FORWARD}, {"forward-all-clicks", no_argument, NULL, @@ -861,6 +866,9 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) { case OPT_CODEC_OPTIONS: opts->codec_options = optarg; break; + case OPT_ENCODER_NAME: + opts->encoder_name = optarg; + break; case OPT_FORCE_ADB_FORWARD: opts->force_adb_forward = true; break; diff --git a/app/src/scrcpy.c b/app/src/scrcpy.c index eae9a1eb8a..a543e11c77 100644 --- a/app/src/scrcpy.c +++ b/app/src/scrcpy.c @@ -318,6 +318,7 @@ scrcpy(const struct scrcpy_options *options) { .show_touches = options->show_touches, .stay_awake = options->stay_awake, .codec_options = options->codec_options, + .encoder_name = options->encoder_name, .force_adb_forward = options->force_adb_forward, }; if (!server_start(&server, options->serial, ¶ms)) { diff --git a/app/src/scrcpy.h b/app/src/scrcpy.h index 5f761f75e5..8548d1f708 100644 --- a/app/src/scrcpy.h +++ b/app/src/scrcpy.h @@ -51,6 +51,7 @@ struct scrcpy_options { const char *push_target; const char *render_driver; const char *codec_options; + const char *encoder_name; enum sc_log_level log_level; enum sc_record_format record_format; struct sc_port_range port_range; @@ -91,6 +92,7 @@ struct scrcpy_options { .push_target = NULL, \ .render_driver = NULL, \ .codec_options = NULL, \ + .encoder_name = NULL, \ .log_level = SC_LOG_LEVEL_INFO, \ .record_format = SC_RECORD_FORMAT_AUTO, \ .port_range = { \ diff --git a/app/src/server.c b/app/src/server.c index 422bbfa58a..9267356b28 100644 --- a/app/src/server.c +++ b/app/src/server.c @@ -294,6 +294,7 @@ execute_server(struct server *server, const struct server_params *params) { params->show_touches ? "true" : "false", params->stay_awake ? "true" : "false", params->codec_options ? params->codec_options : "-", + params->encoder_name ? params->encoder_name : "-", }; #ifdef SERVER_DEBUGGER LOGI("Server debugger waiting for a client on device port " diff --git a/app/src/server.h b/app/src/server.h index 254afe30e1..30ce09bc58 100644 --- a/app/src/server.h +++ b/app/src/server.h @@ -48,6 +48,7 @@ struct server_params { enum sc_log_level log_level; const char *crop; const char *codec_options; + const char *encoder_name; struct sc_port_range port_range; uint16_t max_size; uint32_t bit_rate; diff --git a/server/src/main/java/com/genymobile/scrcpy/Options.java b/server/src/main/java/com/genymobile/scrcpy/Options.java index 06312a37a5..150d06a83d 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Options.java +++ b/server/src/main/java/com/genymobile/scrcpy/Options.java @@ -16,6 +16,7 @@ public class Options { private boolean showTouches; private boolean stayAwake; private String codecOptions; + private String encoderName; public Ln.Level getLogLevel() { return logLevel; @@ -120,4 +121,12 @@ public String getCodecOptions() { public void setCodecOptions(String codecOptions) { this.codecOptions = codecOptions; } + + public String getEncoderName() { + return encoderName; + } + + public void setEncoderName(String encoderName) { + this.encoderName = encoderName; + } } diff --git a/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java b/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java index d722388c85..ee9ea93506 100644 --- a/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java +++ b/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java @@ -26,17 +26,19 @@ public class ScreenEncoder implements Device.RotationListener { private final AtomicBoolean rotationChanged = new AtomicBoolean(); private final ByteBuffer headerBuffer = ByteBuffer.allocate(12); + private String encoderName; private List codecOptions; private int bitRate; private int maxFps; private boolean sendFrameMeta; private long ptsOrigin; - public ScreenEncoder(boolean sendFrameMeta, int bitRate, int maxFps, List codecOptions) { + public ScreenEncoder(boolean sendFrameMeta, int bitRate, int maxFps, List codecOptions, String encoderName) { this.sendFrameMeta = sendFrameMeta; this.bitRate = bitRate; this.maxFps = maxFps; this.codecOptions = codecOptions; + this.encoderName = encoderName; } @Override @@ -69,7 +71,7 @@ private void internalStreamScreen(Device device, FileDescriptor fd) throws IOExc boolean alive; try { do { - MediaCodec codec = createCodec(); + MediaCodec codec = createCodec(encoderName); IBinder display = createDisplay(); ScreenInfo screenInfo = device.getScreenInfo(); Rect contentRect = screenInfo.getContentRect(); @@ -150,7 +152,11 @@ private void writeFrameMeta(FileDescriptor fd, MediaCodec.BufferInfo bufferInfo, IO.writeFully(fd, headerBuffer); } - private static MediaCodec createCodec() throws IOException { + private static MediaCodec createCodec(String encoderName) throws IOException { + if (encoderName != null) { + Ln.d("Creating encoder by name: '" + encoderName + "'"); + return MediaCodec.createByCodecName(encoderName); + } return MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC); } diff --git a/server/src/main/java/com/genymobile/scrcpy/Server.java b/server/src/main/java/com/genymobile/scrcpy/Server.java index 9b7f9de8f7..0e7bd244bd 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Server.java +++ b/server/src/main/java/com/genymobile/scrcpy/Server.java @@ -54,7 +54,8 @@ private static void scrcpy(Options options) throws IOException { boolean tunnelForward = options.isTunnelForward(); try (DesktopConnection connection = DesktopConnection.open(device, tunnelForward)) { - ScreenEncoder screenEncoder = new ScreenEncoder(options.getSendFrameMeta(), options.getBitRate(), options.getMaxFps(), codecOptions); + ScreenEncoder screenEncoder = new ScreenEncoder(options.getSendFrameMeta(), options.getBitRate(), options.getMaxFps(), codecOptions, + options.getEncoderName()); if (options.getControl()) { final Controller controller = new Controller(device, connection); @@ -120,7 +121,7 @@ private static Options createOptions(String... args) { "The server version (" + BuildConfig.VERSION_NAME + ") does not match the client " + "(" + clientVersion + ")"); } - final int expectedParameters = 14; + final int expectedParameters = 15; if (args.length != expectedParameters) { throw new IllegalArgumentException("Expecting " + expectedParameters + " parameters"); } @@ -167,6 +168,9 @@ private static Options createOptions(String... args) { String codecOptions = args[13]; options.setCodecOptions(codecOptions); + String encoderName = "-".equals(args[14]) ? null : args[14]; + options.setEncoderName(encoderName); + return options; } From 363eeea19eb0d347643a4dd9592dac0063396dff Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sun, 8 Nov 2020 17:27:33 +0100 Subject: [PATCH 21/46] Log encoder name When the encoder is selected automatically, log the name of the selected encoder. --- server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java b/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java index ee9ea93506..1dac762984 100644 --- a/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java +++ b/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java @@ -157,7 +157,9 @@ private static MediaCodec createCodec(String encoderName) throws IOException { Ln.d("Creating encoder by name: '" + encoderName + "'"); return MediaCodec.createByCodecName(encoderName); } - return MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC); + MediaCodec codec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC); + Ln.d("Using encoder: '" + codec.getName() + "'"); + return codec; } private static void setCodecOption(MediaFormat format, CodecOption codecOption) { From 42ab8fd61156a7894fb8c6802f7a1b408598e14f Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sat, 7 Nov 2020 15:53:48 +0100 Subject: [PATCH 22/46] List available encoders on invalid name specified If an invalid encoder name is given via the --encoder option, list all the H.264 encoders available on the device. --- .../scrcpy/InvalidEncoderException.java | 23 +++++++++++++++++++ .../com/genymobile/scrcpy/ScreenEncoder.java | 21 ++++++++++++++++- .../java/com/genymobile/scrcpy/Server.java | 10 ++++++++ 3 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 server/src/main/java/com/genymobile/scrcpy/InvalidEncoderException.java diff --git a/server/src/main/java/com/genymobile/scrcpy/InvalidEncoderException.java b/server/src/main/java/com/genymobile/scrcpy/InvalidEncoderException.java new file mode 100644 index 0000000000..1efd2989df --- /dev/null +++ b/server/src/main/java/com/genymobile/scrcpy/InvalidEncoderException.java @@ -0,0 +1,23 @@ +package com.genymobile.scrcpy; + +import android.media.MediaCodecInfo; + +public class InvalidEncoderException extends RuntimeException { + + private final String name; + private final MediaCodecInfo[] availableEncoders; + + public InvalidEncoderException(String name, MediaCodecInfo[] availableEncoders) { + super("There is no encoder having name '" + name + '"'); + this.name = name; + this.availableEncoders = availableEncoders; + } + + public String getName() { + return name; + } + + public MediaCodecInfo[] getAvailableEncoders() { + return availableEncoders; + } +} diff --git a/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java b/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java index 1dac762984..c7c104e4f5 100644 --- a/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java +++ b/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java @@ -5,6 +5,7 @@ import android.graphics.Rect; import android.media.MediaCodec; import android.media.MediaCodecInfo; +import android.media.MediaCodecList; import android.media.MediaFormat; import android.os.IBinder; import android.view.Surface; @@ -12,6 +13,8 @@ import java.io.FileDescriptor; import java.io.IOException; import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; @@ -152,10 +155,26 @@ private void writeFrameMeta(FileDescriptor fd, MediaCodec.BufferInfo bufferInfo, IO.writeFully(fd, headerBuffer); } + private static MediaCodecInfo[] listEncoders() { + List result = new ArrayList<>(); + MediaCodecList list = new MediaCodecList(MediaCodecList.REGULAR_CODECS); + for (MediaCodecInfo codecInfo : list.getCodecInfos()) { + if (codecInfo.isEncoder() && Arrays.asList(codecInfo.getSupportedTypes()).contains(MediaFormat.MIMETYPE_VIDEO_AVC)) { + result.add(codecInfo); + } + } + return result.toArray(new MediaCodecInfo[result.size()]); + } + private static MediaCodec createCodec(String encoderName) throws IOException { if (encoderName != null) { Ln.d("Creating encoder by name: '" + encoderName + "'"); - return MediaCodec.createByCodecName(encoderName); + try { + return MediaCodec.createByCodecName(encoderName); + } catch (IllegalArgumentException e) { + MediaCodecInfo[] encoders = listEncoders(); + throw new InvalidEncoderException(encoderName, encoders); + } } MediaCodec codec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC); Ln.d("Using encoder: '" + codec.getName() + "'"); diff --git a/server/src/main/java/com/genymobile/scrcpy/Server.java b/server/src/main/java/com/genymobile/scrcpy/Server.java index 0e7bd244bd..f501d3d89a 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Server.java +++ b/server/src/main/java/com/genymobile/scrcpy/Server.java @@ -4,6 +4,7 @@ import android.graphics.Rect; import android.media.MediaCodec; +import android.media.MediaCodecInfo; import android.os.BatteryManager; import android.os.Build; @@ -210,6 +211,15 @@ private static void suggestFix(Throwable e) { Ln.e(" scrcpy --display " + id); } } + } else if (e instanceof InvalidEncoderException) { + InvalidEncoderException iee = (InvalidEncoderException) e; + MediaCodecInfo[] encoders = iee.getAvailableEncoders(); + if (encoders != null && encoders.length > 0) { + Ln.e("Try to use one of the available encoders:"); + for (MediaCodecInfo encoder : encoders) { + Ln.e(" scrcpy --encoder-name '" + encoder.getName() + "'"); + } + } } } From 576814bcec5718d325de3a85528e5133cc46be68 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sun, 8 Nov 2020 21:11:12 +0100 Subject: [PATCH 23/46] Document --encoder option Add documentation for the new option --encoder in the manpage and in README.md. --- README.md | 16 ++++++++++++++++ app/scrcpy.1 | 4 ++++ 2 files changed, 20 insertions(+) diff --git a/README.md b/README.md index 95a38409df..1283aa5067 100644 --- a/README.md +++ b/README.md @@ -203,6 +203,22 @@ scrcpy --lock-video-orientation 3 # 90° clockwise This affects recording orientation. +#### Encoder + +Some devices have more than one encoder, and some of them may cause issues or +crash. It is possible to select a different encoder: + +```bash +scrcpy --encoder OMX.qcom.video.encoder.avc +``` + +To list the available encoders, you could pass an invalid encoder name, the +error will give the available encoders: + +```bash +scrcpy --encoder _ +``` + ### Recording It is possible to record the screen while mirroring: diff --git a/app/scrcpy.1 b/app/scrcpy.1 index b16d1004c3..92b8e1e3ce 100644 --- a/app/scrcpy.1 +++ b/app/scrcpy.1 @@ -56,6 +56,10 @@ The list of possible display ids can be listed by "adb shell dumpsys display" Default is 0. +.TP +.BI "\-\-encoder " name +Use a specific MediaCodec encoder (must be a H.264 encoder). + .TP .B \-\-force\-adb\-forward Do not attempt to use "adb reverse" to connect to the device. From 868e762d71695f0a45f593c83215aeb428e475b3 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sat, 14 Nov 2020 22:08:59 +0100 Subject: [PATCH 24/46] Fix size_t format specifier for Windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use "%Iu" on Windows. This fixes the following warning: ../app/src/sys/win/command.c:17:14: warning: unknown conversion type character ‘l’ in format [-Wformat=] 17 | LOGE("Command too long (%" PRIsizet " chars)", len - 1); --- app/src/command.h | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/src/command.h b/app/src/command.h index 28f9fbcf7e..7035139b60 100644 --- a/app/src/command.h +++ b/app/src/command.h @@ -12,11 +12,7 @@ # define PATH_SEPARATOR '\\' # define PRIexitcode "lu" // -# ifdef _WIN64 -# define PRIsizet PRIu64 -# else -# define PRIsizet PRIu32 -# endif +# define PRIsizet "Iu" # define PROCESS_NONE NULL # define NO_EXIT_CODE -1u // max value as unsigned typedef HANDLE process_t; From 30434afc0ad89198e8f7f5567d9d50bbbf8c055c Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Tue, 24 Nov 2020 09:45:18 +0100 Subject: [PATCH 25/46] Upgrade gradle build tools to 4.0.1 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 2755fd62fb..c977c3989b 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.6.2' + classpath 'com.android.tools.build:gradle:4.0.1' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files From 47c897126758ed016881bdf51b2eb643c4a5fe07 Mon Sep 17 00:00:00 2001 From: "SamBe.ng" Date: Mon, 30 Nov 2020 10:16:50 +0100 Subject: [PATCH 26/46] Document shell command to get the device IP PR #1944 Signed-off-by: Romain Vimont --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d10aaa74dc..67f2c14c12 100644 --- a/README.md +++ b/README.md @@ -235,7 +235,13 @@ _Scrcpy_ uses `adb` to communicate with the device, and `adb` can [connect] to a device over TCP/IP: 1. Connect the device to the same Wi-Fi as your computer. -2. Get your device IP address (in Settings → About phone → Status). +2. Get your device IP address, in Settings → About phone → Status, or by + executing this command: + + ```bash + adb shell ip route | awk '{print $9}' + ``` + 3. Enable adb over TCP/IP on your device: `adb tcpip 5555`. 4. Unplug your device. 5. Connect to your device: `adb connect DEVICE_IP:5555` _(replace `DEVICE_IP`)_. From d6078cf20244e6651aea5832e6b78b3aa3459766 Mon Sep 17 00:00:00 2001 From: jianzhang4 Date: Wed, 9 Dec 2020 11:41:17 +0800 Subject: [PATCH 27/46] Fix build errors for macOS PR #1960 Signed-off-by: Romain Vimont --- app/src/sys/unix/command.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/sys/unix/command.c b/app/src/sys/unix/command.c index 64a54e7105..09925c500b 100644 --- a/app/src/sys/unix/command.c +++ b/app/src/sys/unix/command.c @@ -5,6 +5,10 @@ // modern glibc will complain without this #define _DEFAULT_SOURCE +#ifdef __APPLE__ +# define _DARWIN_C_SOURCE // for strdup(), strtok_r(), memset_pattern4() +#endif + #include "command.h" #include "config.h" From c9a4bdb8905042cc9bf14e55b09f4430214c9b31 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sat, 12 Dec 2020 15:56:32 +0100 Subject: [PATCH 28/46] Upgrade platform-tools (30.0.5) for Windows Include the latest version of adb in Windows releases. --- prebuilt-deps/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/prebuilt-deps/Makefile b/prebuilt-deps/Makefile index 9cccd0bd77..c2297b8216 100644 --- a/prebuilt-deps/Makefile +++ b/prebuilt-deps/Makefile @@ -35,6 +35,6 @@ prepare-sdl2: SDL2-2.0.12 prepare-adb: - @./prepare-dep https://dl.google.com/android/repository/platform-tools_r30.0.4-windows.zip \ - 413182fff6c5957911e231b9e97e6be4fc6a539035e3dfb580b5c54bd5950fee \ + @./prepare-dep https://dl.google.com/android/repository/platform-tools_r30.0.5-windows.zip \ + 549ba2bdc31f335eb8a504f005f77606a479cc216d6b64a3e8b64c780003661f \ platform-tools From 5dc3285dbf807553f86346ab16d0891cfeb32ab7 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sat, 12 Dec 2020 15:57:54 +0100 Subject: [PATCH 29/46] Reference FFmpeg Windows builds from GitHub Refs #1753 --- prebuilt-deps/Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/prebuilt-deps/Makefile b/prebuilt-deps/Makefile index c2297b8216..535d569211 100644 --- a/prebuilt-deps/Makefile +++ b/prebuilt-deps/Makefile @@ -10,22 +10,22 @@ prepare-win32: prepare-sdl2 prepare-ffmpeg-shared-win32 prepare-ffmpeg-dev-win32 prepare-win64: prepare-sdl2 prepare-ffmpeg-shared-win64 prepare-ffmpeg-dev-win64 prepare-adb prepare-ffmpeg-shared-win32: - @./prepare-dep https://ffmpeg.zeranoe.com/builds/win32/shared/ffmpeg-4.3.1-win32-shared.zip \ + @./prepare-dep https://github.com/Genymobile/scrcpy/releases/download/v1.16/ffmpeg-4.3.1-win32-shared.zip \ 357af9901a456f4dcbacd107e83a934d344c9cb07ddad8aaf80612eeab7d26d2 \ ffmpeg-4.3.1-win32-shared prepare-ffmpeg-dev-win32: - @./prepare-dep https://ffmpeg.zeranoe.com/builds/win32/dev/ffmpeg-4.3.1-win32-dev.zip \ + @./prepare-dep https://github.com/Genymobile/scrcpy/releases/download/v1.16/ffmpeg-4.3.1-win32-dev.zip \ 230efb08e9bcf225bd474da29676c70e591fc94d8790a740ca801408fddcb78b \ ffmpeg-4.3.1-win32-dev prepare-ffmpeg-shared-win64: - @./prepare-dep https://ffmpeg.zeranoe.com/builds/win64/shared/ffmpeg-4.3.1-win64-shared.zip \ + @./prepare-dep https://github.com/Genymobile/scrcpy/releases/download/v1.16/ffmpeg-4.3.1-win64-shared.zip \ dd29b7f92f48dead4dd940492c7509138c0f99db445076d0a597007298a79940 \ ffmpeg-4.3.1-win64-shared prepare-ffmpeg-dev-win64: - @./prepare-dep https://ffmpeg.zeranoe.com/builds/win64/dev/ffmpeg-4.3.1-win64-dev.zip \ + @./prepare-dep https://github.com/Genymobile/scrcpy/releases/download/v1.16/ffmpeg-4.3.1-win64-dev.zip \ 2e8038242cf8e1bd095c2978f196ff0462b122cc6ef7e74626a6af15459d8b81 \ ffmpeg-4.3.1-win64-dev From 6d151eaef914dd836e206e2877263e47882bba18 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Mon, 14 Dec 2020 09:04:14 +0100 Subject: [PATCH 30/46] Reference encoder section from FAQ --- FAQ.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/FAQ.md b/FAQ.md index ba33542eea..a2298e60db 100644 --- a/FAQ.md +++ b/FAQ.md @@ -199,3 +199,5 @@ scrcpy -m 1920 scrcpy -m 1024 scrcpy -m 800 ``` + +You could also try another [encoder](README.md#encoder). From 904d47057987fcd2bc2bb9494758a611fa6ab2bc Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Mon, 14 Dec 2020 09:35:55 +0100 Subject: [PATCH 31/46] Pause on error from a wrapper script On Windows, scrcpy paused on error before exiting to give the user a chance to see the user message. This was a hack and causes issues when using scrcpy from batch scripts. Disable this pause from the scrcpy binary, and provide a batch wrapper (scrcpy-console.bat) to pause on error. Fixes #1875 --- Makefile.CrossWindows | 2 ++ app/src/main.c | 6 ------ data/scrcpy-console.bat | 4 ++++ 3 files changed, 6 insertions(+), 6 deletions(-) create mode 100644 data/scrcpy-console.bat diff --git a/Makefile.CrossWindows b/Makefile.CrossWindows index 0415af8228..f8a360d974 100644 --- a/Makefile.CrossWindows +++ b/Makefile.CrossWindows @@ -100,6 +100,7 @@ dist-win32: build-server build-win32 build-win32-noconsole cp "$(SERVER_BUILD_DIR)"/server/scrcpy-server "$(DIST)/$(WIN32_TARGET_DIR)/" cp "$(WIN32_BUILD_DIR)"/app/scrcpy.exe "$(DIST)/$(WIN32_TARGET_DIR)/" cp "$(WIN32_NOCONSOLE_BUILD_DIR)"/app/scrcpy.exe "$(DIST)/$(WIN32_TARGET_DIR)/scrcpy-noconsole.exe" + cp data/scrcpy-console.bat "$(DIST)/$(WIN32_TARGET_DIR)" cp prebuilt-deps/ffmpeg-4.3.1-win32-shared/bin/avutil-56.dll "$(DIST)/$(WIN32_TARGET_DIR)/" cp prebuilt-deps/ffmpeg-4.3.1-win32-shared/bin/avcodec-58.dll "$(DIST)/$(WIN32_TARGET_DIR)/" cp prebuilt-deps/ffmpeg-4.3.1-win32-shared/bin/avformat-58.dll "$(DIST)/$(WIN32_TARGET_DIR)/" @@ -115,6 +116,7 @@ dist-win64: build-server build-win64 build-win64-noconsole cp "$(SERVER_BUILD_DIR)"/server/scrcpy-server "$(DIST)/$(WIN64_TARGET_DIR)/" cp "$(WIN64_BUILD_DIR)"/app/scrcpy.exe "$(DIST)/$(WIN64_TARGET_DIR)/" cp "$(WIN64_NOCONSOLE_BUILD_DIR)"/app/scrcpy.exe "$(DIST)/$(WIN64_TARGET_DIR)/scrcpy-noconsole.exe" + cp data/scrcpy-console.bat "$(DIST)/$(WIN64_TARGET_DIR)" cp prebuilt-deps/ffmpeg-4.3.1-win64-shared/bin/avutil-56.dll "$(DIST)/$(WIN64_TARGET_DIR)/" cp prebuilt-deps/ffmpeg-4.3.1-win64-shared/bin/avcodec-58.dll "$(DIST)/$(WIN64_TARGET_DIR)/" cp prebuilt-deps/ffmpeg-4.3.1-win64-shared/bin/avformat-58.dll "$(DIST)/$(WIN64_TARGET_DIR)/" diff --git a/app/src/main.c b/app/src/main.c index d32d989616..71125673b2 100644 --- a/app/src/main.c +++ b/app/src/main.c @@ -98,11 +98,5 @@ main(int argc, char *argv[]) { avformat_network_deinit(); // ignore failure -#if defined (__WINDOWS__) && ! defined (WINDOWS_NOCONSOLE) - if (res != 0) { - fprintf(stderr, "Press Enter to continue...\n"); - getchar(); - } -#endif return res; } diff --git a/data/scrcpy-console.bat b/data/scrcpy-console.bat new file mode 100644 index 0000000000..b90be29a48 --- /dev/null +++ b/data/scrcpy-console.bat @@ -0,0 +1,4 @@ +@echo off +scrcpy.exe %* +:: if the exit code is >= 1, then pause +if errorlevel 1 pause From ea12783bbc935e4bc74329a924cda784b5cfc25a Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Tue, 15 Dec 2020 21:50:58 +0100 Subject: [PATCH 32/46] Upgrade JUnit to 4.13 --- server/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/build.gradle b/server/build.gradle index 4225247280..94a492c68e 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -20,7 +20,7 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13' } apply from: "$project.rootDir/config/android-checkstyle.gradle" From a5f4f582956264aa05563df5de847d4a36f8d2aa Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Tue, 22 Dec 2020 01:14:30 +0100 Subject: [PATCH 33/46] Remove duplicate include --- app/src/sys/unix/command.c | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/sys/unix/command.c b/app/src/sys/unix/command.c index 4c3ff7e2e6..c4c262e484 100644 --- a/app/src/sys/unix/command.c +++ b/app/src/sys/unix/command.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include From 43d3dcbd970640c70272fac9d5e54ff90f8e5eea Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Mon, 14 Dec 2020 10:12:01 +0100 Subject: [PATCH 34/46] Document Windows command line usage PR #1973 Reviewed-by: Yu-Chen Lin --- FAQ.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/FAQ.md b/FAQ.md index ba33542eea..4fda4dd481 100644 --- a/FAQ.md +++ b/FAQ.md @@ -199,3 +199,36 @@ scrcpy -m 1920 scrcpy -m 1024 scrcpy -m 800 ``` + + +## Command line on Windows + +Some Windows users are not familiar with the command line. Here is how to open a +terminal and run `scrcpy` with arguments: + + 1. Press Windows+r, this opens a dialog box. + 2. Type `cmd` and press Enter, this opens a terminal. + 3. Go to your _scrcpy_ directory, by typing (adapt the path): + + ```bat + cd C:\Users\user\Downloads\scrcpy-win64-xxx + ``` + + and press Enter + 4. Type your command. For example: + + ```bat + scrcpy --record file.mkv + ``` + +If you plan to always use the same arguments, create a file `myscrcpy.bat` +(enable [show file extensions] to avoid confusion) in the `scrcpy` directory, +containing your command. For example: + +```bat +scrcpy --prefer-text --turn-screen-off --stay-awake +``` + +Then just double-click on that file. + +[show file extensions]: https://www.howtogeek.com/205086/beginner-how-to-make-windows-show-file-extensions/ From 431c9ee33b49f5ce7317b4c687e00bc436adbc02 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sun, 20 Dec 2020 08:24:04 +0100 Subject: [PATCH 35/46] Improve rotation documentation --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 67f2c14c12..816afc7978 100644 --- a/README.md +++ b/README.md @@ -202,6 +202,8 @@ scrcpy --lock-video-orientation 3 # 90° clockwise This affects recording orientation. +The [window may also be rotated](#rotation) independently. + ### Recording @@ -391,9 +393,9 @@ Note that _scrcpy_ manages 3 different rotations: - MOD+r requests the device to switch between portrait and landscape (the current running app may refuse, if it does support the requested orientation). - - `--lock-video-orientation` changes the mirroring orientation (the orientation - of the video sent from the device to the computer). This affects the - recording. + - [`--lock-video-orientation`](#lock-video-orientation) changes the mirroring + orientation (the orientation of the video sent from the device to the + computer). This affects the recording. - `--rotation` (or MOD+/MOD+) rotates only the window content. This affects only the display, not the recording. From a46733906acf3b59d9d1e26abef99ff4b003f5af Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Tue, 15 Dec 2020 21:50:46 +0100 Subject: [PATCH 36/46] Replace noconsole binary by a wrapper script This simplifies the build system. Refs --- Makefile.CrossWindows | 37 ++++++------------------------------- app/meson.build | 12 +----------- app/src/sys/win/command.c | 7 +------ data/scrcpy-noconsole.vbs | 1 + meson_options.txt | 1 - 5 files changed, 9 insertions(+), 49 deletions(-) create mode 100644 data/scrcpy-noconsole.vbs diff --git a/Makefile.CrossWindows b/Makefile.CrossWindows index f8a360d974..8c093a2fa5 100644 --- a/Makefile.CrossWindows +++ b/Makefile.CrossWindows @@ -11,8 +11,7 @@ .PHONY: default clean \ build-server \ prepare-deps-win32 prepare-deps-win64 \ - build-win32 build-win32-noconsole \ - build-win64 build-win64-noconsole \ + build-win32 build-win64 \ dist-win32 dist-win64 \ zip-win32 zip-win64 \ sums release @@ -21,9 +20,7 @@ GRADLE ?= ./gradlew SERVER_BUILD_DIR := build-server WIN32_BUILD_DIR := build-win32 -WIN32_NOCONSOLE_BUILD_DIR := build-win32-noconsole WIN64_BUILD_DIR := build-win64 -WIN64_NOCONSOLE_BUILD_DIR := build-win64-noconsole DIST := dist WIN32_TARGET_DIR := scrcpy-win32 @@ -39,7 +36,7 @@ release: clean zip-win32 zip-win64 sums clean: $(GRADLE) clean rm -rf "$(SERVER_BUILD_DIR)" "$(WIN32_BUILD_DIR)" "$(WIN64_BUILD_DIR)" \ - "$(WIN32_NOCONSOLE_BUILD_DIR)" "$(WIN64_NOCONSOLE_BUILD_DIR)" "$(DIST)" + "$(DIST)" build-server: [ -d "$(SERVER_BUILD_DIR)" ] || ( mkdir "$(SERVER_BUILD_DIR)" && \ @@ -60,17 +57,6 @@ build-win32: prepare-deps-win32 -Dportable=true ) ninja -C "$(WIN32_BUILD_DIR)" -build-win32-noconsole: prepare-deps-win32 - [ -d "$(WIN32_NOCONSOLE_BUILD_DIR)" ] || ( mkdir "$(WIN32_NOCONSOLE_BUILD_DIR)" && \ - meson "$(WIN32_NOCONSOLE_BUILD_DIR)" \ - --cross-file cross_win32.txt \ - --buildtype release --strip -Db_lto=true \ - -Dcrossbuild_windows=true \ - -Dcompile_server=false \ - -Dwindows_noconsole=true \ - -Dportable=true ) - ninja -C "$(WIN32_NOCONSOLE_BUILD_DIR)" - prepare-deps-win64: -$(MAKE) -C prebuilt-deps prepare-win64 @@ -84,23 +70,12 @@ build-win64: prepare-deps-win64 -Dportable=true ) ninja -C "$(WIN64_BUILD_DIR)" -build-win64-noconsole: prepare-deps-win64 - [ -d "$(WIN64_NOCONSOLE_BUILD_DIR)" ] || ( mkdir "$(WIN64_NOCONSOLE_BUILD_DIR)" && \ - meson "$(WIN64_NOCONSOLE_BUILD_DIR)" \ - --cross-file cross_win64.txt \ - --buildtype release --strip -Db_lto=true \ - -Dcrossbuild_windows=true \ - -Dcompile_server=false \ - -Dwindows_noconsole=true \ - -Dportable=true ) - ninja -C "$(WIN64_NOCONSOLE_BUILD_DIR)" - -dist-win32: build-server build-win32 build-win32-noconsole +dist-win32: build-server build-win32 mkdir -p "$(DIST)/$(WIN32_TARGET_DIR)" cp "$(SERVER_BUILD_DIR)"/server/scrcpy-server "$(DIST)/$(WIN32_TARGET_DIR)/" cp "$(WIN32_BUILD_DIR)"/app/scrcpy.exe "$(DIST)/$(WIN32_TARGET_DIR)/" - cp "$(WIN32_NOCONSOLE_BUILD_DIR)"/app/scrcpy.exe "$(DIST)/$(WIN32_TARGET_DIR)/scrcpy-noconsole.exe" cp data/scrcpy-console.bat "$(DIST)/$(WIN32_TARGET_DIR)" + cp data/scrcpy-noconsole.vbs "$(DIST)/$(WIN32_TARGET_DIR)" cp prebuilt-deps/ffmpeg-4.3.1-win32-shared/bin/avutil-56.dll "$(DIST)/$(WIN32_TARGET_DIR)/" cp prebuilt-deps/ffmpeg-4.3.1-win32-shared/bin/avcodec-58.dll "$(DIST)/$(WIN32_TARGET_DIR)/" cp prebuilt-deps/ffmpeg-4.3.1-win32-shared/bin/avformat-58.dll "$(DIST)/$(WIN32_TARGET_DIR)/" @@ -111,12 +86,12 @@ dist-win32: build-server build-win32 build-win32-noconsole cp prebuilt-deps/platform-tools/AdbWinUsbApi.dll "$(DIST)/$(WIN32_TARGET_DIR)/" cp prebuilt-deps/SDL2-2.0.12/i686-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN32_TARGET_DIR)/" -dist-win64: build-server build-win64 build-win64-noconsole +dist-win64: build-server build-win64 mkdir -p "$(DIST)/$(WIN64_TARGET_DIR)" cp "$(SERVER_BUILD_DIR)"/server/scrcpy-server "$(DIST)/$(WIN64_TARGET_DIR)/" cp "$(WIN64_BUILD_DIR)"/app/scrcpy.exe "$(DIST)/$(WIN64_TARGET_DIR)/" - cp "$(WIN64_NOCONSOLE_BUILD_DIR)"/app/scrcpy.exe "$(DIST)/$(WIN64_TARGET_DIR)/scrcpy-noconsole.exe" cp data/scrcpy-console.bat "$(DIST)/$(WIN64_TARGET_DIR)" + cp data/scrcpy-noconsole.vbs "$(DIST)/$(WIN64_TARGET_DIR)" cp prebuilt-deps/ffmpeg-4.3.1-win64-shared/bin/avutil-56.dll "$(DIST)/$(WIN64_TARGET_DIR)/" cp prebuilt-deps/ffmpeg-4.3.1-win64-shared/bin/avcodec-58.dll "$(DIST)/$(WIN64_TARGET_DIR)/" cp prebuilt-deps/ffmpeg-4.3.1-win64-shared/bin/avformat-58.dll "$(DIST)/$(WIN64_TARGET_DIR)/" diff --git a/app/meson.build b/app/meson.build index 0163dd7fab..28b9d14186 100644 --- a/app/meson.build +++ b/app/meson.build @@ -119,9 +119,6 @@ conf.set('DEFAULT_BIT_RATE', '8000000') # 8Mbps # enable High DPI support conf.set('HIDPI_SUPPORT', get_option('hidpi_support')) -# disable console on Windows -conf.set('WINDOWS_NOCONSOLE', get_option('windows_noconsole')) - # run a server debugger and wait for a client to be attached conf.set('SERVER_DEBUGGER', get_option('server_debugger')) @@ -132,18 +129,11 @@ configure_file(configuration: conf, output: 'config.h') src_dir = include_directories('src') -if get_option('windows_noconsole') - link_args = [ '-Wl,--subsystem,windows' ] -else - link_args = [] -endif - executable('scrcpy', src, dependencies: dependencies, include_directories: src_dir, install: true, - c_args: [], - link_args: link_args) + c_args: []) install_man('scrcpy.1') diff --git a/app/src/sys/win/command.c b/app/src/sys/win/command.c index 105234b034..fbc0d04cb2 100644 --- a/app/src/sys/win/command.c +++ b/app/src/sys/win/command.c @@ -39,12 +39,7 @@ cmd_execute(const char *const argv[], HANDLE *handle) { return PROCESS_ERROR_GENERIC; } -#ifdef WINDOWS_NOCONSOLE - int flags = CREATE_NO_WINDOW; -#else - int flags = 0; -#endif - if (!CreateProcessW(NULL, wide, NULL, NULL, FALSE, flags, NULL, NULL, &si, + if (!CreateProcessW(NULL, wide, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) { SDL_free(wide); *handle = NULL; diff --git a/data/scrcpy-noconsole.vbs b/data/scrcpy-noconsole.vbs new file mode 100644 index 0000000000..e11adba56d --- /dev/null +++ b/data/scrcpy-noconsole.vbs @@ -0,0 +1 @@ +CreateObject("Wscript.Shell").Run "cmd /c scrcpy.exe", 0, false diff --git a/meson_options.txt b/meson_options.txt index c213e7dd43..b962380c9a 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,7 +1,6 @@ option('compile_app', type: 'boolean', value: true, description: 'Build the client') option('compile_server', type: 'boolean', value: true, description: 'Build the server') option('crossbuild_windows', type: 'boolean', value: false, description: 'Build for Windows from Linux') -option('windows_noconsole', type: 'boolean', value: false, description: 'Disable console on Windows (pass -mwindows flag)') option('prebuilt_server', type: 'string', description: 'Path of the prebuilt server') option('portable', type: 'boolean', value: false, description: 'Use scrcpy-server from the same directory as the scrcpy executable') option('hidpi_support', type: 'boolean', value: true, description: 'Enable High DPI support') From 230afd8966d3bb4d792a41e4d87dbdd1cf4e9347 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Thu, 17 Dec 2020 14:54:04 +0100 Subject: [PATCH 37/46] Unify release makefile Before this change, release.sh built some native stuff, and Makefile.CrossWindows built the Windows releases. Instead, use a single release.make to build the whole release. It also avoids to build the server one more time. --- Makefile.CrossWindows => release.make | 38 +++++++++++++++-------- release.sh | 44 +-------------------------- 2 files changed, 27 insertions(+), 55 deletions(-) rename Makefile.CrossWindows => release.make (82%) diff --git a/Makefile.CrossWindows b/release.make similarity index 82% rename from Makefile.CrossWindows rename to release.make index 8c093a2fa5..873762b457 100644 --- a/Makefile.CrossWindows +++ b/release.make @@ -9,15 +9,17 @@ # the server to the device. .PHONY: default clean \ + test \ build-server \ prepare-deps-win32 prepare-deps-win64 \ build-win32 build-win64 \ dist-win32 dist-win64 \ zip-win32 zip-win64 \ - sums release + release GRADLE ?= ./gradlew +TEST_BUILD_DIR := build-test SERVER_BUILD_DIR := build-server WIN32_BUILD_DIR := build-win32 WIN64_BUILD_DIR := build-win64 @@ -30,19 +32,35 @@ VERSION := $(shell git describe --tags --always) WIN32_TARGET := $(WIN32_TARGET_DIR)-$(VERSION).zip WIN64_TARGET := $(WIN64_TARGET_DIR)-$(VERSION).zip -release: clean zip-win32 zip-win64 sums - @echo "Windows archives generated in $(DIST)/" +RELEASE_DIR := release-$(VERSION) + +release: clean test build-server zip-win32 zip-win64 + mkdir -p "$(RELEASE_DIR)" + cp "$(SERVER_BUILD_DIR)/server/scrcpy-server" \ + "$(RELEASE_DIR)/scrcpy-server-$(VERSION)" + cp "$(DIST)/$(WIN32_TARGET)" "$(RELEASE_DIR)" + cp "$(DIST)/$(WIN64_TARGET)" "$(RELEASE_DIR)" + cd "$(RELEASE_DIR)" && \ + sha256sum "scrcpy-server-$(VERSION)" \ + "scrcpy-win32-$(VERSION).zip" \ + "scrcpy-win64-$(VERSION).zip" > SHA256SUMS.txt + @echo "Release generated in $(RELEASE_DIR)/" clean: $(GRADLE) clean - rm -rf "$(SERVER_BUILD_DIR)" "$(WIN32_BUILD_DIR)" "$(WIN64_BUILD_DIR)" \ - "$(DIST)" + rm -rf "$(DIST)" "$(TEST_BUILD_DIR)" "$(SERVER_BUILD_DIR)" \ + "$(WIN32_BUILD_DIR)" "$(WIN64_BUILD_DIR)" + +test: + [ -d "$(TEST_BUILD_DIR)" ] || ( mkdir "$(TEST_BUILD_DIR)" && \ + meson "$(TEST_BUILD_DIR)" -Db_sanitize=address ) + ninja -C "$(TEST_BUILD_DIR)" + $(GRADLE) -p server check build-server: [ -d "$(SERVER_BUILD_DIR)" ] || ( mkdir "$(SERVER_BUILD_DIR)" && \ - meson "$(SERVER_BUILD_DIR)" \ - --buildtype release -Dcompile_app=false ) - ninja -C "$(SERVER_BUILD_DIR)" + meson "$(SERVER_BUILD_DIR)" --buildtype release -Dcompile_app=false ) + ninja -C "$(SERVER_BUILD_DIR)" prepare-deps-win32: -$(MAKE) -C prebuilt-deps prepare-win32 @@ -109,7 +127,3 @@ zip-win32: dist-win32 zip-win64: dist-win64 cd "$(DIST)/$(WIN64_TARGET_DIR)"; \ zip -r "../$(WIN64_TARGET)" . - -sums: - cd "$(DIST)"; \ - sha256sum *.zip > SHA256SUMS.txt diff --git a/release.sh b/release.sh index 4c5afbf138..a824e95898 100755 --- a/release.sh +++ b/release.sh @@ -1,44 +1,2 @@ #!/bin/bash -set -e - -# test locally -TESTDIR=build_test -rm -rf "$TESTDIR" -# run client tests with ASAN enabled -meson "$TESTDIR" -Db_sanitize=address -ninja -C"$TESTDIR" test - -# test server -GRADLE=${GRADLE:-./gradlew} -$GRADLE -p server check - -BUILDDIR=build_release -rm -rf "$BUILDDIR" -meson "$BUILDDIR" --buildtype release --strip -Db_lto=true -cd "$BUILDDIR" -ninja -cd - - -# build Windows releases -make -f Makefile.CrossWindows - -# the generated server must be the same everywhere -cmp "$BUILDDIR/server/scrcpy-server" dist/scrcpy-win32/scrcpy-server -cmp "$BUILDDIR/server/scrcpy-server" dist/scrcpy-win64/scrcpy-server - -# get version name -TAG=$(git describe --tags --always) - -# create release directory -mkdir -p "release-$TAG" -cp "$BUILDDIR/server/scrcpy-server" "release-$TAG/scrcpy-server-$TAG" -cp "dist/scrcpy-win32-$TAG.zip" "release-$TAG/" -cp "dist/scrcpy-win64-$TAG.zip" "release-$TAG/" - -# generate checksums -cd "release-$TAG" -sha256sum "scrcpy-server-$TAG" \ - "scrcpy-win32-$TAG.zip" \ - "scrcpy-win64-$TAG.zip" > SHA256SUMS.txt - -echo "Release generated in release-$TAG/" +make -f release.make From 6ab80e4ce8311ae65c09d2add5f620bdbef369a2 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Fri, 1 Jan 2021 15:49:28 +0100 Subject: [PATCH 38/46] Rename release.make to release.mk It's more standard, and benefits from syntax coloration in vi. --- release.make => release.mk | 0 release.sh | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename release.make => release.mk (100%) diff --git a/release.make b/release.mk similarity index 100% rename from release.make rename to release.mk diff --git a/release.sh b/release.sh index a824e95898..51ce2e3867 100755 --- a/release.sh +++ b/release.sh @@ -1,2 +1,2 @@ #!/bin/bash -make -f release.make +make -f release.mk From d039a7a39a80940c3eca226ce3d7d0555157fa8c Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Fri, 1 Jan 2021 15:53:56 +0100 Subject: [PATCH 39/46] Upgrade SDL (2.0.14) for Windows Include the latest version of SDL in Windows releases. --- cross_win32.txt | 2 +- cross_win64.txt | 2 +- prebuilt-deps/Makefile | 6 +++--- release.mk | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cross_win32.txt b/cross_win32.txt index bef2e5d5f8..a662ae202f 100644 --- a/cross_win32.txt +++ b/cross_win32.txt @@ -17,4 +17,4 @@ endian = 'little' [properties] prebuilt_ffmpeg_shared = 'ffmpeg-4.3.1-win32-shared' prebuilt_ffmpeg_dev = 'ffmpeg-4.3.1-win32-dev' -prebuilt_sdl2 = 'SDL2-2.0.12/i686-w64-mingw32' +prebuilt_sdl2 = 'SDL2-2.0.14/i686-w64-mingw32' diff --git a/cross_win64.txt b/cross_win64.txt index 5a3487382f..de3836d5e7 100644 --- a/cross_win64.txt +++ b/cross_win64.txt @@ -17,4 +17,4 @@ endian = 'little' [properties] prebuilt_ffmpeg_shared = 'ffmpeg-4.3.1-win64-shared' prebuilt_ffmpeg_dev = 'ffmpeg-4.3.1-win64-dev' -prebuilt_sdl2 = 'SDL2-2.0.12/x86_64-w64-mingw32' +prebuilt_sdl2 = 'SDL2-2.0.14/x86_64-w64-mingw32' diff --git a/prebuilt-deps/Makefile b/prebuilt-deps/Makefile index 535d569211..356a944e23 100644 --- a/prebuilt-deps/Makefile +++ b/prebuilt-deps/Makefile @@ -30,9 +30,9 @@ prepare-ffmpeg-dev-win64: ffmpeg-4.3.1-win64-dev prepare-sdl2: - @./prepare-dep https://libsdl.org/release/SDL2-devel-2.0.12-mingw.tar.gz \ - e614a60f797e35ef9f3f96aef3dc6a1d786de3cc7ca6216f97e435c0b6aafc46 \ - SDL2-2.0.12 + @./prepare-dep https://libsdl.org/release/SDL2-devel-2.0.14-mingw.tar.gz \ + 405eaff3eb18f2e08fe669ef9e63bc9a8710b7d343756f238619761e9b60407d \ + SDL2-2.0.14 prepare-adb: @./prepare-dep https://dl.google.com/android/repository/platform-tools_r30.0.5-windows.zip \ diff --git a/release.mk b/release.mk index 873762b457..2a02613500 100644 --- a/release.mk +++ b/release.mk @@ -102,7 +102,7 @@ dist-win32: build-server build-win32 cp prebuilt-deps/platform-tools/adb.exe "$(DIST)/$(WIN32_TARGET_DIR)/" cp prebuilt-deps/platform-tools/AdbWinApi.dll "$(DIST)/$(WIN32_TARGET_DIR)/" cp prebuilt-deps/platform-tools/AdbWinUsbApi.dll "$(DIST)/$(WIN32_TARGET_DIR)/" - cp prebuilt-deps/SDL2-2.0.12/i686-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN32_TARGET_DIR)/" + cp prebuilt-deps/SDL2-2.0.14/i686-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN32_TARGET_DIR)/" dist-win64: build-server build-win64 mkdir -p "$(DIST)/$(WIN64_TARGET_DIR)" @@ -118,7 +118,7 @@ dist-win64: build-server build-win64 cp prebuilt-deps/platform-tools/adb.exe "$(DIST)/$(WIN64_TARGET_DIR)/" cp prebuilt-deps/platform-tools/AdbWinApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/" cp prebuilt-deps/platform-tools/AdbWinUsbApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/" - cp prebuilt-deps/SDL2-2.0.12/x86_64-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN64_TARGET_DIR)/" + cp prebuilt-deps/SDL2-2.0.14/x86_64-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN64_TARGET_DIR)/" zip-win32: dist-win32 cd "$(DIST)/$(WIN32_TARGET_DIR)"; \ From 112adbba87f93d85808561301588219b833f9686 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Fri, 1 Jan 2021 17:16:44 +0100 Subject: [PATCH 40/46] Happy new year 2021! --- LICENSE | 2 +- README.id.md | 2 +- README.ko.md | 2 +- README.md | 2 +- README.pt-br.md | 2 +- README.zh-Hans.md | 2 +- README.zh-Hant.md | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/LICENSE b/LICENSE index bc4bb77d1c..b320f69930 100644 --- a/LICENSE +++ b/LICENSE @@ -188,7 +188,7 @@ identification within third-party archives. Copyright (C) 2018 Genymobile - Copyright (C) 2018-2020 Romain Vimont + Copyright (C) 2018-2021 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.id.md b/README.id.md index 5af566630c..a2cfa3d526 100644 --- a/README.id.md +++ b/README.id.md @@ -675,7 +675,7 @@ Baca [halaman pengembang]. ## Lisensi Copyright (C) 2018 Genymobile - Copyright (C) 2018-2020 Romain Vimont + Copyright (C) 2018-2021 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.ko.md b/README.ko.md index 4e6d8fc58b..58b44dfe68 100644 --- a/README.ko.md +++ b/README.ko.md @@ -477,7 +477,7 @@ _²화면이 꺼진 상태에서 우클릭 시 다시 켜지며, 그 외의 상 ## 라이선스 Copyright (C) 2018 Genymobile - Copyright (C) 2018-2020 Romain Vimont + Copyright (C) 2018-2021 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 816afc7978..2e31b6d557 100644 --- a/README.md +++ b/README.md @@ -737,7 +737,7 @@ Read the [developers page]. ## Licence Copyright (C) 2018 Genymobile - Copyright (C) 2018-2020 Romain Vimont + Copyright (C) 2018-2021 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.pt-br.md b/README.pt-br.md index 654f62cb29..4f2122a66a 100644 --- a/README.pt-br.md +++ b/README.pt-br.md @@ -508,7 +508,7 @@ Leia a [developers page]. ## Licença Copyright (C) 2018 Genymobile - Copyright (C) 2018-2020 Romain Vimont + Copyright (C) 2018-2021 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.zh-Hans.md b/README.zh-Hans.md index 85e178d697..3774c5d97b 100644 --- a/README.zh-Hans.md +++ b/README.zh-Hans.md @@ -703,7 +703,7 @@ _³需要安卓版本 Android >= 7。_ ## 许可协议 Copyright (C) 2018 Genymobile - Copyright (C) 2018-2020 Romain Vimont + Copyright (C) 2018-2021 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.zh-Hant.md b/README.zh-Hant.md index 82eb1d9b8d..b4dc69ec25 100644 --- a/README.zh-Hant.md +++ b/README.zh-Hant.md @@ -682,7 +682,7 @@ _³只支援 Android 7+。_ ## Licence Copyright (C) 2018 Genymobile - Copyright (C) 2018-2020 Romain Vimont + Copyright (C) 2018-2021 Romain Vimont Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From 3ba51211d6bb21fa02c5d05079f15aad089de7ca Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Fri, 1 Jan 2021 17:31:07 +0100 Subject: [PATCH 41/46] Mention how to add default arguments on Windows Mention that it is possible to add default arguments by editing the wrapper scripts. --- FAQ.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/FAQ.md b/FAQ.md index 44cc06d8d8..9801f91cfc 100644 --- a/FAQ.md +++ b/FAQ.md @@ -233,4 +233,7 @@ scrcpy --prefer-text --turn-screen-off --stay-awake Then just double-click on that file. +You could also edit (a copy of) `scrcpy-console.bat` or `scrcpy-noconsole.vbs` +to add some arguments. + [show file extensions]: https://www.howtogeek.com/205086/beginner-how-to-make-windows-show-file-extensions/ From 90f835663004d9dc4969a76a1ef2641af9b02633 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Fri, 1 Jan 2021 12:26:44 +0100 Subject: [PATCH 42/46] Interrupt device threads on stop The (non-daemon) threads were not interrupted on video stream stopped, leaving the server process alive. Interrupt them to wake up their blocking call so that they terminate properly. Refs #1992 --- .../java/com/genymobile/scrcpy/Server.java | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/server/src/main/java/com/genymobile/scrcpy/Server.java b/server/src/main/java/com/genymobile/scrcpy/Server.java index f501d3d89a..f58e38674d 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Server.java +++ b/server/src/main/java/com/genymobile/scrcpy/Server.java @@ -58,12 +58,14 @@ private static void scrcpy(Options options) throws IOException { ScreenEncoder screenEncoder = new ScreenEncoder(options.getSendFrameMeta(), options.getBitRate(), options.getMaxFps(), codecOptions, options.getEncoderName()); + Thread controllerThread = null; + Thread deviceMessageSenderThread = null; if (options.getControl()) { final Controller controller = new Controller(device, connection); // asynchronous - startController(controller); - startDeviceMessageSender(controller.getSender()); + controllerThread = startController(controller); + deviceMessageSenderThread = startDeviceMessageSender(controller.getSender()); device.setClipboardListener(new Device.ClipboardListener() { @Override @@ -79,12 +81,19 @@ public void onClipboardTextChanged(String text) { } catch (IOException e) { // this is expected on close Ln.d("Screen streaming stopped"); + } finally { + if (controllerThread != null) { + controllerThread.interrupt(); + } + if (deviceMessageSenderThread != null) { + deviceMessageSenderThread.interrupt(); + } } } } - private static void startController(final Controller controller) { - new Thread(new Runnable() { + private static Thread startController(final Controller controller) { + Thread thread = new Thread(new Runnable() { @Override public void run() { try { @@ -94,11 +103,13 @@ public void run() { Ln.d("Controller stopped"); } } - }).start(); + }); + thread.start(); + return thread; } - private static void startDeviceMessageSender(final DeviceMessageSender sender) { - new Thread(new Runnable() { + private static Thread startDeviceMessageSender(final DeviceMessageSender sender) { + Thread thread = new Thread(new Runnable() { @Override public void run() { try { @@ -108,7 +119,9 @@ public void run() { Ln.d("Device message sender stopped"); } } - }).start(); + }); + thread.start(); + return thread; } private static Options createOptions(String... args) { From 83910d3b9c961aa30eec4cf01aee7d38fb050fae Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Fri, 1 Jan 2021 16:34:47 +0100 Subject: [PATCH 43/46] Initialize server struct dynamically This will allow to add mutex/cond fields. --- app/src/scrcpy.c | 35 +++++++++++++++++++++-------------- app/src/server.c | 21 +++++++++++++++++++-- app/src/server.h | 19 +------------------ 3 files changed, 41 insertions(+), 34 deletions(-) diff --git a/app/src/scrcpy.c b/app/src/scrcpy.c index a543e11c77..4c502ff683 100644 --- a/app/src/scrcpy.c +++ b/app/src/scrcpy.c @@ -34,7 +34,7 @@ #include "util/log.h" #include "util/net.h" -static struct server server = SERVER_INITIALIZER; +static struct server server; static struct screen screen = SCREEN_INITIALIZER; static struct fps_counter fps_counter; static struct video_buffer video_buffer; @@ -304,6 +304,19 @@ av_log_callback(void *avcl, int level, const char *fmt, va_list vl) { bool scrcpy(const struct scrcpy_options *options) { + if (!server_init(&server)) { + return false; + } + + bool server_started = false; + bool fps_counter_initialized = false; + bool video_buffer_initialized = false; + bool file_handler_initialized = false; + bool recorder_initialized = false; + bool stream_started = false; + bool controller_initialized = false; + bool controller_started = false; + bool record = !!options->record_filename; struct server_params params = { .log_level = options->log_level, @@ -322,18 +335,10 @@ scrcpy(const struct scrcpy_options *options) { .force_adb_forward = options->force_adb_forward, }; if (!server_start(&server, options->serial, ¶ms)) { - return false; + goto end; } - bool ret = false; - - bool fps_counter_initialized = false; - bool video_buffer_initialized = false; - bool file_handler_initialized = false; - bool recorder_initialized = false; - bool stream_started = false; - bool controller_initialized = false; - bool controller_started = false; + server_started = true; if (!sdl_init_and_configure(options->display, options->render_driver, options->disable_screensaver)) { @@ -444,7 +449,7 @@ scrcpy(const struct scrcpy_options *options) { input_manager_init(&input_manager, options); - ret = event_loop(options); + bool ret = event_loop(options); LOGD("quit..."); screen_destroy(&screen); @@ -465,8 +470,10 @@ scrcpy(const struct scrcpy_options *options) { fps_counter_interrupt(&fps_counter); } - // shutdown the sockets and kill the server - server_stop(&server); + if (server_started) { + // shutdown the sockets and kill the server + server_stop(&server); + } // now that the sockets are shutdown, the stream and controller are // interrupted, we can join them diff --git a/app/src/server.c b/app/src/server.c index 9267356b28..42e633affb 100644 --- a/app/src/server.c +++ b/app/src/server.c @@ -353,9 +353,26 @@ close_socket(socket_t socket) { } } -void +bool server_init(struct server *server) { - *server = (struct server) SERVER_INITIALIZER; + server->serial = NULL; + server->process = PROCESS_NONE; + server->wait_server_thread = NULL; + atomic_flag_clear_explicit(&server->server_socket_closed, + memory_order_relaxed); + + server->server_socket = INVALID_SOCKET; + server->video_socket = INVALID_SOCKET; + server->control_socket = INVALID_SOCKET; + + server->port_range.first = 0; + server->port_range.last = 0; + server->local_port = 0; + + server->tunnel_enabled = false; + server->tunnel_forward = false; + + return true; } static int diff --git a/app/src/server.h b/app/src/server.h index 30ce09bc58..823db0ea43 100644 --- a/app/src/server.h +++ b/app/src/server.h @@ -27,23 +27,6 @@ struct server { bool tunnel_forward; // use "adb forward" instead of "adb reverse" }; -#define SERVER_INITIALIZER { \ - .serial = NULL, \ - .process = PROCESS_NONE, \ - .wait_server_thread = NULL, \ - .server_socket_closed = ATOMIC_FLAG_INIT, \ - .server_socket = INVALID_SOCKET, \ - .video_socket = INVALID_SOCKET, \ - .control_socket = INVALID_SOCKET, \ - .port_range = { \ - .first = 0, \ - .last = 0, \ - }, \ - .local_port = 0, \ - .tunnel_enabled = false, \ - .tunnel_forward = false, \ -} - struct server_params { enum sc_log_level log_level; const char *crop; @@ -62,7 +45,7 @@ struct server_params { }; // init default values -void +bool server_init(struct server *server); // push, enable tunnel et start the server From 05e8c1a3c5e955d2941d882e5fe784e6695e3ec4 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Fri, 1 Jan 2021 23:39:29 +0100 Subject: [PATCH 44/46] Call CloseHandle() after wait on Windows TerminateProcess() is "equivalent" to kill(), while WaitForSingleObject() is "equivalent" to waitpid(), so the handle must be closed after WaitForSingleObject(). --- app/src/sys/win/command.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/sys/win/command.c b/app/src/sys/win/command.c index fbc0d04cb2..7b48330060 100644 --- a/app/src/sys/win/command.c +++ b/app/src/sys/win/command.c @@ -56,7 +56,7 @@ cmd_execute(const char *const argv[], HANDLE *handle) { bool cmd_terminate(HANDLE handle) { - return TerminateProcess(handle, 1) && CloseHandle(handle); + return TerminateProcess(handle, 1); } bool @@ -70,6 +70,7 @@ cmd_simple_wait(HANDLE handle, DWORD *exit_code) { if (exit_code) { *exit_code = code; } + CloseHandle(handle); return !code; } From 10b749e27d34f91d04ff165134fda23e6ec2cc3b Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Fri, 1 Jan 2021 12:41:25 +0100 Subject: [PATCH 45/46] Kill the server only after a small delay Let the server terminate properly once all the sockets are closed. If it does not terminate (this can happen if the device is asleep), then kill it. Note: since the server process termination is detected by a flag set after waitpid() returns, there is a small chance that the process terminates (and the PID assigned to a new process) before the flag is set but before the kill() call. This race condition already existed before this commit. Fixes #1992 --- app/src/server.c | 46 ++++++++++++++++++++++++++++++++++++++++++++-- app/src/server.h | 5 +++++ 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/app/src/server.c b/app/src/server.c index 42e633affb..cac7b367c5 100644 --- a/app/src/server.c +++ b/app/src/server.c @@ -11,6 +11,7 @@ #include "config.h" #include "command.h" +#include "util/lock.h" #include "util/log.h" #include "util/net.h" #include "util/str_util.h" @@ -361,6 +362,19 @@ server_init(struct server *server) { atomic_flag_clear_explicit(&server->server_socket_closed, memory_order_relaxed); + server->mutex = SDL_CreateMutex(); + if (!server->mutex) { + return false; + } + + server->process_terminated_cond = SDL_CreateCond(); + if (!server->process_terminated_cond) { + SDL_DestroyMutex(server->mutex); + return false; + } + + server->process_terminated = false; + server->server_socket = INVALID_SOCKET; server->video_socket = INVALID_SOCKET; server->control_socket = INVALID_SOCKET; @@ -379,6 +393,12 @@ static int run_wait_server(void *data) { struct server *server = data; cmd_simple_wait(server->process, NULL); // ignore exit code + + mutex_lock(server->mutex); + server->process_terminated = true; + cond_signal(server->process_terminated_cond); + mutex_unlock(server->mutex); + // no need for synchronization, server_socket is initialized before this // thread was created if (server->server_socket != INVALID_SOCKET @@ -510,17 +530,39 @@ server_stop(struct server *server) { assert(server->process != PROCESS_NONE); - cmd_terminate(server->process); - if (server->tunnel_enabled) { // ignore failure disable_tunnel(server); } + // Give some delay for the server to terminate properly + mutex_lock(server->mutex); + int r = 0; + if (!server->process_terminated) { +#define WATCHDOG_DELAY_MS 1000 + r = cond_wait_timeout(server->process_terminated_cond, + server->mutex, + WATCHDOG_DELAY_MS); + } + mutex_unlock(server->mutex); + + // After this delay, kill the server if it's not dead already. + // On some devices, closing the sockets is not sufficient to wake up the + // blocking calls while the device is asleep. + if (r == SDL_MUTEX_TIMEDOUT) { + // FIXME There is a race condition here: there is a small chance that + // the process is already terminated, and the PID assigned to a new + // process. + LOGW("Killing the server..."); + cmd_terminate(server->process); + } + SDL_WaitThread(server->wait_server_thread, NULL); } void server_destroy(struct server *server) { SDL_free(server->serial); + SDL_DestroyCond(server->process_terminated_cond); + SDL_DestroyMutex(server->mutex); } diff --git a/app/src/server.h b/app/src/server.h index 823db0ea43..9b558aee92 100644 --- a/app/src/server.h +++ b/app/src/server.h @@ -18,6 +18,11 @@ struct server { process_t process; SDL_Thread *wait_server_thread; atomic_flag server_socket_closed; + + SDL_mutex *mutex; + SDL_cond *process_terminated_cond; + bool process_terminated; + socket_t server_socket; // only used if !tunnel_forward socket_t video_socket; socket_t control_socket; From f682b87ba5ed6a5126cc23dff905218b177a2436 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sat, 2 Jan 2021 00:53:32 +0100 Subject: [PATCH 46/46] Bump version to 1.17 --- meson.build | 2 +- server/build.gradle | 4 ++-- server/build_without_gradle.sh | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/meson.build b/meson.build index 11964cf60d..230f8d21ce 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('scrcpy', 'c', - version: '1.16', + version: '1.17', meson_version: '>= 0.48', default_options: [ 'c_std=c11', diff --git a/server/build.gradle b/server/build.gradle index 94a492c68e..08ce2d27db 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -6,8 +6,8 @@ android { applicationId "com.genymobile.scrcpy" minSdkVersion 21 targetSdkVersion 30 - versionCode 19 - versionName "1.16" + versionCode 20 + versionName "1.17" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { diff --git a/server/build_without_gradle.sh b/server/build_without_gradle.sh index e03939a505..9afc5ba779 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=1.16 +SCRCPY_VERSION_NAME=1.17 PLATFORM=${ANDROID_PLATFORM:-30} BUILD_TOOLS=${ANDROID_BUILD_TOOLS:-30.0.0}