1 | /******************************************************************** |
2 | KWin - the KDE window manager |
3 | This file is part of the KDE project. |
4 | |
5 | Copyright (C) 2006 Lubos Lunak <l.lunak@kde.org> |
6 | Copyright (C) 2010, 2011 Martin Gräßlin <mgraesslin@kde.org> |
7 | |
8 | This program is free software; you can redistribute it and/or modify |
9 | it under the terms of the GNU General Public License as published by |
10 | the Free Software Foundation; either version 2 of the License, or |
11 | (at your option) any later version. |
12 | |
13 | This program is distributed in the hope that it will be useful, |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | GNU General Public License for more details. |
17 | |
18 | You should have received a copy of the GNU General Public License |
19 | along 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 | |
76 | namespace KWin |
77 | { |
78 | |
79 | static const QString SCREEN_LOCKER_SERVICE_NAME = QString("org.freedesktop.ScreenSaver" ); |
80 | |
81 | ScreenLockerWatcher::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 | |
99 | ScreenLockerWatcher::~ScreenLockerWatcher() |
100 | { |
101 | } |
102 | |
103 | void 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 | |
120 | void 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 | |
138 | void 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 | |
152 | void ScreenLockerWatcher::activeQueried(QDBusPendingCallWatcher *watcher) |
153 | { |
154 | QDBusPendingReply<bool> reply = *watcher; |
155 | if (!reply.isError()) { |
156 | setLocked(reply.value()); |
157 | } |
158 | watcher->deleteLater(); |
159 | } |
160 | |
161 | void 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 | |
173 | static 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 | |
203 | static void deleteWindowProperty(Window win, long int atom) |
204 | { |
205 | XDeleteProperty(QX11Info::display(), win, atom); |
206 | } |
207 | |
208 | //--------------------- |
209 | |
210 | EffectsHandlerImpl::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 | |
268 | EffectsHandlerImpl::~EffectsHandlerImpl() |
269 | { |
270 | if (keyboard_grab_effect != NULL) |
271 | ungrabKeyboard(); |
272 | foreach (const EffectPair & ep, loaded_effects) |
273 | unloadEffect(ep.first); |
274 | } |
275 | |
276 | void 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 | |
293 | void 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 | |
303 | void 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 | |
312 | void 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 |
360 | void 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 | |
369 | void 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 | |
378 | void 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 | |
394 | void 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 | |
403 | void 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 | |
412 | void 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 | |
421 | void 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 | |
432 | void 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 | |
441 | Effect *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 | |
449 | void 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 | |
458 | void 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 | |
473 | bool EffectsHandlerImpl::hasDecorationShadows() const |
474 | { |
475 | return decorationPlugin()->hasShadows(); |
476 | } |
477 | |
478 | bool EffectsHandlerImpl::decorationsHaveAlpha() const |
479 | { |
480 | return decorationPlugin()->hasAlpha(); |
481 | } |
482 | |
483 | bool EffectsHandlerImpl::decorationSupportsBlurBehind() const |
484 | { |
485 | return decorationPlugin()->supportsBlurBehind(); |
486 | } |
487 | |
488 | // start another painting pass |
489 | void 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 | |
504 | void 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 | |
529 | void EffectsHandlerImpl::slotClientStartUserMovedResized(Client *c) |
530 | { |
531 | emit windowStartUserMovedResized(c->effectWindow()); |
532 | } |
533 | |
534 | void EffectsHandlerImpl::slotClientFinishUserMovedResized(Client *c) |
535 | { |
536 | emit windowFinishUserMovedResized(c->effectWindow()); |
537 | } |
538 | |
539 | void EffectsHandlerImpl::slotClientStepUserMovedResized(Client* c, const QRect& geometry) |
540 | { |
541 | emit windowStepUserMovedResized(c->effectWindow(), geometry); |
542 | } |
543 | |
544 | void 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 | |
552 | void 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 | |
560 | void 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 | |
566 | void 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 | |
575 | void 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 | |
583 | void EffectsHandlerImpl::slotDeletedRemoved(KWin::Deleted *d) |
584 | { |
585 | emit windowDeleted(d->effectWindow()); |
586 | elevated_windows.removeAll(d->effectWindow()); |
587 | } |
588 | |
589 | void EffectsHandlerImpl::slotWindowClosed(KWin::Toplevel *c) |
590 | { |
591 | c->disconnect(this); |
592 | emit windowClosed(c->effectWindow()); |
593 | } |
594 | |
595 | void EffectsHandlerImpl::slotClientActivated(KWin::Client *c) |
596 | { |
597 | emit windowActivated(c ? c->effectWindow() : NULL); |
598 | } |
599 | |
600 | void 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 | |
608 | void 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 | |
616 | void EffectsHandlerImpl::slotClientModalityChanged() |
617 | { |
618 | emit windowModalityChanged(static_cast<Client*>(sender())->effectWindow()); |
619 | } |
620 | |
621 | void EffectsHandlerImpl::slotCurrentTabAboutToChange(EffectWindow *from, EffectWindow *to) |
622 | { |
623 | emit currentTabAboutToChange(from, to); |
624 | } |
625 | |
626 | void EffectsHandlerImpl::slotTabAdded(EffectWindow* w, EffectWindow* to) |
627 | { |
628 | emit tabAdded(w, to); |
629 | } |
630 | |
631 | void EffectsHandlerImpl::slotTabRemoved(EffectWindow *w, EffectWindow* leaderOfFormerGroup) |
632 | { |
633 | emit tabRemoved(w, leaderOfFormerGroup); |
634 | } |
635 | |
636 | void 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 | |
646 | void EffectsHandlerImpl::slotDesktopPresenceChanged(Client *c, int old) |
647 | { |
648 | if (!c->effectWindow()) { |
649 | return; |
650 | } |
651 | emit desktopPresenceChanged(c->effectWindow(), old, c->desktop()); |
652 | } |
653 | |
654 | void 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 | |
663 | void 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 | |
672 | void 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 | |
681 | void EffectsHandlerImpl::setActiveFullScreenEffect(Effect* e) |
682 | { |
683 | fullscreen_effect = e; |
684 | m_compositor->checkUnredirect(); |
685 | } |
686 | |
687 | Effect* EffectsHandlerImpl::activeFullScreenEffect() const |
688 | { |
689 | return fullscreen_effect; |
690 | } |
691 | |
692 | bool 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 | |
703 | void EffectsHandlerImpl::ungrabKeyboard() |
704 | { |
705 | assert(keyboard_grab_effect != NULL); |
706 | ungrabXKeyboard(); |
707 | keyboard_grab_effect = NULL; |
708 | } |
709 | |
710 | void EffectsHandlerImpl::grabbedKeyboardEvent(QKeyEvent* e) |
711 | { |
712 | if (keyboard_grab_effect != NULL) |
713 | keyboard_grab_effect->grabbedKeyboardEvent(e); |
714 | } |
715 | |
716 | void 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 | |
746 | void 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 | |
760 | void* 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 | |
772 | void EffectsHandlerImpl::startMousePolling() |
773 | { |
774 | Cursor::self()->startMousePolling(); |
775 | } |
776 | |
777 | void EffectsHandlerImpl::stopMousePolling() |
778 | { |
779 | Cursor::self()->stopMousePolling(); |
780 | } |
781 | |
782 | bool EffectsHandlerImpl::hasKeyboardGrab() const |
783 | { |
784 | return keyboard_grab_effect != NULL; |
785 | } |
786 | |
787 | void 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 | |
796 | void EffectsHandlerImpl::slotPropertyNotify(Toplevel* t, long int atom) |
797 | { |
798 | if (!registered_atoms.contains(atom)) |
799 | return; |
800 | emit propertyNotify(t->effectWindow(), atom); |
801 | } |
802 | |
803 | void EffectsHandlerImpl::slotPropertyNotify(long int atom) |
804 | { |
805 | if (!registered_atoms.contains(atom)) |
806 | return; |
807 | emit propertyNotify(NULL, atom); |
808 | } |
809 | |
810 | void 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 | |
820 | xcb_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 | |
849 | void 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 | |
871 | QByteArray EffectsHandlerImpl::readRootProperty(long atom, long type, int format) const |
872 | { |
873 | return readWindowProperty(rootWindow(), atom, type, format); |
874 | } |
875 | |
876 | void EffectsHandlerImpl::deleteRootProperty(long atom) const |
877 | { |
878 | deleteWindowProperty(rootWindow(), atom); |
879 | } |
880 | |
881 | void 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 | |
887 | EffectWindow* EffectsHandlerImpl::activeWindow() const |
888 | { |
889 | return Workspace::self()->activeClient() ? Workspace::self()->activeClient()->effectWindow() : NULL; |
890 | } |
891 | |
892 | void 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 | |
904 | void 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 | |
911 | void 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 | |
918 | void EffectsHandlerImpl::setShowingDesktop(bool showing) |
919 | { |
920 | Workspace::self()->setShowingDesktop(showing); |
921 | } |
922 | |
923 | QString EffectsHandlerImpl::currentActivity() const |
924 | { |
925 | #ifdef KWIN_BUILD_ACTIVITIES |
926 | return Activities::self()->current(); |
927 | #else |
928 | return QString(); |
929 | #endif |
930 | } |
931 | |
932 | int EffectsHandlerImpl::currentDesktop() const |
933 | { |
934 | return VirtualDesktopManager::self()->current(); |
935 | } |
936 | |
937 | int EffectsHandlerImpl::numberOfDesktops() const |
938 | { |
939 | return VirtualDesktopManager::self()->count(); |
940 | } |
941 | |
942 | void EffectsHandlerImpl::setCurrentDesktop(int desktop) |
943 | { |
944 | VirtualDesktopManager::self()->setCurrent(desktop); |
945 | } |
946 | |
947 | void EffectsHandlerImpl::setNumberOfDesktops(int desktops) |
948 | { |
949 | VirtualDesktopManager::self()->setCount(desktops); |
950 | } |
951 | |
952 | QSize EffectsHandlerImpl::desktopGridSize() const |
953 | { |
954 | return VirtualDesktopManager::self()->grid().size(); |
955 | } |
956 | |
957 | int EffectsHandlerImpl::desktopGridWidth() const |
958 | { |
959 | return desktopGridSize().width(); |
960 | } |
961 | |
962 | int EffectsHandlerImpl::desktopGridHeight() const |
963 | { |
964 | return desktopGridSize().height(); |
965 | } |
966 | |
967 | int EffectsHandlerImpl::workspaceWidth() const |
968 | { |
969 | return desktopGridWidth() * displayWidth(); |
970 | } |
971 | |
972 | int EffectsHandlerImpl::workspaceHeight() const |
973 | { |
974 | return desktopGridHeight() * displayHeight(); |
975 | } |
976 | |
977 | int EffectsHandlerImpl::desktopAtCoords(QPoint coords) const |
978 | { |
979 | return VirtualDesktopManager::self()->grid().at(coords); |
980 | } |
981 | |
982 | QPoint EffectsHandlerImpl::desktopGridCoords(int id) const |
983 | { |
984 | return VirtualDesktopManager::self()->grid().gridCoords(id); |
985 | } |
986 | |
987 | QPoint 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 | |
995 | int EffectsHandlerImpl::desktopAbove(int desktop, bool wrap) const |
996 | { |
997 | return getDesktop<DesktopAbove>(desktop, wrap); |
998 | } |
999 | |
1000 | int EffectsHandlerImpl::desktopToRight(int desktop, bool wrap) const |
1001 | { |
1002 | return getDesktop<DesktopRight>(desktop, wrap); |
1003 | } |
1004 | |
1005 | int EffectsHandlerImpl::desktopBelow(int desktop, bool wrap) const |
1006 | { |
1007 | return getDesktop<DesktopBelow>(desktop, wrap); |
1008 | } |
1009 | |
1010 | int EffectsHandlerImpl::desktopToLeft(int desktop, bool wrap) const |
1011 | { |
1012 | return getDesktop<DesktopLeft>(desktop, wrap); |
1013 | } |
1014 | |
1015 | QString EffectsHandlerImpl::desktopName(int desktop) const |
1016 | { |
1017 | return VirtualDesktopManager::self()->name(desktop); |
1018 | } |
1019 | |
1020 | bool EffectsHandlerImpl::optionRollOverDesktops() const |
1021 | { |
1022 | return options->isRollOverDesktops(); |
1023 | } |
1024 | |
1025 | double EffectsHandlerImpl::animationTimeFactor() const |
1026 | { |
1027 | return options->animationTimeFactor(); |
1028 | } |
1029 | |
1030 | WindowQuadType EffectsHandlerImpl::newWindowQuadType() |
1031 | { |
1032 | return WindowQuadType(next_window_quad_type++); |
1033 | } |
1034 | |
1035 | int EffectsHandlerImpl::displayWidth() const |
1036 | { |
1037 | return KWin::displayWidth(); |
1038 | } |
1039 | |
1040 | int EffectsHandlerImpl::displayHeight() const |
1041 | { |
1042 | return KWin::displayHeight(); |
1043 | } |
1044 | |
1045 | EffectWindow* 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 | |
1054 | EffectWindowList 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 | |
1065 | void EffectsHandlerImpl::setElevatedWindow(EffectWindow* w, bool set) |
1066 | { |
1067 | elevated_windows.removeAll(w); |
1068 | if (set) |
1069 | elevated_windows.append(w); |
1070 | } |
1071 | |
1072 | void 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 | |
1083 | void 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 | |
1092 | EffectWindowList 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 | |
1106 | void EffectsHandlerImpl::refTabBox() |
1107 | { |
1108 | #ifdef KWIN_BUILD_TABBOX |
1109 | TabBox::TabBox::self()->reference(); |
1110 | #endif |
1111 | } |
1112 | |
1113 | void EffectsHandlerImpl::unrefTabBox() |
1114 | { |
1115 | #ifdef KWIN_BUILD_TABBOX |
1116 | TabBox::TabBox::self()->unreference(); |
1117 | #endif |
1118 | } |
1119 | |
1120 | void EffectsHandlerImpl::closeTabBox() |
1121 | { |
1122 | #ifdef KWIN_BUILD_TABBOX |
1123 | TabBox::TabBox::self()->close(); |
1124 | #endif |
1125 | } |
1126 | |
1127 | QList< int > EffectsHandlerImpl::currentTabBoxDesktopList() const |
1128 | { |
1129 | #ifdef KWIN_BUILD_TABBOX |
1130 | return TabBox::TabBox::self()->currentDesktopList(); |
1131 | #endif |
1132 | return QList< int >(); |
1133 | } |
1134 | |
1135 | int EffectsHandlerImpl::currentTabBoxDesktop() const |
1136 | { |
1137 | #ifdef KWIN_BUILD_TABBOX |
1138 | return TabBox::TabBox::self()->currentDesktop(); |
1139 | #endif |
1140 | return -1; |
1141 | } |
1142 | |
1143 | EffectWindow* 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 | |
1152 | void EffectsHandlerImpl::addRepaintFull() |
1153 | { |
1154 | m_compositor->addRepaintFull(); |
1155 | } |
1156 | |
1157 | void EffectsHandlerImpl::addRepaint(const QRect& r) |
1158 | { |
1159 | m_compositor->addRepaint(r); |
1160 | } |
1161 | |
1162 | void EffectsHandlerImpl::addRepaint(const QRegion& r) |
1163 | { |
1164 | m_compositor->addRepaint(r); |
1165 | } |
1166 | |
1167 | void EffectsHandlerImpl::addRepaint(int x, int y, int w, int h) |
1168 | { |
1169 | m_compositor->addRepaint(x, y, w, h); |
1170 | } |
1171 | |
1172 | int EffectsHandlerImpl::activeScreen() const |
1173 | { |
1174 | return screens()->current(); |
1175 | } |
1176 | |
1177 | int EffectsHandlerImpl::numScreens() const |
1178 | { |
1179 | return screens()->count(); |
1180 | } |
1181 | |
1182 | int EffectsHandlerImpl::screenNumber(const QPoint& pos) const |
1183 | { |
1184 | return screens()->number(pos); |
1185 | } |
1186 | |
1187 | QRect EffectsHandlerImpl::clientArea(clientAreaOption opt, int screen, int desktop) const |
1188 | { |
1189 | return Workspace::self()->clientArea(opt, screen, desktop); |
1190 | } |
1191 | |
1192 | QRect 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 | |
1201 | QRect EffectsHandlerImpl::clientArea(clientAreaOption opt, const QPoint& p, int desktop) const |
1202 | { |
1203 | return Workspace::self()->clientArea(opt, p, desktop); |
1204 | } |
1205 | |
1206 | void EffectsHandlerImpl::defineCursor(Qt::CursorShape shape) |
1207 | { |
1208 | if (!m_mouseInterceptionWindow.isValid()) { |
1209 | return; |
1210 | } |
1211 | m_mouseInterceptionWindow.defineCursor(Cursor::x11Cursor(shape)); |
1212 | } |
1213 | |
1214 | bool 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 | |
1255 | void 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 | |
1268 | QPoint EffectsHandlerImpl::cursorPos() const |
1269 | { |
1270 | return Cursor::pos(); |
1271 | } |
1272 | |
1273 | void 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 | |
1283 | void 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 | |
1293 | unsigned 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 | |
1302 | KLibrary* 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 | |
1321 | void EffectsHandlerImpl::toggleEffect(const QString& name) |
1322 | { |
1323 | if (isEffectLoaded(name)) |
1324 | unloadEffect(name); |
1325 | else |
1326 | loadEffect(name); |
1327 | } |
1328 | |
1329 | QStringList 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 | |
1338 | QStringList 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 | |
1350 | bool 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 | |
1465 | bool 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 | |
1494 | void 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 | |
1523 | void 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 | |
1532 | bool 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 | |
1541 | void 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 | |
1556 | void 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 | |
1568 | QStringList 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 | |
1580 | EffectFrame* 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 | |
1586 | QVariant 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 | |
1602 | QString 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 | |
1625 | bool EffectsHandlerImpl::isScreenLocked() const |
1626 | { |
1627 | return m_screenLockerWatcher->isLocked(); |
1628 | } |
1629 | |
1630 | QString 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 | |
1645 | EffectWindowImpl::EffectWindowImpl(Toplevel *toplevel) |
1646 | : EffectWindow(toplevel) |
1647 | , toplevel(toplevel) |
1648 | , sw(NULL) |
1649 | { |
1650 | } |
1651 | |
1652 | EffectWindowImpl::~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 | |
1661 | bool EffectWindowImpl::isPaintingEnabled() |
1662 | { |
1663 | return sceneWindow()->isPaintingEnabled(); |
1664 | } |
1665 | |
1666 | void EffectWindowImpl::enablePainting(int reason) |
1667 | { |
1668 | sceneWindow()->enablePainting(reason); |
1669 | } |
1670 | |
1671 | void EffectWindowImpl::disablePainting(int reason) |
1672 | { |
1673 | sceneWindow()->disablePainting(reason); |
1674 | } |
1675 | |
1676 | const EffectWindowGroup* EffectWindowImpl::group() const |
1677 | { |
1678 | if (Client* c = dynamic_cast< Client* >(toplevel)) |
1679 | return c->group()->effectGroup(); |
1680 | return NULL; // TODO |
1681 | } |
1682 | |
1683 | void EffectWindowImpl::refWindow() |
1684 | { |
1685 | if (Deleted* d = dynamic_cast< Deleted* >(toplevel)) |
1686 | return d->refWindow(); |
1687 | abort(); // TODO |
1688 | } |
1689 | |
1690 | void EffectWindowImpl::unrefWindow() |
1691 | { |
1692 | if (Deleted* d = dynamic_cast< Deleted* >(toplevel)) |
1693 | return d->unrefWindow(); // delays deletion in case |
1694 | abort(); // TODO |
1695 | } |
1696 | |
1697 | void EffectWindowImpl::setWindow(Toplevel* w) |
1698 | { |
1699 | toplevel = w; |
1700 | setParent(w); |
1701 | } |
1702 | |
1703 | void EffectWindowImpl::setSceneWindow(Scene::Window* w) |
1704 | { |
1705 | sw = w; |
1706 | } |
1707 | |
1708 | QRegion EffectWindowImpl::shape() const |
1709 | { |
1710 | return sw ? sw->shape() : geometry(); |
1711 | } |
1712 | |
1713 | QRect EffectWindowImpl::decorationInnerRect() const |
1714 | { |
1715 | Client *client = dynamic_cast<Client*>(toplevel); |
1716 | return client ? client->transparentRect() : contentsRect(); |
1717 | } |
1718 | |
1719 | QByteArray EffectWindowImpl::readProperty(long atom, long type, int format) const |
1720 | { |
1721 | return readWindowProperty(window()->window(), atom, type, format); |
1722 | } |
1723 | |
1724 | void EffectWindowImpl::deleteProperty(long int atom) const |
1725 | { |
1726 | deleteWindowProperty(window()->window(), atom); |
1727 | } |
1728 | |
1729 | EffectWindow* 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 | |
1738 | template <typename T> |
1739 | EffectWindowList 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 | |
1749 | EffectWindowList 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 | |
1759 | WindowQuadList EffectWindowImpl::buildQuads(bool force) const |
1760 | { |
1761 | return sceneWindow()->buildQuads(force); |
1762 | } |
1763 | |
1764 | void EffectWindowImpl::setData(int role, const QVariant &data) |
1765 | { |
1766 | if (!data.isNull()) |
1767 | dataMap[ role ] = data; |
1768 | else |
1769 | dataMap.remove(role); |
1770 | } |
1771 | |
1772 | QVariant EffectWindowImpl::data(int role) const |
1773 | { |
1774 | if (!dataMap.contains(role)) |
1775 | return QVariant(); |
1776 | return dataMap[ role ]; |
1777 | } |
1778 | |
1779 | EffectWindow* effectWindow(Toplevel* w) |
1780 | { |
1781 | EffectWindowImpl* ret = w->effectWindow(); |
1782 | return ret; |
1783 | } |
1784 | |
1785 | EffectWindow* effectWindow(Scene::Window* w) |
1786 | { |
1787 | EffectWindowImpl* ret = w->window()->effectWindow(); |
1788 | ret->setSceneWindow(w); |
1789 | return ret; |
1790 | } |
1791 | |
1792 | void EffectWindowImpl::elevate(bool elevate) |
1793 | { |
1794 | effects->setElevatedWindow(this, elevate); |
1795 | } |
1796 | |
1797 | void 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 | |
1809 | void EffectWindowImpl::thumbnailDestroyed(QObject *object) |
1810 | { |
1811 | // we know it is a ThumbnailItem |
1812 | m_thumbnails.remove(static_cast<WindowThumbnailItem*>(object)); |
1813 | } |
1814 | |
1815 | void EffectWindowImpl::thumbnailTargetChanged() |
1816 | { |
1817 | if (WindowThumbnailItem *item = qobject_cast<WindowThumbnailItem*>(sender())) { |
1818 | insertThumbnail(item); |
1819 | } |
1820 | } |
1821 | |
1822 | void 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 | |
1832 | void EffectWindowImpl::desktopThumbnailDestroyed(QObject *object) |
1833 | { |
1834 | // we know it is a DesktopThumbnailItem |
1835 | m_desktopThumbnails.removeAll(static_cast<DesktopThumbnailItem*>(object)); |
1836 | } |
1837 | |
1838 | void EffectWindowImpl::referencePreviousWindowPixmap() |
1839 | { |
1840 | if (sw) { |
1841 | sw->referencePreviousPixmap(); |
1842 | } |
1843 | } |
1844 | |
1845 | void EffectWindowImpl::unreferencePreviousWindowPixmap() |
1846 | { |
1847 | if (sw) { |
1848 | sw->unreferencePreviousPixmap(); |
1849 | } |
1850 | } |
1851 | |
1852 | //**************************************** |
1853 | // EffectWindowGroupImpl |
1854 | //**************************************** |
1855 | |
1856 | |
1857 | EffectWindowList 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 | |
1869 | EffectFrameImpl::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 | |
1900 | EffectFrameImpl::~EffectFrameImpl() |
1901 | { |
1902 | delete m_sceneFrame; |
1903 | } |
1904 | |
1905 | const QFont& EffectFrameImpl::font() const |
1906 | { |
1907 | return m_font; |
1908 | } |
1909 | |
1910 | void 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 | |
1926 | void EffectFrameImpl::free() |
1927 | { |
1928 | m_sceneFrame->free(); |
1929 | } |
1930 | |
1931 | const QRect& EffectFrameImpl::geometry() const |
1932 | { |
1933 | return m_geometry; |
1934 | } |
1935 | |
1936 | void 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 | |
1958 | const QPixmap& EffectFrameImpl::icon() const |
1959 | { |
1960 | return m_icon; |
1961 | } |
1962 | |
1963 | void 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 | |
1975 | const QSize& EffectFrameImpl::iconSize() const |
1976 | { |
1977 | return m_iconSize; |
1978 | } |
1979 | |
1980 | void 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 | |
1990 | void EffectFrameImpl::plasmaThemeChanged() |
1991 | { |
1992 | free(); |
1993 | } |
1994 | |
1995 | void 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 | |
2004 | void 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 | |
2011 | Qt::Alignment EffectFrameImpl::alignment() const |
2012 | { |
2013 | return m_alignment; |
2014 | } |
2015 | |
2016 | |
2017 | void |
2018 | EffectFrameImpl::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 | |
2035 | void EffectFrameImpl::setAlignment(Qt::Alignment alignment) |
2036 | { |
2037 | m_alignment = alignment; |
2038 | align(m_geometry); |
2039 | setGeometry(m_geometry); |
2040 | } |
2041 | |
2042 | void 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 | |
2050 | const QString& EffectFrameImpl::text() const |
2051 | { |
2052 | return m_text; |
2053 | } |
2054 | |
2055 | void 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 | |
2072 | void 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 | |
2085 | void 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 | |
2106 | QColor EffectFrameImpl::styledTextColor() |
2107 | { |
2108 | return Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor); |
2109 | } |
2110 | |
2111 | } // namespace |
2112 | |