1 | ////////////////////////////////////////////////////////////////////////////// |
2 | // oxygenblurhelper.cpp |
3 | // handle regions passed to kwin for blurring |
4 | // ------------------- |
5 | // |
6 | // Copyright (c) 2010 Hugo Pereira Da Costa <hugo.pereira@free.fr> |
7 | // |
8 | // Loosely inspired (and largely rewritten) from BeSpin style |
9 | // Copyright (C) 2007 Thomas Luebking <thomas.luebking@web.de> |
10 | // |
11 | // Permission is hereby granted, free of charge, to any person obtaining a copy |
12 | // of this software and associated documentation files (the "Software"), to |
13 | // deal in the Software without restriction, including without limitation the |
14 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
15 | // sell copies of the Software, and to permit persons to whom the Software is |
16 | // furnished to do so, subject to the following conditions: |
17 | // |
18 | // The above copyright notice and this permission notice shall be included in |
19 | // all copies or substantial portions of the Software. |
20 | // |
21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
27 | // IN THE SOFTWARE. |
28 | ////////////////////////////////////////////////////////////////////////////// |
29 | |
30 | #include "oxygenblurhelper.h" |
31 | #include "oxygenblurhelper.moc" |
32 | |
33 | #include "oxygenstyleconfigdata.h" |
34 | |
35 | #include <QtCore/QEvent> |
36 | #include <QtCore/QVector> |
37 | #include <QtGui/QProgressBar> |
38 | #include <QtGui/QPushButton> |
39 | |
40 | #ifdef Q_WS_X11 |
41 | #include <QtGui/QX11Info> |
42 | #include <X11/Xlib.h> |
43 | #include <X11/Xatom.h> |
44 | #endif |
45 | |
46 | namespace Oxygen |
47 | { |
48 | |
49 | //___________________________________________________________ |
50 | BlurHelper::BlurHelper( QObject* parent, StyleHelper& helper ): |
51 | QObject( parent ), |
52 | _helper( helper ), |
53 | _enabled( false ) |
54 | { |
55 | |
56 | #ifdef Q_WS_X11 |
57 | |
58 | // create atom |
59 | _blurAtom = XInternAtom( QX11Info::display(), "_KDE_NET_WM_BLUR_BEHIND_REGION" , False); |
60 | _opaqueAtom = XInternAtom( QX11Info::display(), "_NET_WM_OPAQUE_REGION" , False); |
61 | |
62 | #endif |
63 | |
64 | } |
65 | |
66 | //___________________________________________________________ |
67 | void BlurHelper::registerWidget( QWidget* widget ) |
68 | { |
69 | |
70 | // check if already registered |
71 | if( _widgets.contains( widget ) ) return; |
72 | |
73 | // install event filter |
74 | addEventFilter( widget ); |
75 | |
76 | // add to widgets list |
77 | _widgets.insert( widget ); |
78 | |
79 | // cleanup on destruction |
80 | connect( widget, SIGNAL(destroyed(QObject*)), SLOT(widgetDestroyed(QObject*)) ); |
81 | |
82 | if( enabled() ) |
83 | { |
84 | // schedule shadow area repaint |
85 | _pendingWidgets.insert( widget, widget ); |
86 | update(); |
87 | } |
88 | |
89 | } |
90 | |
91 | //___________________________________________________________ |
92 | void BlurHelper::unregisterWidget( QWidget* widget ) |
93 | { |
94 | // remove event filter |
95 | widget->removeEventFilter( this ); |
96 | |
97 | // remove from widgets |
98 | _widgets.remove( widget ); |
99 | |
100 | if( isTransparent( widget ) ) clear( widget ); |
101 | } |
102 | |
103 | //___________________________________________________________ |
104 | bool BlurHelper::eventFilter( QObject* object, QEvent* event ) |
105 | { |
106 | |
107 | // do nothing if not enabled |
108 | if( !enabled() ) return false; |
109 | |
110 | switch( event->type() ) |
111 | { |
112 | |
113 | case QEvent::Hide: |
114 | { |
115 | QWidget* widget( qobject_cast<QWidget*>( object ) ); |
116 | if( widget && isOpaque( widget ) && isTransparent( widget->window() ) ) |
117 | { |
118 | QWidget* window( widget->window() ); |
119 | _pendingWidgets.insert( window, window ); |
120 | update(); |
121 | } |
122 | break; |
123 | |
124 | } |
125 | |
126 | case QEvent::Show: |
127 | case QEvent::Resize: |
128 | { |
129 | |
130 | // cast to widget and check |
131 | QWidget* widget( qobject_cast<QWidget*>( object ) ); |
132 | if( !widget ) break; |
133 | if( isTransparent( widget ) ) |
134 | { |
135 | |
136 | _pendingWidgets.insert( widget, widget ); |
137 | update(); |
138 | |
139 | } else if( isOpaque( widget ) ) { |
140 | |
141 | QWidget* window( widget->window() ); |
142 | if( isTransparent( window ) ) |
143 | { |
144 | _pendingWidgets.insert( window, window ); |
145 | update(); |
146 | } |
147 | |
148 | } |
149 | |
150 | break; |
151 | } |
152 | |
153 | default: break; |
154 | |
155 | } |
156 | |
157 | // never eat events |
158 | return false; |
159 | |
160 | } |
161 | |
162 | //___________________________________________________________ |
163 | QRegion BlurHelper::blurRegion( QWidget* widget ) const |
164 | { |
165 | |
166 | if( !widget->isVisible() ) return QRegion(); |
167 | |
168 | // get main region |
169 | QRegion region; |
170 | if( |
171 | qobject_cast<const QDockWidget*>( widget ) || |
172 | qobject_cast<const QMenu*>( widget ) || |
173 | qobject_cast<const QToolBar*>( widget ) || |
174 | widget->inherits( "QComboBoxPrivateContainer" ) ) |
175 | { |
176 | |
177 | region = _helper.roundedMask( widget->rect() ); |
178 | |
179 | } else region = widget->mask().isEmpty() ? widget->rect():widget->mask(); |
180 | |
181 | |
182 | // trim blur region to remove unnecessary areas |
183 | trimBlurRegion( widget, widget, region ); |
184 | return region; |
185 | |
186 | } |
187 | |
188 | //___________________________________________________________ |
189 | void BlurHelper::trimBlurRegion( QWidget* parent, QWidget* widget, QRegion& region ) const |
190 | { |
191 | |
192 | |
193 | // loop over children |
194 | foreach( QObject* childObject, widget->children() ) |
195 | { |
196 | QWidget* child( qobject_cast<QWidget*>( childObject ) ); |
197 | if( !(child && child->isVisible()) ) continue; |
198 | |
199 | if( isOpaque( child ) ) |
200 | { |
201 | |
202 | const QPoint offset( child->mapTo( parent, QPoint( 0, 0 ) ) ); |
203 | if( child->mask().isEmpty() ) |
204 | { |
205 | const QRect rect( child->rect().translated( offset ).adjusted( 1, 1, -1, -1 ) ); |
206 | region -= rect; |
207 | |
208 | } else region -= child->mask().translated( offset ); |
209 | |
210 | } else { trimBlurRegion( parent, child, region ); } |
211 | |
212 | } |
213 | |
214 | return; |
215 | |
216 | } |
217 | |
218 | //___________________________________________________________ |
219 | void BlurHelper::update( QWidget* widget ) const |
220 | { |
221 | |
222 | #ifdef Q_WS_X11 |
223 | |
224 | /* |
225 | directly from bespin code. Supposibly prevent playing with some 'pseudo-widgets' |
226 | that have winId matching some other -random- window |
227 | */ |
228 | if( !(widget->testAttribute(Qt::WA_WState_Created) || widget->internalWinId() )) |
229 | { return; } |
230 | |
231 | const QRegion blurRegion( this->blurRegion( widget ) ); |
232 | const QRegion opaqueRegion = QRegion(0, 0, widget->width(), widget->height()) - blurRegion; |
233 | if( blurRegion.isEmpty() ) { |
234 | |
235 | clear( widget ); |
236 | |
237 | } else { |
238 | |
239 | QVector<unsigned long> data; |
240 | foreach( const QRect& rect, blurRegion.rects() ) |
241 | { data << rect.x() << rect.y() << rect.width() << rect.height(); } |
242 | |
243 | XChangeProperty( |
244 | QX11Info::display(), widget->winId(), _blurAtom, XA_CARDINAL, 32, PropModeReplace, |
245 | reinterpret_cast<const unsigned char *>(data.constData()), data.size() ); |
246 | |
247 | data.clear(); |
248 | foreach( const QRect& rect, opaqueRegion.rects() ) |
249 | { data << rect.x() << rect.y() << rect.width() << rect.height(); } |
250 | |
251 | XChangeProperty( |
252 | QX11Info::display(), widget->winId(), _opaqueAtom, XA_CARDINAL, 32, PropModeReplace, |
253 | reinterpret_cast<const unsigned char *>(data.constData()), data.size() ); |
254 | } |
255 | |
256 | // force update |
257 | if( widget->isVisible() ) |
258 | { widget->update(); } |
259 | |
260 | #endif |
261 | |
262 | } |
263 | |
264 | |
265 | //___________________________________________________________ |
266 | void BlurHelper::clear( QWidget* widget ) const |
267 | { |
268 | #ifdef Q_WS_X11 |
269 | XDeleteProperty( QX11Info::display(), widget->winId(), _blurAtom ); |
270 | XDeleteProperty( QX11Info::display(), widget->winId(), _opaqueAtom ); |
271 | #endif |
272 | |
273 | } |
274 | |
275 | //___________________________________________________________ |
276 | bool BlurHelper::isOpaque( const QWidget* widget ) const |
277 | { |
278 | |
279 | return |
280 | (!widget->isWindow()) && |
281 | ( (widget->autoFillBackground() && widget->palette().color( widget->backgroundRole() ).alpha() == 0xff ) || |
282 | widget->testAttribute(Qt::WA_OpaquePaintEvent) ); |
283 | |
284 | } |
285 | |
286 | //___________________________________________________________ |
287 | bool BlurHelper::isTransparent( const QWidget* widget ) const |
288 | { |
289 | |
290 | return |
291 | widget->isWindow() && |
292 | widget->testAttribute( Qt::WA_TranslucentBackground ) && |
293 | |
294 | // widgets using qgraphicsview |
295 | !( widget->graphicsProxyWidget() || |
296 | widget->inherits( "Plasma::Dialog" ) ) && |
297 | |
298 | // flags and special widgets |
299 | ( widget->testAttribute( Qt::WA_StyledBackground ) || |
300 | qobject_cast<const QMenu*>( widget ) || |
301 | qobject_cast<const QDockWidget*>( widget ) || |
302 | qobject_cast<const QToolBar*>( widget ) || |
303 | widget->windowType() == Qt::ToolTip ) && |
304 | _helper.hasAlphaChannel( widget ); |
305 | } |
306 | |
307 | } |
308 | |