File indexing completed on 2024-04-14 15:37:26

0001 /*
0002 *  Copyright 2016  Smith AR <audoban@openmailbox.org>
0003 *                  Michail Vourlakos <mvourlakos@gmail.com>
0004 *
0005 *  This file is part of Latte-Dock
0006 *
0007 *  Latte-Dock is free software; you can redistribute it and/or
0008 *  modify it under the terms of the GNU General Public License as
0009 *  published by the Free Software Foundation; either version 2 of
0010 *  the License, or (at your option) any later version.
0011 *
0012 *  Latte-Dock is distributed in the hope that it will be useful,
0013 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0014 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0015 *  GNU General Public License for more details.
0016 *
0017 *  You should have received a copy of the GNU General Public License
0018 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
0019 */
0020 
0021 #include "xwindowinterface.h"
0022 
0023 // local
0024 #include "tasktools.h"
0025 #include "view/screenedgeghostwindow.h"
0026 #include "view/view.h"
0027 #include "../liblatte2/extras.h"
0028 
0029 // Qt
0030 #include <QDebug>
0031 #include <QTimer>
0032 #include <QtX11Extras/QX11Info>
0033 
0034 // KDE
0035 #include <KDesktopFile>
0036 #include <KWindowSystem>
0037 #include <KWindowInfo>
0038 #include <KIconThemes/KIconLoader>
0039 
0040 // X11
0041 #include <NETWM>
0042 #include <xcb/xcb.h>
0043 
0044 namespace Latte {
0045 namespace WindowSystem {
0046 
0047 XWindowInterface::XWindowInterface(QObject *parent)
0048     : AbstractWindowInterface(parent)
0049 {
0050     m_currentDesktop = QString(KWindowSystem::self()->currentDesktop());
0051 
0052     connect(KWindowSystem::self(), &KWindowSystem::activeWindowChanged, this, &AbstractWindowInterface::activeWindowChanged);
0053     connect(KWindowSystem::self(), &KWindowSystem::windowAdded, this, &AbstractWindowInterface::windowAdded);
0054     connect(KWindowSystem::self(), &KWindowSystem::windowRemoved, this, &AbstractWindowInterface::windowRemoved);
0055 
0056     connect(KWindowSystem::self(), &KWindowSystem::currentDesktopChanged, this, [&](int desktop) {
0057         m_currentDesktop = QString(desktop);
0058         emit currentDesktopChanged();
0059     });
0060 
0061     connect(KWindowSystem::self()
0062             , static_cast<void (KWindowSystem::*)(WId, NET::Properties, NET::Properties2)>
0063             (&KWindowSystem::windowChanged)
0064             , this, &XWindowInterface::windowChangedProxy);
0065 
0066 
0067     for(auto wid : KWindowSystem::self()->windows()) {
0068         emit windowAdded(wid);
0069         windowChangedProxy(wid,0,0);
0070     }
0071 }
0072 
0073 XWindowInterface::~XWindowInterface()
0074 {
0075 }
0076 
0077 void XWindowInterface::setViewExtraFlags(QWindow &view)
0078 {
0079     NETWinInfo winfo(QX11Info::connection()
0080                      , static_cast<xcb_window_t>(view.winId())
0081                      , static_cast<xcb_window_t>(view.winId())
0082                      , 0, 0);
0083 
0084     winfo.setAllowedActions(NET::ActionChangeDesktop);
0085     KWindowSystem::setType(view.winId(), NET::Dock);
0086     KWindowSystem::setState(view.winId(), NET::SkipTaskbar | NET::SkipPager);
0087     KWindowSystem::setOnAllDesktops(view.winId(), true);
0088 }
0089 
0090 void XWindowInterface::setViewStruts(QWindow &view, const QRect &rect
0091                                      , Plasma::Types::Location location)
0092 {
0093     NETExtendedStrut strut;
0094 
0095     const auto screen = view.screen();
0096 
0097     const QRect currentScreen {screen->geometry()};
0098     const QRect wholeScreen {{0, 0}, screen->virtualSize()};
0099 
0100     switch (location) {
0101     case Plasma::Types::TopEdge: {
0102         const int topOffset {screen->geometry().top()};
0103         strut.top_width = rect.height() + topOffset;
0104         strut.top_start = rect.x();
0105         strut.top_end = rect.x() + rect.width() - 1;
0106         break;
0107     }
0108 
0109     case Plasma::Types::BottomEdge: {
0110         const int bottomOffset {wholeScreen.bottom() - currentScreen.bottom()};
0111         strut.bottom_width = rect.height() + bottomOffset;
0112         strut.bottom_start = rect.x();
0113         strut.bottom_end = rect.x() + rect.width() - 1;
0114         break;
0115     }
0116 
0117     case Plasma::Types::LeftEdge: {
0118         const int leftOffset = {screen->geometry().left()};
0119         strut.left_width = rect.width() + leftOffset;
0120         strut.left_start = rect.y();
0121         strut.left_end = rect.y() + rect.height() - 1;
0122         break;
0123     }
0124 
0125     case Plasma::Types::RightEdge: {
0126         const int rightOffset = {wholeScreen.right() - currentScreen.right()};
0127         strut.right_width = rect.width() + rightOffset;
0128         strut.right_start = rect.y();
0129         strut.right_end = rect.y() + rect.height() - 1;
0130         break;
0131     }
0132 
0133     default:
0134         qWarning() << "wrong location:" << qEnumToStr(location);
0135         return;
0136     }
0137 
0138     KWindowSystem::setExtendedStrut(view.winId(),
0139                                     strut.left_width,   strut.left_start,   strut.left_end,
0140                                     strut.right_width,  strut.right_start,  strut.right_end,
0141                                     strut.top_width,    strut.top_start,    strut.top_end,
0142                                     strut.bottom_width, strut.bottom_start, strut.bottom_end
0143                                     );
0144 }
0145 
0146 void XWindowInterface::switchToNextVirtualDesktop() const
0147 {
0148     int desktops = KWindowSystem::numberOfDesktops();
0149 
0150     if (desktops <= 1) {
0151         return;
0152     }
0153 
0154     int curPos = KWindowSystem::currentDesktop();
0155     int nextPos = curPos + 1;
0156 
0157     if (curPos == desktops) {
0158         nextPos = 1;
0159     }
0160 
0161     KWindowSystem::setCurrentDesktop(nextPos);
0162 }
0163 
0164 void XWindowInterface::switchToPreviousVirtualDesktop() const
0165 {
0166     int desktops = KWindowSystem::numberOfDesktops();
0167     if (desktops <= 1) {
0168         return;
0169     }
0170 
0171     int curPos = KWindowSystem::currentDesktop();
0172     int nextPos = curPos - 1;
0173 
0174     if (curPos == 1) {
0175         nextPos = desktops;
0176     }
0177 
0178     KWindowSystem::setCurrentDesktop(nextPos);
0179 }
0180 
0181 void XWindowInterface::setWindowOnActivities(QWindow &window, const QStringList &activities)
0182 {
0183     KWindowSystem::setOnActivities(window.winId(), activities);
0184 }
0185 
0186 void XWindowInterface::removeViewStruts(QWindow &view) const
0187 {
0188     KWindowSystem::setStrut(view.winId(), 0, 0, 0, 0);
0189 }
0190 
0191 WindowId XWindowInterface::activeWindow() const
0192 {
0193     return KWindowSystem::self()->activeWindow();
0194 }
0195 
0196 void XWindowInterface::setKeepAbove(const QDialog &dialog, bool above) const
0197 {
0198     if (above) {
0199         KWindowSystem::setState(dialog.winId(), NET::KeepAbove);
0200     } else {
0201         KWindowSystem::clearState(dialog.winId(), NET::KeepAbove);
0202     }
0203 }
0204 
0205 void XWindowInterface::skipTaskBar(const QDialog &dialog) const
0206 {
0207     KWindowSystem::setState(dialog.winId(), NET::SkipTaskbar);
0208 }
0209 
0210 void XWindowInterface::slideWindow(QWindow &view, AbstractWindowInterface::Slide location) const
0211 {
0212     auto slideLocation = KWindowEffects::NoEdge;
0213 
0214     switch (location) {
0215     case Slide::Top:
0216         slideLocation = KWindowEffects::TopEdge;
0217         break;
0218 
0219     case Slide::Bottom:
0220         slideLocation = KWindowEffects::BottomEdge;
0221         break;
0222 
0223     case Slide::Left:
0224         slideLocation = KWindowEffects::LeftEdge;
0225         break;
0226 
0227     case Slide::Right:
0228         slideLocation = KWindowEffects::RightEdge;
0229         break;
0230 
0231     default:
0232         break;
0233     }
0234 
0235     KWindowEffects::slideWindow(view.winId(), slideLocation, -1);
0236 }
0237 
0238 void XWindowInterface::enableBlurBehind(QWindow &view) const
0239 {
0240     KWindowEffects::enableBlurBehind(view.winId());
0241 }
0242 
0243 void XWindowInterface::setActiveEdge(QWindow *view, bool active) const
0244 {
0245     ViewPart::ScreenEdgeGhostWindow *window = qobject_cast<ViewPart::ScreenEdgeGhostWindow *>(view);
0246 
0247     if (!window) {
0248         return;
0249     }
0250 
0251     xcb_connection_t *c = QX11Info::connection();
0252 
0253     const QByteArray effectName = QByteArrayLiteral("_KDE_NET_WM_SCREEN_EDGE_SHOW");
0254     xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom_unchecked(c, false, effectName.length(), effectName.constData());
0255 
0256     QScopedPointer<xcb_intern_atom_reply_t, QScopedPointerPodDeleter> atom(xcb_intern_atom_reply(c, atomCookie, nullptr));
0257 
0258     if (!atom) {
0259         return;
0260     }
0261 
0262     if (!active) {
0263         xcb_delete_property(c, window->winId(), atom->atom);
0264         window->hideWithMask();
0265         return;
0266     }
0267 
0268     window->showWithMask();
0269 
0270     uint32_t value = 0;
0271 
0272     switch (window->location()) {
0273     case Plasma::Types::TopEdge:
0274         value = 0;
0275         break;
0276 
0277     case Plasma::Types::RightEdge:
0278         value = 1;
0279         break;
0280 
0281     case Plasma::Types::BottomEdge:
0282         value = 2;
0283         break;
0284 
0285     case Plasma::Types::LeftEdge:
0286         value = 3;
0287         break;
0288 
0289     case Plasma::Types::Floating:
0290     default:
0291         value = 4;
0292         break;
0293     }
0294 
0295     int hideType = 0;
0296 
0297     value |= hideType << 8;
0298 
0299     xcb_change_property(c, XCB_PROP_MODE_REPLACE, window->winId(), atom->atom, XCB_ATOM_CARDINAL, 32, 1, &value);
0300 }
0301 
0302 WindowInfoWrap XWindowInterface::requestInfoActive() const
0303 {
0304     return requestInfo(KWindowSystem::activeWindow());
0305 }
0306 
0307 WindowInfoWrap XWindowInterface::requestInfo(WindowId wid) const
0308 {
0309     const KWindowInfo winfo{wid.value<WId>(), NET::WMFrameExtents
0310                 | NET::WMWindowType
0311                 | NET::WMGeometry
0312                 | NET::WMDesktop
0313                 | NET::WMState
0314                 | NET::WMName
0315                 | NET::WMVisibleName,
0316                 NET::WM2WindowClass
0317                 | NET::WM2Activities
0318                 | NET::WM2AllowedActions
0319                 | NET::WM2TransientFor};
0320 
0321     //! update desktop id
0322 
0323     bool isDesktop{false};
0324     if (winfo.windowClassName() == "plasmashell" && isPlasmaDesktop(winfo.geometry())) {
0325         isDesktop = true;
0326         windowsTracker()->setPlasmaDesktop(wid);
0327     }
0328 
0329     WindowInfoWrap winfoWrap;
0330 
0331     if (!winfo.valid()) {
0332         winfoWrap.setIsValid(false);
0333     } else if (isValidWindow(winfo) && !isDesktop) {
0334         winfoWrap.setIsValid(true);
0335         winfoWrap.setWid(wid);
0336         winfoWrap.setParentId(winfo.transientFor());
0337         winfoWrap.setIsActive(KWindowSystem::activeWindow() == wid.value<WId>());
0338         winfoWrap.setIsMinimized(winfo.hasState(NET::Hidden));
0339         winfoWrap.setIsMaxVert(winfo.hasState(NET::MaxVert));
0340         winfoWrap.setIsMaxHoriz(winfo.hasState(NET::MaxHoriz));
0341         winfoWrap.setIsFullscreen(winfo.hasState(NET::FullScreen));
0342         winfoWrap.setIsShaded(winfo.hasState(NET::Shaded));
0343         winfoWrap.setIsOnAllDesktops(winfo.onAllDesktops());
0344         winfoWrap.setIsOnAllActivities(winfo.activities().empty());
0345         winfoWrap.setGeometry(winfo.frameGeometry());
0346         winfoWrap.setIsKeepAbove(winfo.hasState(NET::KeepAbove));
0347         winfoWrap.setHasSkipTaskbar(winfo.hasState(NET::SkipTaskbar));
0348 
0349         //! Window Abilities
0350         winfoWrap.setIsClosable(winfo.actionSupported(NET::ActionClose));
0351         winfoWrap.setIsFullScreenable(winfo.actionSupported(NET::ActionFullScreen));
0352         winfoWrap.setIsMaximizable(winfo.actionSupported(NET::ActionMax));
0353         winfoWrap.setIsMinimizable(winfo.actionSupported(NET::ActionMinimize));
0354         winfoWrap.setIsMovable(winfo.actionSupported(NET::ActionMove));
0355         winfoWrap.setIsResizable(winfo.actionSupported(NET::ActionResize));
0356         winfoWrap.setIsShadeable(winfo.actionSupported(NET::ActionShade));
0357         winfoWrap.setIsVirtualDesktopsChangeable(winfo.actionSupported(NET::ActionChangeDesktop));
0358         //! Window Abilities
0359 
0360         winfoWrap.setDisplay(winfo.visibleName());
0361         winfoWrap.setDesktops({QString(winfo.desktop())});
0362         winfoWrap.setActivities(winfo.activities());
0363     } else if (m_desktopId == wid) {
0364         winfoWrap.setIsValid(true);
0365         winfoWrap.setIsPlasmaDesktop(true);
0366         winfoWrap.setWid(wid);
0367         winfoWrap.setParentId(0);
0368         winfoWrap.setHasSkipTaskbar(true);
0369 
0370         //! Window Abilities
0371         winfoWrap.setIsClosable(false);
0372         winfoWrap.setIsFullScreenable(false);
0373         winfoWrap.setIsGroupable(false);
0374         winfoWrap.setIsMaximizable(false);
0375         winfoWrap.setIsMinimizable(false);
0376         winfoWrap.setIsMovable(false);
0377         winfoWrap.setIsResizable(false);
0378         winfoWrap.setIsShadeable(false);
0379         winfoWrap.setIsVirtualDesktopsChangeable(false);
0380         //! Window Abilities
0381     }
0382 
0383     return winfoWrap;
0384 }
0385 
0386 AppData XWindowInterface::appDataFor(WindowId wid) const
0387 {
0388     return appDataFromUrl(windowUrl(wid));
0389 }
0390 
0391 QUrl XWindowInterface::windowUrl(WindowId wid) const
0392 {
0393     const KWindowInfo info(wid.value<WId>(), 0, NET::WM2WindowClass | NET::WM2DesktopFileName);
0394 
0395     QString desktopFile = QString::fromUtf8(info.desktopFileName());
0396 
0397     if (!desktopFile.isEmpty()) {
0398         KService::Ptr service = KService::serviceByStorageId(desktopFile);
0399 
0400         if (service) {
0401             const QString &menuId = service->menuId();
0402 
0403             // applications: URLs are used to refer to applications by their KService::menuId
0404             // (i.e. .desktop file name) rather than the absolute path to a .desktop file.
0405             if (!menuId.isEmpty()) {
0406                 return QUrl(QStringLiteral("applications:") + menuId);
0407             }
0408 
0409             return QUrl::fromLocalFile(service->entryPath());
0410         }
0411 
0412         if (!desktopFile.endsWith(QLatin1String(".desktop"))) {
0413             desktopFile.append(QLatin1String(".desktop"));
0414         }
0415 
0416         if (KDesktopFile::isDesktopFile(desktopFile) && QFile::exists(desktopFile)) {
0417             return QUrl::fromLocalFile(desktopFile);
0418         }
0419     }
0420 
0421     return windowUrlFromMetadata(info.windowClassClass(),
0422                                  NETWinInfo(QX11Info::connection(), wid.value<WId>(), QX11Info::appRootWindow(), NET::WMPid, NET::Properties2()).pid(),
0423                                  rulesConfig, info.windowClassName());
0424 }
0425 
0426 
0427 bool XWindowInterface::windowCanBeDragged(WindowId wid) const
0428 {
0429     WindowInfoWrap winfo = requestInfo(wid);
0430     return (winfo.isValid()
0431             && !winfo.isMinimized()
0432             && winfo.isMovable()
0433             && inCurrentDesktopActivity(winfo)
0434             && !winfo.isPlasmaDesktop());
0435 }
0436 
0437 bool XWindowInterface::windowCanBeMaximized(WindowId wid) const
0438 {
0439 
0440     WindowInfoWrap winfo = requestInfo(wid);
0441     return (winfo.isValid()
0442             && !winfo.isMinimized()
0443             && winfo.isMaximizable()
0444             && inCurrentDesktopActivity(winfo)
0445             && !winfo.isPlasmaDesktop());
0446 }
0447 
0448 void XWindowInterface::requestActivate(WindowId wid) const
0449 {
0450     KWindowSystem::activateWindow(wid.toInt());
0451 }
0452 
0453 QIcon XWindowInterface::iconFor(WindowId wid) const
0454 {
0455     QIcon icon;
0456 
0457     icon.addPixmap(KWindowSystem::icon(wid.value<WId>(), KIconLoader::SizeSmall, KIconLoader::SizeSmall, false));
0458     icon.addPixmap(KWindowSystem::icon(wid.value<WId>(), KIconLoader::SizeSmallMedium, KIconLoader::SizeSmallMedium, false));
0459     icon.addPixmap(KWindowSystem::icon(wid.value<WId>(), KIconLoader::SizeMedium, KIconLoader::SizeMedium, false));
0460     icon.addPixmap(KWindowSystem::icon(wid.value<WId>(), KIconLoader::SizeLarge, KIconLoader::SizeLarge, false));
0461 
0462     return icon;
0463 }
0464 
0465 WindowId XWindowInterface::winIdFor(QString appId, QRect geometry) const
0466 {
0467     return activeWindow();
0468 }
0469 
0470 void XWindowInterface::requestClose(WindowId wid) const
0471 {
0472     WindowInfoWrap wInfo = requestInfo(wid);
0473 
0474     if (!wInfo.isValid() || wInfo.isPlasmaDesktop()) {
0475         return;
0476     }
0477 
0478     NETRootInfo ri(QX11Info::connection(), NET::CloseWindow);
0479     ri.closeWindowRequest(wInfo.wid().toUInt());
0480 }
0481 
0482 void XWindowInterface::requestMoveWindow(WindowId wid, QPoint from) const
0483 {
0484     WindowInfoWrap wInfo = requestInfo(wid);
0485 
0486     if (!wInfo.isValid() || wInfo.isPlasmaDesktop() || !inCurrentDesktopActivity(wInfo)) {
0487         return;
0488     }
0489 
0490     int borderX = wInfo.geometry().width() > 120 ? 60 : 10;
0491     int borderY{10};
0492 
0493     //! find min/max values for x,y based on active window geometry
0494     int minX = wInfo.geometry().x() + borderX;
0495     int maxX = wInfo.geometry().x() + wInfo.geometry().width() - borderX;
0496     int minY = wInfo.geometry().y() + borderY;
0497     int maxY = wInfo.geometry().y() + wInfo.geometry().height() - borderY;
0498 
0499     //! set the point from which this window will be moved,
0500     //! make sure that it is in window boundaries
0501     int validX = qBound(minX, from.x(), maxX);
0502     int validY = qBound(minY, from.y(), maxY);
0503 
0504     NETRootInfo ri(QX11Info::connection(), NET::WMMoveResize);
0505     ri.moveResizeRequest(wInfo.wid().toUInt(), validX, validY, NET::Move);
0506 }
0507 
0508 void XWindowInterface::requestToggleIsOnAllDesktops(WindowId wid) const
0509 {
0510     WindowInfoWrap wInfo = requestInfo(wid);
0511 
0512     if (!wInfo.isValid() || wInfo.isPlasmaDesktop()) {
0513         return;
0514     }
0515 
0516     if (KWindowSystem::numberOfDesktops() <= 1) {
0517         return;
0518     }
0519 
0520     if (wInfo.isOnAllDesktops()) {
0521         KWindowSystem::setOnDesktop(wid.toUInt(), KWindowSystem::currentDesktop());
0522         KWindowSystem::forceActiveWindow(wid.toUInt());
0523     } else {
0524         KWindowSystem::setOnAllDesktops(wid.toUInt(), true);
0525     }
0526 }
0527 
0528 void XWindowInterface::requestToggleKeepAbove(WindowId wid) const
0529 {
0530     WindowInfoWrap wInfo = requestInfo(wid);
0531 
0532     if (!wInfo.isValid() || wInfo.isPlasmaDesktop()) {
0533         return;
0534     }
0535 
0536     NETWinInfo ni(QX11Info::connection(), wid.toUInt(), QX11Info::appRootWindow(), NET::WMState, NET::Properties2());
0537 
0538     if (wInfo.isKeepAbove()) {
0539         ni.setState(NET::States(), NET::StaysOnTop);
0540     } else {
0541         ni.setState(NET::StaysOnTop, NET::StaysOnTop);
0542     }
0543 }
0544 
0545 void XWindowInterface::requestToggleMinimized(WindowId wid) const
0546 {
0547     WindowInfoWrap wInfo = requestInfo(wid);
0548 
0549     if (!wInfo.isValid() || wInfo.isPlasmaDesktop() || !inCurrentDesktopActivity(wInfo)) {
0550         return;
0551     }
0552 
0553     if (wInfo.isMinimized()) {
0554         bool onCurrent = wInfo.isOnDesktop(m_currentDesktop);
0555 
0556         KWindowSystem::unminimizeWindow(wid.toUInt());
0557 
0558         if (onCurrent) {
0559             KWindowSystem::forceActiveWindow(wid.toUInt());
0560         }
0561     } else {
0562         KWindowSystem::minimizeWindow(wid.toUInt());
0563     }
0564 }
0565 
0566 void XWindowInterface::requestToggleMaximized(WindowId wid) const
0567 {
0568     WindowInfoWrap wInfo = requestInfo(wid);
0569 
0570     if (!windowCanBeMaximized(wid) || !inCurrentDesktopActivity(wInfo)) {
0571         return;
0572     }
0573 
0574     bool restore = wInfo.isMaxHoriz() && wInfo.isMaxVert();
0575 
0576     if (wInfo.isMinimized()) {
0577         KWindowSystem::unminimizeWindow(wid.toUInt());
0578     }
0579 
0580     NETWinInfo ni(QX11Info::connection(), wid.toInt(), QX11Info::appRootWindow(), NET::WMState, NET::Properties2());
0581 
0582     if (restore) {
0583         ni.setState(NET::States(), NET::Max);
0584     } else {
0585         ni.setState(NET::Max, NET::Max);
0586     }
0587 }
0588 
0589 bool XWindowInterface::isValidWindow(WindowId wid) const
0590 {
0591     if (windowsTracker()->isValidFor(wid)) {
0592         return true;
0593     }
0594 
0595     const KWindowInfo winfo{wid.value<WId>(), NET::WMWindowType | NET::WMState};
0596 
0597     return isValidWindow(winfo);
0598 }
0599 
0600 bool XWindowInterface::isValidWindow(const KWindowInfo &winfo) const
0601 {
0602     if (windowsTracker()->isValidFor(winfo.win())) {
0603         return true;
0604     }
0605 
0606     //! ignored windows from tracking
0607     if (m_ignoredWindows.contains(winfo.win())) {
0608         return false;
0609     }
0610 
0611     if (m_desktopId == winfo.win()) {
0612         return false;
0613     }
0614 
0615     bool hasSkipTaskbar = winfo.hasState(NET::SkipTaskbar);
0616     bool hasSkipPager = winfo.hasState(NET::SkipPager);
0617 
0618     bool isSkipped = hasSkipTaskbar && hasSkipPager;
0619 
0620     return !isSkipped;
0621 }
0622 
0623 void XWindowInterface::windowChangedProxy(WId wid, NET::Properties prop1, NET::Properties2 prop2)
0624 {
0625     const KWindowInfo info(wid, NET::WMGeometry, NET::WM2WindowClass);
0626 
0627     const auto winClass = info.windowClassName();
0628 
0629     //! ignored windows do not trackd
0630     if (m_ignoredWindows.contains(wid)) {
0631         return;
0632     }
0633 
0634     if (winClass == "plasmashell") {
0635         //! update desktop id
0636         if (isPlasmaDesktop(info.geometry())) {
0637             m_desktopId = wid;
0638             windowsTracker()->setPlasmaDesktop(wid);
0639             considerWindowChanged(wid);
0640             return;
0641         } else if (isPlasmaPanel(info.geometry())) {
0642             registerPlasmaPanel(wid);
0643             return;
0644         }
0645     }
0646 
0647     //! accept only NET::Properties events,
0648     //! ignore when the user presses a key, or a window is sending X events etc.
0649     //! without needing to (e.g. Firefox, https://bugzilla.mozilla.org/show_bug.cgi?id=1389953)
0650     //! NET::WM2UserTime, NET::WM2IconPixmap etc....
0651     if (prop1 == 0 && !(prop2 & (NET::WM2Activities | NET::WM2TransientFor))) {
0652         return;
0653     }
0654 
0655     //! accept only the following NET:Properties changed signals
0656     //! NET::WMState, NET::WMGeometry, NET::ActiveWindow
0657     if ( !(prop1 & NET::WMState)
0658          && !(prop1 & NET::WMGeometry)
0659          && !(prop1 & NET::ActiveWindow)
0660          && !(prop1 & NET::WMDesktop)
0661          && !(prop1 & (NET::WMName | NET::WMVisibleName)
0662               && !(prop2 & NET::WM2TransientFor)
0663               && !(prop2 & NET::WM2Activities)) ) {
0664         return;
0665     }
0666 
0667     if (!isValidWindow(wid)) {
0668         return;
0669     }
0670 
0671     considerWindowChanged(wid);
0672 }
0673 
0674 }
0675 }