File indexing completed on 2024-05-05 17:42:26
0001 /* This file is part of the KDE libraries 0002 SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org> 0003 SPDX-FileCopyrightText: 2016 Marco Martin <mart@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0006 */ 0007 #include "x11integration.h" 0008 0009 #include <NETWM> 0010 #include <QCoreApplication> 0011 #include <QGuiApplication> 0012 #include <QPlatformSurfaceEvent> 0013 #include <QWindow> 0014 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 0015 #include <private/qtx11extras_p.h> 0016 #else 0017 #include <QX11Info> 0018 #endif 0019 0020 #include <KWindowEffects> 0021 0022 static const char s_schemePropertyName[] = "KDE_COLOR_SCHEME_PATH"; 0023 static const QByteArray s_blurBehindPropertyName = QByteArrayLiteral("ENABLE_BLUR_BEHIND_HINT"); 0024 0025 X11Integration::X11Integration(KdePlatformTheme *platformTheme) 0026 : QObject() 0027 , m_platformTheme(platformTheme) 0028 { 0029 } 0030 0031 X11Integration::~X11Integration() = default; 0032 0033 void X11Integration::init() 0034 { 0035 QCoreApplication::instance()->installEventFilter(this); 0036 } 0037 0038 bool X11Integration::eventFilter(QObject *watched, QEvent *event) 0039 { 0040 // the drag and drop window should NOT be a tooltip 0041 // https://bugreports.qt.io/browse/QTBUG-52560 0042 if (event->type() == QEvent::Show && watched->inherits("QShapedPixmapWindow")) { 0043 // static cast should be safe there 0044 QWindow *w = static_cast<QWindow *>(watched); 0045 NETWinInfo info(QX11Info::connection(), w->winId(), QX11Info::appRootWindow(), NET::WMWindowType, NET::Properties2()); 0046 info.setWindowType(NET::DNDIcon); 0047 // TODO: does this flash the xcb connection? 0048 } 0049 if (event->type() == QEvent::PlatformSurface) { 0050 if (QWindow *w = qobject_cast<QWindow *>(watched)) { 0051 QPlatformSurfaceEvent *pe = static_cast<QPlatformSurfaceEvent *>(event); 0052 if (!w->flags().testFlag(Qt::ForeignWindow)) { 0053 if (pe->surfaceEventType() == QPlatformSurfaceEvent::SurfaceCreated) { 0054 m_platformTheme->windowCreated(w); 0055 auto flags = w->flags(); 0056 // A recent KWin change means it now follows WindowButtonHints on X11 0057 // Some KDE applications use QDialogs for their main window, 0058 // which means the KWin now surfaces those windows having the wrong hints. 0059 // To avoid clients changing, we adjust flags here. 0060 // This is documented as being only available on some platforms, 0061 // so altering is relatively safe. 0062 if (flags.testFlag(Qt::Dialog) && !flags.testFlag(Qt::CustomizeWindowHint)) { 0063 if (!w->transientParent()) { 0064 flags.setFlag(Qt::WindowCloseButtonHint); 0065 flags.setFlag(Qt::WindowMinMaxButtonsHint); 0066 } 0067 w->setFlags(flags); 0068 } 0069 0070 if (qApp->property(s_schemePropertyName).isValid()) { 0071 installColorScheme(w); 0072 } 0073 const auto blurBehindProperty = w->property(s_blurBehindPropertyName.constData()); 0074 if (blurBehindProperty.isValid()) { 0075 KWindowEffects::enableBlurBehind(w, blurBehindProperty.toBool()); 0076 } 0077 installDesktopFileName(w); 0078 } 0079 } 0080 } 0081 } 0082 if (event->type() == QEvent::ApplicationPaletteChange) { 0083 const auto topLevelWindows = QGuiApplication::topLevelWindows(); 0084 for (QWindow *w : topLevelWindows) { 0085 installColorScheme(w); 0086 } 0087 } 0088 return false; 0089 } 0090 0091 void X11Integration::installColorScheme(QWindow *w) 0092 { 0093 if (!w->isTopLevel() || !w->handle() /* e.g. WebEngine's QQuickWindow */) { 0094 return; 0095 } 0096 static xcb_atom_t atom = XCB_ATOM_NONE; 0097 xcb_connection_t *c = QX11Info::connection(); 0098 if (atom == XCB_ATOM_NONE) { 0099 const QByteArray name = QByteArrayLiteral("_KDE_NET_WM_COLOR_SCHEME"); 0100 const xcb_intern_atom_cookie_t cookie = xcb_intern_atom(c, false, name.length(), name.constData()); 0101 QScopedPointer<xcb_intern_atom_reply_t, QScopedPointerPodDeleter> reply(xcb_intern_atom_reply(c, cookie, nullptr)); 0102 if (!reply.isNull()) { 0103 atom = reply->atom; 0104 } else { 0105 // no point in continuing, we don't have the atom 0106 return; 0107 } 0108 } 0109 const QString path = qApp->property(s_schemePropertyName).toString(); 0110 if (path.isEmpty()) { 0111 xcb_delete_property(c, w->winId(), atom); 0112 } else { 0113 xcb_change_property(c, XCB_PROP_MODE_REPLACE, w->winId(), atom, XCB_ATOM_STRING, 8, path.size(), qPrintable(path)); 0114 } 0115 } 0116 0117 void X11Integration::installDesktopFileName(QWindow *w) 0118 { 0119 if (!w->isTopLevel()) { 0120 return; 0121 } 0122 0123 QString desktopFileName = QGuiApplication::desktopFileName(); 0124 if (desktopFileName.isEmpty()) { 0125 return; 0126 } 0127 // handle apps which set the desktopFileName property with filename suffix, 0128 // due to unclear API dox (https://bugreports.qt.io/browse/QTBUG-75521) 0129 if (desktopFileName.endsWith(QLatin1String(".desktop"))) { 0130 desktopFileName.chop(8); 0131 } 0132 NETWinInfo info(QX11Info::connection(), w->winId(), QX11Info::appRootWindow(), NET::Properties(), NET::Properties2()); 0133 info.setDesktopFileName(desktopFileName.toUtf8().constData()); 0134 } 0135 0136 void X11Integration::setWindowProperty(QWindow *window, const QByteArray &name, const QByteArray &value) 0137 { 0138 auto *c = QX11Info::connection(); 0139 0140 xcb_atom_t atom; 0141 auto it = m_atoms.find(name); 0142 if (it == m_atoms.end()) { 0143 const xcb_intern_atom_cookie_t cookie = xcb_intern_atom(c, false, name.length(), name.constData()); 0144 QScopedPointer<xcb_intern_atom_reply_t, QScopedPointerPodDeleter> reply(xcb_intern_atom_reply(c, cookie, nullptr)); 0145 if (!reply.isNull()) { 0146 atom = reply->atom; 0147 m_atoms[name] = atom; 0148 } else { 0149 return; 0150 } 0151 } else { 0152 atom = *it; 0153 } 0154 0155 if (value.isEmpty()) { 0156 xcb_delete_property(c, window->winId(), atom); 0157 } else { 0158 xcb_change_property(c, XCB_PROP_MODE_REPLACE, window->winId(), atom, XCB_ATOM_STRING, 8, value.length(), value.constData()); 0159 } 0160 }