File indexing completed on 2024-05-05 16:19:51

0001 /*
0002     SPDX-FileCopyrightText: 2019 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-or-later
0005 */
0006 
0007 #include "kwindowshadow_p_x11.h"
0008 
0009 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
0010 #include <private/qtx11extras_p.h>
0011 #else
0012 #include <QX11Info>
0013 #endif
0014 
0015 static const QByteArray s_atomName = QByteArrayLiteral("_KDE_NET_WM_SHADOW");
0016 
0017 bool KWindowShadowTilePrivateX11::create()
0018 {
0019     xcb_connection_t *connection = QX11Info::connection();
0020     xcb_window_t rootWindow = QX11Info::appRootWindow();
0021 
0022     const uint16_t width = uint16_t(image.width());
0023     const uint16_t height = uint16_t(image.height());
0024     const uint8_t depth = uint8_t(image.depth());
0025 
0026     pixmap = xcb_generate_id(connection);
0027     gc = xcb_generate_id(connection);
0028 
0029     xcb_create_pixmap(connection, depth, pixmap, rootWindow, width, height);
0030     xcb_create_gc(connection, gc, pixmap, 0, nullptr);
0031 
0032     xcb_put_image(connection, //
0033                   XCB_IMAGE_FORMAT_Z_PIXMAP,
0034                   pixmap,
0035                   gc,
0036                   width,
0037                   height,
0038                   0,
0039                   0,
0040                   0,
0041                   depth,
0042                   image.sizeInBytes(),
0043                   image.constBits());
0044 
0045     return true;
0046 }
0047 
0048 void KWindowShadowTilePrivateX11::destroy()
0049 {
0050     xcb_connection_t *connection = QX11Info::connection();
0051     if (connection) {
0052         xcb_free_pixmap(connection, pixmap);
0053         xcb_free_gc(connection, gc);
0054     }
0055     pixmap = XCB_PIXMAP_NONE;
0056     gc = XCB_NONE;
0057 }
0058 
0059 KWindowShadowTilePrivateX11 *KWindowShadowTilePrivateX11::get(const KWindowShadowTile *tile)
0060 {
0061     KWindowShadowTilePrivate *d = KWindowShadowTilePrivate::get(tile);
0062     return static_cast<KWindowShadowTilePrivateX11 *>(d);
0063 }
0064 
0065 static xcb_atom_t lookupAtom(const QByteArray &atomName)
0066 {
0067     xcb_connection_t *connection = QX11Info::connection();
0068     if (!connection) {
0069         return XCB_ATOM_NONE;
0070     }
0071 
0072     xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom_unchecked(connection, //
0073                                                                     false,
0074                                                                     atomName.size(),
0075                                                                     atomName.constData());
0076     xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(connection, atomCookie, nullptr);
0077 
0078     if (!reply) {
0079         return XCB_ATOM_NONE;
0080     }
0081 
0082     xcb_atom_t atom = reply->atom;
0083     free(reply);
0084 
0085     return atom;
0086 }
0087 
0088 static xcb_pixmap_t nativeHandleForTile(const KWindowShadowTile::Ptr &tile)
0089 {
0090     const auto d = KWindowShadowTilePrivateX11::get(tile.data());
0091     return d->pixmap;
0092 }
0093 
0094 bool KWindowShadowPrivateX11::create()
0095 {
0096     xcb_connection_t *connection = QX11Info::connection();
0097 
0098     const xcb_atom_t atom = lookupAtom(s_atomName);
0099     if (atom == XCB_ATOM_NONE) {
0100         return false;
0101     }
0102 
0103     QVector<quint32> data(12);
0104     int i = 0;
0105 
0106     // Unfortunately we cannot use handle of XCB_PIXMAP_NONE for missing shadow tiles because
0107     // KWin expects **all** shadow tile handles to be valid. Maybe we could address this small
0108     // inconvenience and then remove the empty tile stuff.
0109 
0110     if (topTile) {
0111         data[i++] = nativeHandleForTile(topTile);
0112     } else {
0113         data[i++] = nativeHandleForTile(getOrCreateEmptyTile());
0114     }
0115 
0116     if (topRightTile) {
0117         data[i++] = nativeHandleForTile(topRightTile);
0118     } else {
0119         data[i++] = nativeHandleForTile(getOrCreateEmptyTile());
0120     }
0121 
0122     if (rightTile) {
0123         data[i++] = nativeHandleForTile(rightTile);
0124     } else {
0125         data[i++] = nativeHandleForTile(getOrCreateEmptyTile());
0126     }
0127 
0128     if (bottomRightTile) {
0129         data[i++] = nativeHandleForTile(bottomRightTile);
0130     } else {
0131         data[i++] = nativeHandleForTile(getOrCreateEmptyTile());
0132     }
0133 
0134     if (bottomTile) {
0135         data[i++] = nativeHandleForTile(bottomTile);
0136     } else {
0137         data[i++] = nativeHandleForTile(getOrCreateEmptyTile());
0138     }
0139 
0140     if (bottomLeftTile) {
0141         data[i++] = nativeHandleForTile(bottomLeftTile);
0142     } else {
0143         data[i++] = nativeHandleForTile(getOrCreateEmptyTile());
0144     }
0145 
0146     if (leftTile) {
0147         data[i++] = nativeHandleForTile(leftTile);
0148     } else {
0149         data[i++] = nativeHandleForTile(getOrCreateEmptyTile());
0150     }
0151 
0152     if (topLeftTile) {
0153         data[i++] = nativeHandleForTile(topLeftTile);
0154     } else {
0155         data[i++] = nativeHandleForTile(getOrCreateEmptyTile());
0156     }
0157 
0158     if (topLeftTile || topTile || topRightTile) {
0159         data[i++] = uint32_t(padding.top());
0160     } else {
0161         data[i++] = 1;
0162     }
0163 
0164     if (topRightTile || rightTile || bottomRightTile) {
0165         data[i++] = uint32_t(padding.right());
0166     } else {
0167         data[i++] = 1;
0168     }
0169 
0170     if (bottomRightTile || bottomTile || bottomLeftTile) {
0171         data[i++] = uint32_t(padding.bottom());
0172     } else {
0173         data[i++] = 1;
0174     }
0175 
0176     if (bottomLeftTile || leftTile || topLeftTile) {
0177         data[i++] = uint32_t(padding.left());
0178     } else {
0179         data[i++] = 1;
0180     }
0181 
0182     xcb_change_property(connection, XCB_PROP_MODE_REPLACE, window->winId(), atom, XCB_ATOM_CARDINAL, 32, data.size(), data.constData());
0183     xcb_flush(connection);
0184 
0185     return true;
0186 }
0187 
0188 void KWindowShadowPrivateX11::destroy()
0189 {
0190     emptyTile = nullptr;
0191 
0192     // For some reason, QWindow changes visibility of QSurface::surfaceHandle().
0193     const QSurface *surface = window;
0194 
0195     // Attempting to uninstall the shadow after the platform window had been destroyed.
0196     if (!(surface && surface->surfaceHandle())) {
0197         return;
0198     }
0199 
0200     xcb_connection_t *connection = QX11Info::connection();
0201 
0202     const xcb_atom_t atom = lookupAtom(s_atomName);
0203     if (atom == XCB_ATOM_NONE) {
0204         return;
0205     }
0206 
0207     xcb_delete_property(connection, window->winId(), atom);
0208 }
0209 
0210 KWindowShadowTile::Ptr KWindowShadowPrivateX11::getOrCreateEmptyTile()
0211 {
0212     if (!emptyTile) {
0213         QImage image(QSize(1, 1), QImage::Format_ARGB32);
0214         image.fill(Qt::transparent);
0215 
0216         emptyTile = KWindowShadowTile::Ptr::create();
0217         emptyTile->setImage(image);
0218         emptyTile->create();
0219     }
0220 
0221     return emptyTile;
0222 }