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 }