From e90abc8c8aa7aa0eb7089518496912f5aa0c81cc Mon Sep 17 00:00:00 2001 From: Simon Chan <1330321+yume-chan@users.noreply.github.com> Date: Mon, 27 Nov 2023 17:11:08 +0800 Subject: [PATCH] Restart capture when screen resolution changes --- .../java/com/genymobile/scrcpy/Device.java | 38 +++++++++++- .../scrcpy/wrappers/DisplayManager.java | 59 +++++++++++++++++++ 2 files changed, 95 insertions(+), 2 deletions(-) diff --git a/server/src/main/java/com/genymobile/scrcpy/Device.java b/server/src/main/java/com/genymobile/scrcpy/Device.java index b51ad8d377..756af86e3f 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Device.java +++ b/server/src/main/java/com/genymobile/scrcpy/Device.java @@ -2,6 +2,7 @@ import com.genymobile.scrcpy.wrappers.ClipboardManager; import com.genymobile.scrcpy.wrappers.DisplayControl; +import com.genymobile.scrcpy.wrappers.DisplayManager; import com.genymobile.scrcpy.wrappers.InputManager; import com.genymobile.scrcpy.wrappers.ServiceManager; import com.genymobile.scrcpy.wrappers.SurfaceControl; @@ -10,6 +11,8 @@ import android.content.IOnPrimaryClipChangedListener; import android.graphics.Rect; import android.os.Build; +import android.os.Handler; +import android.os.HandlerThread; import android.os.IBinder; import android.os.SystemClock; import android.view.IDisplayFoldListener; @@ -45,11 +48,11 @@ public interface ClipboardListener { void onClipboardTextChanged(String text); } - private final Size deviceSize; private final Rect crop; private int maxSize; private final int lockVideoOrientation; + private Size deviceSize; private ScreenInfo screenInfo; private RotationListener rotationListener; private FoldListener foldListener; @@ -78,14 +81,45 @@ public Device(Options options) throws ConfigurationException { int displayInfoFlags = displayInfo.getFlags(); - deviceSize = displayInfo.getSize(); crop = options.getCrop(); maxSize = options.getMaxSize(); lockVideoOrientation = options.getLockVideoOrientation(); + deviceSize = displayInfo.getSize(); screenInfo = ScreenInfo.computeScreenInfo(displayInfo.getRotation(), deviceSize, crop, maxSize, lockVideoOrientation); layerStack = displayInfo.getLayerStack(); + HandlerThread displayListenerThread = new HandlerThread("DisplayListenerThread"); + displayListenerThread.start(); + + Handler displayListenerHandler = new Handler(displayListenerThread.getLooper()); + ServiceManager.getDisplayManager().registerDisplayListener(new DisplayManager.DisplayListener() { + @Override + public void onDisplayAdded(int displayId) { + // nothing to do + } + + @Override + public void onDisplayRemoved(int displayId) { + // nothing to do + } + + @Override + public void onDisplayChanged(int displayId) { + if (Device.this.displayId != displayId) { + return; + } + + DisplayInfo displayInfo = ServiceManager.getDisplayManager().getDisplayInfo(displayId); + deviceSize = displayInfo.getSize(); + screenInfo = ScreenInfo.computeScreenInfo(displayInfo.getRotation(), deviceSize, crop, maxSize, lockVideoOrientation); + + if (foldListener != null) { + foldListener.onFoldChanged(displayId, false); + } + } + }, displayListenerHandler); + ServiceManager.getWindowManager().registerRotationWatcher(new IRotationWatcher.Stub() { @Override public void onRotationChanged(int rotation) { diff --git a/server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayManager.java b/server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayManager.java index 17b9ae4ddb..17b79b4dab 100644 --- a/server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayManager.java +++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayManager.java @@ -5,9 +5,11 @@ import com.genymobile.scrcpy.Ln; import com.genymobile.scrcpy.Size; +import android.os.Handler; import android.view.Display; import java.lang.reflect.Field; +import java.lang.reflect.Proxy; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -94,4 +96,61 @@ public int[] getDisplayIds() { throw new AssertionError(e); } } + + public void registerDisplayListener(DisplayListener listener, Handler handler) { + try { + Class displayListenerClass = Class.forName("android.hardware.display.DisplayManager$DisplayListener"); + Object displayListenerProxy = Proxy.newProxyInstance( + ClassLoader.getSystemClassLoader(), + new Class[]{ displayListenerClass }, + (proxy, method, args) -> { + switch (method.getName()) { + case "onDisplayAdded": + listener.onDisplayAdded((int) args[0]); + break; + case "onDisplayRemoved": + listener.onDisplayRemoved((int) args[0]); + break; + case "onDisplayChanged": + listener.onDisplayChanged((int) args[0]); + break; + default: + throw new AssertionError("Unexpected method: " + method.getName()); + } + return null; + }); + manager + .getClass() + .getMethod("registerDisplayListener", displayListenerClass, Handler.class) + .invoke(manager, displayListenerProxy, handler); + } catch (Exception e) { + throw new AssertionError(e); + } + } + + public interface DisplayListener { + /** + * Called whenever a logical display has been added to the system. + * Use {@link DisplayManager#getDisplay} to get more information about + * the display. + * + * @param displayId The id of the logical display that was added. + */ + void onDisplayAdded(int displayId); + + /** + * Called whenever a logical display has been removed from the system. + * + * @param displayId The id of the logical display that was removed. + */ + void onDisplayRemoved(int displayId); + + /** + * Called whenever the properties of a logical {@link android.view.Display}, + * such as size and density, have changed. + * + * @param displayId The id of the logical display that changed. + */ + void onDisplayChanged(int displayId); + } }