File indexing completed on 2024-12-01 13:38:02

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