diff --git a/app/lattecorona.cpp b/app/lattecorona.cpp index 6ad4cb61..370c89d1 100644 --- a/app/lattecorona.cpp +++ b/app/lattecorona.cpp @@ -1,1062 +1,1063 @@ /* * Copyright 2016 Smith AR * 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 "lattecorona.h" // local #include "alternativeshelper.h" #include "lattedockadaptor.h" #include "screenpool.h" #include "indicator/factory.h" #include "layout/centrallayout.h" #include "layout/genericlayout.h" #include "layout/sharedlayout.h" #include "layouts/importer.h" #include "layouts/manager.h" #include "layouts/synchronizer.h" #include "layouts/launcherssignals.h" #include "shortcuts/globalshortcuts.h" #include "package/lattepackage.h" #include "plasma/extended/screenpool.h" #include "plasma/extended/theme.h" #include "settings/universalsettings.h" #include "view/view.h" #include "wm/abstractwindowinterface.h" +#include "wm/schemestracker.h" #include "wm/waylandinterface.h" #include "wm/xwindowinterface.h" // Qt #include #include #include #include #include #include #include #include #include // Plasma #include #include #include #include // KDE #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Latte { Corona::Corona(bool defaultLayoutOnStartup, QString layoutNameOnStartUp, int userSetMemoryUsage, QObject *parent) : Plasma::Corona(parent), m_defaultLayoutOnStartup(defaultLayoutOnStartup), m_userSetMemoryUsage(userSetMemoryUsage), m_layoutNameOnStartUp(layoutNameOnStartUp), m_activityConsumer(new KActivities::Consumer(this)), m_screenPool(new ScreenPool(KSharedConfig::openConfig(), this)), m_indicatorFactory(new Indicator::Factory(this)), m_universalSettings(new UniversalSettings(KSharedConfig::openConfig(), this)), m_globalShortcuts(new GlobalShortcuts(this)), m_plasmaScreenPool(new PlasmaExtended::ScreenPool(this)), m_themeExtended(new PlasmaExtended::Theme(KSharedConfig::openConfig(), this)), m_layoutsManager(new Layouts::Manager(this)) { //! create the window manager if (KWindowSystem::isPlatformWayland()) { m_wm = new WindowSystem::WaylandInterface(this); } else { m_wm = new WindowSystem::XWindowInterface(this); } setupWaylandIntegration(); KPackage::Package package(new Latte::Package(this)); m_screenPool->load(); if (!package.isValid()) { qWarning() << staticMetaObject.className() << "the package" << package.metadata().rawData() << "is invalid!"; return; } else { qDebug() << staticMetaObject.className() << "the package" << package.metadata().rawData() << "is valid!"; } setKPackage(package); //! universal settings / extendedtheme must be loaded after the package has been set m_universalSettings->load(); m_themeExtended->load(); qmlRegisterTypes(); if (m_activityConsumer && (m_activityConsumer->serviceStatus() == KActivities::Consumer::Running)) { load(); } connect(m_activityConsumer, &KActivities::Consumer::serviceStatusChanged, this, &Corona::load); m_viewsScreenSyncTimer.setSingleShot(true); m_viewsScreenSyncTimer.setInterval(m_universalSettings->screenTrackerInterval()); connect(&m_viewsScreenSyncTimer, &QTimer::timeout, this, &Corona::syncLatteViewsToScreens); connect(m_universalSettings, &UniversalSettings::screenTrackerIntervalChanged, this, [this]() { m_viewsScreenSyncTimer.setInterval(m_universalSettings->screenTrackerInterval()); }); //! initialize the background tracer for broadcasted backgrounds m_backgroundTracer = new KDeclarative::QmlObjectSharedEngine(this); m_backgroundTracer->setInitializationDelayed(true); m_backgroundTracer->setSource(kPackage().filePath("backgroundTracer")); m_backgroundTracer->completeInitialization(); //! Dbus adaptor initialization new LatteDockAdaptor(this); QDBusConnection dbus = QDBusConnection::sessionBus(); dbus.registerObject(QStringLiteral("/Latte"), this); } Corona::~Corona() { //! BEGIN: Give the time to slide-out views when closing m_layoutsManager->synchronizer()->hideAllViews(); //! Don't delay the destruction under wayland in any case //! because it creates a crash with kwin effects //! https://bugs.kde.org/show_bug.cgi?id=392890 if (!KWindowSystem::isPlatformWayland()) { QTimer::singleShot(400, [this]() { m_quitTimedEnded = true; }); while (!m_quitTimedEnded) { QGuiApplication::processEvents(QEventLoop::AllEvents, 50); } } //! END: slide-out views when closing m_viewsScreenSyncTimer.stop(); if (m_layoutsManager->memoryUsage() == Types::SingleLayout) { cleanConfig(); } qDebug() << "Latte Corona - unload: containments ..."; m_layoutsManager->unload(); m_wm->deleteLater(); m_globalShortcuts->deleteLater(); m_layoutsManager->deleteLater(); m_screenPool->deleteLater(); m_universalSettings->deleteLater(); m_plasmaScreenPool->deleteLater(); m_backgroundTracer->deleteLater(); m_themeExtended->deleteLater(); m_indicatorFactory->deleteLater(); disconnect(m_activityConsumer, &KActivities::Consumer::serviceStatusChanged, this, &Corona::load); delete m_activityConsumer; qDebug() << "Latte Corona - deleted..."; } void Corona::load() { if (m_activityConsumer && (m_activityConsumer->serviceStatus() == KActivities::Consumer::Running) && m_activitiesStarting) { m_activitiesStarting = false; disconnect(m_activityConsumer, &KActivities::Consumer::serviceStatusChanged, this, &Corona::load); m_layoutsManager->load(); connect(this, &Corona::availableScreenRectChangedFrom, this, &Plasma::Corona::availableScreenRectChanged); connect(this, &Corona::availableScreenRegionChangedFrom, this, &Plasma::Corona::availableScreenRegionChanged); connect(qGuiApp, &QGuiApplication::primaryScreenChanged, this, &Corona::primaryOutputChanged, Qt::UniqueConnection); connect(QApplication::desktop(), &QDesktopWidget::screenCountChanged, this, &Corona::screenCountChanged); connect(m_screenPool, &ScreenPool::primaryPoolChanged, this, &Corona::screenCountChanged); QString assignedLayout = m_layoutsManager->synchronizer()->shouldSwitchToLayout(m_activityConsumer->currentActivity()); QString loadLayoutName = ""; if (!m_defaultLayoutOnStartup && m_layoutNameOnStartUp.isEmpty()) { if (!assignedLayout.isEmpty() && assignedLayout != m_universalSettings->currentLayoutName()) { loadLayoutName = assignedLayout; } else { loadLayoutName = m_universalSettings->currentLayoutName(); } if (!m_layoutsManager->synchronizer()->layoutExists(loadLayoutName)) { loadLayoutName = m_layoutsManager->defaultLayoutName(); m_layoutsManager->importDefaultLayout(false); } } else if (m_defaultLayoutOnStartup) { loadLayoutName = m_layoutsManager->importer()->uniqueLayoutName(m_layoutsManager->defaultLayoutName()); m_layoutsManager->importDefaultLayout(true); } else { loadLayoutName = m_layoutNameOnStartUp; } if (m_userSetMemoryUsage != -1 && !KWindowSystem::isPlatformWayland()) { Types::LayoutsMemoryUsage usage = static_cast(m_userSetMemoryUsage); m_universalSettings->setLayoutsMemoryUsage(usage); } if (KWindowSystem::isPlatformWayland()) { m_universalSettings->setLayoutsMemoryUsage(Types::SingleLayout); } m_layoutsManager->loadLayoutOnStartup(loadLayoutName); //! load screens signals such screenGeometryChanged in order to support //! plasmoid.screenGeometry properly for (QScreen *screen : qGuiApp->screens()) { addOutput(screen); } connect(qGuiApp, &QGuiApplication::screenAdded, this, &Corona::addOutput, Qt::UniqueConnection); } } void Corona::unload() { qDebug() << "unload: removing containments..."; while (!containments().isEmpty()) { //deleting a containment will remove it from the list due to QObject::destroyed connect in Corona //this form doesn't crash, while qDeleteAll(containments()) does delete containments().first(); } } void Corona::setupWaylandIntegration() { if (!KWindowSystem::isPlatformWayland()) { return; } using namespace KWayland::Client; auto connection = ConnectionThread::fromApplication(this); if (!connection) { return; } Registry *registry{new Registry(this)}; registry->create(connection); connect(registry, &Registry::plasmaShellAnnounced, this , [this, registry](quint32 name, quint32 version) { m_waylandCorona = registry->createPlasmaShell(name, version, this); }); QObject::connect(registry, &KWayland::Client::Registry::plasmaWindowManagementAnnounced, [this, registry](quint32 name, quint32 version) { KWayland::Client::PlasmaWindowManagement *pwm = registry->createPlasmaWindowManagement(name, version, this); WindowSystem::WaylandInterface *wI = qobject_cast(m_wm); if (wI) { wI->initWindowManagement(pwm); } }); registry->setup(); connection->roundtrip(); } KActivities::Consumer *Corona::activityConsumer() const { return m_activityConsumer; } KWayland::Client::PlasmaShell *Corona::waylandCoronaInterface() const { return m_waylandCorona; } void Corona::cleanConfig() { auto containmentsEntries = config()->group("Containments"); bool changed = false; for(const auto &cId : containmentsEntries.groupList()) { if (!containmentExists(cId.toUInt())) { //cleanup obsolete containments containmentsEntries.group(cId).deleteGroup(); changed = true; qDebug() << "obsolete containment configuration deleted:" << cId; } else { //cleanup obsolete applets of running containments auto appletsEntries = containmentsEntries.group(cId).group("Applets"); for(const auto &appletId : appletsEntries.groupList()) { if (!appletExists(cId.toUInt(), appletId.toUInt())) { appletsEntries.group(appletId).deleteGroup(); changed = true; qDebug() << "obsolete applet configuration deleted:" << appletId; } } } } if (changed) { config()->sync(); qDebug() << "configuration file cleaned..."; } } bool Corona::containmentExists(uint id) const { for(const auto containment : containments()) { if (id == containment->id()) { return true; } } return false; } bool Corona::appletExists(uint containmentId, uint appletId) const { Plasma::Containment *containment = nullptr; for(const auto cont : containments()) { if (containmentId == cont->id()) { containment = cont; break; } } if (!containment) { return false; } for(const auto applet : containment->applets()) { if (applet->id() == appletId) { return true; } } return false; } KActivities::Consumer *Corona::activitiesConsumer() const { return m_activityConsumer; } GlobalShortcuts *Corona::globalShortcuts() const { return m_globalShortcuts; } ScreenPool *Corona::screenPool() const { return m_screenPool; } UniversalSettings *Corona::universalSettings() const { return m_universalSettings; } WindowSystem::AbstractWindowInterface *Corona::wm() const { return m_wm; } Indicator::Factory *Corona::indicatorFactory() const { return m_indicatorFactory; } Layouts::Manager *Corona::layoutsManager() const { return m_layoutsManager; } PlasmaExtended::ScreenPool *Corona::plasmaScreenPool() const { return m_plasmaScreenPool; } PlasmaExtended::Theme *Corona::themeExtended() const { return m_themeExtended; } int Corona::numScreens() const { return qGuiApp->screens().count(); } QRect Corona::screenGeometry(int id) const { const auto screens = qGuiApp->screens(); const QScreen *screen{qGuiApp->primaryScreen()}; QString screenName; if (m_screenPool->hasId(id)) { screenName = m_screenPool->connector(id); } for(const auto scr : screens) { if (scr->name() == screenName) { screen = scr; break; } } return screen->geometry(); } QRegion Corona::availableScreenRegion(int id) const { return availableScreenRegionWithCriteria(id); } QRegion Corona::availableScreenRegionWithCriteria(int id, QString forLayout) const { const auto screens = qGuiApp->screens(); const QScreen *screen{qGuiApp->primaryScreen()}; QString screenName; if (m_screenPool->hasId(id)) { screenName = m_screenPool->connector(id); } for(auto scr : screens) { if (scr->name() == screenName) { screen = scr; break; } } if (!screen) return QRegion(); QList views; if (forLayout.isEmpty()) { Latte::CentralLayout *currentLayout = m_layoutsManager->currentLayout(); views = currentLayout->latteViews(); } else { Layout::GenericLayout *generic = m_layoutsManager->synchronizer()->centralLayout(forLayout); if (!generic) { //! Identify best active layout to be used for metrics calculations. //! Active layouts are always take into account their shared layouts for their metrics SharedLayout *sharedLayout = m_layoutsManager->synchronizer()->sharedLayout(forLayout); if (sharedLayout) { generic = sharedLayout->currentCentralLayout(); } } if (!generic) { generic = m_layoutsManager->currentLayout(); } views = generic->latteViews(); } QRegion available(screen->geometry()); for (const auto *view : views) { if (view && view->containment() && view->screen() == screen && view->visibility() && (view->visibility()->mode() != Latte::Types::AutoHide)) { int realThickness = view->normalThickness(); // Usually availableScreenRect is used by the desktop, // but Latte don't have desktop, then here just // need calculate available space for top and bottom location, // because the left and right are those who dodge others views switch (view->location()) { case Plasma::Types::TopEdge: if (view->behaveAsPlasmaPanel()) { available -= view->geometry(); } else { QRect realGeometry; int realWidth = view->maxLength() * view->width(); switch (view->alignment()) { case Latte::Types::Left: realGeometry = QRect(view->x(), view->y(), realWidth, realThickness); break; case Latte::Types::Center: case Latte::Types::Justify: realGeometry = QRect(qMax(view->geometry().x(), view->geometry().center().x() - realWidth / 2), view->y(), realWidth, realThickness); break; case Latte::Types::Right: realGeometry = QRect(view->geometry().right() - realWidth + 1, view->y(), realWidth, realThickness); break; } available -= realGeometry; } break; case Plasma::Types::BottomEdge: if (view->behaveAsPlasmaPanel()) { available -= view->geometry(); } else { QRect realGeometry; int realWidth = view->maxLength() * view->width(); int realY = view->geometry().bottom() - realThickness + 1; switch (view->alignment()) { case Latte::Types::Left: realGeometry = QRect(view->x(), realY, realWidth, realThickness); break; case Latte::Types::Center: case Latte::Types::Justify: realGeometry = QRect(qMax(view->geometry().x(), view->geometry().center().x() - realWidth / 2), realY, realWidth, realThickness); break; case Latte::Types::Right: realGeometry = QRect(view->geometry().right() - realWidth + 1, realY, realWidth, realThickness); break; } available -= realGeometry; } break; default: //! bypass clang warnings break; } } } /*qDebug() << "::::: FREE AREAS :::::"; for (int i = 0; i < available.rectCount(); ++i) { qDebug() << available.rects().at(i); } qDebug() << "::::: END OF FREE AREAS :::::";*/ return available; } QRect Corona::availableScreenRect(int id) const { return availableScreenRectWithCriteria(id); } QRect Corona::availableScreenRectWithCriteria(int id, QList modes, QList edges) const { const auto screens = qGuiApp->screens(); const QScreen *screen{qGuiApp->primaryScreen()}; if (m_screenPool->hasId(id)) { QString scrName = m_screenPool->connector(id); for(const auto scr : screens) { if (scr->name() == scrName) { screen = scr; break; } } } if (!screen) return {}; bool allModes = modes.isEmpty(); bool allEdges = edges.isEmpty(); auto available = screen->geometry(); Latte::CentralLayout *currentLayout = m_layoutsManager->currentLayout(); QList views; if (currentLayout) { views = currentLayout->latteViews(); } for (const auto *view : views) { if (view && view->containment() && view->screen() == screen && ((allEdges || edges.contains(view->location())) && (allModes || (view->visibility() && modes.contains(view->visibility()->mode()))))) { // Usually availableScreenRect is used by the desktop, // but Latte don't have desktop, then here just // need calculate available space for top and bottom location, // because the left and right are those who dodge others docks switch (view->location()) { case Plasma::Types::TopEdge: available.setTop(view->y() + view->normalThickness()); break; case Plasma::Types::BottomEdge: available.setBottom(view->y() + view->height() - view->normalThickness()); break; case Plasma::Types::LeftEdge: available.setLeft(view->x() + view->normalThickness()); break; case Plasma::Types::RightEdge: available.setRight(view->x() + view->width() - view->normalThickness()); break; default: //! bypass clang warnings break; } } } return available; } void Corona::addOutput(QScreen *screen) { Q_ASSERT(screen); int id = m_screenPool->id(screen->name()); if (id == -1) { int newId = m_screenPool->firstAvailableId(); m_screenPool->insertScreenMapping(newId, screen->name()); } connect(screen, &QScreen::geometryChanged, this, [ = ]() { const int id = m_screenPool->id(screen->name()); if (id >= 0) { emit screenGeometryChanged(id); emit availableScreenRegionChanged(); emit availableScreenRectChanged(); } }); emit availableScreenRectChanged(); emit screenAdded(m_screenPool->id(screen->name())); } void Corona::primaryOutputChanged() { m_viewsScreenSyncTimer.start(); } void Corona::screenRemoved(QScreen *screen) { Q_ASSERT(screen); } void Corona::screenCountChanged() { m_viewsScreenSyncTimer.start(); } //! the central functions that updates loading/unloading latteviews //! concerning screen changed (for multi-screen setups mainly) void Corona::syncLatteViewsToScreens() { m_layoutsManager->synchronizer()->syncLatteViewsToScreens(); } int Corona::primaryScreenId() const { return m_screenPool->id(qGuiApp->primaryScreen()->name()); } void Corona::closeApplication() { //! this code must be called asynchronously because it is called //! also from qml (Settings window). QTimer::singleShot(5, [this]() { m_layoutsManager->hideLatteSettingsDialog(); m_layoutsManager->synchronizer()->hideAllViews(); }); //! give the time for the views to hide themselves QTimer::singleShot(500, [this]() { qGuiApp->quit(); }); } void Corona::aboutApplication() { if (aboutDialog) { aboutDialog->hide(); aboutDialog->deleteLater(); } aboutDialog = new KAboutApplicationDialog(KAboutData::applicationData()); connect(aboutDialog.data(), &QDialog::finished, aboutDialog.data(), &QObject::deleteLater); m_wm->skipTaskBar(*aboutDialog); m_wm->setKeepAbove(*aboutDialog, true); aboutDialog->show(); } int Corona::screenForContainment(const Plasma::Containment *containment) const { //FIXME: indexOf is not a proper way to support multi-screen // as for environment to environment the indexes change // also there is the following issue triggered // from latteView adaptToScreen() // // in a multi-screen environment that // primary screen is not set to 0 it was // created an endless showing loop at // startup (catch-up race) between // screen:0 and primaryScreen //case in which this containment is child of an applet, hello systray :) if (Plasma::Applet *parentApplet = qobject_cast(containment->parent())) { if (Plasma::Containment *cont = parentApplet->containment()) { return screenForContainment(cont); } else { return -1; } } Latte::CentralLayout *currentLayout = m_layoutsManager->currentLayout(); Latte::View *view = currentLayout->viewForContainment(containment); if (view && view->screen()) { return m_screenPool->id(view->screen()->name()); } //Failed? fallback on lastScreen() //lastScreen() is the correct screen for panels //It is also correct for desktops *that have the correct activity()* //a containment with lastScreen() == 0 but another activity, //won't be associated to a screen // qDebug() << "ShellCorona screenForContainment: " << containment << " Last screen is " << containment->lastScreen(); for (auto screen : qGuiApp->screens()) { // containment->lastScreen() == m_screenPool->id(screen->name()) to check if the lastScreen refers to a screen that exists/it's known if (containment->lastScreen() == m_screenPool->id(screen->name()) && (containment->activity() == m_activityConsumer->currentActivity() || containment->containmentType() == Plasma::Types::PanelContainment || containment->containmentType() == Plasma::Types::CustomPanelContainment)) { return containment->lastScreen(); } } return -1; } void Corona::showAlternativesForApplet(Plasma::Applet *applet) { const QString alternativesQML = kPackage().filePath("appletalternativesui"); if (alternativesQML.isEmpty()) { return; } Latte::CentralLayout *currentLayout = m_layoutsManager->currentLayout(); Latte::View *latteView = currentLayout->viewForContainment(applet->containment()); KDeclarative::QmlObjectSharedEngine *qmlObj{nullptr}; if (latteView) { latteView->setAlternativesIsShown(true); qmlObj = new KDeclarative::QmlObjectSharedEngine(latteView); } else { qmlObj = new KDeclarative::QmlObjectSharedEngine(this); } qmlObj->setInitializationDelayed(true); qmlObj->setSource(QUrl::fromLocalFile(alternativesQML)); AlternativesHelper *helper = new AlternativesHelper(applet, qmlObj); qmlObj->rootContext()->setContextProperty(QStringLiteral("alternativesHelper"), helper); m_alternativesObjects << qmlObj; qmlObj->completeInitialization(); //! Alternative dialog signals connect(helper, &QObject::destroyed, this, [latteView]() { latteView->setAlternativesIsShown(false); }); connect(qmlObj->rootObject(), SIGNAL(visibleChanged(bool)), this, SLOT(alternativesVisibilityChanged(bool))); connect(applet, &Plasma::Applet::destroyedChanged, this, [this, qmlObj](bool destroyed) { if (!destroyed) { return; } QMutableListIterator it(m_alternativesObjects); while (it.hasNext()) { KDeclarative::QmlObjectSharedEngine *obj = it.next(); if (obj == qmlObj) { it.remove(); obj->deleteLater(); } } }); } void Corona::alternativesVisibilityChanged(bool visible) { if (visible) { return; } QObject *root = sender(); QMutableListIterator it(m_alternativesObjects); while (it.hasNext()) { KDeclarative::QmlObjectSharedEngine *obj = it.next(); if (obj->rootObject() == root) { it.remove(); obj->deleteLater(); } } } void Corona::addViewForLayout(QString layoutName) { qDebug() << "loading default layout"; //! Setting mutable for create a containment setImmutability(Plasma::Types::Mutable); QVariantList args; auto defaultContainment = createContainmentDelayed("org.kde.latte.containment", args); defaultContainment->setContainmentType(Plasma::Types::PanelContainment); defaultContainment->init(); if (!defaultContainment || !defaultContainment->kPackage().isValid()) { qWarning() << "the requested containment plugin can not be located or loaded"; return; } auto config = defaultContainment->config(); defaultContainment->restore(config); using Plasma::Types; QList edges{Types::BottomEdge, Types::LeftEdge, Types::TopEdge, Types::RightEdge}; Layout::GenericLayout *currentLayout = m_layoutsManager->synchronizer()->layout(layoutName); if (currentLayout) { edges = currentLayout->freeEdges(defaultContainment->screen()); } if ((edges.count() > 0)) { defaultContainment->setLocation(edges.at(0)); } else { defaultContainment->setLocation(Plasma::Types::BottomEdge); } if (m_layoutsManager->memoryUsage() == Latte::Types::MultipleLayouts) { config.writeEntry("layoutId", layoutName); } defaultContainment->updateConstraints(Plasma::Types::StartupCompletedConstraint); defaultContainment->save(config); requestConfigSync(); defaultContainment->flushPendingConstraintsEvents(); emit containmentAdded(defaultContainment); emit containmentCreated(defaultContainment); defaultContainment->createApplet(QStringLiteral("org.kde.latte.plasmoid")); defaultContainment->createApplet(QStringLiteral("org.kde.plasma.analogclock")); } void Corona::loadDefaultLayout() { addViewForLayout(m_layoutsManager->currentLayoutName()); } QStringList Corona::containmentsIds() { QStringList ids; for(const auto containment : containments()) { ids << QString::number(containment->id()); } return ids; } QStringList Corona::appletsIds() { QStringList ids; for(const auto containment : containments()) { auto applets = containment->config().group("Applets"); ids << applets.groupList(); } return ids; } //! Activate launcher menu through dbus interface void Corona::activateLauncherMenu() { m_globalShortcuts->activateLauncherMenu(); } void Corona::windowColorScheme(QString windowIdAndScheme) { int firstSlash = windowIdAndScheme.indexOf("-"); QString windowIdStr = windowIdAndScheme.mid(0, firstSlash); QString schemeStr = windowIdAndScheme.mid(firstSlash + 1); if (KWindowSystem::isPlatformWayland()) { QTimer::singleShot(200, [this, schemeStr]() { //! [Wayland Case] - give the time to be informed correctly for the active window id //! otherwise the active window id may not be the same with the one trigerred //! the color scheme dbus signal QString windowIdStr = m_wm->activeWindow().toString(); - m_wm->setColorSchemeForWindow(windowIdStr.toUInt(), schemeStr); + m_wm->schemesTracker()->setColorSchemeForWindow(windowIdStr.toUInt(), schemeStr); }); } else { - m_wm->setColorSchemeForWindow(windowIdStr.toUInt(), schemeStr); + m_wm->schemesTracker()->setColorSchemeForWindow(windowIdStr.toUInt(), schemeStr); } } //! update badge for specific view item void Corona::updateDockItemBadge(QString identifier, QString value) { m_globalShortcuts->updateViewItemBadge(identifier, value); } void Corona::switchToLayout(QString layout) { m_layoutsManager->switchToLayout(layout); } void Corona::showSettingsWindow(int page) { Types::LatteConfigPage p = Types::LayoutPage; if (page >= Types::LayoutPage && page <= Types::PreferencesPage) { p = static_cast(page); } m_layoutsManager->showLatteSettingsDialog(p); } void Corona::setContextMenuView(int id) { //! set context menu view id m_contextMenuViewId = id; } QStringList Corona::contextMenuData() { QStringList data; Types::ViewType viewType{Types::DockView}; Latte::CentralLayout *currentLayout = m_layoutsManager->currentLayout(); if (currentLayout) { viewType = currentLayout->latteViewType(m_contextMenuViewId); } data << QString::number((int)m_layoutsManager->memoryUsage()); data << m_layoutsManager->currentLayoutName(); data << QString::number((int)viewType); for(const auto &layoutName : m_layoutsManager->menuLayouts()) { if (m_layoutsManager->synchronizer()->centralLayout(layoutName)) { data << QString("1," + layoutName); } else { data << QString("0," + layoutName); } } //! reset context menu view id m_contextMenuViewId = -1; return data; } void Corona::setBackgroundFromBroadcast(QString activity, QString screenName, QString filename) { if (filename.startsWith("file://")) { filename = filename.remove(0,7); } QMetaObject::invokeMethod(m_backgroundTracer->rootObject(), "setBackgroundFromBroadcast", Q_ARG(QVariant, activity), Q_ARG(QVariant, screenName), Q_ARG(QVariant, filename)); } void Corona::setBroadcastedBackgroundsEnabled(QString activity, QString screenName, bool enabled) { QMetaObject::invokeMethod(m_backgroundTracer->rootObject(), "setBroadcastedBackgroundsEnabled", Q_ARG(QVariant, activity), Q_ARG(QVariant, screenName), Q_ARG(QVariant, enabled)); } inline void Corona::qmlRegisterTypes() const { qmlRegisterType(); qmlRegisterType(); } } diff --git a/app/wm/CMakeLists.txt b/app/wm/CMakeLists.txt index 1884db8c..a6818c61 100644 --- a/app/wm/CMakeLists.txt +++ b/app/wm/CMakeLists.txt @@ -1,10 +1,11 @@ set(lattedock-app_SRCS ${lattedock-app_SRCS} ${CMAKE_CURRENT_SOURCE_DIR}/abstractwindowinterface.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/schemecolors.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/schemecolors.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/schemestracker.cpp ${CMAKE_CURRENT_SOURCE_DIR}/waylandinterface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/windowinfowrap.cpp ${CMAKE_CURRENT_SOURCE_DIR}/windowstracker.cpp ${CMAKE_CURRENT_SOURCE_DIR}/xwindowinterface.cpp PARENT_SCOPE ) diff --git a/app/wm/abstractwindowinterface.cpp b/app/wm/abstractwindowinterface.cpp index c39cc4fd..9dff51dc 100644 --- a/app/wm/abstractwindowinterface.cpp +++ b/app/wm/abstractwindowinterface.cpp @@ -1,150 +1,63 @@ /* * Copyright 2016 Smith AR * 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 "abstractwindowinterface.h" // local +#include "schemestracker.h" #include "windowstracker.h" #include "../lattecorona.h" -// Qt -#include -#include -#include - -// KDE -#include -#include namespace Latte { namespace WindowSystem { AbstractWindowInterface::AbstractWindowInterface(QObject *parent) : QObject(parent) { m_corona = qobject_cast(parent); m_windowsTracker = new WindowsTracker(this); - - updateDefaultScheme(); - - connect(this, &AbstractWindowInterface::windowRemoved, this, [&](WindowId wid) { - m_windowScheme.remove(wid); - }); - - //! track for changing default scheme - QString kdeSettingsFile = QDir::homePath() + "/.config/kdeglobals"; - - KDirWatch::self()->addFile(kdeSettingsFile); - - connect(KDirWatch::self(), &KDirWatch::dirty, this, [ &, kdeSettingsFile](const QString & path) { - if (path == kdeSettingsFile) { - this->updateDefaultScheme(); - } - }); - - connect(KDirWatch::self(), &KDirWatch::created, this, [ &, kdeSettingsFile](const QString & path) { - if (path == kdeSettingsFile) { - this->updateDefaultScheme(); - } - }); + m_schemesTracker = new SchemesTracker(this); } AbstractWindowInterface::~AbstractWindowInterface() { - m_windowScheme.clear(); - //! it is just a reference to a real scheme file - m_schemes.take("kdeglobals"); - qDeleteAll(m_schemes); - m_schemes.clear(); - + m_schemesTracker->deleteLater(); m_windowsTracker->deleteLater(); } -//! Scheme support for windows -void AbstractWindowInterface::updateDefaultScheme() -{ - QString defaultSchemePath = SchemeColors::possibleSchemeFile("kdeglobals"); - - qDebug() << " Windows default color scheme :: " << defaultSchemePath; - - SchemeColors *dScheme; - - if (!m_schemes.contains(defaultSchemePath)) { - dScheme = new SchemeColors(this, defaultSchemePath); - m_schemes[defaultSchemePath] = dScheme; - } else { - dScheme = m_schemes[defaultSchemePath]; - } - - if (!m_schemes.contains("kdeglobals") || m_schemes["kdeglobals"]->schemeFile() != defaultSchemePath) { - m_schemes["kdeglobals"] = dScheme; - } -} - Latte::Corona *AbstractWindowInterface::corona() { return m_corona; } -SchemeColors *AbstractWindowInterface::schemeForWindow(WindowId wid) +SchemesTracker *AbstractWindowInterface::schemesTracker() { - if (!m_windowScheme.contains(wid)) { - return m_schemes["kdeglobals"]; - } else { - return m_schemes[m_windowScheme[wid]]; - } - - return nullptr; + return m_schemesTracker; } WindowsTracker *AbstractWindowInterface::windowsTracker() { return m_windowsTracker; } -void AbstractWindowInterface::setColorSchemeForWindow(WindowId wid, QString scheme) -{ - if (scheme == "kdeglobals" && !m_windowScheme.contains(wid)) { - //default scheme does not have to be set - return; - } - - if (scheme == "kdeglobals") { - //! a window that previously had an explicit set scheme now is set back to default scheme - m_windowScheme.remove(wid); - } else { - QString schemeFile = SchemeColors::possibleSchemeFile(scheme); - - if (!m_schemes.contains(schemeFile)) { - //! when this scheme file has not been loaded yet - m_schemes[schemeFile] = new SchemeColors(this, schemeFile); - } - - m_windowScheme[wid] = schemeFile; - } - - if (wid == activeWindow()) { - emit activeWindowChanged(wid); - } -} - } } diff --git a/app/wm/abstractwindowinterface.h b/app/wm/abstractwindowinterface.h index 70f28117..b88bb57d 100644 --- a/app/wm/abstractwindowinterface.h +++ b/app/wm/abstractwindowinterface.h @@ -1,138 +1,130 @@ /* * Copyright 2016 Smith AR * 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 . */ #ifndef ABSTRACTWINDOWINTERFACE_H #define ABSTRACTWINDOWINTERFACE_H // local #include "schemecolors.h" #include "windowinfowrap.h" #include "../liblatte2/types.h" #include "../liblatte2/extras.h" // C++ #include #include // Qt #include #include #include #include #include #include #include #include // KDE #include // Plasma #include namespace Latte { class Corona; namespace WindowSystem { +class SchemesTracker; class WindowsTracker; } } namespace Latte { namespace WindowSystem { class AbstractWindowInterface : public QObject { Q_OBJECT public: enum class Slide { None, Top, Left, Bottom, Right, }; explicit AbstractWindowInterface(QObject *parent = nullptr); virtual ~AbstractWindowInterface(); virtual void setViewExtraFlags(QWindow &view) = 0; virtual void setViewStruts(QWindow &view, const QRect &rect , Plasma::Types::Location location) = 0; virtual void setWindowOnActivities(QWindow &window, const QStringList &activities) = 0; virtual void removeViewStruts(QWindow &view) const = 0; virtual WindowId activeWindow() const = 0; virtual WindowInfoWrap requestInfo(WindowId wid) const = 0; virtual WindowInfoWrap requestInfoActive() const = 0; virtual bool isOnCurrentDesktop(WindowId wid) const = 0; virtual bool isOnCurrentActivity(WindowId wid) const = 0; virtual const std::list &windows() const = 0; virtual void setKeepAbove(const QDialog &dialog, bool above = true) const = 0; virtual void skipTaskBar(const QDialog &dialog) const = 0; virtual void slideWindow(QWindow &view, Slide location) const = 0; virtual void enableBlurBehind(QWindow &view) const = 0; virtual void setEdgeStateFor(QWindow *view, bool active) const = 0; virtual void releaseMouseEventFor(WindowId wid) const = 0; virtual void requestActivate(WindowId wid) const = 0; virtual void requestToggleMaximized(WindowId wid) const = 0; virtual void requestMoveWindow(WindowId wid, QPoint from) const = 0; virtual bool windowCanBeDragged(WindowId wid) const = 0; virtual WindowId winIdFor(QString appId, QRect geometry) const = 0; Latte::Corona *corona(); - SchemeColors *schemeForWindow(WindowId wId); + SchemesTracker *schemesTracker(); WindowsTracker *windowsTracker(); - void setColorSchemeForWindow(WindowId wId, QString scheme); signals: void activeWindowChanged(WindowId wid); void windowChanged(WindowId winfo); void windowAdded(WindowId wid); void windowRemoved(WindowId wid); void currentDesktopChanged(); void currentActivityChanged(); protected: std::list m_windows; QPointer m_activities; -private slots: - void updateDefaultScheme(); - private: - //! scheme file and its loaded colors - QMap m_schemes; - - //! window id and its corresponding scheme file - QMap m_windowScheme; - Latte::Corona *m_corona; + SchemesTracker *m_schemesTracker; WindowsTracker *m_windowsTracker; }; } } #endif // ABSTRACTWINDOWINTERFACE_H diff --git a/app/wm/abstractwindowinterface.cpp b/app/wm/schemestracker.cpp similarity index 76% copy from app/wm/abstractwindowinterface.cpp copy to app/wm/schemestracker.cpp index c39cc4fd..269563f7 100644 --- a/app/wm/abstractwindowinterface.cpp +++ b/app/wm/schemestracker.cpp @@ -1,150 +1,138 @@ /* -* Copyright 2016 Smith AR -* Michail Vourlakos +* 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 "abstractwindowinterface.h" +#include "schemestracker.h" // local -#include "windowstracker.h" +#include "abstractwindowinterface.h" +#include "schemecolors.h" #include "../lattecorona.h" // Qt -#include #include -#include // KDE #include -#include + namespace Latte { namespace WindowSystem { -AbstractWindowInterface::AbstractWindowInterface(QObject *parent) +SchemesTracker::SchemesTracker(AbstractWindowInterface *parent) : QObject(parent) { - m_corona = qobject_cast(parent); - m_windowsTracker = new WindowsTracker(this); + m_wm = parent; + init(); +} +SchemesTracker::~SchemesTracker() +{ + m_windowScheme.clear(); + //! it is just a reference to a real scheme file + m_schemes.take("kdeglobals"); + qDeleteAll(m_schemes); + m_schemes.clear(); +} + +void SchemesTracker::init() +{ updateDefaultScheme(); - connect(this, &AbstractWindowInterface::windowRemoved, this, [&](WindowId wid) { + connect(m_wm, &AbstractWindowInterface::windowRemoved, this, [&](WindowId wid) { m_windowScheme.remove(wid); }); //! track for changing default scheme QString kdeSettingsFile = QDir::homePath() + "/.config/kdeglobals"; KDirWatch::self()->addFile(kdeSettingsFile); connect(KDirWatch::self(), &KDirWatch::dirty, this, [ &, kdeSettingsFile](const QString & path) { if (path == kdeSettingsFile) { this->updateDefaultScheme(); } }); connect(KDirWatch::self(), &KDirWatch::created, this, [ &, kdeSettingsFile](const QString & path) { if (path == kdeSettingsFile) { this->updateDefaultScheme(); } }); } -AbstractWindowInterface::~AbstractWindowInterface() -{ - m_windowScheme.clear(); - //! it is just a reference to a real scheme file - m_schemes.take("kdeglobals"); - qDeleteAll(m_schemes); - m_schemes.clear(); - - m_windowsTracker->deleteLater(); -} - //! Scheme support for windows -void AbstractWindowInterface::updateDefaultScheme() +void SchemesTracker::updateDefaultScheme() { QString defaultSchemePath = SchemeColors::possibleSchemeFile("kdeglobals"); qDebug() << " Windows default color scheme :: " << defaultSchemePath; SchemeColors *dScheme; if (!m_schemes.contains(defaultSchemePath)) { dScheme = new SchemeColors(this, defaultSchemePath); m_schemes[defaultSchemePath] = dScheme; } else { dScheme = m_schemes[defaultSchemePath]; } if (!m_schemes.contains("kdeglobals") || m_schemes["kdeglobals"]->schemeFile() != defaultSchemePath) { m_schemes["kdeglobals"] = dScheme; } } -Latte::Corona *AbstractWindowInterface::corona() -{ - return m_corona; -} - -SchemeColors *AbstractWindowInterface::schemeForWindow(WindowId wid) +SchemeColors *SchemesTracker::schemeForWindow(WindowId wid) { if (!m_windowScheme.contains(wid)) { return m_schemes["kdeglobals"]; } else { return m_schemes[m_windowScheme[wid]]; } return nullptr; } -WindowsTracker *AbstractWindowInterface::windowsTracker() -{ - return m_windowsTracker; -} - -void AbstractWindowInterface::setColorSchemeForWindow(WindowId wid, QString scheme) +void SchemesTracker::setColorSchemeForWindow(WindowId wid, QString scheme) { if (scheme == "kdeglobals" && !m_windowScheme.contains(wid)) { //default scheme does not have to be set return; } if (scheme == "kdeglobals") { //! a window that previously had an explicit set scheme now is set back to default scheme m_windowScheme.remove(wid); } else { QString schemeFile = SchemeColors::possibleSchemeFile(scheme); if (!m_schemes.contains(schemeFile)) { //! when this scheme file has not been loaded yet m_schemes[schemeFile] = new SchemeColors(this, schemeFile); } m_windowScheme[wid] = schemeFile; } - if (wid == activeWindow()) { - emit activeWindowChanged(wid); + if (wid == m_wm->activeWindow()) { + emit m_wm->activeWindowChanged(wid); } } } } - diff --git a/app/wm/schemestracker.h b/app/wm/schemestracker.h new file mode 100644 index 00000000..bcef94db --- /dev/null +++ b/app/wm/schemestracker.h @@ -0,0 +1,68 @@ +/* +* 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 . +*/ + +#ifndef SCHEMESTRACKER_H +#define SCHEMESTRACKER_H + +#include "windowinfowrap.h" + +// Qt +#include + + +namespace Latte { +namespace WindowSystem { +class AbstractWindowInterface; +class SchemeColors; +} +} + +namespace Latte { +namespace WindowSystem { + +class SchemesTracker : public QObject { + Q_OBJECT + +public: + SchemesTracker(AbstractWindowInterface *parent); + ~SchemesTracker() override; + + SchemeColors *schemeForWindow(WindowId wId); + void setColorSchemeForWindow(WindowId wId, QString scheme); + +private slots: + void updateDefaultScheme(); + +private: + void init(); + +private: + AbstractWindowInterface *m_wm; + + //! scheme file and its loaded colors + QMap m_schemes; + + //! window id and its corresponding scheme file + QMap m_windowScheme; +}; + +} +} + +#endif diff --git a/app/wm/windowstracker.cpp b/app/wm/windowstracker.cpp index 26f6184c..d488c693 100644 --- a/app/wm/windowstracker.cpp +++ b/app/wm/windowstracker.cpp @@ -1,531 +1,532 @@ /* * 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 "abstractwindowinterface.h" #include "schemecolors.h" +#include "schemestracker.h" #include "../lattecorona.h" #include "../layouts/manager.h" #include "../view/view.h" #include "../view/positioner.h" #include "../../liblatte2/types.h" namespace Latte { namespace WindowSystem { WindowsTracker::WindowsTracker(AbstractWindowInterface *parent) : QObject(parent) { m_wm = parent; init(); } WindowsTracker::~WindowsTracker() { } void WindowsTracker::init() { connect(m_wm->corona(), &Plasma::Corona::availableScreenRectChanged, this, &WindowsTracker::updateAvailableScreenGeometries); connect(m_wm, &AbstractWindowInterface::windowChanged, this, [&](WindowId wid) { m_windows[wid] = m_wm->requestInfo(wid); updateViewsHints(); }); connect(m_wm, &AbstractWindowInterface::windowRemoved, this, [&](WindowId wid) { m_windows.remove(wid); updateViewsHints(); }); connect(m_wm, &AbstractWindowInterface::windowAdded, this, [&](WindowId wid) { if (!m_windows.contains(wid)) { m_windows.insert(wid, m_wm->requestInfo(wid)); } updateViewsHints(); }); connect(m_wm, &AbstractWindowInterface::activeWindowChanged, this, [&](WindowId wid) { for (const auto view : m_views.keys()) { WindowId lastWinId = m_views[view].lastActiveWindow; if (m_windows.contains(lastWinId)) { m_windows[lastWinId] = m_wm->requestInfo(lastWinId); } } m_windows[wid] = m_wm->requestInfo(wid); updateViewsHints(); }); connect(m_wm, &AbstractWindowInterface::currentDesktopChanged, this, [&] { updateViewsHints(); }); connect(m_wm, &AbstractWindowInterface::currentActivityChanged, this, [&] { if (m_wm->corona()->layoutsManager()->memoryUsage() == Types::MultipleLayouts) { //! this is needed in MultipleLayouts because there is a chance that multiple //! layouts are providing different available screen geometries in different Activities updateAvailableScreenGeometries(); } updateViewsHints(); }); } void WindowsTracker::initViewHints(Latte::View *view) { if (!m_views.contains(view)) { return; } setActiveWindowMaximized(view, false); setActiveWindowTouching(view, false); setExistsWindowActive(view, false); setExistsWindowTouching(view, false); setExistsWindowMaximized(view, false); setActiveWindowScheme(view, nullptr); setTouchingWindowScheme(view, nullptr); } void WindowsTracker::addView(Latte::View *view) { if (m_views.contains(view)) { return; } ViewHints hints; m_views[view] = hints; updateAvailableScreenGeometries(); updateHints(view); } void WindowsTracker::removeView(Latte::View *view) { if (!m_views.contains(view)) { return; } m_views.remove(view); } //! Views Properties And Hints bool WindowsTracker::enabled(Latte::View *view) { if (!m_views.contains(view)) { return false; } return m_views[view].enabled; } void WindowsTracker::setEnabled(Latte::View *view, const bool enabled) { if (!m_views.contains(view) || m_views[view].enabled == enabled) { return; } m_views[view].enabled = enabled; if (enabled) { updateHints(view); } else { initViewHints(view); } emit enabledChanged(view); } bool WindowsTracker::activeWindowMaximized(Latte::View *view) const { if (!m_views.contains(view)) { return false; } return m_views[view].activeWindowMaximized; } void WindowsTracker::setActiveWindowMaximized(Latte::View *view, bool activeMaximized) { if (!m_views.contains(view) || m_views[view].activeWindowMaximized == activeMaximized) { return; } m_views[view].activeWindowMaximized = activeMaximized; emit activeWindowMaximizedChanged(view); } bool WindowsTracker::activeWindowTouching(Latte::View *view) const { if (!m_views.contains(view)) { return false; } return m_views[view].activeWindowTouching; } void WindowsTracker::setActiveWindowTouching(Latte::View *view, bool activeTouching) { if (!m_views.contains(view) || m_views[view].activeWindowTouching == activeTouching) { return; } m_views[view].activeWindowTouching = activeTouching; emit activeWindowTouchingChanged(view); } bool WindowsTracker::existsWindowActive(Latte::View *view) const { if (!m_views.contains(view)) { return false; } return m_views[view].existsWindowActive; } void WindowsTracker::setExistsWindowActive(Latte::View *view, bool windowActive) { if (!m_views.contains(view) || m_views[view].existsWindowActive == windowActive) { return; } m_views[view].existsWindowActive = windowActive; emit existsWindowActiveChanged(view); } bool WindowsTracker::existsWindowMaximized(Latte::View *view) const { if (!m_views.contains(view)) { return false; } return m_views[view].existsWindowMaximized; } void WindowsTracker::setExistsWindowMaximized(Latte::View *view, bool windowMaximized) { if (!m_views.contains(view) || m_views[view].existsWindowMaximized == windowMaximized) { return; } m_views[view].existsWindowMaximized = windowMaximized; emit existsWindowMaximizedChanged(view); } bool WindowsTracker::existsWindowTouching(Latte::View *view) const { if (!m_views.contains(view)) { return false; } return m_views[view].existsWindowTouching; } void WindowsTracker::setExistsWindowTouching(Latte::View *view, bool windowTouching) { if (!m_views.contains(view) || m_views[view].existsWindowTouching == windowTouching) { return; } m_views[view].existsWindowTouching = windowTouching; emit existsWindowTouchingChanged(view); } SchemeColors *WindowsTracker::activeWindowScheme(Latte::View *view) const { if (!m_views.contains(view)) { return nullptr; } return m_views[view].activeWindowScheme; } void WindowsTracker::setActiveWindowScheme(Latte::View *view, WindowSystem::SchemeColors *scheme) { if (!m_views.contains(view) || m_views[view].activeWindowScheme == scheme) { return; } m_views[view].activeWindowScheme = scheme; emit activeWindowSchemeChanged(view); } SchemeColors *WindowsTracker::touchingWindowScheme(Latte::View *view) const { if (!m_views.contains(view)) { return nullptr; } return m_views[view].touchingWindowScheme; } void WindowsTracker::setTouchingWindowScheme(Latte::View *view, WindowSystem::SchemeColors *scheme) { if (!m_views.contains(view) || m_views[view].touchingWindowScheme == scheme) { return; } m_views[view].touchingWindowScheme = scheme; emit touchingWindowSchemeChanged(view); } WindowInfoWrap WindowsTracker::lastActiveWindowInfo(Latte::View *view) { WindowInfoWrap info; if (!m_views.contains(view)) { return info; } if (!m_windows.contains(m_views[view].lastActiveWindow)) { m_views[view].lastActiveWindow = info.wid(); return info; } return m_wm->requestInfo(m_views[view].lastActiveWindow); } //! Windows Criteria Functions bool WindowsTracker::inCurrentDesktopActivity(const WindowInfoWrap &winfo) { return (winfo.isValid() && m_wm->isOnCurrentDesktop(winfo.wid()) && m_wm->isOnCurrentActivity(winfo.wid())); } bool WindowsTracker::intersects(Latte::View *view, const WindowInfoWrap &winfo) { return (!winfo.isMinimized() && !winfo.isShaded() && winfo.geometry().intersects(view->absoluteGeometry())); } bool WindowsTracker::isActive(const WindowInfoWrap &winfo) { return (winfo.isValid() && winfo.isActive() && !winfo.isMinimized()); } bool WindowsTracker::isActiveInViewScreen(Latte::View *view, const WindowInfoWrap &winfo) { return (winfo.isValid() && winfo.isActive() && !winfo.isMinimized() && m_views[view].availableScreenGeometry.contains(winfo.geometry().center())); } bool WindowsTracker::isMaximizedInViewScreen(Latte::View *view, const WindowInfoWrap &winfo) { auto viewIntersectsMaxVert = [&]() noexcept -> bool { return ((winfo.isMaxVert() || (view->screen() && view->screen()->availableSize().height() <= winfo.geometry().height())) && intersects(view, winfo)); }; auto viewIntersectsMaxHoriz = [&]() noexcept -> bool { return ((winfo.isMaxHoriz() || (view->screen() && view->screen()->availableSize().width() <= winfo.geometry().width())) && intersects(view, 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 return (winfo.isValid() && !winfo.isMinimized() && (winfo.isMaximized() || viewIntersectsMaxVert() || viewIntersectsMaxHoriz()) && m_views[view].availableScreenGeometry.contains(winfo.geometry().center())); } bool WindowsTracker::isTouchingView(Latte::View *view, const WindowSystem::WindowInfoWrap &winfo) { return (winfo.isValid() && intersects(view, winfo)); } bool WindowsTracker::isTouchingViewEdge(Latte::View *view, const WindowInfoWrap &winfo) { if (winfo.isValid() && !winfo.isMinimized()) { bool touchingViewEdge{false}; QRect screenGeometry = view->screenGeometry(); QRect availableScreenGeometry = m_views[view].availableScreenGeometry; bool inCurrentScreen{screenGeometry.contains(winfo.geometry().topLeft()) || screenGeometry.contains(winfo.geometry().bottomRight())}; if (inCurrentScreen) { if (view->location() == Plasma::Types::TopEdge) { touchingViewEdge = (winfo.geometry().y() == availableScreenGeometry.y()); } else if (view->location() == Plasma::Types::BottomEdge) { touchingViewEdge = (winfo.geometry().bottom() == availableScreenGeometry.bottom()); } else if (view->location() == Plasma::Types::LeftEdge) { touchingViewEdge = (winfo.geometry().x() == availableScreenGeometry.x()); } else if (view->location() == Plasma::Types::RightEdge) { touchingViewEdge = (winfo.geometry().right() == availableScreenGeometry.right()); } } return touchingViewEdge; } return false; } void WindowsTracker::cleanupFaultyWindows() { for (const 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); } } } void WindowsTracker::updateAvailableScreenGeometries() { for (const auto view : m_views.keys()) { if (m_views[view].enabled) { int currentScrId = view->positioner()->currentScreenId(); QRect tempAvailableScreenGeometry = m_wm->corona()->availableScreenRectWithCriteria(currentScrId, {Types::AlwaysVisible}, {}); if (tempAvailableScreenGeometry != m_views[view].availableScreenGeometry) { m_views[view].availableScreenGeometry = tempAvailableScreenGeometry; updateHints(view); } } } } void WindowsTracker::updateViewsHints() { for (const auto view : m_views.keys()) { if (m_views[view].enabled) { updateHints(view); } } } void WindowsTracker::updateHints(Latte::View *view) { if (!m_views.contains(view)) { return; } bool foundActive{false}; bool foundActiveInCurScreen{false}; bool foundActiveTouchInCurScreen{false}; bool foundTouchInCurScreen{false}; bool foundMaximizedInCurScreen{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 activeWinId; WindowId touchWinId; WindowId activeTouchWinId; for (const auto &winfo : m_windows) { if (winfo.isPlasmaDesktop() || !inCurrentDesktopActivity(winfo)) { continue; } if (isActive(winfo)) { foundActive = true; } if (isActiveInViewScreen(view, winfo)) { m_views[view].lastActiveWindow = winfo.wid(); foundActiveInCurScreen = true; activeWinId = winfo.wid(); } if (isTouchingViewEdge(view, winfo) || isTouchingView(view, winfo)) { if (winfo.isActive()) { foundActiveTouchInCurScreen = true; activeTouchWinId = winfo.wid(); if (isMaximizedInViewScreen(view, winfo)) { //! active maximized windows have higher priority than the rest maximized windows foundMaximizedInCurScreen = true; maxWinId = winfo.wid(); } } else { foundTouchInCurScreen = true; touchWinId = winfo.wid(); } if (!foundMaximizedInCurScreen && isMaximizedInViewScreen(view, winfo)) { foundMaximizedInCurScreen = true; maxWinId = winfo.wid(); } } if (!existsFaultyWindow && winfo.geometry() == QRect(0, 0, 0, 0)) { existsFaultyWindow = true; } //qDebug() << "window geometry ::: " << winfo.geometry(); } if (existsFaultyWindow) { cleanupFaultyWindows(); } //! HACK: KWin Effects such as ShowDesktop have no way to be identified and as such //! create issues with identifying properly touching and maximized windows. BUT when //! they are enabled then NO ACTIVE window is found. This is a way to identify these //! effects trigerring and disable the touch flags. //! BUG: 404483 //! Disabled because it has fault identifications, e.g. when a window is maximized and //! Latte or Plasma are showing their View settings //foundMaximizedInCurScreen = foundMaximizedInCurScreen && foundActive; //foundTouchInCurScreen = foundTouchInCurScreen && foundActive; //! assign flags setExistsWindowActive(view, foundActiveInCurScreen); setActiveWindowTouching(view, foundActiveTouchInCurScreen); setActiveWindowMaximized(view, (maxWinId.toInt()>0 && (maxWinId == activeTouchWinId))); setExistsWindowMaximized(view, foundMaximizedInCurScreen); setExistsWindowTouching(view, (foundTouchInCurScreen || foundActiveTouchInCurScreen)); //! update color schemes for active and touching windows - setActiveWindowScheme(view, (foundActiveInCurScreen ? m_wm->schemeForWindow(activeWinId) : nullptr)); + setActiveWindowScheme(view, (foundActiveInCurScreen ? m_wm->schemesTracker()->schemeForWindow(activeWinId) : nullptr)); if (foundActiveTouchInCurScreen) { - setTouchingWindowScheme(view, m_wm->schemeForWindow(activeTouchWinId)); + setTouchingWindowScheme(view, m_wm->schemesTracker()->schemeForWindow(activeTouchWinId)); } else if (foundMaximizedInCurScreen) { - setTouchingWindowScheme(view, m_wm->schemeForWindow(maxWinId)); + setTouchingWindowScheme(view, m_wm->schemesTracker()->schemeForWindow(maxWinId)); } else if (foundTouchInCurScreen) { - setTouchingWindowScheme(view, m_wm->schemeForWindow(touchWinId)); + setTouchingWindowScheme(view, m_wm->schemesTracker()->schemeForWindow(touchWinId)); } else { setTouchingWindowScheme(view, nullptr); } //! Debug //qDebug() << "TRACKING | SCREEN: " << view->positioner()->currentScreenId() << " , EDGE:" << view->location() << " , ENABLED:" << enabled(view); //qDebug() << "TRACKING | activeWindowTouching: " << foundActiveTouchInCurScreen << " ,activeWindowMaximized: " << activeWindowMaximized(view); //qDebug() << "TRACKING | existsWindowActive: " << foundActiveInCurScreen << " , existsWindowMaximized:" << existsWindowMaximized(view) // << " , existsWindowTouching:"<