* Copyright (c) 2025 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 "session/host/include/layout_controller.h"
#include <cinttypes>
#include <transaction/rs_transaction.h>
#include <ui/rs_surface_node.h>
#include "display_manager.h"
#include "screen_session_manager_client/include/screen_session_manager_client.h"
#include "session/host/include/scene_persistent_storage.h"
#include "session/host/include/scene_session.h"
#include "session/host/include/session_utils.h"
#include "session_helper.h"
#include "window_helper.h"
#include "window_manager_hilog.h"
#include "wm_common_inner.h"
namespace OHOS::Rosen {
namespace {
constexpr HiviewDFX::HiLogLabel LABEL = {LOG_CORE, HILOG_DOMAIN_WINDOW, "LayoutController"};
}
LayoutController::LayoutController(const sptr<WindowSessionProperty>& property)
{
sessionProperty_ = property;
}
bool LayoutController::SetSessionGlobalRect(const WSRect& rect)
{
std::lock_guard<std::mutex> lock(globalRectMutex_);
if (globalRect_ == rect) {
return false;
}
globalRect_ = rect;
return true;
}
WSRect LayoutController::GetSessionGlobalRect() const
{
if (Session::IsScbCoreEnabled()) {
std::lock_guard<std::mutex> lock(globalRectMutex_);
return globalRect_;
}
return winRect_;
}
void LayoutController::SetClientRect(const WSRect& rect)
{
if (clientRect_ == rect) {
TLOGD(WmsLogTag::WMS_LAYOUT, "id:%{public}d skip same rect", GetSessionPersistentId());
return;
}
clientRect_ = rect;
TLOGD(WmsLogTag::WMS_LAYOUT, "Id:%{public}d, update client rect:%{public}s",
GetSessionPersistentId(), rect.ToString().c_str());
}
WSRect LayoutController::GetClientRect() const
{
return clientRect_;
}
void LayoutController::GetGlobalScaledRect(Rect& globalScaledRect)
{
WSRect scaledRect = GetSessionGlobalRect();
scaledRect.width_ *= GetScaleX();
scaledRect.height_ *= GetScaleY();
globalScaledRect = { scaledRect.posX_, scaledRect.posY_, scaledRect.width_, scaledRect.height_ };
}
WSRect LayoutController::ConvertRelativeRectToGlobal(const WSRect& relativeRect, DisplayId currentDisplayId) const
{
if (currentDisplayId == DISPLAY_ID_INVALID) {
currentDisplayId = sessionProperty_->GetDisplayId();
TLOGW(WmsLogTag::WMS_LAYOUT,
"Invalid displayId, use displayId in sessionProperty: %{public}" PRIu64, currentDisplayId);
}
auto screenSession = ScreenSessionManagerClient::GetInstance().GetScreenSessionById(currentDisplayId);
if (!screenSession) {
TLOGW(WmsLogTag::WMS_LAYOUT, "Screen session is null, displayId:%{public}" PRIu64, currentDisplayId);
return relativeRect;
}
const ScreenProperty& screenProperty = screenSession->GetScreenProperty();
int32_t currentDisplayOffsetX = static_cast<int32_t>(screenProperty.GetStartX());
int32_t currentDisplayOffsetY = static_cast<int32_t>(screenProperty.GetStartY());
TLOGI(WmsLogTag::WMS_LAYOUT, "id:%{public}d, relativeRect:%{public}s, offsetX:%{public}d, offsetY:%{public}d",
GetSessionPersistentId(), relativeRect.ToString().c_str(), currentDisplayOffsetX, currentDisplayOffsetY);
return {relativeRect.posX_ + currentDisplayOffsetX,
relativeRect.posY_ + currentDisplayOffsetY,
relativeRect.width_, relativeRect.height_};
}
WSRect LayoutController::ConvertGlobalRectToRelative(const WSRect& globalRect, DisplayId targetDisplayId) const
{
if (targetDisplayId == DISPLAY_ID_INVALID) {
targetDisplayId = sessionProperty_->GetDisplayId();
TLOGW(WmsLogTag::WMS_LAYOUT,
"Invalid displayId, use displayId in sessionProperty: %{public}" PRIu64, targetDisplayId);
}
auto screenSession = ScreenSessionManagerClient::GetInstance().GetScreenSessionById(targetDisplayId);
if (!screenSession) {
TLOGW(WmsLogTag::WMS_LAYOUT, "Screen session is null, displayId:%{public}" PRIu64, targetDisplayId);
return globalRect;
}
const ScreenProperty& screenProperty = screenSession->GetScreenProperty();
int32_t targetDisplayOffsetX = static_cast<int32_t>(screenProperty.GetStartX());
int32_t targetDisplayOffsetY = static_cast<int32_t>(screenProperty.GetStartY());
TLOGI(WmsLogTag::WMS_LAYOUT, "id:%{public}d, globalRect:%{public}s, offsetX:%{public}d, offsetY:%{public}d",
GetSessionPersistentId(), globalRect.ToString().c_str(), targetDisplayOffsetX, targetDisplayOffsetY);
return {globalRect.posX_ - targetDisplayOffsetX,
globalRect.posY_ - targetDisplayOffsetY,
globalRect.width_, globalRect.height_};
}
int32_t LayoutController::GetSessionPersistentId() const
{
return sessionProperty_->GetPersistentId();
}
void LayoutController::AdjustRectByLimits(WindowLimits limits, float ratio, bool isDecor, float vpr, WSRect& rect)
{
if (isDecor) {
rect.width_ = SessionUtils::ToLayoutWidth(rect.width_, vpr);
rect.height_ = SessionUtils::ToLayoutHeight(rect.height_, vpr);
limits.minWidth_ = SessionUtils::ToLayoutWidth(limits.minWidth_, vpr);
limits.maxWidth_ = SessionUtils::ToLayoutWidth(limits.maxWidth_, vpr);
limits.minHeight_ = SessionUtils::ToLayoutHeight(limits.minHeight_, vpr);
limits.maxHeight_ = SessionUtils::ToLayoutHeight(limits.maxHeight_, vpr);
}
if (static_cast<uint32_t>(rect.height_) > limits.maxHeight_) {
rect.height_ = static_cast<int32_t>(limits.maxHeight_);
rect.width_ = floor(rect.height_ * ratio);
} else if (static_cast<uint32_t>(rect.width_) > limits.maxWidth_) {
rect.width_ = static_cast<int32_t>(limits.maxWidth_);
rect.height_ = floor(rect.width_ / ratio);
} else if (static_cast<uint32_t>(rect.width_) < limits.minWidth_) {
rect.width_ = static_cast<int32_t>(limits.minWidth_);
rect.height_ = ceil(rect.width_ / ratio);
} else if (static_cast<uint32_t>(rect.height_) < limits.minHeight_) {
rect.height_ = static_cast<int32_t>(limits.minHeight_);
rect.width_ = ceil(rect.height_ * ratio);
}
if (isDecor) {
rect.height_ = SessionUtils::ToWinHeight(rect.height_, vpr) ;
rect.width_ = SessionUtils::ToWinWidth(rect.width_, vpr);
}
}
bool LayoutController::AdjustRectByAspectRatio(WSRect& rect, bool isDecorEnable)
{
const int tolerancePx = 2;
WSRect originalRect = rect;
if (sessionProperty_->GetWindowMode() != WindowMode::WINDOW_MODE_FLOATING ||
!WindowHelper::IsMainWindow(sessionProperty_->GetWindowType())) {
return false;
}
if (MathHelper::NearZero(aspectRatio_)) {
return false;
}
float vpr = 1.5f;
auto display = DisplayManager::GetInstance().GetDefaultDisplay();
if (display) {
vpr = display->GetVirtualPixelRatio();
}
int32_t minW;
int32_t maxW;
int32_t minH;
int32_t maxH;
SessionUtils::CalcFloatWindowRectLimits(sessionProperty_->GetWindowLimits(),
getSystemConfigFunc_().maxFloatingWindowSize_, vpr, minW, maxW, minH, maxH);
rect.width_ = std::max(minW, static_cast<int32_t>(rect.width_));
rect.width_ = std::min(maxW, static_cast<int32_t>(rect.width_));
rect.height_ = std::max(minH, static_cast<int32_t>(rect.height_));
rect.height_ = std::min(maxH, static_cast<int32_t>(rect.height_));
if (isDecorEnable) {
if (SessionUtils::ToLayoutWidth(rect.width_, vpr) >
SessionUtils::ToLayoutHeight(rect.height_, vpr) * aspectRatio_) {
rect.width_ = SessionUtils::ToWinWidth(SessionUtils::ToLayoutHeight(rect.height_, vpr)* aspectRatio_, vpr);
} else {
rect.height_ = SessionUtils::ToWinHeight(SessionUtils::ToLayoutWidth(rect.width_, vpr) / aspectRatio_, vpr);
}
} else {
if (rect.width_ > rect.height_ * aspectRatio_) {
rect.width_ = rect.height_ * aspectRatio_;
} else {
rect.height_ = rect.width_ / aspectRatio_;
}
}
AdjustRectByLimits(sessionProperty_->GetWindowLimits(), aspectRatio_, isDecorEnable, vpr, rect);
if (std::abs(static_cast<int32_t>(originalRect.width_) - static_cast<int32_t>(rect.width_)) <= tolerancePx &&
std::abs(static_cast<int32_t>(originalRect.height_) - static_cast<int32_t>(rect.height_)) <= tolerancePx) {
rect = originalRect;
return false;
}
return true;
}
WSRect LayoutController::AdjustRectByAspectRatio(const WSRect& rect, const WindowDecoration& decoration)
{
if (sessionProperty_ == nullptr) {
TLOGE(WmsLogTag::WMS_LAYOUT, "sessionProperty is null");
return rect;
}
auto windowId = GetSessionPersistentId();
auto mode = sessionProperty_->GetWindowMode();
auto type = sessionProperty_->GetWindowType();
if (mode != WindowMode::WINDOW_MODE_FLOATING || !WindowHelper::IsMainWindow(type)) {
TLOGW(WmsLogTag::WMS_LAYOUT, "Skip adjustment, windowId: %{public}d, mode: %{public}u, type: %{public}u",
windowId, static_cast<uint32_t>(mode), static_cast<uint32_t>(type));
return rect;
}
auto displayId = sessionProperty_->GetDisplayId();
auto display = DisplayManager::GetInstance().GetDisplayById(displayId);
if (!display) {
TLOGE(WmsLogTag::WMS_LAYOUT, "display is null, displayId: %{public}" PRIu64, displayId);
return rect;
}
float vpr = display->GetVirtualPixelRatio();
auto limits = sessionProperty_->GetWindowLimits();
SessionUtils::CalcFloatWindowRectLimits(getSystemConfigFunc_().maxFloatingWindowSize_, vpr, limits);
const WSRect& adjustedRect = SessionUtils::AdjustRectByAspectRatio(rect, limits, decoration, aspectRatio_);
TLOGD(WmsLogTag::WMS_LAYOUT, "After adjustment, windowId: %{public}d, rect: %{public}s",
windowId, adjustedRect.ToString().c_str());
return adjustedRect;
}
void LayoutController::SetScale(float scaleX, float scaleY, float pivotX, float pivotY)
{
scaleX_ = scaleX;
scaleY_ = scaleY;
pivotX_ = pivotX;
pivotY_ = pivotY;
}
void LayoutController::SetIgnoreRotateScale(float ignoreRotateScaleX, float ignoreRotateScaleY)
{
ignoreRotateScaleX_ = ignoreRotateScaleX;
ignoreRotateScaleY_ = ignoreRotateScaleY;
}
void LayoutController::SetClientScale(float scaleX, float scaleY, float pivotX, float pivotY)
{
TLOGD(WmsLogTag::WMS_LAYOUT, "Id:%{public}d, preScaleX:%{public}f, preScaleY:%{public}f, "
"newScaleX:%{public}f, newScaleY:%{public}f", GetSessionPersistentId(),
clientScaleX_, clientScaleY_, scaleX, scaleY);
clientScaleX_ = scaleX;
clientScaleY_ = scaleY;
clientPivotX_ = pivotX;
clientPivotY_ = pivotY;
}
bool LayoutController::IsTransformNeedUpdate(float scaleX, float scaleY, float pivotX, float pivotY)
{
bool nearEqual = NearEqual(scaleX_, scaleX) && NearEqual(scaleY_, scaleY) &&
NearEqual(pivotX_, pivotX) && NearEqual(pivotY_, pivotY) &&
NearEqual(clientScaleX_, scaleX) && NearEqual(clientScaleY_, scaleY) &&
NearEqual(clientPivotX_, pivotX) && NearEqual(clientPivotY_, pivotY);
return !nearEqual;
}
void LayoutController::SetSystemConfigFunc(GetSystemConfigFunc&& func)
{
if (!func) {
TLOGW(WmsLogTag::WMS_LAYOUT, "func is null");
return;
}
getSystemConfigFunc_ = std::move(func);
}
}