1 /*
2 SPDX-FileCopyrightText: 2019 Michail Vourlakos <mvourlakos@gmail.com>
3 SPDX-License-Identifier: GPL-2.0-or-later
4 */
5
6 #include "windowstracker.h"
7
8 // local
9 #include "lastactivewindow.h"
10 #include "schemes.h"
11 #include "trackedlayoutinfo.h"
12 #include "trackedviewinfo.h"
13 #include "../abstractwindowinterface.h"
14 #include "../schemecolors.h"
15 #include "../../apptypes.h"
16 #include "../../lattecorona.h"
17 #include "../../layout/genericlayout.h"
18 #include "../../layouts/manager.h"
19 #include "../../view/view.h"
20 #include "../../view/positioner.h"
21
22 // Qt
23 #include <KWindowSystem>
24
25 namespace Latte {
26 namespace WindowSystem {
27 namespace Tracker {
28
Windows(AbstractWindowInterface * parent)29 Windows::Windows(AbstractWindowInterface *parent)
30 : QObject(parent)
31 {
32 m_wm = parent;
33
34 m_extraViewHintsTimer.setInterval(600);
35 m_extraViewHintsTimer.setSingleShot(true);
36
37 connect(&m_extraViewHintsTimer, &QTimer::timeout, this, &Windows::updateExtraViewHints);
38
39 //! delayed application data
40 m_updateApplicationDataTimer.setInterval(1500);
41 m_updateApplicationDataTimer.setSingleShot(true);
42 connect(&m_updateApplicationDataTimer, &QTimer::timeout, this, &Windows::updateApplicationData);
43
44 //! delayed update all hints
45 m_updateAllHintsTimer.setInterval(300);
46 m_updateAllHintsTimer.setSingleShot(true);
47 connect(&m_updateAllHintsTimer, &QTimer::timeout, this, &Windows::updateAllHints);
48
49 init();
50 }
51
~Windows()52 Windows::~Windows()
53 {
54 //! clear all the m_views tracking information
55 for (QHash<Latte::View *, TrackedViewInfo *>::iterator i=m_views.begin(); i!=m_views.end(); ++i) {
56 i.value()->deleteLater();
57 m_views[i.key()] = nullptr;
58 }
59
60 m_views.clear();
61
62 //! clear all the m_layouts tracking layouts
63 for (QHash<Latte::Layout::GenericLayout *, TrackedLayoutInfo *>::iterator i=m_layouts.begin(); i!=m_layouts.end(); ++i) {
64 i.value()->deleteLater();
65 m_layouts[i.key()] = nullptr;
66 }
67
68 m_layouts.clear();
69 }
70
init()71 void Windows::init()
72 {
73 connect(m_wm, &AbstractWindowInterface::windowChanged, this, [&](WindowId wid) {
74 m_windows[wid] = m_wm->requestInfo(wid);
75 updateAllHints();
76
77 emit windowChanged(wid);
78 });
79
80 connect(m_wm, &AbstractWindowInterface::windowRemoved, this, [&](WindowId wid) {
81 m_windows.remove(wid);
82
83 //! application data
84 m_initializedApplicationData.removeAll(wid);
85 m_delayedApplicationData.removeAll(wid);
86
87 updateAllHints();
88
89 emit windowRemoved(wid);
90 });
91
92 connect(m_wm, &AbstractWindowInterface::windowAdded, this, [&](WindowId wid) {
93 if (!m_windows.contains(wid)) {
94 m_windows.insert(wid, m_wm->requestInfo(wid));
95 }
96 updateAllHints();
97 });
98
99 connect(m_wm, &AbstractWindowInterface::activeWindowChanged, this, [&](WindowId wid) {
100 //! for some reason this is needed in order to update properly activeness values
101 //! when the active window changes the previous active windows should be also updated
102 for (const auto view : m_views.keys()) {
103 WindowId lastWinId = m_views[view]->lastActiveWindow()->currentWinId();
104 if ((lastWinId) != wid && m_windows.contains(lastWinId)) {
105 m_windows[lastWinId] = m_wm->requestInfo(lastWinId);
106 }
107 }
108
109 m_windows[wid] = m_wm->requestInfo(wid);
110 updateAllHints();
111
112 emit activeWindowChanged(wid);
113 });
114
115 connect(m_wm, &AbstractWindowInterface::currentDesktopChanged, this, &Windows::updateAllHints);
116 connect(m_wm, &AbstractWindowInterface::currentActivityChanged, this, &Windows::updateAllHints);
117 connect(m_wm, &AbstractWindowInterface::isShowingDesktopChanged, this, &Windows::updateAllHints);
118 }
119
initLayoutHints(Latte::Layout::GenericLayout * layout)120 void Windows::initLayoutHints(Latte::Layout::GenericLayout *layout)
121 {
122 if (!m_layouts.contains(layout)) {
123 return;
124 }
125
126 setActiveWindowMaximized(layout, false);
127 setExistsWindowActive(layout, false);
128 setExistsWindowMaximized(layout, false);
129 setActiveWindowScheme(layout, nullptr);
130 }
131
initViewHints(Latte::View * view)132 void Windows::initViewHints(Latte::View *view)
133 {
134 if (!m_views.contains(view)) {
135 return;
136 }
137
138 setActiveWindowMaximized(view, false);
139 setActiveWindowTouching(view, false);
140 setActiveWindowTouchingEdge(view, false);
141 setExistsWindowActive(view, false);
142 setExistsWindowTouching(view, false);
143 setExistsWindowTouchingEdge(view, false);
144 setExistsWindowMaximized(view, false);
145 setIsTouchingBusyVerticalView(view, false);
146 setActiveWindowScheme(view, nullptr);
147 setTouchingWindowScheme(view, nullptr);
148 }
149
wm()150 AbstractWindowInterface *Windows::wm()
151 {
152 return m_wm;
153 }
154
155
addView(Latte::View * view)156 void Windows::addView(Latte::View *view)
157 {
158 if (m_views.contains(view)) {
159 return;
160 }
161
162 m_views[view] = new TrackedViewInfo(this, view);
163
164 updateScreenGeometries();
165
166 //! Consider Layouts
167 addRelevantLayout(view);
168
169 connect(view, &Latte::View::layoutChanged, this, [&, view]() {
170 addRelevantLayout(view);
171 });
172
173 connect(view, &Latte::View::screenGeometryChanged, this, &Windows::updateScreenGeometries);
174
175 connect(view, &Latte::View::isTouchingBottomViewAndIsBusyChanged, this, &Windows::updateExtraViewHints);
176 connect(view, &Latte::View::isTouchingTopViewAndIsBusyChanged, this, &Windows::updateExtraViewHints);
177 connect(view, &Latte::View::absoluteGeometryChanged, this, &Windows::updateAllHintsAfterTimer);
178
179 updateAllHints();
180
181 emit informationAnnounced(view);
182 }
183
removeView(Latte::View * view)184 void Windows::removeView(Latte::View *view)
185 {
186 if (!m_views.contains(view)) {
187 return;
188 }
189
190 m_views[view]->deleteLater();
191 m_views.remove(view);
192
193 updateRelevantLayouts();
194 }
195
addRelevantLayout(Latte::View * view)196 void Windows::addRelevantLayout(Latte::View *view)
197 {
198 if (view->layout()) {
199 bool initializing {false};
200
201 if (!m_layouts.contains(view->layout())) {
202 initializing = true;
203 m_layouts[view->layout()] = new TrackedLayoutInfo(this, view->layout());
204 }
205
206 //! Update always the AllScreens tracking because there is a chance a view delayed to be assigned in a layout
207 //! and that could create a state the AllScreens tracking will be disabled if there is a View requesting
208 //! tracking and one that it does not during startup
209 updateRelevantLayouts();
210
211 if (initializing) {
212 updateHints(view->layout());
213 emit informationAnnouncedForLayout(view->layout());
214 }
215 }
216 }
217
updateRelevantLayouts()218 void Windows::updateRelevantLayouts()
219 {
220 QList<Latte::Layout::GenericLayout*> orphanedLayouts;
221
222 //! REMOVE Orphaned Relevant layouts that have been removed or they don't contain any Views anymore
223 for (QHash<Latte::Layout::GenericLayout *, TrackedLayoutInfo *>::iterator i=m_layouts.begin(); i!=m_layouts.end(); ++i) {
224 bool hasView{false};
225 for (QHash<Latte::View *, TrackedViewInfo *>::iterator j=m_views.begin(); j!=m_views.end(); ++j) {
226 if (j.key() && i.key() && i.key() == j.key()->layout()) {
227 hasView = true;
228 break;
229 }
230 }
231
232 if (!hasView) {
233 if (i.value()) {
234 i.value()->deleteLater();
235 }
236 orphanedLayouts << i.key();
237 }
238 }
239
240 for(const auto &layout : orphanedLayouts) {
241 m_layouts.remove(layout);
242 }
243
244 //! UPDATE Enabled layout window tracking based on the Views that are requesting windows tracking
245 for (QHash<Latte::Layout::GenericLayout *, TrackedLayoutInfo *>::iterator i=m_layouts.begin(); i!=m_layouts.end(); ++i) {
246 bool hasViewEnabled{false};
247 for (QHash<Latte::View *, TrackedViewInfo *>::iterator j=m_views.begin(); j!=m_views.end(); ++j) {
248 if (i.key() == j.key()->layout() && j.value()->enabled()) {
249 hasViewEnabled = true;
250 break;
251 }
252 }
253
254 if (i.value()) {
255 i.value()->setEnabled(hasViewEnabled);
256
257 if (!hasViewEnabled) {
258 initLayoutHints(i.key());
259 }
260 }
261 }
262 }
263
264 //! Views Properties And Hints
265
enabled(Latte::View * view)266 bool Windows::enabled(Latte::View *view)
267 {
268 if (!m_views.contains(view)) {
269 return false;
270 }
271
272 return m_views[view]->enabled();
273 }
274
setEnabled(Latte::View * view,const bool enabled)275 void Windows::setEnabled(Latte::View *view, const bool enabled)
276 {
277 if (!m_views.contains(view) || m_views[view]->enabled() == enabled) {
278 return;
279 }
280
281 m_views[view]->setEnabled(enabled);
282
283 if (enabled) {
284 updateHints(view);
285 } else {
286 initViewHints(view);
287 }
288
289 updateRelevantLayouts();
290
291 emit enabledChanged(view);
292 }
293
activeWindowMaximized(Latte::View * view) const294 bool Windows::activeWindowMaximized(Latte::View *view) const
295 {
296 if (!m_views.contains(view)) {
297 return false;
298 }
299
300 return m_views[view]->activeWindowMaximized();
301 }
302
setActiveWindowMaximized(Latte::View * view,bool activeMaximized)303 void Windows::setActiveWindowMaximized(Latte::View *view, bool activeMaximized)
304 {
305 if (!m_views.contains(view) || m_views[view]->activeWindowMaximized() == activeMaximized) {
306 return;
307 }
308
309 m_views[view]->setActiveWindowMaximized(activeMaximized);
310 emit activeWindowMaximizedChanged(view);
311 }
312
activeWindowTouching(Latte::View * view) const313 bool Windows::activeWindowTouching(Latte::View *view) const
314 {
315 if (!m_views.contains(view)) {
316 return false;
317 }
318
319 return m_views[view]->activeWindowTouching();
320 }
321
setActiveWindowTouching(Latte::View * view,bool activeTouching)322 void Windows::setActiveWindowTouching(Latte::View *view, bool activeTouching)
323 {
324 if (!m_views.contains(view) || m_views[view]->activeWindowTouching() == activeTouching) {
325 return;
326 }
327
328 m_views[view]->setActiveWindowTouching(activeTouching);
329 emit activeWindowTouchingChanged(view);
330 }
331
activeWindowTouchingEdge(Latte::View * view) const332 bool Windows::activeWindowTouchingEdge(Latte::View *view) const
333 {
334 if (!m_views.contains(view)) {
335 return false;
336 }
337
338 return m_views[view]->activeWindowTouchingEdge();
339 }
340
setActiveWindowTouchingEdge(Latte::View * view,bool activeTouchingEdge)341 void Windows::setActiveWindowTouchingEdge(Latte::View *view, bool activeTouchingEdge)
342 {
343 if (!m_views.contains(view) || m_views[view]->activeWindowTouchingEdge() == activeTouchingEdge) {
344 return;
345 }
346
347 m_views[view]->setActiveWindowTouchingEdge(activeTouchingEdge);
348 emit activeWindowTouchingEdgeChanged(view);
349 }
350
existsWindowActive(Latte::View * view) const351 bool Windows::existsWindowActive(Latte::View *view) const
352 {
353 if (!m_views.contains(view)) {
354 return false;
355 }
356
357 return m_views[view]->existsWindowActive();
358 }
359
setExistsWindowActive(Latte::View * view,bool windowActive)360 void Windows::setExistsWindowActive(Latte::View *view, bool windowActive)
361 {
362 if (!m_views.contains(view) || m_views[view]->existsWindowActive() == windowActive) {
363 return;
364 }
365
366 m_views[view]->setExistsWindowActive(windowActive);
367 emit existsWindowActiveChanged(view);
368 }
369
existsWindowMaximized(Latte::View * view) const370 bool Windows::existsWindowMaximized(Latte::View *view) const
371 {
372 if (!m_views.contains(view)) {
373 return false;
374 }
375
376 return m_views[view]->existsWindowMaximized();
377 }
378
setExistsWindowMaximized(Latte::View * view,bool windowMaximized)379 void Windows::setExistsWindowMaximized(Latte::View *view, bool windowMaximized)
380 {
381 if (!m_views.contains(view) || m_views[view]->existsWindowMaximized() == windowMaximized) {
382 return;
383 }
384
385 m_views[view]->setExistsWindowMaximized(windowMaximized);
386 emit existsWindowMaximizedChanged(view);
387 }
388
existsWindowTouching(Latte::View * view) const389 bool Windows::existsWindowTouching(Latte::View *view) const
390 {
391 if (!m_views.contains(view)) {
392 return false;
393 }
394
395 return m_views[view]->existsWindowTouching();
396 }
397
setExistsWindowTouching(Latte::View * view,bool windowTouching)398 void Windows::setExistsWindowTouching(Latte::View *view, bool windowTouching)
399 {
400 if (!m_views.contains(view) || m_views[view]->existsWindowTouching() == windowTouching) {
401 return;
402 }
403
404 m_views[view]->setExistsWindowTouching(windowTouching);
405 emit existsWindowTouchingChanged(view);
406 }
407
existsWindowTouchingEdge(Latte::View * view) const408 bool Windows::existsWindowTouchingEdge(Latte::View *view) const
409 {
410 if (!m_views.contains(view)) {
411 return false;
412 }
413
414 return m_views[view]->existsWindowTouchingEdge();
415 }
416
setExistsWindowTouchingEdge(Latte::View * view,bool windowTouchingEdge)417 void Windows::setExistsWindowTouchingEdge(Latte::View *view, bool windowTouchingEdge)
418 {
419 if (!m_views.contains(view) || m_views[view]->existsWindowTouchingEdge() == windowTouchingEdge) {
420 return;
421 }
422
423 m_views[view]->setExistsWindowTouchingEdge(windowTouchingEdge);
424 emit existsWindowTouchingEdgeChanged(view);
425 }
426
427
isTouchingBusyVerticalView(Latte::View * view) const428 bool Windows::isTouchingBusyVerticalView(Latte::View *view) const
429 {
430 if (!m_views.contains(view)) {
431 return false;
432 }
433
434 return m_views[view]->isTouchingBusyVerticalView();
435 }
436
setIsTouchingBusyVerticalView(Latte::View * view,bool viewTouching)437 void Windows::setIsTouchingBusyVerticalView(Latte::View *view, bool viewTouching)
438 {
439 if (!m_views.contains(view) || m_views[view]->isTouchingBusyVerticalView() == viewTouching) {
440 return;
441 }
442
443 m_views[view]->setIsTouchingBusyVerticalView(viewTouching);
444 emit isTouchingBusyVerticalViewChanged(view);
445 }
446
activeWindowScheme(Latte::View * view) const447 SchemeColors *Windows::activeWindowScheme(Latte::View *view) const
448 {
449 if (!m_views.contains(view)) {
450 return nullptr;
451 }
452
453 return m_views[view]->activeWindowScheme();
454 }
455
setActiveWindowScheme(Latte::View * view,WindowSystem::SchemeColors * scheme)456 void Windows::setActiveWindowScheme(Latte::View *view, WindowSystem::SchemeColors *scheme)
457 {
458 if (!m_views.contains(view) || m_views[view]->activeWindowScheme() == scheme) {
459 return;
460 }
461
462 m_views[view]->setActiveWindowScheme(scheme);
463 emit activeWindowSchemeChanged(view);
464 }
465
touchingWindowScheme(Latte::View * view) const466 SchemeColors *Windows::touchingWindowScheme(Latte::View *view) const
467 {
468 if (!m_views.contains(view)) {
469 return nullptr;
470 }
471
472 return m_views[view]->touchingWindowScheme();
473 }
474
setTouchingWindowScheme(Latte::View * view,WindowSystem::SchemeColors * scheme)475 void Windows::setTouchingWindowScheme(Latte::View *view, WindowSystem::SchemeColors *scheme)
476 {
477 if (!m_views.contains(view) || m_views[view]->touchingWindowScheme() == scheme) {
478 return;
479 }
480
481 m_views[view]->setTouchingWindowScheme(scheme);
482 emit touchingWindowSchemeChanged(view);
483 }
484
lastActiveWindow(Latte::View * view)485 LastActiveWindow *Windows::lastActiveWindow(Latte::View *view)
486 {
487 if (!m_views.contains(view)) {
488 return nullptr;
489 }
490
491 return m_views[view]->lastActiveWindow();
492 }
493
494 //! Layouts
enabled(Latte::Layout::GenericLayout * layout)495 bool Windows::enabled(Latte::Layout::GenericLayout *layout)
496 {
497 if (!m_layouts.contains(layout)) {
498 return false;
499 }
500
501 return m_layouts[layout]->enabled();
502 }
503
activeWindowMaximized(Latte::Layout::GenericLayout * layout) const504 bool Windows::activeWindowMaximized(Latte::Layout::GenericLayout *layout) const
505 {
506 if (!m_layouts.contains(layout)) {
507 return false;
508 }
509
510 return m_layouts[layout]->activeWindowMaximized();
511 }
512
setActiveWindowMaximized(Latte::Layout::GenericLayout * layout,bool activeMaximized)513 void Windows::setActiveWindowMaximized(Latte::Layout::GenericLayout *layout, bool activeMaximized)
514 {
515 if (!m_layouts.contains(layout) || m_layouts[layout]->activeWindowMaximized() == activeMaximized) {
516 return;
517 }
518
519 m_layouts[layout]->setActiveWindowMaximized(activeMaximized);
520 emit activeWindowMaximizedChangedForLayout(layout);
521 }
522
existsWindowActive(Latte::Layout::GenericLayout * layout) const523 bool Windows::existsWindowActive(Latte::Layout::GenericLayout *layout) const
524 {
525 if (!m_layouts.contains(layout)) {
526 return false;
527 }
528
529 return m_layouts[layout]->existsWindowActive();
530 }
531
setExistsWindowActive(Latte::Layout::GenericLayout * layout,bool windowActive)532 void Windows::setExistsWindowActive(Latte::Layout::GenericLayout *layout, bool windowActive)
533 {
534 if (!m_layouts.contains(layout) || m_layouts[layout]->existsWindowActive() == windowActive) {
535 return;
536 }
537
538 m_layouts[layout]->setExistsWindowActive(windowActive);
539 emit existsWindowActiveChangedForLayout(layout);
540 }
541
existsWindowMaximized(Latte::Layout::GenericLayout * layout) const542 bool Windows::existsWindowMaximized(Latte::Layout::GenericLayout *layout) const
543 {
544 if (!m_layouts.contains(layout)) {
545 return false;
546 }
547
548 return m_layouts[layout]->existsWindowMaximized();
549 }
550
setExistsWindowMaximized(Latte::Layout::GenericLayout * layout,bool windowMaximized)551 void Windows::setExistsWindowMaximized(Latte::Layout::GenericLayout *layout, bool windowMaximized)
552 {
553 if (!m_layouts.contains(layout) || m_layouts[layout]->existsWindowMaximized() == windowMaximized) {
554 return;
555 }
556
557 m_layouts[layout]->setExistsWindowMaximized(windowMaximized);
558 emit existsWindowMaximizedChangedForLayout(layout);
559 }
560
activeWindowScheme(Latte::Layout::GenericLayout * layout) const561 SchemeColors *Windows::activeWindowScheme(Latte::Layout::GenericLayout *layout) const
562 {
563 if (!m_layouts.contains(layout)) {
564 return nullptr;
565 }
566
567 return m_layouts[layout]->activeWindowScheme();
568 }
569
setActiveWindowScheme(Latte::Layout::GenericLayout * layout,WindowSystem::SchemeColors * scheme)570 void Windows::setActiveWindowScheme(Latte::Layout::GenericLayout *layout, WindowSystem::SchemeColors *scheme)
571 {
572 if (!m_layouts.contains(layout) || m_layouts[layout]->activeWindowScheme() == scheme) {
573 return;
574 }
575
576 m_layouts[layout]->setActiveWindowScheme(scheme);
577 emit activeWindowSchemeChangedForLayout(layout);
578 }
579
lastActiveWindow(Latte::Layout::GenericLayout * layout)580 LastActiveWindow *Windows::lastActiveWindow(Latte::Layout::GenericLayout *layout)
581 {
582 if (!m_layouts.contains(layout)) {
583 return nullptr;
584 }
585
586 return m_layouts[layout]->lastActiveWindow();
587 }
588
589
590 //! Windows
isValidFor(const WindowId & wid) const591 bool Windows::isValidFor(const WindowId &wid) const
592 {
593 if (!m_windows.contains(wid)) {
594 return false;
595 }
596
597 return m_windows[wid].isValid();
598 }
599
iconFor(const WindowId & wid)600 QIcon Windows::iconFor(const WindowId &wid)
601 {
602 if (!m_windows.contains(wid)) {
603 return QIcon();
604 }
605
606 if (m_windows[wid].icon().isNull()) {
607 AppData data = m_wm->appDataFor(wid);
608
609 QIcon icon = data.icon;
610
611 if (icon.isNull()) {
612 icon = m_wm->iconFor(wid);
613 }
614
615 m_windows[wid].setIcon(icon);
616 return icon;
617 }
618
619 return m_windows[wid].icon();
620 }
621
appNameFor(const WindowId & wid)622 QString Windows::appNameFor(const WindowId &wid)
623 {
624 if (!m_windows.contains(wid)) {
625 return QString();
626 }
627
628 if(!m_initializedApplicationData.contains(wid) && !m_delayedApplicationData.contains(wid)) {
629 m_delayedApplicationData.append(wid);
630 m_updateApplicationDataTimer.start();
631 }
632
633 if (m_windows[wid].appName().isEmpty()) {
634 AppData data = m_wm->appDataFor(wid);
635
636 m_windows[wid].setAppName(data.name);
637
638 return data.name;
639 }
640
641 return m_windows[wid].appName();
642 }
643
updateApplicationData()644 void Windows::updateApplicationData()
645 {
646 if (m_delayedApplicationData.count() > 0) {
647 for(int i=0; i<m_delayedApplicationData.count(); ++i) {
648 auto wid = m_delayedApplicationData[i];
649
650 if (m_windows.contains(wid)) {
651 AppData data = m_wm->appDataFor(wid);
652
653 QIcon icon = data.icon;
654
655 if (icon.isNull()) {
656 icon = m_wm->iconFor(wid);
657 }
658
659 m_windows[wid].setIcon(icon);
660 m_windows[wid].setAppName(data.name);
661
662 m_initializedApplicationData.append(wid);
663
664 emit applicationDataChanged(wid);
665 }
666 }
667 }
668
669 m_delayedApplicationData.clear();
670 }
671
infoFor(const WindowId & wid) const672 WindowInfoWrap Windows::infoFor(const WindowId &wid) const
673 {
674 if (!m_windows.contains(wid)) {
675 return WindowInfoWrap();
676 }
677
678 return m_windows[wid];
679 }
680
681
682
683 //! Windows Criteria Functions
intersects(Latte::View * view,const WindowInfoWrap & winfo)684 bool Windows::intersects(Latte::View *view, const WindowInfoWrap &winfo)
685 {
686 return (!winfo.isMinimized() && !winfo.isShaded() && winfo.geometry().intersects(view->absoluteGeometry()));
687 }
688
isActive(const WindowInfoWrap & winfo)689 bool Windows::isActive(const WindowInfoWrap &winfo)
690 {
691 return (winfo.isValid() && winfo.isActive() && !winfo.isMinimized());
692 }
693
isActiveInViewScreen(Latte::View * view,const WindowInfoWrap & winfo)694 bool Windows::isActiveInViewScreen(Latte::View *view, const WindowInfoWrap &winfo)
695 {
696 auto screenGeometry = m_views[view]->screenGeometry();
697
698 if (KWindowSystem::isPlatformX11() && view->devicePixelRatio() != 1.0) {
699 //!Fix for X11 Global Scale, I dont think this could be pixel perfect accurate
700 auto factor = view->devicePixelRatio();
701 screenGeometry = QRect(qRound(screenGeometry.x() * factor),
702 qRound(screenGeometry.y() * factor),
703 qRound(screenGeometry.width() * factor),
704 qRound(screenGeometry.height() * factor));
705 }
706
707 return (winfo.isValid() && winfo.isActive() && !winfo.isMinimized()
708 && screenGeometry.contains(winfo.geometry().center()));
709 }
710
isMaximizedInViewScreen(Latte::View * view,const WindowInfoWrap & winfo)711 bool Windows::isMaximizedInViewScreen(Latte::View *view, const WindowInfoWrap &winfo)
712 {
713 auto screenGeometry = m_views[view]->screenGeometry();
714
715 if (KWindowSystem::isPlatformX11() && view->devicePixelRatio() != 1.0) {
716 //!Fix for X11 Global Scale, I dont think this could be pixel perfect accurate
717 auto factor = view->devicePixelRatio();
718 screenGeometry = QRect(qRound(screenGeometry.x() * factor),
719 qRound(screenGeometry.y() * factor),
720 qRound(screenGeometry.width() * factor),
721 qRound(screenGeometry.height() * factor));
722 }
723
724 //! updated implementation to identify the screen that the maximized window is present
725 //! in order to avoid: https://bugs.kde.org/show_bug.cgi?id=397700
726 return (winfo.isValid() && !winfo.isMinimized()
727 && !winfo.isShaded()
728 && winfo.isMaximized()
729 && screenGeometry.contains(winfo.geometry().center()));
730 }
731
isTouchingView(Latte::View * view,const WindowSystem::WindowInfoWrap & winfo)732 bool Windows::isTouchingView(Latte::View *view, const WindowSystem::WindowInfoWrap &winfo)
733 {
734 return (winfo.isValid() && intersects(view, winfo));
735 }
736
isTouchingViewEdge(Latte::View * view,const QRect & windowgeometry)737 bool Windows::isTouchingViewEdge(Latte::View *view, const QRect &windowgeometry)
738 {
739 if (!view) {
740 return false;
741 }
742
743 bool inViewThicknessEdge{false};
744 bool inViewLengthBoundaries{false};
745
746 QRect screenGeometry = view->screenGeometry();
747
748 if (KWindowSystem::isPlatformX11() && view->devicePixelRatio() != 1.0) {
749 //!Fix for X11 Global Scale, I dont think this could be pixel perfect accurate
750 auto factor = view->devicePixelRatio();
751 screenGeometry = QRect(qRound(screenGeometry.x() * factor),
752 qRound(screenGeometry.y() * factor),
753 qRound(screenGeometry.width() * factor),
754 qRound(screenGeometry.height() * factor));
755 }
756
757 bool inCurrentScreen{screenGeometry.contains(windowgeometry.topLeft()) || screenGeometry.contains(windowgeometry.bottomRight())};
758
759 if (inCurrentScreen) {
760 if (view->location() == Plasma::Types::TopEdge) {
761 inViewThicknessEdge = (windowgeometry.y() == view->absoluteGeometry().bottom() + 1);
762 } else if (view->location() == Plasma::Types::BottomEdge) {
763 inViewThicknessEdge = (windowgeometry.bottom() == view->absoluteGeometry().top() - 1);
764 } else if (view->location() == Plasma::Types::LeftEdge) {
765 inViewThicknessEdge = (windowgeometry.x() == view->absoluteGeometry().right() + 1);
766 } else if (view->location() == Plasma::Types::RightEdge) {
767 inViewThicknessEdge = (windowgeometry.right() == view->absoluteGeometry().left() - 1);
768 }
769
770 if (view->formFactor() == Plasma::Types::Horizontal) {
771 int yCenter = view->absoluteGeometry().center().y();
772
773 QPoint leftChecker(windowgeometry.left(), yCenter);
774 QPoint rightChecker(windowgeometry.right(), yCenter);
775
776 bool fulloverlap = (windowgeometry.left()<=view->absoluteGeometry().left()) && (windowgeometry.right()>=view->absoluteGeometry().right());
777
778 inViewLengthBoundaries = fulloverlap || view->absoluteGeometry().contains(leftChecker) || view->absoluteGeometry().contains(rightChecker);
779 } else if (view->formFactor() == Plasma::Types::Vertical) {
780 int xCenter = view->absoluteGeometry().center().x();
781
782 QPoint topChecker(xCenter, windowgeometry.top());
783 QPoint bottomChecker(xCenter, windowgeometry.bottom());
784
785 bool fulloverlap = (windowgeometry.top()<=view->absoluteGeometry().top()) && (windowgeometry.bottom()>=view->absoluteGeometry().bottom());
786
787 inViewLengthBoundaries = fulloverlap || view->absoluteGeometry().contains(topChecker) || view->absoluteGeometry().contains(bottomChecker);
788 }
789 }
790
791 return (inViewThicknessEdge && inViewLengthBoundaries);
792 }
793
isTouchingViewEdge(Latte::View * view,const WindowInfoWrap & winfo)794 bool Windows::isTouchingViewEdge(Latte::View *view, const WindowInfoWrap &winfo)
795 {
796 if (winfo.isValid() && !winfo.isMinimized()) {
797 return isTouchingViewEdge(view, winfo.geometry());
798 }
799
800 return false;
801 }
802
cleanupFaultyWindows()803 void Windows::cleanupFaultyWindows()
804 {
805 for (const auto &key : m_windows.keys()) {
806 auto winfo = m_windows[key];
807
808 //! garbage windows removing
809 if (winfo.wid()<=0 || winfo.geometry() == QRect(0, 0, 0, 0)) {
810 //qDebug() << "Faulty Geometry ::: " << winfo.wid();
811 m_windows.remove(key);
812 }
813 }
814 }
815
816
updateScreenGeometries()817 void Windows::updateScreenGeometries()
818 {
819 for (const auto view : m_views.keys()) {
820 if (m_views[view]->screenGeometry() != view->screenGeometry()) {
821 m_views[view]->setScreenGeometry(view->screenGeometry());
822
823 if (m_views[view]->enabled()) {
824 updateHints(view);
825 }
826 }
827 }
828 }
829
updateAllHintsAfterTimer()830 void Windows::updateAllHintsAfterTimer()
831 {
832 if (!m_updateAllHintsTimer.isActive()) {
833 updateAllHints();
834 m_updateAllHintsTimer.start();
835 }
836 }
837
updateAllHints()838 void Windows::updateAllHints()
839 {
840 for (const auto view : m_views.keys()) {
841 updateHints(view);
842 }
843
844 for (const auto layout : m_layouts.keys()) {
845 updateHints(layout);
846 }
847
848 if (!m_extraViewHintsTimer.isActive()) {
849 m_extraViewHintsTimer.start();
850 }
851 }
852
updateExtraViewHints()853 void Windows::updateExtraViewHints()
854 {
855 for (const auto horView : m_views.keys()) {
856 if (!m_views.contains(horView) || !m_views[horView]->enabled() || !m_views[horView]->isTrackingCurrentActivity()) {
857 continue;
858 }
859
860 if (horView->formFactor() == Plasma::Types::Horizontal) {
861 bool touchingBusyVerticalView{false};
862
863 for (const auto verView : m_views.keys()) {
864 if (!m_views.contains(verView) || !m_views[verView]->enabled() || !m_views[verView]->isTrackingCurrentActivity()) {
865 continue;
866 }
867
868 bool sameScreen = (verView->positioner()->currentScreenId() == horView->positioner()->currentScreenId());
869
870 if (verView->formFactor() == Plasma::Types::Vertical && sameScreen) {
871 bool hasEdgeTouch = isTouchingViewEdge(horView, verView->absoluteGeometry());
872
873 bool topTouch = horView->location() == Plasma::Types::TopEdge && verView->isTouchingTopViewAndIsBusy() && hasEdgeTouch;
874 bool bottomTouch = horView->location() == Plasma::Types::BottomEdge && verView->isTouchingBottomViewAndIsBusy() && hasEdgeTouch;
875
876 if (topTouch || bottomTouch) {
877 touchingBusyVerticalView = true;
878 break;
879 }
880 }
881 }
882
883 //qDebug() << " Touching Busy Vertical View :: " << horView->location() << " - " << horView->positioner()->currentScreenId() << " :: " << touchingBusyVerticalView;
884
885 setIsTouchingBusyVerticalView(horView, touchingBusyVerticalView);
886 }
887 }
888 }
889
updateHints(Latte::View * view)890 void Windows::updateHints(Latte::View *view)
891 {
892 if (!m_views.contains(view) || !m_views[view]->enabled() || !m_views[view]->isTrackingCurrentActivity()) {
893 return;
894 }
895
896 bool foundActive{false};
897 bool foundActiveInCurScreen{false};
898 bool foundActiveTouchInCurScreen{false};
899 bool foundActiveEdgeTouchInCurScreen{false};
900 bool foundTouchInCurScreen{false};
901 bool foundTouchEdgeInCurScreen{false};
902 bool foundMaximizedInCurScreen{false};
903
904 bool foundActiveGroupTouchInCurScreen{false};
905
906 //! the notification window is not sending a remove signal and creates windows of geometry (0x0 0,0),
907 //! maybe a garbage collector here is a good idea!!!
908 bool existsFaultyWindow{false};
909
910 WindowId maxWinId;
911 WindowId activeWinId;
912 WindowId touchWinId;
913 WindowId touchEdgeWinId;
914 WindowId activeTouchWinId;
915 WindowId activeTouchEdgeWinId;
916
917 //qDebug() << " -- TRACKING REPORT (SCREEN)--";
918
919 //! First Pass
920 for (const auto &winfo : m_windows) {
921 if (m_wm->isShowingDesktop()) {
922 break;
923 }
924
925 if (!existsFaultyWindow && (winfo.wid()<=0 || winfo.geometry() == QRect(0, 0, 0, 0))) {
926 existsFaultyWindow = true;
927 }
928
929 if ( !m_wm->inCurrentDesktopActivity(winfo)
930 || m_wm->hasBlockedTracking(winfo.wid())
931 || winfo.isMinimized()) {
932 continue;
933 }
934
935 //qDebug() << " _ _ _ ";
936 //qDebug() << "TRACKING | WINDOW INFO :: " << winfo.wid() << " _ " << winfo.appName() << " _ " << winfo.geometry() << " _ " << winfo.display();
937
938 if (isActive(winfo)) {
939 foundActive = true;
940 }
941
942 if (isActiveInViewScreen(view, winfo)) {
943 foundActiveInCurScreen = true;
944 activeWinId = winfo.wid();
945 }
946
947 //! Maximized windows flags
948 if ((winfo.isActive() && isMaximizedInViewScreen(view, winfo)) //! active maximized windows have higher priority than the rest maximized windows
949 || (!foundMaximizedInCurScreen && isMaximizedInViewScreen(view, winfo))) {
950 foundMaximizedInCurScreen = true;
951 maxWinId = winfo.wid();
952 }
953
954 //! Touching windows flags
955
956 bool touchingViewEdge = isTouchingViewEdge(view, winfo);
957 bool touchingView = isTouchingView(view, winfo);
958
959 if (touchingView) {
960 if (winfo.isActive()) {
961 foundActiveTouchInCurScreen = true;
962 activeTouchWinId = winfo.wid();
963 } else {
964 foundTouchInCurScreen = true;
965 touchWinId = winfo.wid();
966 }
967 }
968
969 if (touchingViewEdge) {
970 if (winfo.isActive()) {
971 foundActiveEdgeTouchInCurScreen = true;
972 activeTouchEdgeWinId = winfo.wid();
973 } else {
974 foundTouchEdgeInCurScreen = true;
975 touchEdgeWinId = winfo.wid();
976 }
977 }
978
979 //qDebug() << "TRACKING | ACTIVE:"<< foundActive << " ACT_TOUCH_CUR_SCR:" << foundActiveTouchInCurScreen << " MAXIM:"<<foundMaximizedInCurScreen;
980 //qDebug() << "TRACKING | TOUCHING VIEW EDGE:"<< touchingViewEdge << " TOUCHING VIEW:" << foundTouchInCurScreen;
981 }
982
983 if (existsFaultyWindow) {
984 cleanupFaultyWindows();
985 }
986
987 //! PASS 2
988 if (!m_wm->isShowingDesktop() && foundActiveInCurScreen && !foundActiveTouchInCurScreen) {
989 //! Second Pass to track also Child windows if needed
990
991 //qDebug() << "Windows Array...";
992 //for (const auto &winfo : m_windows) {
993 // qDebug() << " - " << winfo.wid() << " - " << winfo.isValid() << " - " << winfo.display() << " - " << winfo.geometry() << " parent : " << winfo.parentId();
994 //}
995 //qDebug() << " - - - - - ";
996
997 WindowInfoWrap activeInfo = m_windows[activeWinId];
998 WindowId mainWindowId = activeInfo.isChildWindow() ? activeInfo.parentId() : activeWinId;
999
1000 for (const auto &winfo : m_windows) {
1001 if (!m_wm->inCurrentDesktopActivity(winfo)
1002 || m_wm->hasBlockedTracking(winfo.wid())
1003 || winfo.isMinimized()) {
1004 continue;
1005 }
1006
1007 bool inActiveGroup = (winfo.wid() == mainWindowId || winfo.parentId() == mainWindowId);
1008
1009 //! consider only windows that belong to active window group meaning the main window
1010 //! and its children
1011 if (!inActiveGroup) {
1012 continue;
1013 }
1014
1015 if (isTouchingView(view, winfo)) {
1016 foundActiveGroupTouchInCurScreen = true;
1017 break;
1018 }
1019 }
1020 }
1021
1022
1023 //! HACK: KWin Effects such as ShowDesktop have no way to be identified and as such
1024 //! create issues with identifying properly touching and maximized windows. BUT when
1025 //! they are enabled then NO ACTIVE window is found. This is a way to identify these
1026 //! effects trigerring and disable the touch flags.
1027 //! BUG: 404483
1028 //! Disabled because it has fault identifications, e.g. when a window is maximized and
1029 //! Latte or Plasma are showing their View settings
1030 //foundMaximizedInCurScreen = foundMaximizedInCurScreen && foundActive;
1031 //foundTouchInCurScreen = foundTouchInCurScreen && foundActive;
1032
1033 //! assign flags
1034 setExistsWindowActive(view, foundActiveInCurScreen);
1035 setActiveWindowTouching(view, foundActiveTouchInCurScreen || foundActiveGroupTouchInCurScreen);
1036 setActiveWindowTouchingEdge(view, foundActiveEdgeTouchInCurScreen);
1037 setActiveWindowMaximized(view, (maxWinId.toInt()>0 && (maxWinId == activeTouchWinId || maxWinId == activeTouchEdgeWinId)));
1038 setExistsWindowMaximized(view, foundMaximizedInCurScreen);
1039 setExistsWindowTouching(view, (foundTouchInCurScreen || foundActiveTouchInCurScreen || foundActiveGroupTouchInCurScreen));
1040 setExistsWindowTouchingEdge(view, (foundActiveEdgeTouchInCurScreen || foundTouchEdgeInCurScreen));
1041
1042 //! update color schemes for active and touching windows
1043 setActiveWindowScheme(view, (foundActiveInCurScreen ? m_wm->schemesTracker()->schemeForWindow(activeWinId) : nullptr));
1044
1045 if (foundActiveTouchInCurScreen) {
1046 setTouchingWindowScheme(view, m_wm->schemesTracker()->schemeForWindow(activeTouchWinId));
1047 } else if (foundActiveEdgeTouchInCurScreen) {
1048 setTouchingWindowScheme(view, m_wm->schemesTracker()->schemeForWindow(activeTouchEdgeWinId));
1049 } else if (foundMaximizedInCurScreen) {
1050 setTouchingWindowScheme(view, m_wm->schemesTracker()->schemeForWindow(maxWinId));
1051 } else if (foundTouchInCurScreen) {
1052 setTouchingWindowScheme(view, m_wm->schemesTracker()->schemeForWindow(touchWinId));
1053 } else if (foundTouchEdgeInCurScreen) {
1054 setTouchingWindowScheme(view, m_wm->schemesTracker()->schemeForWindow(touchEdgeWinId));
1055 } else {
1056 setTouchingWindowScheme(view, nullptr);
1057 }
1058
1059 //! update LastActiveWindow
1060 if (foundActiveInCurScreen) {
1061 m_views[view]->setActiveWindow(activeWinId);
1062 }
1063
1064 //! Debug
1065 //qDebug() << "TRACKING | _________ FINAL RESULTS ________";
1066 //qDebug() << "TRACKING | SCREEN: " << view->positioner()->currentScreenId() << " , EDGE:" << view->location() << " , ENABLED:" << enabled(view);
1067 //qDebug() << "TRACKING | activeWindowTouching: " << foundActiveTouchInCurScreen << " ,activeWindowMaximized: " << activeWindowMaximized(view);
1068 //qDebug() << "TRACKING | existsWindowActive: " << foundActiveInCurScreen << " , existsWindowMaximized:" << existsWindowMaximized(view)
1069 // << " , existsWindowTouching:"<<existsWindowTouching(view);
1070 //qDebug() << "TRACKING | activeEdgeWindowTouch: " << activeWindowTouchingEdge(view) << " , existsEdgeWindowTouch:" << existsWindowTouchingEdge(view);
1071 //qDebug() << "TRACKING | existsActiveGroupTouching: " << foundActiveGroupTouchInCurScreen;
1072 }
1073
updateHints(Latte::Layout::GenericLayout * layout)1074 void Windows::updateHints(Latte::Layout::GenericLayout *layout) {
1075 if (!m_layouts.contains(layout) || !m_layouts[layout]->enabled() || !m_layouts[layout]->isTrackingCurrentActivity()) {
1076 return;
1077 }
1078
1079 bool foundActive{false};
1080 bool foundActiveMaximized{false};
1081 bool foundMaximized{false};
1082
1083 //! the notification window is not sending a remove signal and creates windows of geometry (0x0 0,0),
1084 //! maybe a garbage collector here is a good idea!!!
1085 bool existsFaultyWindow{false};
1086
1087 WindowId activeWinId;
1088 WindowId maxWinId;
1089
1090 for (const auto &winfo : m_windows) {
1091 if (m_wm->isShowingDesktop()) {
1092 break;
1093 }
1094
1095 if (!existsFaultyWindow && (winfo.wid()<=0 || winfo.geometry() == QRect(0, 0, 0, 0))) {
1096 existsFaultyWindow = true;
1097 }
1098
1099 if (!m_wm->inCurrentDesktopActivity(winfo)
1100 || m_wm->hasBlockedTracking(winfo.wid())
1101 || winfo.isMinimized()) {
1102 continue;
1103 }
1104
1105 if (isActive(winfo)) {
1106 foundActive = true;
1107 activeWinId = winfo.wid();
1108
1109 if (winfo.isMaximized() && !winfo.isMinimized()) {
1110 foundActiveMaximized = true;
1111 maxWinId = winfo.wid();
1112 }
1113 }
1114
1115 if (!foundActiveMaximized && winfo.isMaximized() && !winfo.isMinimized()) {
1116 foundMaximized = true;
1117 maxWinId = winfo.wid();
1118 }
1119
1120 //qDebug() << "window geometry ::: " << winfo.geometry();
1121 }
1122
1123 if (existsFaultyWindow) {
1124 cleanupFaultyWindows();
1125 }
1126
1127 //! HACK: KWin Effects such as ShowDesktop have no way to be identified and as such
1128 //! create issues with identifying properly touching and maximized windows. BUT when
1129 //! they are enabled then NO ACTIVE window is found. This is a way to identify these
1130 //! effects trigerring and disable the touch flags.
1131 //! BUG: 404483
1132 //! Disabled because it has fault identifications, e.g. when a window is maximized and
1133 //! Latte or Plasma are showing their View settings
1134 //foundMaximizedInCurScreen = foundMaximizedInCurScreen && foundActive;
1135 //foundTouchInCurScreen = foundTouchInCurScreen && foundActive;
1136
1137 //! assign flags
1138 setExistsWindowActive(layout, foundActive);
1139 setActiveWindowMaximized(layout, foundActiveMaximized);
1140 setExistsWindowMaximized(layout, foundActiveMaximized || foundMaximized);
1141
1142 //! update color schemes for active and touching windows
1143 setActiveWindowScheme(layout, (foundActive ? m_wm->schemesTracker()->schemeForWindow(activeWinId) : nullptr));
1144
1145 //! update LastActiveWindow
1146 if (foundActive) {
1147 m_layouts[layout]->setActiveWindow(activeWinId);
1148 }
1149
1150 //! Debug
1151 //qDebug() << " -- TRACKING REPORT (LAYOUT) --";
1152 //qDebug() << "TRACKING | LAYOUT: " << layout->name() << " , ENABLED:" << enabled(layout);
1153 //qDebug() << "TRACKING | existsActiveWindow: " << foundActive << " ,activeWindowMaximized: " << foundActiveMaximized;
1154 //qDebug() << "TRACKING | existsWindowMaximized: " << existsWindowMaximized(layout);
1155 }
1156
1157 }
1158 }
1159 }
1160