1/*
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "window_pair.h"
17
18#include <ability_manager_client.h>
19#include "common_event_manager.h"
20#include "minimize_app.h"
21#include "window_inner_manager.h"
22#include "window_manager_hilog.h"
23#include "window_helper.h"
24#include "window_system_effect.h"
25#include "surface_draw.h"
26
27namespace OHOS {
28namespace Rosen {
29namespace {
30constexpr HiviewDFX::HiLogLabel LABEL = {LOG_CORE, HILOG_DOMAIN_WINDOW, "Pair"};
31const std::string SPLIT_SCREEN_EVENT_NAME = "common.event.SPLIT_SCREEN";
32const std::map<SplitEventMsgType, std::string> splitEventDataMap {
33    {SplitEventMsgType::MSG_SHOW_PRIMARY,                                           "Primary"},
34    {SplitEventMsgType::MSG_SHOW_SECONDARY,                                       "Secondary"},
35    {SplitEventMsgType::MSG_SHOW_DIVIDER,       "common.event.SPLIT_SCREEN.data.show.divider"},
36    {SplitEventMsgType::MSG_DESTROY_DIVIDER, "common.event.SPLIT_SCREEN.data.destroy.divider"}
37};
38}
39
40WindowPair::~WindowPair()
41{
42    WLOGD("~WindowPair");
43    Clear();
44}
45
46void WindowPair::SendSplitScreenCommonEvent(SplitEventMsgType msgType, int32_t missionId)
47{
48    std::string data = splitEventDataMap.at(msgType);
49    std::string identity = IPCSkeleton::ResetCallingIdentity();
50    AAFwk::Want want;
51    want.SetAction(SPLIT_SCREEN_EVENT_NAME);
52    want.SetParam("windowMode", data);
53    want.SetParam("missionId", missionId);
54    EventFwk::CommonEventData commonEventData;
55    commonEventData.SetWant(want);
56    EventFwk::CommonEventManager::PublishCommonEvent(commonEventData);
57    // set ipc identity to raw
58    IPCSkeleton::SetCallingIdentity(identity);
59    WLOGD("Send split screen event: %{public}s", data.c_str());
60}
61
62void WindowPair::NotifyShowRecent(sptr<WindowNode> node)
63{
64    if (node == nullptr) {
65        return;
66    }
67    auto msgType = (node->GetWindowMode() == WindowMode::WINDOW_MODE_SPLIT_SECONDARY) ?
68        SplitEventMsgType::MSG_SHOW_PRIMARY : SplitEventMsgType::MSG_SHOW_SECONDARY;
69    SendSplitScreenCommonEvent(msgType, node->abilityInfo_.missionId_);
70}
71
72void WindowPair::NotifyCreateOrDestroyDivider(sptr<WindowNode> node, bool isDestroy)
73{
74    if (node == nullptr) {
75        return;
76    }
77    auto msgType = isDestroy ? SplitEventMsgType::MSG_DESTROY_DIVIDER : SplitEventMsgType::MSG_SHOW_DIVIDER;
78    SendSplitScreenCommonEvent(msgType, node->abilityInfo_.missionId_);
79}
80
81sptr<WindowNode> WindowPair::Find(sptr<WindowNode>& node)
82{
83    if (node == nullptr) {
84        return nullptr;
85    }
86    if (primary_ != nullptr && primary_->GetWindowId() == node->GetWindowId()) {
87        return primary_;
88    } else if (secondary_ != nullptr && secondary_->GetWindowId() == node->GetWindowId()) {
89        return secondary_;
90    } else if (divider_ != nullptr && divider_->GetWindowId() == node->GetWindowId()) {
91        return divider_;
92    }
93    return nullptr;
94}
95
96bool WindowPair::IsPaired() const
97{
98    if (primary_ == nullptr || secondary_ == nullptr) {
99        return false;
100    }
101    if (primary_->GetWindowMode() == WindowMode::WINDOW_MODE_SPLIT_PRIMARY &&
102        secondary_->GetWindowMode() == WindowMode::WINDOW_MODE_SPLIT_SECONDARY &&
103        divider_ != nullptr) {
104        return true;
105    }
106    return false;
107}
108
109bool WindowPair::IsAbnormalStatus() const
110{
111    if (status_ == WindowPairStatus::SINGLE_SPLIT || status_ == WindowPairStatus::PRIMARY_AND_DIVIDER ||
112        status_ == WindowPairStatus::SECONDARY_AND_DIVIDER) {
113        return true;
114    } else {
115        return false;
116    }
117}
118
119void WindowPair::SetSplitRatio(float ratio)
120{
121    ratio_ = ratio;
122}
123
124float WindowPair::GetSplitRatio() const
125{
126    return ratio_;
127}
128
129WindowPairStatus WindowPair::GetPairStatus() const
130{
131    return status_;
132}
133
134sptr<WindowNode> WindowPair::GetDividerWindow() const
135{
136    return divider_;
137}
138
139bool WindowPair::IsForbidDockSliceMove() const
140{
141    if (status_ != WindowPairStatus::PAIRED_DONE) {
142        return false;
143    }
144    uint32_t flag = static_cast<uint32_t>(WindowFlag::WINDOW_FLAG_FORBID_SPLIT_MOVE);
145    if (primary_ != nullptr && !(primary_->GetWindowFlags() & flag) && secondary_ != nullptr &&
146        !(secondary_->GetWindowFlags() & flag)) {
147        return false;
148    }
149    return true;
150}
151
152bool WindowPair::IsDockSliceInExitSplitModeArea(const std::vector<int32_t>& exitSplitPoints)
153{
154    if (!IsPaired()) {
155        return false;
156    }
157    int32_t dividerOrigin;
158    Rect rect = divider_->GetWindowRect();
159    if (rect.width_ < rect.height_) {
160        dividerOrigin = rect.posX_;
161    } else {
162        dividerOrigin = rect.posY_; // vertical display
163    }
164    if (dividerOrigin < exitSplitPoints[0] || dividerOrigin > exitSplitPoints[1]) {
165        return true;
166    }
167    return false;
168}
169
170void WindowPair::ExitSplitMode()
171{
172    if (!IsPaired()) {
173        return;
174    }
175    Rect dividerRect = divider_->GetWindowRect();
176    sptr<WindowNode> hideNode, recoveryNode;
177    bool isVertical = (dividerRect.height_ < dividerRect.width_) ? true : false;
178    if ((isVertical && (primary_->GetWindowRect().height_ < secondary_->GetWindowRect().height_)) ||
179        (!isVertical && (primary_->GetWindowRect().width_ < secondary_->GetWindowRect().width_))) {
180        hideNode = primary_;
181        recoveryNode = secondary_;
182    } else {
183        hideNode = secondary_;
184        recoveryNode = primary_;
185    }
186    if (recoveryNode != nullptr) {
187        recoveryNode->SetSnapshot(nullptr);
188    }
189    MinimizeApp::AddNeedMinimizeApp(hideNode, MinimizeReason::SPLIT_QUIT);
190    MinimizeApp::ExecuteMinimizeTargetReasons(MinimizeReason::SPLIT_QUIT);
191    WLOGI("Exit Split Mode, Minimize Window %{public}u", hideNode->GetWindowId());
192}
193
194void WindowPair::Clear()
195{
196    WLOGI("Clear window pair.");
197    DumpPairInfo();
198    auto splitModeInfo = (WindowModeSupport::WINDOW_MODE_SUPPORT_SPLIT_PRIMARY |
199                          WindowModeSupport::WINDOW_MODE_SUPPORT_SPLIT_SECONDARY);
200    if (primary_ != nullptr && primary_->GetWindowProperty() != nullptr &&
201        primary_->GetWindowToken() != nullptr) {
202        if (primary_->GetModeSupportInfo() == splitModeInfo) {
203            MinimizeApp::AddNeedMinimizeApp(primary_, MinimizeReason::SPLIT_QUIT);
204            MinimizeApp::ExecuteMinimizeTargetReasons(MinimizeReason::SPLIT_QUIT);
205        } else {
206            if (WindowHelper::IsFullScreenWindow(primary_->GetWindowProperty()->GetLastWindowMode()) &&
207                WindowHelper::IsSplitWindowMode(primary_->GetWindowProperty()->GetWindowMode()) &&
208                primary_->GetWindowType() != WindowType::WINDOW_TYPE_LAUNCHER_RECENT) {
209                primary_->SetWindowSizeChangeReason(WindowSizeChangeReason::SPLIT_TO_FULL);
210            }
211            primary_->GetWindowProperty()->ResumeLastWindowMode();
212            // when change mode, need to reset shadow and radius
213            WindowSystemEffect::SetWindowEffect(primary_);
214            primary_->GetWindowToken()->UpdateWindowMode(primary_->GetWindowMode());
215        }
216    }
217    if (secondary_ != nullptr && secondary_->GetWindowProperty() != nullptr &&
218        secondary_->GetWindowToken() != nullptr) {
219        if (secondary_->GetModeSupportInfo() == splitModeInfo) {
220            MinimizeApp::AddNeedMinimizeApp(secondary_, MinimizeReason::SPLIT_QUIT);
221            MinimizeApp::ExecuteMinimizeTargetReasons(MinimizeReason::SPLIT_QUIT);
222        } else {
223            if (WindowHelper::IsFullScreenWindow(secondary_->GetWindowProperty()->GetLastWindowMode()) &&
224                WindowHelper::IsSplitWindowMode(secondary_->GetWindowProperty()->GetWindowMode()) &&
225                secondary_->GetWindowType() != WindowType::WINDOW_TYPE_LAUNCHER_RECENT) {
226                secondary_->SetWindowSizeChangeReason(WindowSizeChangeReason::SPLIT_TO_FULL);
227            }
228            secondary_->GetWindowProperty()->ResumeLastWindowMode();
229            // when change mode, need to reset shadow and radius
230            WindowSystemEffect::SetWindowEffect(secondary_);
231            secondary_->GetWindowToken()->UpdateWindowMode(secondary_->GetWindowMode());
232        }
233    }
234
235    primary_ = nullptr;
236    secondary_ = nullptr;
237    if (divider_ != nullptr) {
238        NotifyCreateOrDestroyDivider(divider_, true);
239        divider_ = nullptr;
240    }
241    status_ = WindowPairStatus::EMPTY;
242}
243
244bool WindowPair::IsSplitRelated(sptr<WindowNode>& node) const
245{
246    if (node == nullptr) {
247        return false;
248    }
249    return WindowHelper::IsSplitWindowMode((node->GetWindowMode())) ||
250        (node->GetWindowType() == WindowType::WINDOW_TYPE_DOCK_SLICE);
251}
252
253void WindowPair::CheckOrderedPairZorder(
254    sptr<WindowNode>& node, bool& hasPrimaryDialog, bool& hasSecondaryDialog, bool& isPrimaryAbove)
255{
256    if (primary_ != nullptr) {
257        for (auto& child : primary_->children_) {
258            if (child->GetWindowType() == WindowType::WINDOW_TYPE_DIALOG) {
259                // secondary divider primary
260                hasPrimaryDialog = true;
261                isPrimaryAbove = true;
262                break;
263            }
264        }
265    }
266    if (secondary_ != nullptr) {
267        for (auto& child : secondary_->children_) {
268            if (child->GetWindowType() == WindowType::WINDOW_TYPE_DIALOG) {
269                // primary divider secondary
270                hasSecondaryDialog = true;
271                isPrimaryAbove = false;
272                break;
273            }
274        }
275    }
276    if (node->GetWindowMode() == WindowMode::WINDOW_MODE_SPLIT_SECONDARY ||
277        node->GetWindowType() == WindowType::WINDOW_TYPE_DOCK_SLICE) {
278        // primary secondary divider
279        isPrimaryAbove = false;
280    } else if (node->GetWindowMode() == WindowMode::WINDOW_MODE_SPLIT_PRIMARY) {
281        // secondary primary divider
282        isPrimaryAbove = true;
283    }
284
285    return;
286}
287
288std::vector<sptr<WindowNode>> WindowPair::CreateOrderedPair(
289    sptr<WindowNode>& bottom, sptr<WindowNode>& mid, sptr<WindowNode>& top)
290{
291    std::vector<sptr<WindowNode>> orderedPair;
292
293    if (bottom != nullptr) {
294        orderedPair.push_back(bottom);
295    }
296    if (mid != nullptr) {
297        orderedPair.push_back(mid);
298    }
299    if (top != nullptr) {
300        orderedPair.push_back(top);
301    }
302
303    return orderedPair;
304}
305
306std::vector<sptr<WindowNode>> WindowPair::GetOrderedPair(sptr<WindowNode>& node)
307{
308    WLOGI("Get paired node in Z order");
309    std::vector<sptr<WindowNode>> orderedPair;
310    bool hasPrimaryDialog_ = false;
311    bool hasSecondaryDialog_ = false;
312    bool isPrimaryAbove_ = false;
313
314    if (node == nullptr || Find(node) == nullptr) {
315        return orderedPair;
316    }
317
318    CheckOrderedPairZorder(node, hasPrimaryDialog_, hasSecondaryDialog_, isPrimaryAbove_);
319
320    if (hasPrimaryDialog_ && hasSecondaryDialog_) {
321        return CreateOrderedPair(divider_, primary_, secondary_);
322    }
323    if (hasPrimaryDialog_ || hasSecondaryDialog_) {
324        if (isPrimaryAbove_) {
325            return CreateOrderedPair(secondary_, divider_, primary_);
326        } else {
327            return CreateOrderedPair(primary_, divider_, secondary_);
328        }
329    } else {
330        if (isPrimaryAbove_) {
331            return CreateOrderedPair(secondary_, primary_, divider_);
332        } else {
333            return CreateOrderedPair(primary_, secondary_, divider_);
334        }
335    }
336}
337
338std::vector<sptr<WindowNode>> WindowPair::GetPairedWindows()
339{
340    WLOGD("Get primary and secondary of window pair");
341    std::vector<sptr<WindowNode>> pairWindows;
342    if (status_ == WindowPairStatus::PAIRED_DONE && primary_ != nullptr && secondary_ != nullptr) {
343        pairWindows = {primary_, secondary_};
344    }
345    return pairWindows;
346}
347
348bool WindowPair::StatusSupprtedWhenRecentUpdate(sptr<WindowNode>& node)
349{
350    WindowMode recentMode_ = node->GetWindowMode();
351    if (recentMode_ == WindowMode::WINDOW_MODE_SPLIT_PRIMARY &&
352       (status_ == WindowPairStatus::SINGLE_SECONDARY || status_ == WindowPairStatus::SECONDARY_AND_DIVIDER)) {
353        return true;
354    } else if (recentMode_ == WindowMode::WINDOW_MODE_SPLIT_SECONDARY &&
355        (status_ == WindowPairStatus::SINGLE_PRIMARY || status_ == WindowPairStatus::PRIMARY_AND_DIVIDER)) {
356        return true;
357    }
358    return false;
359}
360
361void WindowPair::UpdateIfSplitRelated(sptr<WindowNode>& node)
362{
363    if (node == nullptr) {
364        return;
365    }
366    if (Find(node) == nullptr && !IsSplitRelated(node)) {
367        WLOGFD("Window id: %{public}u is not split related and paired.", node->GetWindowId());
368        return;
369    }
370    if ((node->GetWindowType() == WindowType::WINDOW_TYPE_PLACEHOLDER) &&
371        ((primary_ != nullptr && primary_->GetWindowMode() == node->GetWindowMode()) ||
372        (secondary_ != nullptr && secondary_->GetWindowMode() == node->GetWindowMode()))) {
373        WindowInnerManager::GetInstance().DestroyInnerWindow(displayId_, WindowType::WINDOW_TYPE_PLACEHOLDER);
374        return;
375    }
376    WLOGI("Current status: %{public}u, window id: %{public}u mode: %{public}u",
377        status_, node->GetWindowId(), node->GetWindowMode());
378    // when status not support to start recent, clear split node and return
379    if (node->GetWindowType() == WindowType::WINDOW_TYPE_LAUNCHER_RECENT && node->IsSplitMode() &&
380        !StatusSupprtedWhenRecentUpdate(node)) {
381        Clear();
382        return;
383    }
384    if (status_ == WindowPairStatus::EMPTY) {
385        Insert(node);
386        if (!isAllSplitAppWindowsRestoring_) {
387            WindowMode holderMode = node->GetWindowMode() == WindowMode::WINDOW_MODE_SPLIT_PRIMARY ?
388                WindowMode::WINDOW_MODE_SPLIT_SECONDARY : WindowMode::WINDOW_MODE_SPLIT_PRIMARY;
389            WindowInnerManager::GetInstance().CreateInnerWindow("place_holder", displayId_, DEFAULT_PLACE_HOLDER_RECT,
390                WindowType::WINDOW_TYPE_PLACEHOLDER, holderMode);
391            // notity systemui to create divider window
392            NotifyShowRecent(node);
393        }
394    } else {
395        if (Find(node) == nullptr) {
396            // add new split related node to pair
397            Insert(node);
398        } else {
399            // handle paired nodes change
400            HandlePairedNodesChange();
401        }
402    }
403}
404
405void WindowPair::UpdateWindowPairStatus()
406{
407    WLOGI("Update window pair status.");
408    WindowPairStatus prevStatus = status_;
409    if (primary_ != nullptr && secondary_ != nullptr && divider_ != nullptr) {
410        status_ = WindowPairStatus::PAIRED_DONE;
411    } else if (primary_ != nullptr && secondary_ != nullptr && divider_ == nullptr) {
412        status_ = WindowPairStatus::PRIMARY_AND_SECONDARY;
413    } else if (primary_ != nullptr && secondary_ == nullptr && divider_ == nullptr) {
414        status_ = WindowPairStatus::SINGLE_PRIMARY;
415    } else if (primary_ != nullptr && secondary_ == nullptr && divider_ != nullptr) {
416        status_ = WindowPairStatus::PRIMARY_AND_DIVIDER;
417    } else if (primary_ == nullptr && secondary_ != nullptr && divider_ == nullptr) {
418        status_ = WindowPairStatus::SINGLE_SECONDARY;
419    } else if (primary_ == nullptr && secondary_ != nullptr && divider_ != nullptr) {
420        status_ = WindowPairStatus::SECONDARY_AND_DIVIDER;
421    } else if (primary_ == nullptr && secondary_ == nullptr && divider_ != nullptr) {
422        status_ = WindowPairStatus::SINGLE_SPLIT;
423    } else {
424        status_ = WindowPairStatus::EMPTY;
425    }
426    if ((prevStatus == WindowPairStatus::SINGLE_PRIMARY ||
427        prevStatus == WindowPairStatus::SINGLE_SECONDARY || prevStatus == WindowPairStatus::EMPTY) &&
428        status_ == WindowPairStatus::PRIMARY_AND_SECONDARY) {
429        // notify systemui to create divider
430        NotifyCreateOrDestroyDivider(primary_, false);
431    } else if ((prevStatus == WindowPairStatus::PAIRED_DONE || prevStatus == WindowPairStatus::PRIMARY_AND_SECONDARY) &&
432        (status_ != WindowPairStatus::PAIRED_DONE && status_ != WindowPairStatus::PRIMARY_AND_SECONDARY)) {
433        Clear();
434    }
435    DumpPairInfo();
436}
437
438void WindowPair::SwitchPosition()
439{
440    if (primary_ == nullptr || secondary_ == nullptr) {
441        return;
442    }
443    WLOGI("Switch the pair pos, pri: %{public}u pri-mode: %{public}u, sec: %{public}u sec-mode: %{public}u,",
444        primary_->GetWindowId(), primary_->GetWindowMode(), secondary_->GetWindowId(), secondary_->GetWindowMode());
445    if (primary_->GetWindowMode() == secondary_->GetWindowMode() &&
446        primary_->GetWindowMode() == WindowMode::WINDOW_MODE_SPLIT_PRIMARY) {
447        primary_->SetWindowMode(WindowMode::WINDOW_MODE_SPLIT_SECONDARY);
448        // when change mode, need to reset shadow and radius
449        WindowSystemEffect::SetWindowEffect(primary_);
450        if (primary_->GetWindowToken() != nullptr) {
451            primary_->GetWindowToken()->UpdateWindowMode(WindowMode::WINDOW_MODE_SPLIT_SECONDARY);
452        }
453        std::swap(primary_, secondary_);
454    } else if (primary_->GetWindowMode() == secondary_->GetWindowMode() &&
455        primary_->GetWindowMode() == WindowMode::WINDOW_MODE_SPLIT_SECONDARY) {
456        secondary_->SetWindowMode(WindowMode::WINDOW_MODE_SPLIT_PRIMARY);
457        // when change mode, need to reset shadow and radius
458        WindowSystemEffect::SetWindowEffect(secondary_);
459        if (secondary_->GetWindowToken() != nullptr) {
460            secondary_->GetWindowToken()->UpdateWindowMode(WindowMode::WINDOW_MODE_SPLIT_PRIMARY);
461        }
462        std::swap(primary_, secondary_);
463    }
464}
465
466void WindowPair::HandlePairedNodesChange()
467{
468    WLOGI("Update pair node.");
469    if (primary_ != nullptr && !primary_->IsSplitMode()) {
470        primary_ = nullptr;
471    }
472    if (secondary_ != nullptr && !secondary_->IsSplitMode()) {
473        secondary_ = nullptr;
474    }
475    // paired node mode change
476    if (primary_ != nullptr && secondary_ == nullptr &&
477        primary_->GetWindowMode() == WindowMode::WINDOW_MODE_SPLIT_SECONDARY) {
478        std::swap(primary_, secondary_);
479    } else if (primary_ == nullptr && secondary_ != nullptr &&
480        secondary_->GetWindowMode() == WindowMode::WINDOW_MODE_SPLIT_PRIMARY) {
481        std::swap(primary_, secondary_);
482    } else if (primary_ != nullptr && secondary_ != nullptr &&
483        primary_->GetWindowMode() == secondary_->GetWindowMode()) {
484        // switch position
485        SwitchPosition();
486    }
487    UpdateWindowPairStatus();
488    if (IsAbnormalStatus()) {
489        Clear();
490    }
491}
492
493void WindowPair::Insert(sptr<WindowNode>& node)
494{
495    if (node == nullptr) {
496        return;
497    }
498    WLOGI("Insert a window to pair id: %{public}u", node->GetWindowId());
499    sptr<WindowNode> pairedNode;
500    if (node->GetWindowMode() == WindowMode::WINDOW_MODE_SPLIT_PRIMARY) {
501        pairedNode = primary_;
502        primary_ = node;
503    } else if (node->GetWindowMode() == WindowMode::WINDOW_MODE_SPLIT_SECONDARY) {
504        pairedNode = secondary_;
505        secondary_ = node;
506    } else if (node->GetWindowType() == WindowType::WINDOW_TYPE_DOCK_SLICE) {
507        pairedNode = divider_;
508        divider_ = node;
509    }
510    // minimize invalid paired window
511    if (pairedNode != nullptr && pairedNode->abilityToken_ != nullptr) {
512        MinimizeApp::AddNeedMinimizeApp(pairedNode, MinimizeReason::SPLIT_REPLACE);
513    }
514    UpdateWindowPairStatus();
515    if (IsAbnormalStatus()) {
516        Clear();
517    }
518}
519
520void WindowPair::DumpPairInfo()
521{
522    if (primary_ != nullptr) {
523        WLOGI("[DumpPairInfo] primary id: %{public}u mode: %{public}u", primary_->GetWindowId(),
524            primary_->GetWindowMode());
525    }
526    if (secondary_ != nullptr) {
527        WLOGI("[DumpPairInfo] secondary id: %{public}u mode: %{public}u", secondary_->GetWindowId(),
528            secondary_->GetWindowMode());
529    }
530    if (divider_ != nullptr) {
531        WLOGI("[DumpPairInfo] divider id: %{public}u mode: %{public}u", divider_->GetWindowId(),
532            divider_->GetWindowMode());
533    }
534    WLOGI("[DumpPairInfo] pair status %{public}u", status_);
535}
536
537void WindowPair::HandleRemoveWindow(sptr<WindowNode>& node)
538{
539    if (node == nullptr) {
540        return;
541    }
542    if (Find(node) == nullptr && node->IsSplitMode()) {
543        WLOGI("Resume unpaired split related window id: %{public}u", node->GetWindowId());
544        if (node->GetWindowProperty() != nullptr && node->GetWindowToken() != nullptr) {
545            node->GetWindowProperty()->ResumeLastWindowMode();
546            // when change mode, need to reset shadow and radius
547            WindowSystemEffect::SetWindowEffect(node);
548            node->GetWindowToken()->UpdateWindowMode(node->GetWindowMode());
549        }
550        // target node is not in window pair, need resume mode when remove
551        return;
552    } else if (Find(node) != nullptr) {
553        WLOGI("Pairing window id: %{public}u is remove, clear window pair", node->GetWindowId());
554        Clear();
555    }
556}
557
558void WindowPair::RotateDividerWindow(const Rect& rect)
559{
560    dividerRect_ = rect;
561    // rotate divider when display orientation changed
562    if (divider_ == nullptr) {
563        WLOGE("Rotate divider failed because divider is null");
564        return;
565    }
566    WLOGFD("Rotate divider when display rotate rect:[%{public}d, %{public}d, %{public}u, %{public}u]",
567        rect.posX_, rect.posY_, rect.width_, rect.height_);
568}
569
570void WindowPair::SetDividerRect(const Rect& rect)
571{
572    dividerRect_ = rect;
573}
574
575bool WindowPair::TakePairSnapshot()
576{
577    if (status_ == WindowPairStatus::PAIRED_DONE && primary_ != nullptr && secondary_ != nullptr) {
578        WLOGD("Take pair snapshot id:[%{public}u, %{public}u]", primary_->GetWindowId(), secondary_->GetWindowId());
579        std::shared_ptr<Media::PixelMap> pixelMap;
580        // get pixelmap time out 2000ms
581        if (SurfaceDraw::GetSurfaceSnapshot(primary_->surfaceNode_, pixelMap, SNAPSHOT_TIMEOUT_MS)) {
582            primary_->SetSnapshot(pixelMap);
583        }
584        // get pixelmap time out 2000ms
585        if (SurfaceDraw::GetSurfaceSnapshot(secondary_->surfaceNode_, pixelMap, SNAPSHOT_TIMEOUT_MS)) {
586            secondary_->SetSnapshot(pixelMap);
587        }
588        return true;
589    }
590    return false;
591}
592
593void WindowPair::ClearPairSnapshot()
594{
595    WLOGD("Clear window pair snapshot");
596    if (primary_ != nullptr) {
597        primary_->SetSnapshot(nullptr);
598    }
599    if (secondary_ != nullptr) {
600        secondary_->SetSnapshot(nullptr);
601    }
602}
603
604int32_t WindowPair::GetSplitRatioPoint(float ratio, const Rect& displayRect)
605{
606    if (displayRect.width_ > displayRect.height_) {
607        return displayRect.posX_ +
608            static_cast<uint32_t>((displayRect.width_ - dividerRect_.width_) * ratio);
609    } else {
610        return displayRect.posY_ +
611            static_cast<uint32_t>((displayRect.height_ - dividerRect_.height_) * ratio);
612    }
613}
614
615void WindowPair::CalculateSplitRatioPoints(const Rect& displayRect)
616{
617    exitSplitPoints_.clear();
618    splitRatioPoints_.clear();
619    exitSplitPoints_.push_back(GetSplitRatioPoint(splitRatioConfig_.exitSplitStartRatio, displayRect));
620    exitSplitPoints_.push_back(GetSplitRatioPoint(splitRatioConfig_.exitSplitEndRatio, displayRect));
621    for (const auto& ratio : splitRatioConfig_.splitRatios) {
622        splitRatioPoints_.push_back(GetSplitRatioPoint(ratio, displayRect));
623    }
624}
625
626void WindowPair::SetSplitRatioConfig(const SplitRatioConfig& splitRatioConfig)
627{
628    splitRatioConfig_ = splitRatioConfig;
629}
630
631std::vector<int32_t> WindowPair::GetExitSplitPoints()
632{
633    return exitSplitPoints_;
634}
635
636std::vector<int32_t> WindowPair::GetSplitRatioPoints()
637{
638    return splitRatioPoints_;
639}
640
641bool WindowPair::IsDuringSplit()
642{
643    if (status_ == WindowPairStatus::EMPTY) {
644        return false;
645    }
646
647    if (status_ != WindowPairStatus::PAIRED_DONE || primary_ == nullptr || secondary_ == nullptr) {
648        WLOGFD("missing pairWindows or split status is %{public}u not done", status_);
649        return true;
650    }
651
652    if (primary_->GetWindowType() == WindowType::WINDOW_TYPE_LAUNCHER_RECENT ||
653        secondary_->GetWindowType() == WindowType::WINDOW_TYPE_LAUNCHER_RECENT) {
654        WLOGFD("split is done, but there is recent");
655        return true;
656    }
657
658    return false;
659}
660} // namespace Rosen
661} // namespace OHOS