1 | /******************************************************************** |
2 | KWin - the KDE window manager |
3 | This file is part of the KDE project. |
4 | |
5 | Copyright (C) 2011 Martin Gräßlin <mgraesslin@kde.org> |
6 | |
7 | This program is free software; you can redistribute it and/or modify |
8 | it under the terms of the GNU General Public License as published by |
9 | the Free Software Foundation; either version 2 of the License, or |
10 | (at your option) any later version. |
11 | |
12 | This program is distributed in the hope that it will be useful, |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | GNU General Public License for more details. |
16 | |
17 | You should have received a copy of the GNU General Public License |
18 | along with this program. If not, see <http://www.gnu.org/licenses/>. |
19 | *********************************************************************/ |
20 | #include "shadow.h" |
21 | // kwin |
22 | #include "atoms.h" |
23 | #include "effects.h" |
24 | #include "toplevel.h" |
25 | #include "scene_opengl.h" |
26 | #ifdef KWIN_HAVE_XRENDER_COMPOSITING |
27 | #include "scene_xrender.h" |
28 | #endif |
29 | |
30 | namespace KWin |
31 | { |
32 | |
33 | Shadow::Shadow(Toplevel *toplevel) |
34 | : m_topLevel(toplevel) |
35 | , m_cachedSize(toplevel->geometry().size()) |
36 | { |
37 | connect(m_topLevel, SIGNAL(geometryChanged()), SLOT(geometryChanged())); |
38 | } |
39 | |
40 | Shadow::~Shadow() |
41 | { |
42 | } |
43 | |
44 | Shadow *Shadow::createShadow(Toplevel *toplevel) |
45 | { |
46 | if (!effects) { |
47 | return NULL; |
48 | } |
49 | QVector<long> data = Shadow::readX11ShadowProperty(toplevel->window()); |
50 | if (!data.isEmpty()) { |
51 | Shadow *shadow = NULL; |
52 | if (effects->isOpenGLCompositing()) { |
53 | shadow = new SceneOpenGLShadow(toplevel); |
54 | } else if (effects->compositingType() == XRenderCompositing) { |
55 | #ifdef KWIN_HAVE_XRENDER_COMPOSITING |
56 | shadow = new SceneXRenderShadow(toplevel); |
57 | #endif |
58 | } |
59 | |
60 | if (shadow) { |
61 | if (!shadow->init(data)) { |
62 | delete shadow; |
63 | return NULL; |
64 | } |
65 | if (toplevel->effectWindow() && toplevel->effectWindow()->sceneWindow()) { |
66 | toplevel->effectWindow()->sceneWindow()->updateShadow(shadow); |
67 | } |
68 | } |
69 | return shadow; |
70 | } else { |
71 | return NULL; |
72 | } |
73 | } |
74 | |
75 | QVector< long > Shadow::readX11ShadowProperty(WId id) |
76 | { |
77 | QVector<long> ret; |
78 | Atom type; |
79 | int format, status; |
80 | unsigned long nitems = 0; |
81 | unsigned long = 0; |
82 | unsigned char *data = 0; |
83 | status = XGetWindowProperty(display(), id, atoms->kde_net_wm_shadow, 0, 12, false, XA_CARDINAL, &type, &format, &nitems, &extra, &data); |
84 | if (status == Success && type == XA_CARDINAL && format == 32 && nitems == 12) { |
85 | long* shadow = reinterpret_cast< long* >(data); |
86 | ret.reserve(12); |
87 | for (int i=0; i<12; ++i) { |
88 | ret << shadow[i]; |
89 | } |
90 | XFree(data); |
91 | } |
92 | return ret; |
93 | } |
94 | |
95 | bool Shadow::init(const QVector< long > &data) |
96 | { |
97 | for (int i=0; i<ShadowElementsCount; ++i) { |
98 | QPixmap pix = QPixmap::fromX11Pixmap(data[i], QPixmap::ExplicitlyShared); |
99 | if (pix.isNull() || pix.depth() != 32) { |
100 | return false; |
101 | } |
102 | m_shadowElements[i] = pix.copy(0, 0, pix.width(), pix.height()); |
103 | } |
104 | m_topOffset = data[ShadowElementsCount]; |
105 | m_rightOffset = data[ShadowElementsCount+1]; |
106 | m_bottomOffset = data[ShadowElementsCount+2]; |
107 | m_leftOffset = data[ShadowElementsCount+3]; |
108 | updateShadowRegion(); |
109 | if (!prepareBackend()) { |
110 | return false; |
111 | } |
112 | buildQuads(); |
113 | return true; |
114 | } |
115 | |
116 | void Shadow::updateShadowRegion() |
117 | { |
118 | const QRect top(0, - m_topOffset, m_topLevel->width(), m_topOffset); |
119 | const QRect right(m_topLevel->width(), - m_topOffset, m_rightOffset, m_topLevel->height() + m_topOffset + m_bottomOffset); |
120 | const QRect bottom(0, m_topLevel->height(), m_topLevel->width(), m_bottomOffset); |
121 | const QRect left(- m_leftOffset, - m_topOffset, m_leftOffset, m_topLevel->height() + m_topOffset + m_bottomOffset); |
122 | m_shadowRegion = QRegion(top).united(right).united(bottom).united(left); |
123 | } |
124 | |
125 | void Shadow::buildQuads() |
126 | { |
127 | // prepare window quads |
128 | m_shadowQuads.clear(); |
129 | const QSize top(m_shadowElements[ShadowElementTop].size()); |
130 | const QSize topRight(m_shadowElements[ShadowElementTopRight].size()); |
131 | const QSize right(m_shadowElements[ShadowElementRight].size()); |
132 | const QSize bottomRight(m_shadowElements[ShadowElementBottomRight].size()); |
133 | const QSize bottom(m_shadowElements[ShadowElementBottom].size()); |
134 | const QSize bottomLeft(m_shadowElements[ShadowElementBottomLeft].size()); |
135 | const QSize left(m_shadowElements[ShadowElementLeft].size()); |
136 | const QSize topLeft(m_shadowElements[ShadowElementTopLeft].size()); |
137 | if ((left.width() - m_leftOffset > m_topLevel->width()) || |
138 | (right.width() - m_rightOffset > m_topLevel->width()) || |
139 | (top.height() - m_topOffset > m_topLevel->height()) || |
140 | (bottom.height() - m_bottomOffset > m_topLevel->height())) { |
141 | // if our shadow is bigger than the window, we don't render the shadow |
142 | m_shadowRegion = QRegion(); |
143 | return; |
144 | } |
145 | |
146 | const QRect outerRect(QPoint(-m_leftOffset, -m_topOffset), QPoint(m_topLevel->width() + m_rightOffset, m_topLevel->height() + m_bottomOffset)); |
147 | |
148 | WindowQuad topLeftQuad(WindowQuadShadowTopLeft); |
149 | topLeftQuad[ 0 ] = WindowVertex(outerRect.x(), outerRect.y(), 0.0, 0.0); |
150 | topLeftQuad[ 1 ] = WindowVertex(outerRect.x() + topLeft.width(), outerRect.y(), 1.0, 0.0); |
151 | topLeftQuad[ 2 ] = WindowVertex(outerRect.x() + topLeft.width(), outerRect.y() + topLeft.height(), 1.0, 1.0); |
152 | topLeftQuad[ 3 ] = WindowVertex(outerRect.x(), outerRect.y() + topLeft.height(), 0.0, 1.0); |
153 | m_shadowQuads.append(topLeftQuad); |
154 | |
155 | WindowQuad topQuad(WindowQuadShadowTop); |
156 | topQuad[ 0 ] = WindowVertex(outerRect.x() + topLeft.width(), outerRect.y(), 0.0, 0.0); |
157 | topQuad[ 1 ] = WindowVertex(outerRect.right() - topRight.width(), outerRect.y(), 1.0, 0.0); |
158 | topQuad[ 2 ] = WindowVertex(outerRect.right() - topRight.width(), outerRect.y() + top.height(), 1.0, 1.0); |
159 | topQuad[ 3 ] = WindowVertex(outerRect.x() + topLeft.width(), outerRect.y() + top.height(), 0.0, 1.0); |
160 | m_shadowQuads.append(topQuad); |
161 | |
162 | WindowQuad topRightQuad(WindowQuadShadowTopRight); |
163 | topRightQuad[ 0 ] = WindowVertex(outerRect.right() - topRight.width(), outerRect.y(), 0.0, 0.0); |
164 | topRightQuad[ 1 ] = WindowVertex(outerRect.right(), outerRect.y(), 1.0, 0.0); |
165 | topRightQuad[ 2 ] = WindowVertex(outerRect.right(), outerRect.y() + topRight.height(), 1.0, 1.0); |
166 | topRightQuad[ 3 ] = WindowVertex(outerRect.right() - topRight.width(), outerRect.y() + topRight.height(), 0.0, 1.0); |
167 | m_shadowQuads.append(topRightQuad); |
168 | |
169 | WindowQuad rightQuad(WindowQuadShadowRight); |
170 | rightQuad[ 0 ] = WindowVertex(outerRect.right() - right.width(), outerRect.y() + topRight.height(), 0.0, 0.0); |
171 | rightQuad[ 1 ] = WindowVertex(outerRect.right(), outerRect.y() + topRight.height(), 1.0, 0.0); |
172 | rightQuad[ 2 ] = WindowVertex(outerRect.right(), outerRect.bottom() - bottomRight.height(), 1.0, 1.0); |
173 | rightQuad[ 3 ] = WindowVertex(outerRect.right() - right.width(), outerRect.bottom() - bottomRight.height(), 0.0, 1.0); |
174 | m_shadowQuads.append(rightQuad); |
175 | |
176 | WindowQuad bottomRightQuad(WindowQuadShadowBottomRight); |
177 | bottomRightQuad[ 0 ] = WindowVertex(outerRect.right() - bottomRight.width(), outerRect.bottom() - bottomRight.height(), 0.0, 0.0); |
178 | bottomRightQuad[ 1 ] = WindowVertex(outerRect.right(), outerRect.bottom() - bottomRight.height(), 1.0, 0.0); |
179 | bottomRightQuad[ 2 ] = WindowVertex(outerRect.right(), outerRect.bottom(), 1.0, 1.0); |
180 | bottomRightQuad[ 3 ] = WindowVertex(outerRect.right() - bottomRight.width(), outerRect.bottom(), 0.0, 1.0); |
181 | m_shadowQuads.append(bottomRightQuad); |
182 | |
183 | WindowQuad bottomQuad(WindowQuadShadowBottom); |
184 | bottomQuad[ 0 ] = WindowVertex(outerRect.x() + bottomLeft.width(), outerRect.bottom() - bottom.height(), 0.0, 0.0); |
185 | bottomQuad[ 1 ] = WindowVertex(outerRect.right() - bottomRight.width(), outerRect.bottom() - bottom.height(), 1.0, 0.0); |
186 | bottomQuad[ 2 ] = WindowVertex(outerRect.right() - bottomRight.width(), outerRect.bottom(), 1.0, 1.0); |
187 | bottomQuad[ 3 ] = WindowVertex(outerRect.x() + bottomLeft.width(), outerRect.bottom(), 0.0, 1.0); |
188 | m_shadowQuads.append(bottomQuad); |
189 | |
190 | WindowQuad bottomLeftQuad(WindowQuadShadowBottomLeft); |
191 | bottomLeftQuad[ 0 ] = WindowVertex(outerRect.x(), outerRect.bottom() - bottomLeft.height(), 0.0, 0.0); |
192 | bottomLeftQuad[ 1 ] = WindowVertex(outerRect.x() + bottomLeft.width(), outerRect.bottom() - bottomLeft.height(), 1.0, 0.0); |
193 | bottomLeftQuad[ 2 ] = WindowVertex(outerRect.x() + bottomLeft.width(), outerRect.bottom(), 1.0, 1.0); |
194 | bottomLeftQuad[ 3 ] = WindowVertex(outerRect.x(), outerRect.bottom(), 0.0, 1.0); |
195 | m_shadowQuads.append(bottomLeftQuad); |
196 | |
197 | WindowQuad leftQuad(WindowQuadShadowLeft); |
198 | leftQuad[ 0 ] = WindowVertex(outerRect.x(), outerRect.y() + topLeft.height(), 0.0, 0.0); |
199 | leftQuad[ 1 ] = WindowVertex(outerRect.x() + left.width(), outerRect.y() + topLeft.height(), 1.0, 0.0); |
200 | leftQuad[ 2 ] = WindowVertex(outerRect.x() + left.width(), outerRect.bottom() - bottomLeft.height(), 1.0, 1.0); |
201 | leftQuad[ 3 ] = WindowVertex(outerRect.x(), outerRect.bottom() - bottomLeft.height(), 0.0, 1.0); |
202 | m_shadowQuads.append(leftQuad); |
203 | } |
204 | |
205 | bool Shadow::updateShadow() |
206 | { |
207 | QVector<long> data = Shadow::readX11ShadowProperty(m_topLevel->window()); |
208 | if (data.isEmpty()) { |
209 | if (m_topLevel && m_topLevel->effectWindow() && m_topLevel->effectWindow()->sceneWindow() && |
210 | m_topLevel->effectWindow()->sceneWindow()->shadow()) { |
211 | m_topLevel->effectWindow()->sceneWindow()->updateShadow(0); |
212 | m_topLevel->effectWindow()->buildQuads(true); |
213 | } |
214 | deleteLater(); |
215 | return false; |
216 | } |
217 | init(data); |
218 | if (m_topLevel && m_topLevel->effectWindow()) |
219 | m_topLevel->effectWindow()->buildQuads(true); |
220 | return true; |
221 | } |
222 | |
223 | void Shadow::setToplevel(Toplevel *topLevel) |
224 | { |
225 | m_topLevel = topLevel; |
226 | connect(m_topLevel, SIGNAL(geometryChanged()), SLOT(geometryChanged())); |
227 | } |
228 | void Shadow::geometryChanged() |
229 | { |
230 | if (m_cachedSize == m_topLevel->geometry().size()) { |
231 | return; |
232 | } |
233 | m_cachedSize = m_topLevel->geometry().size(); |
234 | updateShadowRegion(); |
235 | buildQuads(); |
236 | } |
237 | |
238 | } // namespace |
239 | |