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