File indexing completed on 2022-11-29 15:04:29

0001 /*
0002     SPDX-FileCopyrightText: 2016 Smith AR <audoban@openmailbox.org>
0003     SPDX-FileCopyrightText: 2016 Michail Vourlakos <mvourlakos@gmail.com>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "waylandinterface.h"
0009 
0010 // local
0011 #include <coretypes.h>
0012 #include "../view/positioner.h"
0013 #include "../view/view.h"
0014 #include "../view/settings/subconfigview.h"
0015 #include "../view/helpers/screenedgeghostwindow.h"
0016 #include "../lattecorona.h"
0017 
0018 // Qt
0019 #include <QDebug>
0020 #include <QTimer>
0021 #include <QApplication>
0022 #include <QtX11Extras/QX11Info>
0023 #include <QQuickView>
0024 #include <QLatin1String>
0025 
0026 // KDE
0027 #include <KWindowSystem>
0028 #include <KWindowInfo>
0029 #include <KWayland/Client/surface.h>
0030 
0031 #include <KWayland/Client/plasmavirtualdesktop.h>
0032 
0033 
0034 // X11
0035 #include <NETWM>
0036 
0037 using namespace KWayland::Client;
0038 
0039 namespace Latte {
0040 
0041 class Private::GhostWindow : public QQuickView
0042 {
0043     Q_OBJECT
0044 
0045 public:
0046     WindowSystem::WindowId m_winId;
0047 
0048     GhostWindow(WindowSystem::WaylandInterface *waylandInterface)
0049         : m_waylandInterface(waylandInterface) {
0050         setFlags(Qt::FramelessWindowHint
0051                  | Qt::WindowStaysOnTopHint
0052                  | Qt::NoDropShadowWindowHint
0053                  | Qt::WindowDoesNotAcceptFocus);
0054 
0055         setColor(QColor(Qt::transparent));
0056         setClearBeforeRendering(true);
0057 
0058         connect(m_waylandInterface, &WindowSystem::AbstractWindowInterface::latteWindowAdded, this, &GhostWindow::identifyWinId);
0059 
0060         setupWaylandIntegration();
0061         show();
0062     }
0063 
0064     ~GhostWindow() {
0065         m_waylandInterface->unregisterIgnoredWindow(m_winId);
0066         delete m_shellSurface;
0067     }
0068 
0069     void setGeometry(const QRect &rect) {
0070         if (geometry() == rect) {
0071             return;
0072         }
0073 
0074         m_validGeometry = rect;
0075 
0076         setMinimumSize(rect.size());
0077         setMaximumSize(rect.size());
0078         resize(rect.size());
0079 
0080         m_shellSurface->setPosition(rect.topLeft());
0081     }
0082 
0083     void setupWaylandIntegration() {
0084         using namespace KWayland::Client;
0085 
0086         if (m_shellSurface)
0087             return;
0088 
0089         Surface *s{Surface::fromWindow(this)};
0090 
0091         if (!s)
0092             return;
0093 
0094         m_shellSurface = m_waylandInterface->waylandCoronaInterface()->createSurface(s, this);
0095         qDebug() << "wayland ghost window surface was created...";
0096 
0097         m_shellSurface->setSkipTaskbar(true);
0098         m_shellSurface->setPanelTakesFocus(false);
0099         m_shellSurface->setRole(PlasmaShellSurface::Role::Panel);
0100         m_shellSurface->setPanelBehavior(PlasmaShellSurface::PanelBehavior::AlwaysVisible);
0101     }
0102 
0103     KWayland::Client::PlasmaShellSurface *m_shellSurface{nullptr};
0104     WindowSystem::WaylandInterface *m_waylandInterface{nullptr};
0105 
0106     //! geometry() function under wayland does not return nice results
0107     QRect m_validGeometry;
0108 
0109 public slots:
0110     void identifyWinId() {
0111         if (m_winId.isNull()) {
0112             m_winId = m_waylandInterface->winIdFor("latte-dock", m_validGeometry);
0113             m_waylandInterface->registerIgnoredWindow(m_winId);
0114         }
0115     }
0116 };
0117 
0118 namespace WindowSystem {
0119 
0120 WaylandInterface::WaylandInterface(QObject *parent)
0121     : AbstractWindowInterface(parent)
0122 {
0123     m_corona = qobject_cast<Latte::Corona *>(parent);
0124 }
0125 
0126 WaylandInterface::~WaylandInterface()
0127 {
0128 }
0129 
0130 void WaylandInterface::init()
0131 {
0132 }
0133 
0134 void WaylandInterface::initWindowManagement(KWayland::Client::PlasmaWindowManagement *windowManagement)
0135 {
0136     if (m_windowManagement == windowManagement) {
0137         return;
0138     }
0139 
0140     m_windowManagement = windowManagement;
0141 
0142     connect(m_windowManagement, &PlasmaWindowManagement::windowCreated, this, &WaylandInterface::windowCreatedProxy);
0143     connect(m_windowManagement, &PlasmaWindowManagement::activeWindowChanged, this, [&]() noexcept {
0144         auto w = m_windowManagement->activeWindow();
0145         if (!w || (w && (!m_ignoredWindows.contains(w->internalId()))) ) {
0146             emit activeWindowChanged(w ? w->internalId() : 0);
0147         }
0148 
0149     }, Qt::QueuedConnection);
0150 }
0151 
0152 void WaylandInterface::initVirtualDesktopManagement(KWayland::Client::PlasmaVirtualDesktopManagement *virtualDesktopManagement)
0153 {
0154     if (m_virtualDesktopManagement == virtualDesktopManagement) {
0155         return;
0156     }
0157 
0158     m_virtualDesktopManagement = virtualDesktopManagement;
0159 
0160     connect(m_virtualDesktopManagement, &KWayland::Client::PlasmaVirtualDesktopManagement::desktopCreated, this,
0161             [this](const QString &id, quint32 position) {
0162         addDesktop(id, position);
0163     });
0164 
0165     connect(m_virtualDesktopManagement, &KWayland::Client::PlasmaVirtualDesktopManagement::desktopRemoved, this,
0166             [this](const QString &id) {
0167         m_desktops.removeAll(id);
0168 
0169         if (m_currentDesktop == id) {
0170             setCurrentDesktop(QString());
0171         }
0172     });
0173 }
0174 
0175 void WaylandInterface::addDesktop(const QString &id, quint32 position)
0176 {
0177     if (m_desktops.contains(id)) {
0178         return;
0179     }
0180 
0181     m_desktops.append(id);
0182 
0183     const KWayland::Client::PlasmaVirtualDesktop *desktop = m_virtualDesktopManagement->getVirtualDesktop(id);
0184 
0185     QObject::connect(desktop, &KWayland::Client::PlasmaVirtualDesktop::activated, this,
0186                      [desktop, this]() {
0187         setCurrentDesktop(desktop->id());
0188     }
0189     );
0190 
0191     if (desktop->isActive()) {
0192         setCurrentDesktop(id);
0193     }
0194 }
0195 
0196 void WaylandInterface::setCurrentDesktop(QString desktop)
0197 {
0198     if (m_currentDesktop == desktop) {
0199         return;
0200     }
0201 
0202     m_currentDesktop = desktop;
0203     emit currentDesktopChanged();
0204 }
0205 
0206 KWayland::Client::PlasmaShell *WaylandInterface::waylandCoronaInterface() const
0207 {
0208     return m_corona->waylandCoronaInterface();
0209 }
0210 
0211 //! Register Latte Ignored Windows in order to NOT be tracked
0212 void WaylandInterface::registerIgnoredWindow(WindowId wid)
0213 {
0214     if (!wid.isNull() && !m_ignoredWindows.contains(wid)) {
0215         m_ignoredWindows.append(wid);
0216 
0217         KWayland::Client::PlasmaWindow *w = windowFor(wid);
0218 
0219         if (w) {
0220             untrackWindow(w);
0221         }
0222 
0223         emit windowChanged(wid);
0224     }
0225 }
0226 
0227 void WaylandInterface::unregisterIgnoredWindow(WindowId wid)
0228 {
0229     if (m_ignoredWindows.contains(wid)) {
0230         m_ignoredWindows.removeAll(wid);
0231         emit windowRemoved(wid);
0232     }
0233 }
0234 
0235 void WaylandInterface::setViewExtraFlags(QObject *view, bool isPanelWindow, Latte::Types::Visibility mode)
0236 {
0237     KWayland::Client::PlasmaShellSurface *surface = qobject_cast<KWayland::Client::PlasmaShellSurface *>(view);
0238     Latte::View *latteView = qobject_cast<Latte::View *>(view);
0239     Latte::ViewPart::SubConfigView *configView = qobject_cast<Latte::ViewPart::SubConfigView *>(view);
0240 
0241     WindowId winId;
0242 
0243     if (latteView) {
0244         surface = latteView->surface();
0245         winId = latteView->positioner()->trackedWindowId();
0246     } else if (configView) {
0247         surface = configView->surface();
0248         winId = configView->trackedWindowId();
0249     }
0250 
0251     if (!surface) {
0252         return;
0253     }
0254 
0255     surface->setSkipTaskbar(true);
0256     surface->setSkipSwitcher(true);
0257 
0258     bool atBottom{!isPanelWindow && (mode == Latte::Types::WindowsCanCover || mode == Latte::Types::WindowsAlwaysCover)};
0259 
0260     if (isPanelWindow) {
0261         surface->setRole(PlasmaShellSurface::Role::Panel);
0262         surface->setPanelBehavior(PlasmaShellSurface::PanelBehavior::AutoHide);
0263     } else {
0264         surface->setRole(PlasmaShellSurface::Role::Normal);
0265     }
0266 
0267     if (latteView || configView) {
0268         auto w = windowFor(winId);
0269         if (w && !w->isOnAllDesktops()) {
0270             requestToggleIsOnAllDesktops(winId);
0271         }
0272 
0273         //! Layer to be applied
0274         if (mode == Latte::Types::WindowsCanCover || mode == Latte::Types::WindowsAlwaysCover) {
0275             setKeepBelow(winId, true);
0276         } else if (mode == Latte::Types::NormalWindow) {
0277             setKeepBelow(winId, false);
0278             setKeepAbove(winId, false);
0279         } else {
0280             setKeepAbove(winId, true);
0281         }
0282     }
0283 
0284     if (atBottom){
0285         //! trying to workaround WM behavior in order
0286         //!  1. View at the end MUST NOT HAVE FOCUSABILITY (issue example: clicking a single active task is not minimized)
0287         //!  2. View at the end MUST BE AT THE BOTTOM of windows stack
0288 
0289         QTimer::singleShot(50, [this, surface]() {
0290             surface->setRole(PlasmaShellSurface::Role::ToolTip);
0291         });
0292     }
0293 }
0294 
0295 void WaylandInterface::setViewStruts(QWindow &view, const QRect &rect, Plasma::Types::Location location)
0296 {
0297     if (!m_ghostWindows.contains(view.winId())) {
0298         m_ghostWindows[view.winId()] = new Private::GhostWindow(this);
0299     }
0300 
0301     auto w = m_ghostWindows[view.winId()];
0302 
0303     switch (location) {
0304     case Plasma::Types::TopEdge:
0305     case Plasma::Types::BottomEdge:
0306         w->setGeometry({rect.x() + rect.width() / 2, rect.y(), 1, rect.height()});
0307         break;
0308 
0309     case Plasma::Types::LeftEdge:
0310     case Plasma::Types::RightEdge:
0311         w->setGeometry({rect.x(), rect.y() + rect.height() / 2, rect.width(), 1});
0312         break;
0313 
0314     default:
0315         break;
0316     }
0317 }
0318 
0319 void WaylandInterface::switchToNextVirtualDesktop()
0320 {
0321     if (!m_virtualDesktopManagement || m_desktops.count() <= 1) {
0322         return;
0323     }
0324 
0325     int curPos = m_desktops.indexOf(m_currentDesktop);
0326     int nextPos = curPos + 1;
0327 
0328     if (curPos >= m_desktops.count()-1) {
0329         if (isVirtualDesktopNavigationWrappingAround()) {
0330             nextPos = 0;
0331         } else {
0332             return;
0333         }
0334     }
0335 
0336     KWayland::Client::PlasmaVirtualDesktop *desktopObj = m_virtualDesktopManagement->getVirtualDesktop(m_desktops[nextPos]);
0337 
0338     if (desktopObj) {
0339         desktopObj->requestActivate();
0340     }
0341 }
0342 
0343 void WaylandInterface::switchToPreviousVirtualDesktop()
0344 {
0345     if (!m_virtualDesktopManagement || m_desktops.count() <= 1) {
0346         return;
0347     }
0348 
0349     int curPos = m_desktops.indexOf(m_currentDesktop);
0350     int nextPos = curPos - 1;
0351 
0352     if (curPos <= 0) {
0353         if (isVirtualDesktopNavigationWrappingAround()) {
0354             nextPos = m_desktops.count()-1;
0355         } else {
0356             return;
0357         }
0358     }
0359 
0360     KWayland::Client::PlasmaVirtualDesktop *desktopObj = m_virtualDesktopManagement->getVirtualDesktop(m_desktops[nextPos]);
0361 
0362     if (desktopObj) {
0363         desktopObj->requestActivate();
0364     }
0365 }
0366 
0367 void WaylandInterface::setWindowOnActivities(const WindowId &wid, const QStringList &nextactivities)
0368 {
0369     auto winfo = requestInfo(wid);
0370     auto w = windowFor(wid);
0371 
0372     if (!w) {
0373         return;
0374     }
0375 
0376     QStringList curactivities = winfo.activities();
0377 
0378     if (!winfo.isOnAllActivities() && nextactivities.isEmpty()) {
0379         //! window must be set to all activities
0380         for(int i=0; i<curactivities.count(); ++i) {
0381             w->requestLeaveActivity(curactivities[i]);
0382         }
0383     } else if (curactivities != nextactivities) {
0384         QStringList requestenter;
0385         QStringList requestleave;
0386 
0387         for (int i=0; i<nextactivities.count(); ++i) {
0388             if (!curactivities.contains(nextactivities[i])) {
0389                 requestenter << nextactivities[i];
0390             }
0391         }
0392 
0393         for (int i=0; i<curactivities.count(); ++i) {
0394             if (!nextactivities.contains(curactivities[i])) {
0395                 requestleave << curactivities[i];
0396             }
0397         }
0398 
0399         //! leave afterwards from deprecated activities
0400         for (int i=0; i<requestleave.count(); ++i) {
0401             w->requestLeaveActivity(requestleave[i]);
0402         }
0403 
0404         //! first enter to new activities
0405         for (int i=0; i<requestenter.count(); ++i) {
0406             w->requestEnterActivity(requestenter[i]);
0407         }
0408     }
0409 }
0410 
0411 void WaylandInterface::removeViewStruts(QWindow &view)
0412 {
0413     delete m_ghostWindows.take(view.winId());
0414 }
0415 
0416 WindowId WaylandInterface::activeWindow()
0417 {
0418     if (!m_windowManagement) {
0419         return 0;
0420     }
0421 
0422     auto wid = m_windowManagement->activeWindow();
0423 
0424     return wid ? wid->internalId() : 0;
0425 }
0426 
0427 void WaylandInterface::skipTaskBar(const QDialog &dialog)
0428 {
0429     KWindowSystem::setState(dialog.winId(), NET::SkipTaskbar);
0430 }
0431 
0432 void WaylandInterface::slideWindow(QWindow &view, AbstractWindowInterface::Slide location)
0433 {
0434     auto slideLocation = KWindowEffects::NoEdge;
0435 
0436     switch (location) {
0437     case Slide::Top:
0438         slideLocation = KWindowEffects::TopEdge;
0439         break;
0440 
0441     case Slide::Bottom:
0442         slideLocation = KWindowEffects::BottomEdge;
0443         break;
0444 
0445     case Slide::Left:
0446         slideLocation = KWindowEffects::LeftEdge;
0447         break;
0448 
0449     case Slide::Right:
0450         slideLocation = KWindowEffects::RightEdge;
0451         break;
0452 
0453     default:
0454         break;
0455     }
0456 
0457     KWindowEffects::slideWindow(view.winId(), slideLocation, -1);
0458 }
0459 
0460 void WaylandInterface::enableBlurBehind(QWindow &view)
0461 {
0462     KWindowEffects::enableBlurBehind(view.winId());
0463 }
0464 
0465 void WaylandInterface::setActiveEdge(QWindow *view, bool active)
0466 {
0467     ViewPart::ScreenEdgeGhostWindow *window = qobject_cast<ViewPart::ScreenEdgeGhostWindow *>(view);
0468 
0469     if (!window) {
0470         return;
0471     }
0472 
0473     if (window->parentView()->surface() && window->parentView()->visibility()
0474             && (window->parentView()->visibility()->mode() == Types::DodgeActive
0475                 || window->parentView()->visibility()->mode() == Types::DodgeMaximized
0476                 || window->parentView()->visibility()->mode() == Types::DodgeAllWindows
0477                 || window->parentView()->visibility()->mode() == Types::AutoHide)) {
0478         if (active) {
0479             window->showWithMask();
0480             window->surface()->requestHideAutoHidingPanel();
0481         } else {
0482             window->hideWithMask();
0483             window->surface()->requestShowAutoHidingPanel();
0484         }
0485     }
0486 }
0487 
0488 void WaylandInterface::setFrameExtents(QWindow *view, const QMargins &extents)
0489 {
0490     //! do nothing until there is a wayland way to provide this
0491 }
0492 
0493 void WaylandInterface::setInputMask(QWindow *window, const QRect &rect)
0494 {
0495     //! do nothins, QWindow::mask() is sufficient enough in order to define Window input mask
0496 }
0497 
0498 WindowInfoWrap WaylandInterface::requestInfoActive()
0499 {
0500     if (!m_windowManagement) {
0501         return {};
0502     }
0503 
0504     auto w = m_windowManagement->activeWindow();
0505 
0506     if (!w) return {};
0507 
0508     return requestInfo(w->internalId());
0509 }
0510 
0511 WindowInfoWrap WaylandInterface::requestInfo(WindowId wid)
0512 {
0513     WindowInfoWrap winfoWrap;
0514 
0515     auto w = windowFor(wid);
0516 
0517     //!used to track Plasma DesktopView windows because during startup can not be identified properly
0518     bool plasmaBlockedWindow = w && (w->appId() == QLatin1String("org.kde.plasmashell")) && !isAcceptableWindow(w);
0519 
0520     if (w) {
0521         winfoWrap.setIsValid(isValidWindow(w) && !plasmaBlockedWindow);
0522         winfoWrap.setWid(wid);
0523         winfoWrap.setParentId(w->parentWindow() ? w->parentWindow()->internalId() : 0);
0524         winfoWrap.setIsActive(w->isActive());
0525         winfoWrap.setIsMinimized(w->isMinimized());
0526         winfoWrap.setIsMaxVert(w->isMaximized());
0527         winfoWrap.setIsMaxHoriz(w->isMaximized());
0528         winfoWrap.setIsFullscreen(w->isFullscreen());
0529         winfoWrap.setIsShaded(w->isShaded());
0530         winfoWrap.setIsOnAllDesktops(w->isOnAllDesktops());
0531         winfoWrap.setIsOnAllActivities(w->plasmaActivities().isEmpty());
0532         winfoWrap.setIsKeepAbove(w->isKeepAbove());
0533         winfoWrap.setIsKeepBelow(w->isKeepBelow());
0534         winfoWrap.setGeometry(w->geometry());
0535         winfoWrap.setHasSkipSwitcher(w->skipSwitcher());
0536         winfoWrap.setHasSkipTaskbar(w->skipTaskbar());
0537 
0538         //! BEGIN:Window Abilities
0539         winfoWrap.setIsClosable(w->isCloseable());
0540         winfoWrap.setIsFullScreenable(w->isFullscreenable());
0541         winfoWrap.setIsMaximizable(w->isMaximizeable());
0542         winfoWrap.setIsMinimizable(w->isMinimizeable());
0543         winfoWrap.setIsMovable(w->isMovable());
0544         winfoWrap.setIsResizable(w->isResizable());
0545         winfoWrap.setIsShadeable(w->isShadeable());
0546         winfoWrap.setIsVirtualDesktopsChangeable(w->isVirtualDesktopChangeable());
0547         //! END:Window Abilities
0548 
0549         winfoWrap.setDisplay(w->title());
0550         winfoWrap.setDesktops(w->plasmaVirtualDesktops());
0551         winfoWrap.setActivities(w->plasmaActivities());
0552 
0553     } else {
0554         winfoWrap.setIsValid(false);
0555     }
0556 
0557     if (plasmaBlockedWindow) {
0558         windowRemoved(w->internalId());
0559     }
0560 
0561     return winfoWrap;
0562 }
0563 
0564 AppData WaylandInterface::appDataFor(WindowId wid)
0565 {
0566     auto window = windowFor(wid);
0567 
0568     if (window) {
0569         const AppData &data = appDataFromUrl(windowUrlFromMetadata(window->appId(),
0570                                                                    window->pid(), rulesConfig));
0571 
0572         return data;
0573     }
0574 
0575     AppData empty;
0576 
0577     return empty;
0578 }
0579 
0580 KWayland::Client::PlasmaWindow *WaylandInterface::windowFor(WindowId wid)
0581 {
0582     auto it = std::find_if(m_windowManagement->windows().constBegin(), m_windowManagement->windows().constEnd(), [&wid](PlasmaWindow * w) noexcept {
0583             return w->isValid() && w->internalId() == wid;
0584 });
0585 
0586     if (it == m_windowManagement->windows().constEnd()) {
0587         return nullptr;
0588     }
0589 
0590     return *it;
0591 }
0592 
0593 QIcon WaylandInterface::iconFor(WindowId wid)
0594 {
0595     auto window = windowFor(wid);
0596 
0597     if (window) {
0598         return window->icon();
0599     }
0600 
0601 
0602     return QIcon();
0603 }
0604 
0605 WindowId WaylandInterface::winIdFor(QString appId, QString title)
0606 {
0607     auto it = std::find_if(m_windowManagement->windows().constBegin(), m_windowManagement->windows().constEnd(), [&appId, &title](PlasmaWindow * w) noexcept {
0608         return w->isValid() && w->appId() == appId && w->title().startsWith(title);
0609     });
0610 
0611     if (it == m_windowManagement->windows().constEnd()) {
0612         return QVariant();
0613     }
0614 
0615     return (*it)->internalId();
0616 }
0617 
0618 WindowId WaylandInterface::winIdFor(QString appId, QRect geometry)
0619 {
0620     auto it = std::find_if(m_windowManagement->windows().constBegin(), m_windowManagement->windows().constEnd(), [&appId, &geometry](PlasmaWindow * w) noexcept {
0621         return w->isValid() && w->appId() == appId && w->geometry() == geometry;
0622     });
0623 
0624     if (it == m_windowManagement->windows().constEnd()) {
0625         return QVariant();
0626     }
0627 
0628     return (*it)->internalId();
0629 }
0630 
0631 bool WaylandInterface::windowCanBeDragged(WindowId wid)
0632 {
0633     auto w = windowFor(wid);
0634 
0635     if (w && isValidWindow(w)) {
0636         WindowInfoWrap winfo = requestInfo(wid);
0637         return (winfo.isValid()
0638                 && w->isMovable()
0639                 && !winfo.isMinimized()
0640                 && inCurrentDesktopActivity(winfo));
0641     }
0642 
0643     return false;
0644 }
0645 
0646 bool WaylandInterface::windowCanBeMaximized(WindowId wid)
0647 {
0648     auto w = windowFor(wid);
0649 
0650     if (w && isValidWindow(w)) {
0651         WindowInfoWrap winfo = requestInfo(wid);
0652         return (winfo.isValid()
0653                 && w->isMaximizeable()
0654                 && !winfo.isMinimized()
0655                 && inCurrentDesktopActivity(winfo));
0656     }
0657 
0658     return false;
0659 }
0660 
0661 void WaylandInterface::requestActivate(WindowId wid)
0662 {
0663     auto w = windowFor(wid);
0664 
0665     if (w) {
0666         w->requestActivate();
0667     }
0668 }
0669 
0670 void WaylandInterface::requestClose(WindowId wid)
0671 {
0672     auto w = windowFor(wid);
0673 
0674     if (w) {
0675         w->requestClose();
0676     }
0677 }
0678 
0679 
0680 void WaylandInterface::requestMoveWindow(WindowId wid, QPoint from)
0681 {
0682     WindowInfoWrap wInfo = requestInfo(wid);
0683 
0684     if (windowCanBeDragged(wid) && inCurrentDesktopActivity(wInfo)) {
0685         auto w = windowFor(wid);
0686 
0687         if (w && isValidWindow(w)) {
0688             w->requestMove();
0689         }
0690     }
0691 }
0692 
0693 void WaylandInterface::requestToggleIsOnAllDesktops(WindowId wid)
0694 {
0695     auto w = windowFor(wid);
0696 
0697     if (w && isValidWindow(w) && m_desktops.count() > 1) {
0698         if (w->isOnAllDesktops()) {
0699             w->requestEnterVirtualDesktop(m_currentDesktop);
0700         } else {
0701             const QStringList &now = w->plasmaVirtualDesktops();
0702 
0703             foreach (const QString &desktop, now) {
0704                 w->requestLeaveVirtualDesktop(desktop);
0705             }
0706         }
0707     }
0708 }
0709 
0710 void WaylandInterface::requestToggleKeepAbove(WindowId wid)
0711 {
0712     auto w = windowFor(wid);
0713 
0714     if (w) {
0715         w->requestToggleKeepAbove();
0716     }
0717 }
0718 
0719 void WaylandInterface::setKeepAbove(WindowId wid, bool active)
0720 {
0721     auto w = windowFor(wid);
0722 
0723     if (w) {
0724         if (active) {
0725             setKeepBelow(wid, false);
0726         }
0727 
0728         if ((w->isKeepAbove() && active) || (!w->isKeepAbove() && !active)) {
0729             return;
0730         }
0731 
0732         w->requestToggleKeepAbove();
0733     }
0734 }
0735 
0736 void WaylandInterface::setKeepBelow(WindowId wid, bool active)
0737 {
0738     auto w = windowFor(wid);
0739 
0740     if (w) {
0741         if (active) {
0742             setKeepAbove(wid, false);
0743         }
0744 
0745         if ((w->isKeepBelow() && active) || (!w->isKeepBelow() && !active)) {
0746             return;
0747         }
0748 
0749         w->requestToggleKeepBelow();
0750     }
0751 }
0752 
0753 void WaylandInterface::requestToggleMinimized(WindowId wid)
0754 {
0755     auto w = windowFor(wid);
0756     WindowInfoWrap wInfo = requestInfo(wid);
0757 
0758     if (w && isValidWindow(w) && inCurrentDesktopActivity(wInfo)) {
0759         if (!m_currentDesktop.isEmpty()) {
0760             w->requestEnterVirtualDesktop(m_currentDesktop);
0761         }
0762         w->requestToggleMinimized();
0763     }
0764 }
0765 
0766 void WaylandInterface::requestToggleMaximized(WindowId wid)
0767 {
0768     auto w = windowFor(wid);
0769     WindowInfoWrap wInfo = requestInfo(wid);
0770 
0771     if (w && isValidWindow(w) && windowCanBeMaximized(wid) && inCurrentDesktopActivity(wInfo)) {
0772         if (!m_currentDesktop.isEmpty()) {
0773             w->requestEnterVirtualDesktop(m_currentDesktop);
0774         }
0775         w->requestToggleMaximized();
0776     }
0777 }
0778 
0779 bool WaylandInterface::isPlasmaPanel(const KWayland::Client::PlasmaWindow *w) const
0780 {
0781     if (!w || (w->appId() != QLatin1String("org.kde.plasmashell"))) {
0782         return false;
0783     }
0784 
0785     return AbstractWindowInterface::isPlasmaPanel(w->geometry());
0786 }
0787 
0788 bool WaylandInterface::isFullScreenWindow(const KWayland::Client::PlasmaWindow *w) const
0789 {
0790     if (!w) {
0791         return false;
0792     }
0793 
0794     return w->isFullscreen() || AbstractWindowInterface::isFullScreenWindow(w->geometry());
0795 }
0796 
0797 bool WaylandInterface::isSidepanel(const KWayland::Client::PlasmaWindow *w) const
0798 {
0799     if (!w) {
0800         return false;
0801     }
0802 
0803     return AbstractWindowInterface::isSidepanel(w->geometry());
0804 }
0805 
0806 bool WaylandInterface::isValidWindow(const KWayland::Client::PlasmaWindow *w)
0807 {
0808     if (!w || !w->isValid()) {
0809         return false;
0810     }
0811 
0812     if (windowsTracker()->isValidFor(w->internalId())) {
0813         return true;
0814     }
0815 
0816     return isAcceptableWindow(w);
0817 }
0818 
0819 bool WaylandInterface::isAcceptableWindow(const KWayland::Client::PlasmaWindow *w)
0820 {
0821     if (!w || !w->isValid()) {
0822         return false;
0823     }
0824 
0825     //! ignored windows that are not tracked
0826     if (hasBlockedTracking(w->internalId())) {
0827         return false;
0828     }
0829 
0830     //! whitelisted/approved windows
0831     if (isWhitelistedWindow(w->internalId())) {
0832         return true;
0833     }
0834 
0835     //! Window Checks
0836     bool hasSkipTaskbar = w->skipTaskbar();
0837     bool isSkipped = hasSkipTaskbar;
0838     bool hasSkipSwitcher = w->skipSwitcher();
0839     isSkipped = hasSkipTaskbar && hasSkipSwitcher;
0840 
0841     if (isSkipped
0842             && ((w->appId() == QLatin1String("yakuake")
0843                  || (w->appId() == QLatin1String("krunner"))) )) {
0844         registerWhitelistedWindow(w->internalId());
0845     } else if (w->appId() == QLatin1String("org.kde.plasmashell")) {
0846         if (isSkipped && isSidepanel(w)) {
0847             registerWhitelistedWindow(w->internalId());
0848             return true;
0849         } else if (isPlasmaPanel(w) || isFullScreenWindow(w)) {
0850             registerPlasmaIgnoredWindow(w->internalId());
0851             return false;
0852         }
0853     } else if ((w->appId() == QLatin1String("latte-dock"))
0854                || (w->appId().startsWith(QLatin1String("ksmserver")))) {
0855         if (isFullScreenWindow(w)) {
0856             registerIgnoredWindow(w->internalId());
0857             return false;
0858         }
0859     }
0860 
0861     return !isSkipped;
0862 }
0863 
0864 void WaylandInterface::updateWindow()
0865 {
0866     PlasmaWindow *pW = qobject_cast<PlasmaWindow*>(QObject::sender());
0867 
0868     if (isValidWindow(pW)) {
0869         considerWindowChanged(pW->internalId());
0870     }
0871 }
0872 
0873 void WaylandInterface::windowUnmapped()
0874 {
0875     PlasmaWindow *pW = qobject_cast<PlasmaWindow*>(QObject::sender());
0876 
0877     if (pW) {
0878         untrackWindow(pW);
0879         emit windowRemoved(pW->internalId());
0880     }
0881 }
0882 
0883 void WaylandInterface::trackWindow(KWayland::Client::PlasmaWindow *w)
0884 {
0885     if (!w) {
0886         return;
0887     }
0888 
0889     connect(w, &PlasmaWindow::activeChanged, this, &WaylandInterface::updateWindow);
0890     connect(w, &PlasmaWindow::titleChanged, this, &WaylandInterface::updateWindow);
0891     connect(w, &PlasmaWindow::fullscreenChanged, this, &WaylandInterface::updateWindow);
0892     connect(w, &PlasmaWindow::geometryChanged, this, &WaylandInterface::updateWindow);
0893     connect(w, &PlasmaWindow::maximizedChanged, this, &WaylandInterface::updateWindow);
0894     connect(w, &PlasmaWindow::minimizedChanged, this, &WaylandInterface::updateWindow);
0895     connect(w, &PlasmaWindow::shadedChanged, this, &WaylandInterface::updateWindow);
0896     connect(w, &PlasmaWindow::skipTaskbarChanged, this, &WaylandInterface::updateWindow);
0897     connect(w, &PlasmaWindow::onAllDesktopsChanged, this, &WaylandInterface::updateWindow);
0898     connect(w, &PlasmaWindow::parentWindowChanged, this, &WaylandInterface::updateWindow);
0899     connect(w, &PlasmaWindow::plasmaVirtualDesktopEntered, this, &WaylandInterface::updateWindow);
0900     connect(w, &PlasmaWindow::plasmaVirtualDesktopLeft, this, &WaylandInterface::updateWindow);
0901     connect(w, &PlasmaWindow::plasmaActivityEntered, this, &WaylandInterface::updateWindow);
0902     connect(w, &PlasmaWindow::plasmaActivityLeft, this, &WaylandInterface::updateWindow);
0903     connect(w, &PlasmaWindow::unmapped, this, &WaylandInterface::windowUnmapped);
0904 }
0905 
0906 void WaylandInterface::untrackWindow(KWayland::Client::PlasmaWindow *w)
0907 {
0908     if (!w) {
0909         return;
0910     }
0911 
0912     disconnect(w, &PlasmaWindow::activeChanged, this, &WaylandInterface::updateWindow);
0913     disconnect(w, &PlasmaWindow::titleChanged, this, &WaylandInterface::updateWindow);
0914     disconnect(w, &PlasmaWindow::fullscreenChanged, this, &WaylandInterface::updateWindow);
0915     disconnect(w, &PlasmaWindow::geometryChanged, this, &WaylandInterface::updateWindow);
0916     disconnect(w, &PlasmaWindow::maximizedChanged, this, &WaylandInterface::updateWindow);
0917     disconnect(w, &PlasmaWindow::minimizedChanged, this, &WaylandInterface::updateWindow);
0918     disconnect(w, &PlasmaWindow::shadedChanged, this, &WaylandInterface::updateWindow);
0919     disconnect(w, &PlasmaWindow::skipTaskbarChanged, this, &WaylandInterface::updateWindow);
0920     disconnect(w, &PlasmaWindow::onAllDesktopsChanged, this, &WaylandInterface::updateWindow);
0921     disconnect(w, &PlasmaWindow::parentWindowChanged, this, &WaylandInterface::updateWindow);
0922     disconnect(w, &PlasmaWindow::plasmaVirtualDesktopEntered, this, &WaylandInterface::updateWindow);
0923     disconnect(w, &PlasmaWindow::plasmaVirtualDesktopLeft, this, &WaylandInterface::updateWindow);
0924     disconnect(w, &PlasmaWindow::plasmaActivityEntered, this, &WaylandInterface::updateWindow);
0925     disconnect(w, &PlasmaWindow::plasmaActivityLeft, this, &WaylandInterface::updateWindow);
0926     disconnect(w, &PlasmaWindow::unmapped, this, &WaylandInterface::windowUnmapped);
0927 }
0928 
0929 
0930 void WaylandInterface::windowCreatedProxy(KWayland::Client::PlasmaWindow *w)
0931 {
0932     if (!isAcceptableWindow(w))  {
0933         return;
0934     }
0935 
0936     trackWindow(w);
0937     emit windowAdded(w->internalId());
0938 
0939     if (w->appId() == QLatin1String("latte-dock")) {
0940         emit latteWindowAdded();
0941     }
0942 }
0943 
0944 }
0945 }
0946 
0947 #include "waylandinterface.moc"