File indexing completed on 2024-12-01 11:10:46
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 }