File indexing completed on 2024-04-28 13:25:58

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 "xwindowinterface.h"
0009 
0010 // local
0011 #include <coretypes.h>
0012 #include "tasktools.h"
0013 #include "view/view.h"
0014 #include "view/helpers/screenedgeghostwindow.h"
0015 
0016 // Qt
0017 #include <QDebug>
0018 #include <QTimer>
0019 #include <QtX11Extras/QX11Info>
0020 
0021 // KDE
0022 #include <KDesktopFile>
0023 #include <KWindowSystem>
0024 #include <KWindowInfo>
0025 #include <KIconThemes/KIconLoader>
0026 
0027 // X11
0028 #include <NETWM>
0029 #include <xcb/xcb.h>
0030 #include <xcb/shape.h>
0031 
0032 namespace Latte {
0033 namespace WindowSystem {
0034 
0035 XWindowInterface::XWindowInterface(QObject *parent)
0036     : AbstractWindowInterface(parent)
0037 {
0038     m_currentDesktop = QString(KWindowSystem::self()->currentDesktop());
0039 
0040     connect(KWindowSystem::self(), &KWindowSystem::activeWindowChanged, this, &AbstractWindowInterface::activeWindowChanged);
0041     connect(KWindowSystem::self(), &KWindowSystem::windowRemoved, this, &AbstractWindowInterface::windowRemoved);
0042 
0043     connect(KWindowSystem::self(), &KWindowSystem::windowAdded, this, &XWindowInterface::windowAddedProxy);
0044 
0045     connect(KWindowSystem::self(), &KWindowSystem::currentDesktopChanged, this, [&](int desktop) {
0046         m_currentDesktop = QString(desktop);
0047         emit currentDesktopChanged();
0048     });
0049 
0050     connect(KWindowSystem::self()
0051             , static_cast<void (KWindowSystem::*)(WId, NET::Properties, NET::Properties2)>
0052             (&KWindowSystem::windowChanged)
0053             , this, &XWindowInterface::windowChangedProxy);
0054 
0055 
0056     for(auto wid : KWindowSystem::self()->windows()) {
0057         windowAddedProxy(wid);
0058     }
0059 }
0060 
0061 XWindowInterface::~XWindowInterface()
0062 {
0063 }
0064 
0065 void XWindowInterface::setViewExtraFlags(QObject *view,bool isPanelWindow, Latte::Types::Visibility mode)
0066 {
0067     WId winId = -1;
0068 
0069     QQuickView *quickView = qobject_cast<QQuickView *>(view);
0070 
0071     if (quickView) {
0072         winId = quickView->winId();
0073     }
0074 
0075     if (!quickView) {
0076         QQuickWindow *quickWindow = qobject_cast<QQuickWindow *>(view);
0077 
0078         if (quickWindow) {
0079             winId = quickWindow->winId();
0080         }
0081     }
0082 
0083     NETWinInfo winfo(QX11Info::connection()
0084                      , static_cast<xcb_window_t>(winId)
0085                      , static_cast<xcb_window_t>(winId)
0086                      , 0, 0);
0087 
0088     winfo.setAllowedActions(NET::ActionChangeDesktop);
0089 
0090     if (isPanelWindow) {
0091         KWindowSystem::setType(winId, NET::Dock);
0092     } else {
0093         KWindowSystem::setType(winId, NET::Normal);
0094     }
0095 
0096     KWindowSystem::setState(winId, NET::SkipTaskbar | NET::SkipPager | NET::SkipSwitcher);
0097     KWindowSystem::setOnAllDesktops(winId, true);
0098 
0099     //! Layer to be applied
0100     if (mode == Latte::Types::WindowsCanCover || mode == Latte::Types::WindowsAlwaysCover) {
0101         setKeepBelow(winId, true);
0102     } else if (mode == Latte::Types::NormalWindow) {
0103         setKeepBelow(winId, false);
0104         setKeepAbove(winId, false);
0105     } else {
0106         setKeepAbove(winId, true);
0107     }
0108 }
0109 
0110 void XWindowInterface::setViewStruts(QWindow &view, const QRect &rect
0111                                      , Plasma::Types::Location location)
0112 {
0113     NETExtendedStrut strut;
0114 
0115     const auto screen = view.screen();
0116 
0117     const QRect currentScreen {screen->geometry()};
0118     const QRect wholeScreen {{0, 0}, screen->virtualSize()};
0119 
0120     switch (location) {
0121     case Plasma::Types::TopEdge: {
0122         const int topOffset {screen->geometry().top()};
0123         strut.top_width = rect.height() + topOffset;
0124         strut.top_start = rect.x();
0125         strut.top_end = rect.x() + rect.width() - 1;
0126         break;
0127     }
0128 
0129     case Plasma::Types::BottomEdge: {
0130         const int bottomOffset {wholeScreen.bottom() - currentScreen.bottom()};
0131         strut.bottom_width = rect.height() + bottomOffset;
0132         strut.bottom_start = rect.x();
0133         strut.bottom_end = rect.x() + rect.width() - 1;
0134         break;
0135     }
0136 
0137     case Plasma::Types::LeftEdge: {
0138         const int leftOffset = {screen->geometry().left()};
0139         strut.left_width = rect.width() + leftOffset;
0140         strut.left_start = rect.y();
0141         strut.left_end = rect.y() + rect.height() - 1;
0142         break;
0143     }
0144 
0145     case Plasma::Types::RightEdge: {
0146         const int rightOffset = {wholeScreen.right() - currentScreen.right()};
0147         strut.right_width = rect.width() + rightOffset;
0148         strut.right_start = rect.y();
0149         strut.right_end = rect.y() + rect.height() - 1;
0150         break;
0151     }
0152 
0153     default:
0154         qWarning() << "wrong location:" << location;
0155         return;
0156     }
0157 
0158     KWindowSystem::setExtendedStrut(view.winId(),
0159                                     strut.left_width,   strut.left_start,   strut.left_end,
0160                                     strut.right_width,  strut.right_start,  strut.right_end,
0161                                     strut.top_width,    strut.top_start,    strut.top_end,
0162                                     strut.bottom_width, strut.bottom_start, strut.bottom_end
0163                                     );
0164 }
0165 
0166 void XWindowInterface::switchToNextVirtualDesktop()
0167 {
0168     int desktops = KWindowSystem::numberOfDesktops();
0169 
0170     if (desktops <= 1) {
0171         return;
0172     }
0173 
0174     int curPos = KWindowSystem::currentDesktop();
0175     int nextPos = curPos + 1;
0176 
0177     if (curPos == desktops) {
0178         if (isVirtualDesktopNavigationWrappingAround()) {
0179             nextPos = 1;
0180         } else {
0181             return;
0182         }
0183     }
0184 
0185     KWindowSystem::setCurrentDesktop(nextPos);
0186 }
0187 
0188 void XWindowInterface::switchToPreviousVirtualDesktop()
0189 {
0190     int desktops = KWindowSystem::numberOfDesktops();
0191     if (desktops <= 1) {
0192         return;
0193     }
0194 
0195     int curPos = KWindowSystem::currentDesktop();
0196     int nextPos = curPos - 1;
0197 
0198     if (curPos == 1) {
0199         if (isVirtualDesktopNavigationWrappingAround()) {
0200             nextPos = desktops;
0201         } else {
0202             return;
0203         }
0204     }
0205 
0206     KWindowSystem::setCurrentDesktop(nextPos);
0207 }
0208 
0209 void XWindowInterface::setWindowOnActivities(const WindowId &wid, const QStringList &activities)
0210 {
0211     KWindowSystem::setOnActivities(wid.toUInt(), activities);
0212 }
0213 
0214 void XWindowInterface::removeViewStruts(QWindow &view)
0215 {
0216     KWindowSystem::setStrut(view.winId(), 0, 0, 0, 0);
0217 }
0218 
0219 WindowId XWindowInterface::activeWindow()
0220 {
0221     return KWindowSystem::self()->activeWindow();
0222 }
0223 
0224 void XWindowInterface::skipTaskBar(const QDialog &dialog)
0225 {
0226     KWindowSystem::setState(dialog.winId(), NET::SkipTaskbar);
0227 }
0228 
0229 void XWindowInterface::slideWindow(QWindow &view, AbstractWindowInterface::Slide location)
0230 {
0231     auto slideLocation = KWindowEffects::NoEdge;
0232 
0233     switch (location) {
0234     case Slide::Top:
0235         slideLocation = KWindowEffects::TopEdge;
0236         break;
0237 
0238     case Slide::Bottom:
0239         slideLocation = KWindowEffects::BottomEdge;
0240         break;
0241 
0242     case Slide::Left:
0243         slideLocation = KWindowEffects::LeftEdge;
0244         break;
0245 
0246     case Slide::Right:
0247         slideLocation = KWindowEffects::RightEdge;
0248         break;
0249 
0250     default:
0251         break;
0252     }
0253 
0254     KWindowEffects::slideWindow(view.winId(), slideLocation, -1);
0255 }
0256 
0257 void XWindowInterface::enableBlurBehind(QWindow &view)
0258 {
0259     KWindowEffects::enableBlurBehind(view.winId());
0260 }
0261 
0262 void XWindowInterface::setActiveEdge(QWindow *view, bool active)
0263 {
0264     ViewPart::ScreenEdgeGhostWindow *window = qobject_cast<ViewPart::ScreenEdgeGhostWindow *>(view);
0265 
0266     if (!window) {
0267         return;
0268     }
0269 
0270     xcb_connection_t *c = QX11Info::connection();
0271 
0272     const QByteArray effectName = QByteArrayLiteral("_KDE_NET_WM_SCREEN_EDGE_SHOW");
0273     xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom_unchecked(c, false, effectName.length(), effectName.constData());
0274 
0275     QScopedPointer<xcb_intern_atom_reply_t, QScopedPointerPodDeleter> atom(xcb_intern_atom_reply(c, atomCookie, nullptr));
0276 
0277     if (!atom) {
0278         return;
0279     }
0280 
0281     if (!active) {
0282         xcb_delete_property(c, window->winId(), atom->atom);
0283         window->hideWithMask();
0284         return;
0285     }
0286 
0287     window->showWithMask();
0288 
0289     uint32_t value = 0;
0290 
0291     switch (window->location()) {
0292     case Plasma::Types::TopEdge:
0293         value = 0;
0294         break;
0295 
0296     case Plasma::Types::RightEdge:
0297         value = 1;
0298         break;
0299 
0300     case Plasma::Types::BottomEdge:
0301         value = 2;
0302         break;
0303 
0304     case Plasma::Types::LeftEdge:
0305         value = 3;
0306         break;
0307 
0308     case Plasma::Types::Floating:
0309     default:
0310         value = 4;
0311         break;
0312     }
0313 
0314     int hideType = 0;
0315 
0316     value |= hideType << 8;
0317 
0318     xcb_change_property(c, XCB_PROP_MODE_REPLACE, window->winId(), atom->atom, XCB_ATOM_CARDINAL, 32, 1, &value);
0319 }
0320 
0321 QRect XWindowInterface::visibleGeometry(const WindowId &wid, const QRect &frameGeometry) const
0322 {
0323     NETWinInfo ni(QX11Info::connection(), wid.toUInt(), QX11Info::appRootWindow(), 0, NET::WM2GTKFrameExtents);
0324     NETStrut struts = ni.gtkFrameExtents();
0325     QMargins margins(struts.left, struts.top, struts.right, struts.bottom);
0326     QRect visibleGeometry = frameGeometry;
0327 
0328     if (!margins.isNull()) {
0329         visibleGeometry -= margins;
0330     }
0331 
0332     return visibleGeometry;
0333 }
0334 
0335 
0336 
0337 void XWindowInterface::setFrameExtents(QWindow *view, const QMargins &margins)
0338 {
0339     if (!view) {
0340         return;
0341     }
0342 
0343     NETWinInfo ni(QX11Info::connection(), view->winId(), QX11Info::appRootWindow(), 0, NET::WM2GTKFrameExtents);
0344 
0345     if (margins.isNull()) {
0346         //! delete property
0347         xcb_connection_t *c = QX11Info::connection();
0348         const QByteArray atomName = QByteArrayLiteral("_GTK_FRAME_EXTENTS");
0349         xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom_unchecked(c, false, atomName.length(), atomName.constData());
0350         QScopedPointer<xcb_intern_atom_reply_t, QScopedPointerPodDeleter> atom(xcb_intern_atom_reply(c, atomCookie, nullptr));
0351 
0352         if (!atom) {
0353             return;
0354         }
0355 
0356         // qDebug() << "   deleting gtk frame extents atom..";
0357 
0358         xcb_delete_property(c, view->winId(), atom->atom);
0359     } else {
0360         NETStrut struts;
0361         struts.left = margins.left();
0362         struts.top = margins.top();
0363         struts.right = margins.right();
0364         struts.bottom = margins.bottom();
0365 
0366         ni.setGtkFrameExtents(struts);
0367     }
0368 
0369   /*NETWinInfo ni2(QX11Info::connection(), view->winId(), QX11Info::appRootWindow(), 0, NET::WM2GTKFrameExtents);
0370     NETStrut applied = ni2.gtkFrameExtents();
0371     QMargins amargins(applied.left, applied.top, applied.right, applied.bottom);
0372     qDebug() << "     window gtk frame extents applied :: " << amargins;*/
0373 
0374 }
0375 
0376 void XWindowInterface::checkShapeExtension()
0377 {
0378     if (!m_shapeExtensionChecked) {
0379         xcb_connection_t *c = QX11Info::connection();
0380         xcb_prefetch_extension_data(c, &xcb_shape_id);
0381         const xcb_query_extension_reply_t *extension = xcb_get_extension_data(c, &xcb_shape_id);
0382         if (extension->present) {
0383             // query version
0384             auto cookie = xcb_shape_query_version(c);
0385             QScopedPointer<xcb_shape_query_version_reply_t, QScopedPointerPodDeleter> version(xcb_shape_query_version_reply(c, cookie, nullptr));
0386             if (!version.isNull()) {
0387                 m_shapeAvailable = (version->major_version * 0x10 + version->minor_version) >= 0x11;
0388             }
0389         }
0390         m_shapeExtensionChecked = true;
0391     }
0392 }
0393 
0394 void XWindowInterface::setInputMask(QWindow *window, const QRect &rect)
0395 {
0396     if (!window || !window->isVisible()) {
0397         return;
0398     }
0399 
0400     xcb_connection_t *c = QX11Info::connection();
0401 
0402     if (!m_shapeExtensionChecked) {
0403         checkShapeExtension();
0404     }
0405 
0406     if (!m_shapeAvailable) {
0407         return;
0408     }
0409 
0410     if (!rect.isEmpty()) {
0411         xcb_rectangle_t xcbrect;
0412         xcbrect.x = qMax(SHRT_MIN, rect.x());
0413         xcbrect.y = qMax(SHRT_MIN, rect.y());
0414         xcbrect.width = qMin((int)USHRT_MAX, rect.width());
0415         xcbrect.height = qMin((int)USHRT_MAX, rect.height());
0416 
0417         // set input shape, so that it doesn't accept any input events
0418         xcb_shape_rectangles(c, XCB_SHAPE_SO_SET, XCB_SHAPE_SK_INPUT,
0419                              XCB_CLIP_ORDERING_UNSORTED, window->winId(), 0, 0, 1, &xcbrect);
0420     } else {
0421         // delete the shape
0422         xcb_shape_mask(c, XCB_SHAPE_SO_INTERSECT, XCB_SHAPE_SK_INPUT,
0423                        window->winId(), 0, 0, XCB_PIXMAP_NONE);
0424     }
0425 }
0426 
0427 WindowInfoWrap XWindowInterface::requestInfoActive()
0428 {
0429     return requestInfo(KWindowSystem::activeWindow());
0430 }
0431 
0432 WindowInfoWrap XWindowInterface::requestInfo(WindowId wid)
0433 {
0434     const KWindowInfo winfo{wid.value<WId>(), NET::WMFrameExtents
0435                 | NET::WMWindowType
0436                 | NET::WMGeometry
0437                 | NET::WMDesktop
0438                 | NET::WMState
0439                 | NET::WMName
0440                 | NET::WMVisibleName,
0441                 NET::WM2WindowClass
0442                 | NET::WM2Activities
0443                 | NET::WM2AllowedActions
0444                 | NET::WM2TransientFor};
0445 
0446     WindowInfoWrap winfoWrap;
0447 
0448     const auto winClass = QString(winfo.windowClassName());
0449 
0450     //!used to track Plasma DesktopView windows because during startup can not be identified properly
0451     bool plasmaBlockedWindow = (winClass == QLatin1String("plasmashell") && !isAcceptableWindow(wid));
0452 
0453     if (!winfo.valid() || plasmaBlockedWindow) {
0454         winfoWrap.setIsValid(false);
0455     } else if (isValidWindow(wid)) {
0456         winfoWrap.setIsValid(true);
0457         winfoWrap.setWid(wid);
0458         winfoWrap.setParentId(winfo.transientFor());
0459         winfoWrap.setIsActive(KWindowSystem::activeWindow() == wid.value<WId>());
0460         winfoWrap.setIsMinimized(winfo.hasState(NET::Hidden));
0461         winfoWrap.setIsMaxVert(winfo.hasState(NET::MaxVert));
0462         winfoWrap.setIsMaxHoriz(winfo.hasState(NET::MaxHoriz));
0463         winfoWrap.setIsFullscreen(winfo.hasState(NET::FullScreen));
0464         winfoWrap.setIsShaded(winfo.hasState(NET::Shaded));
0465         winfoWrap.setIsOnAllDesktops(winfo.onAllDesktops());
0466         winfoWrap.setIsOnAllActivities(winfo.activities().empty());
0467         winfoWrap.setGeometry(visibleGeometry(wid, winfo.frameGeometry()));
0468         winfoWrap.setIsKeepAbove(winfo.hasState(NET::KeepAbove));
0469         winfoWrap.setIsKeepBelow(winfo.hasState(NET::KeepBelow));
0470         winfoWrap.setHasSkipPager(winfo.hasState(NET::SkipPager));
0471         winfoWrap.setHasSkipSwitcher(winfo.hasState(NET::SkipSwitcher));
0472         winfoWrap.setHasSkipTaskbar(winfo.hasState(NET::SkipTaskbar));
0473 
0474         //! BEGIN:Window Abilities
0475         winfoWrap.setIsClosable(winfo.actionSupported(NET::ActionClose));
0476         winfoWrap.setIsFullScreenable(winfo.actionSupported(NET::ActionFullScreen));
0477         winfoWrap.setIsMaximizable(winfo.actionSupported(NET::ActionMax));
0478         winfoWrap.setIsMinimizable(winfo.actionSupported(NET::ActionMinimize));
0479         winfoWrap.setIsMovable(winfo.actionSupported(NET::ActionMove));
0480         winfoWrap.setIsResizable(winfo.actionSupported(NET::ActionResize));
0481         winfoWrap.setIsShadeable(winfo.actionSupported(NET::ActionShade));
0482         winfoWrap.setIsVirtualDesktopsChangeable(winfo.actionSupported(NET::ActionChangeDesktop));
0483         //! END:Window Abilities
0484 
0485         winfoWrap.setDisplay(winfo.visibleName());
0486         winfoWrap.setDesktops({QString(winfo.desktop())});
0487         winfoWrap.setActivities(winfo.activities());
0488     }
0489 
0490     if (plasmaBlockedWindow) {
0491         windowRemoved(wid);
0492     }
0493 
0494     return winfoWrap;
0495 }
0496 
0497 AppData XWindowInterface::appDataFor(WindowId wid)
0498 {
0499     return appDataFromUrl(windowUrl(wid));
0500 }
0501 
0502 QUrl XWindowInterface::windowUrl(WindowId wid)
0503 {
0504     const KWindowInfo info(wid.value<WId>(), 0, NET::WM2WindowClass | NET::WM2DesktopFileName);
0505 
0506     QString desktopFile = QString::fromUtf8(info.desktopFileName());
0507 
0508     if (!desktopFile.isEmpty()) {
0509         KService::Ptr service = KService::serviceByStorageId(desktopFile);
0510 
0511         if (service) {
0512             const QString &menuId = service->menuId();
0513 
0514             // applications: URLs are used to refer to applications by their KService::menuId
0515             // (i.e. .desktop file name) rather than the absolute path to a .desktop file.
0516             if (!menuId.isEmpty()) {
0517                 return QUrl(QStringLiteral("applications:") + menuId);
0518             }
0519 
0520             return QUrl::fromLocalFile(service->entryPath());
0521         }
0522 
0523         if (!desktopFile.endsWith(QLatin1String(".desktop"))) {
0524             desktopFile.append(QLatin1String(".desktop"));
0525         }
0526 
0527         if (KDesktopFile::isDesktopFile(desktopFile) && QFile::exists(desktopFile)) {
0528             return QUrl::fromLocalFile(desktopFile);
0529         }
0530     }
0531 
0532     return windowUrlFromMetadata(info.windowClassClass(),
0533                                  NETWinInfo(QX11Info::connection(), wid.value<WId>(), QX11Info::appRootWindow(), NET::WMPid, NET::Properties2()).pid(),
0534                                  rulesConfig, info.windowClassName());
0535 }
0536 
0537 bool XWindowInterface::windowCanBeDragged(WindowId wid)
0538 {
0539     WindowInfoWrap winfo = requestInfo(wid);
0540     return (winfo.isValid()
0541             && !winfo.isMinimized()
0542             && winfo.isMovable()
0543             && inCurrentDesktopActivity(winfo));
0544 }
0545 
0546 bool XWindowInterface::windowCanBeMaximized(WindowId wid)
0547 {
0548     WindowInfoWrap winfo = requestInfo(wid);
0549     return (winfo.isValid()
0550             && !winfo.isMinimized()
0551             && winfo.isMaximizable()
0552             && inCurrentDesktopActivity(winfo));
0553 }
0554 
0555 void XWindowInterface::requestActivate(WindowId wid)
0556 {
0557     KWindowSystem::activateWindow(wid.toInt());
0558 }
0559 
0560 QIcon XWindowInterface::iconFor(WindowId wid)
0561 {
0562     QIcon icon;
0563 
0564     icon.addPixmap(KWindowSystem::icon(wid.value<WId>(), KIconLoader::SizeSmall, KIconLoader::SizeSmall, false));
0565     icon.addPixmap(KWindowSystem::icon(wid.value<WId>(), KIconLoader::SizeSmallMedium, KIconLoader::SizeSmallMedium, false));
0566     icon.addPixmap(KWindowSystem::icon(wid.value<WId>(), KIconLoader::SizeMedium, KIconLoader::SizeMedium, false));
0567     icon.addPixmap(KWindowSystem::icon(wid.value<WId>(), KIconLoader::SizeLarge, KIconLoader::SizeLarge, false));
0568 
0569     return icon;
0570 }
0571 
0572 WindowId XWindowInterface::winIdFor(QString appId, QRect geometry)
0573 {
0574     return activeWindow();
0575 }
0576 
0577 WindowId XWindowInterface::winIdFor(QString appId, QString title)
0578 {
0579     return activeWindow();
0580 }
0581 
0582 void XWindowInterface::requestClose(WindowId wid)
0583 {
0584     WindowInfoWrap wInfo = requestInfo(wid);
0585 
0586     if (!wInfo.isValid()) {
0587         return;
0588     }
0589 
0590     NETRootInfo ri(QX11Info::connection(), NET::CloseWindow);
0591     ri.closeWindowRequest(wInfo.wid().toUInt());
0592 }
0593 
0594 void XWindowInterface::requestMoveWindow(WindowId wid, QPoint from)
0595 {
0596     WindowInfoWrap wInfo = requestInfo(wid);
0597 
0598     if (!wInfo.isValid() || !inCurrentDesktopActivity(wInfo)) {
0599         return;
0600     }
0601 
0602     int borderX = wInfo.geometry().width() > 120 ? 60 : 10;
0603     int borderY{10};
0604 
0605     //! find min/max values for x,y based on active window geometry
0606     int minX = wInfo.geometry().x() + borderX;
0607     int maxX = wInfo.geometry().x() + wInfo.geometry().width() - borderX;
0608     int minY = wInfo.geometry().y() + borderY;
0609     int maxY = wInfo.geometry().y() + wInfo.geometry().height() - borderY;
0610 
0611     //! set the point from which this window will be moved,
0612     //! make sure that it is in window boundaries
0613     int validX = qBound(minX, from.x(), maxX);
0614     int validY = qBound(minY, from.y(), maxY);
0615 
0616     NETRootInfo ri(QX11Info::connection(), NET::WMMoveResize);
0617     ri.moveResizeRequest(wInfo.wid().toUInt(), validX, validY, NET::Move);
0618 }
0619 
0620 void XWindowInterface::requestToggleIsOnAllDesktops(WindowId wid)
0621 {
0622     WindowInfoWrap wInfo = requestInfo(wid);
0623 
0624     if (!wInfo.isValid()) {
0625         return;
0626     }
0627 
0628     if (KWindowSystem::numberOfDesktops() <= 1) {
0629         return;
0630     }
0631 
0632     if (wInfo.isOnAllDesktops()) {
0633         KWindowSystem::setOnDesktop(wid.toUInt(), KWindowSystem::currentDesktop());
0634         KWindowSystem::forceActiveWindow(wid.toUInt());
0635     } else {
0636         KWindowSystem::setOnAllDesktops(wid.toUInt(), true);
0637     }
0638 }
0639 
0640 void XWindowInterface::requestToggleKeepAbove(WindowId wid)
0641 {
0642     WindowInfoWrap wInfo = requestInfo(wid);
0643 
0644     if (!wInfo.isValid()) {
0645         return;
0646     }
0647 
0648     NETWinInfo ni(QX11Info::connection(), wid.toUInt(), QX11Info::appRootWindow(), NET::WMState, NET::Properties2());
0649 
0650     if (wInfo.isKeepAbove()) {
0651         ni.setState(NET::States(), NET::KeepAbove);
0652     } else {
0653         ni.setState(NET::KeepAbove, NET::KeepAbove);
0654     }
0655 }
0656 
0657 void XWindowInterface::setKeepAbove(WindowId wid, bool active)
0658 {
0659     if (wid.toUInt() <= 0) {
0660         return;
0661     }
0662 
0663     if (active) {
0664         KWindowSystem::setState(wid.toUInt(), NET::KeepAbove);
0665         KWindowSystem::clearState(wid.toUInt(), NET::KeepBelow);
0666     } else {
0667         KWindowSystem::clearState(wid.toUInt(), NET::KeepAbove);
0668     }
0669 }
0670 
0671 void XWindowInterface::setKeepBelow(WindowId wid, bool active)
0672 {
0673     if (wid.toUInt() <= 0) {
0674         return;
0675     }
0676 
0677     if (active) {
0678         KWindowSystem::setState(wid.toUInt(), NET::KeepBelow);
0679         KWindowSystem::clearState(wid.toUInt(), NET::KeepAbove);
0680     } else {
0681         KWindowSystem::clearState(wid.toUInt(), NET::KeepBelow);
0682     }
0683 }
0684 
0685 
0686 void XWindowInterface::requestToggleMinimized(WindowId wid)
0687 {
0688     WindowInfoWrap wInfo = requestInfo(wid);
0689 
0690     if (!wInfo.isValid() || !inCurrentDesktopActivity(wInfo)) {
0691         return;
0692     }
0693 
0694     if (wInfo.isMinimized()) {
0695         bool onCurrent = wInfo.isOnDesktop(m_currentDesktop);
0696 
0697         KWindowSystem::unminimizeWindow(wid.toUInt());
0698 
0699         if (onCurrent) {
0700             KWindowSystem::forceActiveWindow(wid.toUInt());
0701         }
0702     } else {
0703         KWindowSystem::minimizeWindow(wid.toUInt());
0704     }
0705 }
0706 
0707 void XWindowInterface::requestToggleMaximized(WindowId wid)
0708 {
0709     WindowInfoWrap wInfo = requestInfo(wid);
0710 
0711     if (!windowCanBeMaximized(wid) || !inCurrentDesktopActivity(wInfo)) {
0712         return;
0713     }
0714 
0715     bool restore = wInfo.isMaxHoriz() && wInfo.isMaxVert();
0716 
0717     if (wInfo.isMinimized()) {
0718         KWindowSystem::unminimizeWindow(wid.toUInt());
0719     }
0720 
0721     NETWinInfo ni(QX11Info::connection(), wid.toInt(), QX11Info::appRootWindow(), NET::WMState, NET::Properties2());
0722 
0723     if (restore) {
0724         ni.setState(NET::States(), NET::Max);
0725     } else {
0726         ni.setState(NET::Max, NET::Max);
0727     }
0728 }
0729 
0730 bool XWindowInterface::isValidWindow(WindowId wid)
0731 {
0732     if (windowsTracker()->isValidFor(wid)) {
0733         return true;
0734     }
0735 
0736     return isAcceptableWindow(wid);
0737 }
0738 
0739 bool XWindowInterface::isAcceptableWindow(WindowId wid)
0740 {
0741     const KWindowInfo info(wid.toUInt(), NET::WMGeometry | NET::WMState, NET::WM2WindowClass);
0742 
0743     const auto winClass = QString(info.windowClassName());
0744 
0745     //! ignored windows do not trackd
0746     if (hasBlockedTracking(wid)) {
0747         return false;
0748     }
0749 
0750     //! whitelisted/approved windows
0751     if (isWhitelistedWindow(wid)) {
0752         return true;
0753     }
0754 
0755     //! Window Checks
0756     bool hasSkipTaskbar = info.hasState(NET::SkipTaskbar);
0757     bool hasSkipPager = info.hasState(NET::SkipPager);
0758     bool isSkipped = hasSkipTaskbar && hasSkipPager;
0759 
0760     if (isSkipped
0761             && ((winClass == QLatin1String("yakuake")
0762                  || (winClass == QLatin1String("krunner"))) )) {
0763         registerWhitelistedWindow(wid);
0764     } else if (winClass == QLatin1String("plasmashell")) {
0765         if (isSkipped && isSidepanel(info.geometry())) {
0766             registerWhitelistedWindow(wid);
0767             return true;
0768         } else if (isPlasmaPanel(info.geometry()) || isFullScreenWindow(info.geometry())) {
0769             registerPlasmaIgnoredWindow(wid);
0770             return false;
0771         }
0772     } else if ((winClass == QLatin1String("latte-dock"))
0773                || (winClass == QLatin1String("ksmserver"))) {
0774         if (isFullScreenWindow(info.geometry())) {
0775             registerIgnoredWindow(wid);
0776             return false;
0777         }
0778     }
0779 
0780     return !isSkipped;
0781 }
0782 
0783 void XWindowInterface::windowAddedProxy(WId wid)
0784 {
0785     if (!isAcceptableWindow(wid)) {
0786         return;
0787     }
0788 
0789     emit windowAdded(wid);
0790     considerWindowChanged(wid);
0791 }
0792 
0793 void XWindowInterface::windowChangedProxy(WId wid, NET::Properties prop1, NET::Properties2 prop2)
0794 {
0795     if (!isValidWindow(wid)) {
0796         return;
0797     }
0798 
0799     //! accept only NET::Properties events,
0800     //! ignore when the user presses a key, or a window is sending X events etc.
0801     //! without needing to (e.g. Firefox, https://bugzilla.mozilla.org/show_bug.cgi?id=1389953)
0802     //! NET::WM2UserTime, NET::WM2IconPixmap etc....
0803     if (prop1 == 0 && !(prop2 & (NET::WM2Activities | NET::WM2TransientFor))) {
0804         return;
0805     }
0806 
0807     //! accept only the following NET:Properties changed signals
0808     //! NET::WMState, NET::WMGeometry, NET::ActiveWindow
0809     if ( !(prop1 & NET::WMState)
0810          && !(prop1 & NET::WMGeometry)
0811          && !(prop1 & NET::ActiveWindow)
0812          && !(prop1 & (NET::WMName | NET::WMVisibleName)
0813               && !(prop2 & NET::WM2TransientFor)
0814               && !(prop2 & NET::WM2Activities)) ) {
0815         return;
0816     }
0817 
0818     considerWindowChanged(wid);
0819 }
0820 
0821 }
0822 }