File indexing completed on 2024-05-05 05:34:06

0001 /*
0002     SPDX-FileCopyrightText: 2019 Michail Vourlakos <mvourlakos@gmail.com>
0003     SPDX-License-Identifier: GPL-2.0-or-later
0004 */
0005 
0006 #include "windowstracker.h"
0007 
0008 // local
0009 #include "lastactivewindow.h"
0010 #include "schemes.h"
0011 #include "trackedlayoutinfo.h"
0012 #include "trackedviewinfo.h"
0013 #include "../abstractwindowinterface.h"
0014 #include "../schemecolors.h"
0015 #include "../../apptypes.h"
0016 #include "../../lattecorona.h"
0017 #include "../../layout/genericlayout.h"
0018 #include "../../layouts/manager.h"
0019 #include "../../view/view.h"
0020 #include "../../view/positioner.h"
0021 
0022 // Qt
0023 #include <KWindowSystem>
0024 
0025 namespace Latte {
0026 namespace WindowSystem {
0027 namespace Tracker {
0028 
0029 Windows::Windows(AbstractWindowInterface *parent)
0030     : QObject(parent)
0031 {
0032     m_wm = parent;
0033 
0034     m_extraViewHintsTimer.setInterval(600);
0035     m_extraViewHintsTimer.setSingleShot(true);
0036 
0037     connect(&m_extraViewHintsTimer, &QTimer::timeout, this, &Windows::updateExtraViewHints);
0038 
0039     //! delayed application data
0040     m_updateApplicationDataTimer.setInterval(1500);
0041     m_updateApplicationDataTimer.setSingleShot(true);
0042     connect(&m_updateApplicationDataTimer, &QTimer::timeout, this, &Windows::updateApplicationData);
0043 
0044     //! delayed update all hints
0045     m_updateAllHintsTimer.setInterval(300);
0046     m_updateAllHintsTimer.setSingleShot(true);
0047     connect(&m_updateAllHintsTimer, &QTimer::timeout, this, &Windows::updateAllHints);
0048 
0049     init();
0050 }
0051 
0052 Windows::~Windows()
0053 {
0054     //! clear all the m_views tracking information
0055     for (QHash<Latte::View *, TrackedViewInfo *>::iterator i=m_views.begin(); i!=m_views.end(); ++i) {
0056         i.value()->deleteLater();
0057         m_views[i.key()] = nullptr;
0058     }
0059 
0060     m_views.clear();
0061 
0062     //! clear all the m_layouts tracking layouts
0063     for (QHash<Latte::Layout::GenericLayout *, TrackedLayoutInfo *>::iterator i=m_layouts.begin(); i!=m_layouts.end(); ++i) {
0064         i.value()->deleteLater();
0065         m_layouts[i.key()] = nullptr;
0066     }
0067 
0068     m_layouts.clear();
0069 }
0070 
0071 void Windows::init()
0072 {
0073     connect(m_wm, &AbstractWindowInterface::windowChanged, this, [&](WindowId wid) {
0074         m_windows[wid] = m_wm->requestInfo(wid);
0075         updateAllHints();
0076 
0077         emit windowChanged(wid);
0078     });
0079 
0080     connect(m_wm, &AbstractWindowInterface::windowRemoved, this, [&](WindowId wid) {
0081         m_windows.remove(wid);
0082 
0083         //! application data
0084         m_initializedApplicationData.removeAll(wid);
0085         m_delayedApplicationData.removeAll(wid);
0086 
0087         updateAllHints();
0088 
0089         emit windowRemoved(wid);
0090     });
0091 
0092     connect(m_wm, &AbstractWindowInterface::windowAdded, this, [&](WindowId wid) {
0093         if (!m_windows.contains(wid)) {
0094             m_windows.insert(wid, m_wm->requestInfo(wid));
0095         }
0096         updateAllHints();
0097     });
0098 
0099     connect(m_wm, &AbstractWindowInterface::activeWindowChanged, this, [&](WindowId wid) {
0100         //! for some reason this is needed in order to update properly activeness values
0101         //! when the active window changes the previous active windows should be also updated
0102         for (const auto view : m_views.keys()) {
0103             WindowId lastWinId = m_views[view]->lastActiveWindow()->currentWinId();
0104             if ((lastWinId) != wid && m_windows.contains(lastWinId)) {
0105                 m_windows[lastWinId] = m_wm->requestInfo(lastWinId);
0106             }
0107         }
0108 
0109         m_windows[wid] = m_wm->requestInfo(wid);
0110         updateAllHints();
0111 
0112         emit activeWindowChanged(wid);
0113     });
0114 
0115     connect(m_wm, &AbstractWindowInterface::currentDesktopChanged, this, &Windows::updateAllHints);
0116     connect(m_wm, &AbstractWindowInterface::currentActivityChanged,  this, &Windows::updateAllHints);    
0117     connect(m_wm, &AbstractWindowInterface::isShowingDesktopChanged,  this, &Windows::updateAllHints);
0118 }
0119 
0120 void Windows::initLayoutHints(Latte::Layout::GenericLayout *layout)
0121 {
0122     if (!m_layouts.contains(layout)) {
0123         return;
0124     }
0125 
0126     setActiveWindowMaximized(layout, false);
0127     setExistsWindowActive(layout, false);
0128     setExistsWindowMaximized(layout, false);
0129     setActiveWindowScheme(layout, nullptr);
0130 }
0131 
0132 void Windows::initViewHints(Latte::View *view)
0133 {
0134     if (!m_views.contains(view)) {
0135         return;
0136     }
0137 
0138     setActiveWindowMaximized(view, false);
0139     setActiveWindowTouching(view, false);
0140     setActiveWindowTouchingEdge(view, false);
0141     setExistsWindowActive(view, false);
0142     setExistsWindowTouching(view, false);
0143     setExistsWindowTouchingEdge(view, false);
0144     setExistsWindowMaximized(view, false);
0145     setIsTouchingBusyVerticalView(view, false);
0146     setActiveWindowScheme(view, nullptr);
0147     setTouchingWindowScheme(view, nullptr);
0148 }
0149 
0150 AbstractWindowInterface *Windows::wm()
0151 {
0152     return m_wm;
0153 }
0154 
0155 
0156 void Windows::addView(Latte::View *view)
0157 {
0158     if (m_views.contains(view)) {
0159         return;
0160     }
0161 
0162     m_views[view] = new TrackedViewInfo(this, view);
0163 
0164     updateScreenGeometries();
0165 
0166     //! Consider Layouts
0167     addRelevantLayout(view);
0168 
0169     connect(view, &Latte::View::layoutChanged, this, [&, view]() {
0170         addRelevantLayout(view);
0171     });
0172 
0173     connect(view, &Latte::View::screenGeometryChanged, this, &Windows::updateScreenGeometries);
0174 
0175     connect(view, &Latte::View::isTouchingBottomViewAndIsBusyChanged, this, &Windows::updateExtraViewHints);
0176     connect(view, &Latte::View::isTouchingTopViewAndIsBusyChanged, this, &Windows::updateExtraViewHints);
0177     connect(view, &Latte::View::absoluteGeometryChanged, this, &Windows::updateAllHintsAfterTimer);
0178 
0179     updateAllHints();
0180 
0181     emit informationAnnounced(view);
0182 }
0183 
0184 void Windows::removeView(Latte::View *view)
0185 {
0186     if (!m_views.contains(view)) {
0187         return;
0188     }
0189 
0190     m_views[view]->deleteLater();
0191     m_views.remove(view);
0192 
0193     updateRelevantLayouts();
0194 }
0195 
0196 void Windows::addRelevantLayout(Latte::View *view)
0197 {
0198     if (view->layout()) {
0199         bool initializing {false};
0200 
0201         if (!m_layouts.contains(view->layout())) {
0202             initializing = true;
0203             m_layouts[view->layout()] = new TrackedLayoutInfo(this, view->layout());
0204         }
0205 
0206         //! Update always the AllScreens tracking because there is a chance a view delayed to be assigned in a layout
0207         //! and that could create a state the AllScreens tracking will be disabled if there is a View requesting
0208         //! tracking and one that it does not during startup
0209         updateRelevantLayouts();
0210 
0211         if (initializing) {
0212             updateHints(view->layout());
0213             emit informationAnnouncedForLayout(view->layout());
0214         }
0215     }
0216 }
0217 
0218 void Windows::updateRelevantLayouts()
0219 {
0220     QList<Latte::Layout::GenericLayout*> orphanedLayouts;
0221 
0222     //! REMOVE Orphaned Relevant layouts that have been removed or they don't contain any Views anymore
0223     for (QHash<Latte::Layout::GenericLayout *, TrackedLayoutInfo *>::iterator i=m_layouts.begin(); i!=m_layouts.end(); ++i) {
0224         bool hasView{false};
0225         for (QHash<Latte::View *, TrackedViewInfo *>::iterator j=m_views.begin(); j!=m_views.end(); ++j) {
0226             if (j.key() && i.key() && i.key() == j.key()->layout()) {
0227                 hasView = true;
0228                 break;
0229             }
0230         }
0231 
0232         if (!hasView)  {
0233             if (i.value()) {
0234                 i.value()->deleteLater();
0235             }
0236             orphanedLayouts << i.key();
0237         }
0238     }
0239 
0240     for(const auto &layout : orphanedLayouts) {
0241         m_layouts.remove(layout);
0242     }
0243 
0244     //! UPDATE Enabled layout window tracking based on the Views that are requesting windows tracking
0245     for (QHash<Latte::Layout::GenericLayout *, TrackedLayoutInfo *>::iterator i=m_layouts.begin(); i!=m_layouts.end(); ++i) {
0246         bool hasViewEnabled{false};
0247         for (QHash<Latte::View *, TrackedViewInfo *>::iterator j=m_views.begin(); j!=m_views.end(); ++j) {
0248             if (i.key() == j.key()->layout() && j.value()->enabled()) {
0249                 hasViewEnabled = true;
0250                 break;
0251             }
0252         }
0253 
0254         if (i.value()) {
0255             i.value()->setEnabled(hasViewEnabled);
0256 
0257             if (!hasViewEnabled) {
0258                 initLayoutHints(i.key());
0259             }
0260         }
0261     }
0262 }
0263 
0264 //! Views Properties And Hints
0265 
0266 bool Windows::enabled(Latte::View *view)
0267 {
0268     if (!m_views.contains(view)) {
0269         return false;
0270     }
0271 
0272     return m_views[view]->enabled();
0273 }
0274 
0275 void Windows::setEnabled(Latte::View *view, const bool enabled)
0276 {
0277     if (!m_views.contains(view) || m_views[view]->enabled() == enabled) {
0278         return;
0279     }
0280 
0281     m_views[view]->setEnabled(enabled);
0282 
0283     if (enabled) {
0284         updateHints(view);
0285     } else {
0286         initViewHints(view);
0287     }
0288 
0289     updateRelevantLayouts();
0290 
0291     emit enabledChanged(view);
0292 }
0293 
0294 bool Windows::activeWindowMaximized(Latte::View *view) const
0295 {
0296     if (!m_views.contains(view)) {
0297         return false;
0298     }
0299 
0300     return m_views[view]->activeWindowMaximized();
0301 }
0302 
0303 void Windows::setActiveWindowMaximized(Latte::View *view, bool activeMaximized)
0304 {
0305     if (!m_views.contains(view) || m_views[view]->activeWindowMaximized() == activeMaximized) {
0306         return;
0307     }
0308 
0309     m_views[view]->setActiveWindowMaximized(activeMaximized);
0310     emit activeWindowMaximizedChanged(view);
0311 }
0312 
0313 bool Windows::activeWindowTouching(Latte::View *view) const
0314 {
0315     if (!m_views.contains(view)) {
0316         return false;
0317     }
0318 
0319     return m_views[view]->activeWindowTouching();
0320 }
0321 
0322 void Windows::setActiveWindowTouching(Latte::View *view, bool activeTouching)
0323 {
0324     if (!m_views.contains(view) || m_views[view]->activeWindowTouching() == activeTouching) {
0325         return;
0326     }
0327 
0328     m_views[view]->setActiveWindowTouching(activeTouching);
0329     emit activeWindowTouchingChanged(view);
0330 }
0331 
0332 bool Windows::activeWindowTouchingEdge(Latte::View *view) const
0333 {
0334     if (!m_views.contains(view)) {
0335         return false;
0336     }
0337 
0338     return m_views[view]->activeWindowTouchingEdge();
0339 }
0340 
0341 void Windows::setActiveWindowTouchingEdge(Latte::View *view, bool activeTouchingEdge)
0342 {
0343     if (!m_views.contains(view) || m_views[view]->activeWindowTouchingEdge() == activeTouchingEdge) {
0344         return;
0345     }
0346 
0347     m_views[view]->setActiveWindowTouchingEdge(activeTouchingEdge);
0348     emit activeWindowTouchingEdgeChanged(view);
0349 }
0350 
0351 bool Windows::existsWindowActive(Latte::View *view) const
0352 {
0353     if (!m_views.contains(view)) {
0354         return false;
0355     }
0356 
0357     return m_views[view]->existsWindowActive();
0358 }
0359 
0360 void Windows::setExistsWindowActive(Latte::View *view, bool windowActive)
0361 {
0362     if (!m_views.contains(view) || m_views[view]->existsWindowActive() == windowActive) {
0363         return;
0364     }
0365 
0366     m_views[view]->setExistsWindowActive(windowActive);
0367     emit existsWindowActiveChanged(view);
0368 }
0369 
0370 bool Windows::existsWindowMaximized(Latte::View *view) const
0371 {
0372     if (!m_views.contains(view)) {
0373         return false;
0374     }
0375 
0376     return m_views[view]->existsWindowMaximized();
0377 }
0378 
0379 void Windows::setExistsWindowMaximized(Latte::View *view, bool windowMaximized)
0380 {
0381     if (!m_views.contains(view) || m_views[view]->existsWindowMaximized() == windowMaximized) {
0382         return;
0383     }
0384 
0385     m_views[view]->setExistsWindowMaximized(windowMaximized);
0386     emit existsWindowMaximizedChanged(view);
0387 }
0388 
0389 bool Windows::existsWindowTouching(Latte::View *view) const
0390 {
0391     if (!m_views.contains(view)) {
0392         return false;
0393     }
0394 
0395     return m_views[view]->existsWindowTouching();
0396 }
0397 
0398 void Windows::setExistsWindowTouching(Latte::View *view, bool windowTouching)
0399 {
0400     if (!m_views.contains(view) || m_views[view]->existsWindowTouching() == windowTouching) {
0401         return;
0402     }
0403 
0404     m_views[view]->setExistsWindowTouching(windowTouching);
0405     emit existsWindowTouchingChanged(view);
0406 }
0407 
0408 bool Windows::existsWindowTouchingEdge(Latte::View *view) const
0409 {
0410     if (!m_views.contains(view)) {
0411         return false;
0412     }
0413 
0414     return m_views[view]->existsWindowTouchingEdge();
0415 }
0416 
0417 void Windows::setExistsWindowTouchingEdge(Latte::View *view, bool windowTouchingEdge)
0418 {
0419     if (!m_views.contains(view) || m_views[view]->existsWindowTouchingEdge() == windowTouchingEdge) {
0420         return;
0421     }
0422 
0423     m_views[view]->setExistsWindowTouchingEdge(windowTouchingEdge);
0424     emit existsWindowTouchingEdgeChanged(view);
0425 }
0426 
0427 
0428 bool Windows::isTouchingBusyVerticalView(Latte::View *view) const
0429 {
0430     if (!m_views.contains(view)) {
0431         return false;
0432     }
0433 
0434     return m_views[view]->isTouchingBusyVerticalView();
0435 }
0436 
0437 void Windows::setIsTouchingBusyVerticalView(Latte::View *view, bool viewTouching)
0438 {
0439     if (!m_views.contains(view) || m_views[view]->isTouchingBusyVerticalView() == viewTouching) {
0440         return;
0441     }
0442 
0443     m_views[view]->setIsTouchingBusyVerticalView(viewTouching);
0444     emit isTouchingBusyVerticalViewChanged(view);
0445 }
0446 
0447 SchemeColors *Windows::activeWindowScheme(Latte::View *view) const
0448 {
0449     if (!m_views.contains(view)) {
0450         return nullptr;
0451     }
0452 
0453     return m_views[view]->activeWindowScheme();
0454 }
0455 
0456 void Windows::setActiveWindowScheme(Latte::View *view, WindowSystem::SchemeColors *scheme)
0457 {
0458     if (!m_views.contains(view) || m_views[view]->activeWindowScheme() == scheme) {
0459         return;
0460     }
0461 
0462     m_views[view]->setActiveWindowScheme(scheme);
0463     emit activeWindowSchemeChanged(view);
0464 }
0465 
0466 SchemeColors *Windows::touchingWindowScheme(Latte::View *view) const
0467 {
0468     if (!m_views.contains(view)) {
0469         return nullptr;
0470     }
0471 
0472     return m_views[view]->touchingWindowScheme();
0473 }
0474 
0475 void Windows::setTouchingWindowScheme(Latte::View *view, WindowSystem::SchemeColors *scheme)
0476 {
0477     if (!m_views.contains(view) || m_views[view]->touchingWindowScheme() == scheme) {
0478         return;
0479     }
0480 
0481     m_views[view]->setTouchingWindowScheme(scheme);
0482     emit touchingWindowSchemeChanged(view);
0483 }
0484 
0485 LastActiveWindow *Windows::lastActiveWindow(Latte::View *view)
0486 {
0487     if (!m_views.contains(view)) {
0488         return nullptr;
0489     }
0490 
0491     return m_views[view]->lastActiveWindow();
0492 }
0493 
0494 //! Layouts
0495 bool Windows::enabled(Latte::Layout::GenericLayout *layout)
0496 {
0497     if (!m_layouts.contains(layout)) {
0498         return false;
0499     }
0500 
0501     return m_layouts[layout]->enabled();
0502 }
0503 
0504 bool Windows::activeWindowMaximized(Latte::Layout::GenericLayout *layout) const
0505 {
0506     if (!m_layouts.contains(layout)) {
0507         return false;
0508     }
0509 
0510     return m_layouts[layout]->activeWindowMaximized();
0511 }
0512 
0513 void Windows::setActiveWindowMaximized(Latte::Layout::GenericLayout *layout, bool activeMaximized)
0514 {
0515     if (!m_layouts.contains(layout) || m_layouts[layout]->activeWindowMaximized() == activeMaximized) {
0516         return;
0517     }
0518 
0519     m_layouts[layout]->setActiveWindowMaximized(activeMaximized);
0520     emit activeWindowMaximizedChangedForLayout(layout);
0521 }
0522 
0523 bool Windows::existsWindowActive(Latte::Layout::GenericLayout *layout) const
0524 {
0525     if (!m_layouts.contains(layout)) {
0526         return false;
0527     }
0528 
0529     return m_layouts[layout]->existsWindowActive();
0530 }
0531 
0532 void Windows::setExistsWindowActive(Latte::Layout::GenericLayout *layout, bool windowActive)
0533 {
0534     if (!m_layouts.contains(layout) || m_layouts[layout]->existsWindowActive() == windowActive) {
0535         return;
0536     }
0537 
0538     m_layouts[layout]->setExistsWindowActive(windowActive);
0539     emit existsWindowActiveChangedForLayout(layout);
0540 }
0541 
0542 bool Windows::existsWindowMaximized(Latte::Layout::GenericLayout *layout) const
0543 {
0544     if (!m_layouts.contains(layout)) {
0545         return false;
0546     }
0547 
0548     return m_layouts[layout]->existsWindowMaximized();
0549 }
0550 
0551 void Windows::setExistsWindowMaximized(Latte::Layout::GenericLayout *layout, bool windowMaximized)
0552 {
0553     if (!m_layouts.contains(layout) || m_layouts[layout]->existsWindowMaximized() == windowMaximized) {
0554         return;
0555     }
0556 
0557     m_layouts[layout]->setExistsWindowMaximized(windowMaximized);
0558     emit existsWindowMaximizedChangedForLayout(layout);
0559 }
0560 
0561 SchemeColors *Windows::activeWindowScheme(Latte::Layout::GenericLayout *layout) const
0562 {
0563     if (!m_layouts.contains(layout)) {
0564         return nullptr;
0565     }
0566 
0567     return m_layouts[layout]->activeWindowScheme();
0568 }
0569 
0570 void Windows::setActiveWindowScheme(Latte::Layout::GenericLayout *layout, WindowSystem::SchemeColors *scheme)
0571 {
0572     if (!m_layouts.contains(layout) || m_layouts[layout]->activeWindowScheme() == scheme) {
0573         return;
0574     }
0575 
0576     m_layouts[layout]->setActiveWindowScheme(scheme);
0577     emit activeWindowSchemeChangedForLayout(layout);
0578 }
0579 
0580 LastActiveWindow *Windows::lastActiveWindow(Latte::Layout::GenericLayout *layout)
0581 {
0582     if (!m_layouts.contains(layout)) {
0583         return nullptr;
0584     }
0585 
0586     return m_layouts[layout]->lastActiveWindow();
0587 }
0588 
0589 
0590 //! Windows
0591 bool Windows::isValidFor(const WindowId &wid) const
0592 {
0593     if (!m_windows.contains(wid)) {
0594         return false;
0595     }
0596 
0597     return m_windows[wid].isValid();
0598 }
0599 
0600 QIcon Windows::iconFor(const WindowId &wid)
0601 {
0602     if (!m_windows.contains(wid)) {
0603         return QIcon();
0604     }
0605 
0606     if (m_windows[wid].icon().isNull()) {
0607         AppData data = m_wm->appDataFor(wid);
0608 
0609         QIcon icon = data.icon;
0610 
0611         if (icon.isNull()) {
0612             icon = m_wm->iconFor(wid);
0613         }
0614 
0615         m_windows[wid].setIcon(icon);
0616         return icon;
0617     }
0618 
0619     return m_windows[wid].icon();
0620 }
0621 
0622 QString Windows::appNameFor(const WindowId &wid)
0623 {
0624     if (!m_windows.contains(wid)) {
0625         return QString();
0626     }
0627 
0628     if(!m_initializedApplicationData.contains(wid) && !m_delayedApplicationData.contains(wid)) {
0629         m_delayedApplicationData.append(wid);
0630         m_updateApplicationDataTimer.start();
0631     }
0632 
0633     if (m_windows[wid].appName().isEmpty()) {
0634         AppData data = m_wm->appDataFor(wid);
0635 
0636         m_windows[wid].setAppName(data.name);
0637 
0638         return data.name;
0639     }
0640 
0641     return m_windows[wid].appName();
0642 }
0643 
0644 void Windows::updateApplicationData()
0645 {
0646     if (m_delayedApplicationData.count() > 0) {
0647         for(int i=0; i<m_delayedApplicationData.count(); ++i) {
0648             auto wid = m_delayedApplicationData[i];
0649 
0650             if (m_windows.contains(wid)) {
0651                 AppData data = m_wm->appDataFor(wid);
0652 
0653                 QIcon icon = data.icon;
0654 
0655                 if (icon.isNull()) {
0656                     icon = m_wm->iconFor(wid);
0657                 }
0658 
0659                 m_windows[wid].setIcon(icon);
0660                 m_windows[wid].setAppName(data.name);
0661 
0662                 m_initializedApplicationData.append(wid);
0663 
0664                 emit applicationDataChanged(wid);
0665             }
0666         }
0667     }
0668 
0669     m_delayedApplicationData.clear();
0670 }
0671 
0672 WindowInfoWrap Windows::infoFor(const WindowId &wid) const
0673 {
0674     if (!m_windows.contains(wid)) {
0675         return WindowInfoWrap();
0676     }
0677 
0678     return m_windows[wid];
0679 }
0680 
0681 
0682 
0683 //! Windows Criteria Functions
0684 bool Windows::intersects(Latte::View *view, const WindowInfoWrap &winfo)
0685 {
0686     return (!winfo.isMinimized() && !winfo.isShaded() && winfo.geometry().intersects(view->absoluteGeometry()));
0687 }
0688 
0689 bool Windows::isActive(const WindowInfoWrap &winfo)
0690 {
0691     return (winfo.isValid() && winfo.isActive() && !winfo.isMinimized());
0692 }
0693 
0694 bool Windows::isActiveInViewScreen(Latte::View *view, const WindowInfoWrap &winfo)
0695 {
0696     auto screenGeometry = m_views[view]->screenGeometry();
0697 
0698     if (KWindowSystem::isPlatformX11() && view->devicePixelRatio() != 1.0) {
0699         //!Fix for X11 Global Scale, I dont think this could be pixel perfect accurate
0700         auto factor = view->devicePixelRatio();
0701         screenGeometry = QRect(qRound(screenGeometry.x() * factor),
0702                                qRound(screenGeometry.y() * factor),
0703                                qRound(screenGeometry.width() * factor),
0704                                qRound(screenGeometry.height() * factor));
0705     }
0706 
0707     return (winfo.isValid()
0708             && winfo.isActive()
0709             && !winfo.isMinimized()
0710             && screenGeometry.intersects(winfo.geometry()));
0711 }
0712 
0713 bool Windows::isMaximizedInViewScreen(Latte::View *view, const WindowInfoWrap &winfo)
0714 {
0715     auto screenGeometry = m_views[view]->screenGeometry();
0716 
0717     if (KWindowSystem::isPlatformX11() && view->devicePixelRatio() != 1.0) {
0718         //!Fix for X11 Global Scale, I dont think this could be pixel perfect accurate
0719         auto factor = view->devicePixelRatio();
0720         screenGeometry = QRect(qRound(screenGeometry.x() * factor),
0721                                qRound(screenGeometry.y() * factor),
0722                                qRound(screenGeometry.width() * factor),
0723                                qRound(screenGeometry.height() * factor));
0724     }
0725 
0726     //! updated implementation to identify the screen that the maximized window is present
0727     //! in order to avoid: https://bugs.kde.org/show_bug.cgi?id=397700
0728     return (winfo.isValid()
0729             && !winfo.isMinimized()
0730             && !winfo.isShaded()
0731             && winfo.isMaximized()
0732             && screenGeometry.intersects(winfo.geometry()));
0733 }
0734 
0735 bool Windows::isTouchingView(Latte::View *view, const WindowSystem::WindowInfoWrap &winfo)
0736 {
0737     return (winfo.isValid() && intersects(view, winfo));
0738 }
0739 
0740 bool Windows::isTouchingViewEdge(Latte::View *view, const QRect &windowgeometry)
0741 {
0742     if (!view) {
0743         return false;
0744     }
0745 
0746     bool inViewThicknessEdge{false};
0747     bool inViewLengthBoundaries{false};
0748 
0749     QRect screenGeometry = view->screenGeometry();
0750 
0751     if (KWindowSystem::isPlatformX11() && view->devicePixelRatio() != 1.0) {
0752         //!Fix for X11 Global Scale, I dont think this could be pixel perfect accurate
0753         auto factor = view->devicePixelRatio();
0754         screenGeometry = QRect(qRound(screenGeometry.x() * factor),
0755                                qRound(screenGeometry.y() * factor),
0756                                qRound(screenGeometry.width() * factor),
0757                                qRound(screenGeometry.height() * factor));
0758     }
0759 
0760     bool inCurrentScreen{screenGeometry.contains(windowgeometry.topLeft()) || screenGeometry.contains(windowgeometry.bottomRight())};
0761 
0762     if (inCurrentScreen) {
0763         if (view->location() == Plasma::Types::TopEdge) {
0764             inViewThicknessEdge = (windowgeometry.y() == view->absoluteGeometry().bottom() + 1);
0765         } else if (view->location() == Plasma::Types::BottomEdge) {
0766             inViewThicknessEdge = (windowgeometry.bottom() == view->absoluteGeometry().top() - 1);
0767         } else if (view->location() == Plasma::Types::LeftEdge) {
0768             inViewThicknessEdge = (windowgeometry.x() == view->absoluteGeometry().right() + 1);
0769         } else if (view->location() == Plasma::Types::RightEdge) {
0770             inViewThicknessEdge = (windowgeometry.right() == view->absoluteGeometry().left() - 1);
0771         }
0772 
0773         if (view->formFactor() == Plasma::Types::Horizontal) {
0774             int yCenter = view->absoluteGeometry().center().y();
0775 
0776             QPoint leftChecker(windowgeometry.left(), yCenter);
0777             QPoint rightChecker(windowgeometry.right(), yCenter);
0778 
0779             bool fulloverlap = (windowgeometry.left()<=view->absoluteGeometry().left()) && (windowgeometry.right()>=view->absoluteGeometry().right());
0780 
0781             inViewLengthBoundaries = fulloverlap || view->absoluteGeometry().contains(leftChecker) || view->absoluteGeometry().contains(rightChecker);
0782         } else if (view->formFactor() == Plasma::Types::Vertical) {
0783             int xCenter = view->absoluteGeometry().center().x();
0784 
0785             QPoint topChecker(xCenter, windowgeometry.top());
0786             QPoint bottomChecker(xCenter, windowgeometry.bottom());
0787 
0788             bool fulloverlap = (windowgeometry.top()<=view->absoluteGeometry().top()) && (windowgeometry.bottom()>=view->absoluteGeometry().bottom());
0789 
0790             inViewLengthBoundaries = fulloverlap || view->absoluteGeometry().contains(topChecker) || view->absoluteGeometry().contains(bottomChecker);
0791         }
0792     }
0793 
0794     return (inViewThicknessEdge && inViewLengthBoundaries);
0795 }
0796 
0797 bool Windows::isTouchingViewEdge(Latte::View *view, const WindowInfoWrap &winfo)
0798 {
0799     if (winfo.isValid() &&  !winfo.isMinimized()) {
0800         return isTouchingViewEdge(view, winfo.geometry());
0801     }
0802 
0803     return false;
0804 }
0805 
0806 void Windows::cleanupFaultyWindows()
0807 {
0808     for (const auto &key : m_windows.keys()) {
0809         auto winfo = m_windows[key];
0810 
0811         //! garbage windows removing
0812         if (winfo.wid()<=0 || winfo.geometry() == QRect(0, 0, 0, 0)) {
0813             //qDebug() << "Faulty Geometry ::: " << winfo.wid();
0814             m_windows.remove(key);
0815         }
0816     }
0817 }
0818 
0819 
0820 void Windows::updateScreenGeometries()
0821 {
0822     for (const auto view : m_views.keys()) {
0823         if (m_views[view]->screenGeometry() != view->screenGeometry()) {
0824             m_views[view]->setScreenGeometry(view->screenGeometry());
0825 
0826             if (m_views[view]->enabled()) {
0827                 updateHints(view);
0828             }
0829         }
0830     }
0831 }
0832 
0833 void Windows::updateAllHintsAfterTimer()
0834 {
0835     if (!m_updateAllHintsTimer.isActive()) {
0836         updateAllHints();
0837         m_updateAllHintsTimer.start();
0838     }
0839 }
0840 
0841 void Windows::updateAllHints()
0842 {
0843     for (const auto view : m_views.keys()) {
0844         updateHints(view);
0845     }
0846 
0847     for (const auto layout : m_layouts.keys()) {
0848         updateHints(layout);
0849     }
0850 
0851     if (!m_extraViewHintsTimer.isActive()) {
0852         m_extraViewHintsTimer.start();
0853     }
0854 }
0855 
0856 void Windows::updateExtraViewHints()
0857 {
0858     for (const auto horView : m_views.keys()) {
0859         if (!m_views.contains(horView) || !m_views[horView]->enabled() || !m_views[horView]->isTrackingCurrentActivity()) {
0860             continue;
0861         }
0862 
0863         if (horView->formFactor() == Plasma::Types::Horizontal) {
0864             bool touchingBusyVerticalView{false};
0865 
0866             for (const auto verView : m_views.keys()) {
0867                 if (!m_views.contains(verView) || !m_views[verView]->enabled() || !m_views[verView]->isTrackingCurrentActivity()) {
0868                     continue;
0869                 }
0870 
0871                 bool sameScreen = (verView->positioner()->currentScreenId() == horView->positioner()->currentScreenId());
0872 
0873                 if (verView->formFactor() == Plasma::Types::Vertical && sameScreen) {
0874                     bool hasEdgeTouch = isTouchingViewEdge(horView, verView->absoluteGeometry());
0875 
0876                     bool topTouch = horView->location() == Plasma::Types::TopEdge && verView->isTouchingTopViewAndIsBusy() && hasEdgeTouch;
0877                     bool bottomTouch = horView->location() == Plasma::Types::BottomEdge && verView->isTouchingBottomViewAndIsBusy() && hasEdgeTouch;
0878 
0879                     if (topTouch || bottomTouch) {
0880                         touchingBusyVerticalView = true;
0881                         break;
0882                     }
0883                 }
0884             }
0885 
0886             //qDebug() << " Touching Busy Vertical View :: " << horView->location() << " - " << horView->positioner()->currentScreenId() << " :: " << touchingBusyVerticalView;
0887 
0888             setIsTouchingBusyVerticalView(horView, touchingBusyVerticalView);
0889         }
0890     }
0891 }
0892 
0893 void Windows::updateHints(Latte::View *view)
0894 {
0895     if (!m_views.contains(view) || !m_views[view]->enabled() || !m_views[view]->isTrackingCurrentActivity()) {
0896         return;
0897     }
0898 
0899     bool foundActive{false};
0900     bool foundActiveInCurScreen{false};
0901     bool foundActiveTouchInCurScreen{false};
0902     bool foundActiveEdgeTouchInCurScreen{false};
0903     bool foundTouchInCurScreen{false};
0904     bool foundTouchEdgeInCurScreen{false};
0905     bool foundMaximizedInCurScreen{false};
0906 
0907     bool foundActiveGroupTouchInCurScreen{false};
0908 
0909     //! the notification window is not sending a remove signal and creates windows of geometry (0x0 0,0),
0910     //! maybe a garbage collector here is a good idea!!!
0911     bool existsFaultyWindow{false};
0912 
0913     WindowId maxWinId;
0914     WindowId activeWinId;
0915     WindowId touchWinId;
0916     WindowId touchEdgeWinId;
0917     WindowId activeTouchWinId;
0918     WindowId activeTouchEdgeWinId;
0919 
0920     //qDebug() << " -- TRACKING REPORT (SCREEN)--";
0921 
0922     //! First Pass
0923     for (const auto &winfo : m_windows) {
0924         if (m_wm->isShowingDesktop()) {
0925             break;
0926         }
0927 
0928         if (!existsFaultyWindow && (winfo.wid()<=0 || winfo.geometry() == QRect(0, 0, 0, 0))) {
0929             existsFaultyWindow = true;
0930         }
0931 
0932         if ( !m_wm->inCurrentDesktopActivity(winfo)
0933              || m_wm->hasBlockedTracking(winfo.wid())
0934              || winfo.isMinimized()) {
0935             continue;
0936         }
0937 
0938         //qDebug() << " _ _ _ ";
0939         //qDebug() << "TRACKING | WINDOW INFO :: " << winfo.wid() << " _ " << winfo.appName() << " _ " << winfo.geometry() << " _ " << winfo.display();
0940 
0941         if (isActive(winfo)) {
0942             foundActive = true;
0943         }
0944 
0945         if (isActiveInViewScreen(view, winfo)) {
0946             foundActiveInCurScreen = true;
0947             activeWinId = winfo.wid();
0948         }
0949 
0950         //! Maximized windows flags
0951         if ((winfo.isActive() && isMaximizedInViewScreen(view, winfo)) //! active maximized windows have higher priority than the rest maximized windows
0952                 || (!foundMaximizedInCurScreen && isMaximizedInViewScreen(view, winfo))) {
0953             foundMaximizedInCurScreen = true;
0954             maxWinId = winfo.wid();
0955         }
0956 
0957         //! Touching windows flags
0958 
0959         bool touchingViewEdge = isTouchingViewEdge(view, winfo);
0960         bool touchingView =  isTouchingView(view, winfo);
0961 
0962         if (touchingView) {
0963             if (winfo.isActive()) {
0964                 foundActiveTouchInCurScreen = true;
0965                 activeTouchWinId = winfo.wid();
0966             } else {
0967                 foundTouchInCurScreen = true;
0968                 touchWinId = winfo.wid();
0969             }
0970         }
0971 
0972         if (touchingViewEdge) {
0973             if (winfo.isActive()) {
0974                 foundActiveEdgeTouchInCurScreen = true;
0975                 activeTouchEdgeWinId = winfo.wid();
0976             } else {
0977                 foundTouchEdgeInCurScreen = true;
0978                 touchEdgeWinId = winfo.wid();
0979             }
0980         }
0981 
0982         //qDebug() << "TRACKING |       ACTIVE:"<< foundActive <<  " ACT_TOUCH_CUR_SCR:" << foundActiveTouchInCurScreen << " MAXIM:"<<foundMaximizedInCurScreen;
0983         //qDebug() << "TRACKING |       TOUCHING VIEW EDGE:"<< touchingViewEdge << " TOUCHING VIEW:" << foundTouchInCurScreen;
0984     }
0985 
0986     if (existsFaultyWindow) {
0987         cleanupFaultyWindows();
0988     }
0989 
0990     //! PASS 2
0991     if (!m_wm->isShowingDesktop() && foundActiveInCurScreen && !foundActiveTouchInCurScreen) {
0992         //! Second Pass to track also Child windows if needed
0993 
0994         //qDebug() << "Windows Array...";
0995         //for (const auto &winfo : m_windows) {
0996         //    qDebug() << " - " << winfo.wid() << " - " << winfo.isValid() << " - " << winfo.display() << " - " << winfo.geometry() << " parent : " << winfo.parentId();
0997         //}
0998         //qDebug() << " - - - - - ";
0999 
1000         WindowInfoWrap activeInfo = m_windows[activeWinId];
1001         WindowId mainWindowId = activeInfo.isChildWindow() ? activeInfo.parentId() : activeWinId;
1002 
1003         for (const auto &winfo : m_windows) {
1004             if (!m_wm->inCurrentDesktopActivity(winfo)
1005                     || m_wm->hasBlockedTracking(winfo.wid())
1006                     || winfo.isMinimized()) {
1007                 continue;
1008             }
1009 
1010             bool inActiveGroup = (winfo.wid() == mainWindowId || winfo.parentId() == mainWindowId);
1011 
1012             //! consider only windows that belong to active window group meaning the main window
1013             //! and its children
1014             if (!inActiveGroup) {
1015                 continue;
1016             }
1017 
1018             if (isTouchingView(view, winfo)) {
1019                 foundActiveGroupTouchInCurScreen = true;
1020                 break;
1021             }
1022         }
1023     }
1024 
1025 
1026     //! HACK: KWin Effects such as ShowDesktop have no way to be identified and as such
1027     //! create issues with identifying properly touching and maximized windows. BUT when
1028     //! they are enabled then NO ACTIVE window is found. This is a way to identify these
1029     //! effects trigerring and disable the touch flags.
1030     //! BUG: 404483
1031     //! Disabled because it has fault identifications, e.g. when a window is maximized and
1032     //! Latte or Plasma are showing their View settings
1033     //foundMaximizedInCurScreen = foundMaximizedInCurScreen && foundActive;
1034     //foundTouchInCurScreen = foundTouchInCurScreen && foundActive;
1035 
1036     //! assign flags
1037     setExistsWindowActive(view, foundActiveInCurScreen);
1038     setActiveWindowTouching(view, foundActiveTouchInCurScreen || foundActiveGroupTouchInCurScreen);
1039     setActiveWindowTouchingEdge(view, foundActiveEdgeTouchInCurScreen);
1040     setActiveWindowMaximized(view, (maxWinId.toInt()>0 && (maxWinId == activeTouchWinId || maxWinId == activeTouchEdgeWinId)));
1041     setExistsWindowMaximized(view, foundMaximizedInCurScreen);
1042     setExistsWindowTouching(view, (foundTouchInCurScreen || foundActiveTouchInCurScreen || foundActiveGroupTouchInCurScreen));
1043     setExistsWindowTouchingEdge(view, (foundActiveEdgeTouchInCurScreen || foundTouchEdgeInCurScreen));
1044 
1045     //! update color schemes for active and touching windows
1046     setActiveWindowScheme(view, (foundActiveInCurScreen ? m_wm->schemesTracker()->schemeForWindow(activeWinId) : nullptr));
1047 
1048     if (foundActiveTouchInCurScreen) {
1049         setTouchingWindowScheme(view, m_wm->schemesTracker()->schemeForWindow(activeTouchWinId));
1050     } else if (foundActiveEdgeTouchInCurScreen) {
1051         setTouchingWindowScheme(view, m_wm->schemesTracker()->schemeForWindow(activeTouchEdgeWinId));
1052     } else if (foundMaximizedInCurScreen) {
1053         setTouchingWindowScheme(view, m_wm->schemesTracker()->schemeForWindow(maxWinId));
1054     } else if (foundTouchInCurScreen) {
1055         setTouchingWindowScheme(view, m_wm->schemesTracker()->schemeForWindow(touchWinId));
1056     } else if (foundTouchEdgeInCurScreen) {
1057         setTouchingWindowScheme(view, m_wm->schemesTracker()->schemeForWindow(touchEdgeWinId));
1058     } else {
1059         setTouchingWindowScheme(view, nullptr);
1060     }
1061 
1062     //! update LastActiveWindow
1063     if (foundActiveInCurScreen) {
1064         m_views[view]->setActiveWindow(activeWinId);
1065     }
1066 
1067     //! Debug
1068     //qDebug() << "TRACKING |      _________ FINAL RESULTS ________";
1069     //qDebug() << "TRACKING | SCREEN: " << view->positioner()->currentScreenId() << " , EDGE:" << view->location() << " , ENABLED:" << enabled(view);
1070     //qDebug() << "TRACKING | activeWindowTouching: " << foundActiveTouchInCurScreen << " ,activeWindowMaximized: " << activeWindowMaximized(view);
1071     //qDebug() << "TRACKING | existsWindowActive: " << foundActiveInCurScreen << " , existsWindowMaximized:" << existsWindowMaximized(view)
1072     //         << " , existsWindowTouching:"<<existsWindowTouching(view);
1073     //qDebug() << "TRACKING | activeEdgeWindowTouch: " <<  activeWindowTouchingEdge(view) << " , existsEdgeWindowTouch:" << existsWindowTouchingEdge(view);
1074     //qDebug() << "TRACKING | existsActiveGroupTouching: " << foundActiveGroupTouchInCurScreen;
1075 }
1076 
1077 void Windows::updateHints(Latte::Layout::GenericLayout *layout) {
1078     if (!m_layouts.contains(layout) || !m_layouts[layout]->enabled() || !m_layouts[layout]->isTrackingCurrentActivity()) {
1079         return;
1080     }
1081 
1082     bool foundActive{false};
1083     bool foundActiveMaximized{false};
1084     bool foundMaximized{false};
1085 
1086     //! the notification window is not sending a remove signal and creates windows of geometry (0x0 0,0),
1087     //! maybe a garbage collector here is a good idea!!!
1088     bool existsFaultyWindow{false};
1089 
1090     WindowId activeWinId;
1091     WindowId maxWinId;
1092 
1093     for (const auto &winfo : m_windows) {
1094         if (m_wm->isShowingDesktop()) {
1095             break;
1096         }
1097 
1098         if (!existsFaultyWindow && (winfo.wid()<=0 || winfo.geometry() == QRect(0, 0, 0, 0))) {
1099             existsFaultyWindow = true;
1100         }
1101 
1102         if (!m_wm->inCurrentDesktopActivity(winfo)
1103                 || m_wm->hasBlockedTracking(winfo.wid())
1104                 || winfo.isMinimized()) {
1105             continue;
1106         }
1107 
1108         if (isActive(winfo)) {
1109             foundActive = true;
1110             activeWinId = winfo.wid();
1111 
1112             if (winfo.isMaximized() && !winfo.isMinimized()) {
1113                 foundActiveMaximized = true;
1114                 maxWinId = winfo.wid();
1115             }
1116         }
1117 
1118         if (!foundActiveMaximized && winfo.isMaximized() && !winfo.isMinimized()) {
1119             foundMaximized = true;
1120             maxWinId = winfo.wid();
1121         }
1122 
1123         //qDebug() << "window geometry ::: " << winfo.geometry();
1124     }
1125 
1126     if (existsFaultyWindow) {
1127         cleanupFaultyWindows();
1128     }
1129 
1130     //! HACK: KWin Effects such as ShowDesktop have no way to be identified and as such
1131     //! create issues with identifying properly touching and maximized windows. BUT when
1132     //! they are enabled then NO ACTIVE window is found. This is a way to identify these
1133     //! effects trigerring and disable the touch flags.
1134     //! BUG: 404483
1135     //! Disabled because it has fault identifications, e.g. when a window is maximized and
1136     //! Latte or Plasma are showing their View settings
1137     //foundMaximizedInCurScreen = foundMaximizedInCurScreen && foundActive;
1138     //foundTouchInCurScreen = foundTouchInCurScreen && foundActive;
1139 
1140     //! assign flags
1141     setExistsWindowActive(layout, foundActive);
1142     setActiveWindowMaximized(layout, foundActiveMaximized);
1143     setExistsWindowMaximized(layout, foundActiveMaximized || foundMaximized);
1144 
1145     //! update color schemes for active and touching windows
1146     setActiveWindowScheme(layout, (foundActive ? m_wm->schemesTracker()->schemeForWindow(activeWinId) : nullptr));
1147 
1148     //! update LastActiveWindow
1149     if (foundActive) {
1150         m_layouts[layout]->setActiveWindow(activeWinId);
1151     }
1152 
1153     //! Debug
1154     //qDebug() << " -- TRACKING REPORT (LAYOUT) --";
1155     //qDebug() << "TRACKING | LAYOUT: " << layout->name() << " , ENABLED:" << enabled(layout);
1156     //qDebug() << "TRACKING | existsActiveWindow: " << foundActive << " ,activeWindowMaximized: " << foundActiveMaximized;
1157     //qDebug() << "TRACKING | existsWindowMaximized: " << existsWindowMaximized(layout);
1158 }
1159 
1160 }
1161 }
1162 }