Skip to content

Commit

Permalink
Add display id parameter
Browse files Browse the repository at this point in the history
Add --display command line parameter to specify a display id.

PR #1238 <#1238>

Signed-off-by: Romain Vimont <[email protected]>
  • Loading branch information
peanutwolf authored and rom1v committed Apr 2, 2020
1 parent 5031b2c commit 4150eed
Show file tree
Hide file tree
Showing 16 changed files with 241 additions and 16 deletions.
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,21 @@ scrcpy --no-control
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

It is possible to turn the device screen off while mirroring on start with a
Expand Down
9 changes: 9 additions & 0 deletions app/scrcpy.1
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,15 @@ The values are expressed in the device natural orientation (typically, portrait
.B \-\-max\-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
.B \-f, \-\-fullscreen
Start in fullscreen.
Expand Down
28 changes: 28 additions & 0 deletions app/src/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,15 @@ scrcpy_print_usage(const char *arg0) {
" (typically, portrait for a phone, landscape for a tablet).\n"
" Any --max-size value is computed on the cropped size.\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"
" Start in fullscreen.\n"
"\n"
Expand Down Expand Up @@ -363,6 +372,18 @@ parse_port_range(const char *s, struct port_range *port_range) {
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
parse_record_format(const char *optarg, enum recorder_format *format) {
if (!strcmp(optarg, "mp4")) {
Expand Down Expand Up @@ -407,13 +428,15 @@ guess_record_format(const char *filename) {
#define OPT_WINDOW_BORDERLESS 1011
#define OPT_MAX_FPS 1012
#define OPT_LOCK_VIDEO_ORIENTATION 1013
#define OPT_DISPLAY_ID 1014

bool
scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
static const struct option long_options[] = {
{"always-on-top", no_argument, NULL, OPT_ALWAYS_ON_TOP},
{"bit-rate", required_argument, NULL, 'b'},
{"crop", required_argument, NULL, OPT_CROP},
{"display", required_argument, NULL, OPT_DISPLAY_ID},
{"fullscreen", no_argument, NULL, 'f'},
{"help", no_argument, NULL, 'h'},
{"lock-video-orientation", required_argument, NULL,
Expand Down Expand Up @@ -462,6 +485,11 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
case OPT_CROP:
opts->crop = optarg;
break;
case OPT_DISPLAY_ID:
if (!parse_display_id(optarg, &opts->display_id)) {
return false;
}
break;
case 'f':
opts->fullscreen = true;
break;
Expand Down
1 change: 1 addition & 0 deletions app/src/scrcpy.c
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ scrcpy(const struct scrcpy_options *options) {
.max_fps = options->max_fps,
.lock_video_orientation = options->lock_video_orientation,
.control = options->control,
.display_id = options->display_id,
};
if (!server_start(&server, options->serial, &params)) {
return false;
Expand Down
2 changes: 2 additions & 0 deletions app/src/scrcpy.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ struct scrcpy_options {
int16_t window_y; // WINDOW_POSITION_UNDEFINED for "auto"
uint16_t window_width;
uint16_t window_height;
uint16_t display_id;
bool show_touches;
bool fullscreen;
bool always_on_top;
Expand Down Expand Up @@ -55,6 +56,7 @@ struct scrcpy_options {
.window_y = WINDOW_POSITION_UNDEFINED, \
.window_width = 0, \
.window_height = 0, \
.display_id = 0, \
.show_touches = false, \
.fullscreen = false, \
.always_on_top = false, \
Expand Down
3 changes: 3 additions & 0 deletions app/src/server.c
Original file line number Diff line number Diff line change
Expand Up @@ -234,10 +234,12 @@ execute_server(struct server *server, const struct server_params *params) {
char bit_rate_string[11];
char max_fps_string[6];
char lock_video_orientation_string[3];
char display_id_string[6];
sprintf(max_size_string, "%"PRIu16, params->max_size);
sprintf(bit_rate_string, "%"PRIu32, params->bit_rate);
sprintf(max_fps_string, "%"PRIu16, params->max_fps);
sprintf(lock_video_orientation_string, "%"PRIi8, params->lock_video_orientation);
sprintf(display_id_string, "%"PRIu16, params->display_id);
const char *const cmd[] = {
"shell",
"CLASSPATH=" DEVICE_SERVER_PATH,
Expand All @@ -264,6 +266,7 @@ execute_server(struct server *server, const struct server_params *params) {
params->crop ? params->crop : "-",
"true", // always send frame meta (packet boundaries + timestamp)
params->control ? "true" : "false",
display_id_string,
};
#ifdef SERVER_DEBUGGER
LOGI("Server debugger waiting for a client on device port "
Expand Down
1 change: 1 addition & 0 deletions app/src/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ struct server_params {
uint16_t max_fps;
int8_t lock_video_orientation;
bool control;
uint16_t display_id;
};

// init default values
Expand Down
24 changes: 18 additions & 6 deletions server/src/main/java/com/genymobile/scrcpy/Controller.java
Original file line number Diff line number Diff line change
Expand Up @@ -75,19 +75,29 @@ private void handleEvent() throws IOException {
ControlMessage msg = connection.receiveControlMessage();
switch (msg.getType()) {
case ControlMessage.TYPE_INJECT_KEYCODE:
injectKeycode(msg.getAction(), msg.getKeycode(), msg.getMetaState());
if (device.supportsInputEvents()) {
injectKeycode(msg.getAction(), msg.getKeycode(), msg.getMetaState());
}
break;
case ControlMessage.TYPE_INJECT_TEXT:
injectText(msg.getText());
if (device.supportsInputEvents()) {
injectText(msg.getText());
}
break;
case ControlMessage.TYPE_INJECT_TOUCH_EVENT:
injectTouch(msg.getAction(), msg.getPointerId(), msg.getPosition(), msg.getPressure(), msg.getButtons());
if (device.supportsInputEvents()) {
injectTouch(msg.getAction(), msg.getPointerId(), msg.getPosition(), msg.getPressure(), msg.getButtons());
}
break;
case ControlMessage.TYPE_INJECT_SCROLL_EVENT:
injectScroll(msg.getPosition(), msg.getHScroll(), msg.getVScroll());
if (device.supportsInputEvents()) {
injectScroll(msg.getPosition(), msg.getHScroll(), msg.getVScroll());
}
break;
case ControlMessage.TYPE_BACK_OR_SCREEN_ON:
pressBackOrTurnScreenOn();
if (device.supportsInputEvents()) {
pressBackOrTurnScreenOn();
}
break;
case ControlMessage.TYPE_EXPAND_NOTIFICATION_PANEL:
device.expandNotificationPanel();
Expand All @@ -103,7 +113,9 @@ private void handleEvent() throws IOException {
device.setClipboardText(msg.getText());
break;
case ControlMessage.TYPE_SET_SCREEN_POWER_MODE:
device.setScreenPowerMode(msg.getAction());
if (device.supportsInputEvents()) {
device.setScreenPowerMode(msg.getAction());
}
break;
case ControlMessage.TYPE_ROTATE_DEVICE:
device.rotateDevice();
Expand Down
56 changes: 55 additions & 1 deletion server/src/main/java/com/genymobile/scrcpy/Device.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.genymobile.scrcpy;

import com.genymobile.scrcpy.wrappers.InputManager;
import com.genymobile.scrcpy.wrappers.ServiceManager;
import com.genymobile.scrcpy.wrappers.SurfaceControl;
import com.genymobile.scrcpy.wrappers.WindowManager;
Expand All @@ -25,9 +26,35 @@ public interface RotationListener {
private ScreenInfo screenInfo;
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) {
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());
layerStack = displayInfo.getLayerStack();
isPresentationDisplay = (displayInfoFlags & DisplayInfo.FLAG_PRESENTATION) != 0;

registerRotationWatcher(new IRotationWatcher.Stub() {
@Override
public void onRotationChanged(int rotation) throws RemoteException {
Expand All @@ -41,12 +68,24 @@ public void onRotationChanged(int rotation) throws RemoteException {
}
}
});

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() {
return screenInfo;
}

public int getLayerStack() {
return layerStack;
}

public Point getPhysicalPoint(Position position) {
// it hides the field on purpose, to read it with a lock
@SuppressWarnings("checkstyle:HiddenField")
Expand Down Expand Up @@ -76,7 +115,22 @@ public static String getDeviceName() {
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) {
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);
}

Expand Down
23 changes: 22 additions & 1 deletion server/src/main/java/com/genymobile/scrcpy/DisplayInfo.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,25 @@
package com.genymobile.scrcpy;

public final class DisplayInfo {
private final int displayId;
private final Size size;
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.rotation = rotation;
this.layerStack = layerStack;
this.flags = flags;
}

public int getDisplayId() {
return displayId;
}

public Size getSize() {
Expand All @@ -16,5 +29,13 @@ public Size getSize() {
public int getRotation() {
return rotation;
}

public int getLayerStack() {
return layerStack;
}

public int getFlags() {
return flags;
}
}

Original file line number Diff line number Diff line change
@@ -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;
}
}
9 changes: 9 additions & 0 deletions server/src/main/java/com/genymobile/scrcpy/Options.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public class Options {
private Rect crop;
private boolean sendFrameMeta; // send PTS so that the client may record properly
private boolean control;
private int displayId;

public int getMaxSize() {
return maxSize;
Expand Down Expand Up @@ -75,4 +76,12 @@ public boolean getControl() {
public void setControl(boolean control) {
this.control = control;
}

public int getDisplayId() {
return displayId;
}

public void setDisplayId(int displayId) {
this.displayId = displayId;
}
}
Loading

0 comments on commit 4150eed

Please sign in to comment.