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"