Send PositionMapper to Controller directly

When a new capture starts, send a new PositionMapper to the Controller
without using the global Device as an intermediate.

Now all Device methods are static.

PR #5370 <https://github.com/Genymobile/scrcpy/pull/5370>
This commit is contained in:
Romain Vimont 2024-10-12 09:23:31 +02:00
commit 7024d38199
5 changed files with 45 additions and 38 deletions

View file

@ -139,9 +139,6 @@ public final class Server {
boolean video = options.getVideo(); boolean video = options.getVideo();
boolean audio = options.getAudio(); boolean audio = options.getAudio();
boolean sendDummyByte = options.getSendDummyByte(); boolean sendDummyByte = options.getSendDummyByte();
boolean camera = video && options.getVideoSource() == VideoSource.CAMERA;
final Device device = camera ? null : new Device();
Workarounds.apply(); Workarounds.apply();
@ -153,10 +150,11 @@ public final class Server {
connection.sendDeviceMeta(Device.getDeviceName()); connection.sendDeviceMeta(Device.getDeviceName());
} }
Controller controller = null;
if (control) { if (control) {
ControlChannel controlChannel = connection.getControlChannel(); ControlChannel controlChannel = connection.getControlChannel();
Controller controller = new Controller( controller = new Controller(options.getDisplayId(), controlChannel, cleanUp, options.getClipboardAutosync(), options.getPowerOn());
device, options.getDisplayId(), controlChannel, cleanUp, options.getClipboardAutosync(), options.getPowerOn());
asyncProcessors.add(controller); asyncProcessors.add(controller);
} }
@ -186,7 +184,7 @@ 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, options.getDisplayId(), options.getMaxSize(), options.getCrop(), surfaceCapture = new ScreenCapture(controller, options.getDisplayId(), options.getMaxSize(), options.getCrop(),
options.getLockVideoOrientation()); options.getLockVideoOrientation());
} else { } else {
surfaceCapture = new CameraCapture(options.getCameraId(), options.getCameraFacing(), options.getCameraSize(), surfaceCapture = new CameraCapture(options.getCameraId(), options.getCameraFacing(), options.getCameraSize(),

View file

@ -7,6 +7,7 @@ import com.genymobile.scrcpy.device.Device;
import com.genymobile.scrcpy.device.Point; import com.genymobile.scrcpy.device.Point;
import com.genymobile.scrcpy.device.Position; import com.genymobile.scrcpy.device.Position;
import com.genymobile.scrcpy.util.Ln; import com.genymobile.scrcpy.util.Ln;
import com.genymobile.scrcpy.video.VirtualDisplayListener;
import com.genymobile.scrcpy.wrappers.ClipboardManager; import com.genymobile.scrcpy.wrappers.ClipboardManager;
import com.genymobile.scrcpy.wrappers.InputManager; import com.genymobile.scrcpy.wrappers.InputManager;
import com.genymobile.scrcpy.wrappers.ServiceManager; import com.genymobile.scrcpy.wrappers.ServiceManager;
@ -26,8 +27,9 @@ import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
public class Controller implements AsyncProcessor { public class Controller implements AsyncProcessor, VirtualDisplayListener {
private static final int DEFAULT_DEVICE_ID = 0; private static final int DEFAULT_DEVICE_ID = 0;
@ -40,7 +42,6 @@ public class Controller implements AsyncProcessor {
private UhidManager uhidManager; private UhidManager uhidManager;
private final Device device;
private final int displayId; private final int displayId;
private final boolean supportsInputEvents; private final boolean supportsInputEvents;
private final ControlChannel controlChannel; private final ControlChannel controlChannel;
@ -53,6 +54,8 @@ public class Controller implements AsyncProcessor {
private final AtomicBoolean isSettingClipboard = new AtomicBoolean(); private final AtomicBoolean isSettingClipboard = new AtomicBoolean();
private final AtomicReference<PositionMapper> positionMapper = new AtomicReference<>();
private long lastTouchDown; private long lastTouchDown;
private final PointersState pointersState = new PointersState(); private final PointersState pointersState = new PointersState();
private final MotionEvent.PointerProperties[] pointerProperties = new MotionEvent.PointerProperties[PointersState.MAX_POINTERS]; private final MotionEvent.PointerProperties[] pointerProperties = new MotionEvent.PointerProperties[PointersState.MAX_POINTERS];
@ -60,8 +63,7 @@ public class Controller implements AsyncProcessor {
private boolean keepPowerModeOff; private boolean keepPowerModeOff;
public Controller(Device device, int displayId, ControlChannel controlChannel, CleanUp cleanUp, boolean clipboardAutosync, boolean powerOn) { public Controller(int displayId, ControlChannel controlChannel, CleanUp cleanUp, boolean clipboardAutosync, boolean powerOn) {
this.device = device;
this.displayId = displayId; this.displayId = displayId;
this.controlChannel = controlChannel; this.controlChannel = controlChannel;
this.cleanUp = cleanUp; this.cleanUp = cleanUp;
@ -99,6 +101,11 @@ public class Controller implements AsyncProcessor {
} }
} }
@Override
public void onNewVirtualDisplay(PositionMapper positionMapper) {
this.positionMapper.set(positionMapper);
}
private UhidManager getUhidManager() { private UhidManager getUhidManager() {
if (uhidManager == null) { if (uhidManager == null) {
uhidManager = new UhidManager(sender); uhidManager = new UhidManager(sender);
@ -299,7 +306,7 @@ public class Controller implements AsyncProcessor {
private boolean injectTouch(int action, long pointerId, Position position, float pressure, int actionButton, int buttons) { private boolean injectTouch(int action, long pointerId, Position position, float pressure, int actionButton, int buttons) {
long now = SystemClock.uptimeMillis(); long now = SystemClock.uptimeMillis();
Point point = device.getPhysicalPoint(position); Point point = getPhysicalPoint(position);
if (point == null) { if (point == null) {
Ln.w("Ignore touch event, it was generated for a different device size"); Ln.w("Ignore touch event, it was generated for a different device size");
return false; return false;
@ -407,9 +414,9 @@ public class Controller implements AsyncProcessor {
private boolean injectScroll(Position position, float hScroll, float vScroll, int buttons) { private boolean injectScroll(Position position, float hScroll, float vScroll, int buttons) {
long now = SystemClock.uptimeMillis(); long now = SystemClock.uptimeMillis();
Point point = device.getPhysicalPoint(position); Point point = getPhysicalPoint(position);
if (point == null) { if (point == null) {
// ignore event Ln.w("Ignore scroll event, it was generated for a different device size");
return false; return false;
} }
@ -427,6 +434,17 @@ public class Controller implements AsyncProcessor {
return injectEvent(event, Device.INJECT_MODE_ASYNC); return injectEvent(event, Device.INJECT_MODE_ASYNC);
} }
private Point getPhysicalPoint(Position position) {
// it hides the field on purpose, to read it with atomic access
@SuppressWarnings("checkstyle:HiddenField")
PositionMapper positionMapper = this.positionMapper.get();
if (positionMapper == null) {
return null;
}
return positionMapper.map(position);
}
/** /**
* Schedule a call to set power mode to off after a small delay. * Schedule a call to set power mode to off after a small delay.
*/ */

View file

@ -1,7 +1,6 @@
package com.genymobile.scrcpy.device; package com.genymobile.scrcpy.device;
import com.genymobile.scrcpy.AndroidVersions; import com.genymobile.scrcpy.AndroidVersions;
import com.genymobile.scrcpy.control.PositionMapper;
import com.genymobile.scrcpy.util.Ln; import com.genymobile.scrcpy.util.Ln;
import com.genymobile.scrcpy.wrappers.ClipboardManager; import com.genymobile.scrcpy.wrappers.ClipboardManager;
import com.genymobile.scrcpy.wrappers.DisplayControl; import com.genymobile.scrcpy.wrappers.DisplayControl;
@ -18,8 +17,6 @@ import android.view.InputEvent;
import android.view.KeyCharacterMap; import android.view.KeyCharacterMap;
import android.view.KeyEvent; import android.view.KeyEvent;
import java.util.concurrent.atomic.AtomicReference;
public final class Device { public final class Device {
public static final int POWER_MODE_OFF = SurfaceControl.POWER_MODE_OFF; public static final int POWER_MODE_OFF = SurfaceControl.POWER_MODE_OFF;
@ -32,17 +29,8 @@ 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;
private final AtomicReference<PositionMapper> positionMapper = new AtomicReference<>(); // set by the ScreenCapture instance private Device() {
// not instantiable
public Point getPhysicalPoint(Position position) {
// it hides the field on purpose, to read it with atomic access
@SuppressWarnings("checkstyle:HiddenField")
PositionMapper positionMapper = this.positionMapper.get();
if (positionMapper == null) {
return null;
}
return positionMapper.map(position);
} }
public static String getDeviceName() { public static String getDeviceName() {
@ -54,10 +42,6 @@ public final class Device {
return displayId == 0 || Build.VERSION.SDK_INT >= AndroidVersions.API_29_ANDROID_10; return displayId == 0 || Build.VERSION.SDK_INT >= AndroidVersions.API_29_ANDROID_10;
} }
public void setPositionMapper(PositionMapper positionMapper) {
this.positionMapper.set(positionMapper);
}
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()");

View file

@ -3,7 +3,6 @@ package com.genymobile.scrcpy.video;
import com.genymobile.scrcpy.AndroidVersions; import com.genymobile.scrcpy.AndroidVersions;
import com.genymobile.scrcpy.control.PositionMapper; import com.genymobile.scrcpy.control.PositionMapper;
import com.genymobile.scrcpy.device.ConfigurationException; import com.genymobile.scrcpy.device.ConfigurationException;
import com.genymobile.scrcpy.device.Device;
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.Ln; import com.genymobile.scrcpy.util.Ln;
@ -21,8 +20,7 @@ import android.view.Surface;
public class ScreenCapture extends SurfaceCapture { public class ScreenCapture extends SurfaceCapture {
private final Device device; private final VirtualDisplayListener vdListener;
private final int displayId; private final int displayId;
private int maxSize; private int maxSize;
private final Rect crop; private final Rect crop;
@ -37,8 +35,8 @@ public class ScreenCapture extends SurfaceCapture {
private IRotationWatcher rotationWatcher; private IRotationWatcher rotationWatcher;
private IDisplayFoldListener displayFoldListener; private IDisplayFoldListener displayFoldListener;
public ScreenCapture(Device device, int displayId, int maxSize, Rect crop, int lockVideoOrientation) { public ScreenCapture(VirtualDisplayListener vdListener, int displayId, int maxSize, Rect crop, int lockVideoOrientation) {
this.device = device; this.vdListener = vdListener;
this.displayId = displayId; this.displayId = displayId;
this.maxSize = maxSize; this.maxSize = maxSize;
this.crop = crop; this.crop = crop;
@ -133,8 +131,10 @@ public class ScreenCapture extends SurfaceCapture {
} }
} }
if (vdListener != null) {
PositionMapper positionMapper = PositionMapper.from(screenInfo); PositionMapper positionMapper = PositionMapper.from(screenInfo);
device.setPositionMapper(positionMapper); vdListener.onNewVirtualDisplay(positionMapper);
}
} }
@Override @Override

View file

@ -0,0 +1,7 @@
package com.genymobile.scrcpy.video;
import com.genymobile.scrcpy.control.PositionMapper;
public interface VirtualDisplayListener {
void onNewVirtualDisplay(PositionMapper positionMapper);
}