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 26namespace OHOS { 27namespace Rosen { 28namespace { 29constexpr HiviewDFX::HiLogLabel LABEL = {LOG_CORE, HILOG_DOMAIN_WINDOW, "Tile"}; 30constexpr uint32_t EDGE_INTERVAL = 48; 31constexpr uint32_t MID_INTERVAL = 24; 32} 33 34WindowLayoutPolicyTile::WindowLayoutPolicyTile(DisplayGroupWindowTree& displayGroupWindowTree) 35 : WindowLayoutPolicy(displayGroupWindowTree) 36{ 37} 38 39void 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 61uint32_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 72void 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 116void 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 127bool 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 158void 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 190void WindowLayoutPolicyTile::LayoutTileQueue(DisplayId displayId) 191{ 192 ApplyPresetRectForTileWindows(displayId); 193 for (auto& node : foregroundNodesMap_[displayId]) { 194 LayoutWindowNode(node); 195 } 196} 197 198bool 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 212void 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->GetModeSupportInfo(), WindowMode::WINDOW_MODE_FLOATING)) { 222 WLOGFD("Window don't support floating mode that should be minimized, winId: %{public}u, " 223 "modeSupportInfo: %{public}u", node->GetWindowId(), node->GetModeSupportInfo()); 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 239void 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 253bool 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 272void 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 302void 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 346void 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