• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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