KWin
Loading...
Searching...
No Matches
diminactive.cpp
Go to the documentation of this file.
1/*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 2007 Lubos Lunak <l.lunak@kde.org>
6 SPDX-FileCopyrightText: 2007 Christian Nitschkowski <christian.nitschkowski@kdemail.net>
7 SPDX-FileCopyrightText: 2018 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
8
9 SPDX-License-Identifier: GPL-2.0-or-later
10*/
11
12// own
13#include "diminactive.h"
15
16// KConfigSkeleton
17#include "diminactiveconfig.h"
18
19namespace KWin
20{
21
32static inline bool belongToSameGroup(const EffectWindow *w1, const EffectWindow *w2)
33{
34 return w1 && w2 && w1->group() && w1->group() == w2->group();
35}
36
38{
39 DimInactiveConfig::instance(effects->config());
41
43 this, &DimInactiveEffect::windowActivated);
45 this, &DimInactiveEffect::windowAdded);
47 this, &DimInactiveEffect::windowClosed);
49 this, &DimInactiveEffect::windowDeleted);
51 this, &DimInactiveEffect::activeFullScreenEffectChanged);
52
53 const auto windows = effects->stackingOrder();
54 for (EffectWindow *window : windows) {
55 windowAdded(window);
56 }
57}
58
62
63void DimInactiveEffect::reconfigure(ReconfigureFlags flags)
64{
65 DimInactiveConfig::self()->read();
66
67 // TODO: Use normalized strength param.
68 m_dimStrength = DimInactiveConfig::strength() / 100.0;
69 m_dimPanels = DimInactiveConfig::dimPanels();
70 m_dimDesktop = DimInactiveConfig::dimDesktop();
71 m_dimKeepAbove = DimInactiveConfig::dimKeepAbove();
72 m_dimByGroup = DimInactiveConfig::dimByGroup();
73 m_dimFullScreen = DimInactiveConfig::dimFullScreen();
74
75 updateActiveWindow(effects->activeWindow());
76
77 m_activeWindowGroup = (m_dimByGroup && m_activeWindow)
78 ? m_activeWindow->group()
79 : nullptr;
80
81 m_fullScreenTransition.timeLine.setDuration(
82 std::chrono::milliseconds(static_cast<int>(animationTime(250))));
83
85}
86
87void DimInactiveEffect::prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime)
88{
89 if (m_fullScreenTransition.active) {
90 m_fullScreenTransition.timeLine.advance(presentTime);
91 }
92
93 auto transitionIt = m_transitions.begin();
94 while (transitionIt != m_transitions.end()) {
95 (*transitionIt).advance(presentTime);
96 ++transitionIt;
97 }
98
99 effects->prePaintScreen(data, presentTime);
100}
101
102void DimInactiveEffect::paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, QRegion region, WindowPaintData &data)
103{
104 auto transitionIt = m_transitions.constFind(w);
105 if (transitionIt != m_transitions.constEnd()) {
106 const qreal transitionProgress = (*transitionIt).value();
107 dimWindow(data, m_dimStrength * transitionProgress);
108 effects->paintWindow(renderTarget, viewport, w, mask, region, data);
109 return;
110 }
111
112 auto forceIt = m_forceDim.constFind(w);
113 if (forceIt != m_forceDim.constEnd()) {
114 const qreal forcedStrength = *forceIt;
115 dimWindow(data, forcedStrength);
116 effects->paintWindow(renderTarget, viewport, w, mask, region, data);
117 return;
118 }
119
120 if (canDimWindow(w)) {
121 dimWindow(data, m_dimStrength);
122 }
123
124 effects->paintWindow(renderTarget, viewport, w, mask, region, data);
125}
126
128{
129 if (m_fullScreenTransition.active) {
130 if (m_fullScreenTransition.timeLine.done()) {
131 m_fullScreenTransition.active = false;
132 }
134 }
135
136 auto transitionIt = m_transitions.begin();
137 while (transitionIt != m_transitions.end()) {
138 EffectWindow *w = transitionIt.key();
139 if ((*transitionIt).done()) {
140 transitionIt = m_transitions.erase(transitionIt);
141 } else {
142 ++transitionIt;
143 }
144 w->addRepaintFull();
145 }
146
148}
149
150void DimInactiveEffect::dimWindow(WindowPaintData &data, qreal strength)
151{
152 qreal dimFactor;
153 if (m_fullScreenTransition.active) {
154 dimFactor = 1.0 - m_fullScreenTransition.timeLine.value();
155 } else if (effects->activeFullScreenEffect()) {
156 dimFactor = 0.0;
157 } else {
158 dimFactor = 1.0;
159 }
160
161 data.multiplyBrightness(1.0 - strength * dimFactor);
162 data.multiplySaturation(1.0 - strength * dimFactor);
163}
164
165bool DimInactiveEffect::canDimWindow(const EffectWindow *w) const
166{
167 if (m_activeWindow == w) {
168 return false;
169 }
170
171 if (m_dimByGroup && belongToSameGroup(m_activeWindow, w)) {
172 return false;
173 }
174
175 if (w->isDock() && !m_dimPanels) {
176 return false;
177 }
178
179 if (w->isDesktop() && !m_dimDesktop) {
180 return false;
181 }
182
183 if (w->keepAbove() && !m_dimKeepAbove) {
184 return false;
185 }
186
187 if (w->isFullScreen() && !m_dimFullScreen) {
188 return false;
189 }
190
191 if (w->isPopupWindow() || w->isInputMethod()) {
192 return false;
193 }
194
195 if (w->isX11Client() && !w->isManaged()) {
196 return false;
197 }
198
199 return w->isNormalWindow()
200 || w->isDialog()
201 || w->isUtility()
202 || w->isDock()
203 || w->isDesktop();
204}
205
206void DimInactiveEffect::scheduleInTransition(EffectWindow *w)
207{
208 TimeLine &timeLine = m_transitions[w];
210 std::chrono::milliseconds(static_cast<int>(animationTime(160))));
211 if (timeLine.done()) {
212 // If the Out animation is still active, then we're trucating
213 // duration of the timeline(from 250ms to 160ms). If the timeline
214 // is about to be finished with the old duration, then after
215 // changing duration it will be in the "done" state. Thus, we
216 // have to reset the timeline, otherwise it won't update progress.
217 timeLine.reset();
218 }
220 timeLine.setEasingCurve(QEasingCurve::InOutSine);
221}
222
223void DimInactiveEffect::scheduleGroupInTransition(EffectWindow *w)
224{
225 if (!m_dimByGroup) {
226 scheduleInTransition(w);
227 return;
228 }
229
230 if (!w->group()) {
231 scheduleInTransition(w);
232 return;
233 }
234
235 const auto members = w->group()->members();
236 for (EffectWindow *member : members) {
237 scheduleInTransition(member);
238 }
239}
240
241void DimInactiveEffect::scheduleOutTransition(EffectWindow *w)
242{
243 TimeLine &timeLine = m_transitions[w];
245 std::chrono::milliseconds(static_cast<int>(animationTime(250))));
246 if (timeLine.done()) {
247 timeLine.reset();
248 }
250 timeLine.setEasingCurve(QEasingCurve::InOutSine);
251}
252
253void DimInactiveEffect::scheduleGroupOutTransition(EffectWindow *w)
254{
255 if (!m_dimByGroup) {
256 scheduleOutTransition(w);
257 return;
258 }
259
260 if (!w->group()) {
261 scheduleOutTransition(w);
262 return;
263 }
264
265 const auto members = w->group()->members();
266 for (EffectWindow *member : members) {
267 scheduleOutTransition(member);
268 }
269}
270
271void DimInactiveEffect::scheduleRepaint(EffectWindow *w)
272{
273 if (!m_dimByGroup) {
274 w->addRepaintFull();
275 return;
276 }
277
278 if (!w->group()) {
279 w->addRepaintFull();
280 return;
281 }
282
283 const auto members = w->group()->members();
284 for (EffectWindow *member : members) {
285 member->addRepaintFull();
286 }
287}
288
289void DimInactiveEffect::windowActivated(EffectWindow *w)
290{
291 if (!w) {
292 return;
293 }
294
295 if (m_activeWindow == w) {
296 return;
297 }
298
299 if (m_dimByGroup && belongToSameGroup(m_activeWindow, w)) {
300 m_activeWindow = w;
301 return;
302 }
303
304 // WORKAROUND: Deleted windows do not belong to any of window groups.
305 // So, if one of windows in a window group is closed, the In transition
306 // will be false-triggered for the rest of the window group. In addition
307 // to the active window, keep track of active window group so we can
308 // tell whether "focus" moved from a closed window to some other window
309 // in a window group.
310 if (m_dimByGroup && w->group() && w->group() == m_activeWindowGroup) {
311 m_activeWindow = w;
312 return;
313 }
314
315 EffectWindow *previousActiveWindow = m_activeWindow;
316 m_activeWindow = canDimWindow(w) ? w : nullptr;
317
318 m_activeWindowGroup = (m_dimByGroup && m_activeWindow)
319 ? m_activeWindow->group()
320 : nullptr;
321
322 if (previousActiveWindow) {
323 scheduleGroupOutTransition(previousActiveWindow);
324 scheduleRepaint(previousActiveWindow);
325 }
326
327 if (m_activeWindow) {
328 scheduleGroupInTransition(m_activeWindow);
329 scheduleRepaint(m_activeWindow);
330 }
331}
332
333void DimInactiveEffect::windowAdded(EffectWindow *w)
334{
336 this, &DimInactiveEffect::updateActiveWindow);
338 this, &DimInactiveEffect::updateActiveWindow);
339}
340
341void DimInactiveEffect::windowClosed(EffectWindow *w)
342{
343 // When a window is closed, we should force current dim strength that
344 // is applied to it to avoid flickering when some effect animates
345 // the disappearing of the window. If there is no such effect then
346 // it won't be dimmed.
347 qreal forcedStrength = 0.0;
348 bool shouldForceDim = false;
349
350 auto transitionIt = m_transitions.find(w);
351 if (transitionIt != m_transitions.end()) {
352 forcedStrength = m_dimStrength * (*transitionIt).value();
353 shouldForceDim = true;
354 m_transitions.erase(transitionIt);
355 } else if (m_activeWindow == w) {
356 forcedStrength = 0.0;
357 shouldForceDim = true;
358 } else if (m_dimByGroup && belongToSameGroup(m_activeWindow, w)) {
359 forcedStrength = 0.0;
360 shouldForceDim = true;
361 } else if (canDimWindow(w)) {
362 forcedStrength = m_dimStrength;
363 shouldForceDim = true;
364 }
365
366 if (shouldForceDim) {
367 m_forceDim.insert(w, forcedStrength);
368 }
369
370 if (m_activeWindow == w) {
371 m_activeWindow = nullptr;
372 }
373}
374
375void DimInactiveEffect::windowDeleted(EffectWindow *w)
376{
377 m_forceDim.remove(w);
378
379 // FIXME: Sometimes we can miss the window close signal because KWin
380 // can activate a window that is not ready for painting and the window
381 // gets destroyed immediately. So, we have to remove active transitions
382 // for that window here, otherwise we'll crash in postPaintScreen.
383 m_transitions.remove(w);
384 if (m_activeWindow == w) {
385 m_activeWindow = nullptr;
386 }
387}
388
389void DimInactiveEffect::activeFullScreenEffectChanged()
390{
391 if (m_fullScreenTransition.timeLine.done()) {
392 m_fullScreenTransition.timeLine.reset();
393 }
394 m_fullScreenTransition.timeLine.setDirection(
397 : TimeLine::Backward);
398 m_fullScreenTransition.active = true;
399
401}
402
403void DimInactiveEffect::updateActiveWindow(EffectWindow *w)
404{
405 if (effects->activeWindow() == nullptr) {
406 return;
407 }
408
409 if (effects->activeWindow() != w) {
410 return;
411 }
412
413 // Need to reset m_activeWindow because canDimWindow depends on it.
414 m_activeWindow = nullptr;
415
416 m_activeWindow = canDimWindow(w) ? w : nullptr;
417}
418
419} // namespace KWin
420
421#include "moc_diminactive.cpp"
void prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime) override
void reconfigure(ReconfigureFlags flags) override
void postPaintScreen() override
void paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, QRegion region, WindowPaintData &data) override
Representation of a window used by/for Effect classes.
const EffectWindowGroup * group() const
void windowKeepAboveChanged(KWin::EffectWindow *w)
Q_SCRIPTABLE void addRepaintFull()
void windowFullScreenChanged(KWin::EffectWindow *w)
void windowDeleted(KWin::EffectWindow *w)
void windowClosed(KWin::EffectWindow *w)
void windowActivated(KWin::EffectWindow *w)
QList< EffectWindow * > stackingOrder
void paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const QRegion &region, WindowPaintData &data)
KWin::EffectWindow * activeWindow
void prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime)
KSharedConfigPtr config() const
void windowAdded(KWin::EffectWindow *w)
Q_SCRIPTABLE void addRepaintFull()
Effect * activeFullScreenEffect() const
void activeFullScreenEffectChanged()
void setDuration(std::chrono::milliseconds duration)
Definition timeline.cpp:103
void setDirection(Direction direction)
Definition timeline.cpp:122
void setEasingCurve(const QEasingCurve &easingCurve)
Definition timeline.cpp:155
qreal multiplySaturation(qreal factor)
Definition effect.cpp:315
qreal multiplyBrightness(qreal factor)
Definition effect.cpp:321
static double animationTime(const KConfigGroup &cfg, const QString &key, int defaultTime)
Definition effect.cpp:483
@ ReconfigureAll
Definition effect.h:601
EffectsHandler * effects