1 /*
2  * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "window_layout_policy_tile.h"
17 #include <ability_manager_client.h>
18 #include <hitrace_meter.h>
19 
20 #include "minimize_app.h"
21 #include "window_helper.h"
22 #include "window_inner_manager.h"
23 #include "window_manager_hilog.h"
24 #include "window_system_effect.h"
25 
26 namespace OHOS {
27 namespace Rosen {
28 namespace {
29 constexpr HiviewDFX::HiLogLabel LABEL = {LOG_CORE, HILOG_DOMAIN_WINDOW, "Tile"};
30 constexpr uint32_t EDGE_INTERVAL = 48;
31 constexpr uint32_t MID_INTERVAL = 24;
32 }
33 
WindowLayoutPolicyTile(DisplayGroupWindowTree & displayGroupWindowTree)34 WindowLayoutPolicyTile::WindowLayoutPolicyTile(DisplayGroupWindowTree& displayGroupWindowTree)
35     : WindowLayoutPolicy(displayGroupWindowTree)
36 {
37 }
38 
Launch()39 void WindowLayoutPolicyTile::Launch()
40 {
41     for (auto& iter : DisplayGroupInfo::GetInstance().GetAllDisplayRects()) {
42         const auto& displayId = iter.first;
43         /*
44          * Init tile rects and layout tile queue
45          */
46         InitTileRects(displayId);
47         InitTileQueue(displayId);
48         LayoutTileQueue(displayId);
49 
50         /*
51          * Layout above and below nodes, it is necessary when display rotatation or size change
52          */
53         auto& displayWindowTree = displayGroupWindowTree_[displayId];
54         LayoutWindowNodesByRootType(*(displayWindowTree[WindowRootNodeType::ABOVE_WINDOW_NODE]));
55         LayoutWindowNodesByRootType(*(displayWindowTree[WindowRootNodeType::BELOW_WINDOW_NODE]));
56         WLOGFD("[Launch TileLayout], displayId: %{public}" PRIu64"", displayId);
57     }
58     WLOGI("[Launch TileLayout Finished]");
59 }
60 
GetMaxTileWinNum(DisplayId displayId) const61 uint32_t WindowLayoutPolicyTile::GetMaxTileWinNum(DisplayId displayId) const
62 {
63     float virtualPixelRatio = DisplayGroupInfo::GetInstance().GetDisplayVirtualPixelRatio(displayId);
64     constexpr uint32_t half = 2;
65     uint32_t edgeIntervalVp = static_cast<uint32_t>(EDGE_INTERVAL * half * virtualPixelRatio);
66     uint32_t midIntervalVp = static_cast<uint32_t>(MID_INTERVAL * virtualPixelRatio);
67     uint32_t minFloatingW = static_cast<uint32_t>(MIN_FLOATING_WIDTH * virtualPixelRatio);
68     uint32_t drawableW = limitRectMap_[displayId].width_ - edgeIntervalVp + midIntervalVp;
69     return static_cast<uint32_t>(drawableW / (minFloatingW + midIntervalVp));
70 }
71 
InitTileRects(DisplayId displayId)72 void WindowLayoutPolicyTile::InitTileRects(DisplayId displayId)
73 {
74     // TileLayout don't consider limitRect yet, limitDisplay equals to displayRect
75     const auto& displayRect = DisplayGroupInfo::GetInstance().GetDisplayRect(displayId);
76     if (WindowHelper::IsEmptyRect(displayRect)) {
77         WLOGFE("DisplayRect is empty, displayRect: %{public}" PRIu64"", displayId);
78         return;
79     }
80 
81     limitRectMap_[displayId] = displayRect;
82     float virtualPixelRatio = DisplayGroupInfo::GetInstance().GetDisplayVirtualPixelRatio(displayId);
83     uint32_t edgeIntervalVp = static_cast<uint32_t>(EDGE_INTERVAL * virtualPixelRatio);
84     uint32_t midIntervalVp = static_cast<uint32_t>(MID_INTERVAL * virtualPixelRatio);
85 
86     constexpr float ratio = DEFAULT_ASPECT_RATIO;
87     const Rect& limitRect = limitRectMap_[displayId];
88     constexpr int half = 2;
89     maxTileWinNumMap_[displayId] = GetMaxTileWinNum(displayId);
90     WLOGFD("set max tile window num %{public}u", maxTileWinNumMap_[displayId]);
91     auto& presetRectsForAllLevel = presetRectsMap_[displayId];
92     presetRectsForAllLevel.clear();
93     uint32_t w = displayRect.width_ * ratio;
94     uint32_t h = displayRect.height_ * ratio;
95     w = w > limitRect.width_ ? limitRect.width_ : w;
96     h = h > limitRect.height_ ? limitRect.height_ : h;
97     int32_t x = limitRect.posX_ + (static_cast<int32_t>(limitRect.width_ - w) / half);
98     int32_t y = limitRect.posY_ + (static_cast<int32_t>(limitRect.height_ - h) / half);
99 
100     std::vector<Rect> single = {{ x, y, w, h }};
101     presetRectsForAllLevel.emplace_back(single);
102     for (uint32_t num = 2; num <= maxTileWinNumMap_[displayId]; num++) { // start calc preset with 2 windows
103         w = (limitRect.width_ - edgeIntervalVp * half - midIntervalVp * (num - 1)) / num;
104         std::vector<Rect> curLevel;
105         for (uint32_t i = 0; i < num; i++) {
106             int32_t curX = static_cast<int32_t>(limitRect.posX_ + edgeIntervalVp + i * (w + midIntervalVp));
107             Rect curRect = { curX, y, w, h };
108             WLOGFD("presetRectsForAllLevel: level %{public}u, id %{public}u, tileRect: [%{public}d %{public}d "
109                 "%{public}u %{public}u]", num, i, curX, y, w, h);
110             curLevel.emplace_back(curRect);
111         }
112         presetRectsForAllLevel.emplace_back(curLevel);
113     }
114 }
115 
InitTileQueue(DisplayId displayId)116 void WindowLayoutPolicyTile::InitTileQueue(DisplayId displayId)
117 {
118     foregroundNodesMap_[displayId].clear();
119     const auto& appWindowNodes = *(displayGroupWindowTree_[displayId][WindowRootNodeType::APP_WINDOW_NODE]);
120     for (auto& node : appWindowNodes) {
121         if (WindowHelper::IsMainWindow(node->GetWindowType())) {
122             PushBackNodeInTileQueue(node, displayId);
123         }
124     }
125 }
126 
IsTileRectSatisfiedWithSizeLimits(const sptr<WindowNode> & node)127 bool WindowLayoutPolicyTile::IsTileRectSatisfiedWithSizeLimits(const sptr<WindowNode>& node)
128 {
129     if (!WindowHelper::IsMainWindow(node->GetWindowType())) {
130         return true;
131     }
132     const auto& displayId = node->GetDisplayId();
133     auto& foregroundNodes = foregroundNodesMap_[displayId];
134     auto num = foregroundNodes.size();
135     if (num > maxTileWinNumMap_[displayId] || maxTileWinNumMap_[displayId] == 0) {
136         return false;
137     }
138 
139     // find if node already exits in foreground nodes map
140     if (IsWindowAlreadyInTileQueue(node)) {
141         return true;
142     }
143 
144     UpdateWindowSizeLimits(node);
145     const auto& presetRectsForAllLevel = presetRectsMap_[displayId];
146     Rect tileRect;
147     // if size of foreground nodes is equal to or more than max tile window number
148     if (num == maxTileWinNumMap_[displayId]) {
149         tileRect = *(presetRectsForAllLevel[num - 1].begin());
150     } else {  // if size of foreground nodes is less than max tile window number
151         tileRect = *(presetRectsForAllLevel[num].begin());
152     }
153     WLOGFD("id %{public}u, tileRect: [%{public}d %{public}d %{public}u %{public}u]",
154         node->GetWindowId(), tileRect.posX_, tileRect.posY_, tileRect.width_, tileRect.height_);
155     return WindowHelper::IsRectSatisfiedWithSizeLimits(tileRect, node->GetWindowUpdatedSizeLimits());
156 }
157 
PerformWindowLayout(const sptr<WindowNode> & node,WindowUpdateType updateType)158 void WindowLayoutPolicyTile::PerformWindowLayout(const sptr<WindowNode>& node, WindowUpdateType updateType)
159 {
160     HITRACE_METER(HITRACE_TAG_WINDOW_MANAGER);
161     const auto& windowType = node->GetWindowType();
162     const auto& requestRect = node->GetRequestRect();
163     WLOGI("[PerformWindowLayout] windowId: %{public}u, windowType: %{public}u, updateType: %{public}u, requestRect: "
164         "requestRect: [%{public}d, %{public}d, %{public}u, %{public}u]", node->GetWindowId(), windowType, updateType,
165         requestRect.posX_, requestRect.posY_, requestRect.width_, requestRect.height_);
166     FixWindowRectWithinDisplay(node);
167     switch (updateType) {
168         case WindowUpdateType::WINDOW_UPDATE_ADDED: {
169             if (WindowHelper::IsMainWindow(windowType)) {
170                 PushBackNodeInTileQueue(node, node->GetDisplayId());
171                 LayoutTileQueue(node->GetDisplayId());
172                 return;
173             }
174             break;
175         }
176         case WindowUpdateType::WINDOW_UPDATE_REMOVED: {
177             if (WindowHelper::IsMainWindow(windowType)) {
178                 RemoveNodeFromTileQueue(node);
179                 LayoutTileQueue(node->GetDisplayId());
180             }
181             NotifyClientAndAnimation(node, node->GetRequestRect(), WindowSizeChangeReason::HIDE);
182             return;
183         }
184         default:
185             WLOGFD("Update type is not add or remove");
186     }
187     LayoutWindowNode(node);
188 }
189 
LayoutTileQueue(DisplayId displayId)190 void WindowLayoutPolicyTile::LayoutTileQueue(DisplayId displayId)
191 {
192     ApplyPresetRectForTileWindows(displayId);
193     for (auto& node : foregroundNodesMap_[displayId]) {
194         LayoutWindowNode(node);
195     }
196 }
197 
IsWindowAlreadyInTileQueue(const sptr<WindowNode> & node)198 bool WindowLayoutPolicyTile::IsWindowAlreadyInTileQueue(const sptr<WindowNode>& node)
199 {
200     auto& foregroundNodes = foregroundNodesMap_[node->GetDisplayId()];
201     auto iter = std::find_if(foregroundNodes.begin(), foregroundNodes.end(),
202                              [node](sptr<WindowNode> foregroundNode) {
203                                  return foregroundNode->GetWindowId() == node->GetWindowId();
204                              });
205     if (iter != foregroundNodes.end()) {
206         WLOGFD("Window is already in tile queue, windowId: %{public}d", node->GetWindowId());
207         return true;
208     }
209     return false;
210 }
211 
PushBackNodeInTileQueue(const sptr<WindowNode> & node,DisplayId displayId)212 void WindowLayoutPolicyTile::PushBackNodeInTileQueue(const sptr<WindowNode>& node, DisplayId displayId)
213 {
214     if (node == nullptr) {
215         return;
216     }
217     if (IsWindowAlreadyInTileQueue(node)) {
218         return;
219     }
220 
221     if (!WindowHelper::IsWindowModeSupported(node->GetWindowModeSupportType(), WindowMode::WINDOW_MODE_FLOATING)) {
222         WLOGFD("Window don't support floating mode that should be minimized, winId: %{public}u, "
223             "windowModeSupportType: %{public}u", node->GetWindowId(), node->GetWindowModeSupportType());
224         MinimizeApp::AddNeedMinimizeApp(node, MinimizeReason::INVALID_MODE_OR_SIZE_IN_TILE);
225         return;
226     }
227     auto& foregroundNodes = foregroundNodesMap_[displayId];
228     while (!foregroundNodes.empty() && foregroundNodes.size() >= maxTileWinNumMap_[displayId]) {
229         auto removeNode = foregroundNodes.front();
230         foregroundNodes.pop_front();
231         WLOGFD("Minimize win in queue head for add new win, windowId: %{public}d", removeNode->GetWindowId());
232         MinimizeApp::AddNeedMinimizeApp(removeNode, MinimizeReason::LAYOUT_TILE);
233     }
234     foregroundNodes.push_back(node);
235     WLOGFD("Pusk back win in tile queue, displayId: %{public}" PRIu64", winId: %{public}d",
236         displayId, node->GetWindowId());
237 }
238 
RemoveNodeFromTileQueue(const sptr<WindowNode> & node)239 void WindowLayoutPolicyTile::RemoveNodeFromTileQueue(const sptr<WindowNode>& node)
240 {
241     if (node == nullptr) {
242         return;
243     }
244     DisplayId displayId = node->GetDisplayId();
245     auto& foregroundNodes = foregroundNodesMap_[displayId];
246     auto iter = std::find(foregroundNodes.begin(), foregroundNodes.end(), node);
247     if (iter != foregroundNodes.end()) {
248         WLOGFD("Remove win in tile for win id: %{public}d", node->GetWindowId());
249         foregroundNodes.erase(iter);
250     }
251 }
252 
IsValidTileQueueAndPresetRects(DisplayId displayId)253 bool WindowLayoutPolicyTile::IsValidTileQueueAndPresetRects(DisplayId displayId)
254 {
255     auto& foregroundNodes = foregroundNodesMap_[displayId];
256     uint32_t num = foregroundNodes.size();
257     auto& presetRectsForAllLevel = presetRectsMap_[displayId];
258     if (num > maxTileWinNumMap_[displayId] || num > presetRectsForAllLevel.size() || num == 0) {
259         WLOGE("Invalid tile queue, foreground tileNum: %{public}u, maxTileNum: %{public}u, presetRectsForAllLevel: "
260             "%{public}u", num, maxTileWinNumMap_[displayId], static_cast<uint32_t>(presetRectsForAllLevel.size()));
261         return false;
262     }
263     auto& presetRect = presetRectsForAllLevel[num - 1];
264     if (presetRect.size() != num) {
265         WLOGE("Invalid preset rects, foreground tileNum: %{public}u, presetRect.size(): %{public}u",
266             num, static_cast<uint32_t>(presetRect.size()));
267         return false;
268     }
269     return true;
270 }
271 
RefreshTileQueue(DisplayId displayId,std::vector<sptr<WindowNode>> & needMinimizeNodes,std::vector<sptr<WindowNode>> & needRecoverNodes)272 void WindowLayoutPolicyTile::RefreshTileQueue(DisplayId displayId,
273     std::vector<sptr<WindowNode>>& needMinimizeNodes, std::vector<sptr<WindowNode>>& needRecoverNodes)
274 {
275     /*
276      * Usually, needMinimizeNodes and needRecoverNodes will be empty, there is no need to refresh tile queue
277      */
278     auto& foregroundNodes = foregroundNodesMap_[displayId];
279     if (needMinimizeNodes.empty() && needRecoverNodes.empty()) {
280         WLOGD("No need to refresh tileQueue");
281         return;
282     }
283 
284     WLOGD("Update tile queue for the nodes which should be minimized or recovered");
285     for (auto& miniNode : needMinimizeNodes) {
286         auto iter = std::find(foregroundNodes.begin(), foregroundNodes.end(), miniNode);
287         if (iter != foregroundNodes.end()) {
288             foregroundNodes.erase(iter);
289         }
290     }
291     for (auto& recNode : needRecoverNodes) {
292         auto iter = std::find(foregroundNodes.begin(), foregroundNodes.end(), recNode);
293         if (iter == foregroundNodes.end()) {
294             foregroundNodes.push_back(recNode);
295         }
296     }
297     ApplyPresetRectForTileWindows(displayId);
298     needMinimizeNodes.clear();
299     needRecoverNodes.clear();
300 }
301 
ApplyPresetRectForTileWindows(DisplayId displayId)302 void WindowLayoutPolicyTile::ApplyPresetRectForTileWindows(DisplayId displayId)
303 {
304     if (!(IsValidTileQueueAndPresetRects(displayId))) {
305         return;
306     }
307 
308     auto& foregroundNodes = foregroundNodesMap_[displayId];
309     uint32_t num = foregroundNodes.size();
310     auto rectIt = presetRectsMap_[displayId][num - 1].begin();
311     std::vector<sptr<WindowNode>> needMinimizeNodes;
312     std::vector<sptr<WindowNode>> needRecoverNodes;
313     for (auto node : foregroundNodes) {
314         auto& rect = (*rectIt);
315         if (WindowHelper::IsRectSatisfiedWithSizeLimits(rect, node->GetWindowUpdatedSizeLimits())) {
316             node->SetWindowMode(WindowMode::WINDOW_MODE_FLOATING);
317             // when change mode, need to reset shadow and radius
318             WindowSystemEffect::SetWindowEffect(node);
319             if (node->GetWindowToken()) {
320                 node->GetWindowToken()->UpdateWindowMode(WindowMode::WINDOW_MODE_FLOATING);
321             }
322             node->SetRequestRect(rect);
323             node->SetDecoStatus(true);
324             rectIt++;
325             WLOGFD("Set preset rect for tileWin, id: %{public}d [%{public}d %{public}d %{public}d %{public}d]",
326                 node->GetWindowId(), rect.posX_, rect.posY_, rect.width_, rect.height_);
327         } else {
328             WLOGFD("Minimize node which can't be applied to tileRect, winId: %{public}u", node->GetWindowId());
329             if (num == maxTileWinNumMap_[displayId]) {
330                 // if foreground nodes equal to max tileWinNum, means need to recover one node before minimize curNode
331                 auto recoverNode = MinimizeApp::GetRecoverdNodeFromMinimizeList();
332                 if (recoverNode != nullptr) {
333                     needRecoverNodes.push_back(recoverNode);
334                     WLOGFD("Cancel minimize node from minimizeList, winId: %{public}u", node->GetWindowId());
335                 }
336             }
337             needMinimizeNodes.push_back(node);
338             MinimizeApp::AddNeedMinimizeApp(node, MinimizeReason::INVALID_MODE_OR_SIZE_IN_TILE);
339             break;
340         }
341     }
342 
343     RefreshTileQueue(displayId, needMinimizeNodes, needRecoverNodes);
344 }
345 
UpdateLayoutRect(const sptr<WindowNode> & node)346 void WindowLayoutPolicyTile::UpdateLayoutRect(const sptr<WindowNode>& node)
347 {
348     UpdateWindowSizeLimits(node);
349     bool floatingWindow = (node->GetWindowMode() == WindowMode::WINDOW_MODE_FLOATING);
350     bool needAvoid = (node->GetWindowFlags() & static_cast<uint32_t>(WindowFlag::WINDOW_FLAG_NEED_AVOID));
351     Rect lastRect = node->GetWindowRect();
352     Rect winRect = node->GetRequestRect();
353     WLOGI("[Before TileLayout] windowId: %{public}u, mode: %{public}u, type: %{public}u requestRect: [%{public}d, "
354         "%{public}d, %{public}u, %{public}u]", node->GetWindowId(), node->GetWindowMode(), node->GetWindowType(),
355         winRect.posX_, winRect.posY_, winRect.width_, winRect.height_);
356 
357     if (!floatingWindow) { // fullscreen window
358         const auto& displayRect = DisplayGroupInfo::GetInstance().GetDisplayRect(node->GetDisplayId());
359         const auto& limitDisplayRect = limitRectMap_[node->GetDisplayId()];
360         winRect = needAvoid ? limitDisplayRect : displayRect;
361     }
362 
363     WLOGI("[After TileLayout] windowId: %{public}u, isDecor: %{public}u, winRect: [%{public}d, %{public}d, "
364         "%{public}u, %{public}u]", node->GetWindowId(), node->GetDecoStatus(), winRect.posX_, winRect.posY_,
365         winRect.width_, winRect.height_);
366     node->SetWindowRect(winRect);
367 
368     // postProcess after update winRect
369     CalcAndSetNodeHotZone(winRect, node);
370     UpdateSurfaceBounds(node, winRect, lastRect);
371     NotifyClientAndAnimation(node, winRect, node->GetWindowSizeChangeReason());
372 }
373 } // Rosen
374 } // OHOS
375