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