Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 41f7e9d1 authored by Adrian Roos's avatar Adrian Roos
Browse files

WM: Fix seamless rotation

With the introduction of the surface hierarchy, the seamless rotation
behavior in WSA is no longer correct: it also applies the WindowState's
offset, which leads to that being applied twice.

Instead of doing that, we simply rotate the WSA surface within the place
that WindowState dictates now.

Finally, the location of the WindowState itself also needs to be
transformed into the new orientation.

Fixes: 109927566
Test: atest CoordinateTransformsTest
Test: atest 'WindowStateTests#testSeamlesslyRotateWindow'
Change-Id: I9fb27a0a8a2bddc6ec88a4fcce6d6ea00929fb91
parent f910fdb1
Loading
Loading
Loading
Loading
+1 −4
Original line number Diff line number Diff line
@@ -1107,10 +1107,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
        }

        if (rotateSeamlessly) {
            forAllWindows(w -> {
                    w.mWinAnimator.seamlesslyRotateWindow(getPendingTransaction(),
                            oldRotation, rotation);
            }, true /* traverseTopToBottom */);
            seamlesslyRotate(getPendingTransaction(), oldRotation, rotation);
        }

        mService.mDisplayManagerInternal.performTraversal(getPendingTransaction());
+14 −0
Original line number Diff line number Diff line
@@ -735,6 +735,20 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
        return candidate;
    }

    /**
     * Seamlessly rotates the container, by recomputing the location in the new
     * rotation, and rotating buffers until they are updated for the new rotation.
     *
     * @param t the transaction to perform the seamless rotation in
     * @param oldRotation the rotation we are rotating from
     * @param newRotation the rotation we are rotating to
     */
    void seamlesslyRotate(Transaction t, int oldRotation, int newRotation) {
        for (int i = mChildren.size() - 1; i >= 0; --i) {
            mChildren.get(i).seamlesslyRotate(t, oldRotation, newRotation);
        }
    }

    /**
     * Returns true if this container is opaque and fills all the space made available by its parent
     * container.
+27 −1
Original line number Diff line number Diff line
@@ -150,6 +150,8 @@ import static com.android.server.wm.WindowStateProto.VIEW_VISIBILITY;
import static com.android.server.wm.WindowStateProto.VISIBLE_FRAME;
import static com.android.server.wm.WindowStateProto.VISIBLE_INSETS;
import static com.android.server.wm.WindowStateProto.WINDOW_CONTAINER;
import static com.android.server.wm.utils.CoordinateTransforms.transformRect;
import static com.android.server.wm.utils.CoordinateTransforms.transformToRotation;

import android.annotation.CallSuper;
import android.app.AppOpsManager;
@@ -1811,7 +1813,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
                && (mAttrs.privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0
                && !isDragResizing() && !adjustedForMinimizedDockOrIme
                && getWindowConfiguration().hasMovementAnimations()
                && !mWinAnimator.mLastHidden) {
                && !mWinAnimator.mLastHidden
                && !mSeamlesslyRotated) {
            startMoveAnimation(left, top);
        }

@@ -4850,6 +4853,29 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
        mFrameNumber = frameNumber;
    }

    @Override
    void seamlesslyRotate(Transaction t, int oldRotation, int newRotation) {
        if (!isVisibleNow() || mIsWallpaper) {
            return;
        }
        final Matrix transform = mTmpMatrix;

        mService.markForSeamlessRotation(this, true);

        // We rotated the screen, but have not performed a new layout pass yet. In the mean time,
        // we recompute the coordinates of mFrame in the new orientation, so the surface can be
        // properly placed.
        transformToRotation(oldRotation, newRotation, getDisplayInfo(), transform);
        transformRect(transform, mFrame, null /* tmpRectF */);

        updateSurfacePosition(t);
        mWinAnimator.seamlesslyRotate(t, oldRotation, newRotation);

        // Dispatch to children only after mFrame has been updated, as it's needed in the
        // child's updateSurfacePosition.
        super.seamlesslyRotate(t, oldRotation, newRotation);
    }

    private final class MoveAnimationSpec implements AnimationSpec {

        private final long mDuration;
+5 −31
Original line number Diff line number Diff line
@@ -46,13 +46,13 @@ import static com.android.server.wm.WindowStateAnimatorProto.DRAW_STATE;
import static com.android.server.wm.WindowStateAnimatorProto.LAST_CLIP_RECT;
import static com.android.server.wm.WindowStateAnimatorProto.SURFACE;
import static com.android.server.wm.WindowStateAnimatorProto.SYSTEM_DECOR_RECT;
import static com.android.server.wm.utils.CoordinateTransforms.transformToRotation;

import android.content.Context;
import android.graphics.Matrix;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
import android.os.Debug;
import android.os.Trace;
@@ -1492,40 +1492,14 @@ class WindowStateAnimator {
        }
    }

    void seamlesslyRotateWindow(SurfaceControl.Transaction t,
            int oldRotation, int newRotation) {
    void seamlesslyRotate(SurfaceControl.Transaction t, int oldRotation, int newRotation) {
        final WindowState w = mWin;
        if (!w.isVisibleNow() || w.mIsWallpaper) {
            return;
        }

        final Rect cropRect = mService.mTmpRect;
        final Rect displayRect = mService.mTmpRect2;
        final RectF frameRect = mService.mTmpRectF;
        // We rotated the screen, but have not received a new buffer with the correct size yet. In
        // the mean time, we rotate the buffer we have to the new orientation.
        final Matrix transform = mService.mTmpTransform;

        final float x = w.mFrame.left;
        final float y = w.mFrame.top;
        final float width = w.mFrame.width();
        final float height = w.mFrame.height();

        mService.getDefaultDisplayContentLocked().getBounds(displayRect);
        final float displayWidth = displayRect.width();
        final float displayHeight = displayRect.height();

        // Compute a transform matrix to undo the coordinate space transformation,
        // and present the window at the same physical position it previously occupied.
        final int deltaRotation = DisplayContent.deltaRotation(newRotation, oldRotation);
        DisplayContent.createRotationMatrix(deltaRotation, x, y, displayWidth, displayHeight,
        transformToRotation(oldRotation, newRotation, w.mFrame.width(), w.mFrame.height(),
                transform);

        // We just need to apply a rotation matrix to the window. For example
        // if we have a portrait window and rotate to landscape, the window is still portrait
        // and now extends off the bottom of the screen (and only halfway across). Essentially we
        // apply a transform to display the current buffer at it's old position
        // (in the new coordinate space). We then freeze layer updates until the resize
        // occurs, at which point we undo, them.
        mService.markForSeamlessRotation(w, true);
        transform.getValues(mService.mTmpFloats);

        float DsDx = mService.mTmpFloats[Matrix.MSCALE_X];
+93 −0
Original line number Diff line number Diff line
@@ -22,7 +22,11 @@ import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;

import android.annotation.Dimension;
import android.annotation.Nullable;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
import android.view.DisplayInfo;
import android.view.Surface.Rotation;

public class CoordinateTransforms {
@@ -59,4 +63,93 @@ public class CoordinateTransforms {
                throw new IllegalArgumentException("Unknown rotation: " + rotation);
        }
    }

    /**
     * Sets a matrix such that given a rotation, it transforms that rotation's logical coordinates
     * to physical coordinates.
     *
     * @param rotation the rotation to which the matrix should transform
     * @param out      the matrix to be set
     */
    public static void transformLogicalToPhysicalCoordinates(@Rotation int rotation,
            @Dimension int physicalWidth, @Dimension int physicalHeight, Matrix out) {
        switch (rotation) {
            case ROTATION_0:
                out.reset();
                break;
            case ROTATION_90:
                out.setRotate(90);
                out.preTranslate(0, -physicalWidth);
                break;
            case ROTATION_180:
                out.setRotate(180);
                out.preTranslate(-physicalWidth, -physicalHeight);
                break;
            case ROTATION_270:
                out.setRotate(270);
                out.preTranslate(-physicalHeight, 0);
                break;
            default:
                throw new IllegalArgumentException("Unknown rotation: " + rotation);
        }
    }

    /**
     * Sets a matrix such that given a two rotations, that it transforms coordinates given in the
     * old rotation to coordinates that refer to the same physical location in the new rotation.
     *
     * @param oldRotation the rotation to transform from
     * @param newRotation the rotation to transform to
     * @param info the display info
     * @param out a matrix that will be set to the transform
     */
    public static void transformToRotation(@Rotation int oldRotation,
            @Rotation int newRotation, DisplayInfo info, Matrix out) {
        final boolean flipped = info.rotation == ROTATION_90 || info.rotation == ROTATION_270;
        final int h = flipped ? info.logicalWidth : info.logicalHeight;
        final int w = flipped ? info.logicalHeight : info.logicalWidth;

        final Matrix tmp = new Matrix();
        transformLogicalToPhysicalCoordinates(oldRotation, w, h, out);
        transformPhysicalToLogicalCoordinates(newRotation, w, h, tmp);
        out.postConcat(tmp);
    }

    /**
     * Sets a matrix such that given a two rotations, that it transforms coordinates given in the
     * old rotation to coordinates that refer to the same physical location in the new rotation.
     *
     * @param oldRotation the rotation to transform from
     * @param newRotation the rotation to transform to
     * @param newWidth the width of the area to transform, in the new rotation
     * @param newHeight the height of the area to transform, in the new rotation
     * @param out a matrix that will be set to the transform
     */
    public static void transformToRotation(@Rotation int oldRotation,
            @Rotation int newRotation, int newWidth, int newHeight, Matrix out) {
        final boolean flipped = newRotation == ROTATION_90 || newRotation == ROTATION_270;
        final int h = flipped ? newWidth : newHeight;
        final int w = flipped ? newHeight : newWidth;

        final Matrix tmp = new Matrix();
        transformLogicalToPhysicalCoordinates(oldRotation, w, h, out);
        transformPhysicalToLogicalCoordinates(newRotation, w, h, tmp);
        out.postConcat(tmp);
    }

    /**
     * Transforms a rect using a transformation matrix
     *
     * @param transform the transformation to apply to the rect
     * @param inOutRect the rect to transform
     * @param tmp a temporary value, if null the function will allocate its own.
     */
    public static void transformRect(Matrix transform, Rect inOutRect, @Nullable RectF tmp) {
        if (tmp == null) {
            tmp = new RectF();
        }
        tmp.set(inOutRect);
        transform.mapRect(tmp);
        inOutRect.set((int) tmp.left, (int) tmp.top, (int) tmp.right, (int) tmp.bottom);
    }
}
Loading