/* * Copyright (c) 2022-2023 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "display_group_controller.h" #include "window_helper.h" #include "window_inner_manager.h" #include "window_manager_hilog.h" #include "window_node_container.h" namespace OHOS { namespace Rosen { namespace { constexpr HiviewDFX::HiLogLabel LABEL = {LOG_CORE, HILOG_DOMAIN_WINDOW, "DisplayGroupController"}; } void DisplayGroupController::InitNewDisplay(DisplayId displayId) { // system bar map for display SysBarNodeMap sysBarNodeMap { { WindowType::WINDOW_TYPE_STATUS_BAR, nullptr }, { WindowType::WINDOW_TYPE_NAVIGATION_BAR, nullptr }, }; sysBarNodeMaps_.insert(std::make_pair(displayId, sysBarNodeMap)); SysBarTintMap sysBarTintMap { { WindowType::WINDOW_TYPE_STATUS_BAR, SystemBarRegionTint() }, { WindowType::WINDOW_TYPE_NAVIGATION_BAR, SystemBarRegionTint() }, }; sysBarTintMaps_.insert(std::make_pair(displayId, sysBarTintMap)); // window node maps for display std::map>>> displayWindowTree; displayWindowTree.insert(std::make_pair(WindowRootNodeType::APP_WINDOW_NODE, std::make_unique>>())); displayWindowTree.insert(std::make_pair(WindowRootNodeType::ABOVE_WINDOW_NODE, std::make_unique>>())); displayWindowTree.insert(std::make_pair(WindowRootNodeType::BELOW_WINDOW_NODE, std::make_unique>>())); displayGroupWindowTree_.insert(std::make_pair(displayId, std::move(displayWindowTree))); // window pair for display auto windowPair = new WindowPair(displayId); windowPairMap_.insert(std::make_pair(displayId, windowPair)); } std::vector>* DisplayGroupController::GetWindowNodesByDisplayIdAndRootType(DisplayId displayId, WindowRootNodeType type) { if (displayGroupWindowTree_.find(displayId) != displayGroupWindowTree_.end()) { auto& displayWindowTree = displayGroupWindowTree_[displayId]; if (displayWindowTree.find(type) != displayWindowTree.end()) { return displayWindowTree[type].get(); } } return nullptr; } void DisplayGroupController::AddWindowNodeOnWindowTree(sptr& node, WindowRootNodeType rootType) { std::vector>* rootNodeVectorPtr = GetWindowNodesByDisplayIdAndRootType(node->GetDisplayId(), rootType); if (rootNodeVectorPtr != nullptr) { rootNodeVectorPtr->push_back(node); WLOGFD("add node in node vector of root, displayId: %{public}" PRIu64" windowId: %{public}d, " "rootType: %{public}d", node->GetDisplayId(), node->GetWindowId(), rootType); } else { WLOGFE("add node failed, rootNode vector is empty, windowId: %{public}d, rootType: %{public}d", node->GetWindowId(), rootType); } } void DisplayGroupController::UpdateDisplayGroupWindowTree() { // clear ori window tree of displayGroup for (auto& elem : displayGroupWindowTree_) { for (auto& nodeVec : elem.second) { auto emptyVector = std::vector>(); nodeVec.second->swap(emptyVector); } } std::vector rootNodeType = { WindowRootNodeType::ABOVE_WINDOW_NODE, WindowRootNodeType::APP_WINDOW_NODE, WindowRootNodeType::BELOW_WINDOW_NODE }; for (auto& rootType : rootNodeType) { auto rootNode = windowNodeContainer_->GetRootNode(rootType); if (rootNode == nullptr) { WLOGFE("rootNode is nullptr, %{public}d", rootType); continue; } for (auto& node : rootNode->children_) { AddWindowNodeOnWindowTree(node, rootType); } } } void DisplayGroupController::ProcessCrossNodes(DisplayId defaultDisplayId, DisplayStateChangeType type) { defaultDisplayId_ = defaultDisplayId; for (auto& iter : displayGroupWindowTree_) { auto nodeVec = *(iter.second[WindowRootNodeType::APP_WINDOW_NODE]); for (auto node : nodeVec) { if (node->isShowingOnMultiDisplays_) { WLOGFD("process cross node, windowId: %{public}u, displayId: %{public}" PRIu64"", node->GetWindowId(), node->GetDisplayId()); auto showingDisplays = node->GetShowingDisplays(); DisplayId newDisplayId; if (type == DisplayStateChangeType::SIZE_CHANGE || type == DisplayStateChangeType::UPDATE_ROTATION || type == DisplayStateChangeType::DISPLAY_COMPRESS || type == DisplayStateChangeType::UPDATE_ROTATION_FROM_WINDOW) { newDisplayId = node->GetDisplayId(); } else { newDisplayId = defaultDisplayId; } for (auto& displayId : showingDisplays) { if (displayId == newDisplayId) { continue; } windowNodeContainer_->RemoveNodeFromRSTree(node, displayId, newDisplayId, WindowUpdateType::WINDOW_UPDATE_ACTIVE); } // update shown displays and displayId MoveCrossNodeToTargetDisplay(node, newDisplayId); } } } } void DisplayGroupController::UpdateWindowShowingDisplays(const sptr& node) { auto& displayGroupInfo = DisplayGroupInfo::GetInstance(); auto leftDisplayId = displayGroupInfo.GetLeftDisplayId(); auto rightDisplayId = displayGroupInfo.GetRightDisplayId(); auto displayRectMap = displayGroupInfo.GetAllDisplayRects(); auto showingDisplays = std::vector(); const auto& winRect = node->GetWindowRect(); for (auto& elem : displayRectMap) { auto& curDisplayRect = elem.second; // if window is showing in display region if (((winRect.posX_ + static_cast(winRect.width_)) > curDisplayRect.posX_) && (winRect.posX_ < (curDisplayRect.posX_ + static_cast(curDisplayRect.width_)))) { showingDisplays.push_back(elem.first); } } // if window is not showing on any display, maybe in the left of minPosX display, or the right of maxPosX display if (showingDisplays.empty()) { if (((winRect.posX_ + static_cast(winRect.width_)) <= displayRectMap[leftDisplayId].posX_)) { showingDisplays.push_back(leftDisplayId); } if (winRect.posX_ >= (displayRectMap[rightDisplayId].posX_ + static_cast(displayRectMap[rightDisplayId].width_))) { showingDisplays.push_back(rightDisplayId); } } // mean that this is cross-display window if (showingDisplays.size() > 1) { node->isShowingOnMultiDisplays_ = true; } else { node->isShowingOnMultiDisplays_ = false; } node->SetShowingDisplays(showingDisplays); } void DisplayGroupController::UpdateWindowDisplayIdIfNeeded(const sptr& node) { // current multi-display is only support left-right combination, maxNum is two DisplayId newDisplayId = node->GetDisplayId(); const auto& curShowingDisplays = node->GetShowingDisplays(); if (curShowingDisplays.empty()) { WLOGFE("id:%{public}u not show on any display!", node->GetWindowId()); return; } const auto& winRect = node->GetWindowRect(); if (curShowingDisplays.size() == 1) { newDisplayId = *(curShowingDisplays.begin()); } else { // if more than half width of the window is showing on the display, means the window belongs to this display int32_t halfWidth = static_cast(winRect.width_ * 0.5); const auto& displayRectMap = DisplayGroupInfo::GetInstance().GetAllDisplayRects(); for (auto& elem : displayRectMap) { auto& displayRect = elem.second; if ((winRect.posX_ < displayRect.posX_) && (winRect.posX_ + static_cast(winRect.width_) > displayRect.posX_ + static_cast(displayRect.width_))) { // window covers whole display region newDisplayId = elem.first; break; } if (winRect.posX_ >= displayRect.posX_) { // current display is default display if ((displayRect.posX_ + static_cast(displayRect.width_) - winRect.posX_) >= halfWidth) { newDisplayId = elem.first; break; } } else { // current display is expand display if ((winRect.posX_ + static_cast(winRect.width_) - displayRect.posX_) >= halfWidth) { newDisplayId = elem.first; break; } } } } // update displayId if needed if (node->GetDisplayId() != newDisplayId) { UpdateWindowDisplayId(node, newDisplayId); UpdateDisplayGroupWindowTree(); } } void DisplayGroupController::ChangeToRectInDisplayGroup(const sptr& node, DisplayId displayId) { auto displays = node->GetShowingDisplays(); if (std::find(displays.begin(), displays.end(), displayId) != displays.end()) { WLOGFD("Alreedy show in display %{public}" PRIu64 "", displayId); return; } Rect requestRect = node->GetRequestRect(); const Rect& displayRect = DisplayGroupInfo::GetInstance().GetDisplayRect(displayId); requestRect.posX_ += displayRect.posX_; requestRect.posY_ += displayRect.posY_; node->SetRequestRect(requestRect); std::vector curShowingDisplays = { node->GetDisplayId() }; node->SetShowingDisplays(curShowingDisplays); } void DisplayGroupController::PreProcessWindowNode(const sptr& node, WindowUpdateType type) { if (!windowNodeContainer_->GetLayoutPolicy()->IsMultiDisplay()) { if (type == WindowUpdateType::WINDOW_UPDATE_ADDED) { std::vector curShowingDisplays = { node->GetDisplayId() }; node->SetShowingDisplays(curShowingDisplays); for (auto& childNode : node->children_) { PreProcessWindowNode(childNode, type); } } WLOGFD("Current mode is not multi-display"); return; } switch (type) { case WindowUpdateType::WINDOW_UPDATE_ADDED: { if (!node->isShowingOnMultiDisplays_) { // change rect to rect in display group ChangeToRectInDisplayGroup(node, node->GetDisplayId()); } WLOGFD("preprocess node when add window"); break; } case WindowUpdateType::WINDOW_UPDATE_ACTIVE: { // MoveTo can be called by user, calculate rect in display group if the reason is move if (IsMoveToOrDragMove(node->GetWindowSizeChangeReason())) { ChangeToRectInDisplayGroup(node, defaultDisplayId_); } WLOGFD("preprocess node when update window"); break; } default: break; } for (auto& childNode : node->children_) { PreProcessWindowNode(childNode, type); } } void DisplayGroupController::PostProcessWindowNode(const sptr& node) { if (!windowNodeContainer_->GetLayoutPolicy()->IsMultiDisplay()) { WLOGFD("Current mode is not multi-display"); return; } UpdateWindowShowingDisplays(node); UpdateWindowDisplayIdIfNeeded(node); } void DisplayGroupController::UpdateWindowDisplayId(const sptr& node, DisplayId newDisplayId) { WLOGFD("update node displayId, srcDisplayId: %{public}" PRIu64", newDisplayId: %{public}" PRIu64"", node->GetDisplayId(), newDisplayId); if (node->GetWindowToken()) { node->GetWindowToken()->UpdateDisplayId(node->GetDisplayId(), newDisplayId); } node->SetDisplayId(newDisplayId); } void DisplayGroupController::MoveCrossNodeToTargetDisplay(const sptr& node, DisplayId targetDisplayId) { node->isShowingOnMultiDisplays_ = false; // update showing display std::vector newShowingDisplays = { targetDisplayId }; node->SetShowingDisplays(newShowingDisplays); // update new displayId if (node->GetDisplayId() != targetDisplayId) { UpdateWindowDisplayId(node, targetDisplayId); } for (auto& childNode : node->children_) { MoveCrossNodeToTargetDisplay(childNode, targetDisplayId); } } void DisplayGroupController::MoveNotCrossNodeToDefaultDisplay(const sptr& node, DisplayId displayId) { WLOGFD("windowId: %{public}d, displayId: %{public}" PRIu64"", node->GetWindowId(), displayId); // update new rect in display group const Rect& srcDisplayRect = DisplayGroupInfo::GetInstance().GetDisplayRect(displayId); const Rect& dstDisplayRect = DisplayGroupInfo::GetInstance().GetDisplayRect(defaultDisplayId_); Rect newRect = node->GetRequestRect(); if (node->GetWindowType() == WindowType::WINDOW_TYPE_POINTER) { newRect.posX_ = static_cast(dstDisplayRect.width_ / 2); // default pointerX : displayRect.width / 2 newRect.posY_ = static_cast(dstDisplayRect.height_ / 2); // default pointerY : displayRect.height / 2 } else { newRect.posX_ = newRect.posX_ - srcDisplayRect.posX_ + dstDisplayRect.posX_; newRect.posY_ = newRect.posY_ - srcDisplayRect.posY_ + dstDisplayRect.posY_; } node->SetRequestRect(newRect); // update showing display std::vector newShowingDisplays = { defaultDisplayId_ }; node->SetShowingDisplays(newShowingDisplays); // update new displayId UpdateWindowDisplayId(node, defaultDisplayId_); for (auto& childNode : node->children_) { MoveNotCrossNodeToDefaultDisplay(childNode, displayId); } } void DisplayGroupController::ProcessNotCrossNodesOnDestroyedDisplay(DisplayId displayId, std::vector& windowIds) { if (displayId == defaultDisplayId_) { WLOGFE("Move window nodes failed, displayId is the same as defaultDisplayId"); return; } if (displayGroupWindowTree_.find(displayId) == displayGroupWindowTree_.end()) { WLOGFE("displayId: %{public}" PRIu64" not in display group window tree", displayId); return; } WLOGI("move window nodes for display destroy, displayId: %{public}" PRIu64"", displayId); std::vector rootNodeType = { WindowRootNodeType::ABOVE_WINDOW_NODE, WindowRootNodeType::APP_WINDOW_NODE, WindowRootNodeType::BELOW_WINDOW_NODE }; for (const auto& type : rootNodeType) { if (displayGroupWindowTree_[displayId].find(type) == displayGroupWindowTree_[displayId].end()) { continue; } auto nodesVec = *(displayGroupWindowTree_[displayId][type]); for (auto node : nodesVec) { WLOGFD("node on destroied display, windowId: %{public}d, isShowingOnMulti: %{public}d", node->GetWindowId(), node->isShowingOnMultiDisplays_); if (node->GetDisplayId() != displayId || node->isShowingOnMultiDisplays_) { continue; } // destroy status and navigation bar if (node->GetWindowType() == WindowType::WINDOW_TYPE_STATUS_BAR || node->GetWindowType() == WindowType::WINDOW_TYPE_NAVIGATION_BAR) { windowNodeContainer_->DestroyWindowNode(node, windowIds); WLOGFW("destroy status or navigation bar on destroyed display, windowId: %{public}d", node->GetWindowId()); continue; } // move not cross-display nodes to default display MoveNotCrossNodeToDefaultDisplay(node, displayId); // update RS tree windowNodeContainer_->RemoveNodeFromRSTree(node, displayId, defaultDisplayId_, WindowUpdateType::WINDOW_UPDATE_ACTIVE); windowNodeContainer_->AddNodeOnRSTree(node, defaultDisplayId_, defaultDisplayId_, WindowUpdateType::WINDOW_UPDATE_ACTIVE); } } } void DisplayGroupController::ProcessDisplayCreate(DisplayId defaultDisplayId, sptr displayInfo, const std::map& displayRectMap) { defaultDisplayId_ = defaultDisplayId; WLOGI("defaultDisplay, displayId: %{public}" PRIu64"", defaultDisplayId); DisplayId displayId = displayInfo->GetDisplayId(); InitNewDisplay(displayId); // add displayInfo in displayGroupInfo DisplayGroupInfo::GetInstance().AddDisplayInfo(displayInfo); // modify RSTree and window tree of displayGroup for cross-display nodes ProcessCrossNodes(defaultDisplayId, DisplayStateChangeType::CREATE); UpdateDisplayGroupWindowTree(); const auto& layoutPolicy = windowNodeContainer_->GetLayoutPolicy(); if (layoutPolicy == nullptr) { return; } layoutPolicy->ProcessDisplayCreate(displayId, displayRectMap); ProcessWindowPairWhenDisplayChange(); } void DisplayGroupController::ProcessDisplayDestroy(DisplayId defaultDisplayId, sptr displayInfo, const std::map& displayRectMap, std::vector& windowIds) { defaultDisplayId_ = defaultDisplayId; DisplayGroupInfo::GetInstance().SetDefaultDisplayId(defaultDisplayId); DisplayId displayId = displayInfo->GetDisplayId(); // delete nodes and map element of deleted display ProcessNotCrossNodesOnDestroyedDisplay(displayId, windowIds); // modify RSTree and window tree of displayGroup for cross-display nodes ProcessCrossNodes(defaultDisplayId, DisplayStateChangeType::DESTROY); UpdateDisplayGroupWindowTree(); ClearMapOfDestroyedDisplay(displayId); windowNodeContainer_->GetLayoutPolicy()->ProcessDisplayDestroy(displayId, displayRectMap); ProcessWindowPairWhenDisplayChange(); } void DisplayGroupController::ProcessSystemBarRotation(const sptr& node, const std::map& displayRectMap) { auto rect = node->GetWindowRect(); auto displayId = node->GetDisplayId(); auto iter = displayRectMap.find(displayId); if (iter == displayRectMap.end()) { return; } auto displayRect = iter->second; if (node->GetWindowType() == WindowType::WINDOW_TYPE_STATUS_BAR) { rect.width_ = displayRect.width_; } else if (node->GetWindowType() == WindowType::WINDOW_TYPE_NAVIGATION_BAR) { rect.posY_ = static_cast(displayRect.height_ - rect.height_) + displayRect.posY_; rect.width_ = displayRect.width_; } node->SetRequestRect(rect); } void DisplayGroupController::UpdateNodeSizeChangeReasonWithRotation(DisplayId displayId, const std::map& displayRectMap) { std::vector rootNodeType = { WindowRootNodeType::ABOVE_WINDOW_NODE, WindowRootNodeType::APP_WINDOW_NODE, WindowRootNodeType::BELOW_WINDOW_NODE }; for (auto& rootType : rootNodeType) { std::vector>* rootNodeVectorPtr = GetWindowNodesByDisplayIdAndRootType(displayId, rootType); if (rootNodeVectorPtr == nullptr) { WLOGFE("rootNodeVectorPtr is nullptr, %{public}d, displayId: %{public}" PRIu64, rootType, displayId); return; } for (auto& node : (*rootNodeVectorPtr)) { // DOCK_SLICE not need do rotation animation if (node->GetWindowType() == WindowType::WINDOW_TYPE_DOCK_SLICE) { continue; } if (WindowHelper::IsSystemBarWindow(node->GetWindowType())) { ProcessSystemBarRotation(node, displayRectMap); } node->SetWindowSizeChangeReason(WindowSizeChangeReason::ROTATION); } } } void DisplayGroupController::ProcessDisplayChange(DisplayId defaultDisplayId, sptr displayInfo, const std::map& displayRectMap, DisplayStateChangeType type) { defaultDisplayId_ = defaultDisplayId; auto& displayGroupInfo = DisplayGroupInfo::GetInstance(); displayGroupInfo.SetDefaultDisplayId(defaultDisplayId); DisplayId displayId = displayInfo->GetDisplayId(); WLOGI("display change, displayId: %{public}" PRIu64", type: %{public}d", displayId, type); switch (type) { case DisplayStateChangeType::UPDATE_ROTATION: case DisplayStateChangeType::UPDATE_ROTATION_FROM_WINDOW: { displayGroupInfo.SetDisplayRotation(displayId, displayInfo->GetRotation()); displayGroupInfo.SetDisplayOrientation(displayId, displayInfo->GetDisplayOrientation()); displayGroupInfo.SetDisplayStateChangeType(displayId, type); [[fallthrough]]; } case DisplayStateChangeType::DISPLAY_COMPRESS: case DisplayStateChangeType::SIZE_CHANGE: { ProcessDisplaySizeChangeOrRotation(defaultDisplayId, displayId, displayRectMap, type); break; } case DisplayStateChangeType::VIRTUAL_PIXEL_RATIO_CHANGE: { displayGroupInfo.SetDisplayVirtualPixelRatio(displayId, displayInfo->GetVirtualPixelRatio()); windowNodeContainer_->GetLayoutPolicy()->ProcessDisplayVprChange(displayId); break; } default: { break; } } } void DisplayGroupController::ProcessDisplaySizeChangeOrRotation(DisplayId defaultDisplayId, DisplayId displayId, const std::map& displayRectMap, DisplayStateChangeType type) { // modify RSTree and window tree of displayGroup for cross-display nodes ProcessCrossNodes(defaultDisplayId, type); UpdateDisplayGroupWindowTree(); // update reason after process cross Nodes to get correct display attribution UpdateNodeSizeChangeReasonWithRotation(displayId, displayRectMap); const auto& layoutPolicy = windowNodeContainer_->GetLayoutPolicy(); if (layoutPolicy == nullptr) { return; } layoutPolicy->ProcessDisplaySizeChangeOrRotation(displayId, displayRectMap); ProcessWindowPairWhenDisplayChange(true); } void DisplayGroupController::ClearMapOfDestroyedDisplay(DisplayId displayId) { sysBarTintMaps_.erase(displayId); sysBarNodeMaps_.erase(displayId); displayGroupWindowTree_.erase(displayId); DisplayGroupInfo::GetInstance().RemoveDisplayInfo(displayId); windowPairMap_.erase(displayId); } sptr DisplayGroupController::GetWindowPairByDisplayId(DisplayId displayId) { if (windowPairMap_.find(displayId) != windowPairMap_.end()) { return windowPairMap_[displayId]; } return nullptr; } void DisplayGroupController::ProcessWindowPairWhenDisplayChange(bool rotateDisplay) { for (auto& elem : DisplayGroupInfo::GetInstance().GetAllDisplayRects()) { const auto& displayId = elem.first; const auto& windowPair = GetWindowPairByDisplayId(displayId); if (windowPair == nullptr) { WLOGFE("WindowPair is nullptr, displayId: %{public}" PRIu64"", displayId); return; } const auto& layoutPolicy = windowNodeContainer_->GetLayoutPolicy(); if (layoutPolicy == nullptr) { WLOGFE("LayoutPolicy is nullptr, displayId: %{public}" PRIu64"", displayId); return; } Rect divRect = layoutPolicy->GetDividerRect(displayId); if (rotateDisplay) { windowPair->RotateDividerWindow(divRect); } else { windowPair->SetDividerRect(divRect); } UpdateSplitRatioPoints(displayId); } } void DisplayGroupController::SetSplitRatioConfig(const SplitRatioConfig& splitRatioConfig) { for (auto& elem : DisplayGroupInfo::GetInstance().GetAllDisplayRects()) { const auto& displayId = elem.first; const auto& windowPair = GetWindowPairByDisplayId(displayId); if (windowPair == nullptr) { WLOGFE("WindowPair is nullptr, displayId: %{public}" PRIu64"", displayId); continue; } windowPair->SetSplitRatioConfig(splitRatioConfig); UpdateSplitRatioPoints(displayId); } } void DisplayGroupController::UpdateSplitRatioPoints(DisplayId displayId) { const auto& windowPair = GetWindowPairByDisplayId(displayId); if (windowPair == nullptr) { WLOGFE("WindowPair is nullptr, displayId: %{public}" PRIu64"", displayId); return; } auto displayRects = DisplayGroupInfo::GetInstance().GetAllDisplayRects(); if (displayRects.find(displayId) == displayRects.end()) { return; } windowPair->CalculateSplitRatioPoints(displayRects[displayId]); const auto& layoutPolicy = windowNodeContainer_->GetLayoutPolicy(); if (layoutPolicy == nullptr) { WLOGFE("LayoutPolicy is nullptr, displayId: %{public}" PRIu64"", displayId); return; } layoutPolicy->SetSplitRatioPoints(displayId, windowPair->GetSplitRatioPoints()); } } // namespace Rosen } // namespace OHOS