Compare commits

...

4 Commits

Author SHA1 Message Date
Romain Vimont
5a2b929aac hack_virtual_display 2024-10-06 19:14:21 +02:00
Romain Vimont
9434718970 dpi 2024-10-06 19:05:36 +02:00
Romain Vimont
6ddcc98663 vdevents 2024-10-06 18:39:15 +02:00
Romain Vimont
19178e0df9 move to screencapture 2024-10-06 18:31:45 +02:00
8 changed files with 181 additions and 156 deletions

View File

@ -190,7 +190,8 @@ public final class Server {
options.getSendFrameMeta()); options.getSendFrameMeta());
SurfaceCapture surfaceCapture; SurfaceCapture surfaceCapture;
if (options.getVideoSource() == VideoSource.DISPLAY) { if (options.getVideoSource() == VideoSource.DISPLAY) {
surfaceCapture = new ScreenCapture(device); surfaceCapture = new ScreenCapture(device, options.getDisplayId(), options.getMaxSize(), options.getCrop(),
options.getLockVideoOrientation());
} else { } else {
surfaceCapture = new CameraCapture(options.getCameraId(), options.getCameraFacing(), options.getCameraSize(), surfaceCapture = new CameraCapture(options.getCameraId(), options.getCameraFacing(), options.getCameraSize(),
options.getMaxSize(), options.getCameraAspectRatio(), options.getCameraFps(), options.getCameraHighSpeed()); options.getMaxSize(), options.getCameraAspectRatio(), options.getCameraFps(), options.getCameraHighSpeed());

View File

@ -243,7 +243,7 @@ public class Controller implements AsyncProcessor {
return false; return false;
} }
for (KeyEvent event : events) { for (KeyEvent event : events) {
if (!device.injectEvent(event, Device.INJECT_MODE_ASYNC)) { if (!device.injectMainDisplayEvent(event, Device.INJECT_MODE_ASYNC)) {
return false; return false;
} }
} }
@ -324,7 +324,7 @@ public class Controller implements AsyncProcessor {
// First button pressed: ACTION_DOWN // First button pressed: ACTION_DOWN
MotionEvent downEvent = MotionEvent.obtain(lastTouchDown, now, MotionEvent.ACTION_DOWN, pointerCount, pointerProperties, MotionEvent downEvent = MotionEvent.obtain(lastTouchDown, now, MotionEvent.ACTION_DOWN, pointerCount, pointerProperties,
pointerCoords, 0, buttons, 1f, 1f, DEFAULT_DEVICE_ID, 0, source, 0); pointerCoords, 0, buttons, 1f, 1f, DEFAULT_DEVICE_ID, 0, source, 0);
if (!device.injectEvent(downEvent, Device.INJECT_MODE_ASYNC)) { if (!device.injectVirtualDisplayEvent(downEvent, Device.INJECT_MODE_ASYNC)) {
return false; return false;
} }
} }
@ -335,7 +335,7 @@ public class Controller implements AsyncProcessor {
if (!InputManager.setActionButton(pressEvent, actionButton)) { if (!InputManager.setActionButton(pressEvent, actionButton)) {
return false; return false;
} }
if (!device.injectEvent(pressEvent, Device.INJECT_MODE_ASYNC)) { if (!device.injectVirtualDisplayEvent(pressEvent, Device.INJECT_MODE_ASYNC)) {
return false; return false;
} }
@ -349,7 +349,7 @@ public class Controller implements AsyncProcessor {
if (!InputManager.setActionButton(releaseEvent, actionButton)) { if (!InputManager.setActionButton(releaseEvent, actionButton)) {
return false; return false;
} }
if (!device.injectEvent(releaseEvent, Device.INJECT_MODE_ASYNC)) { if (!device.injectVirtualDisplayEvent(releaseEvent, Device.INJECT_MODE_ASYNC)) {
return false; return false;
} }
@ -357,7 +357,7 @@ public class Controller implements AsyncProcessor {
// Last button released: ACTION_UP // Last button released: ACTION_UP
MotionEvent upEvent = MotionEvent.obtain(lastTouchDown, now, MotionEvent.ACTION_UP, pointerCount, pointerProperties, MotionEvent upEvent = MotionEvent.obtain(lastTouchDown, now, MotionEvent.ACTION_UP, pointerCount, pointerProperties,
pointerCoords, 0, buttons, 1f, 1f, DEFAULT_DEVICE_ID, 0, source, 0); pointerCoords, 0, buttons, 1f, 1f, DEFAULT_DEVICE_ID, 0, source, 0);
if (!device.injectEvent(upEvent, Device.INJECT_MODE_ASYNC)) { if (!device.injectVirtualDisplayEvent(upEvent, Device.INJECT_MODE_ASYNC)) {
return false; return false;
} }
} }
@ -368,7 +368,7 @@ public class Controller implements AsyncProcessor {
MotionEvent event = MotionEvent.obtain(lastTouchDown, now, action, pointerCount, pointerProperties, pointerCoords, 0, buttons, 1f, 1f, MotionEvent event = MotionEvent.obtain(lastTouchDown, now, action, pointerCount, pointerProperties, pointerCoords, 0, buttons, 1f, 1f,
DEFAULT_DEVICE_ID, 0, source, 0); DEFAULT_DEVICE_ID, 0, source, 0);
return device.injectEvent(event, Device.INJECT_MODE_ASYNC); return device.injectVirtualDisplayEvent(event, Device.INJECT_MODE_ASYNC);
} }
private boolean injectScroll(Position position, float hScroll, float vScroll, int buttons) { private boolean injectScroll(Position position, float hScroll, float vScroll, int buttons) {
@ -390,7 +390,7 @@ public class Controller implements AsyncProcessor {
MotionEvent event = MotionEvent.obtain(lastTouchDown, now, MotionEvent.ACTION_SCROLL, 1, pointerProperties, pointerCoords, 0, buttons, 1f, 1f, MotionEvent event = MotionEvent.obtain(lastTouchDown, now, MotionEvent.ACTION_SCROLL, 1, pointerProperties, pointerCoords, 0, buttons, 1f, 1f,
DEFAULT_DEVICE_ID, 0, InputDevice.SOURCE_MOUSE, 0); DEFAULT_DEVICE_ID, 0, InputDevice.SOURCE_MOUSE, 0);
return device.injectEvent(event, Device.INJECT_MODE_ASYNC); return device.injectVirtualDisplayEvent(event, Device.INJECT_MODE_ASYNC);
} }
/** /**

View File

@ -2,7 +2,6 @@ package com.genymobile.scrcpy.device;
import com.genymobile.scrcpy.Options; import com.genymobile.scrcpy.Options;
import com.genymobile.scrcpy.util.Ln; import com.genymobile.scrcpy.util.Ln;
import com.genymobile.scrcpy.util.LogUtils;
import com.genymobile.scrcpy.video.ScreenInfo; import com.genymobile.scrcpy.video.ScreenInfo;
import com.genymobile.scrcpy.wrappers.ClipboardManager; import com.genymobile.scrcpy.wrappers.ClipboardManager;
import com.genymobile.scrcpy.wrappers.DisplayControl; import com.genymobile.scrcpy.wrappers.DisplayControl;
@ -16,8 +15,6 @@ import android.graphics.Rect;
import android.os.Build; import android.os.Build;
import android.os.IBinder; import android.os.IBinder;
import android.os.SystemClock; import android.os.SystemClock;
import android.view.IDisplayFoldListener;
import android.view.IRotationWatcher;
import android.view.InputDevice; import android.view.InputDevice;
import android.view.InputEvent; import android.view.InputEvent;
import android.view.KeyCharacterMap; import android.view.KeyCharacterMap;
@ -37,26 +34,10 @@ public final class Device {
public static final int LOCK_VIDEO_ORIENTATION_UNLOCKED = -1; public static final int LOCK_VIDEO_ORIENTATION_UNLOCKED = -1;
public static final int LOCK_VIDEO_ORIENTATION_INITIAL = -2; public static final int LOCK_VIDEO_ORIENTATION_INITIAL = -2;
public interface RotationListener {
void onRotationChanged(int rotation);
}
public interface FoldListener {
void onFoldChanged(int displayId, boolean folded);
}
public interface ClipboardListener { public interface ClipboardListener {
void onClipboardTextChanged(String text); void onClipboardTextChanged(String text);
} }
private final Rect crop;
private int maxSize;
private final int lockVideoOrientation;
private Size deviceSize;
private ScreenInfo screenInfo;
private RotationListener rotationListener;
private FoldListener foldListener;
private ClipboardListener clipboardListener; private ClipboardListener clipboardListener;
private final AtomicBoolean isSettingClipboard = new AtomicBoolean(); private final AtomicBoolean isSettingClipboard = new AtomicBoolean();
@ -65,71 +46,15 @@ public final class Device {
*/ */
private final int displayId; private final int displayId;
/**
* The surface flinger layer stack associated with this logical display
*/
private final int layerStack;
private final boolean supportsInputEvents; private final boolean supportsInputEvents;
public Device(Options options) throws ConfigurationException { // set by the ScreenCapture instance
private ScreenInfo screenInfo;
private int virtualDisplayId;
public Device(Options options) {
displayId = options.getDisplayId(); displayId = options.getDisplayId();
DisplayInfo displayInfo = ServiceManager.getDisplayManager().getDisplayInfo(displayId); virtualDisplayId = displayId; // by default
if (displayInfo == null) {
Ln.e("Display " + displayId + " not found\n" + LogUtils.buildDisplayListMessage());
throw new ConfigurationException("Unknown display id: " + displayId);
}
int displayInfoFlags = displayInfo.getFlags();
deviceSize = displayInfo.getSize();
crop = options.getCrop();
maxSize = options.getMaxSize();
lockVideoOrientation = options.getLockVideoOrientation();
screenInfo = ScreenInfo.computeScreenInfo(displayInfo.getRotation(), deviceSize, crop, maxSize, lockVideoOrientation);
layerStack = displayInfo.getLayerStack();
ServiceManager.getWindowManager().registerRotationWatcher(new IRotationWatcher.Stub() {
@Override
public void onRotationChanged(int rotation) {
synchronized (Device.this) {
screenInfo = screenInfo.withDeviceRotation(rotation);
// notify
if (rotationListener != null) {
rotationListener.onRotationChanged(rotation);
}
}
}
}, displayId);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ServiceManager.getWindowManager().registerDisplayFoldListener(new IDisplayFoldListener.Stub() {
@Override
public void onDisplayFoldChanged(int displayId, boolean folded) {
if (Device.this.displayId != displayId) {
// Ignore events related to other display ids
return;
}
synchronized (Device.this) {
DisplayInfo displayInfo = ServiceManager.getDisplayManager().getDisplayInfo(displayId);
if (displayInfo == null) {
Ln.e("Display " + displayId + " not found\n" + LogUtils.buildDisplayListMessage());
return;
}
deviceSize = displayInfo.getSize();
screenInfo = ScreenInfo.computeScreenInfo(displayInfo.getRotation(), deviceSize, crop, maxSize, lockVideoOrientation);
// notify
if (foldListener != null) {
foldListener.onFoldChanged(displayId, folded);
}
}
}
});
}
if (options.getControl() && options.getClipboardAutosync()) { if (options.getControl() && options.getClipboardAutosync()) {
// If control and autosync are enabled, synchronize Android clipboard to the computer automatically // If control and autosync are enabled, synchronize Android clipboard to the computer automatically
@ -157,38 +82,20 @@ public final class Device {
} }
} }
if ((displayInfoFlags & DisplayInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS) == 0) {
Ln.w("Display doesn't have FLAG_SUPPORTS_PROTECTED_BUFFERS flag, mirroring can be restricted");
}
// main display or any display on Android >= Q // main display or any display on Android >= Q
supportsInputEvents = displayId == 0 || Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q; supportsInputEvents = options.getDisplayId() == 0 || Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q;
if (!supportsInputEvents) { if (!supportsInputEvents) {
Ln.w("Input events are not supported for secondary displays before Android 10"); Ln.w("Input events are not supported for secondary displays before Android 10");
} }
} }
public int getDisplayId() {
return displayId;
}
public synchronized void setMaxSize(int newMaxSize) {
maxSize = newMaxSize;
screenInfo = ScreenInfo.computeScreenInfo(screenInfo.getReverseVideoRotation(), deviceSize, crop, newMaxSize, lockVideoOrientation);
}
public synchronized ScreenInfo getScreenInfo() {
return screenInfo;
}
public int getLayerStack() {
return layerStack;
}
public Point getPhysicalPoint(Position position) { public Point getPhysicalPoint(Position position) {
// it hides the field on purpose, to read it with a lock // it hides the field on purpose, to read it with a lock
@SuppressWarnings("checkstyle:HiddenField") @SuppressWarnings("checkstyle:HiddenField")
ScreenInfo screenInfo = getScreenInfo(); // read with synchronization ScreenInfo screenInfo = getScreenInfo(); // read with synchronization
if (screenInfo == null) {
return null;
}
// ignore the locked video orientation, the events will apply in coordinates considered in the physical device orientation // ignore the locked video orientation, the events will apply in coordinates considered in the physical device orientation
Size unlockedVideoSize = screenInfo.getUnlockedVideoSize(); Size unlockedVideoSize = screenInfo.getUnlockedVideoSize();
@ -222,6 +129,22 @@ public final class Device {
return supportsInputEvents; return supportsInputEvents;
} }
private synchronized ScreenInfo getScreenInfo() {
return screenInfo;
}
public synchronized void setScreenInfo(ScreenInfo screenInfo) {
this.screenInfo = screenInfo;
}
private synchronized int getVirtualDisplayId() {
return virtualDisplayId;
}
public synchronized void setVirtualDisplayId(int virtualDisplayId) {
this.virtualDisplayId = virtualDisplayId;
}
public static boolean injectEvent(InputEvent inputEvent, int displayId, int injectMode) { public static boolean injectEvent(InputEvent inputEvent, int displayId, int injectMode) {
if (!supportsInputEvents(displayId)) { if (!supportsInputEvents(displayId)) {
throw new AssertionError("Could not inject input event if !supportsInputEvents()"); throw new AssertionError("Could not inject input event if !supportsInputEvents()");
@ -234,10 +157,14 @@ public final class Device {
return ServiceManager.getInputManager().injectInputEvent(inputEvent, injectMode); return ServiceManager.getInputManager().injectInputEvent(inputEvent, injectMode);
} }
public boolean injectEvent(InputEvent event, int injectMode) { public boolean injectMainDisplayEvent(InputEvent event, int injectMode) {
return injectEvent(event, displayId, injectMode); return injectEvent(event, displayId, injectMode);
} }
public boolean injectVirtualDisplayEvent(InputEvent event, int injectMode) {
return injectEvent(event, virtualDisplayId, injectMode);
}
public static boolean injectKeyEvent(int action, int keyCode, int repeat, int metaState, int displayId, int injectMode) { public static boolean injectKeyEvent(int action, int keyCode, int repeat, int metaState, int displayId, int injectMode) {
long now = SystemClock.uptimeMillis(); long now = SystemClock.uptimeMillis();
KeyEvent event = new KeyEvent(now, now, action, keyCode, repeat, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, KeyEvent event = new KeyEvent(now, now, action, keyCode, repeat, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0,
@ -262,14 +189,6 @@ public final class Device {
return ServiceManager.getPowerManager().isScreenOn(); return ServiceManager.getPowerManager().isScreenOn();
} }
public synchronized void setRotationListener(RotationListener rotationListener) {
this.rotationListener = rotationListener;
}
public synchronized void setFoldListener(FoldListener foldlistener) {
this.foldListener = foldlistener;
}
public synchronized void setClipboardListener(ClipboardListener clipboardListener) { public synchronized void setClipboardListener(ClipboardListener clipboardListener) {
this.clipboardListener = clipboardListener; this.clipboardListener = clipboardListener;
} }

View File

@ -6,15 +6,17 @@ public final class DisplayInfo {
private final int rotation; private final int rotation;
private final int layerStack; private final int layerStack;
private final int flags; private final int flags;
private final int logicalDensityDpi;
public static final int FLAG_SUPPORTS_PROTECTED_BUFFERS = 0x00000001; public static final int FLAG_SUPPORTS_PROTECTED_BUFFERS = 0x00000001;
public DisplayInfo(int displayId, Size size, int rotation, int layerStack, int flags) { public DisplayInfo(int displayId, Size size, int rotation, int layerStack, int flags, int logicalDensityDpi) {
this.displayId = displayId; this.displayId = displayId;
this.size = size; this.size = size;
this.rotation = rotation; this.rotation = rotation;
this.layerStack = layerStack; this.layerStack = layerStack;
this.flags = flags; this.flags = flags;
this.logicalDensityDpi = logicalDensityDpi;
} }
public int getDisplayId() { public int getDisplayId() {
@ -36,5 +38,9 @@ public final class DisplayInfo {
public int getFlags() { public int getFlags() {
return flags; return flags;
} }
public int getLogicalDensityDpi() {
return logicalDensityDpi;
}
} }

View File

@ -1,42 +1,115 @@
package com.genymobile.scrcpy.video; package com.genymobile.scrcpy.video;
import com.genymobile.scrcpy.device.ConfigurationException;
import com.genymobile.scrcpy.device.Device; import com.genymobile.scrcpy.device.Device;
import com.genymobile.scrcpy.device.DisplayInfo;
import com.genymobile.scrcpy.device.Size; import com.genymobile.scrcpy.device.Size;
import com.genymobile.scrcpy.util.Ln; import com.genymobile.scrcpy.util.Ln;
import com.genymobile.scrcpy.util.LogUtils;
import com.genymobile.scrcpy.wrappers.ServiceManager; import com.genymobile.scrcpy.wrappers.ServiceManager;
import com.genymobile.scrcpy.wrappers.SurfaceControl; import com.genymobile.scrcpy.wrappers.SurfaceControl;
import android.graphics.Rect; import android.graphics.Rect;
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.os.IBinder; import android.os.IBinder;
import android.view.IDisplayFoldListener;
import android.view.IRotationWatcher;
import android.view.Surface; import android.view.Surface;
public class ScreenCapture extends SurfaceCapture implements Device.RotationListener, Device.FoldListener { public class ScreenCapture extends SurfaceCapture {
private final Device device; private final Device device;
private final int displayId;
private int maxSize;
private final Rect crop;
private final int lockVideoOrientation;
private int layerStack;
private int dpi;
private Size deviceSize;
private ScreenInfo screenInfo;
private IBinder display; private IBinder display;
private VirtualDisplay virtualDisplay; private VirtualDisplay virtualDisplay;
public ScreenCapture(Device device) { private IRotationWatcher rotationWatcher;
private IDisplayFoldListener displayFoldListener;
public ScreenCapture(Device device, int displayId, int maxSize, Rect crop, int lockVideoOrientation) {
this.device = device; this.device = device;
this.displayId = displayId;
this.maxSize = maxSize;
this.crop = crop;
this.lockVideoOrientation = lockVideoOrientation;
} }
@Override @Override
public void init() { public void init() throws ConfigurationException {
device.setRotationListener(this); DisplayInfo displayInfo = ServiceManager.getDisplayManager().getDisplayInfo(displayId);
device.setFoldListener(this); if (displayInfo == null) {
Ln.e("Display " + displayId + " not found\n" + LogUtils.buildDisplayListMessage());
throw new ConfigurationException("Unknown display id: " + displayId);
}
deviceSize = displayInfo.getSize();
screenInfo = ScreenInfo.computeScreenInfo(displayInfo.getRotation(), deviceSize, crop, maxSize, lockVideoOrientation);
device.setScreenInfo(screenInfo);
layerStack = displayInfo.getLayerStack();
dpi = displayInfo.getLogicalDensityDpi();
if (displayId == 0) {
rotationWatcher = new IRotationWatcher.Stub() {
@Override
public void onRotationChanged(int rotation) {
synchronized (ScreenCapture.this) {
screenInfo = screenInfo.withDeviceRotation(rotation);
device.setScreenInfo(screenInfo);
}
}
};
ServiceManager.getWindowManager().registerRotationWatcher(rotationWatcher, displayId);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
displayFoldListener = new IDisplayFoldListener.Stub() {
@Override
public void onDisplayFoldChanged(int displayId, boolean folded) {
if (ScreenCapture.this.displayId != displayId) {
// Ignore events related to other display ids
return;
}
synchronized (ScreenCapture.this) {
DisplayInfo displayInfo = ServiceManager.getDisplayManager().getDisplayInfo(displayId);
if (displayInfo == null) {
Ln.e("Display " + displayId + " not found\n" + LogUtils.buildDisplayListMessage());
return;
}
deviceSize = displayInfo.getSize();
screenInfo = ScreenInfo.computeScreenInfo(displayInfo.getRotation(), deviceSize, crop, maxSize, lockVideoOrientation);
device.setScreenInfo(screenInfo);
}
}
};
ServiceManager.getWindowManager().registerDisplayFoldListener(displayFoldListener);
}
if ((displayInfo.getFlags() & DisplayInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS) == 0) {
Ln.w("Display doesn't have FLAG_SUPPORTS_PROTECTED_BUFFERS flag, mirroring can be restricted");
}
} }
@Override @Override
public void start(Surface surface) { public void start(Surface surface) {
ScreenInfo screenInfo = device.getScreenInfo();
Rect contentRect = screenInfo.getContentRect(); Rect contentRect = screenInfo.getContentRect();
// does not include the locked video orientation // does not include the locked video orientation
Rect unlockedVideoRect = screenInfo.getUnlockedVideoSize().toRect(); Rect unlockedVideoRect = screenInfo.getUnlockedVideoSize().toRect();
int videoRotation = screenInfo.getVideoRotation(); int videoRotation = screenInfo.getVideoRotation();
int layerStack = device.getLayerStack();
if (display != null) { if (display != null) {
SurfaceControl.destroyDisplay(display); SurfaceControl.destroyDisplay(display);
@ -49,8 +122,12 @@ public class ScreenCapture extends SurfaceCapture implements Device.RotationList
try { try {
Rect videoRect = screenInfo.getVideoSize().toRect(); Rect videoRect = screenInfo.getVideoSize().toRect();
int flags = DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC | DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY | (1
<< 6) /* DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORT_TOUCH */ | 1 << 8 | 1 << 9 | 1 << 10 | 1 << 11 | 1 << 12 | 1 << 13 | 1 << 14;
virtualDisplay = ServiceManager.getDisplayManager() virtualDisplay = ServiceManager.getDisplayManager()
.createVirtualDisplay("scrcpy", videoRect.width(), videoRect.height(), device.getDisplayId(), surface); .createVirtualDisplay("scrcpy", videoRect.width(), videoRect.height(), dpi, surface, flags);
device.setVirtualDisplayId(virtualDisplay.getDisplay().getDisplayId());
Ln.d("Display: using DisplayManager API"); Ln.d("Display: using DisplayManager API");
} catch (Exception displayManagerException) { } catch (Exception displayManagerException) {
try { try {
@ -67,8 +144,12 @@ public class ScreenCapture extends SurfaceCapture implements Device.RotationList
@Override @Override
public void release() { public void release() {
device.setRotationListener(null); if (rotationWatcher != null) {
device.setFoldListener(null); ServiceManager.getWindowManager().unregisterRotationWatcher(rotationWatcher);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ServiceManager.getWindowManager().unregisterDisplayFoldListener(displayFoldListener);
}
if (display != null) { if (display != null) {
SurfaceControl.destroyDisplay(display); SurfaceControl.destroyDisplay(display);
display = null; display = null;
@ -80,26 +161,18 @@ public class ScreenCapture extends SurfaceCapture implements Device.RotationList
} }
@Override @Override
public Size getSize() { public synchronized Size getSize() {
return device.getScreenInfo().getVideoSize(); return screenInfo.getVideoSize();
} }
@Override @Override
public boolean setMaxSize(int maxSize) { public synchronized boolean setMaxSize(int newMaxSize) {
device.setMaxSize(maxSize); maxSize = newMaxSize;
screenInfo = ScreenInfo.computeScreenInfo(screenInfo.getReverseVideoRotation(), deviceSize, crop, newMaxSize, lockVideoOrientation);
device.setScreenInfo(screenInfo);
return true; return true;
} }
@Override
public void onFoldChanged(int displayId, boolean folded) {
requestReset();
}
@Override
public void onRotationChanged(int rotation) {
requestReset();
}
private static IBinder createDisplay() throws Exception { private static IBinder createDisplay() throws Exception {
// Since Android 12 (preview), secure displays could not be created with shell permissions anymore. // Since Android 12 (preview), secure displays could not be created with shell permissions anymore.
// On Android 12 preview, SDK_INT is still R (not S), but CODENAME is "S". // On Android 12 preview, SDK_INT is still R (not S), but CODENAME is "S".

View File

@ -1,5 +1,6 @@
package com.genymobile.scrcpy.video; package com.genymobile.scrcpy.video;
import com.genymobile.scrcpy.device.ConfigurationException;
import com.genymobile.scrcpy.device.Size; import com.genymobile.scrcpy.device.Size;
import android.view.Surface; import android.view.Surface;
@ -34,7 +35,7 @@ public abstract class SurfaceCapture {
/** /**
* Called once before the capture starts. * Called once before the capture starts.
*/ */
public abstract void init() throws IOException; public abstract void init() throws ConfigurationException, IOException;
/** /**
* Called after the capture ends (if and only if {@link #init()} has been called). * Called after the capture ends (if and only if {@link #init()} has been called).

View File

@ -1,15 +1,18 @@
package com.genymobile.scrcpy.wrappers; package com.genymobile.scrcpy.wrappers;
import com.genymobile.scrcpy.FakeContext;
import com.genymobile.scrcpy.device.DisplayInfo; import com.genymobile.scrcpy.device.DisplayInfo;
import com.genymobile.scrcpy.device.Size; import com.genymobile.scrcpy.device.Size;
import com.genymobile.scrcpy.util.Command; import com.genymobile.scrcpy.util.Command;
import com.genymobile.scrcpy.util.Ln; import com.genymobile.scrcpy.util.Ln;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context;
import android.hardware.display.VirtualDisplay; import android.hardware.display.VirtualDisplay;
import android.view.Display; import android.view.Display;
import android.view.Surface; import android.view.Surface;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.regex.Matcher; import java.util.regex.Matcher;
@ -39,7 +42,7 @@ public final class DisplayManager {
public static DisplayInfo parseDisplayInfo(String dumpsysDisplayOutput, int displayId) { public static DisplayInfo parseDisplayInfo(String dumpsysDisplayOutput, int displayId) {
Pattern regex = Pattern.compile( Pattern regex = Pattern.compile(
"^ mOverrideDisplayInfo=DisplayInfo\\{\".*?, displayId " + displayId + ".*?(, FLAG_.*)?, real ([0-9]+) x ([0-9]+).*?, " "^ mOverrideDisplayInfo=DisplayInfo\\{\".*?, displayId " + displayId + ".*?(, FLAG_.*)?, real ([0-9]+) x ([0-9]+).*?, "
+ "rotation ([0-9]+).*?, layerStack ([0-9]+)", + "rotation ([0-9]+).*?, density ([0-9]+).*?, layerStack ([0-9]+)",
Pattern.MULTILINE); Pattern.MULTILINE);
Matcher m = regex.matcher(dumpsysDisplayOutput); Matcher m = regex.matcher(dumpsysDisplayOutput);
if (!m.find()) { if (!m.find()) {
@ -49,9 +52,10 @@ public final class DisplayManager {
int width = Integer.parseInt(m.group(2)); int width = Integer.parseInt(m.group(2));
int height = Integer.parseInt(m.group(3)); int height = Integer.parseInt(m.group(3));
int rotation = Integer.parseInt(m.group(4)); int rotation = Integer.parseInt(m.group(4));
int layerStack = Integer.parseInt(m.group(5)); int density = Integer.parseInt(m.group(5));
int layerStack = Integer.parseInt(m.group(6));
return new DisplayInfo(displayId, new Size(width, height), rotation, layerStack, flags); return new DisplayInfo(displayId, new Size(width, height), rotation, layerStack, flags, density);
} }
private static DisplayInfo getDisplayInfoFromDumpsysDisplay(int displayId) { private static DisplayInfo getDisplayInfoFromDumpsysDisplay(int displayId) {
@ -98,7 +102,8 @@ public final class DisplayManager {
int rotation = cls.getDeclaredField("rotation").getInt(displayInfo); int rotation = cls.getDeclaredField("rotation").getInt(displayInfo);
int layerStack = cls.getDeclaredField("layerStack").getInt(displayInfo); int layerStack = cls.getDeclaredField("layerStack").getInt(displayInfo);
int flags = cls.getDeclaredField("flags").getInt(displayInfo); int flags = cls.getDeclaredField("flags").getInt(displayInfo);
return new DisplayInfo(displayId, new Size(width, height), rotation, layerStack, flags); int logicalDensityDpi = cls.getDeclaredField("logicalDensityDpi").getInt(displayInfo);
return new DisplayInfo(displayId, new Size(width, height), rotation, layerStack, flags, logicalDensityDpi);
} catch (ReflectiveOperationException e) { } catch (ReflectiveOperationException e) {
throw new AssertionError(e); throw new AssertionError(e);
} }
@ -115,13 +120,17 @@ public final class DisplayManager {
private Method getCreateVirtualDisplayMethod() throws NoSuchMethodException { private Method getCreateVirtualDisplayMethod() throws NoSuchMethodException {
if (createVirtualDisplayMethod == null) { if (createVirtualDisplayMethod == null) {
createVirtualDisplayMethod = android.hardware.display.DisplayManager.class createVirtualDisplayMethod = android.hardware.display.DisplayManager.class
.getMethod("createVirtualDisplay", String.class, int.class, int.class, int.class, Surface.class); .getMethod("createVirtualDisplay", String.class, int.class, int.class, int.class, Surface.class, int.class);
} }
return createVirtualDisplayMethod; return createVirtualDisplayMethod;
} }
public VirtualDisplay createVirtualDisplay(String name, int width, int height, int displayIdToMirror, Surface surface) throws Exception { public VirtualDisplay createVirtualDisplay(String name, int width, int height, int dpi, Surface surface, int flags) throws Exception {
Method method = getCreateVirtualDisplayMethod(); //Method method = getCreateVirtualDisplayMethod();
return (VirtualDisplay) method.invoke(null, name, width, height, displayIdToMirror, surface); Constructor<android.hardware.display.DisplayManager> ctor = android.hardware.display.DisplayManager.class.getDeclaredConstructor(Context.class);
ctor.setAccessible(true);
android.hardware.display.DisplayManager dm = ctor.newInstance(FakeContext.get());
return dm.createVirtualDisplay(name, width, height, dpi, surface, flags);
//return (VirtualDisplay) method.invoke(null, name, width, height, dpi, surface, flags);
} }
} }

View File

@ -200,13 +200,29 @@ public final class WindowManager {
} }
} }
public void unregisterRotationWatcher(IRotationWatcher rotationWatcher) {
try {
manager.getClass().getMethod("removeRotationWatcher", IRotationWatcher.class).invoke(manager, rotationWatcher);
} catch (Exception e) {
Ln.e("Could not unregister rotation watcher", e);
}
}
@TargetApi(29) @TargetApi(29)
public void registerDisplayFoldListener(IDisplayFoldListener foldListener) { public void registerDisplayFoldListener(IDisplayFoldListener foldListener) {
try { try {
Class<?> cls = manager.getClass(); manager.getClass().getMethod("registerDisplayFoldListener", IDisplayFoldListener.class).invoke(manager, foldListener);
cls.getMethod("registerDisplayFoldListener", IDisplayFoldListener.class).invoke(manager, foldListener);
} catch (Exception e) { } catch (Exception e) {
Ln.e("Could not register display fold listener", e); Ln.e("Could not register display fold listener", e);
} }
} }
@TargetApi(29)
public void unregisterDisplayFoldListener(IDisplayFoldListener foldListener) {
try {
manager.getClass().getMethod("unregisterDisplayFoldListener", IDisplayFoldListener.class).invoke(manager, foldListener);
} catch (Exception e) {
Ln.e("Could not unregister display fold listener", e);
}
}
} }