Compare commits
4 Commits
master
...
virtual_di
Author | SHA1 | Date | |
---|---|---|---|
|
5a2b929aac | ||
|
9434718970 | ||
|
6ddcc98663 | ||
|
19178e0df9 |
@ -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());
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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".
|
||||||
|
@ -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).
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user