1/********************************************************************
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5Copyright (C) 2006 Lubos Lunak <l.lunak@kde.org>
6Copyright (C) 2010, 2011 Martin Gräßlin <mgraesslin@kde.org>
7
8This program is free software; you can redistribute it and/or modify
9it under the terms of the GNU General Public License as published by
10the Free Software Foundation; either version 2 of the License, or
11(at your option) any later version.
12
13This program is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License
19along with this program. If not, see <http://www.gnu.org/licenses/>.
20*********************************************************************/
21
22#include "effects.h"
23
24#include "effectsadaptor.h"
25#ifdef KWIN_BUILD_ACTIVITIES
26#include "activities.h"
27#endif
28#include "decorations.h"
29#include "deleted.h"
30#include "client.h"
31#include "cursor.h"
32#include "group.h"
33#include "scene_xrender.h"
34#include "scene_opengl.h"
35#include "unmanaged.h"
36#ifdef KWIN_BUILD_TABBOX
37#include "tabbox.h"
38#endif
39#ifdef KWIN_BUILD_SCREENEDGES
40#include "screenedge.h"
41#endif
42#ifdef KWIN_BUILD_SCRIPTING
43#include "scripting/scriptedeffect.h"
44#endif
45#include "screens.h"
46#include "thumbnailitem.h"
47#include "virtualdesktops.h"
48#include "workspace.h"
49#include "kwinglutils.h"
50
51#include <QFile>
52#include <QFutureWatcher>
53#include <QtConcurrentRun>
54#include <QDBusServiceWatcher>
55#include <QDBusPendingCallWatcher>
56
57#include <KDE/KDebug>
58#include <KDE/KLibrary>
59#include <KDE/KDesktopFile>
60#include <KDE/KConfigGroup>
61#include <KDE/KGlobal>
62#include <KDE/KStandardDirs>
63#include <KDE/KService>
64#include <KDE/KServiceTypeTrader>
65#include <KDE/KPluginInfo>
66#include <Plasma/Theme>
67
68#include <assert.h>
69#include "composite.h"
70#include "xcbutils.h"
71
72// dbus generated
73#include "screenlocker_interface.h"
74
75
76namespace KWin
77{
78
79static const QString SCREEN_LOCKER_SERVICE_NAME = QString("org.freedesktop.ScreenSaver");
80
81ScreenLockerWatcher::ScreenLockerWatcher(QObject *parent)
82 : QObject(parent)
83 , m_interface(NULL)
84 , m_serviceWatcher(new QDBusServiceWatcher(this))
85 , m_locked(false)
86{
87 connect(m_serviceWatcher, SIGNAL(serviceOwnerChanged(QString,QString,QString)), SLOT(serviceOwnerChanged(QString,QString,QString)));
88 m_serviceWatcher->setWatchMode(QDBusServiceWatcher::WatchForOwnerChange);
89 m_serviceWatcher->addWatchedService(SCREEN_LOCKER_SERVICE_NAME);
90 // check whether service is registered
91 QFutureWatcher<QDBusReply<bool> > *watcher = new QFutureWatcher<QDBusReply<bool> >(this);
92 connect(watcher, SIGNAL(finished()), SLOT(serviceRegisteredQueried()));
93 connect(watcher, SIGNAL(canceled()), watcher, SLOT(deleteLater()));
94 watcher->setFuture(QtConcurrent::run(QDBusConnection::sessionBus().interface(),
95 &QDBusConnectionInterface::isServiceRegistered,
96 SCREEN_LOCKER_SERVICE_NAME));
97}
98
99ScreenLockerWatcher::~ScreenLockerWatcher()
100{
101}
102
103void ScreenLockerWatcher::serviceOwnerChanged(const QString &serviceName, const QString &oldOwner, const QString &newOwner)
104{
105 Q_UNUSED(oldOwner)
106 if (serviceName != SCREEN_LOCKER_SERVICE_NAME) {
107 return;
108 }
109 delete m_interface;
110 m_interface = NULL;
111 m_locked = false;
112 if (!newOwner.isEmpty()) {
113 m_interface = new OrgFreedesktopScreenSaverInterface(newOwner, QString(), QDBusConnection::sessionBus(), this);
114 connect(m_interface, SIGNAL(ActiveChanged(bool)), SLOT(setLocked(bool)));
115 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(m_interface->GetActive(), this);
116 connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), SLOT(activeQueried(QDBusPendingCallWatcher*)));
117 }
118}
119
120void ScreenLockerWatcher::serviceRegisteredQueried()
121{
122 QFutureWatcher<QDBusReply<bool> > *watcher = dynamic_cast<QFutureWatcher<QDBusReply<bool> > *>(sender());
123 if (!watcher) {
124 return;
125 }
126 const QDBusReply<bool> &reply = watcher->result();
127 if (reply.isValid() && reply.value()) {
128 QFutureWatcher<QDBusReply<QString> > *ownerWatcher = new QFutureWatcher<QDBusReply<QString> >(this);
129 connect(ownerWatcher, SIGNAL(finished()), SLOT(serviceOwnerQueried()));
130 connect(ownerWatcher, SIGNAL(canceled()), ownerWatcher, SLOT(deleteLater()));
131 ownerWatcher->setFuture(QtConcurrent::run(QDBusConnection::sessionBus().interface(),
132 &QDBusConnectionInterface::serviceOwner,
133 SCREEN_LOCKER_SERVICE_NAME));
134 }
135 watcher->deleteLater();
136}
137
138void ScreenLockerWatcher::serviceOwnerQueried()
139{
140 QFutureWatcher<QDBusReply<QString> > *watcher = dynamic_cast<QFutureWatcher<QDBusReply<QString> > *>(sender());
141 if (!watcher) {
142 return;
143 }
144 const QDBusReply<QString> reply = watcher->result();
145 if (reply.isValid()) {
146 serviceOwnerChanged(SCREEN_LOCKER_SERVICE_NAME, QString(), reply.value());
147 }
148
149 watcher->deleteLater();
150}
151
152void ScreenLockerWatcher::activeQueried(QDBusPendingCallWatcher *watcher)
153{
154 QDBusPendingReply<bool> reply = *watcher;
155 if (!reply.isError()) {
156 setLocked(reply.value());
157 }
158 watcher->deleteLater();
159}
160
161void ScreenLockerWatcher::setLocked(bool activated)
162{
163 if (m_locked == activated) {
164 return;
165 }
166 m_locked = activated;
167 emit locked(m_locked);
168}
169
170//---------------------
171// Static
172
173static QByteArray readWindowProperty(Window win, long atom, long type, int format)
174{
175 int len = 32768;
176 for (;;) {
177 unsigned char* data;
178 Atom rtype;
179 int rformat;
180 unsigned long nitems, after;
181 if (XGetWindowProperty(QX11Info::display(), win,
182 atom, 0, len, False, AnyPropertyType,
183 &rtype, &rformat, &nitems, &after, &data) == Success) {
184 if (after > 0) {
185 XFree(data);
186 len *= 2;
187 continue;
188 }
189 if (long(rtype) == type && rformat == format) {
190 int bytelen = format == 8 ? nitems : format == 16 ? nitems * sizeof(short) : nitems * sizeof(long);
191 QByteArray ret(reinterpret_cast< const char* >(data), bytelen);
192 XFree(data);
193 return ret;
194 } else { // wrong format, type or something
195 XFree(data);
196 return QByteArray();
197 }
198 } else // XGetWindowProperty() failed
199 return QByteArray();
200 }
201}
202
203static void deleteWindowProperty(Window win, long int atom)
204{
205 XDeleteProperty(QX11Info::display(), win, atom);
206}
207
208//---------------------
209
210EffectsHandlerImpl::EffectsHandlerImpl(Compositor *compositor, Scene *scene)
211 : EffectsHandler(scene->compositingType())
212 , keyboard_grab_effect(NULL)
213 , fullscreen_effect(0)
214 , next_window_quad_type(EFFECT_QUAD_TYPE_START)
215 , m_compositor(compositor)
216 , m_scene(scene)
217 , m_screenLockerWatcher(new ScreenLockerWatcher(this))
218 , m_desktopRendering(false)
219 , m_currentRenderedDesktop(0)
220{
221 new EffectsAdaptor(this);
222 QDBusConnection dbus = QDBusConnection::sessionBus();
223 dbus.registerObject("/Effects", this);
224 dbus.registerService("org.kde.kwin.Effects");
225 // init is important, otherwise causes crashes when quads are build before the first painting pass start
226 m_currentBuildQuadsIterator = m_activeEffects.constEnd();
227
228 Workspace *ws = Workspace::self();
229 VirtualDesktopManager *vds = VirtualDesktopManager::self();
230 connect(ws, SIGNAL(currentDesktopChanged(int,KWin::Client*)), SLOT(slotDesktopChanged(int,KWin::Client*)));
231 connect(ws, SIGNAL(desktopPresenceChanged(KWin::Client*,int)), SLOT(slotDesktopPresenceChanged(KWin::Client*,int)));
232 connect(ws, SIGNAL(clientAdded(KWin::Client*)), this, SLOT(slotClientAdded(KWin::Client*)));
233 connect(ws, SIGNAL(unmanagedAdded(KWin::Unmanaged*)), this, SLOT(slotUnmanagedAdded(KWin::Unmanaged*)));
234 connect(ws, SIGNAL(clientActivated(KWin::Client*)), this, SLOT(slotClientActivated(KWin::Client*)));
235 connect(ws, SIGNAL(deletedRemoved(KWin::Deleted*)), this, SLOT(slotDeletedRemoved(KWin::Deleted*)));
236 connect(vds, SIGNAL(countChanged(uint,uint)), SIGNAL(numberDesktopsChanged(uint)));
237 connect(Cursor::self(), SIGNAL(mouseChanged(QPoint,QPoint,Qt::MouseButtons,Qt::MouseButtons,Qt::KeyboardModifiers,Qt::KeyboardModifiers)),
238 SIGNAL(mouseChanged(QPoint,QPoint,Qt::MouseButtons,Qt::MouseButtons,Qt::KeyboardModifiers,Qt::KeyboardModifiers)));
239 connect(ws, SIGNAL(propertyNotify(long)), this, SLOT(slotPropertyNotify(long)));
240#ifdef KWIN_BUILD_ACTIVITIES
241 Activities *activities = Activities::self();
242 connect(activities, SIGNAL(added(QString)), SIGNAL(activityAdded(QString)));
243 connect(activities, SIGNAL(removed(QString)), SIGNAL(activityRemoved(QString)));
244 connect(activities, SIGNAL(currentChanged(QString)), SIGNAL(currentActivityChanged(QString)));
245#endif
246 connect(ws, SIGNAL(stackingOrderChanged()), SIGNAL(stackingOrderChanged()));
247#ifdef KWIN_BUILD_TABBOX
248 TabBox::TabBox *tabBox = TabBox::TabBox::self();
249 connect(tabBox, SIGNAL(tabBoxAdded(int)), SIGNAL(tabBoxAdded(int)));
250 connect(tabBox, SIGNAL(tabBoxUpdated()), SIGNAL(tabBoxUpdated()));
251 connect(tabBox, SIGNAL(tabBoxClosed()), SIGNAL(tabBoxClosed()));
252 connect(tabBox, SIGNAL(tabBoxKeyEvent(QKeyEvent*)), SIGNAL(tabBoxKeyEvent(QKeyEvent*)));
253#endif
254#ifdef KWIN_BUILD_SCREENEDGES
255 connect(ScreenEdges::self(), SIGNAL(approaching(ElectricBorder,qreal,QRect)), SIGNAL(screenEdgeApproaching(ElectricBorder,qreal,QRect)));
256#endif
257 connect(m_screenLockerWatcher, SIGNAL(locked(bool)), SIGNAL(screenLockingChanged(bool)));
258 // connect all clients
259 foreach (Client *c, ws->clientList()) {
260 setupClientConnections(c);
261 }
262 foreach (Unmanaged *u, ws->unmanagedList()) {
263 setupUnmanagedConnections(u);
264 }
265 reconfigure();
266}
267
268EffectsHandlerImpl::~EffectsHandlerImpl()
269{
270 if (keyboard_grab_effect != NULL)
271 ungrabKeyboard();
272 foreach (const EffectPair & ep, loaded_effects)
273 unloadEffect(ep.first);
274}
275
276void EffectsHandlerImpl::setupClientConnections(Client* c)
277{
278 connect(c, SIGNAL(windowClosed(KWin::Toplevel*,KWin::Deleted*)), this, SLOT(slotWindowClosed(KWin::Toplevel*)));
279 connect(c, SIGNAL(clientMaximizedStateChanged(KWin::Client*,KDecorationDefines::MaximizeMode)), this, SLOT(slotClientMaximized(KWin::Client*,KDecorationDefines::MaximizeMode)));
280 connect(c, SIGNAL(clientStartUserMovedResized(KWin::Client*)), this, SLOT(slotClientStartUserMovedResized(KWin::Client*)));
281 connect(c, SIGNAL(clientStepUserMovedResized(KWin::Client*,QRect)), this, SLOT(slotClientStepUserMovedResized(KWin::Client*,QRect)));
282 connect(c, SIGNAL(clientFinishUserMovedResized(KWin::Client*)), this, SLOT(slotClientFinishUserMovedResized(KWin::Client*)));
283 connect(c, SIGNAL(opacityChanged(KWin::Toplevel*,qreal)), this, SLOT(slotOpacityChanged(KWin::Toplevel*,qreal)));
284 connect(c, SIGNAL(clientMinimized(KWin::Client*,bool)), this, SLOT(slotClientMinimized(KWin::Client*,bool)));
285 connect(c, SIGNAL(clientUnminimized(KWin::Client*,bool)), this, SLOT(slotClientUnminimized(KWin::Client*,bool)));
286 connect(c, SIGNAL(modalChanged()), this, SLOT(slotClientModalityChanged()));
287 connect(c, SIGNAL(geometryShapeChanged(KWin::Toplevel*,QRect)), this, SLOT(slotGeometryShapeChanged(KWin::Toplevel*,QRect)));
288 connect(c, SIGNAL(paddingChanged(KWin::Toplevel*,QRect)), this, SLOT(slotPaddingChanged(KWin::Toplevel*,QRect)));
289 connect(c, SIGNAL(damaged(KWin::Toplevel*,QRect)), this, SLOT(slotWindowDamaged(KWin::Toplevel*,QRect)));
290 connect(c, SIGNAL(propertyNotify(KWin::Toplevel*,long)), this, SLOT(slotPropertyNotify(KWin::Toplevel*,long)));
291}
292
293void EffectsHandlerImpl::setupUnmanagedConnections(Unmanaged* u)
294{
295 connect(u, SIGNAL(windowClosed(KWin::Toplevel*,KWin::Deleted*)), this, SLOT(slotWindowClosed(KWin::Toplevel*)));
296 connect(u, SIGNAL(opacityChanged(KWin::Toplevel*,qreal)), this, SLOT(slotOpacityChanged(KWin::Toplevel*,qreal)));
297 connect(u, SIGNAL(geometryShapeChanged(KWin::Toplevel*,QRect)), this, SLOT(slotGeometryShapeChanged(KWin::Toplevel*,QRect)));
298 connect(u, SIGNAL(paddingChanged(KWin::Toplevel*,QRect)), this, SLOT(slotPaddingChanged(KWin::Toplevel*,QRect)));
299 connect(u, SIGNAL(damaged(KWin::Toplevel*,QRect)), this, SLOT(slotWindowDamaged(KWin::Toplevel*,QRect)));
300 connect(u, SIGNAL(propertyNotify(KWin::Toplevel*,long)), this, SLOT(slotPropertyNotify(KWin::Toplevel*,long)));
301}
302
303void EffectsHandlerImpl::reconfigure()
304{
305 // perform querying for the services in a thread
306 QFutureWatcher<KService::List> *watcher = new QFutureWatcher<KService::List>(this);
307 connect(watcher, SIGNAL(finished()), this, SLOT(slotEffectsQueried()));
308 watcher->setFuture(QtConcurrent::run(KServiceTypeTrader::self(), &KServiceTypeTrader::query, QString("KWin/Effect"), QString()));
309 watcher->waitForFinished(); // TODO: remove once KConfigGroup is thread safe, bug #321576
310}
311
312void EffectsHandlerImpl::slotEffectsQueried()
313{
314 QFutureWatcher<KService::List> *watcher = dynamic_cast< QFutureWatcher<KService::List>* >(sender());
315 if (!watcher) {
316 // slot invoked not from a FutureWatcher
317 return;
318 }
319
320 KService::List offers = watcher->result();
321 QStringList effectsToBeLoaded;
322 QStringList checkDefault;
323 KSharedConfig::Ptr _config = KGlobal::config();
324 KConfigGroup conf(_config, "Plugins");
325
326 // First unload necessary effects
327 foreach (const KService::Ptr & service, offers) {
328 KPluginInfo plugininfo(service);
329 plugininfo.load(conf);
330
331 if (plugininfo.isPluginEnabledByDefault()) {
332 const QString key = plugininfo.pluginName() + QString::fromLatin1("Enabled");
333 if (!conf.hasKey(key))
334 checkDefault.append(plugininfo.pluginName());
335 }
336
337 bool isloaded = isEffectLoaded(plugininfo.pluginName());
338 bool shouldbeloaded = plugininfo.isPluginEnabled();
339 if (!shouldbeloaded && isloaded)
340 unloadEffect(plugininfo.pluginName());
341 if (shouldbeloaded)
342 effectsToBeLoaded.append(plugininfo.pluginName());
343 }
344 QStringList newLoaded;
345 // Then load those that should be loaded
346 foreach (const QString & effectName, effectsToBeLoaded) {
347 if (!isEffectLoaded(effectName)) {
348 if (loadEffect(effectName, checkDefault.contains(effectName)))
349 newLoaded.append(effectName);
350 }
351 }
352 foreach (const EffectPair & ep, loaded_effects) {
353 if (!newLoaded.contains(ep.first)) // don't reconfigure newly loaded effects
354 ep.second->reconfigure(Effect::ReconfigureAll);
355 }
356 watcher->deleteLater();
357}
358
359// the idea is that effects call this function again which calls the next one
360void EffectsHandlerImpl::prePaintScreen(ScreenPrePaintData& data, int time)
361{
362 if (m_currentPaintScreenIterator != m_activeEffects.constEnd()) {
363 (*m_currentPaintScreenIterator++)->prePaintScreen(data, time);
364 --m_currentPaintScreenIterator;
365 }
366 // no special final code
367}
368
369void EffectsHandlerImpl::paintScreen(int mask, QRegion region, ScreenPaintData& data)
370{
371 if (m_currentPaintScreenIterator != m_activeEffects.constEnd()) {
372 (*m_currentPaintScreenIterator++)->paintScreen(mask, region, data);
373 --m_currentPaintScreenIterator;
374 } else
375 m_scene->finalPaintScreen(mask, region, data);
376}
377
378void EffectsHandlerImpl::paintDesktop(int desktop, int mask, QRegion region, ScreenPaintData &data)
379{
380 if (desktop < 1 || desktop > numberOfDesktops()) {
381 return;
382 }
383 m_currentRenderedDesktop = desktop;
384 m_desktopRendering = true;
385 // save the paint screen iterator
386 EffectsIterator savedIterator = m_currentPaintScreenIterator;
387 m_currentPaintScreenIterator = m_activeEffects.constBegin();
388 effects->paintScreen(mask, region, data);
389 // restore the saved iterator
390 m_currentPaintScreenIterator = savedIterator;
391 m_desktopRendering = false;
392}
393
394void EffectsHandlerImpl::postPaintScreen()
395{
396 if (m_currentPaintScreenIterator != m_activeEffects.constEnd()) {
397 (*m_currentPaintScreenIterator++)->postPaintScreen();
398 --m_currentPaintScreenIterator;
399 }
400 // no special final code
401}
402
403void EffectsHandlerImpl::prePaintWindow(EffectWindow* w, WindowPrePaintData& data, int time)
404{
405 if (m_currentPaintWindowIterator != m_activeEffects.constEnd()) {
406 (*m_currentPaintWindowIterator++)->prePaintWindow(w, data, time);
407 --m_currentPaintWindowIterator;
408 }
409 // no special final code
410}
411
412void EffectsHandlerImpl::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data)
413{
414 if (m_currentPaintWindowIterator != m_activeEffects.constEnd()) {
415 (*m_currentPaintWindowIterator++)->paintWindow(w, mask, region, data);
416 --m_currentPaintWindowIterator;
417 } else
418 m_scene->finalPaintWindow(static_cast<EffectWindowImpl*>(w), mask, region, data);
419}
420
421void EffectsHandlerImpl::paintEffectFrame(EffectFrame* frame, QRegion region, double opacity, double frameOpacity)
422{
423 if (m_currentPaintEffectFrameIterator != m_activeEffects.constEnd()) {
424 (*m_currentPaintEffectFrameIterator++)->paintEffectFrame(frame, region, opacity, frameOpacity);
425 --m_currentPaintEffectFrameIterator;
426 } else {
427 const EffectFrameImpl* frameImpl = static_cast<const EffectFrameImpl*>(frame);
428 frameImpl->finalRender(region, opacity, frameOpacity);
429 }
430}
431
432void EffectsHandlerImpl::postPaintWindow(EffectWindow* w)
433{
434 if (m_currentPaintWindowIterator != m_activeEffects.constEnd()) {
435 (*m_currentPaintWindowIterator++)->postPaintWindow(w);
436 --m_currentPaintWindowIterator;
437 }
438 // no special final code
439}
440
441Effect *EffectsHandlerImpl::provides(Effect::Feature ef)
442{
443 for (int i = 0; i < loaded_effects.size(); ++i)
444 if (loaded_effects.at(i).second->provides(ef))
445 return loaded_effects.at(i).second;
446 return NULL;
447}
448
449void EffectsHandlerImpl::drawWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data)
450{
451 if (m_currentDrawWindowIterator != m_activeEffects.constEnd()) {
452 (*m_currentDrawWindowIterator++)->drawWindow(w, mask, region, data);
453 --m_currentDrawWindowIterator;
454 } else
455 m_scene->finalDrawWindow(static_cast<EffectWindowImpl*>(w), mask, region, data);
456}
457
458void EffectsHandlerImpl::buildQuads(EffectWindow* w, WindowQuadList& quadList)
459{
460 static bool initIterator = true;
461 if (initIterator) {
462 m_currentBuildQuadsIterator = m_activeEffects.constBegin();
463 initIterator = false;
464 }
465 if (m_currentBuildQuadsIterator != m_activeEffects.constEnd()) {
466 (*m_currentBuildQuadsIterator++)->buildQuads(w, quadList);
467 --m_currentBuildQuadsIterator;
468 }
469 if (m_currentBuildQuadsIterator == m_activeEffects.constBegin())
470 initIterator = true;
471}
472
473bool EffectsHandlerImpl::hasDecorationShadows() const
474{
475 return decorationPlugin()->hasShadows();
476}
477
478bool EffectsHandlerImpl::decorationsHaveAlpha() const
479{
480 return decorationPlugin()->hasAlpha();
481}
482
483bool EffectsHandlerImpl::decorationSupportsBlurBehind() const
484{
485 return decorationPlugin()->supportsBlurBehind();
486}
487
488// start another painting pass
489void EffectsHandlerImpl::startPaint()
490{
491 m_activeEffects.clear();
492 m_activeEffects.reserve(loaded_effects.count());
493 for(QVector< KWin::EffectPair >::const_iterator it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
494 if (it->second->isActive()) {
495 m_activeEffects << it->second;
496 }
497 }
498 m_currentDrawWindowIterator = m_activeEffects.constBegin();
499 m_currentPaintWindowIterator = m_activeEffects.constBegin();
500 m_currentPaintScreenIterator = m_activeEffects.constBegin();
501 m_currentPaintEffectFrameIterator = m_activeEffects.constBegin();
502}
503
504void EffectsHandlerImpl::slotClientMaximized(KWin::Client *c, KDecorationDefines::MaximizeMode maxMode)
505{
506 bool horizontal = false;
507 bool vertical = false;
508 switch (maxMode) {
509 case KDecorationDefines::MaximizeHorizontal:
510 horizontal = true;
511 break;
512 case KDecorationDefines::MaximizeVertical:
513 vertical = true;
514 break;
515 case KDecorationDefines::MaximizeFull:
516 horizontal = true;
517 vertical = true;
518 break;
519 case KDecorationDefines::MaximizeRestore: // fall through
520 default:
521 // default - nothing to do
522 break;
523 }
524 if (EffectWindowImpl *w = c->effectWindow()) {
525 emit windowMaximizedStateChanged(w, horizontal, vertical);
526 }
527}
528
529void EffectsHandlerImpl::slotClientStartUserMovedResized(Client *c)
530{
531 emit windowStartUserMovedResized(c->effectWindow());
532}
533
534void EffectsHandlerImpl::slotClientFinishUserMovedResized(Client *c)
535{
536 emit windowFinishUserMovedResized(c->effectWindow());
537}
538
539void EffectsHandlerImpl::slotClientStepUserMovedResized(Client* c, const QRect& geometry)
540{
541 emit windowStepUserMovedResized(c->effectWindow(), geometry);
542}
543
544void EffectsHandlerImpl::slotOpacityChanged(Toplevel *t, qreal oldOpacity)
545{
546 if (t->opacity() == oldOpacity || !t->effectWindow()) {
547 return;
548 }
549 emit windowOpacityChanged(t->effectWindow(), oldOpacity, (qreal)t->opacity());
550}
551
552void EffectsHandlerImpl::slotClientAdded(Client *c)
553{
554 if (c->readyForPainting())
555 slotClientShown(c);
556 else
557 connect(c, SIGNAL(windowShown(KWin::Toplevel*)), SLOT(slotClientShown(KWin::Toplevel*)));
558}
559
560void EffectsHandlerImpl::slotUnmanagedAdded(Unmanaged *u)
561{
562 // it's never initially ready but has synthetic 50ms delay
563 connect(u, SIGNAL(windowShown(KWin::Toplevel*)), SLOT(slotUnmanagedShown(KWin::Toplevel*)));
564}
565
566void EffectsHandlerImpl::slotClientShown(KWin::Toplevel *t)
567{
568 Q_ASSERT(dynamic_cast<Client*>(t));
569 Client *c = static_cast<Client*>(t);
570 setupClientConnections(c);
571 if (!c->tabGroup()) // the "window" has already been there
572 emit windowAdded(c->effectWindow());
573}
574
575void EffectsHandlerImpl::slotUnmanagedShown(KWin::Toplevel *t)
576{ // regardless, unmanaged windows are -yet?- not synced anyway
577 Q_ASSERT(dynamic_cast<Unmanaged*>(t));
578 Unmanaged *u = static_cast<Unmanaged*>(t);
579 setupUnmanagedConnections(u);
580 emit windowAdded(u->effectWindow());
581}
582
583void EffectsHandlerImpl::slotDeletedRemoved(KWin::Deleted *d)
584{
585 emit windowDeleted(d->effectWindow());
586 elevated_windows.removeAll(d->effectWindow());
587}
588
589void EffectsHandlerImpl::slotWindowClosed(KWin::Toplevel *c)
590{
591 c->disconnect(this);
592 emit windowClosed(c->effectWindow());
593}
594
595void EffectsHandlerImpl::slotClientActivated(KWin::Client *c)
596{
597 emit windowActivated(c ? c->effectWindow() : NULL);
598}
599
600void EffectsHandlerImpl::slotClientMinimized(Client *c, bool animate)
601{
602 // TODO: notify effects even if it should not animate?
603 if (animate) {
604 emit windowMinimized(c->effectWindow());
605 }
606}
607
608void EffectsHandlerImpl::slotClientUnminimized(Client* c, bool animate)
609{
610 // TODO: notify effects even if it should not animate?
611 if (animate) {
612 emit windowUnminimized(c->effectWindow());
613 }
614}
615
616void EffectsHandlerImpl::slotClientModalityChanged()
617{
618 emit windowModalityChanged(static_cast<Client*>(sender())->effectWindow());
619}
620
621void EffectsHandlerImpl::slotCurrentTabAboutToChange(EffectWindow *from, EffectWindow *to)
622{
623 emit currentTabAboutToChange(from, to);
624}
625
626void EffectsHandlerImpl::slotTabAdded(EffectWindow* w, EffectWindow* to)
627{
628 emit tabAdded(w, to);
629}
630
631void EffectsHandlerImpl::slotTabRemoved(EffectWindow *w, EffectWindow* leaderOfFormerGroup)
632{
633 emit tabRemoved(w, leaderOfFormerGroup);
634}
635
636void EffectsHandlerImpl::slotDesktopChanged(int old, Client *c)
637{
638 const int newDesktop = VirtualDesktopManager::self()->current();
639 if (old != 0 && newDesktop != old) {
640 emit desktopChanged(old, newDesktop, c ? c->effectWindow() : 0);
641 // TODO: remove in 4.10
642 emit desktopChanged(old, newDesktop);
643 }
644}
645
646void EffectsHandlerImpl::slotDesktopPresenceChanged(Client *c, int old)
647{
648 if (!c->effectWindow()) {
649 return;
650 }
651 emit desktopPresenceChanged(c->effectWindow(), old, c->desktop());
652}
653
654void EffectsHandlerImpl::slotWindowDamaged(Toplevel* t, const QRect& r)
655{
656 if (!t->effectWindow()) {
657 // can happen during tear down of window
658 return;
659 }
660 emit windowDamaged(t->effectWindow(), r);
661}
662
663void EffectsHandlerImpl::slotGeometryShapeChanged(Toplevel* t, const QRect& old)
664{
665 // during late cleanup effectWindow() may be already NULL
666 // in some functions that may still call this
667 if (t == NULL || t->effectWindow() == NULL)
668 return;
669 emit windowGeometryShapeChanged(t->effectWindow(), old);
670}
671
672void EffectsHandlerImpl::slotPaddingChanged(Toplevel* t, const QRect& old)
673{
674 // during late cleanup effectWindow() may be already NULL
675 // in some functions that may still call this
676 if (t == NULL || t->effectWindow() == NULL)
677 return;
678 emit windowPaddingChanged(t->effectWindow(), old);
679}
680
681void EffectsHandlerImpl::setActiveFullScreenEffect(Effect* e)
682{
683 fullscreen_effect = e;
684 m_compositor->checkUnredirect();
685}
686
687Effect* EffectsHandlerImpl::activeFullScreenEffect() const
688{
689 return fullscreen_effect;
690}
691
692bool EffectsHandlerImpl::grabKeyboard(Effect* effect)
693{
694 if (keyboard_grab_effect != NULL)
695 return false;
696 bool ret = grabXKeyboard();
697 if (!ret)
698 return false;
699 keyboard_grab_effect = effect;
700 return true;
701}
702
703void EffectsHandlerImpl::ungrabKeyboard()
704{
705 assert(keyboard_grab_effect != NULL);
706 ungrabXKeyboard();
707 keyboard_grab_effect = NULL;
708}
709
710void EffectsHandlerImpl::grabbedKeyboardEvent(QKeyEvent* e)
711{
712 if (keyboard_grab_effect != NULL)
713 keyboard_grab_effect->grabbedKeyboardEvent(e);
714}
715
716void EffectsHandlerImpl::startMouseInterception(Effect *effect, Qt::CursorShape shape)
717{
718 if (m_grabbedMouseEffects.contains(effect)) {
719 return;
720 }
721 m_grabbedMouseEffects.append(effect);
722 if (m_grabbedMouseEffects.size() != 1) {
723 return;
724 }
725 // NOTE: it is intended to not perform an XPointerGrab on X11. See documentation in kwineffects.h
726 // The mouse grab is implemented by using a full screen input only window
727 if (!m_mouseInterceptionWindow.isValid()) {
728 const QRect geo(0, 0, displayWidth(), displayHeight());
729 const uint32_t mask = XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK | XCB_CW_CURSOR;
730 const uint32_t values[] = {
731 true,
732 XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_POINTER_MOTION,
733 Cursor::x11Cursor(shape)
734 };
735 m_mouseInterceptionWindow.reset(Xcb::createInputWindow(geo, mask, values));
736 }
737 m_mouseInterceptionWindow.map();
738 m_mouseInterceptionWindow.raise();
739 // Raise electric border windows above the input windows
740 // so they can still be triggered.
741#ifdef KWIN_BUILD_SCREENEDGES
742 ScreenEdges::self()->ensureOnTop();
743#endif
744}
745
746void EffectsHandlerImpl::stopMouseInterception(Effect *effect)
747{
748 if (!m_grabbedMouseEffects.contains(effect)) {
749 return;
750 }
751 m_grabbedMouseEffects.removeAll(effect);
752 if (m_grabbedMouseEffects.isEmpty()) {
753 m_mouseInterceptionWindow.unmap();
754#ifdef KWIN_BUILD_SCREENEDGES
755 Workspace::self()->stackScreenEdgesUnderOverrideRedirect();
756#endif
757 }
758}
759
760void* EffectsHandlerImpl::getProxy(QString name)
761{
762 // All effects start with "kwin4_effect_", prepend it to the name
763 name.prepend("kwin4_effect_");
764
765 for (QVector< EffectPair >::const_iterator it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it)
766 if ((*it).first == name)
767 return (*it).second->proxy();
768
769 return NULL;
770}
771
772void EffectsHandlerImpl::startMousePolling()
773{
774 Cursor::self()->startMousePolling();
775}
776
777void EffectsHandlerImpl::stopMousePolling()
778{
779 Cursor::self()->stopMousePolling();
780}
781
782bool EffectsHandlerImpl::hasKeyboardGrab() const
783{
784 return keyboard_grab_effect != NULL;
785}
786
787void EffectsHandlerImpl::desktopResized(const QSize &size)
788{
789 m_scene->screenGeometryChanged(size);
790 if (m_mouseInterceptionWindow.isValid()) {
791 m_mouseInterceptionWindow.setGeometry(QRect(0, 0, size.width(), size.height()));
792 }
793 emit screenGeometryChanged(size);
794}
795
796void EffectsHandlerImpl::slotPropertyNotify(Toplevel* t, long int atom)
797{
798 if (!registered_atoms.contains(atom))
799 return;
800 emit propertyNotify(t->effectWindow(), atom);
801}
802
803void EffectsHandlerImpl::slotPropertyNotify(long int atom)
804{
805 if (!registered_atoms.contains(atom))
806 return;
807 emit propertyNotify(NULL, atom);
808}
809
810void EffectsHandlerImpl::registerPropertyType(long atom, bool reg)
811{
812 if (reg)
813 ++registered_atoms[ atom ]; // initialized to 0 if not present yet
814 else {
815 if (--registered_atoms[ atom ] == 0)
816 registered_atoms.remove(atom);
817 }
818}
819
820xcb_atom_t EffectsHandlerImpl::announceSupportProperty(const QByteArray &propertyName, Effect *effect)
821{
822 PropertyEffectMap::iterator it = m_propertiesForEffects.find(propertyName);
823 if (it != m_propertiesForEffects.end()) {
824 // property has already been registered for an effect
825 // just append Effect and return the atom stored in m_managedProperties
826 if (!it.value().contains(effect)) {
827 it.value().append(effect);
828 }
829 return m_managedProperties.value(propertyName);
830 }
831 // get the atom for the propertyName
832 ScopedCPointer<xcb_intern_atom_reply_t> atomReply(xcb_intern_atom_reply(connection(),
833 xcb_intern_atom_unchecked(connection(), false, propertyName.size(), propertyName.constData()),
834 NULL));
835 if (atomReply.isNull()) {
836 return XCB_ATOM_NONE;
837 }
838 m_compositor->keepSupportProperty(atomReply->atom);
839 // announce property on root window
840 unsigned char dummy = 0;
841 xcb_change_property(connection(), XCB_PROP_MODE_REPLACE, rootWindow(), atomReply->atom, atomReply->atom, 8, 1, &dummy);
842 // TODO: add to _NET_SUPPORTED
843 m_managedProperties.insert(propertyName, atomReply->atom);
844 m_propertiesForEffects.insert(propertyName, QList<Effect*>() << effect);
845 registerPropertyType(atomReply->atom, true);
846 return atomReply->atom;
847}
848
849void EffectsHandlerImpl::removeSupportProperty(const QByteArray &propertyName, Effect *effect)
850{
851 PropertyEffectMap::iterator it = m_propertiesForEffects.find(propertyName);
852 if (it == m_propertiesForEffects.end()) {
853 // property is not registered - nothing to do
854 return;
855 }
856 if (!it.value().contains(effect)) {
857 // property is not registered for given effect - nothing to do
858 return;
859 }
860 it.value().removeAll(effect);
861 if (!it.value().isEmpty()) {
862 // property still registered for another effect - nothing further to do
863 return;
864 }
865 const xcb_atom_t atom = m_managedProperties.take(propertyName);
866 registerPropertyType(atom, false);
867 m_propertiesForEffects.remove(propertyName);
868 m_compositor->removeSupportProperty(atom); // delayed removal
869}
870
871QByteArray EffectsHandlerImpl::readRootProperty(long atom, long type, int format) const
872{
873 return readWindowProperty(rootWindow(), atom, type, format);
874}
875
876void EffectsHandlerImpl::deleteRootProperty(long atom) const
877{
878 deleteWindowProperty(rootWindow(), atom);
879}
880
881void EffectsHandlerImpl::activateWindow(EffectWindow* c)
882{
883 if (Client* cl = dynamic_cast< Client* >(static_cast<EffectWindowImpl*>(c)->window()))
884 Workspace::self()->activateClient(cl, true);
885}
886
887EffectWindow* EffectsHandlerImpl::activeWindow() const
888{
889 return Workspace::self()->activeClient() ? Workspace::self()->activeClient()->effectWindow() : NULL;
890}
891
892void EffectsHandlerImpl::moveWindow(EffectWindow* w, const QPoint& pos, bool snap, double snapAdjust)
893{
894 Client* cl = dynamic_cast< Client* >(static_cast<EffectWindowImpl*>(w)->window());
895 if (!cl || !cl->isMovable())
896 return;
897
898 if (snap)
899 cl->move(Workspace::self()->adjustClientPosition(cl, pos, true, snapAdjust));
900 else
901 cl->move(pos);
902}
903
904void EffectsHandlerImpl::windowToDesktop(EffectWindow* w, int desktop)
905{
906 Client* cl = dynamic_cast< Client* >(static_cast<EffectWindowImpl*>(w)->window());
907 if (cl && !cl->isDesktop() && !cl->isDock())
908 Workspace::self()->sendClientToDesktop(cl, desktop, true);
909}
910
911void EffectsHandlerImpl::windowToScreen(EffectWindow* w, int screen)
912{
913 Client* cl = dynamic_cast< Client* >(static_cast<EffectWindowImpl*>(w)->window());
914 if (cl && !cl->isDesktop() && !cl->isDock())
915 Workspace::self()->sendClientToScreen(cl, screen);
916}
917
918void EffectsHandlerImpl::setShowingDesktop(bool showing)
919{
920 Workspace::self()->setShowingDesktop(showing);
921}
922
923QString EffectsHandlerImpl::currentActivity() const
924{
925#ifdef KWIN_BUILD_ACTIVITIES
926 return Activities::self()->current();
927#else
928 return QString();
929#endif
930}
931
932int EffectsHandlerImpl::currentDesktop() const
933{
934 return VirtualDesktopManager::self()->current();
935}
936
937int EffectsHandlerImpl::numberOfDesktops() const
938{
939 return VirtualDesktopManager::self()->count();
940}
941
942void EffectsHandlerImpl::setCurrentDesktop(int desktop)
943{
944 VirtualDesktopManager::self()->setCurrent(desktop);
945}
946
947void EffectsHandlerImpl::setNumberOfDesktops(int desktops)
948{
949 VirtualDesktopManager::self()->setCount(desktops);
950}
951
952QSize EffectsHandlerImpl::desktopGridSize() const
953{
954 return VirtualDesktopManager::self()->grid().size();
955}
956
957int EffectsHandlerImpl::desktopGridWidth() const
958{
959 return desktopGridSize().width();
960}
961
962int EffectsHandlerImpl::desktopGridHeight() const
963{
964 return desktopGridSize().height();
965}
966
967int EffectsHandlerImpl::workspaceWidth() const
968{
969 return desktopGridWidth() * displayWidth();
970}
971
972int EffectsHandlerImpl::workspaceHeight() const
973{
974 return desktopGridHeight() * displayHeight();
975}
976
977int EffectsHandlerImpl::desktopAtCoords(QPoint coords) const
978{
979 return VirtualDesktopManager::self()->grid().at(coords);
980}
981
982QPoint EffectsHandlerImpl::desktopGridCoords(int id) const
983{
984 return VirtualDesktopManager::self()->grid().gridCoords(id);
985}
986
987QPoint EffectsHandlerImpl::desktopCoords(int id) const
988{
989 QPoint coords = VirtualDesktopManager::self()->grid().gridCoords(id);
990 if (coords.x() == -1)
991 return QPoint(-1, -1);
992 return QPoint(coords.x() * displayWidth(), coords.y() * displayHeight());
993}
994
995int EffectsHandlerImpl::desktopAbove(int desktop, bool wrap) const
996{
997 return getDesktop<DesktopAbove>(desktop, wrap);
998}
999
1000int EffectsHandlerImpl::desktopToRight(int desktop, bool wrap) const
1001{
1002 return getDesktop<DesktopRight>(desktop, wrap);
1003}
1004
1005int EffectsHandlerImpl::desktopBelow(int desktop, bool wrap) const
1006{
1007 return getDesktop<DesktopBelow>(desktop, wrap);
1008}
1009
1010int EffectsHandlerImpl::desktopToLeft(int desktop, bool wrap) const
1011{
1012 return getDesktop<DesktopLeft>(desktop, wrap);
1013}
1014
1015QString EffectsHandlerImpl::desktopName(int desktop) const
1016{
1017 return VirtualDesktopManager::self()->name(desktop);
1018}
1019
1020bool EffectsHandlerImpl::optionRollOverDesktops() const
1021{
1022 return options->isRollOverDesktops();
1023}
1024
1025double EffectsHandlerImpl::animationTimeFactor() const
1026{
1027 return options->animationTimeFactor();
1028}
1029
1030WindowQuadType EffectsHandlerImpl::newWindowQuadType()
1031{
1032 return WindowQuadType(next_window_quad_type++);
1033}
1034
1035int EffectsHandlerImpl::displayWidth() const
1036{
1037 return KWin::displayWidth();
1038}
1039
1040int EffectsHandlerImpl::displayHeight() const
1041{
1042 return KWin::displayHeight();
1043}
1044
1045EffectWindow* EffectsHandlerImpl::findWindow(WId id) const
1046{
1047 if (Client* w = Workspace::self()->findClient(WindowMatchPredicate(id)))
1048 return w->effectWindow();
1049 if (Unmanaged* w = Workspace::self()->findUnmanaged(WindowMatchPredicate(id)))
1050 return w->effectWindow();
1051 return NULL;
1052}
1053
1054EffectWindowList EffectsHandlerImpl::stackingOrder() const
1055{
1056 ToplevelList list = Workspace::self()->xStackingOrder();
1057 EffectWindowList ret;
1058 foreach (Toplevel *t, list) {
1059 if (EffectWindow *w = effectWindow(t))
1060 ret.append(w);
1061 }
1062 return ret;
1063}
1064
1065void EffectsHandlerImpl::setElevatedWindow(EffectWindow* w, bool set)
1066{
1067 elevated_windows.removeAll(w);
1068 if (set)
1069 elevated_windows.append(w);
1070}
1071
1072void EffectsHandlerImpl::setTabBoxWindow(EffectWindow* w)
1073{
1074#ifdef KWIN_BUILD_TABBOX
1075 if (Client* c = dynamic_cast< Client* >(static_cast< EffectWindowImpl* >(w)->window())) {
1076 TabBox::TabBox::self()->setCurrentClient(c);
1077 }
1078#else
1079 Q_UNUSED(w)
1080#endif
1081}
1082
1083void EffectsHandlerImpl::setTabBoxDesktop(int desktop)
1084{
1085#ifdef KWIN_BUILD_TABBOX
1086 TabBox::TabBox::self()->setCurrentDesktop(desktop);
1087#else
1088 Q_UNUSED(desktop)
1089#endif
1090}
1091
1092EffectWindowList EffectsHandlerImpl::currentTabBoxWindowList() const
1093{
1094#ifdef KWIN_BUILD_TABBOX
1095 EffectWindowList ret;
1096 ClientList clients;
1097 clients = TabBox::TabBox::self()->currentClientList();
1098 foreach (Client * c, clients)
1099 ret.append(c->effectWindow());
1100 return ret;
1101#else
1102 return EffectWindowList();
1103#endif
1104}
1105
1106void EffectsHandlerImpl::refTabBox()
1107{
1108#ifdef KWIN_BUILD_TABBOX
1109 TabBox::TabBox::self()->reference();
1110#endif
1111}
1112
1113void EffectsHandlerImpl::unrefTabBox()
1114{
1115#ifdef KWIN_BUILD_TABBOX
1116 TabBox::TabBox::self()->unreference();
1117#endif
1118}
1119
1120void EffectsHandlerImpl::closeTabBox()
1121{
1122#ifdef KWIN_BUILD_TABBOX
1123 TabBox::TabBox::self()->close();
1124#endif
1125}
1126
1127QList< int > EffectsHandlerImpl::currentTabBoxDesktopList() const
1128{
1129#ifdef KWIN_BUILD_TABBOX
1130 return TabBox::TabBox::self()->currentDesktopList();
1131#endif
1132 return QList< int >();
1133}
1134
1135int EffectsHandlerImpl::currentTabBoxDesktop() const
1136{
1137#ifdef KWIN_BUILD_TABBOX
1138 return TabBox::TabBox::self()->currentDesktop();
1139#endif
1140 return -1;
1141}
1142
1143EffectWindow* EffectsHandlerImpl::currentTabBoxWindow() const
1144{
1145#ifdef KWIN_BUILD_TABBOX
1146 if (Client* c = TabBox::TabBox::self()->currentClient())
1147 return c->effectWindow();
1148#endif
1149 return NULL;
1150}
1151
1152void EffectsHandlerImpl::addRepaintFull()
1153{
1154 m_compositor->addRepaintFull();
1155}
1156
1157void EffectsHandlerImpl::addRepaint(const QRect& r)
1158{
1159 m_compositor->addRepaint(r);
1160}
1161
1162void EffectsHandlerImpl::addRepaint(const QRegion& r)
1163{
1164 m_compositor->addRepaint(r);
1165}
1166
1167void EffectsHandlerImpl::addRepaint(int x, int y, int w, int h)
1168{
1169 m_compositor->addRepaint(x, y, w, h);
1170}
1171
1172int EffectsHandlerImpl::activeScreen() const
1173{
1174 return screens()->current();
1175}
1176
1177int EffectsHandlerImpl::numScreens() const
1178{
1179 return screens()->count();
1180}
1181
1182int EffectsHandlerImpl::screenNumber(const QPoint& pos) const
1183{
1184 return screens()->number(pos);
1185}
1186
1187QRect EffectsHandlerImpl::clientArea(clientAreaOption opt, int screen, int desktop) const
1188{
1189 return Workspace::self()->clientArea(opt, screen, desktop);
1190}
1191
1192QRect EffectsHandlerImpl::clientArea(clientAreaOption opt, const EffectWindow* c) const
1193{
1194 const Toplevel* t = static_cast< const EffectWindowImpl* >(c)->window();
1195 if (const Client* cl = dynamic_cast< const Client* >(t))
1196 return Workspace::self()->clientArea(opt, cl);
1197 else
1198 return Workspace::self()->clientArea(opt, t->geometry().center(), VirtualDesktopManager::self()->current());
1199}
1200
1201QRect EffectsHandlerImpl::clientArea(clientAreaOption opt, const QPoint& p, int desktop) const
1202{
1203 return Workspace::self()->clientArea(opt, p, desktop);
1204}
1205
1206void EffectsHandlerImpl::defineCursor(Qt::CursorShape shape)
1207{
1208 if (!m_mouseInterceptionWindow.isValid()) {
1209 return;
1210 }
1211 m_mouseInterceptionWindow.defineCursor(Cursor::x11Cursor(shape));
1212}
1213
1214bool EffectsHandlerImpl::checkInputWindowEvent(XEvent* e)
1215{
1216 if (e->type != ButtonPress && e->type != ButtonRelease && e->type != MotionNotify)
1217 return false;
1218 if (m_grabbedMouseEffects.isEmpty() || m_mouseInterceptionWindow != e->xany.window) {
1219 return false;
1220 }
1221 foreach (Effect *effect, m_grabbedMouseEffects) {
1222 switch(e->type) {
1223 case ButtonPress: {
1224 XButtonEvent* e2 = &e->xbutton;
1225 Qt::MouseButton button = x11ToQtMouseButton(e2->button);
1226 Qt::MouseButtons buttons = x11ToQtMouseButtons(e2->state) | button;
1227 QMouseEvent ev(QEvent::MouseButtonPress,
1228 QPoint(e2->x, e2->y), QPoint(e2->x_root, e2->y_root),
1229 button, buttons, x11ToQtKeyboardModifiers(e2->state));
1230 effect->windowInputMouseEvent(&ev);
1231 break; // --->
1232 }
1233 case ButtonRelease: {
1234 XButtonEvent* e2 = &e->xbutton;
1235 Qt::MouseButton button = x11ToQtMouseButton(e2->button);
1236 Qt::MouseButtons buttons = x11ToQtMouseButtons(e2->state) & ~button;
1237 QMouseEvent ev(QEvent::MouseButtonRelease,
1238 QPoint(e2->x, e2->y), QPoint(e2->x_root, e2->y_root),
1239 button, buttons, x11ToQtKeyboardModifiers(e2->state));
1240 effect->windowInputMouseEvent(&ev);
1241 break; // --->
1242 }
1243 case MotionNotify: {
1244 XMotionEvent* e2 = &e->xmotion;
1245 QMouseEvent ev(QEvent::MouseMove, QPoint(e2->x, e2->y), QPoint(e2->x_root, e2->y_root),
1246 Qt::NoButton, x11ToQtMouseButtons(e2->state), x11ToQtKeyboardModifiers(e2->state));
1247 effect->windowInputMouseEvent(&ev);
1248 break; // --->
1249 }
1250 }
1251 }
1252 return true; // eat event
1253}
1254
1255void EffectsHandlerImpl::checkInputWindowStacking()
1256{
1257 if (m_grabbedMouseEffects.isEmpty()) {
1258 return;
1259 }
1260 m_mouseInterceptionWindow.raise();
1261 // Raise electric border windows above the input windows
1262 // so they can still be triggered. TODO: Do both at once.
1263#ifdef KWIN_BUILD_SCREENEDGES
1264 ScreenEdges::self()->ensureOnTop();
1265#endif
1266}
1267
1268QPoint EffectsHandlerImpl::cursorPos() const
1269{
1270 return Cursor::pos();
1271}
1272
1273void EffectsHandlerImpl::reserveElectricBorder(ElectricBorder border, Effect *effect)
1274{
1275#ifdef KWIN_BUILD_SCREENEDGES
1276 ScreenEdges::self()->reserve(border, effect, "borderActivated");
1277#else
1278 Q_UNUSED(border)
1279 Q_UNUSED(effect)
1280#endif
1281}
1282
1283void EffectsHandlerImpl::unreserveElectricBorder(ElectricBorder border, Effect *effect)
1284{
1285#ifdef KWIN_BUILD_SCREENEDGES
1286 ScreenEdges::self()->unreserve(border, effect);
1287#else
1288 Q_UNUSED(border)
1289 Q_UNUSED(effect)
1290#endif
1291}
1292
1293unsigned long EffectsHandlerImpl::xrenderBufferPicture()
1294{
1295#ifdef KWIN_HAVE_XRENDER_COMPOSITING
1296 if (SceneXrender* s = dynamic_cast< SceneXrender* >(m_scene))
1297 return s->bufferPicture();
1298#endif
1299 return None;
1300}
1301
1302KLibrary* EffectsHandlerImpl::findEffectLibrary(KService* service)
1303{
1304 QString libname = service->library();
1305#ifdef KWIN_HAVE_OPENGLES
1306 if (libname.startsWith(QLatin1String("kwin4_effect_"))) {
1307 libname.replace("kwin4_effect_", "kwin4_effect_gles_");
1308 }
1309#endif
1310 libname.replace("kwin", KWIN_NAME);
1311 KLibrary* library = new KLibrary(libname);
1312 if (!library) {
1313 kError(1212) << "couldn't open library for effect '" <<
1314 service->name() << "'" << endl;
1315 return 0;
1316 }
1317
1318 return library;
1319}
1320
1321void EffectsHandlerImpl::toggleEffect(const QString& name)
1322{
1323 if (isEffectLoaded(name))
1324 unloadEffect(name);
1325 else
1326 loadEffect(name);
1327}
1328
1329QStringList EffectsHandlerImpl::loadedEffects() const
1330{
1331 QStringList listModules;
1332 for (QVector< EffectPair >::const_iterator it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
1333 listModules << (*it).first;
1334 }
1335 return listModules;
1336}
1337
1338QStringList EffectsHandlerImpl::listOfEffects() const
1339{
1340 KService::List offers = KServiceTypeTrader::self()->query("KWin/Effect");
1341 QStringList listOfModules;
1342 // First unload necessary effects
1343 foreach (const KService::Ptr & service, offers) {
1344 KPluginInfo plugininfo(service);
1345 listOfModules << plugininfo.pluginName();
1346 }
1347 return listOfModules;
1348}
1349
1350bool EffectsHandlerImpl::loadEffect(const QString& name, bool checkDefault)
1351{
1352 m_compositor->addRepaintFull();
1353
1354 if (!name.startsWith(QLatin1String("kwin4_effect_")))
1355 kWarning(1212) << "Effect names usually have kwin4_effect_ prefix" ;
1356
1357 // Make sure a single effect won't be loaded multiple times
1358 for (QVector< EffectPair >::const_iterator it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
1359 if ((*it).first == name) {
1360 kDebug(1212) << "EffectsHandler::loadEffect : Effect already loaded : " << name;
1361 return true;
1362 }
1363 }
1364
1365
1366 kDebug(1212) << "Trying to load " << name;
1367 QString internalname = name.toLower();
1368
1369 QString constraint = QString("[X-KDE-PluginInfo-Name] == '%1'").arg(internalname);
1370 KService::List offers = KServiceTypeTrader::self()->query("KWin/Effect", constraint);
1371 if (offers.isEmpty()) {
1372 kError(1212) << "Couldn't find effect " << name << endl;
1373 return false;
1374 }
1375 KService::Ptr service = offers.first();
1376
1377 if (service->property("X-Plasma-API").toString() == "javascript") {
1378 // this is a scripted effect - use different loader
1379 return loadScriptedEffect(name, service.data());
1380 }
1381
1382 KLibrary* library = findEffectLibrary(service.data());
1383 if (!library) {
1384 return false;
1385 }
1386
1387 QString version_symbol = "effect_version_" + name;
1388 KLibrary::void_function_ptr version_func = library->resolveFunction(version_symbol.toAscii());
1389 if (version_func == NULL) {
1390 kWarning(1212) << "Effect " << name << " does not provide required API version, ignoring.";
1391 delete library;
1392 return false;
1393 }
1394 typedef int (*t_versionfunc)();
1395 int version = reinterpret_cast< t_versionfunc >(version_func)(); // call it
1396 // Version must be the same or less, but major must be the same.
1397 // With major 0 minor must match exactly.
1398 if (version > KWIN_EFFECT_API_VERSION
1399 || (version >> 8) != KWIN_EFFECT_API_VERSION_MAJOR
1400 || (KWIN_EFFECT_API_VERSION_MAJOR == 0 && version != KWIN_EFFECT_API_VERSION)) {
1401 kWarning(1212) << "Effect " << name << " requires unsupported API version " << version;
1402 delete library;
1403 return false;
1404 }
1405
1406 const QString enabledByDefault_symbol = "effect_enabledbydefault_" + name;
1407 KLibrary::void_function_ptr enabledByDefault_func = library->resolveFunction(enabledByDefault_symbol.toAscii().data());
1408
1409 const QString supported_symbol = "effect_supported_" + name;
1410 KLibrary::void_function_ptr supported_func = library->resolveFunction(supported_symbol.toAscii().data());
1411
1412 const QString create_symbol = "effect_create_" + name;
1413 KLibrary::void_function_ptr create_func = library->resolveFunction(create_symbol.toAscii().data());
1414
1415 if (supported_func) {
1416 typedef bool (*t_supportedfunc)();
1417 t_supportedfunc supported = reinterpret_cast<t_supportedfunc>(supported_func);
1418 if (!supported()) {
1419 kWarning(1212) << "EffectsHandler::loadEffect : Effect " << name << " is not supported" ;
1420 library->unload();
1421 return false;
1422 }
1423 }
1424
1425 if (checkDefault && enabledByDefault_func) {
1426 typedef bool (*t_enabledByDefaultfunc)();
1427 t_enabledByDefaultfunc enabledByDefault = reinterpret_cast<t_enabledByDefaultfunc>(enabledByDefault_func);
1428
1429 if (!enabledByDefault()) {
1430 library->unload();
1431 return false;
1432 }
1433 }
1434
1435 if (!create_func) {
1436 kError(1212) << "EffectsHandler::loadEffect : effect_create function not found" << endl;
1437 library->unload();
1438 return false;
1439 }
1440
1441 typedef Effect*(*t_createfunc)();
1442 t_createfunc create = reinterpret_cast<t_createfunc>(create_func);
1443
1444 // Make sure all depenedencies have been loaded
1445 // TODO: detect circular deps
1446 KPluginInfo plugininfo(service);
1447 QStringList dependencies = plugininfo.dependencies();
1448 foreach (const QString & depName, dependencies) {
1449 if (!loadEffect(depName)) {
1450 kError(1212) << "EffectsHandler::loadEffect : Couldn't load dependencies for effect " << name << endl;
1451 library->unload();
1452 return false;
1453 }
1454 }
1455
1456 Effect* e = create();
1457
1458 effect_order.insert(service->property("X-KDE-Ordering").toInt(), EffectPair(name, e));
1459 effectsChanged();
1460 effect_libraries[ name ] = library;
1461
1462 return true;
1463}
1464
1465bool EffectsHandlerImpl::loadScriptedEffect(const QString& name, KService *service)
1466{
1467#ifdef KWIN_BUILD_SCRIPTING
1468 const KDesktopFile df("services", service->entryPath());
1469 const QString scriptName = df.desktopGroup().readEntry<QString>("X-Plasma-MainScript", "");
1470 if (scriptName.isEmpty()) {
1471 kDebug(1212) << "X-Plasma-MainScript not set";
1472 return false;
1473 }
1474 const QString scriptFile = KStandardDirs::locate("data", QLatin1String(KWIN_NAME) + "/effects/" + name + "/contents/" + scriptName);
1475 if (scriptFile.isNull()) {
1476 kDebug(1212) << "Could not locate the effect script";
1477 return false;
1478 }
1479 ScriptedEffect *effect = ScriptedEffect::create(name, scriptFile);
1480 if (!effect) {
1481 kDebug(1212) << "Could not initialize scripted effect: " << name;
1482 return false;
1483 }
1484 effect_order.insert(service->property("X-KDE-Ordering").toInt(), EffectPair(name, effect));
1485 effectsChanged();
1486 return true;
1487#else
1488 Q_UNUSED(name)
1489 Q_UNUSED(service)
1490 return false;
1491#endif
1492}
1493
1494void EffectsHandlerImpl::unloadEffect(const QString& name)
1495{
1496 m_compositor->addRepaintFull();
1497
1498 for (QMap< int, EffectPair >::iterator it = effect_order.begin(); it != effect_order.end(); ++it) {
1499 if (it.value().first == name) {
1500 kDebug(1212) << "EffectsHandler::unloadEffect : Unloading Effect : " << name;
1501 if (activeFullScreenEffect() == it.value().second) {
1502 setActiveFullScreenEffect(0);
1503 }
1504 stopMouseInterception(it.value().second);
1505 // remove support properties for the effect
1506 const QList<QByteArray> properties = m_propertiesForEffects.keys();
1507 foreach (const QByteArray &property, properties) {
1508 removeSupportProperty(property, it.value().second);
1509 }
1510 delete it.value().second;
1511 effect_order.erase(it);
1512 effectsChanged();
1513 if (effect_libraries.contains(name)) {
1514 effect_libraries[ name ]->unload();
1515 }
1516 return;
1517 }
1518 }
1519
1520 kDebug(1212) << "EffectsHandler::unloadEffect : Effect not loaded : " << name;
1521}
1522
1523void EffectsHandlerImpl::reconfigureEffect(const QString& name)
1524{
1525 for (QVector< EffectPair >::const_iterator it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it)
1526 if ((*it).first == name) {
1527 (*it).second->reconfigure(Effect::ReconfigureAll);
1528 return;
1529 }
1530}
1531
1532bool EffectsHandlerImpl::isEffectLoaded(const QString& name) const
1533{
1534 for (QVector< EffectPair >::const_iterator it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it)
1535 if ((*it).first == name)
1536 return true;
1537
1538 return false;
1539}
1540
1541void EffectsHandlerImpl::reloadEffect(Effect *effect)
1542{
1543 QString effectName;
1544 for (QVector< EffectPair >::const_iterator it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
1545 if ((*it).second == effect) {
1546 effectName = (*it).first;
1547 break;
1548 }
1549 }
1550 if (!effectName.isNull()) {
1551 unloadEffect(effectName);
1552 loadEffect(effectName);
1553 }
1554}
1555
1556void EffectsHandlerImpl::effectsChanged()
1557{
1558 loaded_effects.clear();
1559 m_activeEffects.clear(); // it's possible to have a reconfigure and a quad rebuild between two paint cycles - bug #308201
1560// kDebug(1212) << "Recreating effects' list:";
1561 foreach (const EffectPair & effect, effect_order) {
1562// kDebug(1212) << effect.first;
1563 loaded_effects.append(effect);
1564 }
1565 m_activeEffects.reserve(loaded_effects.count());
1566}
1567
1568QStringList EffectsHandlerImpl::activeEffects() const
1569{
1570 QStringList ret;
1571 for(QVector< KWin::EffectPair >::const_iterator it = loaded_effects.constBegin(),
1572 end = loaded_effects.constEnd(); it != end; ++it) {
1573 if (it->second->isActive()) {
1574 ret << it->first;
1575 }
1576 }
1577 return ret;
1578}
1579
1580EffectFrame* EffectsHandlerImpl::effectFrame(EffectFrameStyle style, bool staticSize, const QPoint& position, Qt::Alignment alignment) const
1581{
1582 return new EffectFrameImpl(style, staticSize, position, alignment);
1583}
1584
1585
1586QVariant EffectsHandlerImpl::kwinOption(KWinOption kwopt)
1587{
1588 switch (kwopt) {
1589 case CloseButtonCorner:
1590 return decorationPlugin()->closeButtonCorner();
1591#ifdef KWIN_BUILD_SCREENEDGES
1592 case SwitchDesktopOnScreenEdge:
1593 return ScreenEdges::self()->isDesktopSwitching();
1594 case SwitchDesktopOnScreenEdgeMovingWindows:
1595 return ScreenEdges::self()->isDesktopSwitchingMovingClients();
1596#endif
1597 default:
1598 return QVariant(); // an invalid one
1599 }
1600}
1601
1602QString EffectsHandlerImpl::supportInformation(const QString &name) const
1603{
1604 if (!isEffectLoaded(name)) {
1605 return QString();
1606 }
1607 for (QVector< EffectPair >::const_iterator it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
1608 if ((*it).first == name) {
1609 QString support((*it).first + ":\n");
1610 const QMetaObject *metaOptions = (*it).second->metaObject();
1611 for (int i=0; i<metaOptions->propertyCount(); ++i) {
1612 const QMetaProperty property = metaOptions->property(i);
1613 if (QLatin1String(property.name()) == "objectName") {
1614 continue;
1615 }
1616 support.append(QLatin1String(property.name()) % ": " % (*it).second->property(property.name()).toString() % '\n');
1617 }
1618 return support;
1619 }
1620 }
1621 return QString();
1622}
1623
1624
1625bool EffectsHandlerImpl::isScreenLocked() const
1626{
1627 return m_screenLockerWatcher->isLocked();
1628}
1629
1630QString EffectsHandlerImpl::debug(const QString& name, const QString& parameter) const
1631{
1632 QString internalName = name.startsWith("kwin4_effect_") ? name : "kwin4_effect_" + name;
1633 for (QVector< EffectPair >::const_iterator it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
1634 if ((*it).first == internalName) {
1635 return it->second->debug(parameter);
1636 }
1637 }
1638 return QString();
1639}
1640
1641//****************************************
1642// EffectWindowImpl
1643//****************************************
1644
1645EffectWindowImpl::EffectWindowImpl(Toplevel *toplevel)
1646 : EffectWindow(toplevel)
1647 , toplevel(toplevel)
1648 , sw(NULL)
1649{
1650}
1651
1652EffectWindowImpl::~EffectWindowImpl()
1653{
1654 QVariant cachedTextureVariant = data(LanczosCacheRole);
1655 if (cachedTextureVariant.isValid()) {
1656 GLTexture *cachedTexture = static_cast< GLTexture*>(cachedTextureVariant.value<void*>());
1657 delete cachedTexture;
1658 }
1659}
1660
1661bool EffectWindowImpl::isPaintingEnabled()
1662{
1663 return sceneWindow()->isPaintingEnabled();
1664}
1665
1666void EffectWindowImpl::enablePainting(int reason)
1667{
1668 sceneWindow()->enablePainting(reason);
1669}
1670
1671void EffectWindowImpl::disablePainting(int reason)
1672{
1673 sceneWindow()->disablePainting(reason);
1674}
1675
1676const EffectWindowGroup* EffectWindowImpl::group() const
1677{
1678 if (Client* c = dynamic_cast< Client* >(toplevel))
1679 return c->group()->effectGroup();
1680 return NULL; // TODO
1681}
1682
1683void EffectWindowImpl::refWindow()
1684{
1685 if (Deleted* d = dynamic_cast< Deleted* >(toplevel))
1686 return d->refWindow();
1687 abort(); // TODO
1688}
1689
1690void EffectWindowImpl::unrefWindow()
1691{
1692 if (Deleted* d = dynamic_cast< Deleted* >(toplevel))
1693 return d->unrefWindow(); // delays deletion in case
1694 abort(); // TODO
1695}
1696
1697void EffectWindowImpl::setWindow(Toplevel* w)
1698{
1699 toplevel = w;
1700 setParent(w);
1701}
1702
1703void EffectWindowImpl::setSceneWindow(Scene::Window* w)
1704{
1705 sw = w;
1706}
1707
1708QRegion EffectWindowImpl::shape() const
1709{
1710 return sw ? sw->shape() : geometry();
1711}
1712
1713QRect EffectWindowImpl::decorationInnerRect() const
1714{
1715 Client *client = dynamic_cast<Client*>(toplevel);
1716 return client ? client->transparentRect() : contentsRect();
1717}
1718
1719QByteArray EffectWindowImpl::readProperty(long atom, long type, int format) const
1720{
1721 return readWindowProperty(window()->window(), atom, type, format);
1722}
1723
1724void EffectWindowImpl::deleteProperty(long int atom) const
1725{
1726 deleteWindowProperty(window()->window(), atom);
1727}
1728
1729EffectWindow* EffectWindowImpl::findModal()
1730{
1731 if (Client* c = dynamic_cast< Client* >(toplevel)) {
1732 if (Client* c2 = c->findModal())
1733 return c2->effectWindow();
1734 }
1735 return NULL;
1736}
1737
1738template <typename T>
1739EffectWindowList getMainWindows(Toplevel *toplevel)
1740{
1741 T *c = static_cast<T*>(toplevel);
1742 EffectWindowList ret;
1743 ClientList mainclients = c->mainClients();
1744 foreach (Client * tmp, mainclients)
1745 ret.append(tmp->effectWindow());
1746 return ret;
1747}
1748
1749EffectWindowList EffectWindowImpl::mainWindows() const
1750{
1751 if (toplevel->isClient()) {
1752 return getMainWindows<Client>(toplevel);
1753 } else if (toplevel->isDeleted()) {
1754 return getMainWindows<Deleted>(toplevel);
1755 }
1756 return EffectWindowList();
1757}
1758
1759WindowQuadList EffectWindowImpl::buildQuads(bool force) const
1760{
1761 return sceneWindow()->buildQuads(force);
1762}
1763
1764void EffectWindowImpl::setData(int role, const QVariant &data)
1765{
1766 if (!data.isNull())
1767 dataMap[ role ] = data;
1768 else
1769 dataMap.remove(role);
1770}
1771
1772QVariant EffectWindowImpl::data(int role) const
1773{
1774 if (!dataMap.contains(role))
1775 return QVariant();
1776 return dataMap[ role ];
1777}
1778
1779EffectWindow* effectWindow(Toplevel* w)
1780{
1781 EffectWindowImpl* ret = w->effectWindow();
1782 return ret;
1783}
1784
1785EffectWindow* effectWindow(Scene::Window* w)
1786{
1787 EffectWindowImpl* ret = w->window()->effectWindow();
1788 ret->setSceneWindow(w);
1789 return ret;
1790}
1791
1792void EffectWindowImpl::elevate(bool elevate)
1793{
1794 effects->setElevatedWindow(this, elevate);
1795}
1796
1797void EffectWindowImpl::registerThumbnail(AbstractThumbnailItem *item)
1798{
1799 if (WindowThumbnailItem *thumb = qobject_cast<WindowThumbnailItem*>(item)) {
1800 insertThumbnail(thumb);
1801 connect(thumb, SIGNAL(destroyed(QObject*)), SLOT(thumbnailDestroyed(QObject*)));
1802 connect(thumb, SIGNAL(wIdChanged(qulonglong)), SLOT(thumbnailTargetChanged()));
1803 } else if (DesktopThumbnailItem *desktopThumb = qobject_cast<DesktopThumbnailItem*>(item)) {
1804 m_desktopThumbnails.append(desktopThumb);
1805 connect(desktopThumb, SIGNAL(destroyed(QObject*)), SLOT(desktopThumbnailDestroyed(QObject*)));
1806 }
1807}
1808
1809void EffectWindowImpl::thumbnailDestroyed(QObject *object)
1810{
1811 // we know it is a ThumbnailItem
1812 m_thumbnails.remove(static_cast<WindowThumbnailItem*>(object));
1813}
1814
1815void EffectWindowImpl::thumbnailTargetChanged()
1816{
1817 if (WindowThumbnailItem *item = qobject_cast<WindowThumbnailItem*>(sender())) {
1818 insertThumbnail(item);
1819 }
1820}
1821
1822void EffectWindowImpl::insertThumbnail(WindowThumbnailItem *item)
1823{
1824 EffectWindow *w = effects->findWindow(item->wId());
1825 if (w) {
1826 m_thumbnails.insert(item, QWeakPointer<EffectWindowImpl>(static_cast<EffectWindowImpl*>(w)));
1827 } else {
1828 m_thumbnails.insert(item, QWeakPointer<EffectWindowImpl>());
1829 }
1830}
1831
1832void EffectWindowImpl::desktopThumbnailDestroyed(QObject *object)
1833{
1834 // we know it is a DesktopThumbnailItem
1835 m_desktopThumbnails.removeAll(static_cast<DesktopThumbnailItem*>(object));
1836}
1837
1838void EffectWindowImpl::referencePreviousWindowPixmap()
1839{
1840 if (sw) {
1841 sw->referencePreviousPixmap();
1842 }
1843}
1844
1845void EffectWindowImpl::unreferencePreviousWindowPixmap()
1846{
1847 if (sw) {
1848 sw->unreferencePreviousPixmap();
1849 }
1850}
1851
1852//****************************************
1853// EffectWindowGroupImpl
1854//****************************************
1855
1856
1857EffectWindowList EffectWindowGroupImpl::members() const
1858{
1859 EffectWindowList ret;
1860 foreach (Toplevel * c, group->members())
1861 ret.append(c->effectWindow());
1862 return ret;
1863}
1864
1865//****************************************
1866// EffectFrameImpl
1867//****************************************
1868
1869EffectFrameImpl::EffectFrameImpl(EffectFrameStyle style, bool staticSize, QPoint position, Qt::Alignment alignment)
1870 : QObject(0)
1871 , EffectFrame()
1872 , m_style(style)
1873 , m_static(staticSize)
1874 , m_point(position)
1875 , m_alignment(alignment)
1876 , m_shader(NULL)
1877{
1878 if (m_style == EffectFrameStyled) {
1879 m_frame.setImagePath("widgets/background");
1880 m_frame.setCacheAllRenderedFrames(true);
1881 connect(Plasma::Theme::defaultTheme(), SIGNAL(themeChanged()), this, SLOT(plasmaThemeChanged()));
1882 }
1883 m_selection.setImagePath("widgets/viewitem");
1884 m_selection.setElementPrefix("hover");
1885 m_selection.setCacheAllRenderedFrames(true);
1886 m_selection.setEnabledBorders(Plasma::FrameSvg::AllBorders);
1887
1888 if (effects->isOpenGLCompositing()) {
1889 m_sceneFrame = new SceneOpenGL::EffectFrame(this, static_cast<SceneOpenGL*>(Compositor::self()->scene()));
1890 } else if (effects->compositingType() == XRenderCompositing) {
1891#ifdef KWIN_HAVE_XRENDER_COMPOSITING
1892 m_sceneFrame = new SceneXrender::EffectFrame(this);
1893#endif
1894 } else {
1895 // that should not happen and will definitely crash!
1896 m_sceneFrame = NULL;
1897 }
1898}
1899
1900EffectFrameImpl::~EffectFrameImpl()
1901{
1902 delete m_sceneFrame;
1903}
1904
1905const QFont& EffectFrameImpl::font() const
1906{
1907 return m_font;
1908}
1909
1910void EffectFrameImpl::setFont(const QFont& font)
1911{
1912 if (m_font == font) {
1913 return;
1914 }
1915 m_font = font;
1916 QRect oldGeom = m_geometry;
1917 if (!m_text.isEmpty()) {
1918 autoResize();
1919 }
1920 if (oldGeom == m_geometry) {
1921 // Wasn't updated in autoResize()
1922 m_sceneFrame->freeTextFrame();
1923 }
1924}
1925
1926void EffectFrameImpl::free()
1927{
1928 m_sceneFrame->free();
1929}
1930
1931const QRect& EffectFrameImpl::geometry() const
1932{
1933 return m_geometry;
1934}
1935
1936void EffectFrameImpl::setGeometry(const QRect& geometry, bool force)
1937{
1938 QRect oldGeom = m_geometry;
1939 m_geometry = geometry;
1940 if (m_geometry == oldGeom && !force) {
1941 return;
1942 }
1943 effects->addRepaint(oldGeom);
1944 effects->addRepaint(m_geometry);
1945 if (m_geometry.size() == oldGeom.size() && !force) {
1946 return;
1947 }
1948
1949 if (m_style == EffectFrameStyled) {
1950 qreal left, top, right, bottom;
1951 m_frame.getMargins(left, top, right, bottom); // m_geometry is the inner geometry
1952 m_frame.resizeFrame(m_geometry.adjusted(-left, -top, right, bottom).size());
1953 }
1954
1955 free();
1956}
1957
1958const QPixmap& EffectFrameImpl::icon() const
1959{
1960 return m_icon;
1961}
1962
1963void EffectFrameImpl::setIcon(const QPixmap& icon)
1964{
1965 m_icon = icon;
1966 if (isCrossFade()) {
1967 m_sceneFrame->crossFadeIcon();
1968 }
1969 if (m_iconSize.isEmpty()) { // Set a size if we don't already have one
1970 setIconSize(m_icon.size());
1971 }
1972 m_sceneFrame->freeIconFrame();
1973}
1974
1975const QSize& EffectFrameImpl::iconSize() const
1976{
1977 return m_iconSize;
1978}
1979
1980void EffectFrameImpl::setIconSize(const QSize& size)
1981{
1982 if (m_iconSize == size) {
1983 return;
1984 }
1985 m_iconSize = size;
1986 autoResize();
1987 m_sceneFrame->freeIconFrame();
1988}
1989
1990void EffectFrameImpl::plasmaThemeChanged()
1991{
1992 free();
1993}
1994
1995void EffectFrameImpl::render(QRegion region, double opacity, double frameOpacity)
1996{
1997 if (m_geometry.isEmpty()) {
1998 return; // Nothing to display
1999 }
2000 m_shader = NULL;
2001 effects->paintEffectFrame(this, region, opacity, frameOpacity);
2002}
2003
2004void EffectFrameImpl::finalRender(QRegion region, double opacity, double frameOpacity) const
2005{
2006 region = infiniteRegion(); // TODO: Old region doesn't seem to work with OpenGL
2007
2008 m_sceneFrame->render(region, opacity, frameOpacity);
2009}
2010
2011Qt::Alignment EffectFrameImpl::alignment() const
2012{
2013 return m_alignment;
2014}
2015
2016
2017void
2018EffectFrameImpl::align(QRect &geometry)
2019{
2020 if (m_alignment & Qt::AlignLeft)
2021 geometry.moveLeft(m_point.x());
2022 else if (m_alignment & Qt::AlignRight)
2023 geometry.moveLeft(m_point.x() - geometry.width());
2024 else
2025 geometry.moveLeft(m_point.x() - geometry.width() / 2);
2026 if (m_alignment & Qt::AlignTop)
2027 geometry.moveTop(m_point.y());
2028 else if (m_alignment & Qt::AlignBottom)
2029 geometry.moveTop(m_point.y() - geometry.height());
2030 else
2031 geometry.moveTop(m_point.y() - geometry.height() / 2);
2032}
2033
2034
2035void EffectFrameImpl::setAlignment(Qt::Alignment alignment)
2036{
2037 m_alignment = alignment;
2038 align(m_geometry);
2039 setGeometry(m_geometry);
2040}
2041
2042void EffectFrameImpl::setPosition(const QPoint& point)
2043{
2044 m_point = point;
2045 QRect geometry = m_geometry; // this is important, setGeometry need call repaint for old & new geometry
2046 align(geometry);
2047 setGeometry(geometry);
2048}
2049
2050const QString& EffectFrameImpl::text() const
2051{
2052 return m_text;
2053}
2054
2055void EffectFrameImpl::setText(const QString& text)
2056{
2057 if (m_text == text) {
2058 return;
2059 }
2060 if (isCrossFade()) {
2061 m_sceneFrame->crossFadeText();
2062 }
2063 m_text = text;
2064 QRect oldGeom = m_geometry;
2065 autoResize();
2066 if (oldGeom == m_geometry) {
2067 // Wasn't updated in autoResize()
2068 m_sceneFrame->freeTextFrame();
2069 }
2070}
2071
2072void EffectFrameImpl::setSelection(const QRect& selection)
2073{
2074 if (selection == m_selectionGeometry) {
2075 return;
2076 }
2077 m_selectionGeometry = selection;
2078 if (m_selectionGeometry.size() != m_selection.frameSize().toSize()) {
2079 m_selection.resizeFrame(m_selectionGeometry.size());
2080 }
2081 // TODO; optimize to only recreate when resizing
2082 m_sceneFrame->freeSelection();
2083}
2084
2085void EffectFrameImpl::autoResize()
2086{
2087 if (m_static)
2088 return; // Not automatically resizing
2089
2090 QRect geometry;
2091 // Set size
2092 if (!m_text.isEmpty()) {
2093 QFontMetrics metrics(m_font);
2094 geometry.setSize(metrics.size(0, m_text));
2095 }
2096 if (!m_icon.isNull() && !m_iconSize.isEmpty()) {
2097 geometry.setLeft(-m_iconSize.width());
2098 if (m_iconSize.height() > geometry.height())
2099 geometry.setHeight(m_iconSize.height());
2100 }
2101
2102 align(geometry);
2103 setGeometry(geometry);
2104}
2105
2106QColor EffectFrameImpl::styledTextColor()
2107{
2108 return Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor);
2109}
2110
2111} // namespace
2112