File indexing completed on 2024-11-10 04:57:02

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 // own
0010 #include "kscreen.h"
0011 #include "core/output.h"
0012 #include "effect/effecthandler.h"
0013 // KConfigSkeleton
0014 #include "kscreenconfig.h"
0015 #include <QDebug>
0016 
0017 /**
0018  * How this effect works:
0019  *
0020  * Effect announces that it is around through property _KDE_KWIN_KSCREEN_SUPPORT on the root window.
0021  *
0022  * KScreen watches for this property and when it wants to adjust screens, KScreen goes
0023  * through the following protocol:
0024  * 1. KScreen sets the property value to 1
0025  * 2. Effect starts to fade out all windows
0026  * 3. When faded out the effect sets property value to 2
0027  * 4. KScreen adjusts the screens
0028  * 5. KScreen sets property value to 3
0029  * 6. Effect starts to fade in all windows again
0030  * 7. Effect sets back property value to 0
0031  *
0032  * The property has type 32 bits cardinal. To test it use:
0033  * xprop -root -f _KDE_KWIN_KSCREEN_SUPPORT 32c -set _KDE_KWIN_KSCREEN_SUPPORT 1
0034  *
0035  * The states are:
0036  * 0: normal
0037  * 1: fading out
0038  * 2: faded out
0039  * 3: fading in
0040  */
0041 
0042 Q_LOGGING_CATEGORY(KWIN_KSCREEN, "kwin_effect_kscreen", QtWarningMsg)
0043 
0044 namespace KWin
0045 {
0046 
0047 KscreenEffect::KscreenEffect()
0048     : Effect()
0049     , m_atom(effects->waylandDisplay() ? XCB_ATOM_NONE : effects->announceSupportProperty("_KDE_KWIN_KSCREEN_SUPPORT", this))
0050 {
0051     KscreenConfig::instance(effects->config());
0052     if (!effects->waylandDisplay()) {
0053         connect(effects, &EffectsHandler::propertyNotify, this, &KscreenEffect::propertyNotify);
0054         connect(effects, &EffectsHandler::xcbConnectionChanged, this, [this]() {
0055             m_atom = effects->announceSupportProperty(QByteArrayLiteral("_KDE_KWIN_KSCREEN_SUPPORT"), this);
0056         });
0057     }
0058     reconfigure(ReconfigureAll);
0059 
0060     const QList<Output *> screens = effects->screens();
0061     for (auto screen : screens) {
0062         addScreen(screen);
0063     }
0064     connect(effects, &EffectsHandler::screenAdded, this, &KscreenEffect::addScreen);
0065     connect(effects, &EffectsHandler::screenRemoved, this, [this](KWin::Output *screen) {
0066         m_waylandStates.remove(screen);
0067     });
0068 }
0069 
0070 KscreenEffect::~KscreenEffect()
0071 {
0072 }
0073 
0074 void KscreenEffect::addScreen(Output *screen)
0075 {
0076     connect(screen, &Output::wakeUp, this, [this, screen] {
0077         auto &state = m_waylandStates[screen];
0078         state.m_timeLine.setDuration(std::chrono::milliseconds(animationTime<KscreenConfig>(250)));
0079         setState(state, StateFadingIn);
0080     });
0081     connect(screen, &Output::aboutToTurnOff, this, [this, screen](std::chrono::milliseconds dimmingIn) {
0082         auto &state = m_waylandStates[screen];
0083         state.m_timeLine.setDuration(dimmingIn);
0084         setState(state, StateFadingOut);
0085     });
0086 }
0087 
0088 void KscreenEffect::reconfigure(ReconfigureFlags flags)
0089 {
0090     KscreenConfig::self()->read();
0091     m_xcbState.m_timeLine.setDuration(
0092         std::chrono::milliseconds(animationTime<KscreenConfig>(250)));
0093 }
0094 
0095 void KscreenEffect::prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime)
0096 {
0097     if (isScreenActive(data.screen)) {
0098         auto &state = !effects->waylandDisplay() ? m_xcbState : m_waylandStates[data.screen];
0099         m_currentScreen = data.screen;
0100 
0101         if (state.m_state == StateFadingIn || state.m_state == StateFadingOut) {
0102             state.m_timeLine.advance(presentTime);
0103             if (state.m_timeLine.done()) {
0104                 switchState(state);
0105                 if (state.m_state == StateNormal) {
0106                     m_waylandStates.remove(data.screen);
0107                 }
0108             }
0109         }
0110     }
0111 
0112     effects->prePaintScreen(data, presentTime);
0113 }
0114 
0115 void KscreenEffect::postPaintScreen()
0116 {
0117     if (isScreenActive(m_currentScreen)) {
0118         auto &state = !effects->waylandDisplay() ? m_xcbState : m_waylandStates[m_currentScreen];
0119         if (state.m_state == StateFadingIn || state.m_state == StateFadingOut) {
0120             effects->addRepaintFull();
0121         }
0122     }
0123     m_currentScreen = nullptr;
0124 }
0125 
0126 void KscreenEffect::prePaintWindow(EffectWindow *w, WindowPrePaintData &data, std::chrono::milliseconds presentTime)
0127 {
0128     auto screen = w->screen();
0129     if (isScreenActive(screen)) {
0130         auto &state = !effects->waylandDisplay() ? m_xcbState : m_waylandStates[screen];
0131         if (state.m_state != StateNormal) {
0132             data.setTranslucent();
0133         }
0134     }
0135     effects->prePaintWindow(w, data, presentTime);
0136 }
0137 
0138 void KscreenEffect::paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, QRegion region, WindowPaintData &data)
0139 {
0140     auto screen = w->screen();
0141     if (isScreenActive(screen)) {
0142         auto &state = !effects->waylandDisplay() ? m_xcbState : m_waylandStates[screen];
0143         // fade to black and fully opaque
0144         switch (state.m_state) {
0145         case StateFadingOut:
0146             data.setOpacity(data.opacity() + (1.0 - data.opacity()) * state.m_timeLine.value());
0147             data.multiplyBrightness(1.0 - state.m_timeLine.value());
0148             break;
0149         case StateFadedOut:
0150             data.multiplyOpacity(0.0);
0151             data.multiplyBrightness(0.0);
0152             break;
0153         case StateFadingIn:
0154             data.setOpacity(data.opacity() + (1.0 - data.opacity()) * (1.0 - state.m_timeLine.value()));
0155             data.multiplyBrightness(state.m_timeLine.value());
0156             break;
0157         default:
0158             // no adjustment
0159             break;
0160         }
0161     }
0162     effects->paintWindow(renderTarget, viewport, w, mask, region, data);
0163 }
0164 
0165 void KscreenEffect::setState(ScreenState &state, FadeOutState newState)
0166 {
0167     if (state.m_state == newState) {
0168         return;
0169     }
0170 
0171     state.m_state = newState;
0172     state.m_timeLine.reset();
0173     effects->addRepaintFull();
0174 }
0175 
0176 void KscreenEffect::propertyNotify(EffectWindow *window, long int atom)
0177 {
0178     if (window || atom != m_atom || m_atom == XCB_ATOM_NONE) {
0179         return;
0180     }
0181 
0182     const QByteArray byteData = effects->readRootProperty(m_atom, XCB_ATOM_CARDINAL, 32);
0183     const uint32_t *data = byteData.isEmpty() ? nullptr : reinterpret_cast<const uint32_t *>(byteData.data());
0184     if (!data || data[0] >= LastState) { // Property was deleted
0185         if (data) {
0186             qCDebug(KWIN_KSCREEN) << "Incorrect Property state, immediate stop: " << data[0];
0187         }
0188         setState(m_xcbState, StateNormal);
0189         return;
0190     }
0191 
0192     setState(m_xcbState, FadeOutState(data[0]));
0193 }
0194 
0195 void KscreenEffect::switchState(ScreenState &state)
0196 {
0197     long value = -1l;
0198     if (state.m_state == StateFadingOut) {
0199         state.m_state = StateFadedOut;
0200         value = 2l;
0201     } else if (state.m_state == StateFadingIn) {
0202         state.m_state = StateNormal;
0203         value = 0l;
0204     }
0205     if (value != -1l && m_atom != XCB_ATOM_NONE) {
0206         xcb_change_property(effects->xcbConnection(), XCB_PROP_MODE_REPLACE, effects->x11RootWindow(), m_atom, XCB_ATOM_CARDINAL, 32, 1, &value);
0207     }
0208 }
0209 
0210 bool KscreenEffect::isActive() const
0211 {
0212     return !m_waylandStates.isEmpty() || (!effects->waylandDisplay() && m_atom && m_xcbState.m_state != StateNormal);
0213 }
0214 
0215 bool KscreenEffect::isScreenActive(Output *screen) const
0216 {
0217     return m_waylandStates.contains(screen) || (!effects->waylandDisplay() && m_atom && m_xcbState.m_state != StateNormal);
0218 }
0219 
0220 } // namespace KWin
0221 
0222 #include "moc_kscreen.cpp"