File indexing completed on 2024-05-05 14:06:12

0001 /*
0002     SPDX-FileCopyrightText: 2016 Smith AR <audoban@openmailbox.org>
0003     SPDX-FileCopyrightText: 2016 Michail Vourlakos <mvourlakos@gmail.com>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "visibilitymanager.h"
0009 
0010 // local
0011 #include "positioner.h"
0012 #include "view.h"
0013 #include "helpers/floatinggapwindow.h"
0014 #include "helpers/screenedgeghostwindow.h"
0015 #include "windowstracker/currentscreentracker.h"
0016 #include "../apptypes.h"
0017 #include "../lattecorona.h"
0018 #include "../screenpool.h"
0019 #include "../layouts/manager.h"
0020 #include "../wm/abstractwindowinterface.h"
0021 
0022 // Qt
0023 #include <QDebug>
0024 
0025 // KDE
0026 #include <KWindowSystem>
0027 #include <KWayland/Client/plasmashell.h>
0028 #include <KWayland/Client/surface.h>
0029 
0030 //! Hide Timer can create cases that when it is low it does not allow the
0031 //! view to be show. For example !compositing+kwin_edges+hide inteval<50ms
0032 //!   FIXED: As it appears because we dont hide any view anymore before its sliding in
0033 //!   animation has ended that probably allows to set the hide minimum interval to zero
0034 //!   without any further issues, such as to not show the view even though the
0035 //!   user is touching the screen edge
0036 const int HIDEMINIMUMINTERVAL = 0;
0037 //! After calling SidebarAutoHide panel to show for example through Sidebar button
0038 //! or global shortcuts we make sure bar will be shown enough time
0039 //! in order for the user to observe its contents
0040 const int SIDEBARAUTOHIDEMINIMUMSHOW = 1000;
0041 
0042 
0043 namespace Latte {
0044 namespace ViewPart {
0045 
0046 //! BEGIN: VisiblityManager implementation
0047 const QRect VisibilityManager::ISHIDDENMASK = QRect(-1, -1, 1, 1);
0048 
0049 VisibilityManager::VisibilityManager(PlasmaQuick::ContainmentView *view)
0050     : QObject(view)
0051 {
0052     qDebug() << "VisibilityManager creating...";
0053 
0054     m_latteView = qobject_cast<Latte::View *>(view);
0055     m_corona = qobject_cast<Latte::Corona *>(view->corona());
0056     m_wm = m_corona->wm();
0057 
0058     connect(this, &VisibilityManager::hidingIsBlockedChanged, this, &VisibilityManager::onHidingIsBlockedChanged);
0059 
0060     connect(this, &VisibilityManager::slideOutFinished, this, &VisibilityManager::updateHiddenState);
0061     connect(this, &VisibilityManager::slideInFinished, this, &VisibilityManager::updateHiddenState);
0062 
0063     connect(this, &VisibilityManager::enableKWinEdgesChanged, this, &VisibilityManager::updateKWinEdgesSupport);
0064     connect(this, &VisibilityManager::modeChanged, this, &VisibilityManager::updateKWinEdgesSupport);
0065     connect(this, &VisibilityManager::modeChanged, this, &VisibilityManager::updateSidebarState);
0066 
0067     connect(this, &VisibilityManager::isFloatingGapWindowEnabledChanged, this, &VisibilityManager::onIsFloatingGapWindowEnabledChanged);
0068 
0069     connect(this, &VisibilityManager::mustBeShown, this, [&]() {
0070         if (m_latteView && !m_latteView->isVisible()) {
0071             m_latteView->setVisible(true);
0072         }
0073     });
0074 
0075     if (m_latteView) {
0076         connect(m_latteView, &Latte::View::eventTriggered, this, &VisibilityManager::viewEventManager);
0077         connect(m_latteView, &Latte::View::behaveAsPlasmaPanelChanged , this, &VisibilityManager::updateKWinEdgesSupport);
0078         connect(m_latteView, &Latte::View::byPassWMChanged, this, &VisibilityManager::updateKWinEdgesSupport);
0079 
0080         connect(m_latteView, &Latte::View::inEditModeChanged, this, &VisibilityManager::initViewFlags);
0081 
0082         //! Frame Extents
0083         connect(m_latteView, &Latte::View::headThicknessGapChanged, this, &VisibilityManager::onHeadThicknessChanged);
0084         connect(m_latteView, &Latte::View::locationChanged, this, [&]() {
0085             if (!m_latteView->behaveAsPlasmaPanel()) {
0086                 //! Resend frame extents because their geometry has changed
0087                 const bool forceUpdate{true};
0088                 publishFrameExtents(forceUpdate);
0089             }
0090         });
0091 
0092         connect(m_latteView, &Latte::View::typeChanged, this, [&]() {
0093             if (m_latteView->inEditMode()) {
0094                 //! Resend frame extents because type has changed
0095                 const bool forceUpdate{true};
0096                 publishFrameExtents(forceUpdate);
0097             }
0098         });
0099 
0100         connect(m_latteView, &Latte::View::forcedShown, this, [&]() {
0101             //! Resend frame extents to compositor otherwise because compositor cleared
0102             //! them with no reason when the user is closing an activity
0103             const bool forceUpdate{true};
0104             publishFrameExtents(forceUpdate);
0105         });
0106 
0107         connect(this, &VisibilityManager::modeChanged, this, [&]() {
0108             emit m_latteView->availableScreenRectChangedFrom(m_latteView);
0109         });
0110 
0111         //! Send frame extents on startup, this is really necessary when recreating a view.
0112         //! Such a case is when toggling byPassWM and a view is recreated after disabling editing mode
0113         const bool forceUpdate{true};
0114         publishFrameExtents(forceUpdate);
0115     }
0116 
0117     m_timerShow.setSingleShot(true);
0118     m_timerHide.setSingleShot(true);
0119 
0120     connect(&m_timerShow, &QTimer::timeout, this, [&]() {
0121         if (m_isHidden ||  m_isBelowLayer) {
0122             //   qDebug() << "must be shown";
0123             emit mustBeShown();
0124         }
0125     });
0126     connect(&m_timerHide, &QTimer::timeout, this, [&]() {
0127         if (!hidingIsBlocked() && !m_isHidden && !m_isBelowLayer && !m_dragEnter) {
0128             if (m_isFloatingGapWindowEnabled) {
0129                 //! first check if mouse is inside the floating gap
0130                 checkMouseInFloatingArea();
0131             } else {
0132                 //! immediate call
0133                 emit mustBeHide();
0134             }
0135         }
0136     });
0137 
0138     m_timerPublishFrameExtents.setInterval(1500);
0139     m_timerPublishFrameExtents.setSingleShot(true);
0140     connect(&m_timerPublishFrameExtents, &QTimer::timeout, this, [&]() { publishFrameExtents(); });
0141 
0142     m_timerBlockStrutsUpdate.setInterval(1000);
0143     m_timerBlockStrutsUpdate.setSingleShot(true);
0144     connect(&m_timerBlockStrutsUpdate, &QTimer::timeout, this, [&]() { updateStrutsBasedOnLayoutsAndActivities(); });
0145 
0146     restoreConfig();
0147 
0148     //! connect save values after they have been restored
0149     connect(this, &VisibilityManager::enableKWinEdgesChanged, this, &VisibilityManager::saveConfig);
0150     connect(this, &VisibilityManager::modeChanged, this, &VisibilityManager::saveConfig);
0151     connect(this, &VisibilityManager::raiseOnDesktopChanged, this, &VisibilityManager::saveConfig);
0152     connect(this, &VisibilityManager::raiseOnActivityChanged, this, &VisibilityManager::saveConfig);
0153     connect(this, &VisibilityManager::timerShowChanged, this, &VisibilityManager::saveConfig);
0154     connect(this, &VisibilityManager::timerHideChanged, this, &VisibilityManager::saveConfig);
0155 }
0156 
0157 VisibilityManager::~VisibilityManager()
0158 {
0159     qDebug() << "VisibilityManager deleting...";
0160     m_wm->removeViewStruts(*m_latteView);
0161 
0162     if (m_edgeGhostWindow) {
0163         m_edgeGhostWindow->deleteLater();
0164     }
0165 
0166     if (m_floatingGapWindow) {
0167         m_floatingGapWindow->deleteLater();
0168     }
0169 }
0170 
0171 //! Struts
0172 int VisibilityManager::strutsThickness() const
0173 {
0174     return m_strutsThickness;
0175 }
0176 
0177 void VisibilityManager::setStrutsThickness(int thickness)
0178 {
0179     if (m_strutsThickness == thickness) {
0180         return;
0181     }
0182 
0183     m_strutsThickness = thickness;
0184     emit strutsThicknessChanged();
0185 }
0186 
0187 Types::Visibility VisibilityManager::mode() const
0188 {
0189     return m_mode;
0190 }
0191 
0192 void VisibilityManager::initViewFlags()
0193 {
0194     if ((m_mode == Types::WindowsCanCover || m_mode == Types::WindowsAlwaysCover) && (!m_latteView->inEditMode())) {
0195         setViewOnBackLayer();
0196     } else {
0197         setViewOnFrontLayer();
0198     }
0199 }
0200 
0201 void VisibilityManager::setViewOnBackLayer()
0202 {
0203     m_wm->setViewExtraFlags(m_latteView, false, Types::WindowsAlwaysCover);
0204     setIsBelowLayer(true);
0205 }
0206 
0207 void VisibilityManager::setViewOnFrontLayer()
0208 {
0209     m_wm->setViewExtraFlags(m_latteView, true);
0210     setIsBelowLayer(false);
0211     if (KWindowSystem::isPlatformX11()) {
0212         m_latteView->raise();
0213     }
0214 }
0215 
0216 void VisibilityManager::setMode(Latte::Types::Visibility mode)
0217 {
0218     if (m_mode == mode) {
0219         return;
0220     }
0221 
0222     qDebug() << "Updating visibility mode to  :::: " << mode;
0223 
0224     Q_ASSERT_X(mode != Types::None, staticMetaObject.className(), "set visibility to Types::None");
0225 
0226     // clear mode
0227     for (auto &c : m_connections) {
0228         disconnect(c);
0229     }
0230 
0231     int base{0};
0232 
0233     m_publishedStruts = QRect();
0234 
0235     if (m_mode == Types::AlwaysVisible) {
0236         //! remove struts for old always visible mode
0237         m_wm->removeViewStruts(*m_latteView);
0238     }
0239 
0240     m_timerShow.stop();
0241     m_timerHide.stop();
0242     m_mode = mode;
0243 
0244     initViewFlags();
0245 
0246     if (mode != Types::AlwaysVisible && mode != Types::WindowsGoBelow) {
0247         m_connections[0] = connect(m_wm, &WindowSystem::AbstractWindowInterface::currentDesktopChanged, this, [&] {
0248             if (m_raiseOnDesktopChange) {
0249                 raiseViewTemporarily();
0250             }
0251         });
0252         m_connections[1] = connect(m_wm, &WindowSystem::AbstractWindowInterface::currentActivityChanged, this, [&]() {
0253             if (m_raiseOnActivityChange) {
0254                 raiseViewTemporarily();
0255             } else {
0256                 updateHiddenState();
0257             }
0258         });
0259 
0260         base = 2;
0261     }
0262 
0263     switch (m_mode) {
0264     case Types::AlwaysVisible: {
0265         if (m_latteView->containment() && m_latteView->screen()) {
0266             updateStrutsBasedOnLayoutsAndActivities();
0267         }
0268 
0269         m_connections[base] = connect(this, &VisibilityManager::strutsThicknessChanged, &VisibilityManager::updateStrutsAfterTimer);
0270 
0271         // disabling this call because it was creating too many struts calls and   ???
0272         // could create reduced responsiveness for DynamicStruts Scenario(for example ??
0273         // when dragging active window from a floating dock/panel) ???
0274         m_connections[base+1] = connect(m_latteView, &Latte::View::absoluteGeometryChanged, this, &VisibilityManager::updateStrutsAfterTimer);
0275 
0276         m_connections[base+2] = connect(m_corona->activitiesConsumer(), &KActivities::Consumer::currentActivityChanged, this, [&]() {
0277             if (m_corona && m_corona->layoutsManager()->memoryUsage() == MemoryUsage::MultipleLayouts) {
0278                 updateStrutsBasedOnLayoutsAndActivities(true);
0279             }
0280         });
0281 
0282         //! respect canSetStrut that must be disabled under x11 when an alwaysvisible screen edge is common between two or more screens
0283         m_connections[base+3] = connect(m_corona->screenPool(), &Latte::ScreenPool::screenGeometryChanged, this, &VisibilityManager::updateStrutsAfterTimer);
0284 
0285         m_connections[base+4] = connect(m_latteView, &Latte::View::activitiesChanged, this, [&]() {
0286             updateStrutsBasedOnLayoutsAndActivities(true);
0287         });
0288 
0289         raiseView(true);
0290         break;
0291     }
0292 
0293     case Types::AutoHide: {
0294         m_connections[base] = connect(this, &VisibilityManager::containsMouseChanged, this, [&]() {
0295             raiseView(m_containsMouse);
0296         });
0297 
0298         raiseView(m_containsMouse);
0299         break;
0300     }
0301 
0302     case Types::DodgeActive: {
0303         m_connections[base] = connect(this, &VisibilityManager::containsMouseChanged
0304                                       , this, &VisibilityManager::dodgeActive);
0305         m_connections[base+1] = connect(m_latteView->windowsTracker()->currentScreen(), &TrackerPart::CurrentScreenTracker::activeWindowTouchingChanged
0306                                         , this, &VisibilityManager::dodgeActive);
0307 
0308         dodgeActive();
0309         break;
0310     }
0311 
0312     case Types::DodgeMaximized: {
0313         m_connections[base] = connect(this, &VisibilityManager::containsMouseChanged
0314                                       , this, &VisibilityManager::dodgeMaximized);
0315         m_connections[base+1] = connect(m_latteView->windowsTracker()->currentScreen(), &TrackerPart::CurrentScreenTracker::activeWindowMaximizedChanged
0316                                         , this, &VisibilityManager::dodgeMaximized);
0317 
0318         dodgeMaximized();
0319         break;
0320     }
0321 
0322     case Types::DodgeAllWindows: {
0323         m_connections[base] = connect(this, &VisibilityManager::containsMouseChanged
0324                                       , this, &VisibilityManager::dodgeAllWindows);
0325 
0326         m_connections[base+1] = connect(m_latteView->windowsTracker()->currentScreen(), &TrackerPart::CurrentScreenTracker::existsWindowTouchingChanged
0327                                         , this, &VisibilityManager::dodgeAllWindows);
0328 
0329         dodgeAllWindows();
0330         break;
0331     }
0332 
0333     case Types::WindowsGoBelow:
0334         break;
0335 
0336     case Types::WindowsCanCover:
0337         m_connections[base] = connect(this, &VisibilityManager::containsMouseChanged, this, [&]() {
0338             raiseView(m_containsMouse);
0339         });
0340 
0341         raiseView(m_containsMouse);
0342         break;
0343 
0344     case Types::WindowsAlwaysCover:
0345         break;
0346 
0347     case Types::SidebarOnDemand:
0348         m_connections[base] = connect(m_latteView, &Latte::View::inEditModeChanged, this, [&]() {
0349             if (!m_latteView->inEditMode()) {
0350                 m_isRequestedShownSidebarOnDemand = false;
0351                 updateHiddenState();
0352             }
0353         });
0354 
0355         m_isRequestedShownSidebarOnDemand = false;
0356         updateHiddenState();
0357         break;
0358 
0359     case Types::SidebarAutoHide:
0360         m_connections[base] = connect(this, &VisibilityManager::containsMouseChanged, this, [&]() {
0361             if (!m_latteView->inEditMode()) {
0362                 updateHiddenState();
0363             }
0364         });
0365         
0366         m_connections[base+1] = connect(m_latteView, &Latte::View::inEditModeChanged, this, [&]() {
0367             if (m_latteView->inEditMode() && !m_isHidden) {
0368                 updateHiddenState();
0369             }
0370         });
0371 
0372         toggleHiddenState();
0373         break;
0374 
0375     default:
0376         break;
0377     }
0378 
0379     emit modeChanged();
0380 }
0381 
0382 void VisibilityManager::updateStrutsAfterTimer()
0383 {
0384     bool execute = !m_timerBlockStrutsUpdate.isActive();
0385 
0386     m_timerBlockStrutsUpdate.start();
0387 
0388     if (execute) {
0389         updateStrutsBasedOnLayoutsAndActivities();
0390     }
0391 }
0392 
0393 void VisibilityManager::updateSidebarState()
0394 {
0395     bool cursidebarstate = ((m_mode == Types::SidebarOnDemand)
0396                             || (m_mode == Types::SidebarAutoHide));
0397 
0398     if (m_isSidebar == cursidebarstate) {
0399         return;
0400     }
0401 
0402     m_isSidebar == cursidebarstate;
0403     emit isSidebarChanged();
0404 
0405 }
0406 
0407 void VisibilityManager::updateStrutsBasedOnLayoutsAndActivities(bool forceUpdate)
0408 {
0409     bool inMultipleLayoutsAndCurrent = (m_corona->layoutsManager()->memoryUsage() == MemoryUsage::MultipleLayouts
0410                                         && m_latteView->layout() && !m_latteView->positioner()->inRelocationAnimation()
0411                                         && m_latteView->layout()->isCurrent());
0412 
0413     if (m_strutsThickness>0 && canSetStrut() && (m_corona->layoutsManager()->memoryUsage() == MemoryUsage::SingleLayout || inMultipleLayoutsAndCurrent)) {
0414         QRect computedStruts = acceptableStruts();
0415         if (m_publishedStruts != computedStruts || forceUpdate) {
0416             //! Force update is needed when very important events happen in DE and there is a chance
0417             //! that previously even though struts where sent the DE did not accept them.
0418             //! Such a case is when STOPPING an Activity and windows faulty become invisible even
0419             //! though they should not. In such case setting struts when the windows are hidden
0420             //! the struts do not take any effect
0421             m_publishedStruts = computedStruts;
0422             m_wm->setViewStruts(*m_latteView, m_publishedStruts, m_latteView->location());
0423         }
0424     } else {
0425         m_publishedStruts = QRect();
0426         m_wm->removeViewStruts(*m_latteView);
0427     }
0428 }
0429 
0430 bool VisibilityManager::canSetStrut() const
0431 {
0432     if (m_latteView->positioner()->isOffScreen()) {
0433         return false;
0434     }
0435 
0436     if (!KWindowSystem::isPlatformX11() || m_wm->isKWinRunning()) {
0437         // we always trust wayland and kwin to provide proper struts
0438         return true;
0439     }
0440 
0441     if (qGuiApp->screens().count() < 2) {
0442         return true;
0443     }
0444 
0445     /*Alternative DEs*/
0446 
0447     const QRect thisScreen = m_latteView->screen()->geometry();
0448 
0449     // Extended struts against a screen edge near to another screen are really harmful, so windows maximized under the panel is a lesser pain
0450     // TODO: force "windows can cover" in those cases?
0451     for (QScreen *screen : qGuiApp->screens()) {
0452         if (!screen || m_latteView->screen() == screen) {
0453             continue;
0454         }
0455 
0456         const QRect otherScreen = screen->geometry();
0457 
0458         switch (m_latteView->location()) {
0459         case Plasma::Types::TopEdge:
0460             if (otherScreen.bottom() <= thisScreen.top()) {
0461                 return false;
0462             }
0463             break;
0464         case Plasma::Types::BottomEdge:
0465             if (otherScreen.top() >= thisScreen.bottom()) {
0466                 return false;
0467             }
0468             break;
0469         case Plasma::Types::RightEdge:
0470             if (otherScreen.left() >= thisScreen.right()) {
0471                 return false;
0472             }
0473             break;
0474         case Plasma::Types::LeftEdge:
0475             if (otherScreen.right() <= thisScreen.left()) {
0476                 return false;
0477             }
0478             break;
0479         default:
0480             return false;
0481         }
0482     }
0483 
0484     return true;
0485 }
0486 
0487 QRect VisibilityManager::acceptableStruts()
0488 {
0489     QRect calcs;
0490 
0491     switch (m_latteView->location()) {
0492     case Plasma::Types::TopEdge: {
0493         calcs = QRect(m_latteView->x(), m_latteView->screenGeometry().top(), m_latteView->width(), m_strutsThickness);
0494         break;
0495     }
0496 
0497     case Plasma::Types::BottomEdge: {
0498         int y = m_latteView->screenGeometry().bottom() - m_strutsThickness + 1 /* +1, is needed in order to not leave a gap at screen_edge*/;
0499         calcs = QRect(m_latteView->x(), y, m_latteView->width(), m_strutsThickness);
0500         break;
0501     }
0502 
0503     case Plasma::Types::LeftEdge: {
0504         calcs = QRect(m_latteView->screenGeometry().left(), m_latteView->y(), m_strutsThickness, m_latteView->height());
0505         break;
0506     }
0507 
0508     case Plasma::Types::RightEdge: {
0509         int x = m_latteView->screenGeometry().right() - m_strutsThickness + 1 /* +1, is needed in order to not leave a gap at screen_edge*/;
0510         calcs = QRect(x, m_latteView->y(), m_strutsThickness, m_latteView->height());
0511         break;
0512     }
0513     }
0514 
0515     return calcs;
0516 }
0517 
0518 bool VisibilityManager::raiseOnDesktop() const
0519 {
0520     return m_raiseOnDesktopChange;
0521 }
0522 
0523 void VisibilityManager::setRaiseOnDesktop(bool enable)
0524 {
0525     if (enable == m_raiseOnDesktopChange)
0526         return;
0527 
0528     m_raiseOnDesktopChange = enable;
0529     emit raiseOnDesktopChanged();
0530 }
0531 
0532 bool VisibilityManager::raiseOnActivity() const
0533 {
0534     return m_raiseOnActivityChange;
0535 }
0536 
0537 void VisibilityManager::setRaiseOnActivity(bool enable)
0538 {
0539     if (enable == m_raiseOnActivityChange)
0540         return;
0541 
0542     m_raiseOnActivityChange = enable;
0543     emit raiseOnActivityChanged();
0544 }
0545 
0546 bool VisibilityManager::isBelowLayer() const
0547 {
0548     return m_isBelowLayer;
0549 }
0550 
0551 void VisibilityManager::setIsBelowLayer(bool below)
0552 {
0553     if (m_isBelowLayer == below) {
0554         return;
0555     }
0556 
0557     m_isBelowLayer = below;
0558 
0559     updateGhostWindowState();
0560 
0561     emit isBelowLayerChanged();
0562 }
0563 
0564 bool VisibilityManager::isHidden() const
0565 {
0566     return m_isHidden;
0567 }
0568 
0569 void VisibilityManager::setIsHidden(bool isHidden)
0570 {
0571     if (m_isHidden == isHidden)
0572         return;
0573 
0574     m_isHidden = isHidden;
0575     updateGhostWindowState();
0576 
0577     emit isHiddenChanged();
0578 }
0579 
0580 bool VisibilityManager::isShownFully() const
0581 {
0582     return m_isShownFully;
0583 }
0584 
0585 void VisibilityManager::setIsShownFully(bool fully)
0586 {
0587     if (m_isShownFully == fully) {
0588         return;
0589     }
0590 
0591     m_isShownFully = fully;
0592     emit isShownFullyChanged();
0593 }
0594 
0595 bool VisibilityManager::hidingIsBlocked() const
0596 {
0597     return (m_blockHidingEvents.count() > 0);
0598 }
0599 
0600 bool VisibilityManager::isFloatingGapWindowEnabled() const
0601 {
0602     return m_isFloatingGapWindowEnabled;
0603 }
0604 
0605 void VisibilityManager::setIsFloatingGapWindowEnabled(bool enabled)
0606 {
0607     if (m_isFloatingGapWindowEnabled == enabled) {
0608         return;
0609     }
0610 
0611     m_isFloatingGapWindowEnabled = enabled;
0612     emit isFloatingGapWindowEnabledChanged();
0613 }
0614 
0615 bool VisibilityManager::hasBlockHidingEvent(const QString &type)
0616 {
0617     return (!type.isEmpty() && m_blockHidingEvents.contains(type));
0618 }
0619 
0620 void VisibilityManager::addBlockHidingEvent(const QString &type)
0621 {
0622     if (m_blockHidingEvents.contains(type) || type.isEmpty()) {
0623         return;
0624     }
0625     //qDebug() << " org.kde.late {{ ++++ adding block hiding event :: " << type;
0626 
0627     bool prevHidingIsBlocked = hidingIsBlocked();
0628 
0629     m_blockHidingEvents << type;
0630 
0631     if (prevHidingIsBlocked != hidingIsBlocked()) {
0632         emit hidingIsBlockedChanged();
0633     }
0634 }
0635 
0636 void VisibilityManager::removeBlockHidingEvent(const QString &type)
0637 {
0638     if (!m_blockHidingEvents.contains(type) || type.isEmpty()) {
0639         return;
0640     }
0641     //qDebug() << " org.kde.latte {{ ---- remove block hiding event :: " << type;
0642 
0643     bool prevHidingIsBlocked = hidingIsBlocked();
0644 
0645     m_blockHidingEvents.removeAll(type);
0646 
0647     if (prevHidingIsBlocked != hidingIsBlocked()) {
0648         emit hidingIsBlockedChanged();
0649     }
0650 }
0651 
0652 void VisibilityManager::onHidingIsBlockedChanged()
0653 {
0654     if (hidingIsBlocked()) {
0655         m_timerHide.stop();
0656         emit mustBeShown();
0657     } else {
0658         updateHiddenState();
0659     }
0660 }
0661 
0662 void VisibilityManager::onHeadThicknessChanged()
0663 {
0664     if (!m_timerPublishFrameExtents.isActive()) {
0665         m_timerPublishFrameExtents.start();
0666     }
0667 }
0668 
0669 void VisibilityManager::publishFrameExtents(bool forceUpdate)
0670 {   
0671     if (m_frameExtentsHeadThicknessGap != m_latteView->headThicknessGap()
0672             || m_frameExtentsLocation != m_latteView->location()
0673             || forceUpdate) {
0674 
0675         m_frameExtentsLocation = m_latteView->location();
0676         m_frameExtentsHeadThicknessGap = m_latteView->headThicknessGap();
0677 
0678         if (KWindowSystem::isPlatformX11() && m_latteView->devicePixelRatio()!=1.0) {
0679             //!Fix for X11 Global Scale
0680             m_frameExtentsHeadThicknessGap = qRound(m_frameExtentsHeadThicknessGap * m_latteView->devicePixelRatio());
0681         }
0682 
0683         QMargins frameExtents(0, 0, 0, 0);
0684 
0685         if (m_latteView->location() == Plasma::Types::LeftEdge) {
0686             frameExtents.setRight(m_frameExtentsHeadThicknessGap);
0687         } else if (m_latteView->location() == Plasma::Types::TopEdge) {
0688             frameExtents.setBottom(m_frameExtentsHeadThicknessGap);
0689         } else if (m_latteView->location() == Plasma::Types::RightEdge) {
0690             frameExtents.setLeft(m_frameExtentsHeadThicknessGap);
0691         } else {
0692             frameExtents.setTop(m_frameExtentsHeadThicknessGap);
0693         }
0694 
0695         bool bypasswm{m_latteView->byPassWM() && KWindowSystem::isPlatformX11()};
0696 
0697         qDebug() << " -> Frame Extents :: " << m_frameExtentsLocation << " __ " << " extents :: " << frameExtents << " bypasswm :: " << bypasswm;
0698 
0699         if (!frameExtents.isNull() && !m_latteView->behaveAsPlasmaPanel() && !bypasswm) {
0700             //! When a view returns its frame extents to zero then that triggers a compositor
0701             //! strange behavior that moves/hides the view totally and freezes entire Latte
0702             //! this is why we have blocked that setting
0703             m_wm->setFrameExtents(m_latteView, frameExtents);
0704         } else if (m_latteView->behaveAsPlasmaPanel() || bypasswm) {
0705             QMargins panelExtents(0, 0, 0, 0);
0706             m_wm->setFrameExtents(m_latteView, panelExtents);
0707             emit frameExtentsCleared();
0708         }
0709     }
0710 }
0711 
0712 int VisibilityManager::timerShow() const
0713 {
0714     return m_timerShow.interval();
0715 }
0716 
0717 void VisibilityManager::setTimerShow(int msec)
0718 {
0719     if (m_timerShow.interval() == msec) {
0720         return;
0721     }
0722 
0723     m_timerShow.setInterval(msec);
0724     emit timerShowChanged();
0725 }
0726 
0727 int VisibilityManager::timerHide() const
0728 {
0729     return m_timerHideInterval;
0730 }
0731 
0732 void VisibilityManager::setTimerHide(int msec)
0733 {
0734     int interval = qMax(HIDEMINIMUMINTERVAL, msec);
0735 
0736     if (m_timerHideInterval == interval) {
0737         return;
0738     }
0739 
0740     m_timerHideInterval = interval;
0741     m_timerHide.setInterval(interval);
0742     emit timerHideChanged();
0743 }
0744 
0745 bool VisibilityManager::isSidebar() const
0746 {
0747     return m_mode == Latte::Types::SidebarOnDemand || m_mode == Latte::Types::SidebarAutoHide;
0748 }
0749 
0750 bool VisibilityManager::supportsKWinEdges() const
0751 {
0752     return (m_edgeGhostWindow != nullptr);
0753 }
0754 
0755 void VisibilityManager::updateGhostWindowState()
0756 {
0757     if (supportsKWinEdges()) {
0758         bool inCurrentLayout = (m_corona->layoutsManager()->memoryUsage() == MemoryUsage::SingleLayout ||
0759                                 (m_corona->layoutsManager()->memoryUsage() == MemoryUsage::MultipleLayouts
0760                                  && m_latteView->layout() && !m_latteView->positioner()->inRelocationAnimation()
0761                                  && m_latteView->layout()->isCurrent()));
0762 
0763         if (inCurrentLayout) {
0764             if (m_mode == Latte::Types::WindowsCanCover) {
0765                 m_wm->setActiveEdge(m_edgeGhostWindow, m_isBelowLayer && !m_containsMouse);
0766             } else {
0767                 bool activated = (m_isHidden && !windowContainsMouse());
0768 
0769                 m_wm->setActiveEdge(m_edgeGhostWindow, activated);
0770             }
0771         } else {
0772             m_wm->setActiveEdge(m_edgeGhostWindow, false);
0773         }
0774     }
0775 }
0776 
0777 void VisibilityManager::hide()
0778 {
0779     if (KWindowSystem::isPlatformX11()) {
0780         m_latteView->setVisible(false);
0781     }
0782 }
0783 
0784 void VisibilityManager::show()
0785 {
0786     if (KWindowSystem::isPlatformX11()) {
0787         m_latteView->setVisible(true);
0788     }
0789 }
0790 
0791 void VisibilityManager::toggleHiddenState()
0792 {
0793     if (!m_latteView->inEditMode()) {
0794         if (isSidebar()) {
0795             // if (m_blockHidingEvents.contains(Q_FUNC_INFO)) {
0796             //    removeBlockHidingEvent(Q_FUNC_INFO);
0797             // }
0798 
0799             if (m_mode == Latte::Types::SidebarOnDemand) {
0800                 m_isRequestedShownSidebarOnDemand = !m_isRequestedShownSidebarOnDemand;
0801                 updateHiddenState();
0802             } else if (m_mode == Latte::Types::SidebarAutoHide) {
0803                 if (m_isHidden) {
0804                     emit mustBeShown();
0805                     startTimerHide(SIDEBARAUTOHIDEMINIMUMSHOW + m_timerHideInterval);
0806                 } else {
0807                     emit mustBeHide();
0808                 }
0809             }
0810         } else {
0811             /*    if (!m_isHidden && !m_blockHidingEvents.contains(Q_FUNC_INFO)) {
0812                 addBlockHidingEvent(Q_FUNC_INFO);
0813             } else if (m_isHidden) {
0814                 removeBlockHidingEvent(Q_FUNC_INFO);
0815             }*/
0816         }
0817     }
0818 }
0819 
0820 void VisibilityManager::updateHiddenState()
0821 {
0822     if (m_dragEnter)
0823         return;
0824 
0825     switch (m_mode) {
0826     case Types::AutoHide:
0827     case Types::WindowsCanCover:
0828         raiseView(m_containsMouse);
0829         break;
0830 
0831     case Types::DodgeActive:
0832         dodgeActive();
0833         break;
0834 
0835     case Types::DodgeMaximized:
0836         dodgeMaximized();
0837         break;
0838 
0839     case Types::DodgeAllWindows:
0840         dodgeAllWindows();
0841         break;
0842 
0843     case Types::SidebarOnDemand:
0844         raiseView(m_isRequestedShownSidebarOnDemand);
0845         break;
0846 
0847     case Types::SidebarAutoHide:
0848         raiseView(m_latteView->inEditMode() || (m_containsMouse && !m_isHidden));
0849         break;
0850 
0851     default:
0852         break;
0853     }
0854 }
0855 
0856 void VisibilityManager::raiseView(bool raise)
0857 {
0858     if (m_mode == Latte::Types::SidebarOnDemand) {
0859         if (raise && m_isHidden) {
0860             emit mustBeShown();
0861         } else if (!raise && !m_isHidden && !m_dragEnter && !hidingIsBlocked()) {
0862             emit mustBeHide();
0863         }
0864         return;
0865     }
0866 
0867     if (raise) {
0868         m_timerHide.stop();
0869 
0870         if (!m_timerShow.isActive()) {
0871             m_timerShow.start();
0872         }
0873     } else if (!m_dragEnter && !hidingIsBlocked()) {
0874         m_timerShow.stop();
0875 
0876         if (m_hideNow) {
0877             m_hideNow = false;
0878             emit mustBeHide();
0879         } else if (!m_timerHide.isActive()) {
0880             startTimerHide();
0881         }
0882     }
0883 }
0884 
0885 void VisibilityManager::raiseViewTemporarily()
0886 {
0887     if (m_raiseTemporarily)
0888         return;
0889 
0890     m_raiseTemporarily = true;
0891     m_timerHide.stop();
0892     m_timerShow.stop();
0893 
0894     if (m_isHidden)
0895         emit mustBeShown();
0896 
0897     QTimer::singleShot(qBound(1800, 2 * m_timerHide.interval(), 3000), this, [&]() {
0898         m_raiseTemporarily = false;
0899         m_hideNow = true;
0900         updateHiddenState();
0901     });
0902 }
0903 
0904 bool VisibilityManager::isValidMode() const
0905 {
0906     return (m_mode != Types::None && m_mode != Types::NormalWindow);
0907 }
0908 
0909 void VisibilityManager::applyActivitiesToHiddenWindows(const QStringList &activities)
0910 {
0911     if (m_edgeGhostWindow) {
0912         m_wm->setWindowOnActivities(m_edgeGhostWindow->trackedWindowId(), activities);
0913     }
0914 
0915     if (m_floatingGapWindow) {
0916         m_wm->setWindowOnActivities(m_floatingGapWindow->trackedWindowId(), activities);
0917     }
0918 }
0919 
0920 void VisibilityManager::startTimerHide(const int &msec)
0921 {
0922     if (msec == 0) {
0923         int secs = m_timerHideInterval;
0924 
0925         if (!KWindowSystem::compositingActive()) {
0926             //! this is needed in order to give view time to show and
0927             //! for floating case to give time to user to reach the view with its mouse
0928             secs = qMax(m_timerHideInterval, m_latteView->screenEdgeMargin() > 0 ? 700 : 200);
0929         }
0930 
0931         m_timerHide.start(secs);
0932     } else {
0933         m_timerHide.start(msec);
0934     }
0935 }
0936 
0937 void VisibilityManager::dodgeActive()
0938 {
0939     if (m_raiseTemporarily)
0940         return;
0941 
0942     //!don't send false raiseView signal when containing mouse
0943     if (m_containsMouse) {
0944         raiseView(true);
0945         return;
0946     }
0947 
0948     raiseView(!m_latteView->windowsTracker()->currentScreen()->activeWindowTouching());
0949 }
0950 
0951 void VisibilityManager::dodgeMaximized()
0952 {
0953     if (m_raiseTemporarily)
0954         return;
0955 
0956     //!don't send false raiseView signal when containing mouse
0957     if (m_containsMouse) {
0958         raiseView(true);
0959         return;
0960     }
0961 
0962     raiseView(!m_latteView->windowsTracker()->currentScreen()->activeWindowMaximized());
0963 }
0964 
0965 void VisibilityManager::dodgeAllWindows()
0966 {
0967     if (m_raiseTemporarily)
0968         return;
0969 
0970     if (m_containsMouse) {
0971         raiseView(true);
0972         return;
0973     }
0974 
0975     bool windowIntersects{m_latteView->windowsTracker()->currentScreen()->activeWindowTouching() || m_latteView->windowsTracker()->currentScreen()->existsWindowTouching()};
0976 
0977     raiseView(!windowIntersects);
0978 }
0979 
0980 void VisibilityManager::saveConfig()
0981 {
0982     if (!m_latteView->containment()) {
0983         return;
0984     }
0985 
0986     auto config = m_latteView->containment()->config();
0987 
0988     config.writeEntry("enableKWinEdges", m_enableKWinEdgesFromUser);
0989     config.writeEntry("timerShow", m_timerShow.interval());
0990     config.writeEntry("timerHide", m_timerHideInterval);
0991     config.writeEntry("raiseOnDesktopChange", m_raiseOnDesktopChange);
0992     config.writeEntry("raiseOnActivityChange", m_raiseOnActivityChange);
0993     config.writeEntry("visibility", static_cast<int>(m_mode));
0994 
0995 }
0996 
0997 void VisibilityManager::restoreConfig()
0998 {
0999     auto config = m_latteView->containment()->config();
1000     setTimerHide(qMax(HIDEMINIMUMINTERVAL, config.readEntry("timerHide", 700)));
1001     setTimerShow(config.readEntry("timerShow", 0));
1002     setEnableKWinEdges(config.readEntry("enableKWinEdges", true));
1003     setRaiseOnDesktop(config.readEntry("raiseOnDesktopChange", false));
1004     setRaiseOnActivity(config.readEntry("raiseOnActivityChange", false));
1005 
1006     setMode((Types::Visibility)(config.readEntry("visibility", (int)(Types::DodgeActive))));
1007 }
1008 
1009 bool VisibilityManager::containsMouse() const
1010 {
1011     return m_containsMouse;
1012 }
1013 
1014 void VisibilityManager::setContainsMouse(bool contains)
1015 {
1016     if (m_containsMouse == contains) {
1017         return;
1018     }
1019 
1020     m_containsMouse = contains;
1021     emit containsMouseChanged();
1022 }
1023 
1024 bool VisibilityManager::windowContainsMouse()
1025 {
1026     return m_containsMouse || (m_edgeGhostWindow && m_edgeGhostWindow->containsMouse());
1027 }
1028 
1029 void VisibilityManager::checkMouseInFloatingArea()
1030 {
1031     if (m_isFloatingGapWindowEnabled) {
1032         if (!m_floatingGapWindow) {
1033             createFloatingGapWindow();
1034         }
1035 
1036         m_floatingGapWindow->callAsyncContainsMouse();
1037     }
1038 }
1039 
1040 void VisibilityManager::viewEventManager(QEvent *ev)
1041 {
1042     switch (ev->type()) {
1043     case QEvent::Enter:
1044         setContainsMouse(true);
1045         break;
1046 
1047     case QEvent::Leave:
1048         m_dragEnter = false;
1049         setContainsMouse(false);
1050         break;
1051 
1052     case QEvent::DragEnter:
1053         m_dragEnter = true;
1054 
1055         if (m_isHidden && !isSidebar()) {
1056             emit mustBeShown();
1057         }
1058 
1059         break;
1060 
1061     case QEvent::DragLeave:
1062     case QEvent::Drop:
1063         m_dragEnter = false;
1064         updateHiddenState();
1065         break;
1066 
1067     default:
1068         break;
1069     }
1070 }
1071 
1072 //! KWin Edges Support functions
1073 bool VisibilityManager::enableKWinEdges() const
1074 {
1075     return m_enableKWinEdgesFromUser;
1076 }
1077 
1078 void VisibilityManager::setEnableKWinEdges(bool enable)
1079 {
1080     if (m_enableKWinEdgesFromUser == enable) {
1081         return;
1082     }
1083 
1084     m_enableKWinEdgesFromUser = enable;
1085 
1086     emit enableKWinEdgesChanged();
1087 }
1088 
1089 void VisibilityManager::updateKWinEdgesSupport()
1090 {
1091     if ((m_mode == Types::AutoHide
1092          || m_mode == Types::DodgeActive
1093          || m_mode == Types::DodgeAllWindows
1094          || m_mode == Types::DodgeMaximized)
1095             && !m_latteView->byPassWM()) {
1096 
1097         if (m_enableKWinEdgesFromUser || m_latteView->behaveAsPlasmaPanel()) {
1098             createEdgeGhostWindow();
1099         } else if (!m_enableKWinEdgesFromUser) {
1100             deleteEdgeGhostWindow();
1101         }
1102     } else if (m_mode == Types::WindowsCanCover) {
1103         createEdgeGhostWindow();
1104     } else {
1105         deleteEdgeGhostWindow();
1106     }
1107 }
1108 
1109 void VisibilityManager::onIsFloatingGapWindowEnabledChanged()
1110 {
1111     if (m_isFloatingGapWindowEnabled) {
1112         createFloatingGapWindow();
1113     } else {
1114         deleteFloatingGapWindow();
1115     }
1116 }
1117 
1118 void VisibilityManager::createEdgeGhostWindow()
1119 {
1120     if (!m_edgeGhostWindow) {
1121         m_edgeGhostWindow = new ScreenEdgeGhostWindow(m_latteView);
1122 
1123         connect(m_edgeGhostWindow, &ScreenEdgeGhostWindow::containsMouseChanged, this, [ = ](bool contains) {
1124             if (contains) {
1125                 raiseView(true);
1126             } else {
1127                 m_timerShow.stop();
1128                 updateGhostWindowState();
1129             }
1130         });
1131 
1132         connect(m_edgeGhostWindow, &ScreenEdgeGhostWindow::dragEntered, this, [&]() {
1133             if (m_isHidden) {
1134                 emit mustBeShown();
1135             }
1136         });
1137 
1138         m_connectionsKWinEdges[0] = connect(m_wm, &WindowSystem::AbstractWindowInterface::currentActivityChanged,
1139                                             this, [&]() {
1140             bool inCurrentLayout = (m_corona->layoutsManager()->memoryUsage() == MemoryUsage::SingleLayout ||
1141                                     (m_corona->layoutsManager()->memoryUsage() == MemoryUsage::MultipleLayouts
1142                                      && m_latteView->layout() && !m_latteView->positioner()->inRelocationAnimation()
1143                                      && m_latteView->layout()->isCurrent()));
1144 
1145             if (m_edgeGhostWindow) {
1146                 if (inCurrentLayout) {
1147                     m_wm->setActiveEdge(m_edgeGhostWindow, m_isHidden);
1148                 } else {
1149                     m_wm->setActiveEdge(m_edgeGhostWindow, false);
1150                 }
1151             }
1152         });
1153 
1154         emit supportsKWinEdgesChanged();
1155     }
1156 }
1157 
1158 void VisibilityManager::deleteEdgeGhostWindow()
1159 {
1160     if (m_edgeGhostWindow) {
1161         m_edgeGhostWindow->deleteLater();
1162         m_edgeGhostWindow = nullptr;
1163 
1164         for (auto &c : m_connectionsKWinEdges) {
1165             disconnect(c);
1166         }
1167 
1168         emit supportsKWinEdgesChanged();
1169     }
1170 }
1171 
1172 void VisibilityManager::createFloatingGapWindow()
1173 {
1174     if (!m_floatingGapWindow) {
1175         m_floatingGapWindow = new FloatingGapWindow(m_latteView);
1176 
1177         connect(m_floatingGapWindow, &FloatingGapWindow::asyncContainsMouseChanged, this, [ = ](bool contains) {
1178             if (contains) {
1179                 if (m_isFloatingGapWindowEnabled && !m_isHidden) {
1180                     //! immediate call after contains mouse checks for mouse in sensitive floating areas
1181                     updateHiddenState();
1182                 }
1183             } else {
1184                 if (m_isFloatingGapWindowEnabled && !m_isHidden) {
1185                     //! immediate call after contains mouse checks for mouse in sensitive floating areas
1186                     emit mustBeHide();
1187                 }
1188             }
1189         });
1190     }
1191 }
1192 
1193 void VisibilityManager::deleteFloatingGapWindow()
1194 {
1195     if (m_floatingGapWindow) {
1196         m_floatingGapWindow->deleteLater();
1197         m_floatingGapWindow = nullptr;
1198     }
1199 }
1200 
1201 bool VisibilityManager::supportsFloatingGap() const
1202 {
1203     return (m_floatingGapWindow != nullptr);
1204 }
1205 
1206 
1207 //! END: VisibilityManager implementation
1208 
1209 }
1210 }