File indexing completed on 2024-04-21 03:59:22

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