# How scrcpy Handles Multi-Display Configurations: A Deep Dive into Android Screen Mirroring

> Discover how scrcpy masterfully handles Android multi display configurations. Learn about mirroring logical displays with --display_id and creating virtual displays via --new-display.

- Repository: [Genymobile/scrcpy](https://github.com/Genymobile/scrcpy)
- Tags: deep-dive
- Published: 2026-02-25

---

**scrcpy supports multi-display configurations by allowing users to mirror existing logical displays via `--display_id` or create isolated virtual displays via `--new-display`, with dedicated code paths in the server component handling input injection, power management, and geometry changes per display.**

The Genymobile/scrcpy project provides a robust implementation for Android display streaming that extends beyond simple screen mirroring. Understanding how the codebase handles multiple displays reveals the architectural decisions that enable developers to target specific screens, create virtual environments, and manage power states across heterogeneous display topologies.

## Selecting and Mirroring Existing Displays

When targeting a specific logical display, scrcpy parses the `--display_id=<id>` command-line argument in [`server/src/main/java/com/genymobile/scrcpy/Options.java`](https://github.com/Genymobile/scrcpy/blob/main/server/src/main/java/com/genymobile/scrcpy/Options.java). The parser stores this value in `Options.displayId`, which defaults to `0` (the primary display) if unspecified.

During video source initialization, the `DeviceStreamer` creates a `VideoSource.DISPLAY` instance that passes the chosen `displayId` to `DisplayManager.getDisplayInfo(displayId)`. This wrapper method, located in [`server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayManager.java`](https://github.com/Genymobile/scrcpy/blob/main/server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayManager.java), queries the Android framework's hidden `DisplayManagerGlobal` API to retrieve the display's size, rotation, and DPI.

All subsequent video capture operations and input injections reference this specific `displayId`, ensuring the stream remains isolated to the target screen even when the device contains multiple physical or logical displays.

## Creating Virtual Displays with Custom Parameters

For scenarios requiring an isolated display environment, scrcpy implements virtual display creation through the `--new-display=<size>/<dpi>` option. The parser in [`Options.java`](https://github.com/Genymobile/scrcpy/blob/main/Options.java) instantiates a `NewDisplay` object (defined in [`server/src/main/java/com/genymobile/scrcpy/device/NewDisplay.java`](https://github.com/Genymobile/scrcpy/blob/main/server/src/main/java/com/genymobile/scrcpy/device/NewDisplay.java)) to hold the optional width, height, and density parameters.

When this option is present, `NewDisplayCapture.startNew()` (located in [`server/src/main/java/com/genymobile/scrcpy/video/NewDisplayCapture.java`](https://github.com/Genymobile/scrcpy/blob/main/server/src/main/java/com/genymobile/scrcpy/video/NewDisplayCapture.java)) invokes `DisplayManager.createNewVirtualDisplay()`. This wrapper method calls the hidden Android API `android.hardware.display.DisplayManager#createVirtualDisplay`, creating a surface-backed virtual display that exists independently of the device's physical screens.

The virtual display serves as the video frame source, allowing applications to run in an isolated environment while scrcpy streams the content to the host machine.

## Handling Display Geometry Changes

Virtual displays in scrcpy support dynamic geometry changes through the `VirtualDisplayListener` interface. In [`NewDisplayCapture.java`](https://github.com/Genymobile/scrcpy/blob/main/NewDisplayCapture.java), the constructor registers a listener that monitors size changes via `displaySizeMonitor.start()`.

When the virtual display's dimensions change (either programmatically or through system events), the listener recomputes the transformation matrices stored in `eventTransform` and `displayTransform`. These matrices ensure that touch input coordinates map correctly to the new display geometry and that the video stream maintains proper aspect ratio and orientation.

This mechanism allows scrcpy to adapt to rotating displays or resolution changes without requiring a full reconnection.

## Input Injection for Non-Primary Displays

Input event routing to secondary displays requires specific handling in [`server/src/main/java/com/genymobile/scrcpy/device/Device.java`](https://github.com/Genymobile/scrcpy/blob/main/server/src/main/java/com/genymobile/scrcpy/device/Device.java). The `injectEvent()` method first validates display support via `supportsInputEvents(displayId)`.

For non-zero `displayId` values (indicating a secondary logical display), the method calls `InputManager.setDisplayId(inputEvent, displayId)` before injection. This hidden API method attaches the target display identifier to the input event, ensuring the Android framework routes the touch, key, or motion event to the correct display rather than the default primary screen.

The method then injects the event using either synchronous or asynchronous mode based on the `INJECT_MODE` parameter.

## Power Management Across Multiple Physical Displays

On Android 10+ (API level 29 and above), scrcpy implements coordinated power management across all physical displays. The `setDisplayPower()` method in [`Device.java`](https://github.com/Genymobile/scrcpy/blob/main/Device.java) handles display power state changes (on/off) by enumerating all physical display connections.

The method obtains the list of physical display IDs through `SurfaceControl.getPhysicalDisplayIds()` on Android 10-13, or `DisplayControl.getPhysicalDisplayIds()` on Android 14+. It then iterates over each physical display ID, applying the requested power mode to each screen individually.

This ensures that when scrcpy requests a display power change, all physical displays on the device respond consistently, preventing scenarios where the primary display powers off while secondary displays remain active.

## Application Launch and Rotation Control

scrcpy provides per-display application launching through `Device.startApp()`. When launching an activity on a specific display, the method constructs an `ActivityOptions` bundle and calls `setLaunchDisplayId(displayId)` (available on Android 8+). This system API directive instructs the Android Activity Manager to start the application on the specified logical display rather than the default display.

For display rotation, `Device.rotateDevice()` differentiates between the primary display (ID 0) and secondary displays. The method forwards rotation requests to the appropriate manager—either `WindowManager` for the primary display or `DisplayManager` for secondary logical displays—ensuring that rotation commands affect the intended target screen.

## Summary

- **scrcpy** supports multi-display configurations through two primary mechanisms: mirroring existing logical displays via `--display_id` and creating isolated virtual displays via `--new-display`.
- The **Options.java** parser handles command-line arguments, storing display preferences in `displayId` and `NewDisplay` objects for downstream consumption.
- **DisplayManager** wrappers interface with hidden Android APIs to query display info, create virtual displays, and manage display geometry changes dynamically.
- **Device.java** centralizes per-display operations including input injection via `InputManager.setDisplayId()`, power management across multiple physical displays, application launching with `setLaunchDisplayId()`, and rotation control.
- Virtual displays automatically handle geometry changes through `VirtualDisplayListener` callbacks that recompute transformation matrices for accurate input mapping.

## Frequently Asked Questions

### Can scrcpy mirror a secondary display while the primary display remains off?

Yes. You can use `--display_id=1` (or the appropriate display ID) to target a secondary logical display while using `--turn-screen-off` or power commands that affect only the targeted display. On Android 10+, the `setDisplayPower()` method in [`Device.java`](https://github.com/Genymobile/scrcpy/blob/main/Device.java) iterates through all physical displays, but you can control individual logical displays separately through the display ID parameter.

### How does scrcpy handle input events when using a virtual display?

When using `--new-display`, scrcpy creates a virtual display with a unique display ID. The `Device.injectEvent()` method automatically routes input events to this display by calling `InputManager.setDisplayId(inputEvent, displayId)` before injection. This ensures touch and keyboard events reach the virtual display rather than the physical primary screen, even though the virtual display has no physical touch layer.

### What happens to the video stream if the target display changes resolution?

If the target display is a virtual display created via `--new-display`, the `NewDisplayCapture` class registers a `VirtualDisplayListener` that monitors size changes. When the resolution changes, the listener updates the `eventTransform` and `displayTransform` matrices to maintain correct coordinate mapping and aspect ratio. The video stream continues without requiring reconnection, though the client may need to handle the new resolution dimensions.

### Can I launch an application directly onto a secondary display using scrcpy?

Yes. The `Device.startApp()` method in the scrcpy server constructs an `ActivityOptions` bundle and calls `setLaunchDisplayId(displayId)` (available on Android 8.0+). When you specify a display ID via `--display_id`, subsequent app launches via the start app feature will target that specific logical display, allowing you to open applications directly on secondary screens or virtual displays.