/* * Copyright 2019 Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "windowstracker.h" // local #include "positioner.h" #include "view.h" #include "../lattecorona.h" #include "../../liblatte2/types.h" namespace Latte { namespace ViewPart { WindowsTracker::WindowsTracker(Latte::View *parent) : QObject(parent), m_latteView(parent) { qDebug() << "WindowsTracker creating..."; m_corona = qobject_cast(m_latteView->corona()); m_wm = m_corona->wm(); } WindowsTracker::~WindowsTracker() { qDebug() << "WindowsTracker removing..."; } bool WindowsTracker::existsWindowMaximized() const { return m_windowIsMaximizedFlag; } void WindowsTracker::setExistsWindowMaximized(bool windowMaximized) { if (m_windowIsMaximizedFlag == windowMaximized) { return; } m_windowIsMaximizedFlag = windowMaximized; emit existsWindowMaximizedChanged(); } bool WindowsTracker::existsWindowTouching() const { return m_windowIsTouchingFlag; } void WindowsTracker::setExistsWindowTouching(bool windowTouching) { if (m_windowIsTouchingFlag == windowTouching) { return; } m_windowIsTouchingFlag = windowTouching; emit existsWindowTouchingChanged(); } SchemeColors *WindowsTracker::touchingWindowScheme() const { return m_touchingScheme; } void WindowsTracker::setTouchingWindowScheme(SchemeColors *scheme) { if (m_touchingScheme == scheme) { return; } m_touchingScheme = scheme; emit touchingWindowSchemeChanged(); } bool WindowsTracker::enabled() const { return m_enabled; } void WindowsTracker::setEnabled(bool active) { if (m_enabled == active) { return; } m_enabled = active; if (m_enabled) { m_windows.clear(); for (const auto &wid : m_wm->windows()) { m_windows.insert(wid, m_wm->requestInfo(wid)); } m_connections[0] = connect(m_corona, &Plasma::Corona::availableScreenRectChanged, this, &WindowsTracker::updateAvailableScreenGeometry); m_connections[1] = connect(m_wm, &WindowSystem::windowChanged, this, [&](WindowId wid) { m_windows[wid] = m_wm->requestInfo(wid); updateFlags(); }); m_connections[2] = connect(m_wm, &WindowSystem::windowRemoved, this, [&](WindowId wid) { m_windows.remove(wid); }); m_connections[3] = connect(m_wm, &WindowSystem::windowAdded, this, [&](WindowId wid) { m_windows.insert(wid, m_wm->requestInfo(wid)); updateFlags(); }); m_connections[4] = connect(m_wm, &WindowSystem::activeWindowChanged, this, [&](WindowId wid) { if (m_windows.contains(m_lastActiveWindowWid)) { m_windows[m_lastActiveWindowWid] = m_wm->requestInfo(m_lastActiveWindowWid); } m_windows[wid] = m_wm->requestInfo(wid); m_lastActiveWindowWid = wid; updateFlags(); }); m_connections[5] = connect(m_wm, &WindowSystem::currentDesktopChanged, this, [&] { updateFlags(); }); m_connections[6] = connect(m_wm, &WindowSystem::currentActivityChanged, this, [&] { updateFlags(); }); updateAvailableScreenGeometry(); updateFlags(); } else { // clear mode for (auto &c : m_connections) { disconnect(c); } m_windows.clear(); } emit enabledChanged(); } void WindowsTracker::updateAvailableScreenGeometry() { if (!m_latteView || !m_latteView->containment()) { return; } int currentScrId = m_latteView->positioner()->currentScreenId(); QRect tempAvailableScreenGeometry = m_corona->availableScreenRectWithCriteria(currentScrId, {Types::AlwaysVisible}, {}); if (tempAvailableScreenGeometry != m_availableScreenGeometry) { m_availableScreenGeometry = tempAvailableScreenGeometry; updateFlags(); } } void WindowsTracker::updateFlags() { bool foundTouch{false}; bool foundMaximized{false}; //! the notification window is not sending a remove signal and creates windows of geometry (0x0 0,0), //! maybe a garbage collector here is a good idea!!! bool existsFaultyWindow{false}; WindowId maxWinId; WindowId touchWinId; for (const auto &winfo : m_windows) { if (isMaximizedInCurrentScreen(winfo)) { foundMaximized = true; maxWinId = winfo.wid(); } if (winfo.isActive() && (isTouchingPanelEdge(winfo) || (m_latteView->visibility()->intersects(winfo)))) { foundTouch = true; touchWinId = winfo.wid(); } if (!existsFaultyWindow && winfo.geometry() == QRect(0, 0, 0, 0)) { existsFaultyWindow = true; } //qDebug() << "window geometry ::: " << winfo.geometry(); } if (existsFaultyWindow) { cleanupFaultyWindows(); } setExistsWindowMaximized(foundMaximized); setExistsWindowTouching(foundTouch); //! update color scheme for touching window if (foundTouch) { //! first the touching one because that would mean it is active setTouchingWindowScheme(m_wm->schemeForWindow(touchWinId)); } else if (foundMaximized) { setTouchingWindowScheme(m_wm->schemeForWindow(maxWinId)); } else { setTouchingWindowScheme(nullptr); } } bool WindowsTracker::isMaximizedInCurrentScreen(const WindowInfoWrap &winfo) { //! updated implementation to identify the screen that the maximized window is present //! in order to avoid: https://bugs.kde.org/show_bug.cgi?id=397700 if (winfo.isValid() && !winfo.isMinimized() && m_wm->isOnCurrentDesktop(winfo.wid()) && m_wm->isOnCurrentActivity(winfo.wid())) { if (winfo.isMaximized() && m_availableScreenGeometry.contains(winfo.geometry().center())) { return true; } } return false; } bool WindowsTracker::isTouchingPanelEdge(const WindowInfoWrap &winfo) { if (winfo.isValid() && !winfo.isMinimized() && m_wm->isOnCurrentDesktop(winfo.wid()) && m_wm->isOnCurrentActivity(winfo.wid())) { bool touchingPanelEdge{false}; QRect screenGeometry = m_latteView->screenGeometry(); bool inCurrentScreen{screenGeometry.contains(winfo.geometry().topLeft()) || screenGeometry.contains(winfo.geometry().bottomRight())}; if (inCurrentScreen) { if (m_latteView->location() == Plasma::Types::TopEdge) { touchingPanelEdge = (winfo.geometry().y() == m_availableScreenGeometry.y()); } else if (m_latteView->location() == Plasma::Types::BottomEdge) { touchingPanelEdge = (winfo.geometry().bottom() == m_availableScreenGeometry.bottom()); } else if (m_latteView->location() == Plasma::Types::LeftEdge) { touchingPanelEdge = (winfo.geometry().x() == m_availableScreenGeometry.x()); } else if (m_latteView->location() == Plasma::Types::RightEdge) { touchingPanelEdge = (winfo.geometry().right() == m_availableScreenGeometry.right()); } } return touchingPanelEdge; } return false; } void WindowsTracker::cleanupFaultyWindows() { foreach (auto key, m_windows.keys()) { auto winfo = m_windows[key]; //! garbage windows removing if (winfo.geometry() == QRect(0, 0, 0, 0)) { //qDebug() << "Faulty Geometry ::: " << winfo.wid(); m_windows.remove(key); } } } //! Window Functions void WindowsTracker::setWindowOnActivities(QWindow &window, const QStringList &activities) { m_wm->setWindowOnActivities(window, activities); } void WindowsTracker::requestToggleMaximizeForActiveWindow() { WindowInfoWrap actInfo = m_wm->requestInfoActive(); //active window can be toggled only when it is in the same screen if (actInfo.isValid() && !actInfo.geometry().isNull() && m_latteView->screenGeometry().contains(actInfo.geometry().center())) { m_wm->requestToggleMaximized(actInfo.wid()); } } void WindowsTracker::requestMoveActiveWindow(int localX, int localY) { WindowInfoWrap actInfo = m_wm->requestInfoActive(); //active window can be dragged only when it is in the same screen if (actInfo.isValid() && !actInfo.geometry().isNull() && m_latteView->screenGeometry().contains(actInfo.geometry().center())) { QPoint globalPoint{m_latteView->x() + localX, m_latteView->y() + localY}; m_wm->requestMoveWindow(actInfo.wid(), globalPoint); //! This timer is needed because otherwise the mouse position //! in the dragged window changes to TopLeft corner QTimer::singleShot(250, this, [&, actInfo, globalPoint]() { m_wm->releaseMouseEventFor(m_latteView->winId()); }); // setContainsMouse(false); // updateHiddenState(); } } bool WindowsTracker::activeWindowCanBeDragged() { WindowInfoWrap actInfo = m_wm->requestInfoActive(); //active window can be dragged only when it is in the same screen if (actInfo.isValid() && !actInfo.geometry().isNull() && m_latteView->screenGeometry().contains(actInfo.geometry().center())) { return m_wm->windowCanBeDragged(actInfo.wid()); } return false; } } }