1/********************************************************************
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5Copyright (C) 2011 Martin Gräßlin <mgraesslin@kde.org>
6
7This program is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 2 of the License, or
10(at your option) any later version.
11
12This program is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along 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
30namespace KWin
31{
32
33Shadow::Shadow(Toplevel *toplevel)
34 : m_topLevel(toplevel)
35 , m_cachedSize(toplevel->geometry().size())
36{
37 connect(m_topLevel, SIGNAL(geometryChanged()), SLOT(geometryChanged()));
38}
39
40Shadow::~Shadow()
41{
42}
43
44Shadow *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
75QVector< 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 extra = 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
95bool 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
116void 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
125void 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
205bool 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
223void Shadow::setToplevel(Toplevel *topLevel)
224{
225 m_topLevel = topLevel;
226 connect(m_topLevel, SIGNAL(geometryChanged()), SLOT(geometryChanged()));
227}
228void 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