Add display id parameter
Add --display command line parameter to specify a display id. PR #1238 <https://github.com/Genymobile/scrcpy/pull/1238> Signed-off-by: Romain Vimont <rom@rom1v.com>
This commit is contained in:
parent
5031b2c8ff
commit
4150eedcdf
16 changed files with 241 additions and 16 deletions
15
README.md
15
README.md
|
@ -353,6 +353,21 @@ scrcpy --no-control
|
||||||
scrcpy -n
|
scrcpy -n
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Display
|
||||||
|
|
||||||
|
If several displays are available, it is possible to select the display to
|
||||||
|
mirror:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scrcpy --display 1
|
||||||
|
```
|
||||||
|
|
||||||
|
The list of display ids can be retrieved by:
|
||||||
|
|
||||||
|
```
|
||||||
|
adb shell dumpsys display # search "mDisplayId=" in the output
|
||||||
|
```
|
||||||
|
|
||||||
#### Turn screen off
|
#### Turn screen off
|
||||||
|
|
||||||
It is possible to turn the device screen off while mirroring on start with a
|
It is possible to turn the device screen off while mirroring on start with a
|
||||||
|
|
|
@ -33,6 +33,15 @@ The values are expressed in the device natural orientation (typically, portrait
|
||||||
.B \-\-max\-size
|
.B \-\-max\-size
|
||||||
value is computed on the cropped size.
|
value is computed on the cropped size.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BI "\-\-display " id
|
||||||
|
Specify the display id to mirror.
|
||||||
|
|
||||||
|
The list of possible display ids can be listed by "adb shell dumpsys display"
|
||||||
|
(search "mDisplayId=" in the output).
|
||||||
|
|
||||||
|
Default is 0.
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B \-f, \-\-fullscreen
|
.B \-f, \-\-fullscreen
|
||||||
Start in fullscreen.
|
Start in fullscreen.
|
||||||
|
|
|
@ -36,6 +36,15 @@ scrcpy_print_usage(const char *arg0) {
|
||||||
" (typically, portrait for a phone, landscape for a tablet).\n"
|
" (typically, portrait for a phone, landscape for a tablet).\n"
|
||||||
" Any --max-size value is computed on the cropped size.\n"
|
" Any --max-size value is computed on the cropped size.\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
" --display id\n"
|
||||||
|
" Specify the display id to mirror.\n"
|
||||||
|
"\n"
|
||||||
|
" The list of possible display ids can be listed by:\n"
|
||||||
|
" adb shell dumpsys display\n"
|
||||||
|
" (search \"mDisplayId=\" in the output)\n"
|
||||||
|
"\n"
|
||||||
|
" Default is 0.\n"
|
||||||
|
"\n"
|
||||||
" -f, --fullscreen\n"
|
" -f, --fullscreen\n"
|
||||||
" Start in fullscreen.\n"
|
" Start in fullscreen.\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
@ -363,6 +372,18 @@ parse_port_range(const char *s, struct port_range *port_range) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
parse_display_id(const char *s, uint16_t *display_id) {
|
||||||
|
long value;
|
||||||
|
bool ok = parse_integer_arg(s, &value, false, 0, 0xFFFF, "display id");
|
||||||
|
if (!ok) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*display_id = (uint16_t) value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
parse_record_format(const char *optarg, enum recorder_format *format) {
|
parse_record_format(const char *optarg, enum recorder_format *format) {
|
||||||
if (!strcmp(optarg, "mp4")) {
|
if (!strcmp(optarg, "mp4")) {
|
||||||
|
@ -407,6 +428,7 @@ guess_record_format(const char *filename) {
|
||||||
#define OPT_WINDOW_BORDERLESS 1011
|
#define OPT_WINDOW_BORDERLESS 1011
|
||||||
#define OPT_MAX_FPS 1012
|
#define OPT_MAX_FPS 1012
|
||||||
#define OPT_LOCK_VIDEO_ORIENTATION 1013
|
#define OPT_LOCK_VIDEO_ORIENTATION 1013
|
||||||
|
#define OPT_DISPLAY_ID 1014
|
||||||
|
|
||||||
bool
|
bool
|
||||||
scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
|
scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
|
||||||
|
@ -414,6 +436,7 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
|
||||||
{"always-on-top", no_argument, NULL, OPT_ALWAYS_ON_TOP},
|
{"always-on-top", no_argument, NULL, OPT_ALWAYS_ON_TOP},
|
||||||
{"bit-rate", required_argument, NULL, 'b'},
|
{"bit-rate", required_argument, NULL, 'b'},
|
||||||
{"crop", required_argument, NULL, OPT_CROP},
|
{"crop", required_argument, NULL, OPT_CROP},
|
||||||
|
{"display", required_argument, NULL, OPT_DISPLAY_ID},
|
||||||
{"fullscreen", no_argument, NULL, 'f'},
|
{"fullscreen", no_argument, NULL, 'f'},
|
||||||
{"help", no_argument, NULL, 'h'},
|
{"help", no_argument, NULL, 'h'},
|
||||||
{"lock-video-orientation", required_argument, NULL,
|
{"lock-video-orientation", required_argument, NULL,
|
||||||
|
@ -462,6 +485,11 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
|
||||||
case OPT_CROP:
|
case OPT_CROP:
|
||||||
opts->crop = optarg;
|
opts->crop = optarg;
|
||||||
break;
|
break;
|
||||||
|
case OPT_DISPLAY_ID:
|
||||||
|
if (!parse_display_id(optarg, &opts->display_id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case 'f':
|
case 'f':
|
||||||
opts->fullscreen = true;
|
opts->fullscreen = true;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -286,6 +286,7 @@ scrcpy(const struct scrcpy_options *options) {
|
||||||
.max_fps = options->max_fps,
|
.max_fps = options->max_fps,
|
||||||
.lock_video_orientation = options->lock_video_orientation,
|
.lock_video_orientation = options->lock_video_orientation,
|
||||||
.control = options->control,
|
.control = options->control,
|
||||||
|
.display_id = options->display_id,
|
||||||
};
|
};
|
||||||
if (!server_start(&server, options->serial, ¶ms)) {
|
if (!server_start(&server, options->serial, ¶ms)) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -25,6 +25,7 @@ struct scrcpy_options {
|
||||||
int16_t window_y; // WINDOW_POSITION_UNDEFINED for "auto"
|
int16_t window_y; // WINDOW_POSITION_UNDEFINED for "auto"
|
||||||
uint16_t window_width;
|
uint16_t window_width;
|
||||||
uint16_t window_height;
|
uint16_t window_height;
|
||||||
|
uint16_t display_id;
|
||||||
bool show_touches;
|
bool show_touches;
|
||||||
bool fullscreen;
|
bool fullscreen;
|
||||||
bool always_on_top;
|
bool always_on_top;
|
||||||
|
@ -55,6 +56,7 @@ struct scrcpy_options {
|
||||||
.window_y = WINDOW_POSITION_UNDEFINED, \
|
.window_y = WINDOW_POSITION_UNDEFINED, \
|
||||||
.window_width = 0, \
|
.window_width = 0, \
|
||||||
.window_height = 0, \
|
.window_height = 0, \
|
||||||
|
.display_id = 0, \
|
||||||
.show_touches = false, \
|
.show_touches = false, \
|
||||||
.fullscreen = false, \
|
.fullscreen = false, \
|
||||||
.always_on_top = false, \
|
.always_on_top = false, \
|
||||||
|
|
|
@ -234,10 +234,12 @@ execute_server(struct server *server, const struct server_params *params) {
|
||||||
char bit_rate_string[11];
|
char bit_rate_string[11];
|
||||||
char max_fps_string[6];
|
char max_fps_string[6];
|
||||||
char lock_video_orientation_string[3];
|
char lock_video_orientation_string[3];
|
||||||
|
char display_id_string[6];
|
||||||
sprintf(max_size_string, "%"PRIu16, params->max_size);
|
sprintf(max_size_string, "%"PRIu16, params->max_size);
|
||||||
sprintf(bit_rate_string, "%"PRIu32, params->bit_rate);
|
sprintf(bit_rate_string, "%"PRIu32, params->bit_rate);
|
||||||
sprintf(max_fps_string, "%"PRIu16, params->max_fps);
|
sprintf(max_fps_string, "%"PRIu16, params->max_fps);
|
||||||
sprintf(lock_video_orientation_string, "%"PRIi8, params->lock_video_orientation);
|
sprintf(lock_video_orientation_string, "%"PRIi8, params->lock_video_orientation);
|
||||||
|
sprintf(display_id_string, "%"PRIu16, params->display_id);
|
||||||
const char *const cmd[] = {
|
const char *const cmd[] = {
|
||||||
"shell",
|
"shell",
|
||||||
"CLASSPATH=" DEVICE_SERVER_PATH,
|
"CLASSPATH=" DEVICE_SERVER_PATH,
|
||||||
|
@ -264,6 +266,7 @@ execute_server(struct server *server, const struct server_params *params) {
|
||||||
params->crop ? params->crop : "-",
|
params->crop ? params->crop : "-",
|
||||||
"true", // always send frame meta (packet boundaries + timestamp)
|
"true", // always send frame meta (packet boundaries + timestamp)
|
||||||
params->control ? "true" : "false",
|
params->control ? "true" : "false",
|
||||||
|
display_id_string,
|
||||||
};
|
};
|
||||||
#ifdef SERVER_DEBUGGER
|
#ifdef SERVER_DEBUGGER
|
||||||
LOGI("Server debugger waiting for a client on device port "
|
LOGI("Server debugger waiting for a client on device port "
|
||||||
|
|
|
@ -44,6 +44,7 @@ struct server_params {
|
||||||
uint16_t max_fps;
|
uint16_t max_fps;
|
||||||
int8_t lock_video_orientation;
|
int8_t lock_video_orientation;
|
||||||
bool control;
|
bool control;
|
||||||
|
uint16_t display_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
// init default values
|
// init default values
|
||||||
|
|
|
@ -75,19 +75,29 @@ public class Controller {
|
||||||
ControlMessage msg = connection.receiveControlMessage();
|
ControlMessage msg = connection.receiveControlMessage();
|
||||||
switch (msg.getType()) {
|
switch (msg.getType()) {
|
||||||
case ControlMessage.TYPE_INJECT_KEYCODE:
|
case ControlMessage.TYPE_INJECT_KEYCODE:
|
||||||
|
if (device.supportsInputEvents()) {
|
||||||
injectKeycode(msg.getAction(), msg.getKeycode(), msg.getMetaState());
|
injectKeycode(msg.getAction(), msg.getKeycode(), msg.getMetaState());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case ControlMessage.TYPE_INJECT_TEXT:
|
case ControlMessage.TYPE_INJECT_TEXT:
|
||||||
|
if (device.supportsInputEvents()) {
|
||||||
injectText(msg.getText());
|
injectText(msg.getText());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case ControlMessage.TYPE_INJECT_TOUCH_EVENT:
|
case ControlMessage.TYPE_INJECT_TOUCH_EVENT:
|
||||||
|
if (device.supportsInputEvents()) {
|
||||||
injectTouch(msg.getAction(), msg.getPointerId(), msg.getPosition(), msg.getPressure(), msg.getButtons());
|
injectTouch(msg.getAction(), msg.getPointerId(), msg.getPosition(), msg.getPressure(), msg.getButtons());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case ControlMessage.TYPE_INJECT_SCROLL_EVENT:
|
case ControlMessage.TYPE_INJECT_SCROLL_EVENT:
|
||||||
|
if (device.supportsInputEvents()) {
|
||||||
injectScroll(msg.getPosition(), msg.getHScroll(), msg.getVScroll());
|
injectScroll(msg.getPosition(), msg.getHScroll(), msg.getVScroll());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case ControlMessage.TYPE_BACK_OR_SCREEN_ON:
|
case ControlMessage.TYPE_BACK_OR_SCREEN_ON:
|
||||||
|
if (device.supportsInputEvents()) {
|
||||||
pressBackOrTurnScreenOn();
|
pressBackOrTurnScreenOn();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case ControlMessage.TYPE_EXPAND_NOTIFICATION_PANEL:
|
case ControlMessage.TYPE_EXPAND_NOTIFICATION_PANEL:
|
||||||
device.expandNotificationPanel();
|
device.expandNotificationPanel();
|
||||||
|
@ -103,7 +113,9 @@ public class Controller {
|
||||||
device.setClipboardText(msg.getText());
|
device.setClipboardText(msg.getText());
|
||||||
break;
|
break;
|
||||||
case ControlMessage.TYPE_SET_SCREEN_POWER_MODE:
|
case ControlMessage.TYPE_SET_SCREEN_POWER_MODE:
|
||||||
|
if (device.supportsInputEvents()) {
|
||||||
device.setScreenPowerMode(msg.getAction());
|
device.setScreenPowerMode(msg.getAction());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case ControlMessage.TYPE_ROTATE_DEVICE:
|
case ControlMessage.TYPE_ROTATE_DEVICE:
|
||||||
device.rotateDevice();
|
device.rotateDevice();
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package com.genymobile.scrcpy;
|
package com.genymobile.scrcpy;
|
||||||
|
|
||||||
|
import com.genymobile.scrcpy.wrappers.InputManager;
|
||||||
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 com.genymobile.scrcpy.wrappers.WindowManager;
|
import com.genymobile.scrcpy.wrappers.WindowManager;
|
||||||
|
@ -25,9 +26,35 @@ public final class Device {
|
||||||
private ScreenInfo screenInfo;
|
private ScreenInfo screenInfo;
|
||||||
private RotationListener rotationListener;
|
private RotationListener rotationListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logical display identifier
|
||||||
|
*/
|
||||||
|
private final int displayId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The surface flinger layer stack associated with this logical display
|
||||||
|
*/
|
||||||
|
private final int layerStack;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The FLAG_PRESENTATION from the DisplayInfo
|
||||||
|
*/
|
||||||
|
private final boolean isPresentationDisplay;
|
||||||
|
|
||||||
public Device(Options options) {
|
public Device(Options options) {
|
||||||
DisplayInfo displayInfo = serviceManager.getDisplayManager().getDisplayInfo();
|
displayId = options.getDisplayId();
|
||||||
|
DisplayInfo displayInfo = serviceManager.getDisplayManager().getDisplayInfo(displayId);
|
||||||
|
if (displayInfo == null) {
|
||||||
|
int[] displayIds = serviceManager.getDisplayManager().getDisplayIds();
|
||||||
|
throw new InvalidDisplayIdException(displayId, displayIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
int displayInfoFlags = displayInfo.getFlags();
|
||||||
|
|
||||||
screenInfo = ScreenInfo.computeScreenInfo(displayInfo, options.getCrop(), options.getMaxSize(), options.getLockedVideoOrientation());
|
screenInfo = ScreenInfo.computeScreenInfo(displayInfo, options.getCrop(), options.getMaxSize(), options.getLockedVideoOrientation());
|
||||||
|
layerStack = displayInfo.getLayerStack();
|
||||||
|
isPresentationDisplay = (displayInfoFlags & DisplayInfo.FLAG_PRESENTATION) != 0;
|
||||||
|
|
||||||
registerRotationWatcher(new IRotationWatcher.Stub() {
|
registerRotationWatcher(new IRotationWatcher.Stub() {
|
||||||
@Override
|
@Override
|
||||||
public void onRotationChanged(int rotation) throws RemoteException {
|
public void onRotationChanged(int rotation) throws RemoteException {
|
||||||
|
@ -41,12 +68,24 @@ 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");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!supportsInputEvents()) {
|
||||||
|
Ln.w("Input events are not supported for displays with FLAG_PRESENTATION enabled for devices with API lower than 29");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized ScreenInfo getScreenInfo() {
|
public synchronized ScreenInfo getScreenInfo() {
|
||||||
return screenInfo;
|
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")
|
||||||
|
@ -76,7 +115,22 @@ public final class Device {
|
||||||
return Build.MODEL;
|
return Build.MODEL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean supportsInputEvents() {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return !isPresentationDisplay;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean injectInputEvent(InputEvent inputEvent, int mode) {
|
public boolean injectInputEvent(InputEvent inputEvent, int mode) {
|
||||||
|
if (!supportsInputEvents()) {
|
||||||
|
throw new AssertionError("Could not inject input event if !supportsInputEvents()");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (displayId != 0 && !InputManager.setDisplayId(inputEvent, displayId)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return serviceManager.getInputManager().injectInputEvent(inputEvent, mode);
|
return serviceManager.getInputManager().injectInputEvent(inputEvent, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,25 @@
|
||||||
package com.genymobile.scrcpy;
|
package com.genymobile.scrcpy;
|
||||||
|
|
||||||
public final class DisplayInfo {
|
public final class DisplayInfo {
|
||||||
|
private final int displayId;
|
||||||
private final Size size;
|
private final Size size;
|
||||||
private final int rotation;
|
private final int rotation;
|
||||||
|
private final int layerStack;
|
||||||
|
private final int flags;
|
||||||
|
|
||||||
public DisplayInfo(Size size, int rotation) {
|
public static final int FLAG_SUPPORTS_PROTECTED_BUFFERS = 0x00000001;
|
||||||
|
public static final int FLAG_PRESENTATION = 0x00000008;
|
||||||
|
|
||||||
|
public DisplayInfo(int displayId, Size size, int rotation, int layerStack, int flags) {
|
||||||
|
this.displayId = displayId;
|
||||||
this.size = size;
|
this.size = size;
|
||||||
this.rotation = rotation;
|
this.rotation = rotation;
|
||||||
|
this.layerStack = layerStack;
|
||||||
|
this.flags = flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDisplayId() {
|
||||||
|
return displayId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Size getSize() {
|
public Size getSize() {
|
||||||
|
@ -16,5 +29,13 @@ public final class DisplayInfo {
|
||||||
public int getRotation() {
|
public int getRotation() {
|
||||||
return rotation;
|
return rotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getLayerStack() {
|
||||||
|
return layerStack;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getFlags() {
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
package com.genymobile.scrcpy;
|
||||||
|
|
||||||
|
public class InvalidDisplayIdException extends RuntimeException {
|
||||||
|
|
||||||
|
private final int displayId;
|
||||||
|
private final int[] availableDisplayIds;
|
||||||
|
|
||||||
|
public InvalidDisplayIdException(int displayId, int[] availableDisplayIds) {
|
||||||
|
super("There is no display having id " + displayId);
|
||||||
|
this.displayId = displayId;
|
||||||
|
this.availableDisplayIds = availableDisplayIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDisplayId() {
|
||||||
|
return displayId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int[] getAvailableDisplayIds() {
|
||||||
|
return availableDisplayIds;
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ public class Options {
|
||||||
private Rect crop;
|
private Rect crop;
|
||||||
private boolean sendFrameMeta; // send PTS so that the client may record properly
|
private boolean sendFrameMeta; // send PTS so that the client may record properly
|
||||||
private boolean control;
|
private boolean control;
|
||||||
|
private int displayId;
|
||||||
|
|
||||||
public int getMaxSize() {
|
public int getMaxSize() {
|
||||||
return maxSize;
|
return maxSize;
|
||||||
|
@ -75,4 +76,12 @@ public class Options {
|
||||||
public void setControl(boolean control) {
|
public void setControl(boolean control) {
|
||||||
this.control = control;
|
this.control = control;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getDisplayId() {
|
||||||
|
return displayId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDisplayId(int displayId) {
|
||||||
|
this.displayId = displayId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,10 +71,12 @@ public class ScreenEncoder implements Device.RotationListener {
|
||||||
// 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();
|
||||||
|
|
||||||
setSize(format, videoRect.width(), videoRect.height());
|
setSize(format, videoRect.width(), videoRect.height());
|
||||||
configure(codec, format);
|
configure(codec, format);
|
||||||
Surface surface = codec.createInputSurface();
|
Surface surface = codec.createInputSurface();
|
||||||
setDisplaySurface(display, surface, videoRotation, contentRect, unlockedVideoRect);
|
setDisplaySurface(display, surface, videoRotation, contentRect, unlockedVideoRect, layerStack);
|
||||||
codec.start();
|
codec.start();
|
||||||
try {
|
try {
|
||||||
alive = encode(codec, fd);
|
alive = encode(codec, fd);
|
||||||
|
@ -177,12 +179,12 @@ public class ScreenEncoder implements Device.RotationListener {
|
||||||
format.setInteger(MediaFormat.KEY_HEIGHT, height);
|
format.setInteger(MediaFormat.KEY_HEIGHT, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void setDisplaySurface(IBinder display, Surface surface, int orientation, Rect deviceRect, Rect displayRect) {
|
private static void setDisplaySurface(IBinder display, Surface surface, int orientation, Rect deviceRect, Rect displayRect, int layerStack) {
|
||||||
SurfaceControl.openTransaction();
|
SurfaceControl.openTransaction();
|
||||||
try {
|
try {
|
||||||
SurfaceControl.setDisplaySurface(display, surface);
|
SurfaceControl.setDisplaySurface(display, surface);
|
||||||
SurfaceControl.setDisplayProjection(display, orientation, deviceRect, displayRect);
|
SurfaceControl.setDisplayProjection(display, orientation, deviceRect, displayRect);
|
||||||
SurfaceControl.setDisplayLayerStack(display, 0);
|
SurfaceControl.setDisplayLayerStack(display, layerStack);
|
||||||
} finally {
|
} finally {
|
||||||
SurfaceControl.closeTransaction();
|
SurfaceControl.closeTransaction();
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,8 +80,8 @@ public final class Server {
|
||||||
"The server version (" + BuildConfig.VERSION_NAME + ") does not match the client " + "(" + clientVersion + ")");
|
"The server version (" + BuildConfig.VERSION_NAME + ") does not match the client " + "(" + clientVersion + ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.length != 9) {
|
if (args.length != 10) {
|
||||||
throw new IllegalArgumentException("Expecting 9 parameters");
|
throw new IllegalArgumentException("Expecting 10 parameters");
|
||||||
}
|
}
|
||||||
|
|
||||||
Options options = new Options();
|
Options options = new Options();
|
||||||
|
@ -111,6 +111,9 @@ public final class Server {
|
||||||
boolean control = Boolean.parseBoolean(args[8]);
|
boolean control = Boolean.parseBoolean(args[8]);
|
||||||
options.setControl(control);
|
options.setControl(control);
|
||||||
|
|
||||||
|
int displayId = Integer.parseInt(args[9]);
|
||||||
|
options.setDisplayId(displayId);
|
||||||
|
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,6 +152,16 @@ public final class Server {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (e instanceof InvalidDisplayIdException) {
|
||||||
|
InvalidDisplayIdException idie = (InvalidDisplayIdException) e;
|
||||||
|
int[] displayIds = idie.getAvailableDisplayIds();
|
||||||
|
if (displayIds != null && displayIds.length > 0) {
|
||||||
|
Ln.e("Try to use one of the available display ids:");
|
||||||
|
for (int id : displayIds) {
|
||||||
|
Ln.e(" scrcpy --display " + id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String... args) throws Exception {
|
public static void main(String... args) throws Exception {
|
||||||
|
|
|
@ -12,15 +12,28 @@ public final class DisplayManager {
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DisplayInfo getDisplayInfo() {
|
public DisplayInfo getDisplayInfo(int displayId) {
|
||||||
try {
|
try {
|
||||||
Object displayInfo = manager.getClass().getMethod("getDisplayInfo", int.class).invoke(manager, 0);
|
Object displayInfo = manager.getClass().getMethod("getDisplayInfo", int.class).invoke(manager, displayId);
|
||||||
|
if (displayInfo == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
Class<?> cls = displayInfo.getClass();
|
Class<?> cls = displayInfo.getClass();
|
||||||
// width and height already take the rotation into account
|
// width and height already take the rotation into account
|
||||||
int width = cls.getDeclaredField("logicalWidth").getInt(displayInfo);
|
int width = cls.getDeclaredField("logicalWidth").getInt(displayInfo);
|
||||||
int height = cls.getDeclaredField("logicalHeight").getInt(displayInfo);
|
int height = cls.getDeclaredField("logicalHeight").getInt(displayInfo);
|
||||||
int rotation = cls.getDeclaredField("rotation").getInt(displayInfo);
|
int rotation = cls.getDeclaredField("rotation").getInt(displayInfo);
|
||||||
return new DisplayInfo(new Size(width, height), rotation);
|
int layerStack = cls.getDeclaredField("layerStack").getInt(displayInfo);
|
||||||
|
int flags = cls.getDeclaredField("flags").getInt(displayInfo);
|
||||||
|
return new DisplayInfo(displayId, new Size(width, height), rotation, layerStack, flags);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int[] getDisplayIds() {
|
||||||
|
try {
|
||||||
|
return (int[]) manager.getClass().getMethod("getDisplayIds").invoke(manager);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new AssertionError(e);
|
throw new AssertionError(e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,8 @@ public final class InputManager {
|
||||||
private final IInterface manager;
|
private final IInterface manager;
|
||||||
private Method injectInputEventMethod;
|
private Method injectInputEventMethod;
|
||||||
|
|
||||||
|
private static Method setDisplayIdMethod;
|
||||||
|
|
||||||
public InputManager(IInterface manager) {
|
public InputManager(IInterface manager) {
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
}
|
}
|
||||||
|
@ -37,4 +39,23 @@ public final class InputManager {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Method getSetDisplayIdMethod() throws NoSuchMethodException {
|
||||||
|
if (setDisplayIdMethod == null) {
|
||||||
|
setDisplayIdMethod = InputEvent.class.getMethod("setDisplayId", int.class);
|
||||||
|
}
|
||||||
|
return setDisplayIdMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean setDisplayId(InputEvent inputEvent, int displayId) {
|
||||||
|
try {
|
||||||
|
Method method = getSetDisplayIdMethod();
|
||||||
|
method.invoke(inputEvent, displayId);
|
||||||
|
return true;
|
||||||
|
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
|
||||||
|
// just a warning, it might happen on old devices
|
||||||
|
Ln.w("Cannot associate a display id to the input event");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue