Handle virtual display rotation

Listen to display size changes and rotate the virtual display
accordingly.

Note: use `git show -b` to Show this commit ignoring whitespace changes.

Fixes #5428 <https://github.com/Genymobile/scrcpy/issues/5428>
Refs #5370 <https://github.com/Genymobile/scrcpy/pull/5370>
PR #5455 <https://github.com/Genymobile/scrcpy/pull/5455>
This commit is contained in:
Romain Vimont 2024-11-10 13:47:02 +01:00
parent 39d51ff2cc
commit 9b03bfc3ae

View File

@ -6,10 +6,13 @@ import com.genymobile.scrcpy.control.PositionMapper;
import com.genymobile.scrcpy.device.DisplayInfo; import com.genymobile.scrcpy.device.DisplayInfo;
import com.genymobile.scrcpy.device.NewDisplay; import com.genymobile.scrcpy.device.NewDisplay;
import com.genymobile.scrcpy.device.Size; import com.genymobile.scrcpy.device.Size;
import com.genymobile.scrcpy.opengl.AffineOpenGLFilter;
import com.genymobile.scrcpy.opengl.OpenGLFilter;
import com.genymobile.scrcpy.opengl.OpenGLRunner;
import com.genymobile.scrcpy.util.AffineMatrix;
import com.genymobile.scrcpy.util.Ln; import com.genymobile.scrcpy.util.Ln;
import com.genymobile.scrcpy.wrappers.ServiceManager; import com.genymobile.scrcpy.wrappers.ServiceManager;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay; import android.hardware.display.VirtualDisplay;
import android.os.Build; import android.os.Build;
import android.view.Surface; import android.view.Surface;
@ -19,8 +22,8 @@ import java.io.IOException;
public class NewDisplayCapture extends SurfaceCapture { public class NewDisplayCapture extends SurfaceCapture {
// Internal fields copied from android.hardware.display.DisplayManager // Internal fields copied from android.hardware.display.DisplayManager
private static final int VIRTUAL_DISPLAY_FLAG_PUBLIC = DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC; private static final int VIRTUAL_DISPLAY_FLAG_PUBLIC = android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
private static final int VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY = DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY; private static final int VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY = android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
private static final int VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH = 1 << 6; private static final int VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH = 1 << 6;
private static final int VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT = 1 << 7; private static final int VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT = 1 << 7;
private static final int VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL = 1 << 8; private static final int VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL = 1 << 8;
@ -35,12 +38,18 @@ public class NewDisplayCapture extends SurfaceCapture {
private final VirtualDisplayListener vdListener; private final VirtualDisplayListener vdListener;
private final NewDisplay newDisplay; private final NewDisplay newDisplay;
private final DisplaySizeMonitor displaySizeMonitor = new DisplaySizeMonitor();
private AffineMatrix displayTransform;
private OpenGLRunner glRunner;
private Size mainDisplaySize; private Size mainDisplaySize;
private int mainDisplayDpi; private int mainDisplayDpi;
private int maxSize; // only used if newDisplay.getSize() != null private int maxSize; // only used if newDisplay.getSize() != null
private VirtualDisplay virtualDisplay; private VirtualDisplay virtualDisplay;
private Size size; private Size size; // the logical size of the display (including rotation)
private Size physicalSize; // the physical size of the display (without rotation)
private int dpi; private int dpi;
public NewDisplayCapture(VirtualDisplayListener vdListener, Options options) { public NewDisplayCapture(VirtualDisplayListener vdListener, Options options) {
@ -69,11 +78,27 @@ public class NewDisplayCapture extends SurfaceCapture {
@Override @Override
public void prepare() { public void prepare() {
if (!newDisplay.hasExplicitSize()) { if (virtualDisplay == null) {
size = mainDisplaySize.limit(maxSize).round8(); if (!newDisplay.hasExplicitSize()) {
} size = mainDisplaySize.limit(maxSize).round8();
if (!newDisplay.hasExplicitDpi()) { }
dpi = scaleDpi(mainDisplaySize, mainDisplayDpi, size); if (!newDisplay.hasExplicitDpi()) {
dpi = scaleDpi(mainDisplaySize, mainDisplayDpi, size);
}
physicalSize = size;
// Set the current display size to avoid an unnecessary call to invalidate()
displaySizeMonitor.setSessionDisplaySize(size);
} else {
DisplayInfo displayInfo = ServiceManager.getDisplayManager().getDisplayInfo(virtualDisplay.getDisplay().getDisplayId());
size = displayInfo.getSize();
dpi = displayInfo.getDpi();
VideoFilter displayFilter = new VideoFilter(size);
displayFilter.addRotation(displayInfo.getRotation());
// The display info gives the oriented size, but the virtual display video always remains in the origin orientation
displayTransform = displayFilter.getInverseTransform();
physicalSize = displayFilter.getOutputSize();
} }
} }
@ -100,28 +125,48 @@ public class NewDisplayCapture extends SurfaceCapture {
.createNewVirtualDisplay("scrcpy", size.getWidth(), size.getHeight(), dpi, surface, flags); .createNewVirtualDisplay("scrcpy", size.getWidth(), size.getHeight(), dpi, surface, flags);
virtualDisplayId = virtualDisplay.getDisplay().getDisplayId(); virtualDisplayId = virtualDisplay.getDisplay().getDisplayId();
Ln.i("New display: " + size.getWidth() + "x" + size.getHeight() + "/" + dpi + " (id=" + virtualDisplayId + ")"); Ln.i("New display: " + size.getWidth() + "x" + size.getHeight() + "/" + dpi + " (id=" + virtualDisplayId + ")");
displaySizeMonitor.start(virtualDisplayId, this::invalidate);
} catch (Exception e) { } catch (Exception e) {
Ln.e("Could not create display", e); Ln.e("Could not create display", e);
throw new AssertionError("Could not create display"); throw new AssertionError("Could not create display");
} }
if (vdListener != null) {
PositionMapper positionMapper = new PositionMapper(size, null);
vdListener.onNewVirtualDisplay(virtualDisplayId, positionMapper);
}
} }
@Override @Override
public void start(Surface surface) throws IOException { public void start(Surface surface) throws IOException {
if (displayTransform != null) {
assert glRunner == null;
OpenGLFilter glFilter = new AffineOpenGLFilter(displayTransform);
glRunner = new OpenGLRunner(glFilter);
surface = glRunner.start(physicalSize, size, surface);
}
if (virtualDisplay == null) { if (virtualDisplay == null) {
startNew(surface); startNew(surface);
} else { } else {
virtualDisplay.setSurface(surface); virtualDisplay.setSurface(surface);
} }
if (vdListener != null) {
// The virtual display rotation must only be applied to video, it is already taken into account when injecting events!
PositionMapper positionMapper = PositionMapper.create(size, null, size);
vdListener.onNewVirtualDisplay(virtualDisplay.getDisplay().getDisplayId(), positionMapper);
}
}
@Override
public void stop() {
if (glRunner != null) {
glRunner.stopAndRelease();
glRunner = null;
}
} }
@Override @Override
public void release() { public void release() {
displaySizeMonitor.stopAndRelease();
if (virtualDisplay != null) { if (virtualDisplay != null) {
virtualDisplay.release(); virtualDisplay.release();
virtualDisplay = null; virtualDisplay = null;