1 /*
2     KWin - the KDE window manager
3     This file is part of the KDE project.
4 
5     SPDX-FileCopyrightText: 2006 Lubos Lunak <l.lunak@kde.org>
6     SPDX-FileCopyrightText: 2010, 2011 Martin Gräßlin <mgraesslin@kde.org>
7 
8     SPDX-License-Identifier: GPL-2.0-or-later
9 */
10 
11 #include "effects.h"
12 
13 #include "abstract_output.h"
14 #include "effectsadaptor.h"
15 #include "effectloader.h"
16 #ifdef KWIN_BUILD_ACTIVITIES
17 #include "activities.h"
18 #endif
19 #include "deleted.h"
20 #include "x11client.h"
21 #include "cursor.h"
22 #include "group.h"
23 #include "internal_client.h"
24 #include "osd.h"
25 #include "pointer_input.h"
26 #include "unmanaged.h"
27 #ifdef KWIN_BUILD_TABBOX
28 #include "tabbox.h"
29 #endif
30 #include "screenedge.h"
31 #include "scripting/scriptedeffect.h"
32 #include "screens.h"
33 #include "screenlockerwatcher.h"
34 #include "virtualdesktops.h"
35 #include "window_property_notify_x11_filter.h"
36 #include "workspace.h"
37 #include "kwinglutils.h"
38 #include "kwineffectquickview.h"
39 
40 #include <QDebug>
41 #include <QMouseEvent>
42 #include <QWheelEvent>
43 
44 #include <Plasma/Theme>
45 
46 #include "composite.h"
47 #include "xcbutils.h"
48 #include "platform.h"
49 #include "waylandclient.h"
50 #include "wayland_server.h"
51 
52 #include "decorations/decorationbridge.h"
53 #include <KDecoration2/DecorationSettings>
54 
55 namespace KWin
56 {
57 //---------------------
58 // Static
59 
readWindowProperty(xcb_window_t win,xcb_atom_t atom,xcb_atom_t type,int format)60 static QByteArray readWindowProperty(xcb_window_t win, xcb_atom_t atom, xcb_atom_t type, int format)
61 {
62     if (win == XCB_WINDOW_NONE) {
63         return QByteArray();
64     }
65     uint32_t len = 32768;
66     for (;;) {
67         Xcb::Property prop(false, win, atom, XCB_ATOM_ANY, 0, len);
68         if (prop.isNull()) {
69             // get property failed
70             return QByteArray();
71         }
72         if (prop->bytes_after > 0) {
73             len *= 2;
74             continue;
75         }
76         return prop.toByteArray(format, type);
77     }
78 }
79 
deleteWindowProperty(xcb_window_t win,long int atom)80 static void deleteWindowProperty(xcb_window_t win, long int atom)
81 {
82     if (win == XCB_WINDOW_NONE) {
83         return;
84     }
85     xcb_delete_property(kwinApp()->x11Connection(), win, atom);
86 }
87 
registerSupportProperty(const QByteArray & propertyName)88 static xcb_atom_t registerSupportProperty(const QByteArray &propertyName)
89 {
90     auto c = kwinApp()->x11Connection();
91     if (!c) {
92         return XCB_ATOM_NONE;
93     }
94     // get the atom for the propertyName
95     ScopedCPointer<xcb_intern_atom_reply_t> atomReply(xcb_intern_atom_reply(c,
96         xcb_intern_atom_unchecked(c, false, propertyName.size(), propertyName.constData()),
97         nullptr));
98     if (atomReply.isNull()) {
99         return XCB_ATOM_NONE;
100     }
101     // announce property on root window
102     unsigned char dummy = 0;
103     xcb_change_property(c, XCB_PROP_MODE_REPLACE, kwinApp()->x11RootWindow(), atomReply->atom, atomReply->atom, 8, 1, &dummy);
104     // TODO: add to _NET_SUPPORTED
105     return atomReply->atom;
106 }
107 
108 //---------------------
109 
EffectsHandlerImpl(Compositor * compositor,Scene * scene)110 EffectsHandlerImpl::EffectsHandlerImpl(Compositor *compositor, Scene *scene)
111     : EffectsHandler(scene->compositingType())
112     , keyboard_grab_effect(nullptr)
113     , fullscreen_effect(nullptr)
114     , m_compositor(compositor)
115     , m_scene(scene)
116     , m_desktopRendering(false)
117     , m_currentRenderedDesktop(0)
118     , m_effectLoader(new EffectLoader(this))
119     , m_trackingCursorChanges(0)
120 {
121     qRegisterMetaType<QVector<KWin::EffectWindow*>>();
122     connect(m_effectLoader, &AbstractEffectLoader::effectLoaded, this,
123         [this](Effect *effect, const QString &name) {
124             effect_order.insert(effect->requestedEffectChainPosition(), EffectPair(name, effect));
125             loaded_effects << EffectPair(name, effect);
126             effectsChanged();
127         }
128     );
129     m_effectLoader->setConfig(kwinApp()->config());
130     new EffectsAdaptor(this);
131     QDBusConnection dbus = QDBusConnection::sessionBus();
132     dbus.registerObject(QStringLiteral("/Effects"), this);
133 
134     Workspace *ws = Workspace::self();
135     VirtualDesktopManager *vds = VirtualDesktopManager::self();
136     connect(ws, &Workspace::showingDesktopChanged,
137             this, &EffectsHandlerImpl::showingDesktopChanged);
138     connect(ws, &Workspace::currentDesktopChanged, this,
139         [this](int old, AbstractClient *c) {
140             const int newDesktop = VirtualDesktopManager::self()->current();
141             if (old != 0 && newDesktop != old) {
142                 Q_EMIT desktopChanged(old, newDesktop, c ? c->effectWindow() : nullptr);
143                 // TODO: remove in 4.10
144                 Q_EMIT desktopChanged(old, newDesktop);
145             }
146         }
147     );
148     connect(ws, &Workspace::desktopPresenceChanged, this,
149         [this](AbstractClient *c, int old) {
150             if (!c->effectWindow()) {
151                 return;
152             }
153             Q_EMIT desktopPresenceChanged(c->effectWindow(), old, c->desktop());
154         }
155     );
156     connect(ws, &Workspace::clientAdded, this,
157         [this](AbstractClient *c) {
158             if (c->readyForPainting())
159                 slotClientShown(c);
160             else
161                 connect(c, &Toplevel::windowShown, this, &EffectsHandlerImpl::slotClientShown);
162         }
163     );
164     connect(ws, &Workspace::unmanagedAdded, this,
165         [this](Unmanaged *u) {
166             // it's never initially ready but has synthetic 50ms delay
167             connect(u, &Toplevel::windowShown, this, &EffectsHandlerImpl::slotUnmanagedShown);
168         }
169     );
170     connect(ws, &Workspace::internalClientAdded, this,
171         [this](InternalClient *client) {
172             setupClientConnections(client);
173             Q_EMIT windowAdded(client->effectWindow());
174         }
175     );
176     connect(ws, &Workspace::clientActivated, this,
177         [this](KWin::AbstractClient *c) {
178             Q_EMIT windowActivated(c ? c->effectWindow() : nullptr);
179         }
180     );
181     connect(ws, &Workspace::deletedRemoved, this,
182         [this](KWin::Deleted *d) {
183             Q_EMIT windowDeleted(d->effectWindow());
184             elevated_windows.removeAll(d->effectWindow());
185         }
186     );
187     connect(ws->sessionManager(), &SessionManager::stateChanged, this,
188             &KWin::EffectsHandler::sessionStateChanged);
189     connect(vds, &VirtualDesktopManager::countChanged, this, &EffectsHandler::numberDesktopsChanged);
190     connect(Cursors::self()->mouse(), &Cursor::mouseChanged, this, &EffectsHandler::mouseChanged);
191     connect(Screens::self(), &Screens::countChanged, this, &EffectsHandler::numberScreensChanged);
192     connect(Screens::self(), &Screens::sizeChanged, this, &EffectsHandler::virtualScreenSizeChanged);
193     connect(Screens::self(), &Screens::geometryChanged, this, &EffectsHandler::virtualScreenGeometryChanged);
194 #ifdef KWIN_BUILD_ACTIVITIES
195     if (Activities *activities = Activities::self()) {
196         connect(activities, &Activities::added,          this, &EffectsHandler::activityAdded);
197         connect(activities, &Activities::removed,        this, &EffectsHandler::activityRemoved);
198         connect(activities, &Activities::currentChanged, this, &EffectsHandler::currentActivityChanged);
199     }
200 #endif
201     connect(ws, &Workspace::stackingOrderChanged, this, &EffectsHandler::stackingOrderChanged);
202 #ifdef KWIN_BUILD_TABBOX
203     TabBox::TabBox *tabBox = TabBox::TabBox::self();
204     connect(tabBox, &TabBox::TabBox::tabBoxAdded,    this, &EffectsHandler::tabBoxAdded);
205     connect(tabBox, &TabBox::TabBox::tabBoxUpdated,  this, &EffectsHandler::tabBoxUpdated);
206     connect(tabBox, &TabBox::TabBox::tabBoxClosed,   this, &EffectsHandler::tabBoxClosed);
207     connect(tabBox, &TabBox::TabBox::tabBoxKeyEvent, this, &EffectsHandler::tabBoxKeyEvent);
208 #endif
209     connect(ScreenEdges::self(), &ScreenEdges::approaching, this, &EffectsHandler::screenEdgeApproaching);
210     connect(ScreenLockerWatcher::self(), &ScreenLockerWatcher::locked, this, &EffectsHandler::screenLockingChanged);
211     connect(ScreenLockerWatcher::self(), &ScreenLockerWatcher::aboutToLock, this, &EffectsHandler::screenAboutToLock);
212 
213     connect(kwinApp(), &Application::x11ConnectionChanged, this,
214         [this] {
215             registered_atoms.clear();
216             for (auto it = m_propertiesForEffects.keyBegin(); it != m_propertiesForEffects.keyEnd(); it++) {
217                 const auto atom = registerSupportProperty(*it);
218                 if (atom == XCB_ATOM_NONE) {
219                     continue;
220                 }
221                 m_compositor->keepSupportProperty(atom);
222                 m_managedProperties.insert(*it, atom);
223                 registerPropertyType(atom, true);
224             }
225             if (kwinApp()->x11Connection()) {
226                 m_x11WindowPropertyNotify = std::make_unique<WindowPropertyNotifyX11Filter>(this);
227             } else {
228                 m_x11WindowPropertyNotify.reset();
229             }
230             Q_EMIT xcbConnectionChanged();
231         }
232     );
233 
234     if (kwinApp()->x11Connection()) {
235         m_x11WindowPropertyNotify = std::make_unique<WindowPropertyNotifyX11Filter>(this);
236     }
237 
238     // connect all clients
239     for (AbstractClient *client : ws->allClientList()) {
240         if (client->readyForPainting()) {
241             setupClientConnections(client);
242         } else {
243             connect(client, &Toplevel::windowShown, this, &EffectsHandlerImpl::slotClientShown);
244         }
245     }
246     for (Unmanaged *u : ws->unmanagedList()) {
247         setupUnmanagedConnections(u);
248     }
249     for (InternalClient *client : ws->internalClients()) {
250         setupClientConnections(client);
251     }
252 
253     connect(kwinApp()->platform(), &Platform::outputEnabled, this, &EffectsHandlerImpl::slotOutputEnabled);
254     connect(kwinApp()->platform(), &Platform::outputDisabled, this, &EffectsHandlerImpl::slotOutputDisabled);
255 
256     const QVector<AbstractOutput *> outputs = kwinApp()->platform()->enabledOutputs();
257     for (AbstractOutput *output : outputs) {
258         slotOutputEnabled(output);
259     }
260 
261     reconfigure();
262 }
263 
~EffectsHandlerImpl()264 EffectsHandlerImpl::~EffectsHandlerImpl()
265 {
266     unloadAllEffects();
267 }
268 
unloadAllEffects()269 void EffectsHandlerImpl::unloadAllEffects()
270 {
271     for (const EffectPair &pair : qAsConst(loaded_effects)) {
272         destroyEffect(pair.second);
273     }
274 
275     effect_order.clear();
276     m_effectLoader->clear();
277 
278     effectsChanged();
279 }
280 
setupClientConnections(AbstractClient * c)281 void EffectsHandlerImpl::setupClientConnections(AbstractClient* c)
282 {
283     connect(c, &AbstractClient::windowClosed, this, &EffectsHandlerImpl::slotWindowClosed);
284     connect(c, static_cast<void (AbstractClient::*)(KWin::AbstractClient*, MaximizeMode)>(&AbstractClient::clientMaximizedStateChanged),
285             this, &EffectsHandlerImpl::slotClientMaximized);
286     connect(c, &AbstractClient::clientStartUserMovedResized, this,
287         [this](AbstractClient *c) {
288             Q_EMIT windowStartUserMovedResized(c->effectWindow());
289         }
290     );
291     connect(c, &AbstractClient::clientStepUserMovedResized, this,
292         [this](AbstractClient *c, const QRect &geometry) {
293             Q_EMIT windowStepUserMovedResized(c->effectWindow(), geometry);
294         }
295     );
296     connect(c, &AbstractClient::clientFinishUserMovedResized, this,
297         [this](AbstractClient *c) {
298             Q_EMIT windowFinishUserMovedResized(c->effectWindow());
299         }
300     );
301     connect(c, &AbstractClient::opacityChanged, this, &EffectsHandlerImpl::slotOpacityChanged);
302     connect(c, &AbstractClient::clientMinimized, this,
303         [this](AbstractClient *c, bool animate) {
304             // TODO: notify effects even if it should not animate?
305             if (animate) {
306                 Q_EMIT windowMinimized(c->effectWindow());
307             }
308         }
309     );
310     connect(c, &AbstractClient::clientUnminimized, this,
311         [this](AbstractClient* c, bool animate) {
312             // TODO: notify effects even if it should not animate?
313             if (animate) {
314                 Q_EMIT windowUnminimized(c->effectWindow());
315             }
316         }
317     );
318     connect(c, &AbstractClient::modalChanged,         this, &EffectsHandlerImpl::slotClientModalityChanged);
319     connect(c, &AbstractClient::geometryShapeChanged, this, &EffectsHandlerImpl::slotGeometryShapeChanged);
320     connect(c, &AbstractClient::frameGeometryChanged, this, &EffectsHandlerImpl::slotFrameGeometryChanged);
321     connect(c, &AbstractClient::damaged,              this, &EffectsHandlerImpl::slotWindowDamaged);
322     connect(c, &AbstractClient::unresponsiveChanged, this,
323         [this, c](bool unresponsive) {
324             Q_EMIT windowUnresponsiveChanged(c->effectWindow(), unresponsive);
325         }
326     );
327     connect(c, &AbstractClient::windowShown, this,
328         [this](Toplevel *c) {
329             Q_EMIT windowShown(c->effectWindow());
330         }
331     );
332     connect(c, &AbstractClient::windowHidden, this,
333         [this](Toplevel *c) {
334             Q_EMIT windowHidden(c->effectWindow());
335         }
336     );
337     connect(c, &AbstractClient::keepAboveChanged, this,
338         [this, c](bool above) {
339             Q_UNUSED(above)
340             Q_EMIT windowKeepAboveChanged(c->effectWindow());
341         }
342     );
343     connect(c, &AbstractClient::keepBelowChanged, this,
344         [this, c](bool below) {
345             Q_UNUSED(below)
346             Q_EMIT windowKeepBelowChanged(c->effectWindow());
347         }
348     );
349     connect(c, &AbstractClient::fullScreenChanged, this,
350         [this, c]() {
351             Q_EMIT windowFullScreenChanged(c->effectWindow());
352         }
353     );
354     connect(c, &AbstractClient::visibleGeometryChanged, this, [this, c]() {
355         Q_EMIT windowExpandedGeometryChanged(c->effectWindow());
356     });
357 }
358 
setupUnmanagedConnections(Unmanaged * u)359 void EffectsHandlerImpl::setupUnmanagedConnections(Unmanaged* u)
360 {
361     connect(u, &Unmanaged::windowClosed,         this, &EffectsHandlerImpl::slotWindowClosed);
362     connect(u, &Unmanaged::opacityChanged,       this, &EffectsHandlerImpl::slotOpacityChanged);
363     connect(u, &Unmanaged::geometryShapeChanged, this, &EffectsHandlerImpl::slotGeometryShapeChanged);
364     connect(u, &Unmanaged::frameGeometryChanged, this, &EffectsHandlerImpl::slotFrameGeometryChanged);
365     connect(u, &Unmanaged::damaged,              this, &EffectsHandlerImpl::slotWindowDamaged);
366     connect(u, &Unmanaged::visibleGeometryChanged, this, [this, u]() {
367         Q_EMIT windowExpandedGeometryChanged(u->effectWindow());
368     });
369 }
370 
reconfigure()371 void EffectsHandlerImpl::reconfigure()
372 {
373     m_effectLoader->queryAndLoadAll();
374 }
375 
376 // the idea is that effects call this function again which calls the next one
prePaintScreen(ScreenPrePaintData & data,std::chrono::milliseconds presentTime)377 void EffectsHandlerImpl::prePaintScreen(ScreenPrePaintData& data, std::chrono::milliseconds presentTime)
378 {
379     if (m_currentPaintScreenIterator != m_activeEffects.constEnd()) {
380         (*m_currentPaintScreenIterator++)->prePaintScreen(data, presentTime);
381         --m_currentPaintScreenIterator;
382     }
383     // no special final code
384 }
385 
paintScreen(int mask,const QRegion & region,ScreenPaintData & data)386 void EffectsHandlerImpl::paintScreen(int mask, const QRegion &region, ScreenPaintData& data)
387 {
388     if (m_currentPaintScreenIterator != m_activeEffects.constEnd()) {
389         (*m_currentPaintScreenIterator++)->paintScreen(mask, region, data);
390         --m_currentPaintScreenIterator;
391     } else
392         m_scene->finalPaintScreen(mask, region, data);
393 }
394 
paintDesktop(int desktop,int mask,QRegion region,ScreenPaintData & data)395 void EffectsHandlerImpl::paintDesktop(int desktop, int mask, QRegion region, ScreenPaintData &data)
396 {
397     if (desktop < 1 || desktop > numberOfDesktops()) {
398         return;
399     }
400     m_currentRenderedDesktop = desktop;
401     m_desktopRendering = true;
402     // save the paint screen iterator
403     EffectsIterator savedIterator = m_currentPaintScreenIterator;
404     m_currentPaintScreenIterator = m_activeEffects.constBegin();
405     effects->paintScreen(mask, region, data);
406     // restore the saved iterator
407     m_currentPaintScreenIterator = savedIterator;
408     m_desktopRendering = false;
409 }
410 
postPaintScreen()411 void EffectsHandlerImpl::postPaintScreen()
412 {
413     if (m_currentPaintScreenIterator != m_activeEffects.constEnd()) {
414         (*m_currentPaintScreenIterator++)->postPaintScreen();
415         --m_currentPaintScreenIterator;
416     }
417     // no special final code
418 }
419 
prePaintWindow(EffectWindow * w,WindowPrePaintData & data,std::chrono::milliseconds presentTime)420 void EffectsHandlerImpl::prePaintWindow(EffectWindow* w, WindowPrePaintData& data, std::chrono::milliseconds presentTime)
421 {
422     if (m_currentPaintWindowIterator != m_activeEffects.constEnd()) {
423         (*m_currentPaintWindowIterator++)->prePaintWindow(w, data, presentTime);
424         --m_currentPaintWindowIterator;
425     }
426     // no special final code
427 }
428 
paintWindow(EffectWindow * w,int mask,const QRegion & region,WindowPaintData & data)429 void EffectsHandlerImpl::paintWindow(EffectWindow* w, int mask, const QRegion &region, WindowPaintData& data)
430 {
431     if (m_currentPaintWindowIterator != m_activeEffects.constEnd()) {
432         (*m_currentPaintWindowIterator++)->paintWindow(w, mask, region, data);
433         --m_currentPaintWindowIterator;
434     } else
435         m_scene->finalPaintWindow(static_cast<EffectWindowImpl*>(w), mask, region, data);
436 }
437 
paintEffectFrame(EffectFrame * frame,const QRegion & region,double opacity,double frameOpacity)438 void EffectsHandlerImpl::paintEffectFrame(EffectFrame* frame, const QRegion &region, double opacity, double frameOpacity)
439 {
440     if (m_currentPaintEffectFrameIterator != m_activeEffects.constEnd()) {
441         (*m_currentPaintEffectFrameIterator++)->paintEffectFrame(frame, region, opacity, frameOpacity);
442         --m_currentPaintEffectFrameIterator;
443     } else {
444         const EffectFrameImpl* frameImpl = static_cast<const EffectFrameImpl*>(frame);
445         frameImpl->finalRender(region, opacity, frameOpacity);
446     }
447 }
448 
postPaintWindow(EffectWindow * w)449 void EffectsHandlerImpl::postPaintWindow(EffectWindow* w)
450 {
451     if (m_currentPaintWindowIterator != m_activeEffects.constEnd()) {
452         (*m_currentPaintWindowIterator++)->postPaintWindow(w);
453         --m_currentPaintWindowIterator;
454     }
455     // no special final code
456 }
457 
provides(Effect::Feature ef)458 Effect *EffectsHandlerImpl::provides(Effect::Feature ef)
459 {
460     for (int i = 0; i < loaded_effects.size(); ++i)
461         if (loaded_effects.at(i).second->provides(ef))
462             return loaded_effects.at(i).second;
463     return nullptr;
464 }
465 
drawWindow(EffectWindow * w,int mask,const QRegion & region,WindowPaintData & data)466 void EffectsHandlerImpl::drawWindow(EffectWindow* w, int mask, const QRegion &region, WindowPaintData& data)
467 {
468     if (m_currentDrawWindowIterator != m_activeEffects.constEnd()) {
469         (*m_currentDrawWindowIterator++)->drawWindow(w, mask, region, data);
470         --m_currentDrawWindowIterator;
471     } else
472         m_scene->finalDrawWindow(static_cast<EffectWindowImpl*>(w), mask, region, data);
473 }
474 
hasDecorationShadows() const475 bool EffectsHandlerImpl::hasDecorationShadows() const
476 {
477     return false;
478 }
479 
decorationsHaveAlpha() const480 bool EffectsHandlerImpl::decorationsHaveAlpha() const
481 {
482     return true;
483 }
484 
decorationSupportsBlurBehind() const485 bool EffectsHandlerImpl::decorationSupportsBlurBehind() const
486 {
487     return Decoration::DecorationBridge::self()->needsBlur();
488 }
489 
490 // start another painting pass
startPaint()491 void EffectsHandlerImpl::startPaint()
492 {
493     m_activeEffects.clear();
494     m_activeEffects.reserve(loaded_effects.count());
495     for(QVector< KWin::EffectPair >::const_iterator it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
496         if (it->second->isActive()) {
497             m_activeEffects << it->second;
498         }
499     }
500     m_currentDrawWindowIterator = m_activeEffects.constBegin();
501     m_currentPaintWindowIterator = m_activeEffects.constBegin();
502     m_currentPaintScreenIterator = m_activeEffects.constBegin();
503     m_currentPaintEffectFrameIterator = m_activeEffects.constBegin();
504 }
505 
slotClientMaximized(KWin::AbstractClient * c,MaximizeMode maxMode)506 void EffectsHandlerImpl::slotClientMaximized(KWin::AbstractClient *c, MaximizeMode maxMode)
507 {
508     bool horizontal = false;
509     bool vertical = false;
510     switch (maxMode) {
511     case MaximizeHorizontal:
512         horizontal = true;
513         break;
514     case MaximizeVertical:
515         vertical = true;
516         break;
517     case MaximizeFull:
518         horizontal = true;
519         vertical = true;
520         break;
521     case MaximizeRestore: // fall through
522     default:
523         // default - nothing to do
524         break;
525     }
526     if (EffectWindowImpl *w = c->effectWindow()) {
527         Q_EMIT windowMaximizedStateChanged(w, horizontal, vertical);
528     }
529 }
530 
slotOpacityChanged(Toplevel * t,qreal oldOpacity)531 void EffectsHandlerImpl::slotOpacityChanged(Toplevel *t, qreal oldOpacity)
532 {
533     if (t->opacity() == oldOpacity || !t->effectWindow()) {
534         return;
535     }
536     Q_EMIT windowOpacityChanged(t->effectWindow(), oldOpacity, (qreal)t->opacity());
537 }
538 
slotClientShown(KWin::Toplevel * t)539 void EffectsHandlerImpl::slotClientShown(KWin::Toplevel *t)
540 {
541     Q_ASSERT(qobject_cast<AbstractClient *>(t));
542     AbstractClient *c = static_cast<AbstractClient *>(t);
543     disconnect(c, &Toplevel::windowShown, this, &EffectsHandlerImpl::slotClientShown);
544     setupClientConnections(c);
545     Q_EMIT windowAdded(c->effectWindow());
546 }
547 
slotUnmanagedShown(KWin::Toplevel * t)548 void EffectsHandlerImpl::slotUnmanagedShown(KWin::Toplevel *t)
549 {   // regardless, unmanaged windows are -yet?- not synced anyway
550     Q_ASSERT(qobject_cast<Unmanaged *>(t));
551     Unmanaged *u = static_cast<Unmanaged*>(t);
552     setupUnmanagedConnections(u);
553     Q_EMIT windowAdded(u->effectWindow());
554 }
555 
slotWindowClosed(KWin::Toplevel * c,KWin::Deleted * d)556 void EffectsHandlerImpl::slotWindowClosed(KWin::Toplevel *c, KWin::Deleted *d)
557 {
558     c->disconnect(this);
559     if (d) {
560         Q_EMIT windowClosed(c->effectWindow());
561     }
562 }
563 
slotClientModalityChanged()564 void EffectsHandlerImpl::slotClientModalityChanged()
565 {
566     Q_EMIT windowModalityChanged(static_cast<X11Client *>(sender())->effectWindow());
567 }
568 
slotCurrentTabAboutToChange(EffectWindow * from,EffectWindow * to)569 void EffectsHandlerImpl::slotCurrentTabAboutToChange(EffectWindow *from, EffectWindow *to)
570 {
571     Q_EMIT currentTabAboutToChange(from, to);
572 }
573 
slotTabAdded(EffectWindow * w,EffectWindow * to)574 void EffectsHandlerImpl::slotTabAdded(EffectWindow* w, EffectWindow* to)
575 {
576     Q_EMIT tabAdded(w, to);
577 }
578 
slotTabRemoved(EffectWindow * w,EffectWindow * leaderOfFormerGroup)579 void EffectsHandlerImpl::slotTabRemoved(EffectWindow *w, EffectWindow* leaderOfFormerGroup)
580 {
581     Q_EMIT tabRemoved(w, leaderOfFormerGroup);
582 }
583 
slotWindowDamaged(Toplevel * t,const QRegion & r)584 void EffectsHandlerImpl::slotWindowDamaged(Toplevel* t, const QRegion& r)
585 {
586     if (!t->effectWindow()) {
587         // can happen during tear down of window
588         return;
589     }
590     Q_EMIT windowDamaged(t->effectWindow(), r);
591 }
592 
slotGeometryShapeChanged(Toplevel * t,const QRect & old)593 void EffectsHandlerImpl::slotGeometryShapeChanged(Toplevel* t, const QRect& old)
594 {
595     // during late cleanup effectWindow() may be already NULL
596     // in some functions that may still call this
597     if (t == nullptr || t->effectWindow() == nullptr)
598         return;
599     Q_EMIT windowGeometryShapeChanged(t->effectWindow(), old);
600 }
601 
slotFrameGeometryChanged(Toplevel * toplevel,const QRect & oldGeometry)602 void EffectsHandlerImpl::slotFrameGeometryChanged(Toplevel *toplevel, const QRect &oldGeometry)
603 {
604     // effectWindow() might be nullptr during tear down of the client.
605     if (toplevel->effectWindow()) {
606         Q_EMIT windowFrameGeometryChanged(toplevel->effectWindow(), oldGeometry);
607     }
608 }
609 
setActiveFullScreenEffect(Effect * e)610 void EffectsHandlerImpl::setActiveFullScreenEffect(Effect* e)
611 {
612     if (fullscreen_effect == e) {
613         return;
614     }
615     const bool activeChanged = (e == nullptr || fullscreen_effect == nullptr);
616     fullscreen_effect = e;
617     Q_EMIT activeFullScreenEffectChanged();
618     if (activeChanged) {
619         Q_EMIT hasActiveFullScreenEffectChanged();
620     }
621 }
622 
activeFullScreenEffect() const623 Effect* EffectsHandlerImpl::activeFullScreenEffect() const
624 {
625     return fullscreen_effect;
626 }
627 
hasActiveFullScreenEffect() const628 bool EffectsHandlerImpl::hasActiveFullScreenEffect() const
629 {
630     return fullscreen_effect;
631 }
632 
grabKeyboard(Effect * effect)633 bool EffectsHandlerImpl::grabKeyboard(Effect* effect)
634 {
635     if (keyboard_grab_effect != nullptr)
636         return false;
637     if (!doGrabKeyboard()) {
638         return false;
639     }
640     keyboard_grab_effect = effect;
641     return true;
642 }
643 
doGrabKeyboard()644 bool EffectsHandlerImpl::doGrabKeyboard()
645 {
646     return true;
647 }
648 
ungrabKeyboard()649 void EffectsHandlerImpl::ungrabKeyboard()
650 {
651     Q_ASSERT(keyboard_grab_effect != nullptr);
652     doUngrabKeyboard();
653     keyboard_grab_effect = nullptr;
654 }
655 
doUngrabKeyboard()656 void EffectsHandlerImpl::doUngrabKeyboard()
657 {
658 }
659 
grabbedKeyboardEvent(QKeyEvent * e)660 void EffectsHandlerImpl::grabbedKeyboardEvent(QKeyEvent* e)
661 {
662     if (keyboard_grab_effect != nullptr)
663         keyboard_grab_effect->grabbedKeyboardEvent(e);
664 }
665 
startMouseInterception(Effect * effect,Qt::CursorShape shape)666 void EffectsHandlerImpl::startMouseInterception(Effect *effect, Qt::CursorShape shape)
667 {
668     if (m_grabbedMouseEffects.contains(effect)) {
669         return;
670     }
671     m_grabbedMouseEffects.append(effect);
672     if (m_grabbedMouseEffects.size() != 1) {
673         return;
674     }
675     doStartMouseInterception(shape);
676 }
677 
doStartMouseInterception(Qt::CursorShape shape)678 void EffectsHandlerImpl::doStartMouseInterception(Qt::CursorShape shape)
679 {
680     input()->pointer()->setEffectsOverrideCursor(shape);
681 }
682 
stopMouseInterception(Effect * effect)683 void EffectsHandlerImpl::stopMouseInterception(Effect *effect)
684 {
685     if (!m_grabbedMouseEffects.contains(effect)) {
686         return;
687     }
688     m_grabbedMouseEffects.removeAll(effect);
689     if (m_grabbedMouseEffects.isEmpty()) {
690         doStopMouseInterception();
691     }
692 }
693 
doStopMouseInterception()694 void EffectsHandlerImpl::doStopMouseInterception()
695 {
696     input()->pointer()->removeEffectsOverrideCursor();
697 }
698 
isMouseInterception() const699 bool EffectsHandlerImpl::isMouseInterception() const
700 {
701     return m_grabbedMouseEffects.count() > 0;
702 }
703 
704 
touchDown(qint32 id,const QPointF & pos,quint32 time)705 bool EffectsHandlerImpl::touchDown(qint32 id, const QPointF &pos, quint32 time)
706 {
707     // TODO: reverse call order?
708     for (auto it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
709         if (it->second->touchDown(id, pos, time)) {
710             return true;
711         }
712     }
713     return false;
714 }
715 
touchMotion(qint32 id,const QPointF & pos,quint32 time)716 bool EffectsHandlerImpl::touchMotion(qint32 id, const QPointF &pos, quint32 time)
717 {
718     // TODO: reverse call order?
719     for (auto it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
720         if (it->second->touchMotion(id, pos, time)) {
721             return true;
722         }
723     }
724     return false;
725 }
726 
touchUp(qint32 id,quint32 time)727 bool EffectsHandlerImpl::touchUp(qint32 id, quint32 time)
728 {
729     // TODO: reverse call order?
730     for (auto it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
731         if (it->second->touchUp(id, time)) {
732             return true;
733         }
734     }
735     return false;
736 }
737 
registerGlobalShortcut(const QKeySequence & shortcut,QAction * action)738 void EffectsHandlerImpl::registerGlobalShortcut(const QKeySequence &shortcut, QAction *action)
739 {
740     input()->registerShortcut(shortcut, action);
741 }
742 
registerPointerShortcut(Qt::KeyboardModifiers modifiers,Qt::MouseButton pointerButtons,QAction * action)743 void EffectsHandlerImpl::registerPointerShortcut(Qt::KeyboardModifiers modifiers, Qt::MouseButton pointerButtons, QAction *action)
744 {
745     input()->registerPointerShortcut(modifiers, pointerButtons, action);
746 }
747 
registerAxisShortcut(Qt::KeyboardModifiers modifiers,PointerAxisDirection axis,QAction * action)748 void EffectsHandlerImpl::registerAxisShortcut(Qt::KeyboardModifiers modifiers, PointerAxisDirection axis, QAction *action)
749 {
750     input()->registerAxisShortcut(modifiers, axis, action);
751 }
752 
registerRealtimeTouchpadSwipeShortcut(SwipeDirection dir,QAction * onUp,std::function<void (qreal)> progressCallback)753 void EffectsHandlerImpl::registerRealtimeTouchpadSwipeShortcut(SwipeDirection dir, QAction* onUp, std::function<void(qreal)> progressCallback)
754 {
755     input()->registerRealtimeTouchpadSwipeShortcut(dir, onUp, progressCallback);
756 }
757 
registerTouchpadSwipeShortcut(SwipeDirection direction,QAction * action)758 void EffectsHandlerImpl::registerTouchpadSwipeShortcut(SwipeDirection direction, QAction *action)
759 {
760     input()->registerTouchpadSwipeShortcut(direction, action);
761 }
762 
getProxy(QString name)763 void* EffectsHandlerImpl::getProxy(QString name)
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 nullptr;
770 }
771 
startMousePolling()772 void EffectsHandlerImpl::startMousePolling()
773 {
774     if (Cursors::self()->mouse())
775         Cursors::self()->mouse()->startMousePolling();
776 }
777 
stopMousePolling()778 void EffectsHandlerImpl::stopMousePolling()
779 {
780     if (Cursors::self()->mouse())
781         Cursors::self()->mouse()->stopMousePolling();
782 }
783 
hasKeyboardGrab() const784 bool EffectsHandlerImpl::hasKeyboardGrab() const
785 {
786     return keyboard_grab_effect != nullptr;
787 }
788 
registerPropertyType(long atom,bool reg)789 void EffectsHandlerImpl::registerPropertyType(long atom, bool reg)
790 {
791     if (reg)
792         ++registered_atoms[ atom ]; // initialized to 0 if not present yet
793     else {
794         if (--registered_atoms[ atom ] == 0)
795             registered_atoms.remove(atom);
796     }
797 }
798 
announceSupportProperty(const QByteArray & propertyName,Effect * effect)799 xcb_atom_t EffectsHandlerImpl::announceSupportProperty(const QByteArray &propertyName, Effect *effect)
800 {
801     PropertyEffectMap::iterator it = m_propertiesForEffects.find(propertyName);
802     if (it != m_propertiesForEffects.end()) {
803         // property has already been registered for an effect
804         // just append Effect and return the atom stored in m_managedProperties
805         if (!it.value().contains(effect)) {
806             it.value().append(effect);
807         }
808         return m_managedProperties.value(propertyName, XCB_ATOM_NONE);
809     }
810     m_propertiesForEffects.insert(propertyName, QList<Effect*>() << effect);
811     const auto atom = registerSupportProperty(propertyName);
812     if (atom == XCB_ATOM_NONE) {
813         return atom;
814     }
815     m_compositor->keepSupportProperty(atom);
816     m_managedProperties.insert(propertyName, atom);
817     registerPropertyType(atom, true);
818     return atom;
819 }
820 
removeSupportProperty(const QByteArray & propertyName,Effect * effect)821 void EffectsHandlerImpl::removeSupportProperty(const QByteArray &propertyName, Effect *effect)
822 {
823     PropertyEffectMap::iterator it = m_propertiesForEffects.find(propertyName);
824     if (it == m_propertiesForEffects.end()) {
825         // property is not registered - nothing to do
826         return;
827     }
828     if (!it.value().contains(effect)) {
829         // property is not registered for given effect - nothing to do
830         return;
831     }
832     it.value().removeAll(effect);
833     if (!it.value().isEmpty()) {
834         // property still registered for another effect - nothing further to do
835         return;
836     }
837     const xcb_atom_t atom = m_managedProperties.take(propertyName);
838     registerPropertyType(atom, false);
839     m_propertiesForEffects.remove(propertyName);
840     m_compositor->removeSupportProperty(atom); // delayed removal
841 }
842 
readRootProperty(long atom,long type,int format) const843 QByteArray EffectsHandlerImpl::readRootProperty(long atom, long type, int format) const
844 {
845     if (!kwinApp()->x11Connection()) {
846         return QByteArray();
847     }
848     return readWindowProperty(kwinApp()->x11RootWindow(), atom, type, format);
849 }
850 
activateWindow(EffectWindow * c)851 void EffectsHandlerImpl::activateWindow(EffectWindow* c)
852 {
853     if (auto cl = qobject_cast<AbstractClient *>(static_cast<EffectWindowImpl *>(c)->window())) {
854         Workspace::self()->activateClient(cl, true);
855     }
856 }
857 
activeWindow() const858 EffectWindow* EffectsHandlerImpl::activeWindow() const
859 {
860     return Workspace::self()->activeClient() ? Workspace::self()->activeClient()->effectWindow() : nullptr;
861 }
862 
moveWindow(EffectWindow * w,const QPoint & pos,bool snap,double snapAdjust)863 void EffectsHandlerImpl::moveWindow(EffectWindow* w, const QPoint& pos, bool snap, double snapAdjust)
864 {
865     auto cl = qobject_cast<AbstractClient *>(static_cast<EffectWindowImpl *>(w)->window());
866     if (!cl || !cl->isMovable())
867         return;
868 
869     if (snap)
870         cl->move(Workspace::self()->adjustClientPosition(cl, pos, true, snapAdjust));
871     else
872         cl->move(pos);
873 }
874 
windowToDesktop(EffectWindow * w,int desktop)875 void EffectsHandlerImpl::windowToDesktop(EffectWindow* w, int desktop)
876 {
877     auto cl = qobject_cast<AbstractClient *>(static_cast<EffectWindowImpl *>(w)->window());
878     if (cl && !cl->isDesktop() && !cl->isDock()) {
879         Workspace::self()->sendClientToDesktop(cl, desktop, true);
880     }
881 }
882 
windowToDesktops(EffectWindow * w,const QVector<uint> & desktopIds)883 void EffectsHandlerImpl::windowToDesktops(EffectWindow *w, const QVector<uint> &desktopIds)
884 {
885     AbstractClient* cl = qobject_cast< AbstractClient* >(static_cast<EffectWindowImpl*>(w)->window());
886     if (!cl || cl->isDesktop() || cl->isDock()) {
887         return;
888     }
889     QVector<VirtualDesktop*> desktops;
890     desktops.reserve(desktopIds.count());
891     for (uint x11Id: desktopIds) {
892         if (x11Id > VirtualDesktopManager::self()->count()) {
893             continue;
894         }
895         VirtualDesktop *d = VirtualDesktopManager::self()->desktopForX11Id(x11Id);
896         Q_ASSERT(d);
897         if (desktops.contains(d)) {
898             continue;
899         }
900         desktops << d;
901     }
902     cl->setDesktops(desktops);
903 }
904 
windowToScreen(EffectWindow * w,int screen)905 void EffectsHandlerImpl::windowToScreen(EffectWindow* w, int screen)
906 {
907     AbstractOutput *output = kwinApp()->platform()->findOutput(screen);
908     if (!output) {
909         return;
910     }
911     auto cl = qobject_cast<AbstractClient *>(static_cast<EffectWindowImpl *>(w)->window());
912     if (cl && !cl->isDesktop() && !cl->isDock()) {
913         Workspace::self()->sendClientToOutput(cl, output);
914     }
915 }
916 
setShowingDesktop(bool showing)917 void EffectsHandlerImpl::setShowingDesktop(bool showing)
918 {
919     Workspace::self()->setShowingDesktop(showing);
920 }
921 
currentActivity() const922 QString EffectsHandlerImpl::currentActivity() const
923 {
924 #ifdef KWIN_BUILD_ACTIVITIES
925     if (!Activities::self()) {
926         return QString();
927     }
928     return Activities::self()->current();
929 #else
930     return QString();
931 #endif
932 }
933 
currentDesktop() const934 int EffectsHandlerImpl::currentDesktop() const
935 {
936     return VirtualDesktopManager::self()->current();
937 }
938 
numberOfDesktops() const939 int EffectsHandlerImpl::numberOfDesktops() const
940 {
941     return VirtualDesktopManager::self()->count();
942 }
943 
setCurrentDesktop(int desktop)944 void EffectsHandlerImpl::setCurrentDesktop(int desktop)
945 {
946     VirtualDesktopManager::self()->setCurrent(desktop);
947 }
948 
setNumberOfDesktops(int desktops)949 void EffectsHandlerImpl::setNumberOfDesktops(int desktops)
950 {
951     VirtualDesktopManager::self()->setCount(desktops);
952 }
953 
desktopGridSize() const954 QSize EffectsHandlerImpl::desktopGridSize() const
955 {
956     return VirtualDesktopManager::self()->grid().size();
957 }
958 
desktopGridWidth() const959 int EffectsHandlerImpl::desktopGridWidth() const
960 {
961     return desktopGridSize().width();
962 }
963 
desktopGridHeight() const964 int EffectsHandlerImpl::desktopGridHeight() const
965 {
966     return desktopGridSize().height();
967 }
968 
workspaceWidth() const969 int EffectsHandlerImpl::workspaceWidth() const
970 {
971     return desktopGridWidth() * Screens::self()->size().width();
972 }
973 
workspaceHeight() const974 int EffectsHandlerImpl::workspaceHeight() const
975 {
976     return desktopGridHeight() * Screens::self()->size().height();
977 }
978 
desktopAtCoords(QPoint coords) const979 int EffectsHandlerImpl::desktopAtCoords(QPoint coords) const
980 {
981     if (auto vd = VirtualDesktopManager::self()->grid().at(coords)) {
982         return vd->x11DesktopNumber();
983     }
984     return 0;
985 }
986 
desktopGridCoords(int id) const987 QPoint EffectsHandlerImpl::desktopGridCoords(int id) const
988 {
989     return VirtualDesktopManager::self()->grid().gridCoords(id);
990 }
991 
desktopCoords(int id) const992 QPoint EffectsHandlerImpl::desktopCoords(int id) const
993 {
994     QPoint coords = VirtualDesktopManager::self()->grid().gridCoords(id);
995     if (coords.x() == -1)
996         return QPoint(-1, -1);
997     const QSize displaySize = Screens::self()->size();
998     return QPoint(coords.x() * displaySize.width(), coords.y() * displaySize.height());
999 }
1000 
desktopAbove(int desktop,bool wrap) const1001 int EffectsHandlerImpl::desktopAbove(int desktop, bool wrap) const
1002 {
1003     return getDesktop<DesktopAbove>(desktop, wrap);
1004 }
1005 
desktopToRight(int desktop,bool wrap) const1006 int EffectsHandlerImpl::desktopToRight(int desktop, bool wrap) const
1007 {
1008     return getDesktop<DesktopRight>(desktop, wrap);
1009 }
1010 
desktopBelow(int desktop,bool wrap) const1011 int EffectsHandlerImpl::desktopBelow(int desktop, bool wrap) const
1012 {
1013     return getDesktop<DesktopBelow>(desktop, wrap);
1014 }
1015 
desktopToLeft(int desktop,bool wrap) const1016 int EffectsHandlerImpl::desktopToLeft(int desktop, bool wrap) const
1017 {
1018     return getDesktop<DesktopLeft>(desktop, wrap);
1019 }
1020 
desktopName(int desktop) const1021 QString EffectsHandlerImpl::desktopName(int desktop) const
1022 {
1023     const VirtualDesktop *vd = VirtualDesktopManager::self()->desktopForX11Id(desktop);
1024     return vd ? vd->name() : QString();
1025 }
1026 
optionRollOverDesktops() const1027 bool EffectsHandlerImpl::optionRollOverDesktops() const
1028 {
1029     return options->isRollOverDesktops();
1030 }
1031 
animationTimeFactor() const1032 double EffectsHandlerImpl::animationTimeFactor() const
1033 {
1034     return options->animationTimeFactor();
1035 }
1036 
findWindow(WId id) const1037 EffectWindow* EffectsHandlerImpl::findWindow(WId id) const
1038 {
1039     if (X11Client *w = Workspace::self()->findClient(Predicate::WindowMatch, id))
1040         return w->effectWindow();
1041     if (Unmanaged* w = Workspace::self()->findUnmanaged(id))
1042         return w->effectWindow();
1043     return nullptr;
1044 }
1045 
findWindow(KWaylandServer::SurfaceInterface * surf) const1046 EffectWindow* EffectsHandlerImpl::findWindow(KWaylandServer::SurfaceInterface *surf) const
1047 {
1048     if (waylandServer()) {
1049         if (AbstractClient *w = waylandServer()->findClient(surf)) {
1050             return w->effectWindow();
1051         }
1052     }
1053     return nullptr;
1054 }
1055 
findWindow(QWindow * w) const1056 EffectWindow *EffectsHandlerImpl::findWindow(QWindow *w) const
1057 {
1058     if (Toplevel *toplevel = workspace()->findInternal(w)) {
1059         return toplevel->effectWindow();
1060     }
1061     return nullptr;
1062 }
1063 
findWindow(const QUuid & id) const1064 EffectWindow *EffectsHandlerImpl::findWindow(const QUuid &id) const
1065 {
1066     if (const auto client = workspace()->findAbstractClient([&id] (const AbstractClient *c) { return c->internalId() == id; })) {
1067         return client->effectWindow();
1068     }
1069     if (const auto unmanaged = workspace()->findUnmanaged([&id] (const Unmanaged *c) { return c->internalId() == id; })) {
1070         return unmanaged->effectWindow();
1071     }
1072     return nullptr;
1073 }
1074 
stackingOrder() const1075 EffectWindowList EffectsHandlerImpl::stackingOrder() const
1076 {
1077     QList<Toplevel *> list = Workspace::self()->xStackingOrder();
1078     EffectWindowList ret;
1079     for (Toplevel *t : list) {
1080         if (EffectWindow *w = effectWindow(t))
1081             ret.append(w);
1082     }
1083     return ret;
1084 }
1085 
setElevatedWindow(KWin::EffectWindow * w,bool set)1086 void EffectsHandlerImpl::setElevatedWindow(KWin::EffectWindow* w, bool set)
1087 {
1088     elevated_windows.removeAll(w);
1089     if (set)
1090         elevated_windows.append(w);
1091 }
1092 
setTabBoxWindow(EffectWindow * w)1093 void EffectsHandlerImpl::setTabBoxWindow(EffectWindow* w)
1094 {
1095 #ifdef KWIN_BUILD_TABBOX
1096     if (auto c = qobject_cast<AbstractClient *>(static_cast<EffectWindowImpl *>(w)->window())) {
1097         TabBox::TabBox::self()->setCurrentClient(c);
1098     }
1099 #else
1100     Q_UNUSED(w)
1101 #endif
1102 }
1103 
setTabBoxDesktop(int desktop)1104 void EffectsHandlerImpl::setTabBoxDesktop(int desktop)
1105 {
1106 #ifdef KWIN_BUILD_TABBOX
1107     TabBox::TabBox::self()->setCurrentDesktop(desktop);
1108 #else
1109     Q_UNUSED(desktop)
1110 #endif
1111 }
1112 
currentTabBoxWindowList() const1113 EffectWindowList EffectsHandlerImpl::currentTabBoxWindowList() const
1114 {
1115 #ifdef KWIN_BUILD_TABBOX
1116     const auto clients = TabBox::TabBox::self()->currentClientList();
1117     EffectWindowList ret;
1118     ret.reserve(clients.size());
1119     std::transform(std::cbegin(clients), std::cend(clients),
1120         std::back_inserter(ret),
1121         [](auto client) { return client->effectWindow(); });
1122     return ret;
1123 #else
1124     return EffectWindowList();
1125 #endif
1126 }
1127 
refTabBox()1128 void EffectsHandlerImpl::refTabBox()
1129 {
1130 #ifdef KWIN_BUILD_TABBOX
1131     TabBox::TabBox::self()->reference();
1132 #endif
1133 }
1134 
unrefTabBox()1135 void EffectsHandlerImpl::unrefTabBox()
1136 {
1137 #ifdef KWIN_BUILD_TABBOX
1138     TabBox::TabBox::self()->unreference();
1139 #endif
1140 }
1141 
closeTabBox()1142 void EffectsHandlerImpl::closeTabBox()
1143 {
1144 #ifdef KWIN_BUILD_TABBOX
1145     TabBox::TabBox::self()->close();
1146 #endif
1147 }
1148 
currentTabBoxDesktopList() const1149 QList< int > EffectsHandlerImpl::currentTabBoxDesktopList() const
1150 {
1151 #ifdef KWIN_BUILD_TABBOX
1152     return TabBox::TabBox::self()->currentDesktopList();
1153 #else
1154     return QList< int >();
1155 #endif
1156 }
1157 
currentTabBoxDesktop() const1158 int EffectsHandlerImpl::currentTabBoxDesktop() const
1159 {
1160 #ifdef KWIN_BUILD_TABBOX
1161     return TabBox::TabBox::self()->currentDesktop();
1162 #else
1163     return -1;
1164 #endif
1165 }
1166 
currentTabBoxWindow() const1167 EffectWindow* EffectsHandlerImpl::currentTabBoxWindow() const
1168 {
1169 #ifdef KWIN_BUILD_TABBOX
1170     if (auto c = TabBox::TabBox::self()->currentClient())
1171         return c->effectWindow();
1172 #endif
1173     return nullptr;
1174 }
1175 
addRepaintFull()1176 void EffectsHandlerImpl::addRepaintFull()
1177 {
1178     m_compositor->addRepaintFull();
1179 }
1180 
addRepaint(const QRect & r)1181 void EffectsHandlerImpl::addRepaint(const QRect& r)
1182 {
1183     m_compositor->addRepaint(r);
1184 }
1185 
addRepaint(const QRegion & r)1186 void EffectsHandlerImpl::addRepaint(const QRegion& r)
1187 {
1188     m_compositor->addRepaint(r);
1189 }
1190 
addRepaint(int x,int y,int w,int h)1191 void EffectsHandlerImpl::addRepaint(int x, int y, int w, int h)
1192 {
1193     m_compositor->addRepaint(x, y, w, h);
1194 }
1195 
activeScreen() const1196 int EffectsHandlerImpl::activeScreen() const
1197 {
1198     return kwinApp()->platform()->enabledOutputs().indexOf(workspace()->activeOutput());
1199 }
1200 
numScreens() const1201 int EffectsHandlerImpl::numScreens() const
1202 {
1203     return Screens::self()->count();
1204 }
1205 
screenNumber(const QPoint & pos) const1206 int EffectsHandlerImpl::screenNumber(const QPoint& pos) const
1207 {
1208     return Screens::self()->number(pos);
1209 }
1210 
clientArea(clientAreaOption opt,int screen,int desktop) const1211 QRect EffectsHandlerImpl::clientArea(clientAreaOption opt, int screen, int desktop) const
1212 {
1213     return Workspace::self()->clientArea(opt, screen, desktop);
1214 }
1215 
clientArea(clientAreaOption opt,const EffectWindow * c) const1216 QRect EffectsHandlerImpl::clientArea(clientAreaOption opt, const EffectWindow* c) const
1217 {
1218     const Toplevel* t = static_cast< const EffectWindowImpl* >(c)->window();
1219     return Workspace::self()->clientArea(opt, t);
1220 }
1221 
clientArea(clientAreaOption opt,const QPoint & p,int desktop) const1222 QRect EffectsHandlerImpl::clientArea(clientAreaOption opt, const QPoint& p, int desktop) const
1223 {
1224     return Workspace::self()->clientArea(opt, p, desktop);
1225 }
1226 
virtualScreenGeometry() const1227 QRect EffectsHandlerImpl::virtualScreenGeometry() const
1228 {
1229     return Screens::self()->geometry();
1230 }
1231 
virtualScreenSize() const1232 QSize EffectsHandlerImpl::virtualScreenSize() const
1233 {
1234     return Screens::self()->size();
1235 }
1236 
defineCursor(Qt::CursorShape shape)1237 void EffectsHandlerImpl::defineCursor(Qt::CursorShape shape)
1238 {
1239     input()->pointer()->setEffectsOverrideCursor(shape);
1240 }
1241 
checkInputWindowEvent(QMouseEvent * e)1242 bool EffectsHandlerImpl::checkInputWindowEvent(QMouseEvent *e)
1243 {
1244     if (m_grabbedMouseEffects.isEmpty()) {
1245         return false;
1246     }
1247     Q_FOREACH (Effect *effect, m_grabbedMouseEffects) {
1248         effect->windowInputMouseEvent(e);
1249     }
1250     return true;
1251 }
1252 
checkInputWindowEvent(QWheelEvent * e)1253 bool EffectsHandlerImpl::checkInputWindowEvent(QWheelEvent *e)
1254 {
1255     if (m_grabbedMouseEffects.isEmpty()) {
1256         return false;
1257     }
1258     Q_FOREACH (Effect *effect, m_grabbedMouseEffects) {
1259         effect->windowInputMouseEvent(e);
1260     }
1261     return true;
1262 }
1263 
connectNotify(const QMetaMethod & signal)1264 void EffectsHandlerImpl::connectNotify(const QMetaMethod &signal)
1265 {
1266     if (signal == QMetaMethod::fromSignal(&EffectsHandler::cursorShapeChanged)) {
1267         if (!m_trackingCursorChanges) {
1268             connect(Cursors::self()->mouse(), &Cursor::cursorChanged, this, &EffectsHandler::cursorShapeChanged);
1269             Cursors::self()->mouse()->startCursorTracking();
1270         }
1271         ++m_trackingCursorChanges;
1272     }
1273     EffectsHandler::connectNotify(signal);
1274 }
1275 
disconnectNotify(const QMetaMethod & signal)1276 void EffectsHandlerImpl::disconnectNotify(const QMetaMethod &signal)
1277 {
1278     if (signal == QMetaMethod::fromSignal(&EffectsHandler::cursorShapeChanged)) {
1279         Q_ASSERT(m_trackingCursorChanges > 0);
1280         if (!--m_trackingCursorChanges) {
1281             Cursors::self()->mouse()->stopCursorTracking();
1282             disconnect(Cursors::self()->mouse(), &Cursor::cursorChanged, this, &EffectsHandler::cursorShapeChanged);
1283         }
1284     }
1285     EffectsHandler::disconnectNotify(signal);
1286 }
1287 
1288 
checkInputWindowStacking()1289 void EffectsHandlerImpl::checkInputWindowStacking()
1290 {
1291     if (m_grabbedMouseEffects.isEmpty()) {
1292         return;
1293     }
1294     doCheckInputWindowStacking();
1295 }
1296 
doCheckInputWindowStacking()1297 void EffectsHandlerImpl::doCheckInputWindowStacking()
1298 {
1299 }
1300 
cursorPos() const1301 QPoint EffectsHandlerImpl::cursorPos() const
1302 {
1303     return Cursors::self()->mouse()->pos();
1304 }
1305 
reserveElectricBorder(ElectricBorder border,Effect * effect)1306 void EffectsHandlerImpl::reserveElectricBorder(ElectricBorder border, Effect *effect)
1307 {
1308     ScreenEdges::self()->reserve(border, effect, "borderActivated");
1309 }
1310 
unreserveElectricBorder(ElectricBorder border,Effect * effect)1311 void EffectsHandlerImpl::unreserveElectricBorder(ElectricBorder border, Effect *effect)
1312 {
1313     ScreenEdges::self()->unreserve(border, effect);
1314 }
1315 
registerTouchBorder(ElectricBorder border,QAction * action)1316 void EffectsHandlerImpl::registerTouchBorder(ElectricBorder border, QAction *action)
1317 {
1318     ScreenEdges::self()->reserveTouch(border, action);
1319 }
1320 
unregisterTouchBorder(ElectricBorder border,QAction * action)1321 void EffectsHandlerImpl::unregisterTouchBorder(ElectricBorder border, QAction *action)
1322 {
1323     ScreenEdges::self()->unreserveTouch(border, action);
1324 }
1325 
scenePainter()1326 QPainter *EffectsHandlerImpl::scenePainter()
1327 {
1328     return m_scene->scenePainter();
1329 }
1330 
toggleEffect(const QString & name)1331 void EffectsHandlerImpl::toggleEffect(const QString& name)
1332 {
1333     if (isEffectLoaded(name))
1334         unloadEffect(name);
1335     else
1336         loadEffect(name);
1337 }
1338 
loadedEffects() const1339 QStringList EffectsHandlerImpl::loadedEffects() const
1340 {
1341     QStringList listModules;
1342     listModules.reserve(loaded_effects.count());
1343     std::transform(loaded_effects.constBegin(), loaded_effects.constEnd(),
1344         std::back_inserter(listModules),
1345         [](const EffectPair &pair) { return pair.first; });
1346     return listModules;
1347 }
1348 
listOfEffects() const1349 QStringList EffectsHandlerImpl::listOfEffects() const
1350 {
1351     return m_effectLoader->listOfKnownEffects();
1352 }
1353 
loadEffect(const QString & name)1354 bool EffectsHandlerImpl::loadEffect(const QString& name)
1355 {
1356     makeOpenGLContextCurrent();
1357     m_compositor->addRepaintFull();
1358 
1359     return m_effectLoader->loadEffect(name);
1360 }
1361 
unloadEffect(const QString & name)1362 void EffectsHandlerImpl::unloadEffect(const QString& name)
1363 {
1364     auto it = std::find_if(effect_order.begin(), effect_order.end(),
1365         [name](EffectPair &pair) {
1366             return pair.first == name;
1367         }
1368     );
1369     if (it == effect_order.end()) {
1370         qCDebug(KWIN_CORE) << "EffectsHandler::unloadEffect : Effect not loaded :" << name;
1371         return;
1372     }
1373 
1374     qCDebug(KWIN_CORE) << "EffectsHandler::unloadEffect : Unloading Effect :" << name;
1375     destroyEffect((*it).second);
1376     effect_order.erase(it);
1377     effectsChanged();
1378 
1379     m_compositor->addRepaintFull();
1380 }
1381 
destroyEffect(Effect * effect)1382 void EffectsHandlerImpl::destroyEffect(Effect *effect)
1383 {
1384     makeOpenGLContextCurrent();
1385 
1386     if (fullscreen_effect == effect) {
1387         setActiveFullScreenEffect(nullptr);
1388     }
1389 
1390     if (keyboard_grab_effect == effect) {
1391         ungrabKeyboard();
1392     }
1393 
1394     stopMouseInterception(effect);
1395 
1396     const QList<QByteArray> properties = m_propertiesForEffects.keys();
1397     for (const QByteArray &property : properties) {
1398         removeSupportProperty(property, effect);
1399     }
1400 
1401     delete effect;
1402 }
1403 
reconfigureEffect(const QString & name)1404 void EffectsHandlerImpl::reconfigureEffect(const QString& name)
1405 {
1406     for (QVector< EffectPair >::const_iterator it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it)
1407         if ((*it).first == name) {
1408             kwinApp()->config()->reparseConfiguration();
1409             makeOpenGLContextCurrent();
1410             (*it).second->reconfigure(Effect::ReconfigureAll);
1411             return;
1412         }
1413 }
1414 
isEffectLoaded(const QString & name) const1415 bool EffectsHandlerImpl::isEffectLoaded(const QString& name) const
1416 {
1417     auto it = std::find_if(loaded_effects.constBegin(), loaded_effects.constEnd(),
1418         [&name](const EffectPair &pair) { return pair.first == name; });
1419     return it != loaded_effects.constEnd();
1420 }
1421 
isEffectSupported(const QString & name)1422 bool EffectsHandlerImpl::isEffectSupported(const QString &name)
1423 {
1424     // If the effect is loaded, it is obviously supported.
1425     if (isEffectLoaded(name)) {
1426         return true;
1427     }
1428 
1429     // next checks might require a context
1430     makeOpenGLContextCurrent();
1431     m_compositor->addRepaintFull();
1432 
1433     return m_effectLoader->isEffectSupported(name);
1434 
1435 }
1436 
areEffectsSupported(const QStringList & names)1437 QList<bool> EffectsHandlerImpl::areEffectsSupported(const QStringList &names)
1438 {
1439     QList<bool> retList;
1440     retList.reserve(names.count());
1441     std::transform(names.constBegin(), names.constEnd(),
1442         std::back_inserter(retList),
1443         [this](const QString &name) {
1444             return isEffectSupported(name);
1445         });
1446     return retList;
1447 }
1448 
reloadEffect(Effect * effect)1449 void EffectsHandlerImpl::reloadEffect(Effect *effect)
1450 {
1451     QString effectName;
1452     for (QVector< EffectPair >::const_iterator it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
1453         if ((*it).second == effect) {
1454             effectName = (*it).first;
1455             break;
1456         }
1457     }
1458     if (!effectName.isNull()) {
1459         unloadEffect(effectName);
1460         m_effectLoader->loadEffect(effectName);
1461     }
1462 }
1463 
effectsChanged()1464 void EffectsHandlerImpl::effectsChanged()
1465 {
1466     loaded_effects.clear();
1467     m_activeEffects.clear(); // it's possible to have a reconfigure and a quad rebuild between two paint cycles - bug #308201
1468 
1469     loaded_effects.reserve(effect_order.count());
1470     std::copy(effect_order.constBegin(), effect_order.constEnd(),
1471         std::back_inserter(loaded_effects));
1472 
1473     m_activeEffects.reserve(loaded_effects.count());
1474 }
1475 
activeEffects() const1476 QStringList EffectsHandlerImpl::activeEffects() const
1477 {
1478     QStringList ret;
1479     for(QVector< KWin::EffectPair >::const_iterator it = loaded_effects.constBegin(),
1480                                                     end = loaded_effects.constEnd(); it != end; ++it) {
1481             if (it->second->isActive()) {
1482                 ret << it->first;
1483             }
1484         }
1485     return ret;
1486 }
1487 
blocksDirectScanout() const1488 bool EffectsHandlerImpl::blocksDirectScanout() const
1489 {
1490     for(QVector< KWin::EffectPair >::const_iterator it = loaded_effects.constBegin(),
1491                                                     end = loaded_effects.constEnd(); it != end; ++it) {
1492         if (it->second->isActive() && it->second->blocksDirectScanout()) {
1493             return true;
1494         }
1495     }
1496     return false;
1497 }
1498 
waylandDisplay() const1499 KWaylandServer::Display *EffectsHandlerImpl::waylandDisplay() const
1500 {
1501     if (waylandServer()) {
1502         return waylandServer()->display();
1503     }
1504     return nullptr;
1505 }
1506 
effectFrame(EffectFrameStyle style,bool staticSize,const QPoint & position,Qt::Alignment alignment) const1507 EffectFrame* EffectsHandlerImpl::effectFrame(EffectFrameStyle style, bool staticSize, const QPoint& position, Qt::Alignment alignment) const
1508 {
1509     return new EffectFrameImpl(style, staticSize, position, alignment);
1510 }
1511 
1512 
kwinOption(KWinOption kwopt)1513 QVariant EffectsHandlerImpl::kwinOption(KWinOption kwopt)
1514 {
1515     switch (kwopt) {
1516     case CloseButtonCorner: {
1517         // TODO: this could become per window and be derived from the actual position in the deco
1518         const auto settings = Decoration::DecorationBridge::self()->settings();
1519         return settings && settings->decorationButtonsLeft().contains(KDecoration2::DecorationButtonType::Close) ? Qt::TopLeftCorner : Qt::TopRightCorner;
1520     }
1521     case SwitchDesktopOnScreenEdge:
1522         return ScreenEdges::self()->isDesktopSwitching();
1523     case SwitchDesktopOnScreenEdgeMovingWindows:
1524         return ScreenEdges::self()->isDesktopSwitchingMovingClients();
1525     default:
1526         return QVariant(); // an invalid one
1527     }
1528 }
1529 
supportInformation(const QString & name) const1530 QString EffectsHandlerImpl::supportInformation(const QString &name) const
1531 {
1532     auto it = std::find_if(loaded_effects.constBegin(), loaded_effects.constEnd(),
1533         [name](const EffectPair &pair) { return pair.first == name; });
1534     if (it == loaded_effects.constEnd()) {
1535         return QString();
1536     }
1537 
1538     QString support((*it).first + QLatin1String(":\n"));
1539     const QMetaObject *metaOptions = (*it).second->metaObject();
1540     for (int i=0; i<metaOptions->propertyCount(); ++i) {
1541         const QMetaProperty property = metaOptions->property(i);
1542         if (qstrcmp(property.name(), "objectName") == 0) {
1543             continue;
1544         }
1545         support += QString::fromUtf8(property.name()) + QLatin1String(": ") + (*it).second->property(property.name()).toString() + QLatin1Char('\n');
1546     }
1547 
1548     return support;
1549 }
1550 
1551 
isScreenLocked() const1552 bool EffectsHandlerImpl::isScreenLocked() const
1553 {
1554     return ScreenLockerWatcher::self()->isLocked();
1555 }
1556 
debug(const QString & name,const QString & parameter) const1557 QString EffectsHandlerImpl::debug(const QString& name, const QString& parameter) const
1558 {
1559     QString internalName = name.toLower();;
1560     for (QVector< EffectPair >::const_iterator it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
1561         if ((*it).first == internalName) {
1562             return it->second->debug(parameter);
1563         }
1564     }
1565     return QString();
1566 }
1567 
makeOpenGLContextCurrent()1568 bool EffectsHandlerImpl::makeOpenGLContextCurrent()
1569 {
1570     return m_scene->makeOpenGLContextCurrent();
1571 }
1572 
doneOpenGLContextCurrent()1573 void EffectsHandlerImpl::doneOpenGLContextCurrent()
1574 {
1575     m_scene->doneOpenGLContextCurrent();
1576 }
1577 
animationsSupported() const1578 bool EffectsHandlerImpl::animationsSupported() const
1579 {
1580     static const QByteArray forceEnvVar = qgetenv("KWIN_EFFECTS_FORCE_ANIMATIONS");
1581     if (!forceEnvVar.isEmpty()) {
1582         static const int forceValue = forceEnvVar.toInt();
1583         return forceValue == 1;
1584     }
1585     return m_scene->animationsSupported();
1586 }
1587 
highlightWindows(const QVector<EffectWindow * > & windows)1588 void EffectsHandlerImpl::highlightWindows(const QVector<EffectWindow *> &windows)
1589 {
1590     Effect *e = provides(Effect::HighlightWindows);
1591     if (!e) {
1592         return;
1593     }
1594     e->perform(Effect::HighlightWindows, QVariantList{QVariant::fromValue(windows)});
1595 }
1596 
cursorImage() const1597 PlatformCursorImage EffectsHandlerImpl::cursorImage() const
1598 {
1599     return kwinApp()->platform()->cursorImage();
1600 }
1601 
hideCursor()1602 void EffectsHandlerImpl::hideCursor()
1603 {
1604     kwinApp()->platform()->hideCursor();
1605 }
1606 
showCursor()1607 void EffectsHandlerImpl::showCursor()
1608 {
1609     kwinApp()->platform()->showCursor();
1610 }
1611 
startInteractiveWindowSelection(std::function<void (KWin::EffectWindow *)> callback)1612 void EffectsHandlerImpl::startInteractiveWindowSelection(std::function<void(KWin::EffectWindow*)> callback)
1613 {
1614     kwinApp()->platform()->startInteractiveWindowSelection(
1615         [callback] (KWin::Toplevel *t) {
1616             if (t && t->effectWindow()) {
1617                 callback(t->effectWindow());
1618             } else {
1619                 callback(nullptr);
1620             }
1621         }
1622     );
1623 }
1624 
startInteractivePositionSelection(std::function<void (const QPoint &)> callback)1625 void EffectsHandlerImpl::startInteractivePositionSelection(std::function<void(const QPoint&)> callback)
1626 {
1627     kwinApp()->platform()->startInteractivePositionSelection(callback);
1628 }
1629 
showOnScreenMessage(const QString & message,const QString & iconName)1630 void EffectsHandlerImpl::showOnScreenMessage(const QString &message, const QString &iconName)
1631 {
1632     OSD::show(message, iconName);
1633 }
1634 
hideOnScreenMessage(OnScreenMessageHideFlags flags)1635 void EffectsHandlerImpl::hideOnScreenMessage(OnScreenMessageHideFlags flags)
1636 {
1637     OSD::HideFlags osdFlags;
1638     if (flags.testFlag(OnScreenMessageHideFlag::SkipsCloseAnimation)) {
1639         osdFlags |= OSD::HideFlag::SkipCloseAnimation;
1640     }
1641     OSD::hide(osdFlags);
1642 }
1643 
config() const1644 KSharedConfigPtr EffectsHandlerImpl::config() const
1645 {
1646     return kwinApp()->config();
1647 }
1648 
inputConfig() const1649 KSharedConfigPtr EffectsHandlerImpl::inputConfig() const
1650 {
1651     return InputConfig::self()->inputConfig();
1652 }
1653 
findEffect(const QString & name) const1654 Effect *EffectsHandlerImpl::findEffect(const QString &name) const
1655 {
1656     auto it = std::find_if(loaded_effects.constBegin(), loaded_effects.constEnd(),
1657         [name] (const EffectPair &pair) {
1658             return pair.first == name;
1659         }
1660     );
1661     if (it == loaded_effects.constEnd()) {
1662         return nullptr;
1663     }
1664     return (*it).second;
1665 }
1666 
renderEffectQuickView(EffectQuickView * w) const1667 void EffectsHandlerImpl::renderEffectQuickView(EffectQuickView *w) const
1668 {
1669     if (!w->isVisible()) {
1670         return;
1671     }
1672     scene()->paintEffectQuickView(w);
1673 }
1674 
sessionState() const1675 SessionState EffectsHandlerImpl::sessionState() const
1676 {
1677     return Workspace::self()->sessionManager()->state();
1678 }
1679 
screens() const1680 QList<EffectScreen *> EffectsHandlerImpl::screens() const
1681 {
1682     return m_effectScreens;
1683 }
1684 
screenAt(const QPoint & point) const1685 EffectScreen *EffectsHandlerImpl::screenAt(const QPoint &point) const
1686 {
1687     return m_effectScreens.value(screenNumber(point));
1688 }
1689 
findScreen(const QString & name) const1690 EffectScreen *EffectsHandlerImpl::findScreen(const QString &name) const
1691 {
1692     for (EffectScreen *screen : qAsConst(m_effectScreens)) {
1693         if (screen->name() == name) {
1694             return screen;
1695         }
1696     }
1697     return nullptr;
1698 }
1699 
findScreen(int screenId) const1700 EffectScreen *EffectsHandlerImpl::findScreen(int screenId) const
1701 {
1702     return m_effectScreens.value(screenId);
1703 }
1704 
slotOutputEnabled(AbstractOutput * output)1705 void EffectsHandlerImpl::slotOutputEnabled(AbstractOutput *output)
1706 {
1707     EffectScreen *screen = new EffectScreenImpl(output, this);
1708     m_effectScreens.append(screen);
1709     Q_EMIT screenAdded(screen);
1710 }
1711 
slotOutputDisabled(AbstractOutput * output)1712 void EffectsHandlerImpl::slotOutputDisabled(AbstractOutput *output)
1713 {
1714     auto it = std::find_if(m_effectScreens.begin(), m_effectScreens.end(), [&output](EffectScreen *screen) {
1715         return static_cast<EffectScreenImpl *>(screen)->platformOutput() == output;
1716     });
1717     if (it != m_effectScreens.end()) {
1718         EffectScreen *screen = *it;
1719         m_effectScreens.erase(it);
1720         Q_EMIT screenRemoved(screen);
1721         delete screen;
1722     }
1723 }
1724 
renderScreen(EffectScreen * screen)1725 void EffectsHandlerImpl::renderScreen(EffectScreen *screen)
1726 {
1727     auto output = static_cast<EffectScreenImpl *>(screen)->platformOutput();
1728     scene()->paintScreen(output, Compositor::self()->windowsToRender());
1729 }
1730 
1731 //****************************************
1732 // EffectScreenImpl
1733 //****************************************
1734 
EffectScreenImpl(AbstractOutput * output,QObject * parent)1735 EffectScreenImpl::EffectScreenImpl(AbstractOutput *output, QObject *parent)
1736     : EffectScreen(parent)
1737     , m_platformOutput(output)
1738 {
1739     connect(output, &AbstractOutput::aboutToChange, this, &EffectScreen::aboutToChange);
1740     connect(output, &AbstractOutput::changed, this, &EffectScreen::changed);
1741     connect(output, &AbstractOutput::wakeUp, this, &EffectScreen::wakeUp);
1742     connect(output, &AbstractOutput::aboutToTurnOff, this, &EffectScreen::aboutToTurnOff);
1743     connect(output, &AbstractOutput::scaleChanged, this, &EffectScreen::devicePixelRatioChanged);
1744     connect(output, &AbstractOutput::geometryChanged, this, &EffectScreen::geometryChanged);
1745 }
1746 
platformOutput() const1747 AbstractOutput *EffectScreenImpl::platformOutput() const
1748 {
1749     return m_platformOutput;
1750 }
1751 
name() const1752 QString EffectScreenImpl::name() const
1753 {
1754     return m_platformOutput->name();
1755 }
1756 
devicePixelRatio() const1757 qreal EffectScreenImpl::devicePixelRatio() const
1758 {
1759     return m_platformOutput->scale();
1760 }
1761 
geometry() const1762 QRect EffectScreenImpl::geometry() const
1763 {
1764     return m_platformOutput->geometry();
1765 }
1766 
transform() const1767 EffectScreen::Transform EffectScreenImpl::transform() const
1768 {
1769     return EffectScreen::Transform(m_platformOutput->transform());
1770 }
1771 
1772 //****************************************
1773 // EffectWindowImpl
1774 //****************************************
1775 
EffectWindowImpl(Toplevel * toplevel)1776 EffectWindowImpl::EffectWindowImpl(Toplevel *toplevel)
1777     : EffectWindow(toplevel)
1778     , toplevel(toplevel)
1779     , sw(nullptr)
1780 {
1781     // Deleted windows are not managed. So, when windowClosed signal is
1782     // emitted, effects can't distinguish managed windows from unmanaged
1783     // windows(e.g. combo box popups, popup menus, etc). Save value of the
1784     // managed property during construction of EffectWindow. At that time,
1785     // parent can be Client, XdgShellClient, or Unmanaged. So, later on, when
1786     // an instance of Deleted becomes parent of the EffectWindow, effects
1787     // can still figure out whether it is/was a managed window.
1788     managed = toplevel->isClient();
1789 
1790     waylandClient = qobject_cast<KWin::WaylandClient *>(toplevel) != nullptr;
1791     x11Client = qobject_cast<KWin::X11Client *>(toplevel) != nullptr ||
1792         qobject_cast<KWin::Unmanaged *>(toplevel) != nullptr;
1793 }
1794 
~EffectWindowImpl()1795 EffectWindowImpl::~EffectWindowImpl()
1796 {
1797     QVariant cachedTextureVariant = data(LanczosCacheRole);
1798     if (cachedTextureVariant.isValid()) {
1799         GLTexture *cachedTexture = static_cast< GLTexture*>(cachedTextureVariant.value<void*>());
1800         delete cachedTexture;
1801     }
1802 }
1803 
isPaintingEnabled()1804 bool EffectWindowImpl::isPaintingEnabled()
1805 {
1806     return sceneWindow()->isPaintingEnabled();
1807 }
1808 
enablePainting(int reason)1809 void EffectWindowImpl::enablePainting(int reason)
1810 {
1811     sceneWindow()->enablePainting(reason);
1812 }
1813 
disablePainting(int reason)1814 void EffectWindowImpl::disablePainting(int reason)
1815 {
1816     sceneWindow()->disablePainting(reason);
1817 }
1818 
addRepaint(const QRect & r)1819 void EffectWindowImpl::addRepaint(const QRect &r)
1820 {
1821     toplevel->addRepaint(r);
1822 }
1823 
addRepaint(int x,int y,int w,int h)1824 void EffectWindowImpl::addRepaint(int x, int y, int w, int h)
1825 {
1826     toplevel->addRepaint(x, y, w, h);
1827 }
1828 
addRepaintFull()1829 void EffectWindowImpl::addRepaintFull()
1830 {
1831     toplevel->addRepaintFull();
1832 }
1833 
addLayerRepaint(const QRect & r)1834 void EffectWindowImpl::addLayerRepaint(const QRect &r)
1835 {
1836     toplevel->addLayerRepaint(r);
1837 }
1838 
addLayerRepaint(int x,int y,int w,int h)1839 void EffectWindowImpl::addLayerRepaint(int x, int y, int w, int h)
1840 {
1841     toplevel->addLayerRepaint(x, y, w, h);
1842 }
1843 
group() const1844 const EffectWindowGroup* EffectWindowImpl::group() const
1845 {
1846     if (auto c = qobject_cast<X11Client *>(toplevel)) {
1847         return c->group()->effectGroup();
1848     }
1849     return nullptr; // TODO
1850 }
1851 
refWindow()1852 void EffectWindowImpl::refWindow()
1853 {
1854     if (auto d = qobject_cast<Deleted *>(toplevel)) {
1855         return d->refWindow();
1856     }
1857     abort(); // TODO
1858 }
1859 
unrefWindow()1860 void EffectWindowImpl::unrefWindow()
1861 {
1862     if (auto d = qobject_cast<Deleted *>(toplevel)) {
1863         return d->unrefWindow();   // delays deletion in case
1864     }
1865     abort(); // TODO
1866 }
1867 
1868 #define TOPLEVEL_HELPER( rettype, prototype, toplevelPrototype) \
1869     rettype EffectWindowImpl::prototype ( ) const \
1870     { \
1871         return toplevel->toplevelPrototype(); \
1872     }
1873 
1874 TOPLEVEL_HELPER(double, opacity, opacity)
1875 TOPLEVEL_HELPER(bool, hasAlpha, hasAlpha)
1876 TOPLEVEL_HELPER(int, x, x)
1877 TOPLEVEL_HELPER(int, y, y)
1878 TOPLEVEL_HELPER(int, width, width)
1879 TOPLEVEL_HELPER(int, height, height)
1880 TOPLEVEL_HELPER(QPoint, pos, pos)
1881 TOPLEVEL_HELPER(QSize, size, size)
1882 TOPLEVEL_HELPER(int, screen, screen)
1883 TOPLEVEL_HELPER(QRect, geometry, frameGeometry)
1884 TOPLEVEL_HELPER(QRect, frameGeometry, frameGeometry)
1885 TOPLEVEL_HELPER(QRect, bufferGeometry, bufferGeometry)
1886 TOPLEVEL_HELPER(QRect, clientGeometry, clientGeometry)
1887 TOPLEVEL_HELPER(QRect, expandedGeometry, visibleGeometry)
1888 TOPLEVEL_HELPER(QRect, rect, rect)
1889 TOPLEVEL_HELPER(int, desktop, desktop)
1890 TOPLEVEL_HELPER(bool, isDesktop, isDesktop)
1891 TOPLEVEL_HELPER(bool, isDock, isDock)
1892 TOPLEVEL_HELPER(bool, isToolbar, isToolbar)
1893 TOPLEVEL_HELPER(bool, isMenu, isMenu)
1894 TOPLEVEL_HELPER(bool, isNormalWindow, isNormalWindow)
1895 TOPLEVEL_HELPER(bool, isDialog, isDialog)
1896 TOPLEVEL_HELPER(bool, isSplash, isSplash)
1897 TOPLEVEL_HELPER(bool, isUtility, isUtility)
1898 TOPLEVEL_HELPER(bool, isDropdownMenu, isDropdownMenu)
1899 TOPLEVEL_HELPER(bool, isPopupMenu, isPopupMenu)
1900 TOPLEVEL_HELPER(bool, isTooltip, isTooltip)
1901 TOPLEVEL_HELPER(bool, isNotification, isNotification)
1902 TOPLEVEL_HELPER(bool, isCriticalNotification, isCriticalNotification)
1903 TOPLEVEL_HELPER(bool, isOnScreenDisplay, isOnScreenDisplay)
1904 TOPLEVEL_HELPER(bool, isComboBox, isComboBox)
1905 TOPLEVEL_HELPER(bool, isDNDIcon, isDNDIcon)
1906 TOPLEVEL_HELPER(bool, isDeleted, isDeleted)
1907 TOPLEVEL_HELPER(QString, windowRole, windowRole)
1908 TOPLEVEL_HELPER(QStringList, activities, activities)
1909 TOPLEVEL_HELPER(bool, skipsCloseAnimation, skipsCloseAnimation)
1910 TOPLEVEL_HELPER(KWaylandServer::SurfaceInterface *, surface, surface)
1911 TOPLEVEL_HELPER(bool, isPopupWindow, isPopupWindow)
1912 TOPLEVEL_HELPER(bool, isOutline, isOutline)
1913 TOPLEVEL_HELPER(bool, isLockScreen, isLockScreen)
1914 TOPLEVEL_HELPER(pid_t, pid, pid)
1915 TOPLEVEL_HELPER(qlonglong, windowId, window)
1916 
1917 #undef TOPLEVEL_HELPER
1918 
1919 #define CLIENT_HELPER_WITH_DELETED( rettype, prototype, propertyname, defaultValue ) \
1920     rettype EffectWindowImpl::prototype ( ) const \
1921     { \
1922         auto client = qobject_cast<AbstractClient *>(toplevel); \
1923         if (client) { \
1924             return client->propertyname(); \
1925         } \
1926         auto deleted = qobject_cast<Deleted *>(toplevel); \
1927         if (deleted) { \
1928             return deleted->propertyname(); \
1929         } \
1930         return defaultValue; \
1931     }
1932 
1933 CLIENT_HELPER_WITH_DELETED(bool, isMinimized, isMinimized, false)
1934 CLIENT_HELPER_WITH_DELETED(bool, isModal, isModal, false)
1935 CLIENT_HELPER_WITH_DELETED(bool, isFullScreen, isFullScreen, false)
1936 CLIENT_HELPER_WITH_DELETED(bool, keepAbove, keepAbove, false)
1937 CLIENT_HELPER_WITH_DELETED(bool, keepBelow, keepBelow, false)
1938 CLIENT_HELPER_WITH_DELETED(QString, caption, caption, QString());
1939 CLIENT_HELPER_WITH_DELETED(QVector<uint>, desktops, x11DesktopIds, QVector<uint>());
1940 
1941 #undef CLIENT_HELPER_WITH_DELETED
1942 
1943 // legacy from tab groups, can be removed when no effects use this any more.
isCurrentTab() const1944 bool EffectWindowImpl::isCurrentTab() const
1945 {
1946     return true;
1947 }
1948 
windowClass() const1949 QString EffectWindowImpl::windowClass() const
1950 {
1951     return toplevel->resourceName() + QLatin1Char(' ') + toplevel->resourceClass();
1952 }
1953 
contentsRect() const1954 QRect EffectWindowImpl::contentsRect() const
1955 {
1956     return QRect(toplevel->clientPos(), toplevel->clientSize());
1957 }
1958 
windowType() const1959 NET::WindowType EffectWindowImpl::windowType() const
1960 {
1961     return toplevel->windowType();
1962 }
1963 
1964 #define CLIENT_HELPER( rettype, prototype, propertyname, defaultValue ) \
1965     rettype EffectWindowImpl::prototype ( ) const \
1966     { \
1967         auto client = qobject_cast<AbstractClient *>(toplevel); \
1968         if (client) { \
1969             return client->propertyname(); \
1970         } \
1971         return defaultValue; \
1972     }
1973 
CLIENT_HELPER(bool,isMovable,isMovable,false)1974 CLIENT_HELPER(bool, isMovable, isMovable, false)
1975 CLIENT_HELPER(bool, isMovableAcrossScreens, isMovableAcrossScreens, false)
1976 CLIENT_HELPER(bool, isUserMove, isInteractiveMove, false)
1977 CLIENT_HELPER(bool, isUserResize, isInteractiveResize, false)
1978 CLIENT_HELPER(QRect, iconGeometry, iconGeometry, QRect())
1979 CLIENT_HELPER(bool, isSpecialWindow, isSpecialWindow, true)
1980 CLIENT_HELPER(bool, acceptsFocus, wantsInput, true) // We don't actually know...
1981 CLIENT_HELPER(QIcon, icon, icon, QIcon())
1982 CLIENT_HELPER(bool, isSkipSwitcher, skipSwitcher, false)
1983 CLIENT_HELPER(bool, decorationHasAlpha, decorationHasAlpha, false)
1984 CLIENT_HELPER(bool, isUnresponsive, unresponsive, false)
1985 
1986 #undef CLIENT_HELPER
1987 
1988 QSize EffectWindowImpl::basicUnit() const
1989 {
1990     if (auto client = qobject_cast<X11Client *>(toplevel)){
1991         return client->basicUnit();
1992     }
1993     return QSize(1,1);
1994 }
1995 
setWindow(Toplevel * w)1996 void EffectWindowImpl::setWindow(Toplevel* w)
1997 {
1998     toplevel = w;
1999     setParent(w);
2000 }
2001 
setSceneWindow(Scene::Window * w)2002 void EffectWindowImpl::setSceneWindow(Scene::Window* w)
2003 {
2004     sw = w;
2005 }
2006 
decorationInnerRect() const2007 QRect EffectWindowImpl::decorationInnerRect() const
2008 {
2009     return toplevel->rect() - toplevel->frameMargins();
2010 }
2011 
readProperty(long atom,long type,int format) const2012 QByteArray EffectWindowImpl::readProperty(long atom, long type, int format) const
2013 {
2014     if (!kwinApp()->x11Connection()) {
2015         return QByteArray();
2016     }
2017     return readWindowProperty(window()->window(), atom, type, format);
2018 }
2019 
deleteProperty(long int atom) const2020 void EffectWindowImpl::deleteProperty(long int atom) const
2021 {
2022     if (kwinApp()->x11Connection()) {
2023         deleteWindowProperty(window()->window(), atom);
2024     }
2025 }
2026 
findModal()2027 EffectWindow* EffectWindowImpl::findModal()
2028 {
2029     auto client = qobject_cast<AbstractClient *>(toplevel);
2030     if (!client) {
2031         return nullptr;
2032     }
2033 
2034     AbstractClient *modal = client->findModal();
2035     if (modal) {
2036         return modal->effectWindow();
2037     }
2038 
2039     return nullptr;
2040 }
2041 
transientFor()2042 EffectWindow* EffectWindowImpl::transientFor()
2043 {
2044     auto client = qobject_cast<AbstractClient *>(toplevel);
2045     if (!client) {
2046         return nullptr;
2047     }
2048 
2049     AbstractClient *transientFor = client->transientFor();
2050     if (transientFor) {
2051         return transientFor->effectWindow();
2052     }
2053 
2054     return nullptr;
2055 }
2056 
internalWindow() const2057 QWindow *EffectWindowImpl::internalWindow() const
2058 {
2059     auto client = qobject_cast<InternalClient *>(toplevel);
2060     if (!client) {
2061         return nullptr;
2062     }
2063     return client->internalWindow();
2064 }
2065 
2066 template <typename T>
getMainWindows(T * c)2067 EffectWindowList getMainWindows(T *c)
2068 {
2069     const auto mainclients = c->mainClients();
2070     EffectWindowList ret;
2071     ret.reserve(mainclients.size());
2072     std::transform(std::cbegin(mainclients), std::cend(mainclients),
2073         std::back_inserter(ret),
2074         [](auto client) { return client->effectWindow(); });
2075     return ret;
2076 }
2077 
mainWindows() const2078 EffectWindowList EffectWindowImpl::mainWindows() const
2079 {
2080     if (auto client = qobject_cast<AbstractClient *>(toplevel)) {
2081         return getMainWindows(client);
2082     }
2083     if (auto deleted = qobject_cast<Deleted *>(toplevel)) {
2084         return getMainWindows(deleted);
2085     }
2086     return {};
2087 }
2088 
setData(int role,const QVariant & data)2089 void EffectWindowImpl::setData(int role, const QVariant &data)
2090 {
2091     if (!data.isNull())
2092         dataMap[ role ] = data;
2093     else
2094         dataMap.remove(role);
2095     Q_EMIT effects->windowDataChanged(this, role);
2096 }
2097 
data(int role) const2098 QVariant EffectWindowImpl::data(int role) const
2099 {
2100     return dataMap.value(role);
2101 }
2102 
effectWindow(Toplevel * w)2103 EffectWindow* effectWindow(Toplevel* w)
2104 {
2105     EffectWindowImpl* ret = w->effectWindow();
2106     return ret;
2107 }
2108 
effectWindow(Scene::Window * w)2109 EffectWindow* effectWindow(Scene::Window* w)
2110 {
2111     EffectWindowImpl* ret = w->window()->effectWindow();
2112     ret->setSceneWindow(w);
2113     return ret;
2114 }
2115 
elevate(bool elevate)2116 void EffectWindowImpl::elevate(bool elevate)
2117 {
2118     effects->setElevatedWindow(this, elevate);
2119 }
2120 
minimize()2121 void EffectWindowImpl::minimize()
2122 {
2123     if (auto client = qobject_cast<AbstractClient *>(toplevel)) {
2124         client->minimize();
2125     }
2126 }
2127 
unminimize()2128 void EffectWindowImpl::unminimize()
2129 {
2130     if (auto client = qobject_cast<AbstractClient *>(toplevel)) {
2131         client->unminimize();
2132     }
2133 }
2134 
closeWindow()2135 void EffectWindowImpl::closeWindow()
2136 {
2137     if (auto client = qobject_cast<AbstractClient *>(toplevel)) {
2138         client->closeWindow();
2139     }
2140 }
2141 
referencePreviousWindowPixmap()2142 void EffectWindowImpl::referencePreviousWindowPixmap()
2143 {
2144     if (sw) {
2145         sw->referencePreviousPixmap();
2146     }
2147 }
2148 
unreferencePreviousWindowPixmap()2149 void EffectWindowImpl::unreferencePreviousWindowPixmap()
2150 {
2151     if (sw) {
2152         sw->unreferencePreviousPixmap();
2153     }
2154 }
2155 
isManaged() const2156 bool EffectWindowImpl::isManaged() const
2157 {
2158     return managed;
2159 }
2160 
isWaylandClient() const2161 bool EffectWindowImpl::isWaylandClient() const
2162 {
2163     return waylandClient;
2164 }
2165 
isX11Client() const2166 bool EffectWindowImpl::isX11Client() const
2167 {
2168     return x11Client;
2169 }
2170 
2171 
2172 //****************************************
2173 // EffectWindowGroupImpl
2174 //****************************************
2175 
2176 
members() const2177 EffectWindowList EffectWindowGroupImpl::members() const
2178 {
2179     const auto memberList = group->members();
2180     EffectWindowList ret;
2181     ret.reserve(memberList.size());
2182     std::transform(std::cbegin(memberList), std::cend(memberList),
2183         std::back_inserter(ret),
2184         [](auto toplevel) { return toplevel->effectWindow(); });
2185     return ret;
2186 }
2187 
2188 //****************************************
2189 // EffectFrameImpl
2190 //****************************************
2191 
EffectFrameImpl(EffectFrameStyle style,bool staticSize,QPoint position,Qt::Alignment alignment)2192 EffectFrameImpl::EffectFrameImpl(EffectFrameStyle style, bool staticSize, QPoint position, Qt::Alignment alignment)
2193     : QObject(nullptr)
2194     , EffectFrame()
2195     , m_style(style)
2196     , m_static(staticSize)
2197     , m_point(position)
2198     , m_alignment(alignment)
2199     , m_shader(nullptr)
2200     , m_theme(new Plasma::Theme(this))
2201 {
2202     if (m_style == EffectFrameStyled) {
2203         m_frame.setImagePath(QStringLiteral("widgets/background"));
2204         m_frame.setCacheAllRenderedFrames(true);
2205         connect(m_theme, &Plasma::Theme::themeChanged, this, &EffectFrameImpl::plasmaThemeChanged);
2206     }
2207     m_selection.setImagePath(QStringLiteral("widgets/viewitem"));
2208     m_selection.setElementPrefix(QStringLiteral("hover"));
2209     m_selection.setCacheAllRenderedFrames(true);
2210     m_selection.setEnabledBorders(Plasma::FrameSvg::AllBorders);
2211 
2212     m_sceneFrame = Compositor::self()->scene()->createEffectFrame(this);
2213 }
2214 
~EffectFrameImpl()2215 EffectFrameImpl::~EffectFrameImpl()
2216 {
2217     delete m_sceneFrame;
2218 }
2219 
font() const2220 const QFont& EffectFrameImpl::font() const
2221 {
2222     return m_font;
2223 }
2224 
setFont(const QFont & font)2225 void EffectFrameImpl::setFont(const QFont& font)
2226 {
2227     if (m_font == font) {
2228         return;
2229     }
2230     m_font = font;
2231     QRect oldGeom = m_geometry;
2232     if (!m_text.isEmpty()) {
2233         autoResize();
2234     }
2235     if (oldGeom == m_geometry) {
2236         // Wasn't updated in autoResize()
2237         m_sceneFrame->freeTextFrame();
2238     }
2239 }
2240 
free()2241 void EffectFrameImpl::free()
2242 {
2243     m_sceneFrame->free();
2244 }
2245 
geometry() const2246 const QRect& EffectFrameImpl::geometry() const
2247 {
2248     return m_geometry;
2249 }
2250 
setGeometry(const QRect & geometry,bool force)2251 void EffectFrameImpl::setGeometry(const QRect& geometry, bool force)
2252 {
2253     QRect oldGeom = m_geometry;
2254     m_geometry = geometry;
2255     if (m_geometry == oldGeom && !force) {
2256         return;
2257     }
2258     effects->addRepaint(oldGeom);
2259     effects->addRepaint(m_geometry);
2260     if (m_geometry.size() == oldGeom.size() && !force) {
2261         return;
2262     }
2263 
2264     if (m_style == EffectFrameStyled) {
2265         qreal left, top, right, bottom;
2266         m_frame.getMargins(left, top, right, bottom);   // m_geometry is the inner geometry
2267         m_frame.resizeFrame(m_geometry.adjusted(-left, -top, right, bottom).size());
2268     }
2269 
2270     free();
2271 }
2272 
icon() const2273 const QIcon& EffectFrameImpl::icon() const
2274 {
2275     return m_icon;
2276 }
2277 
setIcon(const QIcon & icon)2278 void EffectFrameImpl::setIcon(const QIcon& icon)
2279 {
2280     m_icon = icon;
2281     if (isCrossFade()) {
2282         m_sceneFrame->crossFadeIcon();
2283     }
2284     if (m_iconSize.isEmpty() && !m_icon.availableSizes().isEmpty()) { // Set a size if we don't already have one
2285         setIconSize(m_icon.availableSizes().constFirst());
2286     }
2287     m_sceneFrame->freeIconFrame();
2288 }
2289 
iconSize() const2290 const QSize& EffectFrameImpl::iconSize() const
2291 {
2292     return m_iconSize;
2293 }
2294 
setIconSize(const QSize & size)2295 void EffectFrameImpl::setIconSize(const QSize& size)
2296 {
2297     if (m_iconSize == size) {
2298         return;
2299     }
2300     m_iconSize = size;
2301     autoResize();
2302     m_sceneFrame->freeIconFrame();
2303 }
2304 
plasmaThemeChanged()2305 void EffectFrameImpl::plasmaThemeChanged()
2306 {
2307     free();
2308 }
2309 
render(const QRegion & region,double opacity,double frameOpacity)2310 void EffectFrameImpl::render(const QRegion &region, double opacity, double frameOpacity)
2311 {
2312     if (m_geometry.isEmpty()) {
2313         return; // Nothing to display
2314     }
2315     m_shader = nullptr;
2316     setScreenProjectionMatrix(static_cast<EffectsHandlerImpl*>(effects)->scene()->screenProjectionMatrix());
2317     effects->paintEffectFrame(this, region, opacity, frameOpacity);
2318 }
2319 
finalRender(QRegion region,double opacity,double frameOpacity) const2320 void EffectFrameImpl::finalRender(QRegion region, double opacity, double frameOpacity) const
2321 {
2322     region = infiniteRegion(); // TODO: Old region doesn't seem to work with OpenGL
2323 
2324     m_sceneFrame->render(region, opacity, frameOpacity);
2325 }
2326 
alignment() const2327 Qt::Alignment EffectFrameImpl::alignment() const
2328 {
2329     return m_alignment;
2330 }
2331 
2332 
2333 void
align(QRect & geometry)2334 EffectFrameImpl::align(QRect &geometry)
2335 {
2336     if (m_alignment & Qt::AlignLeft)
2337         geometry.moveLeft(m_point.x());
2338     else if (m_alignment & Qt::AlignRight)
2339         geometry.moveLeft(m_point.x() - geometry.width());
2340     else
2341         geometry.moveLeft(m_point.x() - geometry.width() / 2);
2342     if (m_alignment & Qt::AlignTop)
2343         geometry.moveTop(m_point.y());
2344     else if (m_alignment & Qt::AlignBottom)
2345         geometry.moveTop(m_point.y() - geometry.height());
2346     else
2347         geometry.moveTop(m_point.y() - geometry.height() / 2);
2348 }
2349 
2350 
setAlignment(Qt::Alignment alignment)2351 void EffectFrameImpl::setAlignment(Qt::Alignment alignment)
2352 {
2353     m_alignment = alignment;
2354     align(m_geometry);
2355     setGeometry(m_geometry);
2356 }
2357 
setPosition(const QPoint & point)2358 void EffectFrameImpl::setPosition(const QPoint& point)
2359 {
2360     m_point = point;
2361     QRect geometry = m_geometry; // this is important, setGeometry need call repaint for old & new geometry
2362     align(geometry);
2363     setGeometry(geometry);
2364 }
2365 
text() const2366 const QString& EffectFrameImpl::text() const
2367 {
2368     return m_text;
2369 }
2370 
setText(const QString & text)2371 void EffectFrameImpl::setText(const QString& text)
2372 {
2373     if (m_text == text) {
2374         return;
2375     }
2376     if (isCrossFade()) {
2377         m_sceneFrame->crossFadeText();
2378     }
2379     m_text = text;
2380     QRect oldGeom = m_geometry;
2381     autoResize();
2382     if (oldGeom == m_geometry) {
2383         // Wasn't updated in autoResize()
2384         m_sceneFrame->freeTextFrame();
2385     }
2386 }
2387 
setSelection(const QRect & selection)2388 void EffectFrameImpl::setSelection(const QRect& selection)
2389 {
2390     if (selection == m_selectionGeometry) {
2391         return;
2392     }
2393     m_selectionGeometry = selection;
2394     if (m_selectionGeometry.size() != m_selection.frameSize().toSize()) {
2395         m_selection.resizeFrame(m_selectionGeometry.size());
2396     }
2397     // TODO; optimize to only recreate when resizing
2398     m_sceneFrame->freeSelection();
2399 }
2400 
autoResize()2401 void EffectFrameImpl::autoResize()
2402 {
2403     if (m_static)
2404         return; // Not automatically resizing
2405 
2406     QRect geometry;
2407     // Set size
2408     if (!m_text.isEmpty()) {
2409         QFontMetrics metrics(m_font);
2410         geometry.setSize(metrics.size(0, m_text));
2411     }
2412     if (!m_icon.isNull() && !m_iconSize.isEmpty()) {
2413         geometry.setLeft(-m_iconSize.width());
2414         if (m_iconSize.height() > geometry.height())
2415             geometry.setHeight(m_iconSize.height());
2416     }
2417 
2418     align(geometry);
2419     setGeometry(geometry);
2420 }
2421 
styledTextColor()2422 QColor EffectFrameImpl::styledTextColor()
2423 {
2424     return m_theme->color(Plasma::Theme::TextColor);
2425 }
2426 
2427 } // namespace
2428