kscreen effect: Work per screen

Makes it possible to apply the dpms settings per screen instead of
applying it to all of them, which is wrong at many levels.
Will be even more important with other effects like rotation.
This commit is contained in:
Aleix Pol 2021-07-26 17:07:29 +02:00 committed by Aleix Pol Gonzalez
parent ed57ac39e2
commit 475993db70
6 changed files with 111 additions and 67 deletions

View file

@ -42,8 +42,6 @@ namespace KWin
KscreenEffect::KscreenEffect()
: Effect()
, m_lastPresentTime(std::chrono::milliseconds::zero())
, m_state(StateNormal)
, m_atom(effects->announceSupportProperty("_KDE_KWIN_KSCREEN_SUPPORT", this))
{
initConfig<KscreenConfig>();
@ -60,6 +58,9 @@ KscreenEffect::KscreenEffect()
addScreen(screen);
}
connect(effects, &EffectsHandler::screenAdded, this, &KscreenEffect::addScreen);
connect(effects, &EffectsHandler::screenRemoved, this, [this] (KWin::EffectScreen *screen) {
m_waylandStates.remove(screen);
});
}
KscreenEffect::~KscreenEffect()
@ -68,12 +69,15 @@ KscreenEffect::~KscreenEffect()
void KscreenEffect::addScreen(EffectScreen *screen)
{
connect(screen, &EffectScreen::wakeUp, this, [this] {
setState(StateFadingIn);
connect(screen, &EffectScreen::wakeUp, this, [this, screen] {
auto &state = m_waylandStates[screen];
state.m_timeLine.setDuration(std::chrono::milliseconds(animationTime<KscreenConfig>(250)));
setState(state, StateFadingIn);
});
connect(screen, &EffectScreen::aboutToTurnOff, this, [this] (std::chrono::milliseconds dimmingIn) {
m_timeLine.setDuration(dimmingIn);
setState(StateFadingOut);
connect(screen, &EffectScreen::aboutToTurnOff, this, [this, screen] (std::chrono::milliseconds dimmingIn) {
auto &state = m_waylandStates[screen];
state.m_timeLine.setDuration(dimmingIn);
setState(state, StateFadingOut);
});
}
@ -82,28 +86,32 @@ void KscreenEffect::reconfigure(ReconfigureFlags flags)
Q_UNUSED(flags)
KscreenConfig::self()->read();
m_timeLine.setDuration(
m_xcbState.m_timeLine.setDuration(
std::chrono::milliseconds(animationTime<KscreenConfig>(250)));
}
void KscreenEffect::prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime)
{
std::chrono::milliseconds delta = std::chrono::milliseconds::zero();
if (m_lastPresentTime.count()) {
delta = presentTime - m_lastPresentTime;
}
if (m_state == StateFadingIn || m_state == StateFadingOut) {
m_timeLine.update(delta);
if (m_timeLine.done()) {
switchState();
if (isScreenActive(data.screen)) {
std::chrono::milliseconds delta = std::chrono::milliseconds::zero();
auto &state = !effects->waylandDisplay() ? m_xcbState : m_waylandStates[data.screen];
m_currentScreen = data.screen;
if (state.m_lastPresentTime.count()) {
delta = presentTime - state.m_lastPresentTime;
}
}
if (isActive()) {
m_lastPresentTime = presentTime;
} else {
m_lastPresentTime = std::chrono::milliseconds::zero();
if (state.m_state == StateFadingIn || state.m_state == StateFadingOut) {
state.m_timeLine.update(delta);
if (state.m_timeLine.done()) {
switchState(state);
m_waylandStates.remove(data.screen);
}
}
if (isActive()) {
state.m_lastPresentTime = presentTime;
} else {
state.m_lastPresentTime = std::chrono::milliseconds::zero();
}
}
effects->prePaintScreen(data, presentTime);
@ -111,51 +119,63 @@ void KscreenEffect::prePaintScreen(ScreenPrePaintData &data, std::chrono::millis
void KscreenEffect::postPaintScreen()
{
if (m_state == StateFadingIn || m_state == StateFadingOut) {
effects->addRepaintFull();
if (isScreenActive(m_currentScreen)) {
auto &state = !effects->waylandDisplay() ? m_xcbState : m_waylandStates[m_currentScreen];
if (state.m_state == StateFadingIn || state.m_state == StateFadingOut) {
effects->addRepaintFull();
}
}
m_currentScreen = nullptr;
}
void KscreenEffect::prePaintWindow(EffectWindow *w, WindowPrePaintData &data, std::chrono::milliseconds presentTime)
{
if (m_state != StateNormal) {
data.setTranslucent();
auto screen = effects->findScreen(w->screen());
if (isScreenActive(screen)) {
auto &state = !effects->waylandDisplay() ? m_xcbState : m_waylandStates[screen];
if (state.m_state != StateNormal) {
data.setTranslucent();
}
}
effects->prePaintWindow(w, data, presentTime);
}
void KscreenEffect::paintWindow(EffectWindow *w, int mask, QRegion region, WindowPaintData &data)
{
//fade to black and fully opaque
switch (m_state) {
case StateFadingOut:
data.setOpacity(data.opacity() + (1.0 - data.opacity()) * m_timeLine.value());
data.multiplyBrightness(1.0 - m_timeLine.value());
break;
case StateFadedOut:
data.multiplyOpacity(0.0);
data.multiplyBrightness(0.0);
break;
case StateFadingIn:
data.setOpacity(data.opacity() + (1.0 - data.opacity()) * (1.0 - m_timeLine.value()));
data.multiplyBrightness(m_timeLine.value());
break;
default:
// no adjustment
break;
auto screen = effects->findScreen(w->screen());
if (isScreenActive(screen)) {
auto &state = !effects->waylandDisplay() ? m_xcbState : m_waylandStates[screen];
//fade to black and fully opaque
switch (state.m_state) {
case StateFadingOut:
data.setOpacity(data.opacity() + (1.0 - data.opacity()) * state.m_timeLine.value());
data.multiplyBrightness(1.0 - state.m_timeLine.value());
break;
case StateFadedOut:
data.multiplyOpacity(0.0);
data.multiplyBrightness(0.0);
break;
case StateFadingIn:
data.setOpacity(data.opacity() + (1.0 - data.opacity()) * (1.0 - state.m_timeLine.value()));
data.multiplyBrightness(state.m_timeLine.value());
break;
default:
// no adjustment
break;
}
}
effects->paintWindow(w, mask, region, data);
}
void KscreenEffect::setState(FadeOutState state)
void KscreenEffect::setState(ScreenState &state, FadeOutState newState)
{
if (m_state == state) {
if (state.m_state == newState) {
return;
}
m_state = state;
m_timeLine.reset();
m_lastPresentTime = std::chrono::milliseconds::zero();
state.m_state = newState;
state.m_timeLine.reset();
state.m_lastPresentTime = std::chrono::milliseconds::zero();
effects->addRepaintFull();
}
@ -164,27 +184,28 @@ void KscreenEffect::propertyNotify(EffectWindow *window, long int atom)
if (window || atom != m_atom || m_atom == XCB_ATOM_NONE) {
return;
}
const QByteArray byteData = effects->readRootProperty(m_atom, XCB_ATOM_CARDINAL, 32);
const uint32_t *data = byteData.isEmpty() ? nullptr : reinterpret_cast<const uint32_t *>(byteData.data());
if (!data || data[0] >= LastState) { // Property was deleted
if (data) {
qCDebug(KWINEFFECTS) << "Incorrect Property state, immediate stop: " << data[0];
}
setState(StateNormal);
setState(m_xcbState, StateNormal);
return;
}
setState(FadeOutState(data[0]));
setState(m_xcbState, FadeOutState(data[0]));
}
void KscreenEffect::switchState()
void KscreenEffect::switchState(ScreenState &state)
{
long value = -1l;
if (m_state == StateFadingOut) {
m_state = StateFadedOut;
if (state.m_state == StateFadingOut) {
state.m_state = StateFadedOut;
value = 2l;
} else if (m_state == StateFadingIn) {
m_state = StateNormal;
} else if (state.m_state == StateFadingIn) {
state.m_state = StateNormal;
value = 0l;
}
if (value != -1l && m_atom != XCB_ATOM_NONE) {
@ -194,7 +215,12 @@ void KscreenEffect::switchState()
bool KscreenEffect::isActive() const
{
return m_state != StateNormal;
return !m_waylandStates.isEmpty() || (!effects->waylandDisplay() && m_atom && m_xcbState.m_state != StateNormal);
}
bool KscreenEffect::isScreenActive(EffectScreen *screen) const
{
return m_waylandStates.contains(screen) || (!effects->waylandDisplay() && m_atom && m_xcbState.m_state != StateNormal);
}
} // namespace KWin

View file

@ -38,7 +38,6 @@ private Q_SLOTS:
void propertyNotify(KWin::EffectWindow *window, long atom);
private:
void switchState();
enum FadeOutState {
StateNormal,
StateFadingOut,
@ -46,12 +45,20 @@ private:
StateFadingIn,
LastState
};
void setState(FadeOutState state);
void addScreen(EffectScreen *screen);
struct ScreenState {
TimeLine m_timeLine;
std::chrono::milliseconds m_lastPresentTime = std::chrono::milliseconds::zero();
FadeOutState m_state = StateNormal;
};
TimeLine m_timeLine;
std::chrono::milliseconds m_lastPresentTime;
FadeOutState m_state;
void switchState(ScreenState &state);
void setState(ScreenState &state, FadeOutState newState);
void addScreen(EffectScreen *screen);
bool isScreenActive(EffectScreen *screen) const;
QHash<EffectScreen *, ScreenState> m_waylandStates;
ScreenState m_xcbState;
EffectScreen *m_currentScreen = nullptr;
xcb_atom_t m_atom;
};

View file

@ -3066,6 +3066,7 @@ class KWINEFFECTS_EXPORT ScreenPrePaintData
public:
int mask;
QRegion paint;
EffectScreen *screen = nullptr;
};
/**

View file

@ -232,7 +232,11 @@ void DrmOutput::setDpmsMode(DpmsMode mode)
} else {
m_turnOffTimer.stop();
setDrmDpmsMode(mode);
Q_EMIT wakeUp();
if (mode != dpmsMode()) {
setDpmsModeInternal(mode);
Q_EMIT wakeUp();
}
}
}

View file

@ -68,7 +68,8 @@ void WaylandOutput::init(const QPoint &logicalPosition, const QSize &pixelSize)
mode.size = pixelSize;
mode.flags = ModeFlag::Current;
mode.refreshRate = refreshRate;
initialize("model_TODO", "manufacturer_TODO", "eisa_TODO", "serial_TODO", pixelSize, { mode }, {});
static uint i = 0;
initialize(QStringLiteral("model_%1").arg(i++), "manufacturer_TODO", "eisa_TODO", "serial_TODO", pixelSize, { mode }, {});
setGeometry(logicalPosition, pixelSize);
setScale(backend()->initialOutputScale());
}
@ -97,8 +98,11 @@ void WaylandOutput::setDpmsMode(KWin::AbstractWaylandOutput::DpmsMode mode)
} else {
m_turnOffTimer.stop();
m_backend->clearDpmsFilter();
setDpmsModeInternal(mode);
Q_EMIT wakeUp();
if (mode != dpmsMode()) {
setDpmsModeInternal(mode);
Q_EMIT wakeUp();
}
}
}

View file

@ -164,9 +164,11 @@ void Scene::paintScreen(const QRegion &damage, const QRegion &repaint,
QRegion region = damage;
auto screen = effects->findScreen(painted_screen);
ScreenPrePaintData pdata;
pdata.mask = (damage == displayRegion) ? 0 : PAINT_SCREEN_REGION;
pdata.paint = region;
pdata.screen = screen;
effects->prePaintScreen(pdata, m_expectedPresentTimestamp);
region = pdata.paint;
@ -188,7 +190,7 @@ void Scene::paintScreen(const QRegion &damage, const QRegion &repaint,
painted_region = region;
repaint_region = repaint;
ScreenPaintData data(projection, effects->findScreen(painted_screen));
ScreenPaintData data(projection, screen);
effects->paintScreen(mask, region, data);
Q_EMIT frameRendered();