mirror of
https://github.com/Genymobile/scrcpy.git
synced 2026-04-21 01:33:36 +00:00
Reset capture on rotation (fix square displays)
`DisplayMonitor` previously only triggered a capture reset when the
display size changed. In most cases, rotation also changes dimensions,
so the behavior was correct… except for square displays where width and
height remain unchanged.
However, rotation still requires a capture reset even when dimensions do
not change, to ensure the orientation filter is applied so virtual
displays are rendered correctly.
To reproduce the issue:
scrcpy --new-display=600x600 --start-app=com.android.settings
Then press Alt+r to rotate the Settings app.
PR #6770 <https://github.com/Genymobile/scrcpy/pull/6770>
This commit is contained in:
parent
b38ba2f687
commit
03878083fb
4 changed files with 94 additions and 40 deletions
|
|
@ -2,7 +2,6 @@ package com.genymobile.scrcpy.display;
|
|||
|
||||
import com.genymobile.scrcpy.AndroidVersions;
|
||||
import com.genymobile.scrcpy.device.Device;
|
||||
import com.genymobile.scrcpy.model.Size;
|
||||
import com.genymobile.scrcpy.util.Ln;
|
||||
import com.genymobile.scrcpy.wrappers.DisplayManager;
|
||||
import com.genymobile.scrcpy.wrappers.DisplayWindowListener;
|
||||
|
|
@ -14,10 +13,10 @@ import android.os.Handler;
|
|||
import android.os.HandlerThread;
|
||||
import android.view.IDisplayWindowListener;
|
||||
|
||||
public class DisplaySizeMonitor {
|
||||
public class DisplayMonitor {
|
||||
|
||||
public interface Listener {
|
||||
void onDisplaySizeChanged();
|
||||
void onDisplayPropertiesChanged();
|
||||
}
|
||||
|
||||
// On Android 14, DisplayListener may be broken (it never sends events). This is fixed in recent Android 14 upgrades, but we can't really
|
||||
|
|
@ -33,7 +32,7 @@ public class DisplaySizeMonitor {
|
|||
|
||||
private int displayId = Device.DISPLAY_ID_NONE;
|
||||
|
||||
private Size sessionDisplaySize;
|
||||
private DisplayProperties props;
|
||||
|
||||
private Listener listener;
|
||||
|
||||
|
|
@ -51,11 +50,11 @@ public class DisplaySizeMonitor {
|
|||
Handler handler = new Handler(handlerThread.getLooper());
|
||||
displayListenerHandle = ServiceManager.getDisplayManager().registerDisplayListener(eventDisplayId -> {
|
||||
if (Ln.isEnabled(Ln.Level.VERBOSE)) {
|
||||
Ln.v("DisplaySizeMonitor: onDisplayChanged(" + eventDisplayId + ")");
|
||||
Ln.v("DisplayMonitor: onDisplayChanged(" + eventDisplayId + ")");
|
||||
}
|
||||
|
||||
if (eventDisplayId == displayId) {
|
||||
checkDisplaySizeChanged();
|
||||
checkDisplayPropertiesChanged();
|
||||
}
|
||||
}, handler);
|
||||
} else {
|
||||
|
|
@ -63,11 +62,11 @@ public class DisplaySizeMonitor {
|
|||
@Override
|
||||
public void onDisplayConfigurationChanged(int eventDisplayId, Configuration newConfig) {
|
||||
if (Ln.isEnabled(Ln.Level.VERBOSE)) {
|
||||
Ln.v("DisplaySizeMonitor: onDisplayConfigurationChanged(" + eventDisplayId + ")");
|
||||
Ln.v("DisplayMonitor: onDisplayConfigurationChanged(" + eventDisplayId + ")");
|
||||
}
|
||||
|
||||
if (eventDisplayId == displayId) {
|
||||
checkDisplaySizeChanged();
|
||||
checkDisplayPropertiesChanged();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -97,38 +96,38 @@ public class DisplaySizeMonitor {
|
|||
}
|
||||
}
|
||||
|
||||
private synchronized Size getAndSetSessionDisplaySize(Size sessionDisplaySize) {
|
||||
Size oldDisplaySize = this.sessionDisplaySize;
|
||||
this.sessionDisplaySize = sessionDisplaySize;
|
||||
return oldDisplaySize;
|
||||
private synchronized DisplayProperties getAndSetDisplayProperties(DisplayProperties props) {
|
||||
DisplayProperties oldProps = this.props;
|
||||
this.props = props;
|
||||
return oldProps;
|
||||
}
|
||||
|
||||
public synchronized void setSessionDisplaySize(Size sessionDisplaySize) {
|
||||
this.sessionDisplaySize = sessionDisplaySize;
|
||||
public synchronized void setSessionDisplayProperties(DisplayProperties props) {
|
||||
this.props = props;
|
||||
}
|
||||
|
||||
private void checkDisplaySizeChanged() {
|
||||
private void checkDisplayPropertiesChanged() {
|
||||
DisplayInfo di = ServiceManager.getDisplayManager().getDisplayInfo(displayId);
|
||||
if (di == null) {
|
||||
Ln.w("DisplayInfo for " + displayId + " cannot be retrieved");
|
||||
// We can't compare with the current size, so reset unconditionally
|
||||
Size oldDisplaySize = getAndSetSessionDisplaySize(null); // exchange with synchronization
|
||||
// We can't compare with the current properties, so reset unconditionally
|
||||
DisplayProperties oldProps = getAndSetDisplayProperties(null); // exchange with synchronization
|
||||
if (Ln.isEnabled(Ln.Level.VERBOSE)) {
|
||||
Ln.v("DisplaySizeMonitor: requestReset(): " + oldDisplaySize + " -> (unknown)");
|
||||
Ln.v("DisplayMonitor: requestReset(): " + oldProps + " -> (unknown)");
|
||||
}
|
||||
listener.onDisplaySizeChanged();
|
||||
listener.onDisplayPropertiesChanged();
|
||||
} else {
|
||||
Size size = di.getSize();
|
||||
DisplayProperties newProps = new DisplayProperties(di.getSize(), di.getRotation());
|
||||
|
||||
Size oldDisplaySize = getAndSetSessionDisplaySize(size); // exchange with synchronization
|
||||
if (!size.equals(oldDisplaySize)) {
|
||||
// Reset only if the size is different
|
||||
DisplayProperties oldProps = getAndSetDisplayProperties(newProps); // exchange with synchronization
|
||||
if (!newProps.equals(oldProps)) {
|
||||
// Reset only if the properties are different
|
||||
if (Ln.isEnabled(Ln.Level.VERBOSE)) {
|
||||
Ln.v("DisplaySizeMonitor: requestReset(): " + oldDisplaySize + " -> " + size);
|
||||
Ln.v("DisplayMonitor: requestReset(): " + oldProps + " -> " + newProps);
|
||||
}
|
||||
listener.onDisplaySizeChanged();
|
||||
listener.onDisplayPropertiesChanged();
|
||||
} else if (Ln.isEnabled(Ln.Level.VERBOSE)) {
|
||||
Ln.v("DisplaySizeMonitor: Size not changed (" + size + "): do not requestReset()");
|
||||
Ln.v("DisplayMonitor: DisplayProperties not changed (" + newProps + "): do not requestReset()");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
package com.genymobile.scrcpy.display;
|
||||
|
||||
import com.genymobile.scrcpy.model.Size;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public final class DisplayProperties {
|
||||
private Size size;
|
||||
private int rotation;
|
||||
|
||||
public DisplayProperties(Size size, int rotation) {
|
||||
assert size != null;
|
||||
assert rotation >= 0 && rotation < 4;
|
||||
this.size = size;
|
||||
this.rotation = rotation;
|
||||
}
|
||||
|
||||
public Size getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public void setSize(Size size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public int getRotation() {
|
||||
return rotation;
|
||||
}
|
||||
|
||||
public void setRotation(int rotation) {
|
||||
this.rotation = rotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
DisplayProperties that = (DisplayProperties) o;
|
||||
return rotation == that.rotation && Objects.equals(size, that.size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(size, rotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return size + " [rotation=" + rotation + "]";
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,8 @@ import com.genymobile.scrcpy.AndroidVersions;
|
|||
import com.genymobile.scrcpy.Options;
|
||||
import com.genymobile.scrcpy.control.PositionMapper;
|
||||
import com.genymobile.scrcpy.display.DisplayInfo;
|
||||
import com.genymobile.scrcpy.display.DisplaySizeMonitor;
|
||||
import com.genymobile.scrcpy.display.DisplayMonitor;
|
||||
import com.genymobile.scrcpy.display.DisplayProperties;
|
||||
import com.genymobile.scrcpy.model.NewDisplay;
|
||||
import com.genymobile.scrcpy.model.Orientation;
|
||||
import com.genymobile.scrcpy.model.Size;
|
||||
|
|
@ -42,7 +43,7 @@ public class NewDisplayCapture extends SurfaceCapture {
|
|||
private final VirtualDisplayListener vdListener;
|
||||
private final NewDisplay newDisplay;
|
||||
|
||||
private final DisplaySizeMonitor displaySizeMonitor = new DisplaySizeMonitor();
|
||||
private final DisplayMonitor displayMonitor = new DisplayMonitor();
|
||||
|
||||
private AffineMatrix displayTransform;
|
||||
private AffineMatrix eventTransform;
|
||||
|
|
@ -112,8 +113,8 @@ public class NewDisplayCapture extends SurfaceCapture {
|
|||
}
|
||||
|
||||
displayRotation = 0;
|
||||
// Set the current display size to avoid an unnecessary call to invalidate()
|
||||
displaySizeMonitor.setSessionDisplaySize(displaySize);
|
||||
// Set the current display properties to avoid an unnecessary call to invalidate()
|
||||
displayMonitor.setSessionDisplayProperties(new DisplayProperties(displaySize, displayRotation));
|
||||
} else {
|
||||
DisplayInfo displayInfo = ServiceManager.getDisplayManager().getDisplayInfo(virtualDisplay.getDisplay().getDisplayId());
|
||||
displaySize = displayInfo.getSize();
|
||||
|
|
@ -194,7 +195,7 @@ public class NewDisplayCapture extends SurfaceCapture {
|
|||
ServiceManager.getWindowManager().setDisplayImePolicy(virtualDisplayId, displayImePolicy);
|
||||
}
|
||||
|
||||
displaySizeMonitor.start(virtualDisplayId, this::invalidate);
|
||||
displayMonitor.start(virtualDisplayId, this::invalidate);
|
||||
} catch (Exception e) {
|
||||
Ln.e("Could not create display", e);
|
||||
throw new AssertionError("Could not create display");
|
||||
|
|
@ -232,7 +233,7 @@ public class NewDisplayCapture extends SurfaceCapture {
|
|||
|
||||
@Override
|
||||
public void release() {
|
||||
displaySizeMonitor.stopAndRelease();
|
||||
displayMonitor.stopAndRelease();
|
||||
|
||||
if (virtualDisplay != null) {
|
||||
virtualDisplay.release();
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ import com.genymobile.scrcpy.Options;
|
|||
import com.genymobile.scrcpy.control.PositionMapper;
|
||||
import com.genymobile.scrcpy.device.Device;
|
||||
import com.genymobile.scrcpy.display.DisplayInfo;
|
||||
import com.genymobile.scrcpy.display.DisplaySizeMonitor;
|
||||
import com.genymobile.scrcpy.display.DisplayMonitor;
|
||||
import com.genymobile.scrcpy.display.DisplayProperties;
|
||||
import com.genymobile.scrcpy.model.ConfigurationException;
|
||||
import com.genymobile.scrcpy.model.Orientation;
|
||||
import com.genymobile.scrcpy.model.Size;
|
||||
|
|
@ -38,7 +39,7 @@ public class ScreenCapture extends SurfaceCapture {
|
|||
private DisplayInfo displayInfo;
|
||||
private Size videoSize;
|
||||
|
||||
private final DisplaySizeMonitor displaySizeMonitor = new DisplaySizeMonitor();
|
||||
private final DisplayMonitor displayMonitor = new DisplayMonitor();
|
||||
|
||||
private IBinder display;
|
||||
private VirtualDisplay virtualDisplay;
|
||||
|
|
@ -60,7 +61,7 @@ public class ScreenCapture extends SurfaceCapture {
|
|||
|
||||
@Override
|
||||
public void init() {
|
||||
displaySizeMonitor.start(displayId, this::invalidate);
|
||||
displayMonitor.start(displayId, this::invalidate);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -76,23 +77,24 @@ public class ScreenCapture extends SurfaceCapture {
|
|||
}
|
||||
|
||||
Size displaySize = displayInfo.getSize();
|
||||
displaySizeMonitor.setSessionDisplaySize(displaySize);
|
||||
int displayRotation = displayInfo.getRotation();
|
||||
displayMonitor.setSessionDisplayProperties(new DisplayProperties(displaySize, displayRotation));
|
||||
|
||||
if (captureOrientationLock == Orientation.Lock.LockedInitial) {
|
||||
// The user requested to lock the video orientation to the current orientation
|
||||
captureOrientationLock = Orientation.Lock.LockedValue;
|
||||
captureOrientation = Orientation.fromRotation(displayInfo.getRotation());
|
||||
captureOrientation = Orientation.fromRotation(displayRotation);
|
||||
}
|
||||
|
||||
VideoFilter filter = new VideoFilter(displaySize);
|
||||
|
||||
if (crop != null) {
|
||||
boolean transposed = (displayInfo.getRotation() % 2) != 0;
|
||||
boolean transposed = (displayRotation % 2) != 0;
|
||||
filter.addCrop(crop, transposed);
|
||||
}
|
||||
|
||||
boolean locked = captureOrientationLock != Orientation.Lock.Unlocked;
|
||||
filter.addOrientation(displayInfo.getRotation(), locked, captureOrientation);
|
||||
filter.addOrientation(displayRotation, locked, captureOrientation);
|
||||
filter.addAngle(angle);
|
||||
|
||||
transform = filter.getInverseTransform();
|
||||
|
|
@ -169,7 +171,7 @@ public class ScreenCapture extends SurfaceCapture {
|
|||
|
||||
@Override
|
||||
public void release() {
|
||||
displaySizeMonitor.stopAndRelease();
|
||||
displayMonitor.stopAndRelease();
|
||||
|
||||
if (display != null) {
|
||||
SurfaceControl.destroyDisplay(display);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue