Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restart capture when screen resolution changes #4469

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 36 additions & 16 deletions server/src/main/java/com/genymobile/scrcpy/Device.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -33,8 +36,8 @@ public final class Device {
public static final int LOCK_VIDEO_ORIENTATION_UNLOCKED = -1;
public static final int LOCK_VIDEO_ORIENTATION_INITIAL = -2;

public interface RotationListener {
void onRotationChanged(int rotation);
public interface DisplayChangeListener {
void onDisplayChanged();
}

public interface FoldListener {
Expand All @@ -45,13 +48,13 @@ 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 DisplayChangeListener displayChangeListener;
private FoldListener foldListener;
private ClipboardListener clipboardListener;
private final AtomicBoolean isSettingClipboard = new AtomicBoolean();
Expand All @@ -78,27 +81,44 @@ 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();

ServiceManager.getWindowManager().registerRotationWatcher(new IRotationWatcher.Stub() {
HandlerThread displayListenerThread = new HandlerThread("DisplayListenerThread");
displayListenerThread.start();

Handler displayListenerHandler = new Handler(displayListenerThread.getLooper());
ServiceManager.getDisplayManager().registerDisplayListener(new DisplayManager.DisplayListener() {
@Override
public void onRotationChanged(int rotation) {
synchronized (Device.this) {
screenInfo = screenInfo.withDeviceRotation(rotation);
public void onDisplayAdded(int displayId) {
// nothing to do
}

// notify
if (rotationListener != null) {
rotationListener.onRotationChanged(rotation);
}
@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 (displayChangeListener != null) {
displayChangeListener.onDisplayChanged();
}
}
}, displayId);
}, displayListenerHandler, DisplayManager.EVENT_FLAG_DISPLAY_CHANGED);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ServiceManager.getWindowManager().registerDisplayFoldListener(new IDisplayFoldListener.Stub() {
Expand Down Expand Up @@ -254,8 +274,8 @@ public static boolean isScreenOn() {
return ServiceManager.getPowerManager().isScreenOn();
}

public synchronized void setRotationListener(RotationListener rotationListener) {
this.rotationListener = rotationListener;
public synchronized void setDisplayChangeListener(DisplayChangeListener displayChangeListener) {
this.displayChangeListener = displayChangeListener;
}

public synchronized void setFoldListener(FoldListener foldlistener) {
Expand Down
8 changes: 4 additions & 4 deletions server/src/main/java/com/genymobile/scrcpy/ScreenCapture.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import android.os.IBinder;
import android.view.Surface;

public class ScreenCapture extends SurfaceCapture implements Device.RotationListener, Device.FoldListener {
public class ScreenCapture extends SurfaceCapture implements Device.DisplayChangeListener, Device.FoldListener {

private final Device device;
private IBinder display;
Expand All @@ -18,7 +18,7 @@ public ScreenCapture(Device device) {

@Override
public void init() {
device.setRotationListener(this);
device.setDisplayChangeListener(this);
device.setFoldListener(this);
}

Expand All @@ -41,7 +41,7 @@ public void start(Surface surface) {

@Override
public void release() {
device.setRotationListener(null);
device.setDisplayChangeListener(null);
device.setFoldListener(null);
if (display != null) {
SurfaceControl.destroyDisplay(display);
Expand All @@ -65,7 +65,7 @@ public void onFoldChanged(int displayId, boolean folded) {
}

@Override
public void onRotationChanged(int rotation) {
public void onDisplayChanged() {
requestReset();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,22 @@
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;

public final class DisplayManager {
/**
* Event type for when a display is changed.
*
* @see #registerDisplayListener(DisplayListener, Handler, long)
*/
public static final long EVENT_FLAG_DISPLAY_CHANGED = 1L << 2;

private final Object manager; // instance of hidden class android.hardware.display.DisplayManagerGlobal

public DisplayManager(Object manager) {
Expand Down Expand Up @@ -94,4 +103,69 @@ public int[] getDisplayIds() {
throw new AssertionError(e);
}
}

public void registerDisplayListener(DisplayListener listener, Handler handler, long eventMask) {
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;
});
try {
manager
.getClass()
.getMethod("registerDisplayListener", displayListenerClass, Handler.class, long.class)
.invoke(manager, displayListenerProxy, handler, eventMask);
} catch (NoSuchMethodException e) {
manager
.getClass()
.getMethod("registerDisplayListener", displayListenerClass, Handler.class)
.invoke(manager, displayListenerProxy, handler);
}
} catch (Exception e) {
// Rotation and screen size won't be updated, not a fatal error
Ln.w("Could not register display listener", 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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,22 +95,6 @@ public void thawRotation() {
}
}

public void registerRotationWatcher(IRotationWatcher rotationWatcher, int displayId) {
try {
Class<?> cls = manager.getClass();
try {
// display parameter added since this commit:
// https://android.googlesource.com/platform/frameworks/base/+/35fa3c26adcb5f6577849fd0df5228b1f67cf2c6%5E%21/#F1
cls.getMethod("watchRotation", IRotationWatcher.class, int.class).invoke(manager, rotationWatcher, displayId);
} catch (NoSuchMethodException e) {
// old version
cls.getMethod("watchRotation", IRotationWatcher.class).invoke(manager, rotationWatcher);
}
} catch (Exception e) {
Ln.e("Could not register rotation watcher", e);
}
}

@TargetApi(29)
public void registerDisplayFoldListener(IDisplayFoldListener foldListener) {
try {
Expand Down