1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.wm;
18 
19 import static android.view.Surface.ROTATION_270;
20 import static android.view.Surface.ROTATION_90;
21 
22 import android.graphics.Matrix;
23 import android.view.DisplayInfo;
24 import android.view.Surface;
25 import android.view.Surface.Rotation;
26 import android.view.SurfaceControl.Transaction;
27 
28 import com.android.server.wm.utils.CoordinateTransforms;
29 
30 import java.io.PrintWriter;
31 import java.io.StringWriter;
32 
33 /**
34  * Helper class for seamless rotation.
35  *
36  * Works by transforming the {@link WindowState} back into the old display rotation.
37  *
38  * Uses {@link android.view.SurfaceControl#deferTransactionUntil(Surface, long)} instead of
39  * latching on the buffer size to allow for seamless 180 degree rotations.
40  */
41 public class SeamlessRotator {
42 
43     private final Matrix mTransform = new Matrix();
44     private final float[] mFloat9 = new float[9];
45     private final int mOldRotation;
46     private final int mNewRotation;
47 
SeamlessRotator(@otation int oldRotation, @Rotation int newRotation, DisplayInfo info)48     public SeamlessRotator(@Rotation int oldRotation, @Rotation int newRotation, DisplayInfo info) {
49         mOldRotation = oldRotation;
50         mNewRotation = newRotation;
51 
52         final boolean flipped = info.rotation == ROTATION_90 || info.rotation == ROTATION_270;
53         final int h = flipped ? info.logicalWidth : info.logicalHeight;
54         final int w = flipped ? info.logicalHeight : info.logicalWidth;
55 
56         final Matrix tmp = new Matrix();
57         CoordinateTransforms.transformLogicalToPhysicalCoordinates(oldRotation, w, h, mTransform);
58         CoordinateTransforms.transformPhysicalToLogicalCoordinates(newRotation, w, h, tmp);
59         mTransform.postConcat(tmp);
60     }
61 
62     /**
63      * Applies a transform to the {@link WindowState} surface that undoes the effect of the global
64      * display rotation.
65      */
unrotate(Transaction transaction, WindowState win)66     public void unrotate(Transaction transaction, WindowState win) {
67         transaction.setMatrix(win.getSurfaceControl(), mTransform, mFloat9);
68 
69         // WindowState sets the position of the window so transform the position and update it.
70         final float[] winSurfacePos = {win.mLastSurfacePosition.x, win.mLastSurfacePosition.y};
71         mTransform.mapPoints(winSurfacePos);
72         transaction.setPosition(win.getSurfaceControl(), winSurfacePos[0], winSurfacePos[1]);
73     }
74 
75     /**
76      * Returns the rotation of the display before it started rotating.
77      *
78      * @return the old rotation of the display
79      */
80     @Rotation
getOldRotation()81     public int getOldRotation() {
82         return mOldRotation;
83     }
84 
85     /**
86      * Removes the transform and sets the previously known surface position for {@link WindowState}
87      * surface that undoes the effect of the global display rotation.
88      *
89      * Removing the transform and the result of the {@link WindowState} layout are both tied to the
90      * {@link WindowState} next frame, such that they apply at the same time the client draws the
91      * window in the new orientation.
92      *
93      * In the case of a rotation timeout, we want to remove the transform immediately and not defer
94      * it.
95      */
finish(WindowState win, boolean timeout)96     public void finish(WindowState win, boolean timeout) {
97         mTransform.reset();
98         final Transaction t = win.getPendingTransaction();
99         t.setMatrix(win.mSurfaceControl, mTransform, mFloat9);
100         t.setPosition(win.mSurfaceControl, win.mLastSurfacePosition.x, win.mLastSurfacePosition.y);
101         if (win.mWinAnimator.mSurfaceController != null && !timeout) {
102             t.deferTransactionUntil(win.mSurfaceControl,
103                     win.mWinAnimator.mSurfaceController.getHandle(), win.getFrameNumber());
104             t.deferTransactionUntil(win.mWinAnimator.mSurfaceController.mSurfaceControl,
105                     win.mWinAnimator.mSurfaceController.getHandle(), win.getFrameNumber());
106         }
107     }
108 
dump(PrintWriter pw)109     public void dump(PrintWriter pw) {
110         pw.print("{old="); pw.print(mOldRotation); pw.print(", new="); pw.print(mNewRotation);
111         pw.print("}");
112     }
113 
114     @Override
toString()115     public String toString() {
116         StringWriter sw = new StringWriter();
117         dump(new PrintWriter(sw));
118         return "ForcedSeamlessRotator" + sw.toString();
119     }
120 }
121