File indexing completed on 2024-04-14 03:57:03

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 1999 Matthias Ettrich <ettrich@kde.org>
0004     SPDX-FileCopyrightText: 2007 Lubos Lunak <l.lunak@kde.org>
0005     SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
0006 
0007     SPDX-License-Identifier: LGPL-2.1-or-later
0008 */
0009 
0010 #include "kx11extras.h"
0011 
0012 // clang-format off
0013 #include <kxerrorhandler_p.h>
0014 #include <fixx11h.h>
0015 #include <kxutils_p.h>
0016 // clang-format on
0017 
0018 #include "cptr_p.h"
0019 #include "kwindowsystem.h"
0020 #include "kwindowsystem_debug.h"
0021 #include "netwm.h"
0022 
0023 #include <QAbstractNativeEventFilter>
0024 #include <QGuiApplication>
0025 #include <QMetaMethod>
0026 #include <QRect>
0027 #include <QScreen>
0028 #include <private/qtx11extras_p.h>
0029 
0030 #include <X11/Xatom.h>
0031 #include <X11/Xutil.h>
0032 #include <X11/extensions/Xfixes.h>
0033 #include <xcb/xcb.h>
0034 #include <xcb/xfixes.h>
0035 
0036 // QPoint and QSize all have handy / operators which are useful for scaling, positions and sizes for high DPI support
0037 // QRect does not, so we create one for internal purposes within this class
0038 inline QRect operator/(const QRect &rectangle, qreal factor)
0039 {
0040     return QRect(rectangle.topLeft() / factor, rectangle.size() / factor);
0041 }
0042 
0043 class MainThreadInstantiator : public QObject
0044 {
0045     Q_OBJECT
0046 
0047 public:
0048     MainThreadInstantiator(KX11Extras::FilterInfo _what);
0049     Q_INVOKABLE NETEventFilter *createNETEventFilter();
0050 
0051 private:
0052     KX11Extras::FilterInfo m_what;
0053 };
0054 
0055 class NETEventFilter : public NETRootInfo, public QAbstractNativeEventFilter
0056 {
0057 public:
0058     NETEventFilter(KX11Extras::FilterInfo _what);
0059     ~NETEventFilter() override;
0060     void activate();
0061     QList<WId> windows;
0062     QList<WId> stackingOrder;
0063 
0064     struct StrutData {
0065         StrutData(WId window_, const NETStrut &strut_, int desktop_)
0066             : window(window_)
0067             , strut(strut_)
0068             , desktop(desktop_)
0069         {
0070         }
0071         WId window;
0072         NETStrut strut;
0073         int desktop;
0074     };
0075     QList<StrutData> strutWindows;
0076     QList<WId> possibleStrutWindows;
0077     bool strutSignalConnected;
0078     bool compositingEnabled;
0079     bool haveXfixes;
0080     KX11Extras::FilterInfo what;
0081     int xfixesEventBase;
0082     bool mapViewport();
0083 
0084     bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *) override;
0085 
0086     void updateStackingOrder();
0087     bool removeStrutWindow(WId);
0088 
0089 protected:
0090     void addClient(xcb_window_t) override;
0091     void removeClient(xcb_window_t) override;
0092 
0093 private:
0094     bool nativeEventFilter(xcb_generic_event_t *event);
0095     xcb_window_t winId;
0096     xcb_window_t m_appRootWindow;
0097 };
0098 
0099 static Atom net_wm_cm;
0100 static void create_atoms();
0101 
0102 static inline const QRect &displayGeometry()
0103 {
0104     static QRect displayGeometry;
0105     static bool isDirty = true;
0106 
0107     if (isDirty) {
0108         static QList<QMetaObject::Connection> connections;
0109         auto dirtify = [&] {
0110             isDirty = true;
0111             for (const QMetaObject::Connection &con : std::as_const(connections)) {
0112                 QObject::disconnect(con);
0113             }
0114             connections.clear();
0115         };
0116 
0117         QObject::connect(qApp, &QGuiApplication::screenAdded, dirtify);
0118         QObject::connect(qApp, &QGuiApplication::screenRemoved, dirtify);
0119         const QList<QScreen *> screenList = QGuiApplication::screens();
0120         QRegion region;
0121         for (int i = 0; i < screenList.count(); ++i) {
0122             const QScreen *screen = screenList.at(i);
0123             connections << QObject::connect(screen, &QScreen::geometryChanged, dirtify);
0124             const QRect geometry = screen->geometry();
0125             const qreal dpr = screen->devicePixelRatio();
0126             region += QRect(geometry.topLeft(), geometry.size() * dpr);
0127         }
0128         displayGeometry = region.boundingRect();
0129         isDirty = false;
0130     }
0131 
0132     return displayGeometry;
0133 }
0134 
0135 static inline int displayWidth()
0136 {
0137     return displayGeometry().width();
0138 }
0139 
0140 static inline int displayHeight()
0141 {
0142     return displayGeometry().height();
0143 }
0144 
0145 // clang-format off
0146 static const NET::Properties windowsProperties = NET::ClientList | NET::ClientListStacking |
0147                                                  NET::Supported |
0148                                                  NET::NumberOfDesktops |
0149                                                  NET::DesktopGeometry |
0150                                                  NET::DesktopViewport |
0151                                                  NET::CurrentDesktop |
0152                                                  NET::DesktopNames |
0153                                                  NET::ActiveWindow |
0154                                                  NET::WorkArea;
0155 static const NET::Properties2 windowsProperties2 = NET::WM2ShowingDesktop;
0156 
0157 // ClientList and ClientListStacking is not per-window information, but a desktop information,
0158 // so track it even with only INFO_BASIC
0159 static const NET::Properties desktopProperties = NET::ClientList | NET::ClientListStacking |
0160                                                  NET::Supported |
0161                                                  NET::NumberOfDesktops |
0162                                                  NET::DesktopGeometry |
0163                                                  NET::DesktopViewport |
0164                                                  NET::CurrentDesktop |
0165                                                  NET::DesktopNames |
0166                                                  NET::ActiveWindow |
0167                                                  NET::WorkArea;
0168 static const NET::Properties2 desktopProperties2 = NET::WM2ShowingDesktop;
0169 // clang-format on
0170 
0171 MainThreadInstantiator::MainThreadInstantiator(KX11Extras::FilterInfo _what)
0172     : QObject()
0173     , m_what(_what)
0174 {
0175 }
0176 
0177 NETEventFilter *MainThreadInstantiator::createNETEventFilter()
0178 {
0179     return new NETEventFilter(m_what);
0180 }
0181 
0182 NETEventFilter::NETEventFilter(KX11Extras::FilterInfo _what)
0183     : NETRootInfo(QX11Info::connection(),
0184                   _what >= KX11Extras::INFO_WINDOWS ? windowsProperties : desktopProperties,
0185                   _what >= KX11Extras::INFO_WINDOWS ? windowsProperties2 : desktopProperties2,
0186                   QX11Info::appScreen(),
0187                   false)
0188     , QAbstractNativeEventFilter()
0189     , strutSignalConnected(false)
0190     , compositingEnabled(false)
0191     , haveXfixes(false)
0192     , what(_what)
0193     , winId(XCB_WINDOW_NONE)
0194     , m_appRootWindow(QX11Info::appRootWindow())
0195 {
0196     QCoreApplication::instance()->installNativeEventFilter(this);
0197 
0198     int errorBase;
0199     if ((haveXfixes = XFixesQueryExtension(QX11Info::display(), &xfixesEventBase, &errorBase))) {
0200         create_atoms();
0201         winId = xcb_generate_id(QX11Info::connection());
0202         uint32_t values[] = {true, XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY};
0203         xcb_create_window(QX11Info::connection(),
0204                           XCB_COPY_FROM_PARENT,
0205                           winId,
0206                           m_appRootWindow,
0207                           0,
0208                           0,
0209                           1,
0210                           1,
0211                           0,
0212                           XCB_WINDOW_CLASS_INPUT_ONLY,
0213                           XCB_COPY_FROM_PARENT,
0214                           XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK,
0215                           values);
0216         XFixesSelectSelectionInput(QX11Info::display(),
0217                                    winId,
0218                                    net_wm_cm,
0219                                    XFixesSetSelectionOwnerNotifyMask | XFixesSelectionWindowDestroyNotifyMask | XFixesSelectionClientCloseNotifyMask);
0220         compositingEnabled = XGetSelectionOwner(QX11Info::display(), net_wm_cm) != None;
0221     }
0222 }
0223 
0224 NETEventFilter::~NETEventFilter()
0225 {
0226     if (QX11Info::connection() && winId != XCB_WINDOW_NONE) {
0227         xcb_destroy_window(QX11Info::connection(), winId);
0228         winId = XCB_WINDOW_NONE;
0229     }
0230 }
0231 
0232 // not virtual, but it's called directly only from init()
0233 void NETEventFilter::activate()
0234 {
0235     NETRootInfo::activate();
0236     updateStackingOrder();
0237 }
0238 
0239 bool NETEventFilter::nativeEventFilter(const QByteArray &eventType, void *message, qintptr *)
0240 {
0241     if (eventType != "xcb_generic_event_t") {
0242         // only interested in XCB events of course
0243         return false;
0244     }
0245     return nativeEventFilter(reinterpret_cast<xcb_generic_event_t *>(message));
0246 }
0247 
0248 bool NETEventFilter::nativeEventFilter(xcb_generic_event_t *ev)
0249 {
0250     KWindowSystem *s_q = KWindowSystem::self();
0251     const uint8_t eventType = ev->response_type & ~0x80;
0252 
0253     if (eventType == xfixesEventBase + XCB_XFIXES_SELECTION_NOTIFY) {
0254         xcb_xfixes_selection_notify_event_t *event = reinterpret_cast<xcb_xfixes_selection_notify_event_t *>(ev);
0255         if (event->window == winId) {
0256             bool haveOwner = event->owner != XCB_WINDOW_NONE;
0257             if (compositingEnabled != haveOwner) {
0258                 compositingEnabled = haveOwner;
0259                 Q_EMIT KX11Extras::self()->compositingChanged(compositingEnabled);
0260             }
0261             return true;
0262         }
0263         // Qt compresses XFixesSelectionNotifyEvents without caring about the actual window
0264         // gui/kernel/qapplication_x11.cpp
0265         // until that can be assumed fixed, we also react on events on the root (caused by Qts own compositing tracker)
0266         if (event->window == m_appRootWindow) {
0267             if (event->selection == net_wm_cm) {
0268                 bool haveOwner = event->owner != XCB_WINDOW_NONE;
0269                 if (compositingEnabled != haveOwner) {
0270                     compositingEnabled = haveOwner;
0271                     Q_EMIT KX11Extras::self()->compositingChanged(compositingEnabled);
0272                 }
0273                 // NOTICE this is not our event, we just randomly captured it from Qt -> pass on
0274                 return false;
0275             }
0276         }
0277         return false;
0278     }
0279 
0280     xcb_window_t eventWindow = XCB_WINDOW_NONE;
0281     switch (eventType) {
0282     case XCB_CLIENT_MESSAGE:
0283         eventWindow = reinterpret_cast<xcb_client_message_event_t *>(ev)->window;
0284         break;
0285     case XCB_PROPERTY_NOTIFY:
0286         eventWindow = reinterpret_cast<xcb_property_notify_event_t *>(ev)->window;
0287         break;
0288     case XCB_CONFIGURE_NOTIFY:
0289         eventWindow = reinterpret_cast<xcb_configure_notify_event_t *>(ev)->window;
0290         break;
0291     }
0292 
0293     if (eventWindow == m_appRootWindow) {
0294         int old_current_desktop = currentDesktop();
0295         xcb_window_t old_active_window = activeWindow();
0296         int old_number_of_desktops = numberOfDesktops();
0297         bool old_showing_desktop = showingDesktop();
0298         NET::Properties props;
0299         NET::Properties2 props2;
0300         NETRootInfo::event(ev, &props, &props2);
0301 
0302         if ((props & CurrentDesktop) && currentDesktop() != old_current_desktop) {
0303             Q_EMIT KX11Extras::self()->currentDesktopChanged(currentDesktop());
0304         }
0305         if ((props & DesktopViewport) && mapViewport() && currentDesktop() != old_current_desktop) {
0306             Q_EMIT KX11Extras::self()->currentDesktopChanged(currentDesktop());
0307         }
0308         if ((props & ActiveWindow) && activeWindow() != old_active_window) {
0309             Q_EMIT KX11Extras::self()->activeWindowChanged(activeWindow());
0310         }
0311         if (props & DesktopNames) {
0312             Q_EMIT KX11Extras::self()->desktopNamesChanged();
0313         }
0314         if ((props & NumberOfDesktops) && numberOfDesktops() != old_number_of_desktops) {
0315             Q_EMIT KX11Extras::self()->numberOfDesktopsChanged(numberOfDesktops());
0316         }
0317         if ((props & DesktopGeometry) && mapViewport() && numberOfDesktops() != old_number_of_desktops) {
0318             Q_EMIT KX11Extras::self()->numberOfDesktopsChanged(numberOfDesktops());
0319         }
0320         if (props & WorkArea) {
0321             Q_EMIT KX11Extras::self()->workAreaChanged();
0322         }
0323         if (props & ClientListStacking) {
0324             updateStackingOrder();
0325             Q_EMIT KX11Extras::self()->stackingOrderChanged();
0326         }
0327         if ((props2 & WM2ShowingDesktop) && showingDesktop() != old_showing_desktop) {
0328             Q_EMIT s_q->showingDesktopChanged(showingDesktop());
0329         }
0330     } else if (windows.contains(eventWindow)) {
0331         NETWinInfo ni(QX11Info::connection(), eventWindow, m_appRootWindow, NET::Properties(), NET::Properties2());
0332         NET::Properties dirtyProperties;
0333         NET::Properties2 dirtyProperties2;
0334         ni.event(ev, &dirtyProperties, &dirtyProperties2);
0335         if (eventType == XCB_PROPERTY_NOTIFY) {
0336             xcb_property_notify_event_t *event = reinterpret_cast<xcb_property_notify_event_t *>(ev);
0337             if (event->atom == XCB_ATOM_WM_HINTS) {
0338                 dirtyProperties |= NET::WMIcon; // support for old icons
0339             } else if (event->atom == XCB_ATOM_WM_NAME) {
0340                 dirtyProperties |= NET::WMName; // support for old name
0341             } else if (event->atom == XCB_ATOM_WM_ICON_NAME) {
0342                 dirtyProperties |= NET::WMIconName; // support for old iconic name
0343             }
0344         }
0345         if (mapViewport() && (dirtyProperties & (NET::WMState | NET::WMGeometry))) {
0346             /* geometry change -> possible viewport change
0347              * state change -> possible NET::Sticky change
0348              */
0349             dirtyProperties |= NET::WMDesktop;
0350         }
0351         if ((dirtyProperties & NET::WMStrut) != 0) {
0352             removeStrutWindow(eventWindow);
0353             if (!possibleStrutWindows.contains(eventWindow)) {
0354                 possibleStrutWindows.append(eventWindow);
0355             }
0356         }
0357         if (dirtyProperties || dirtyProperties2) {
0358             Q_EMIT KX11Extras::self()->windowChanged(eventWindow, dirtyProperties, dirtyProperties2);
0359 
0360             if ((dirtyProperties & NET::WMStrut) != 0) {
0361                 Q_EMIT KX11Extras::self()->strutChanged();
0362             }
0363         }
0364     }
0365 
0366     return false;
0367 }
0368 
0369 bool NETEventFilter::removeStrutWindow(WId w)
0370 {
0371     for (QList<StrutData>::Iterator it = strutWindows.begin(); it != strutWindows.end(); ++it) {
0372         if ((*it).window == w) {
0373             strutWindows.erase(it);
0374             return true;
0375         }
0376     }
0377     return false;
0378 }
0379 
0380 void NETEventFilter::updateStackingOrder()
0381 {
0382     stackingOrder.clear();
0383     for (int i = 0; i < clientListStackingCount(); i++) {
0384         stackingOrder.append(clientListStacking()[i]);
0385     }
0386 }
0387 
0388 void NETEventFilter::addClient(xcb_window_t w)
0389 {
0390     if ((what >= KX11Extras::INFO_WINDOWS)) {
0391         xcb_connection_t *c = QX11Info::connection();
0392         UniqueCPointer<xcb_get_window_attributes_reply_t> attr(xcb_get_window_attributes_reply(c, xcb_get_window_attributes_unchecked(c, w), nullptr));
0393 
0394         uint32_t events = XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY;
0395         if (attr) {
0396             events = events | attr->your_event_mask;
0397         }
0398         xcb_change_window_attributes(c, w, XCB_CW_EVENT_MASK, &events);
0399     }
0400 
0401     bool emit_strutChanged = false;
0402 
0403     if (strutSignalConnected) {
0404         NETWinInfo info(QX11Info::connection(), w, QX11Info::appRootWindow(), NET::WMStrut | NET::WMDesktop, NET::Properties2());
0405         NETStrut strut = info.strut();
0406         if (strut.left || strut.top || strut.right || strut.bottom) {
0407             strutWindows.append(StrutData(w, strut, info.desktop()));
0408             emit_strutChanged = true;
0409         }
0410     } else {
0411         possibleStrutWindows.append(w);
0412     }
0413 
0414     windows.append(w);
0415     Q_EMIT KX11Extras::self()->windowAdded(w);
0416     if (emit_strutChanged) {
0417         Q_EMIT KX11Extras::self()->strutChanged();
0418     }
0419 }
0420 
0421 void NETEventFilter::removeClient(xcb_window_t w)
0422 {
0423     bool emit_strutChanged = removeStrutWindow(w);
0424     if (strutSignalConnected && possibleStrutWindows.contains(w)) {
0425         NETWinInfo info(QX11Info::connection(), w, QX11Info::appRootWindow(), NET::WMStrut, NET::Properties2());
0426         NETStrut strut = info.strut();
0427         if (strut.left || strut.top || strut.right || strut.bottom) {
0428             emit_strutChanged = true;
0429         }
0430     }
0431 
0432     possibleStrutWindows.removeAll(w);
0433     windows.removeAll(w);
0434     Q_EMIT KX11Extras::self()->windowRemoved(w);
0435     if (emit_strutChanged) {
0436         Q_EMIT KX11Extras::self()->strutChanged();
0437     }
0438 }
0439 
0440 bool NETEventFilter::mapViewport()
0441 {
0442     // compiz claims support even though it doesn't use virtual desktops :(
0443     //    if( isSupported( NET::DesktopViewport ) && !isSupported( NET::NumberOfDesktops ))
0444 
0445     // this test is duplicated in KWindowSystem::mapViewport()
0446     if (isSupported(NET::DesktopViewport) && numberOfDesktops(true) <= 1
0447         && (desktopGeometry().width > displayWidth() || desktopGeometry().height > displayHeight())) {
0448         return true;
0449     }
0450     return false;
0451 }
0452 
0453 static bool atoms_created = false;
0454 
0455 static Atom _wm_protocols;
0456 static Atom _wm_change_state;
0457 static Atom kwm_utf8_string;
0458 
0459 static void create_atoms()
0460 {
0461     if (!atoms_created) {
0462         const int max = 20;
0463         Atom *atoms[max];
0464         const char *names[max];
0465         Atom atoms_return[max];
0466         int n = 0;
0467 
0468         atoms[n] = &_wm_protocols;
0469         names[n++] = "WM_PROTOCOLS";
0470 
0471         atoms[n] = &_wm_change_state;
0472         names[n++] = "WM_CHANGE_STATE";
0473 
0474         atoms[n] = &kwm_utf8_string;
0475         names[n++] = "UTF8_STRING";
0476 
0477         char net_wm_cm_name[100];
0478         sprintf(net_wm_cm_name, "_NET_WM_CM_S%d", QX11Info::appScreen());
0479         atoms[n] = &net_wm_cm;
0480         names[n++] = net_wm_cm_name;
0481 
0482         // we need a const_cast for the shitty X API
0483         XInternAtoms(QX11Info::display(), const_cast<char **>(names), n, false, atoms_return);
0484         for (int i = 0; i < n; i++) {
0485             *atoms[i] = atoms_return[i];
0486         }
0487 
0488         atoms_created = True;
0489     }
0490 }
0491 
0492 #define CHECK_X11                                                                                                                                              \
0493     if (!KWindowSystem::isPlatformX11()) {                                                                                                                     \
0494         qCWarning(LOG_KWINDOWSYSTEM) << Q_FUNC_INFO << "may only be used on X11";                                                                              \
0495         return {};                                                                                                                                             \
0496     }
0497 
0498 #define CHECK_X11_VOID                                                                                                                                         \
0499     if (!KWindowSystem::isPlatformX11()) {                                                                                                                     \
0500         qCWarning(LOG_KWINDOWSYSTEM) << Q_FUNC_INFO << "may only be used on X11";                                                                              \
0501         return;                                                                                                                                                \
0502     }
0503 
0504 // WARNING
0505 // you have to call s_d_func() again after calling this function if you want a valid pointer!
0506 void KX11Extras::init(FilterInfo what)
0507 {
0508     NETEventFilter *const s_d = s_d_func();
0509 
0510     if (what >= INFO_WINDOWS) {
0511         what = INFO_WINDOWS;
0512     } else {
0513         what = INFO_BASIC;
0514     }
0515 
0516     if (!s_d || s_d->what < what) {
0517         const bool wasCompositing = s_d ? s_d->compositingEnabled : false;
0518         MainThreadInstantiator instantiator(what);
0519         NETEventFilter *filter;
0520         if (instantiator.thread() == QCoreApplication::instance()->thread()) {
0521             filter = instantiator.createNETEventFilter();
0522         } else {
0523             // the instantiator is not in the main app thread, which implies
0524             // we are being called in a thread that is not the main app thread
0525             // so we move the instantiator to the main app thread and invoke
0526             // the method with a blocking call
0527             instantiator.moveToThread(QCoreApplication::instance()->thread());
0528             QMetaObject::invokeMethod(&instantiator, "createNETEventFilter", Qt::BlockingQueuedConnection, Q_RETURN_ARG(NETEventFilter *, filter));
0529         }
0530         d.reset(filter);
0531         d->activate();
0532         if (wasCompositing != s_d_func()->compositingEnabled) {
0533             Q_EMIT KX11Extras::self()->compositingChanged(s_d_func()->compositingEnabled);
0534         }
0535     }
0536 }
0537 
0538 KX11Extras *KX11Extras::self()
0539 {
0540     static KX11Extras instance;
0541     return &instance;
0542 }
0543 
0544 QList<WId> KX11Extras::windows()
0545 {
0546     CHECK_X11
0547     KX11Extras::self()->init(INFO_BASIC);
0548     return KX11Extras::self()->s_d_func()->windows;
0549 }
0550 
0551 bool KX11Extras::hasWId(WId w)
0552 {
0553     CHECK_X11
0554     return windows().contains(w);
0555 }
0556 
0557 QList<WId> KX11Extras::stackingOrder()
0558 {
0559     CHECK_X11
0560     KX11Extras::self()->init(INFO_BASIC);
0561     return KX11Extras::self()->s_d_func()->stackingOrder;
0562 }
0563 
0564 WId KX11Extras::activeWindow()
0565 {
0566     CHECK_X11
0567     NETEventFilter *const s_d = KX11Extras::self()->s_d_func();
0568     if (s_d) {
0569         return s_d->activeWindow();
0570     }
0571     NETRootInfo info(QX11Info::connection(), NET::ActiveWindow, NET::Properties2(), QX11Info::appScreen());
0572     return info.activeWindow();
0573 }
0574 
0575 void KX11Extras::activateWindow(WId win, long time)
0576 {
0577     CHECK_X11_VOID
0578     NETRootInfo info(QX11Info::connection(), NET::Properties(), NET::Properties2(), QX11Info::appScreen());
0579     if (time == 0) {
0580         time = QX11Info::appUserTime();
0581     }
0582     info.setActiveWindow(win, NET::FromApplication, time, QGuiApplication::focusWindow() ? QGuiApplication::focusWindow()->winId() : 0);
0583 }
0584 
0585 void KX11Extras::forceActiveWindow(WId win, long time)
0586 {
0587     CHECK_X11_VOID
0588     NETRootInfo info(QX11Info::connection(), NET::Properties(), NET::Properties2(), QX11Info::appScreen());
0589     if (time == 0) {
0590         time = QX11Info::appTime();
0591     }
0592     info.setActiveWindow(win, NET::FromTool, time, 0);
0593 }
0594 
0595 void KX11Extras::forceActiveWindow(QWindow *win, long time)
0596 {
0597     CHECK_X11_VOID
0598     forceActiveWindow(win->winId(), time);
0599 }
0600 
0601 bool KX11Extras::compositingActive()
0602 {
0603     CHECK_X11
0604     KX11Extras::self()->init(INFO_BASIC);
0605     if (KX11Extras::self()->s_d_func()->haveXfixes) {
0606         return KX11Extras::self()->s_d_func()->compositingEnabled;
0607     } else {
0608         create_atoms();
0609         return XGetSelectionOwner(QX11Info::display(), net_wm_cm);
0610     }
0611 }
0612 
0613 int KX11Extras::currentDesktop()
0614 {
0615     CHECK_X11
0616     if (!QX11Info::connection()) {
0617         return 1;
0618     }
0619 
0620     if (mapViewport()) {
0621         KX11Extras::self()->init(INFO_BASIC);
0622         NETEventFilter *const s_d = KX11Extras::self()->s_d_func();
0623         NETPoint p = s_d->desktopViewport(s_d->currentDesktop(true));
0624         return KX11Extras::self()->viewportToDesktop(QPoint(p.x, p.y) / qApp->devicePixelRatio());
0625     }
0626 
0627     NETEventFilter *const s_d = KX11Extras::self()->s_d_func();
0628     if (s_d) {
0629         return s_d->currentDesktop(true);
0630     }
0631     NETRootInfo info(QX11Info::connection(), NET::CurrentDesktop, NET::Properties2(), QX11Info::appScreen());
0632     return info.currentDesktop(true);
0633 }
0634 
0635 int KX11Extras::numberOfDesktops()
0636 {
0637     CHECK_X11
0638     if (!QX11Info::connection()) {
0639         return 1;
0640     }
0641 
0642     if (mapViewport()) {
0643         KX11Extras::self()->init(INFO_BASIC);
0644         NETEventFilter *const s_d = KX11Extras::self()->s_d_func();
0645         NETSize s = s_d->desktopGeometry();
0646         return s.width / displayWidth() * s.height / displayHeight();
0647     }
0648 
0649     NETEventFilter *const s_d = KX11Extras::self()->s_d_func();
0650     if (s_d) {
0651         return s_d->numberOfDesktops(true);
0652     }
0653     NETRootInfo info(QX11Info::connection(), NET::NumberOfDesktops, NET::Properties2(), QX11Info::appScreen());
0654     return info.numberOfDesktops(true);
0655 }
0656 
0657 void KX11Extras::setCurrentDesktop(int desktop)
0658 {
0659     CHECK_X11_VOID
0660     if (mapViewport()) {
0661         KX11Extras::self()->init(INFO_BASIC);
0662         NETEventFilter *const s_d = KX11Extras::self()->s_d_func();
0663         NETRootInfo info(QX11Info::connection(), NET::Properties(), NET::Properties2(), QX11Info::appScreen());
0664         QPoint pos = KX11Extras::self()->desktopToViewport(desktop, true);
0665         NETPoint p;
0666         p.x = pos.x();
0667         p.y = pos.y();
0668         info.setDesktopViewport(s_d->currentDesktop(true), p);
0669         return;
0670     }
0671     NETRootInfo info(QX11Info::connection(), NET::Properties(), NET::Properties2(), QX11Info::appScreen());
0672     info.setCurrentDesktop(desktop, true);
0673 }
0674 
0675 void KX11Extras::setOnAllDesktops(WId win, bool b)
0676 {
0677     CHECK_X11_VOID
0678     if (mapViewport()) {
0679         if (b) {
0680             setState(win, NET::Sticky);
0681         } else {
0682             clearState(win, NET::Sticky);
0683         }
0684         return;
0685     }
0686     NETWinInfo info(QX11Info::connection(), win, QX11Info::appRootWindow(), NET::WMDesktop, NET::Properties2());
0687     if (b) {
0688         info.setDesktop(NETWinInfo::OnAllDesktops, true);
0689     } else if (info.desktop(true) == NETWinInfo::OnAllDesktops) {
0690         NETRootInfo rinfo(QX11Info::connection(), NET::CurrentDesktop, NET::Properties2(), QX11Info::appScreen());
0691         info.setDesktop(rinfo.currentDesktop(true), true);
0692     }
0693 }
0694 
0695 void KX11Extras::setOnDesktop(WId win, int desktop)
0696 {
0697     CHECK_X11_VOID
0698     if (mapViewport()) {
0699         if (desktop == NET::OnAllDesktops) {
0700             return setOnAllDesktops(win, true);
0701         } else {
0702             clearState(win, NET::Sticky);
0703         }
0704         KX11Extras::self()->init(INFO_BASIC);
0705         QPoint p = KX11Extras::self()->desktopToViewport(desktop, false);
0706         Window dummy;
0707         int x;
0708         int y;
0709         unsigned int w;
0710         unsigned int h;
0711         unsigned int b;
0712         unsigned int dp;
0713         XGetGeometry(QX11Info::display(), win, &dummy, &x, &y, &w, &h, &b, &dp);
0714         // get global position
0715         XTranslateCoordinates(QX11Info::display(), win, QX11Info::appRootWindow(), 0, 0, &x, &y, &dummy);
0716         x += w / 2; // center
0717         y += h / 2;
0718         // transform to coordinates on the current "desktop"
0719         x = x % displayWidth();
0720         y = y % displayHeight();
0721         if (x < 0) {
0722             x = x + displayWidth();
0723         }
0724         if (y < 0) {
0725             y = y + displayHeight();
0726         }
0727         x += p.x(); // move to given "desktop"
0728         y += p.y();
0729         x -= w / 2; // from center back to topleft
0730         y -= h / 2;
0731         p = KX11Extras::self()->constrainViewportRelativePosition(QPoint(x, y));
0732         int flags = (NET::FromTool << 12) | (0x03 << 8) | 10; // from tool(?), x/y, static gravity
0733         NETEventFilter *const s_d = KX11Extras::self()->s_d_func();
0734         s_d->moveResizeWindowRequest(win, flags, p.x(), p.y(), w, h);
0735         return;
0736     }
0737     NETWinInfo info(QX11Info::connection(), win, QX11Info::appRootWindow(), NET::WMDesktop, NET::Properties2());
0738     info.setDesktop(desktop, true);
0739 }
0740 
0741 void KX11Extras::setOnActivities(WId win, const QStringList &activities)
0742 {
0743     CHECK_X11_VOID
0744     NETWinInfo info(QX11Info::connection(), win, QX11Info::appRootWindow(), NET::Properties(), NET::WM2Activities);
0745     info.setActivities(activities.join(QLatin1Char(',')).toLatin1().constData());
0746 }
0747 
0748 QPixmap KX11Extras::icon(WId win, int width, int height, bool scale)
0749 {
0750     CHECK_X11
0751     return icon(win, width, height, scale, NETWM | WMHints | ClassHint | XApp);
0752 }
0753 
0754 QPixmap iconFromNetWinInfo(int width, int height, bool scale, int flags, NETWinInfo *info)
0755 {
0756     QPixmap result;
0757     if (!info) {
0758         return result;
0759     }
0760     if (flags & KX11Extras::NETWM) {
0761         NETIcon ni = info->icon(width, height);
0762         if (ni.data && ni.size.width > 0 && ni.size.height > 0) {
0763             QImage img((uchar *)ni.data, (int)ni.size.width, (int)ni.size.height, QImage::Format_ARGB32);
0764             if (scale && width > 0 && height > 0 && img.size() != QSize(width, height) && !img.isNull()) {
0765                 img = img.scaled(width, height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
0766             }
0767             if (!img.isNull()) {
0768                 result = QPixmap::fromImage(img);
0769             }
0770             return result;
0771         }
0772     }
0773 
0774     if (flags & KX11Extras::WMHints) {
0775         xcb_pixmap_t p = info->icccmIconPixmap();
0776         xcb_pixmap_t p_mask = info->icccmIconPixmapMask();
0777 
0778         if (p != XCB_PIXMAP_NONE) {
0779             QPixmap pm = KXUtils::createPixmapFromHandle(info->xcbConnection(), p, p_mask);
0780             if (scale && width > 0 && height > 0 && !pm.isNull() //
0781                 && (pm.width() != width || pm.height() != height)) {
0782                 result = QPixmap::fromImage(pm.toImage().scaled(width, height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
0783             } else {
0784                 result = pm;
0785             }
0786         }
0787     }
0788 
0789     // Since width can be any arbitrary size, but the icons cannot,
0790     // take the nearest value for best results (ignoring 22 pixel
0791     // icons as they don't exist for apps):
0792     int iconWidth;
0793     if (width < 24) {
0794         iconWidth = 16;
0795     } else if (width < 40) {
0796         iconWidth = 32;
0797     } else if (width < 56) {
0798         iconWidth = 48;
0799     } else if (width < 96) {
0800         iconWidth = 64;
0801     } else if (width < 192) {
0802         iconWidth = 128;
0803     } else {
0804         iconWidth = 256;
0805     }
0806 
0807     if (flags & KX11Extras::ClassHint) {
0808         // Try to load the icon from the classhint if the app didn't specify
0809         // its own:
0810         if (result.isNull()) {
0811             const QIcon icon = QIcon::fromTheme(QString::fromUtf8(info->windowClassClass()).toLower());
0812             const QPixmap pm = icon.isNull() ? QPixmap() : icon.pixmap(iconWidth, iconWidth);
0813             if (scale && !pm.isNull()) {
0814                 result = QPixmap::fromImage(pm.toImage().scaled(width, height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
0815             } else {
0816                 result = pm;
0817             }
0818         }
0819     }
0820 
0821     if (flags & KX11Extras::XApp) {
0822         // If the icon is still a null pixmap, load the icon for X applications
0823         // as a last resort:
0824         if (result.isNull()) {
0825             const QIcon icon = QIcon::fromTheme(QStringLiteral("xorg"));
0826             const QPixmap pm = icon.isNull() ? QPixmap() : icon.pixmap(iconWidth, iconWidth);
0827             if (scale && !pm.isNull()) {
0828                 result = QPixmap::fromImage(pm.toImage().scaled(width, height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
0829             } else {
0830                 result = pm;
0831             }
0832         }
0833     }
0834     return result;
0835 }
0836 
0837 QPixmap KX11Extras::icon(WId win, int width, int height, bool scale, int flags)
0838 {
0839     CHECK_X11
0840     NETWinInfo info(QX11Info::connection(), win, QX11Info::appRootWindow(), NET::WMIcon, NET::WM2WindowClass | NET::WM2IconPixmap);
0841     return iconFromNetWinInfo(width, height, scale, flags, &info);
0842 }
0843 
0844 QPixmap KX11Extras::icon(WId win, int width, int height, bool scale, int flags, NETWinInfo *info)
0845 {
0846     // No CHECK_X11 here, kwin_wayland calls this to get the icon for XWayland windows
0847     width *= qGuiApp->devicePixelRatio();
0848     height *= qGuiApp->devicePixelRatio();
0849 
0850     if (info) {
0851         return iconFromNetWinInfo(width, height, scale, flags, info);
0852     }
0853     CHECK_X11
0854 
0855     NETWinInfo newInfo(QX11Info::connection(), win, QX11Info::appRootWindow(), NET::WMIcon, NET::WM2WindowClass | NET::WM2IconPixmap);
0856 
0857     return iconFromNetWinInfo(width, height, scale, flags, &newInfo);
0858 }
0859 
0860 // enum values for ICCCM 4.1.2.4 and 4.1.4, defined to not depend on xcb-icccm
0861 enum {
0862     _ICCCM_WM_STATE_WITHDRAWN = 0,
0863     _ICCCM_WM_STATE_NORMAL = 1,
0864     _ICCCM_WM_STATE_ICONIC = 3,
0865 };
0866 
0867 void KX11Extras::minimizeWindow(WId win)
0868 {
0869     CHECK_X11_VOID
0870     create_atoms();
0871     // as described in ICCCM 4.1.4
0872     xcb_client_message_event_t ev;
0873     memset(&ev, 0, sizeof(ev));
0874     ev.response_type = XCB_CLIENT_MESSAGE;
0875     ev.window = win;
0876     ev.type = _wm_change_state;
0877     ev.format = 32;
0878     ev.data.data32[0] = _ICCCM_WM_STATE_ICONIC;
0879     ev.data.data32[1] = 0;
0880     ev.data.data32[2] = 0;
0881     ev.data.data32[3] = 0;
0882     ev.data.data32[4] = 0;
0883     xcb_send_event(QX11Info::connection(),
0884                    false,
0885                    QX11Info::appRootWindow(),
0886                    XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
0887                    reinterpret_cast<const char *>(&ev));
0888 }
0889 
0890 void KX11Extras::unminimizeWindow(WId win)
0891 {
0892     CHECK_X11_VOID
0893     xcb_map_window(QX11Info::connection(), win);
0894 }
0895 
0896 QRect KX11Extras::workArea(int desktop)
0897 {
0898     CHECK_X11
0899     KX11Extras::self()->init(INFO_BASIC);
0900     int desk = (desktop > 0 && desktop <= (int)KX11Extras::self()->s_d_func()->numberOfDesktops()) ? desktop : currentDesktop();
0901     if (desk <= 0) {
0902         return displayGeometry() / qApp->devicePixelRatio();
0903     }
0904 
0905     NETRect r = KX11Extras::self()->s_d_func()->workArea(desk);
0906     if (r.size.width <= 0 || r.size.height <= 0) { // not set
0907         return displayGeometry() / qApp->devicePixelRatio();
0908     }
0909 
0910     return QRect(r.pos.x, r.pos.y, r.size.width, r.size.height) / qApp->devicePixelRatio();
0911 }
0912 
0913 QRect KX11Extras::workArea(const QList<WId> &exclude, int desktop)
0914 {
0915     CHECK_X11
0916     KX11Extras::self()->init(INFO_WINDOWS); // invalidates s_d_func's return value
0917     NETEventFilter *const s_d = KX11Extras::self()->s_d_func();
0918 
0919     QRect all = displayGeometry();
0920     QRect a = all;
0921 
0922     if (desktop == -1) {
0923         desktop = s_d->currentDesktop();
0924     }
0925 
0926     QList<WId>::ConstIterator it1;
0927     for (it1 = s_d->windows.constBegin(); it1 != s_d->windows.constEnd(); ++it1) {
0928         if (exclude.contains(*it1)) {
0929             continue;
0930         }
0931 
0932         // Kicker (very) extensively calls this function, causing hundreds of roundtrips just
0933         // to repeatedly find out struts of all windows. Therefore strut values for strut
0934         // windows are cached here.
0935         NETStrut strut;
0936         auto it2 = s_d->strutWindows.begin();
0937         for (; it2 != s_d->strutWindows.end(); ++it2) {
0938             if ((*it2).window == *it1) {
0939                 break;
0940             }
0941         }
0942 
0943         if (it2 != s_d->strutWindows.end()) {
0944             if (!((*it2).desktop == desktop || (*it2).desktop == NETWinInfo::OnAllDesktops)) {
0945                 continue;
0946             }
0947 
0948             strut = (*it2).strut;
0949         } else if (s_d->possibleStrutWindows.contains(*it1)) {
0950             NETWinInfo info(QX11Info::connection(), (*it1), QX11Info::appRootWindow(), NET::WMStrut | NET::WMDesktop, NET::Properties2());
0951             strut = info.strut();
0952             s_d->possibleStrutWindows.removeAll(*it1);
0953             s_d->strutWindows.append(NETEventFilter::StrutData(*it1, info.strut(), info.desktop()));
0954 
0955             if (!(info.desktop() == desktop || info.desktop() == NETWinInfo::OnAllDesktops)) {
0956                 continue;
0957             }
0958         } else {
0959             continue; // not a strut window
0960         }
0961 
0962         QRect r = all;
0963         if (strut.left > 0) {
0964             r.setLeft(r.left() + (int)strut.left);
0965         }
0966         if (strut.top > 0) {
0967             r.setTop(r.top() + (int)strut.top);
0968         }
0969         if (strut.right > 0) {
0970             r.setRight(r.right() - (int)strut.right);
0971         }
0972         if (strut.bottom > 0) {
0973             r.setBottom(r.bottom() - (int)strut.bottom);
0974         }
0975 
0976         a = a.intersected(r);
0977     }
0978     return a / qApp->devicePixelRatio();
0979 }
0980 
0981 QString KX11Extras::desktopName(int desktop)
0982 {
0983     CHECK_X11
0984     KX11Extras::self()->init(INFO_BASIC);
0985     NETEventFilter *const s_d = KX11Extras::self()->s_d_func();
0986 
0987     bool isDesktopSane = (desktop > 0 && desktop <= (int)s_d->numberOfDesktops());
0988     const char *name = s_d->desktopName(isDesktopSane ? desktop : currentDesktop());
0989 
0990     if (name && name[0]) {
0991         return QString::fromUtf8(name);
0992     }
0993 
0994     return KWindowSystem::tr("Desktop %1").arg(desktop);
0995 }
0996 
0997 void KX11Extras::setDesktopName(int desktop, const QString &name)
0998 {
0999     CHECK_X11_VOID
1000     NETEventFilter *const s_d = KX11Extras::self()->s_d_func();
1001 
1002     if (desktop <= 0 || desktop > (int)numberOfDesktops()) {
1003         desktop = currentDesktop();
1004     }
1005 
1006     if (s_d) {
1007         s_d->setDesktopName(desktop, name.toUtf8().constData());
1008         return;
1009     }
1010 
1011     NETRootInfo info(QX11Info::connection(), NET::Properties(), NET::Properties2(), QX11Info::appScreen());
1012     info.setDesktopName(desktop, name.toUtf8().constData());
1013 }
1014 
1015 QString KX11Extras::readNameProperty(WId win, unsigned long atom)
1016 {
1017     CHECK_X11
1018     XTextProperty tp;
1019     char **text = nullptr;
1020     int count;
1021     QString result;
1022     if (XGetTextProperty(QX11Info::display(), win, &tp, atom) != 0 && tp.value != nullptr) {
1023         create_atoms();
1024 
1025         if (tp.encoding == kwm_utf8_string) {
1026             result = QString::fromUtf8((const char *)tp.value);
1027         } else if (XmbTextPropertyToTextList(QX11Info::display(), &tp, &text, &count) == Success && text != nullptr && count > 0) {
1028             result = QString::fromLocal8Bit(text[0]);
1029         } else if (tp.encoding == XA_STRING) {
1030             result = QString::fromLocal8Bit((const char *)tp.value);
1031         }
1032         if (text != nullptr) {
1033             XFreeStringList(text);
1034         }
1035         XFree(tp.value);
1036     }
1037     return result;
1038 }
1039 
1040 bool KX11Extras::mapViewport()
1041 {
1042     CHECK_X11
1043     NETEventFilter *const s_d = KX11Extras::self()->s_d_func();
1044     if (s_d) {
1045         return s_d->mapViewport();
1046     }
1047 
1048     // Handle case of not having a QGuiApplication
1049     if (!QX11Info::connection()) {
1050         return false;
1051     }
1052 
1053     // avoid creating KWindowSystemPrivate
1054     NETRootInfo infos(QX11Info::connection(), NET::Supported, NET::Properties2(), QX11Info::appScreen());
1055     if (!infos.isSupported(NET::DesktopViewport)) {
1056         return false;
1057     }
1058     NETRootInfo info(QX11Info::connection(), NET::NumberOfDesktops | NET::CurrentDesktop | NET::DesktopGeometry, NET::Properties2(), QX11Info::appScreen());
1059     if (info.numberOfDesktops(true) <= 1 && (info.desktopGeometry().width > displayWidth() || info.desktopGeometry().height > displayHeight())) {
1060         return true;
1061     }
1062     return false;
1063 }
1064 
1065 int KX11Extras::viewportWindowToDesktop(const QRect &rect)
1066 {
1067     CHECK_X11
1068     const QRect r = rect / qApp->devicePixelRatio();
1069 
1070     KX11Extras::self()->init(INFO_BASIC);
1071     NETEventFilter *const s_d = KX11Extras::self()->s_d_func();
1072     QPoint p = r.center();
1073     // make absolute
1074     p = QPoint(p.x() + s_d->desktopViewport(s_d->currentDesktop(true)).x, p.y() + s_d->desktopViewport(s_d->currentDesktop(true)).y);
1075     NETSize s = s_d->desktopGeometry();
1076     QSize vs(displayWidth(), displayHeight());
1077     int xs = s.width / vs.width();
1078     int x = p.x() < 0 ? 0 : p.x() >= s.width ? xs - 1 : p.x() / vs.width();
1079     int ys = s.height / vs.height();
1080     int y = p.y() < 0 ? 0 : p.y() >= s.height ? ys - 1 : p.y() / vs.height();
1081     return y * xs + x + 1;
1082 }
1083 
1084 void KX11Extras::setExtendedStrut(WId win,
1085                                   qreal left_width,
1086                                   qreal left_start,
1087                                   qreal left_end,
1088                                   qreal right_width,
1089                                   qreal right_start,
1090                                   qreal right_end,
1091                                   qreal top_width,
1092                                   qreal top_start,
1093                                   qreal top_end,
1094                                   qreal bottom_width,
1095                                   qreal bottom_start,
1096                                   qreal bottom_end)
1097 {
1098     CHECK_X11_VOID
1099     const qreal dpr = qApp->devicePixelRatio();
1100 
1101     NETWinInfo info(QX11Info::connection(), win, QX11Info::appRootWindow(), NET::Properties(), NET::Properties2());
1102     NETExtendedStrut strut;
1103     strut.left_width = std::lround(left_width * dpr);
1104     strut.right_width = std::lround(right_width * dpr);
1105     strut.top_width = std::lround(top_width * dpr);
1106     strut.bottom_width = std::lround(bottom_width * dpr);
1107     strut.left_start = std::lround(left_start * dpr);
1108     strut.left_end = std::lround(left_end * dpr);
1109     strut.right_start = std::lround(right_start * dpr);
1110     strut.right_end = std::lround(right_end * dpr);
1111     strut.top_start = std::lround(top_start * dpr);
1112     strut.top_end = std::lround(top_end * dpr);
1113     strut.bottom_start = std::lround(bottom_start * dpr);
1114     strut.bottom_end = std::lround(bottom_end * dpr);
1115     info.setExtendedStrut(strut);
1116     NETStrut oldstrut;
1117     oldstrut.left = std::lround(left_width * dpr);
1118     oldstrut.right = std::lround(right_width * dpr);
1119     oldstrut.top = std::lround(top_width * dpr);
1120     oldstrut.bottom = std::lround(bottom_width * dpr);
1121     info.setStrut(oldstrut);
1122 }
1123 
1124 void KX11Extras::setStrut(WId win, qreal left, qreal right, qreal top, qreal bottom)
1125 {
1126     CHECK_X11_VOID
1127     const qreal dpr = qApp->devicePixelRatio();
1128 
1129     int w = displayWidth();
1130     int h = displayHeight();
1131     setExtendedStrut(win,
1132                      std::lround(left * dpr),
1133                      0,
1134                      std::lround(left * dpr) != 0 ? w : 0,
1135                      std::lround(right * dpr),
1136                      0,
1137                      std::lround(right * dpr) != 0 ? w : 0,
1138                      std::lround(top * dpr),
1139                      0,
1140                      std::lround(top * dpr) != 0 ? h : 0,
1141                      std::lround(bottom * dpr),
1142                      0,
1143                      std::lround(bottom * dpr) != 0 ? h : 0);
1144 }
1145 
1146 // optimalization - create private only when needed and only for what is needed
1147 void KX11Extras::connectNotify(const QMetaMethod &signal)
1148 {
1149     CHECK_X11_VOID
1150     FilterInfo what = INFO_BASIC;
1151     if (signal == QMetaMethod::fromSignal(&KX11Extras::workAreaChanged)) {
1152         what = INFO_WINDOWS;
1153     } else if (signal == QMetaMethod::fromSignal(&KX11Extras::strutChanged)) {
1154         what = INFO_WINDOWS;
1155     } else if (signal == QMetaMethod::fromSignal(&KX11Extras::windowChanged)) {
1156         what = INFO_WINDOWS;
1157     }
1158 
1159     init(what);
1160     NETEventFilter *const s_d = s_d_func();
1161     if (!s_d->strutSignalConnected && signal == QMetaMethod::fromSignal(&KX11Extras::strutChanged)) {
1162         s_d->strutSignalConnected = true;
1163     }
1164     QObject::connectNotify(signal);
1165 }
1166 
1167 void KX11Extras::setType(WId win, NET::WindowType windowType)
1168 {
1169     CHECK_X11_VOID
1170     NETWinInfo info(QX11Info::connection(), win, QX11Info::appRootWindow(), NET::Properties(), NET::Properties2());
1171     info.setWindowType(windowType);
1172 }
1173 
1174 void KX11Extras::setState(WId win, NET::States state)
1175 {
1176     CHECK_X11_VOID
1177     NETWinInfo info(QX11Info::connection(), win, QX11Info::appRootWindow(), NET::WMState, NET::Properties2());
1178     info.setState(state, state);
1179 }
1180 
1181 void KX11Extras::clearState(WId win, NET::States state)
1182 {
1183     CHECK_X11_VOID
1184     NETWinInfo info(QX11Info::connection(), win, QX11Info::appRootWindow(), NET::WMState, NET::Properties2());
1185     info.setState(NET::States(), state);
1186 }
1187 
1188 int KX11Extras::viewportToDesktop(const QPoint &p)
1189 {
1190     CHECK_X11
1191     KX11Extras::self()->init(INFO_BASIC);
1192     NETEventFilter *const s_d = KX11Extras::self()->s_d_func();
1193     NETSize s = s_d->desktopGeometry();
1194     QSize vs(displayWidth(), displayHeight());
1195     int xs = s.width / vs.width();
1196     int x = p.x() < 0 ? 0 : p.x() >= s.width ? xs - 1 : p.x() / vs.width();
1197     int ys = s.height / vs.height();
1198     int y = p.y() < 0 ? 0 : p.y() >= s.height ? ys - 1 : p.y() / vs.height();
1199     return y * xs + x + 1;
1200 }
1201 
1202 QPoint KX11Extras::constrainViewportRelativePosition(const QPoint &pos)
1203 {
1204     CHECK_X11
1205     init(INFO_BASIC);
1206     NETEventFilter *const s_d = s_d_func();
1207     NETSize s = s_d->desktopGeometry();
1208     NETPoint c = s_d->desktopViewport(s_d->currentDesktop(true));
1209     int x = (pos.x() + c.x) % s.width;
1210     int y = (pos.y() + c.y) % s.height;
1211     if (x < 0) {
1212         x += s.width;
1213     }
1214     if (y < 0) {
1215         y += s.height;
1216     }
1217     return QPoint(x - c.x, y - c.y);
1218 }
1219 
1220 QPoint KX11Extras::desktopToViewport(int desktop, bool absolute)
1221 {
1222     CHECK_X11
1223     init(INFO_BASIC);
1224     NETEventFilter *const s_d = s_d_func();
1225     NETSize s = s_d->desktopGeometry();
1226     QSize vs(displayWidth(), displayHeight());
1227     int xs = s.width / vs.width();
1228     int ys = s.height / vs.height();
1229     if (desktop <= 0 || desktop > xs * ys) {
1230         return QPoint(0, 0);
1231     }
1232     --desktop;
1233     QPoint ret(vs.width() * (desktop % xs), vs.height() * (desktop / xs));
1234     if (!absolute) {
1235         ret = QPoint(ret.x() - s_d->desktopViewport(s_d->currentDesktop(true)).x, ret.y() - s_d->desktopViewport(s_d->currentDesktop(true)).y);
1236         if (ret.x() >= s.width) {
1237             ret.setX(ret.x() - s.width);
1238         }
1239         if (ret.x() < 0) {
1240             ret.setX(ret.x() + s.width);
1241         }
1242         if (ret.y() >= s.height) {
1243             ret.setY(ret.y() - s.height);
1244         }
1245         if (ret.y() < 0) {
1246             ret.setY(ret.y() + s.height);
1247         }
1248     }
1249     return ret;
1250 }
1251 
1252 bool KX11Extras::showingDesktop()
1253 {
1254     KX11Extras::self()->init(INFO_BASIC);
1255     return KX11Extras::self()->s_d_func()->showingDesktop();
1256 }
1257 
1258 void KX11Extras::setShowingDesktop(bool showing)
1259 {
1260     NETRootInfo info(QX11Info::connection(), NET::Properties(), NET::WM2ShowingDesktop, QX11Info::appScreen());
1261     info.setShowingDesktop(showing);
1262 }
1263 
1264 #include "kx11extras.moc"
1265 #include "moc_kx11extras.cpp"