# How Scrcpy Handles Screen Orientation and Rotation: A Deep Dive into the Source Code

> Explore how scrcpy handles screen orientation and rotation across device, server, and client layers. Understand the Android WindowManager, video pipeline, and rendering for seamless control.

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

---

**Scrcpy manages screen orientation through three distinct layers: device-side rotation controlled by the Android WindowManager, capture orientation enforced by the server video pipeline, and client-side display orientation applied before rendering on the host.**

Mirroring an Android display to a desktop requires precise handling of **scrcpy screen orientation and rotation** to ensure the video stream appears correctly regardless of how the physical device is held. The Genymobile/scrcpy repository implements this through a sophisticated pipeline that separates device rotation, capture orientation, and final display orientation. This article examines the source code to explain how these systems interact, from the Java server running on the Android device to the C client rendering on the desktop.

## Understanding the Three Layers of Orientation

Scrcpy distinguishes between three orientation concepts to provide flexible control:

| Concept | Applied By | Purpose |
|---------|-----------|---------|
| **Device rotation** | Android WindowManager and Device helpers | Physical rotation of the Android screen (portrait ↔ landscape) |
| **Capture orientation** | Server video pipeline (`--capture-orientation`) | Orientation of the raw video stream sent to the client |
| **Client/display orientation** | Desktop client (`--orientation`, `--display-orientation`) | Final rotation applied before rendering or recording |

## Device-Side Rotation Handling

The Android server queries and controls physical device rotation through reflection-based wrappers around the Android WindowManager service.

### Querying Current Rotation

`WindowManager.getRotation()` obtains the current device rotation by invoking either `getDefaultDisplayRotation` (Android 12+) or the legacy `getRotation` method via reflection.

```java
public int getRotation() {
    try {
        Method method = getGetRotationMethod();
        return (int) method.invoke(manager);
    } catch (ReflectiveOperationException e) {
        Ln.e("Could not invoke method", e);
        return 0;
    }
}

```

*Source: [`server/src/main/java/com/genymobile/scrcpy/wrappers/WindowManager.java`](https://github.com/Genymobile/scrcpy/blob/main/server/src/main/java/com/genymobile/scrcpy/wrappers/WindowManager.java)*

### Freezing and Applying Rotation

When the user requests a forced rotation via `scrcpy --rotate-device`, the server calls `Device.rotateDevice(int displayId)`:

```java
public static void rotateDevice(int displayId) {
    WindowManager wm = ServiceManager.getWindowManager();
    boolean accelerometerRotation = !wm.isRotationFrozen(displayId);
    int currentRotation = getCurrentRotation(displayId);
    int newRotation = (currentRotation & 1) ^ 1; // toggle portrait↔landscape
    Ln.i("Device rotation requested: " + (newRotation == 0 ? "portrait" : "landscape"));
    wm.freezeRotation(displayId, newRotation);
    if (accelerometerRotation) {
        wm.thawRotation(displayId);
    }
}

```

*Source: [`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 method toggles between portrait and landscape, freezes the rotation to the new value, and restores auto-rotation if it was previously enabled.

## Mapping Android Rotation to Scrcpy's Internal Orientation

The `Orientation` enum normalizes Android's counter-clockwise rotation values to scrcpy's clockwise orientation system:

```java
public enum Orientation {
    Orient0("0"), Orient90("90"), Orient180("180"), Orient270("270"),
    Flip0("flip0"), Flip90("flip90"), Flip180("flip180"), Flip270("flip270");
    
    public static Orientation fromRotation(int ccwRotation) {
        // Display rotation is expressed counter‑clockwise, orientation is clockwise
        int cwRotation = (4 - ccwRotation) % 4;
        return values()[cwRotation];
    }
}

```

*Source: [`server/src/main/java/com/genymobile/scrcpy/device/Orientation.java`](https://github.com/Genymobile/scrcpy/blob/main/server/src/main/java/com/genymobile/scrcpy/device/Orientation.java)*

The `fromRotation` method converts Android's 0-3 counter-clockwise values to the corresponding clockwise enum values used throughout the scrcpy pipeline.

## Capture Orientation in the Server Video Pipeline

The `--capture-orientation` option controls the orientation of the raw video stream independent of the physical device rotation.

### Parsing the Capture Orientation Option

`Options.parseCaptureOrientation` handles the `@` prefix for locking orientation:

```java
private static Pair<Orientation.Lock, Orientation> parseCaptureOrientation(String value) {
    if (value.isEmpty()) throw new IllegalArgumentException("Empty capture orientation string");
    Orientation.Lock lock;
    if (value.charAt(0) == '@') {
        value = value.substring(1);
        lock = value.isEmpty() ? Orientation.Lock.LockedInitial : Orientation.Lock.LockedValue;
    } else {
        lock = Orientation.Lock.Unlocked;
    }
    return Pair.create(lock, Orientation.getByName(value));
}

```

*Source: [`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)*

### Applying the Transform

In `NewDisplayCapture`, the server handles the oriented display size while ensuring input coordinates remain unrotated:

```java
// DisplayInfo gives the oriented size (so videoSize includes the display rotation)
// This additional display rotation must not be included in the input events transform

```

*Source: [`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)*

When a capture orientation is specified, the server applies a rotation matrix via `SurfaceControl.setDisplayProjection` but does not rotate the coordinate system used for touch input events.

## Client-Side Orientation and Rendering

The desktop client applies final transformations to the video stream before display or recording.

### Core Logic in screen.c

The `get_oriented_size` function in [`app/src/screen.c`](https://github.com/Genymobile/scrcpy/blob/main/app/src/screen.c) calculates dimensions after rotation:

```c
enum sc_orientation get_oriented_size(struct sc_size size, enum sc_orientation orientation) {
    if (sc_orientation_is_swap(orientation)) {
        // swap width/height when the orientation swaps axes
        // ...
    }
    // ...
}

```

*Source: [`app/src/screen.c`](https://github.com/Genymobile/scrcpy/blob/main/app/src/screen.c)*

### Changing Orientation at Runtime

The `sc_screen_set_orientation` function updates the display orientation dynamically:

```c
sc_screen_set_orientation(struct sc_screen *screen, enum sc_orientation orientation) {
    if (orientation == screen->orientation) return;
    get_oriented_size(screen->frame_size, orientation);
    screen->orientation = orientation;
    LOGI("Display orientation set to %s", sc_orientation_get_name(orientation));
}

```

*Source: [`app/src/screen.c`](https://github.com/Genymobile/scrcpy/blob/main/app/src/screen.c)*

### Recording Orientation

The recorder handles orientation metadata for output files:

```c
sc_recorder_set_orientation(AVStream *stream, enum sc_orientation orientation) {
    // writes rotation metadata for MP4/Matroska containers
}

```

*Source: [`app/src/recorder.c`](https://github.com/Genymobile/scrcpy/blob/main/app/src/recorder.c)*

## Practical Code Examples

### Rotate the Device to Landscape

Force the Android device to landscape mode:

```bash
scrcpy --rotate-device

```

This triggers `Device.rotateDevice()` which calls `WindowManager.freezeRotation()` to lock the orientation.

### Lock Capture Orientation

Keep the video stream fixed regardless of device rotation:

```bash
scrcpy --capture-orientation=@90

```

The `@` prefix creates a `LockedValue` lock, and the server applies a 90-degree rotation matrix via `SurfaceControl.setDisplayProjection`.

### Change Display Orientation on the Host

Rotate the scrcpy window independently of the device:

```bash
scrcpy --orientation=180

```

The client applies this rotation in `sc_screen_set_orientation()` before rendering.

### Record with Custom Orientation

Save video with specific rotation metadata:

```bash
scrcpy --record=screen.mp4 --record-orientation=flip270

```

The recorder writes the appropriate rotation tag into the MP4 container via `sc_recorder_set_orientation()`.

## Summary

- **Device rotation** is queried via `WindowManager.getRotation()` and controlled through `Device.rotateDevice()`, which freezes or thaws the Android orientation state.
- **Capture orientation** (`--capture-orientation`) locks the video stream orientation on the server side using `SurfaceControl.setDisplayProjection`, independent of physical device rotation.
- **Client orientation** (`--orientation`, `--display-orientation`) applies final transformations in [`screen.c`](https://github.com/Genymobile/scrcpy/blob/main/screen.c) before rendering, with `sc_screen_set_orientation()` handling runtime changes.
- **Recording orientation** is managed separately in [`recorder.c`](https://github.com/Genymobile/scrcpy/blob/main/recorder.c), writing rotation metadata to the output file container.

## Frequently Asked Questions

### How does scrcpy handle device rotation when the phone is physically turned?

Scrcpy detects physical rotation through `WindowManager.getRotation()` in [`WindowManager.java`](https://github.com/Genymobile/scrcpy/blob/main/WindowManager.java), which queries the Android display service. When the device rotates and no capture orientation lock is active, the server restarts the video encoder to match the new dimensions. If the `--capture-orientation` flag is locked with the `@` prefix, the server maintains the specified orientation regardless of physical rotation by applying a rotation matrix via `SurfaceControl.setDisplayProjection`.

### What is the difference between --capture-orientation and --orientation?

`--capture-orientation` controls the video stream on the Android device before transmission. It determines how the screen content is captured and encoded, and can be locked to a specific value (e.g., `@90`) to prevent rotation when the device turns. `--orientation` controls how the client displays the received video on the desktop, applying a final rotation in [`screen.c`](https://github.com/Genymobile/scrcpy/blob/main/screen.c) before rendering without affecting the underlying video stream or the device itself.

### How does scrcpy rotate the device screen programmatically?

When using `scrcpy --rotate-device`, the client sends a control message that triggers `Device.rotateDevice()` in [`Device.java`](https://github.com/Genymobile/scrcpy/blob/main/Device.java). This method queries the current rotation via `WindowManager`, calculates the opposite orientation (toggling between portrait and landscape), and calls `WindowManager.freezeRotation()` to lock the device to the new orientation. If the device was previously in auto-rotation mode, `thawRotation()` is called afterward to restore automatic rotation behavior.

### Where is the orientation metadata stored when recording video?

Recording orientation is handled in [`app/src/recorder.c`](https://github.com/Genymobile/scrcpy/blob/main/app/src/recorder.c) via `sc_recorder_set_orientation()`. This function writes rotation metadata into the output file container (MP4 or Matroska) using the appropriate AVStream fields. The orientation value comes from the `--record-orientation` command-line option and is transmitted from the server to the client as part of the video stream metadata, ensuring the recorded file plays back with the correct rotation regardless of the display orientation used during capture.