1 | /* |
2 | SPDX-FileCopyrightText: 2019 Vlad Zahorodnii <vlad.zahorodnii@kde.org> |
3 | |
4 | SPDX-License-Identifier: LGPL-2.1-or-later |
5 | */ |
6 | |
7 | #include "kwindowshadow_p_x11.h" |
8 | |
9 | #include <private/qtx11extras_p.h> |
10 | |
11 | static const QByteArray s_atomName = QByteArrayLiteral("_KDE_NET_WM_SHADOW" ); |
12 | |
13 | bool KWindowShadowTilePrivateX11::create() |
14 | { |
15 | xcb_connection_t *connection = QX11Info::connection(); |
16 | xcb_window_t rootWindow = QX11Info::appRootWindow(); |
17 | |
18 | const uint16_t width = uint16_t(image.width()); |
19 | const uint16_t height = uint16_t(image.height()); |
20 | const uint8_t depth = uint8_t(image.depth()); |
21 | |
22 | pixmap = xcb_generate_id(c: connection); |
23 | gc = xcb_generate_id(c: connection); |
24 | |
25 | xcb_create_pixmap(c: connection, depth, pid: pixmap, drawable: rootWindow, width, height); |
26 | xcb_create_gc(c: connection, cid: gc, drawable: pixmap, value_mask: 0, value_list: nullptr); |
27 | |
28 | xcb_put_image(c: connection, // |
29 | format: XCB_IMAGE_FORMAT_Z_PIXMAP, |
30 | drawable: pixmap, |
31 | gc, |
32 | width, |
33 | height, |
34 | dst_x: 0, |
35 | dst_y: 0, |
36 | left_pad: 0, |
37 | depth, |
38 | data_len: image.sizeInBytes(), |
39 | data: image.constBits()); |
40 | |
41 | return true; |
42 | } |
43 | |
44 | void KWindowShadowTilePrivateX11::destroy() |
45 | { |
46 | xcb_connection_t *connection = QX11Info::connection(); |
47 | if (connection) { |
48 | xcb_free_pixmap(c: connection, pixmap); |
49 | xcb_free_gc(c: connection, gc); |
50 | } |
51 | pixmap = XCB_PIXMAP_NONE; |
52 | gc = XCB_NONE; |
53 | } |
54 | |
55 | KWindowShadowTilePrivateX11 *KWindowShadowTilePrivateX11::get(const KWindowShadowTile *tile) |
56 | { |
57 | KWindowShadowTilePrivate *d = KWindowShadowTilePrivate::get(tile); |
58 | return static_cast<KWindowShadowTilePrivateX11 *>(d); |
59 | } |
60 | |
61 | static xcb_atom_t lookupAtom(const QByteArray &atomName) |
62 | { |
63 | xcb_connection_t *connection = QX11Info::connection(); |
64 | if (!connection) { |
65 | return XCB_ATOM_NONE; |
66 | } |
67 | |
68 | xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom_unchecked(c: connection, // |
69 | only_if_exists: false, |
70 | name_len: atomName.size(), |
71 | name: atomName.constData()); |
72 | xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(c: connection, cookie: atomCookie, e: nullptr); |
73 | |
74 | if (!reply) { |
75 | return XCB_ATOM_NONE; |
76 | } |
77 | |
78 | xcb_atom_t atom = reply->atom; |
79 | free(ptr: reply); |
80 | |
81 | return atom; |
82 | } |
83 | |
84 | static xcb_pixmap_t nativeHandleForTile(const KWindowShadowTile::Ptr &tile) |
85 | { |
86 | const auto d = KWindowShadowTilePrivateX11::get(tile: tile.data()); |
87 | return d->pixmap; |
88 | } |
89 | |
90 | bool KWindowShadowPrivateX11::create() |
91 | { |
92 | xcb_connection_t *connection = QX11Info::connection(); |
93 | |
94 | const xcb_atom_t atom = lookupAtom(atomName: s_atomName); |
95 | if (atom == XCB_ATOM_NONE) { |
96 | return false; |
97 | } |
98 | |
99 | QList<quint32> data(12); |
100 | int i = 0; |
101 | |
102 | // Unfortunately we cannot use handle of XCB_PIXMAP_NONE for missing shadow tiles because |
103 | // KWin expects **all** shadow tile handles to be valid. Maybe we could address this small |
104 | // inconvenience and then remove the empty tile stuff. |
105 | |
106 | if (topTile) { |
107 | data[i++] = nativeHandleForTile(tile: topTile); |
108 | } else { |
109 | data[i++] = nativeHandleForTile(tile: getOrCreateEmptyTile()); |
110 | } |
111 | |
112 | if (topRightTile) { |
113 | data[i++] = nativeHandleForTile(tile: topRightTile); |
114 | } else { |
115 | data[i++] = nativeHandleForTile(tile: getOrCreateEmptyTile()); |
116 | } |
117 | |
118 | if (rightTile) { |
119 | data[i++] = nativeHandleForTile(tile: rightTile); |
120 | } else { |
121 | data[i++] = nativeHandleForTile(tile: getOrCreateEmptyTile()); |
122 | } |
123 | |
124 | if (bottomRightTile) { |
125 | data[i++] = nativeHandleForTile(tile: bottomRightTile); |
126 | } else { |
127 | data[i++] = nativeHandleForTile(tile: getOrCreateEmptyTile()); |
128 | } |
129 | |
130 | if (bottomTile) { |
131 | data[i++] = nativeHandleForTile(tile: bottomTile); |
132 | } else { |
133 | data[i++] = nativeHandleForTile(tile: getOrCreateEmptyTile()); |
134 | } |
135 | |
136 | if (bottomLeftTile) { |
137 | data[i++] = nativeHandleForTile(tile: bottomLeftTile); |
138 | } else { |
139 | data[i++] = nativeHandleForTile(tile: getOrCreateEmptyTile()); |
140 | } |
141 | |
142 | if (leftTile) { |
143 | data[i++] = nativeHandleForTile(tile: leftTile); |
144 | } else { |
145 | data[i++] = nativeHandleForTile(tile: getOrCreateEmptyTile()); |
146 | } |
147 | |
148 | if (topLeftTile) { |
149 | data[i++] = nativeHandleForTile(tile: topLeftTile); |
150 | } else { |
151 | data[i++] = nativeHandleForTile(tile: getOrCreateEmptyTile()); |
152 | } |
153 | |
154 | if (topLeftTile || topTile || topRightTile) { |
155 | data[i++] = uint32_t(padding.top()); |
156 | } else { |
157 | data[i++] = 1; |
158 | } |
159 | |
160 | if (topRightTile || rightTile || bottomRightTile) { |
161 | data[i++] = uint32_t(padding.right()); |
162 | } else { |
163 | data[i++] = 1; |
164 | } |
165 | |
166 | if (bottomRightTile || bottomTile || bottomLeftTile) { |
167 | data[i++] = uint32_t(padding.bottom()); |
168 | } else { |
169 | data[i++] = 1; |
170 | } |
171 | |
172 | if (bottomLeftTile || leftTile || topLeftTile) { |
173 | data[i++] = uint32_t(padding.left()); |
174 | } else { |
175 | data[i++] = 1; |
176 | } |
177 | |
178 | xcb_change_property(c: connection, mode: XCB_PROP_MODE_REPLACE, window: window->winId(), property: atom, type: XCB_ATOM_CARDINAL, format: 32, data_len: data.size(), data: data.constData()); |
179 | xcb_flush(c: connection); |
180 | |
181 | return true; |
182 | } |
183 | |
184 | void KWindowShadowPrivateX11::destroy() |
185 | { |
186 | emptyTile = nullptr; |
187 | |
188 | // For some reason, QWindow changes visibility of QSurface::surfaceHandle(). |
189 | const QSurface *surface = window; |
190 | |
191 | // Attempting to uninstall the shadow after the platform window had been destroyed. |
192 | if (!(surface && surface->surfaceHandle())) { |
193 | return; |
194 | } |
195 | |
196 | xcb_connection_t *connection = QX11Info::connection(); |
197 | |
198 | const xcb_atom_t atom = lookupAtom(atomName: s_atomName); |
199 | if (atom == XCB_ATOM_NONE) { |
200 | return; |
201 | } |
202 | |
203 | xcb_delete_property(c: connection, window: window->winId(), property: atom); |
204 | } |
205 | |
206 | KWindowShadowTile::Ptr KWindowShadowPrivateX11::getOrCreateEmptyTile() |
207 | { |
208 | if (!emptyTile) { |
209 | QImage image(QSize(1, 1), QImage::Format_ARGB32); |
210 | image.fill(color: Qt::transparent); |
211 | |
212 | emptyTile = KWindowShadowTile::Ptr::create(); |
213 | emptyTile->setImage(image); |
214 | emptyTile->create(); |
215 | } |
216 | |
217 | return emptyTile; |
218 | } |
219 | |