1 /*
2 * Copyright (c) 2024-2025 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 "session/host/include/pc_fold_screen_manager.h"
17 #include "display_manager.h"
18 #include "window_manager_hilog.h"
19 #include "wm_math.h"
20
21 namespace OHOS::Rosen {
22 namespace {
23 // moving
24 constexpr int32_t MOVING_RESPONSE = 50;
25 constexpr float MOVING_DAMPING_RATIO = 0.98f;
26 const RSAnimationTimingProtocol MOVING_TIMING_PROTOCOL(MOVING_RESPONSE); // animation time
27 const RSAnimationTimingCurve MOVING_CURVE =
28 RSAnimationTimingCurve::CreateSpring(static_cast<float>(MOVING_RESPONSE / 1000.0f), MOVING_DAMPING_RATIO, 0.0f);
29
30 // throw-slip
31 constexpr float THROW_BACKTRACING_DURATION = 100.0f;
32 constexpr int32_t THROW_BACKTRACING_THRESHOLD = 200;
33 constexpr float THROW_SLIP_TIME = 416.0f;
34 constexpr float THROW_SLIP_DAMPING_RATIO = 0.7947f; // stiffness = 228, damping = 24
35 constexpr float THROW_SLIP_DECELERATION_RATE = 0.002;
36 const RSAnimationTimingProtocol THROW_SLIP_TIMING_PROTOCOL(std::round(THROW_SLIP_TIME)); // animation time
37 const RSAnimationTimingCurve THROW_SLIP_CURVE =
38 RSAnimationTimingCurve::CreateSpring(THROW_SLIP_TIME / 1000.0f, THROW_SLIP_DAMPING_RATIO, 0.0f);
39
40 // arrange rule
41 constexpr int32_t RULE_TRANS_X = 48; // dp
42 constexpr int32_t MIN_DECOR_HEIGHT = 37;
43 constexpr WSRect RECT_ZERO = { 0, 0, 0, 0 };
44
45 // all displayId
46 constexpr DisplayId DEFAULT_DISPLAY_ID = 0;
47 constexpr DisplayId VIRTUAL_DISPLAY_ID = 999;
48 } // namespace
49
50 WM_IMPLEMENT_SINGLE_INSTANCE(PcFoldScreenManager);
51
52 // LCOV_EXCL_START
UpdateFoldScreenStatus(DisplayId displayId,SuperFoldStatus status,const WSRect & defaultDisplayRect,const WSRect & virtualDisplayRect,const WSRect & foldCreaseRect)53 void PcFoldScreenManager::UpdateFoldScreenStatus(DisplayId displayId, SuperFoldStatus status,
54 const WSRect& defaultDisplayRect, const WSRect& virtualDisplayRect, const WSRect& foldCreaseRect)
55 {
56 DisplayId preDisplayId = DEFAULT_DISPLAY_ID;
57 SuperFoldStatus preStatus = SuperFoldStatus::UNKNOWN;
58 {
59 std::unique_lock<std::shared_mutex> lock(displayInfoMutex_);
60 preDisplayId = displayId_;
61 preStatus = screenFoldStatus_;
62 }
63 SetDisplayInfo(displayId, status);
64 SetDisplayRects(defaultDisplayRect, virtualDisplayRect, foldCreaseRect);
65 if (preDisplayId == displayId && preStatus == status) {
66 return;
67 }
68 ExecuteFoldScreenStatusChangeCallbacks(displayId, status, preStatus);
69 }
70
SetDisplayInfo(DisplayId displayId,SuperFoldStatus status)71 void PcFoldScreenManager::SetDisplayInfo(DisplayId displayId, SuperFoldStatus status)
72 {
73 std::unique_lock<std::shared_mutex> lock(displayInfoMutex_);
74 if (displayId_ == displayId && screenFoldStatus_ == status) {
75 return;
76 }
77 TLOGI(WmsLogTag::WMS_MAIN, "display: %{public}" PRIu64", fold status: %{public}d",
78 displayId, static_cast<int32_t>(status));
79 prevScreenFoldStatus_ = screenFoldStatus_;
80 screenFoldStatus_ = status;
81 ResetArrangeRule();
82 displayId_ = displayId;
83 auto display = DisplayManager::GetInstance().GetDisplayById(displayId);
84 if (display == nullptr) {
85 TLOGE(WmsLogTag::WMS_MAIN, "Failed to get display");
86 return;
87 }
88 vpr_ = display->GetVirtualPixelRatio();
89 TLOGI(WmsLogTag::WMS_MAIN, "vpr: %{public}f", vpr_);
90 }
91
SetDisplayRects(const WSRect & defaultDisplayRect,const WSRect & virtualDisplayRect,const WSRect & foldCreaseRect)92 void PcFoldScreenManager::SetDisplayRects(
93 const WSRect& defaultDisplayRect, const WSRect& virtualDisplayRect, const WSRect& foldCreaseRect)
94 {
95 TLOGI(WmsLogTag::WMS_LAYOUT_PC, "%{public}s, %{public}s, %{public}s", defaultDisplayRect.ToString().c_str(),
96 virtualDisplayRect.ToString().c_str(), foldCreaseRect.ToString().c_str());
97 std::unique_lock<std::shared_mutex> lock(rectsMutex_);
98 defaultDisplayRect_ = defaultDisplayRect;
99 virtualDisplayRect_ = virtualDisplayRect;
100 foldCreaseRect_ = foldCreaseRect;
101 }
102
GetScreenFoldStatus() const103 SuperFoldStatus PcFoldScreenManager::GetScreenFoldStatus() const
104 {
105 std::shared_lock<std::shared_mutex> lock(displayInfoMutex_);
106 return screenFoldStatus_;
107 }
108
GetScreenFoldStatus(DisplayId displayId) const109 SuperFoldStatus PcFoldScreenManager::GetScreenFoldStatus(DisplayId displayId) const
110 {
111 std::shared_lock<std::shared_mutex> lock(displayInfoMutex_);
112 if (displayId_ != displayId) {
113 return SuperFoldStatus::UNKNOWN;
114 }
115 return screenFoldStatus_;
116 }
117
IsHalfFolded(DisplayId displayId) const118 bool PcFoldScreenManager::IsHalfFolded(DisplayId displayId) const
119 {
120 std::shared_lock<std::shared_mutex> lock(displayInfoMutex_);
121 return screenFoldStatus_ == SuperFoldStatus::HALF_FOLDED && displayId_ == displayId;
122 }
123
IsHalfFoldedOnMainDisplay(DisplayId displayId) const124 bool PcFoldScreenManager::IsHalfFoldedOnMainDisplay(DisplayId displayId) const
125 {
126 std::shared_lock<std::shared_mutex> lock(displayInfoMutex_);
127 return screenFoldStatus_ == SuperFoldStatus::HALF_FOLDED && displayId == DEFAULT_DISPLAY_ID;
128 }
129
IsPcFoldScreen(DisplayId displayId) const130 bool PcFoldScreenManager::IsPcFoldScreen(DisplayId displayId) const
131 {
132 return displayId == DEFAULT_DISPLAY_ID || displayId == VIRTUAL_DISPLAY_ID;
133 }
134
UpdateSystemKeyboardStatus(bool hasSystemKeyboard)135 void PcFoldScreenManager::UpdateSystemKeyboardStatus(bool hasSystemKeyboard)
136 {
137 TLOGI(WmsLogTag::WMS_KEYBOARD, "status: %{public}d", hasSystemKeyboard);
138 std::unique_lock<std::shared_mutex> lock(displayInfoMutex_);
139 if (hasSystemKeyboard_ == hasSystemKeyboard) {
140 return;
141 }
142 hasSystemKeyboard_ = hasSystemKeyboard;
143 ExecuteSystemKeyboardStatusChangeCallbacks(displayId_, hasSystemKeyboard_);
144 }
145
HasSystemKeyboard() const146 bool PcFoldScreenManager::HasSystemKeyboard() const
147 {
148 std::shared_lock<std::shared_mutex> lock(displayInfoMutex_);
149 return hasSystemKeyboard_;
150 }
151
GetVpr() const152 float PcFoldScreenManager::GetVpr() const
153 {
154 std::shared_lock<std::shared_mutex> lock(displayInfoMutex_);
155 return vpr_;
156 }
157
GetVirtualDisplayPosY() const158 int32_t PcFoldScreenManager::GetVirtualDisplayPosY() const
159 {
160 std::shared_lock<std::shared_mutex> lock(rectsMutex_);
161 return defaultDisplayRect_.height_ + foldCreaseRect_.height_;
162 }
163
GetDisplayRects() const164 std::tuple<WSRect, WSRect, WSRect> PcFoldScreenManager::GetDisplayRects() const
165 {
166 std::shared_lock<std::shared_mutex> lock(rectsMutex_);
167 return { defaultDisplayRect_, virtualDisplayRect_, foldCreaseRect_ };
168 }
169
GetMovingTimingProtocol()170 RSAnimationTimingProtocol PcFoldScreenManager::GetMovingTimingProtocol()
171 {
172 return MOVING_TIMING_PROTOCOL;
173 }
174
GetMovingTimingCurve()175 RSAnimationTimingCurve PcFoldScreenManager::GetMovingTimingCurve()
176 {
177 return MOVING_CURVE;
178 }
179
GetThrowSlipTimingProtocol()180 RSAnimationTimingProtocol PcFoldScreenManager::GetThrowSlipTimingProtocol()
181 {
182 return THROW_SLIP_TIMING_PROTOCOL;
183 }
184
GetThrowSlipTimingCurve()185 RSAnimationTimingCurve PcFoldScreenManager::GetThrowSlipTimingCurve()
186 {
187 return THROW_SLIP_CURVE;
188 }
189
CalculateScreenSide(const WSRect & rect)190 ScreenSide PcFoldScreenManager::CalculateScreenSide(const WSRect& rect)
191 {
192 int32_t midPosY = rect.height_ / 2 + rect.posY_; // 2: center
193 const auto& [defaultDisplayRect, virtualDisplayRect, foldCreaseRect] = GetDisplayRects();
194 return midPosY <= (foldCreaseRect.posY_ + foldCreaseRect.height_ / 2) ? // 2: center
195 ScreenSide::FOLD_B : ScreenSide::FOLD_C;
196 }
197
CalculateScreenSide(int32_t posY)198 ScreenSide PcFoldScreenManager::CalculateScreenSide(int32_t posY)
199 {
200 const auto& [defaultDisplayRect, virtualDisplayRect, foldCreaseRect] = GetDisplayRects();
201 return posY < foldCreaseRect.posY_ ? ScreenSide::FOLD_B : ScreenSide::FOLD_C;
202 }
203
IsCrossFoldCrease(const WSRect & rect)204 bool PcFoldScreenManager::IsCrossFoldCrease(const WSRect& rect)
205 {
206 const auto& [defaultDisplayRect, virtualDisplayRect, foldCreaseRect] = GetDisplayRects();
207 const int32_t midScreenY = foldCreaseRect.posY_ + foldCreaseRect.height_ / 2; // 2: center
208 return rect.posY_ < midScreenY && rect.posY_ + rect.height_ > midScreenY;
209 }
210
ResetArrangeRule()211 void PcFoldScreenManager::ResetArrangeRule()
212 {
213 std::unique_lock<std::mutex> lock(arrangedRectsMutex_);
214 defaultArrangedRect_ = RECT_ZERO;
215 virtualArrangedRect_ = RECT_ZERO;
216 }
217
ResetArrangeRule(const WSRect & rect)218 void PcFoldScreenManager::ResetArrangeRule(const WSRect& rect)
219 {
220 ResetArrangeRule(CalculateScreenSide(rect));
221 }
222
ResetArrangeRule(ScreenSide side)223 void PcFoldScreenManager::ResetArrangeRule(ScreenSide side)
224 {
225 if (side != ScreenSide::FOLD_B && side != ScreenSide::FOLD_C) {
226 TLOGD(WmsLogTag::WMS_LAYOUT_PC, "invalid side: %{public}d", static_cast<int32_t>(side));
227 return;
228 }
229 std::unique_lock<std::mutex> lock(arrangedRectsMutex_);
230 if (side == ScreenSide::FOLD_B) {
231 defaultArrangedRect_ = RECT_ZERO;
232 } else { // FOLD_C
233 virtualArrangedRect_ = RECT_ZERO;
234 }
235 }
236
ResizeToFullScreen(WSRect & rect,int32_t topAvoidHeight,int32_t botAvoidHeight)237 void PcFoldScreenManager::ResizeToFullScreen(WSRect& rect, int32_t topAvoidHeight, int32_t botAvoidHeight)
238 {
239 ScreenSide side = CalculateScreenSide(rect);
240 TLOGD(WmsLogTag::WMS_LAYOUT_PC, "side: %{public}d, rect: %{public}s",
241 static_cast<int32_t>(side), rect.ToString().c_str());
242 if (side != ScreenSide::FOLD_B && side != ScreenSide::FOLD_C) {
243 TLOGW(WmsLogTag::WMS_LAYOUT_PC, "rule not avaliable, side %{public}d", static_cast<int32_t>(side));
244 return;
245 }
246
247 ResetArrangeRule(side);
248 // calculate limit rect
249 const auto& [defaultDisplayRect, virtualDisplayRect, foldCreaseRect] = GetDisplayRects();
250 WSRect limitRect = RECT_ZERO;
251 if (side == ScreenSide::FOLD_B) {
252 limitRect.posX_ = defaultDisplayRect.posX_;
253 limitRect.posY_ = defaultDisplayRect.posY_ + topAvoidHeight;
254 limitRect.width_ = defaultDisplayRect.width_;
255 limitRect.height_ = foldCreaseRect.posY_ - limitRect.posY_;
256 } else { // FOLD_C
257 limitRect.posX_ = virtualDisplayRect.posX_;
258 limitRect.posY_ = foldCreaseRect.posY_ + foldCreaseRect.height_;
259 limitRect.width_ = virtualDisplayRect.width_;
260 limitRect.height_ = virtualDisplayRect.posY_ + virtualDisplayRect.height_ - botAvoidHeight - limitRect.posY_;
261 }
262
263 rect = limitRect;
264 }
265
NeedDoThrowSlip(const WSRect & rect,const WSRectF & velocity,ScreenSide & throwSide)266 bool PcFoldScreenManager::NeedDoThrowSlip(const WSRect& rect, const WSRectF& velocity, ScreenSide& throwSide)
267 {
268 TLOGD(WmsLogTag::WMS_LAYOUT_PC, "rect: %{public}s, velocity: %{public}s, throwSide: %{public}d",
269 rect.ToString().c_str(), velocity.ToString().c_str(), static_cast<int32_t>(throwSide));
270
271 // velocity check
272 const WSRect& backtracingRect = CalculateThrowBacktracingRect(rect, velocity);
273 if (!CheckVelocityOrientation(backtracingRect, velocity) && !IsCrossFoldCrease(rect)) {
274 TLOGD(WmsLogTag::WMS_LAYOUT_PC, "orientation check failed, rect: %{public}s, velocity: %{public}s",
275 backtracingRect.ToString().c_str(), velocity.ToString().c_str());
276 return false;
277 }
278
279 ScreenSide startSide = CalculateScreenSide(backtracingRect);
280 if (IsCrossFoldCrease(rect)) {
281 startSide = MathHelper::GreatNotEqual(velocity.posY_, 0.0f) ? ScreenSide::FOLD_B : ScreenSide::FOLD_C;
282 }
283 const WSRect& endRect = CalculateThrowEnd(backtracingRect, velocity);
284 const ScreenSide endSide = CalculateScreenSide(endRect);
285 TLOGD(WmsLogTag::WMS_LAYOUT_PC, "backtracingRect: %{public}s, endRect: %{public}s",
286 backtracingRect.ToString().c_str(), endRect.ToString().c_str());
287 if (startSide == ScreenSide::FOLD_B && endSide == ScreenSide::FOLD_C) {
288 throwSide = startSide;
289 return true;
290 }
291 if (startSide == ScreenSide::FOLD_C && endSide == ScreenSide::FOLD_B) {
292 throwSide = startSide;
293 return true;
294 }
295 return false;
296 }
297
298 /*
299 * only for fullscreen cross-axis throw slip
300 */
NeedDoEasyThrowSlip(const WSRect & rect,const WSRect & startRect,const WSRectF & velocity,ScreenSide & throwSide)301 bool PcFoldScreenManager::NeedDoEasyThrowSlip(const WSRect& rect, const WSRect& startRect,
302 const WSRectF& velocity, ScreenSide& throwSide)
303 {
304 TLOGD(WmsLogTag::WMS_LAYOUT_PC,
305 "rect: %{public}s, startRect: %{public}s, velocity: %{public}s, throwSide: %{public}d",
306 rect.ToString().c_str(), startRect.ToString().c_str(),
307 velocity.ToString().c_str(), static_cast<int32_t>(throwSide));
308
309 ScreenSide startSide = CalculateScreenSide(startRect);
310 if (startSide == throwSide) {
311 return NeedDoThrowSlip(rect, velocity, throwSide);
312 }
313
314 const auto& [defaultDisplayRect, virtualDisplayRect, foldCreaseRect] = GetDisplayRects();
315 WSRect easyThrowRect = rect;
316 int32_t midY = rect.posY_ + rect.height_ / 2; // 2: center
317 if (startSide == ScreenSide::FOLD_B) {
318 if (midY > virtualDisplayRect.posY_ + virtualDisplayRect.height_ / 2) { // 2: center
319 return false;
320 }
321 easyThrowRect.posY_ = foldCreaseRect.posY_ - easyThrowRect.height_ / 2; // 2: center
322 } else {
323 if (midY < defaultDisplayRect.posY_ + defaultDisplayRect.height_ / 2) { // 2: center
324 return false;
325 }
326 easyThrowRect.posY_ = foldCreaseRect.posY_ + foldCreaseRect.height_ - easyThrowRect.height_ / 2; // 2: center
327 }
328 return NeedDoThrowSlip(easyThrowRect, velocity, throwSide);
329 }
330
CheckVelocityOrientation(const WSRect & rect,const WSRectF & velocity)331 bool PcFoldScreenManager::CheckVelocityOrientation(const WSRect& rect, const WSRectF& velocity)
332 {
333 const auto& [defaultDisplayRect, virtualDisplayRect, foldCreaseRect] = GetDisplayRects();
334 const int32_t centerX = rect.posX_ + rect.width_ / 2; // 2: center
335 const int32_t centerY = rect.posY_ + rect.height_ / 2; // 2: center
336 ScreenSide startSide = CalculateScreenSide(rect);
337 int32_t aimX = 0;
338 int32_t aimY = 0;
339 if (startSide == ScreenSide::FOLD_B) {
340 if (MathHelper::LessNotEqual(velocity.posX_, 0.0f)) {
341 aimX = defaultDisplayRect.posX_;
342 aimY = defaultDisplayRect.posY_ + defaultDisplayRect.height_;
343 } else {
344 aimX = defaultDisplayRect.posX_ + defaultDisplayRect.width_;
345 aimY = defaultDisplayRect.posY_ + defaultDisplayRect.height_;
346 }
347 return MathHelper::GreatNotEqual(velocity.posY_,
348 std::abs(static_cast<float>(aimY - centerY) /
349 MathHelper::NonZero(static_cast<float>(aimX - centerX)) * velocity.posX_));
350 }
351 if (startSide == ScreenSide::FOLD_C) {
352 if (MathHelper::LessNotEqual(velocity.posX_, 0.0f)) {
353 aimX = virtualDisplayRect.posX_;
354 aimY = virtualDisplayRect.posY_;
355 } else {
356 aimX = virtualDisplayRect.posX_ + virtualDisplayRect.width_;
357 aimY = virtualDisplayRect.posY_;
358 }
359 return MathHelper::LessNotEqual(velocity.posY_,
360 -std::abs(static_cast<float>(aimY - centerY) /
361 MathHelper::NonZero(static_cast<float>(aimX - centerX)) * velocity.posX_));
362 }
363
364 return false;
365 }
366
CalculateThrowBacktracingRect(const WSRect & rect,const WSRectF & velocity)367 WSRect PcFoldScreenManager::CalculateThrowBacktracingRect(const WSRect& rect, const WSRectF& velocity)
368 {
369 int32_t midPosY = rect.height_ / 2 + rect.posY_; // 2: center
370 const auto& [defaultDisplayRect, virtualDisplayRect, foldCreaseRect] = GetDisplayRects();
371 const int32_t midScreenY = defaultDisplayRect.posY_ + defaultDisplayRect.height_;
372 bool isInUpperThreshold = midPosY < midScreenY && midPosY >= (midScreenY - THROW_BACKTRACING_THRESHOLD);
373 bool isInLowerThreshold = midPosY >= midScreenY && midPosY <= (midScreenY + THROW_BACKTRACING_THRESHOLD);
374 if ((!isInUpperThreshold && MathHelper::LessNotEqual(velocity.posY_, 0.0f)) ||
375 (!isInLowerThreshold && MathHelper::GreatNotEqual(velocity.posY_, 0.0f))) {
376 return rect;
377 }
378 return WSRect{static_cast<int32_t>(rect.posX_ - velocity.posX_ * THROW_BACKTRACING_DURATION),
379 static_cast<int32_t>(rect.posY_ - velocity.posY_ * THROW_BACKTRACING_DURATION),
380 rect.width_, rect.height_};
381 }
382
CalculateThrowEnd(const WSRect & rect,const WSRectF & velocity)383 WSRect PcFoldScreenManager::CalculateThrowEnd(const WSRect& rect, const WSRectF& velocity)
384 {
385 return WSRect{ static_cast<int32_t>(rect.posX_ + velocity.posX_ / THROW_SLIP_DECELERATION_RATE),
386 static_cast<int32_t>(rect.posY_ + velocity.posY_ / THROW_SLIP_DECELERATION_RATE),
387 rect.width_, rect.height_ };
388 }
389
390 /*
391 * move rect to other side
392 * @param rect: current side, moved to other side
393 * @param titleHeight: used in arrange rule to avoid title bar
394 */
ThrowSlipToOppositeSide(ScreenSide startSide,WSRect & rect,int32_t topAvoidHeight,int32_t botAvoidHeight,int32_t titleHeight)395 bool PcFoldScreenManager::ThrowSlipToOppositeSide(ScreenSide startSide, WSRect& rect,
396 int32_t topAvoidHeight, int32_t botAvoidHeight, int32_t titleHeight)
397 {
398 if (startSide != ScreenSide::FOLD_B && startSide != ScreenSide::FOLD_C) {
399 return false;
400 }
401
402 const auto& [defaultDisplayRect, virtualDisplayRect, foldCreaseRect] = GetDisplayRects();
403 int32_t topLimit = 0;
404 int32_t botLimit = 0;
405 const WSRect defaultLimitRect = {
406 defaultDisplayRect.posX_, defaultDisplayRect.posY_ + topAvoidHeight,
407 defaultDisplayRect.width_, defaultDisplayRect.height_ - topAvoidHeight };
408 const WSRect virtualLimitRect = {
409 virtualDisplayRect.posX_, virtualDisplayRect.posY_,
410 virtualDisplayRect.width_, virtualDisplayRect.height_ - botAvoidHeight };
411 const WSRect& startLimitRect = startSide == ScreenSide::FOLD_B ? defaultLimitRect : virtualLimitRect;
412 const WSRect& endLimitRect = startSide == ScreenSide::FOLD_B ? virtualLimitRect : defaultLimitRect;
413 if (rect.height_ < startLimitRect.height_ && rect.height_ < endLimitRect.height_ &&
414 rect.posY_ > startLimitRect.posY_) {
415 float ratio = static_cast<float>(endLimitRect.height_ - rect.height_) /
416 static_cast<float>(startLimitRect.height_ - rect.height_);
417 // after mutiple throw-slip, posY converges
418 if (MathHelper::GreatNotEqual(ratio, 1.0f)) {
419 rect.posY_ = MathHelper::Floor(ratio * static_cast<float>(rect.posY_ - startLimitRect.posY_)) +
420 endLimitRect.posY_;
421 } else {
422 rect.posY_ = MathHelper::Ceil(ratio * static_cast<float>(rect.posY_ - startLimitRect.posY_)) +
423 endLimitRect.posY_;
424 }
425 } else {
426 rect.posY_ = rect.posY_ + endLimitRect.posY_ - startLimitRect.posY_;
427 }
428 // top limit first
429 rect.posY_ = std::max(std::min(rect.posY_, endLimitRect.posY_ + endLimitRect.height_ - rect.height_),
430 endLimitRect.posY_);
431 TLOGD(WmsLogTag::WMS_LAYOUT_PC, "end posY: %{public}d", rect.posY_);
432 return true;
433 }
434
MappingRectInScreenSide(ScreenSide side,WSRect & rect,int32_t topAvoidHeight,int32_t botAvoidHeight)435 void PcFoldScreenManager::MappingRectInScreenSide(ScreenSide side, WSRect& rect,
436 int32_t topAvoidHeight, int32_t botAvoidHeight)
437 {
438 TLOGD(WmsLogTag::WMS_LAYOUT_PC, "side: %{public}d, rect: %{public}s, avoid heights: [%{public}d,%{public}d]",
439 static_cast<int32_t>(side), rect.ToString().c_str(), topAvoidHeight, botAvoidHeight);
440 WSRect topLeftLimit = RECT_ZERO;
441 WSRect botRightLimit = RECT_ZERO;
442 const auto& [defaultDisplayRect, virtualDisplayRect, foldCreaseRect] = GetDisplayRects();
443 float vpr = GetVpr();
444 switch (side) {
445 case ScreenSide::FOLD_B:
446 topLeftLimit.posX_ = MathHelper::Ceil(RULE_TRANS_X * vpr) - rect.width_;
447 topLeftLimit.posY_ = topAvoidHeight;
448 botRightLimit.posX_ = std::max(0,
449 MathHelper::Floor(defaultDisplayRect.width_ - RULE_TRANS_X * vpr));
450 botRightLimit.posY_ = std::max(0, foldCreaseRect.posY_ - rect.height_);
451 botRightLimit.width_ = defaultDisplayRect.width_;
452 botRightLimit.height_ = std::max(0, foldCreaseRect.posY_ - topLeftLimit.posY_);
453 break;
454 case ScreenSide::FOLD_C:
455 topLeftLimit.posX_ = MathHelper::Ceil(RULE_TRANS_X * vpr) - rect.width_;
456 topLeftLimit.posY_ = foldCreaseRect.posY_ + foldCreaseRect.height_;
457 botRightLimit.posX_ = std::max(0,
458 MathHelper::Floor(virtualDisplayRect.width_ - RULE_TRANS_X * vpr));
459 botRightLimit.posY_ = std::max(foldCreaseRect.posY_ + foldCreaseRect.height_,
460 MathHelper::Floor(virtualDisplayRect.posY_ + virtualDisplayRect.height_ -
461 botAvoidHeight - MIN_DECOR_HEIGHT * vpr));
462 botRightLimit.width_ = virtualDisplayRect.width_;
463 botRightLimit.height_ = std::max(0,
464 virtualDisplayRect.posY_ + virtualDisplayRect.height_ - topLeftLimit.posY_ - botAvoidHeight);
465 break;
466 default:
467 TLOGW(WmsLogTag::WMS_LAYOUT_PC, "invalid side: %{public}d", static_cast<int32_t>(side));
468 return;
469 }
470 rect.posX_ = std::max(rect.posX_, topLeftLimit.posX_);
471 rect.posY_ = std::max(rect.posY_, topLeftLimit.posY_);
472 rect.posX_ = std::min(rect.posX_, botRightLimit.posX_);
473 rect.posY_ = std::min(rect.posY_, botRightLimit.posY_);
474 rect.width_ = std::min(rect.width_, botRightLimit.width_);
475 rect.height_ = std::min(rect.height_, botRightLimit.height_);
476 TLOGD(WmsLogTag::WMS_LAYOUT_PC, "limit rects: [%{public}s,%{public}s], mapped rect: %{public}s",
477 topLeftLimit.ToString().c_str(), botRightLimit.ToString().c_str(), rect.ToString().c_str());
478 }
479
MappingRectInScreenSideWithArrangeRule(ScreenSide side,WSRect & rect,int32_t topAvoidHeight,int32_t botAvoidHeight,int32_t titleHeight)480 void PcFoldScreenManager::MappingRectInScreenSideWithArrangeRule(ScreenSide side, WSRect& rect,
481 int32_t topAvoidHeight, int32_t botAvoidHeight, int32_t titleHeight)
482 {
483 TLOGD(WmsLogTag::WMS_LAYOUT_PC, "side: %{public}d, rect: %{public}s",
484 static_cast<int32_t>(side), rect.ToString().c_str());
485 if (side != ScreenSide::FOLD_B && side != ScreenSide::FOLD_C) {
486 TLOGW(WmsLogTag::WMS_LAYOUT_PC, "rule not avaliable, side %{public}d", static_cast<int32_t>(side));
487 return;
488 }
489
490 // calculate limit rect
491 const auto& [defaultDisplayRect, virtualDisplayRect, foldCreaseRect] = GetDisplayRects();
492 WSRect limitRect = RECT_ZERO;
493 if (side == ScreenSide::FOLD_B) {
494 limitRect.posX_ = defaultDisplayRect.posX_;
495 limitRect.posY_ = defaultDisplayRect.posY_ + topAvoidHeight;
496 limitRect.width_ = defaultDisplayRect.width_;
497 limitRect.height_ = foldCreaseRect.posY_ - limitRect.posY_;
498 } else { // FOLD_C
499 limitRect.posX_ = virtualDisplayRect.posX_;
500 limitRect.posY_ = foldCreaseRect.posY_ + foldCreaseRect.height_;
501 limitRect.width_ = virtualDisplayRect.width_;
502 limitRect.height_ = virtualDisplayRect.posY_ + virtualDisplayRect.height_ - botAvoidHeight - limitRect.posY_;
503 }
504
505 {
506 std::unique_lock<std::mutex> lock(arrangedRectsMutex_);
507 WSRect& lastArrangedRect = (side == ScreenSide::FOLD_B) ? defaultArrangedRect_ : virtualArrangedRect_;
508 if (lastArrangedRect.IsEmpty()) {
509 ApplyInitArrangeRule(rect, lastArrangedRect, limitRect, titleHeight);
510 TLOGD(WmsLogTag::WMS_LAYOUT_PC, "init rule, limit: %{public}s, arranged: %{public}s, rect: %{public}s",
511 limitRect.ToString().c_str(), lastArrangedRect.ToString().c_str(), rect.ToString().c_str());
512 return;
513 }
514
515 ApplyArrangeRule(rect, lastArrangedRect, limitRect, titleHeight);
516 TLOGD(WmsLogTag::WMS_LAYOUT_PC, "apply rule, limit: %{public}s, arranged: %{public}s, rect: %{public}s",
517 limitRect.ToString().c_str(), lastArrangedRect.ToString().c_str(), rect.ToString().c_str());
518 }
519 }
520
521 /*
522 * init rule: move rect to center of display
523 * @param titleHeight: in vp
524 */
ApplyInitArrangeRule(WSRect & rect,WSRect & lastArrangedRect,const WSRect & limitRect,int32_t titleHeight)525 void PcFoldScreenManager::ApplyInitArrangeRule(WSRect& rect, WSRect& lastArrangedRect,
526 const WSRect& limitRect, int32_t titleHeight)
527 {
528 rect.posX_ = std::max(limitRect.posX_, limitRect.posX_ + (limitRect.width_ - rect.width_) / 2); // 2:center align
529 rect.posY_ = std::max(limitRect.posY_, limitRect.posY_ + (limitRect.height_ - rect.height_) / 2); // 2:center align
530 float vpr = GetVpr();
531 lastArrangedRect = { rect.posX_, rect.posY_, RULE_TRANS_X * vpr, titleHeight * vpr };
532 }
533
534 /*
535 * init rule: move rect to bottom-right of last arranged position
536 * @param titleHeight: in vp
537 */
ApplyArrangeRule(WSRect & rect,WSRect & lastArrangedRect,const WSRect & limitRect,int32_t titleHeight)538 void PcFoldScreenManager::ApplyArrangeRule(WSRect& rect, WSRect& lastArrangedRect,
539 const WSRect& limitRect, int32_t titleHeight)
540 {
541 rect.posX_ = lastArrangedRect.posX_ + lastArrangedRect.width_;
542 rect.posY_ = lastArrangedRect.posY_ + lastArrangedRect.height_;
543 // new column
544 if (rect.posY_ + rect.height_ > limitRect.posY_ + limitRect.height_) {
545 rect.posY_ = limitRect.posY_;
546 }
547 // reset to top-left
548 if (rect.posX_ + rect.width_ > limitRect.posX_ + limitRect.width_) {
549 rect.posX_ = limitRect.posX_;
550 rect.posY_ = limitRect.posY_;
551 }
552 float vpr = GetVpr();
553 lastArrangedRect = { rect.posX_, rect.posY_, RULE_TRANS_X * vpr, titleHeight * vpr};
554 }
555
RegisterFoldScreenStatusChangeCallback(int32_t persistentId,const std::weak_ptr<FoldScreenStatusChangeCallback> & func)556 void PcFoldScreenManager::RegisterFoldScreenStatusChangeCallback(int32_t persistentId,
557 const std::weak_ptr<FoldScreenStatusChangeCallback>& func)
558 {
559 TLOGI(WmsLogTag::WMS_LAYOUT_PC, "id: %{public}d", persistentId);
560 std::unique_lock<std::mutex> lock(callbackMutex_);
561 auto [_, result] = foldScreenStatusChangeCallbacks_.insert_or_assign(persistentId, func);
562 if (!result) {
563 TLOGW(WmsLogTag::WMS_LAYOUT_PC, "callback has registered");
564 }
565 }
566
UnregisterFoldScreenStatusChangeCallback(int32_t persistentId)567 void PcFoldScreenManager::UnregisterFoldScreenStatusChangeCallback(int32_t persistentId)
568 {
569 TLOGI(WmsLogTag::WMS_LAYOUT_PC, "id: %{public}d", persistentId);
570 std::unique_lock<std::mutex> lock(callbackMutex_);
571 auto iter = foldScreenStatusChangeCallbacks_.find(persistentId);
572 if (iter == foldScreenStatusChangeCallbacks_.end()) {
573 TLOGW(WmsLogTag::WMS_LAYOUT_PC, "callback not registered");
574 return;
575 }
576 foldScreenStatusChangeCallbacks_.erase(iter);
577 }
578
ExecuteFoldScreenStatusChangeCallbacks(DisplayId displayId,SuperFoldStatus status,SuperFoldStatus prevStatus)579 void PcFoldScreenManager::ExecuteFoldScreenStatusChangeCallbacks(DisplayId displayId,
580 SuperFoldStatus status, SuperFoldStatus prevStatus)
581 {
582 std::unordered_map<int32_t, std::weak_ptr<FoldScreenStatusChangeCallback>> foldScreenStatusChangeCallbacksCopy;
583 {
584 std::unique_lock<std::mutex> lock(callbackMutex_);
585 for (auto iter = foldScreenStatusChangeCallbacks_.begin(); iter != foldScreenStatusChangeCallbacks_.end();) {
586 auto callback = iter->second.lock();
587 if (callback == nullptr) {
588 TLOGW(WmsLogTag::WMS_LAYOUT_PC, "callback invalid, id: %{public}d", iter->first);
589 iter = foldScreenStatusChangeCallbacks_.erase(iter);
590 continue;
591 }
592 iter++;
593 }
594 foldScreenStatusChangeCallbacksCopy = foldScreenStatusChangeCallbacks_;
595 }
596 for (auto iter = foldScreenStatusChangeCallbacksCopy.begin(); iter != foldScreenStatusChangeCallbacksCopy.end();) {
597 auto callback = iter->second.lock();
598 if (callback != nullptr) {
599 (*callback)(displayId, status, prevStatus);
600 }
601 iter++;
602 }
603 }
604
RegisterSystemKeyboardStatusChangeCallback(int32_t persistentId,const std::weak_ptr<SystemKeyboardStatusChangeCallback> & func)605 void PcFoldScreenManager::RegisterSystemKeyboardStatusChangeCallback(int32_t persistentId,
606 const std::weak_ptr<SystemKeyboardStatusChangeCallback>& func)
607 {
608 TLOGI(WmsLogTag::WMS_LAYOUT_PC, "id: %{public}d", persistentId);
609 std::unique_lock<std::mutex> lock(callbackMutex_);
610 auto [_, result] = systemKeyboardStatusChangeCallbacks_.insert_or_assign(persistentId, func);
611 if (!result) {
612 TLOGW(WmsLogTag::WMS_LAYOUT_PC, "callback has registered");
613 }
614 }
615
UnregisterSystemKeyboardStatusChangeCallback(int32_t persistentId)616 void PcFoldScreenManager::UnregisterSystemKeyboardStatusChangeCallback(int32_t persistentId)
617 {
618 TLOGI(WmsLogTag::WMS_LAYOUT_PC, "id: %{public}d", persistentId);
619 std::unique_lock<std::mutex> lock(callbackMutex_);
620 auto iter = systemKeyboardStatusChangeCallbacks_.find(persistentId);
621 if (iter == systemKeyboardStatusChangeCallbacks_.end()) {
622 TLOGW(WmsLogTag::WMS_LAYOUT_PC, "callback not registered");
623 return;
624 }
625 systemKeyboardStatusChangeCallbacks_.erase(iter);
626 }
627
ExecuteSystemKeyboardStatusChangeCallbacks(DisplayId displayId,bool hasSystemKeyboard)628 void PcFoldScreenManager::ExecuteSystemKeyboardStatusChangeCallbacks(DisplayId displayId, bool hasSystemKeyboard)
629 {
630 std::unordered_map<int32_t, std::weak_ptr<SystemKeyboardStatusChangeCallback>>
631 systemKeyboardStatusChangeCallbacksCopy;
632 {
633 std::unique_lock<std::mutex> lock(callbackMutex_);
634 for (auto iter = systemKeyboardStatusChangeCallbacks_.begin();
635 iter != systemKeyboardStatusChangeCallbacks_.end();) {
636 auto callback = iter->second.lock();
637 if (callback == nullptr) {
638 TLOGW(WmsLogTag::WMS_LAYOUT_PC, "callback invalid, id: %{public}d", iter->first);
639 iter = systemKeyboardStatusChangeCallbacks_.erase(iter);
640 continue;
641 }
642 iter++;
643 }
644 systemKeyboardStatusChangeCallbacksCopy = systemKeyboardStatusChangeCallbacks_;
645 }
646 for (auto iter = systemKeyboardStatusChangeCallbacksCopy.begin();
647 iter != systemKeyboardStatusChangeCallbacksCopy.end();) {
648 auto callback = iter->second.lock();
649 if (callback != nullptr) {
650 (*callback)(displayId, hasSystemKeyboard);
651 }
652 iter++;
653 }
654 }
655 // LCOV_EXCL_STOP
656 } // namespace OHOS::Rosen
657