File indexing completed on 2024-05-05 12:24:30

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