2020-08-02 22:22:19 +00:00
|
|
|
/*
|
|
|
|
KWin - the KDE window manager
|
|
|
|
This file is part of the KDE project.
|
2008-02-21 13:20:22 +00:00
|
|
|
|
2020-08-02 22:22:19 +00:00
|
|
|
SPDX-FileCopyrightText: 2008 Martin Gräßlin <mgraesslin@kde.org>
|
2008-02-21 13:20:22 +00:00
|
|
|
|
2020-08-02 22:22:19 +00:00
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
*/
|
2008-02-21 13:20:22 +00:00
|
|
|
#include "coverswitch.h"
|
2012-09-19 15:59:11 +00:00
|
|
|
// KConfigSkeleton
|
|
|
|
#include "coverswitchconfig.h"
|
2008-02-21 13:20:22 +00:00
|
|
|
|
|
|
|
#include <kwinconfig.h>
|
2015-11-30 14:48:14 +00:00
|
|
|
#include <QFile>
|
2013-09-05 08:29:33 +00:00
|
|
|
#include <QIcon>
|
2010-12-18 20:11:45 +00:00
|
|
|
#include <QMatrix4x4>
|
2011-03-13 15:56:25 +00:00
|
|
|
#include <QMouseEvent>
|
2017-11-08 10:23:00 +00:00
|
|
|
#include <QFontMetrics>
|
2014-03-17 15:24:10 +00:00
|
|
|
#include <KLocalizedString>
|
2008-02-21 13:20:22 +00:00
|
|
|
#include <kcolorscheme.h>
|
|
|
|
|
2013-05-21 08:33:02 +00:00
|
|
|
#include <kwinglplatform.h>
|
2008-02-21 13:20:22 +00:00
|
|
|
|
2015-03-06 03:41:04 +00:00
|
|
|
#include <cmath>
|
2008-02-21 13:20:22 +00:00
|
|
|
|
|
|
|
namespace KWin
|
|
|
|
{
|
|
|
|
|
|
|
|
CoverSwitchEffect::CoverSwitchEffect()
|
2011-01-30 14:34:42 +00:00
|
|
|
: mActivated(0)
|
|
|
|
, angle(60.0)
|
|
|
|
, animation(false)
|
|
|
|
, start(false)
|
|
|
|
, stop(false)
|
|
|
|
, stopRequested(false)
|
|
|
|
, startRequested(false)
|
Provide expected presentation time to effects
Effects are given the interval between two consecutive frames. The main
flaw of this approach is that if the Compositor transitions from the idle
state to "active" state, i.e. when there is something to repaint,
effects may see a very large interval between the last painted frame and
the current. In order to address this issue, the Scene invalidates the
timer that is used to measure time between consecutive frames before the
Compositor is about to become idle.
While this works perfectly fine with Xinerama-style rendering, with per
screen rendering, determining whether the compositor is about to idle is
rather a tedious task mostly because a single output can't be used for
the test.
Furthermore, since the Compositor schedules pointless repaints just to
ensure that it's idle, it might take several attempts to figure out
whether the scene timer must be invalidated if you use (true) per screen
rendering.
Ideally, all effects should use a timeline helper that is aware of the
underlying render loop and its timings. However, this option is off the
table because it will involve a lot of work to implement it.
Alternative and much simpler option is to pass the expected presentation
time to effects rather than time between consecutive frames. This means
that effects are responsible for determining how much animation timelines
have to be advanced. Typically, an effect would have to store the
presentation timestamp provided in either prePaint{Screen,Window} and
use it in the subsequent prePaint{Screen,Window} call to estimate the
amount of time passed between the next and the last frames.
Unfortunately, this is an API incompatible change. However, it shouldn't
take a lot of work to port third-party binary effects, which don't use the
AnimationEffect class, to the new API. On the bright side, we no longer
need to be concerned about the Compositor getting idle.
We do still try to determine whether the Compositor is about to idle,
primarily, because the OpenGL render backend swaps buffers on present,
but that will change with the ongoing compositing timing rework.
2020-11-20 15:44:04 +00:00
|
|
|
, lastPresentTime(std::chrono::milliseconds::zero())
|
2011-01-30 14:34:42 +00:00
|
|
|
, zPosition(900.0)
|
|
|
|
, scaleFactor(0.0)
|
|
|
|
, direction(Left)
|
Use nullptr everywhere
Summary:
Because KWin is a very old project, we use three kinds of null pointer
literals: 0, NULL, and nullptr. Since C++11, it's recommended to use
nullptr keyword.
This change converts all usages of 0 and NULL literal to nullptr. Even
though it breaks git history, we need to do it in order to have consistent
code as well to ease code reviews (it's very tempting for some people to
add unrelated changes to their patches, e.g. converting NULL to nullptr).
Test Plan: Compiles.
Reviewers: #kwin, davidedmundson, romangg
Reviewed By: #kwin, davidedmundson, romangg
Subscribers: romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D23618
2019-09-19 14:46:54 +00:00
|
|
|
, selected_window(nullptr)
|
|
|
|
, captionFrame(nullptr)
|
2011-01-30 14:34:42 +00:00
|
|
|
, primaryTabBox(false)
|
|
|
|
, secondaryTabBox(false)
|
|
|
|
{
|
2016-12-02 19:27:43 +00:00
|
|
|
initConfig<CoverSwitchConfig>();
|
2011-01-30 14:34:42 +00:00
|
|
|
reconfigure(ReconfigureAll);
|
2009-02-21 04:53:13 +00:00
|
|
|
|
|
|
|
// Caption frame
|
2011-01-30 14:34:42 +00:00
|
|
|
captionFont.setBold(true);
|
|
|
|
captionFont.setPointSize(captionFont.pointSize() * 2);
|
2010-12-19 13:41:26 +00:00
|
|
|
|
2013-06-03 08:38:04 +00:00
|
|
|
if (effects->compositingType() == OpenGL2Compositing) {
|
2016-01-26 14:38:42 +00:00
|
|
|
m_reflectionShader = ShaderManager::instance()->generateShaderFromResources(ShaderTrait::MapTexture, QString(), QStringLiteral("coverswitch-reflection.glsl"));
|
2013-06-03 08:38:04 +00:00
|
|
|
} else {
|
Use nullptr everywhere
Summary:
Because KWin is a very old project, we use three kinds of null pointer
literals: 0, NULL, and nullptr. Since C++11, it's recommended to use
nullptr keyword.
This change converts all usages of 0 and NULL literal to nullptr. Even
though it breaks git history, we need to do it in order to have consistent
code as well to ease code reviews (it's very tempting for some people to
add unrelated changes to their patches, e.g. converting NULL to nullptr).
Test Plan: Compiles.
Reviewers: #kwin, davidedmundson, romangg
Reviewed By: #kwin, davidedmundson, romangg
Subscribers: romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D23618
2019-09-19 14:46:54 +00:00
|
|
|
m_reflectionShader = nullptr;
|
2013-06-03 08:38:04 +00:00
|
|
|
}
|
2019-01-01 20:48:53 +00:00
|
|
|
connect(effects, &EffectsHandler::windowClosed, this, &CoverSwitchEffect::slotWindowClosed);
|
|
|
|
connect(effects, &EffectsHandler::tabBoxAdded, this, &CoverSwitchEffect::slotTabBoxAdded);
|
|
|
|
connect(effects, &EffectsHandler::tabBoxClosed, this, &CoverSwitchEffect::slotTabBoxClosed);
|
|
|
|
connect(effects, &EffectsHandler::tabBoxUpdated, this, &CoverSwitchEffect::slotTabBoxUpdated);
|
|
|
|
connect(effects, &EffectsHandler::tabBoxKeyEvent, this, &CoverSwitchEffect::slotTabBoxKeyEvent);
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2008-10-02 09:27:32 +00:00
|
|
|
|
|
|
|
CoverSwitchEffect::~CoverSwitchEffect()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2010-07-18 16:32:37 +00:00
|
|
|
delete captionFrame;
|
2010-12-19 13:41:26 +00:00
|
|
|
delete m_reflectionShader;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2008-10-02 09:27:32 +00:00
|
|
|
|
2008-12-04 08:47:07 +00:00
|
|
|
bool CoverSwitchEffect::supported()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2016-08-10 07:24:53 +00:00
|
|
|
return effects->isOpenGLCompositing() && effects->animationsSupported();
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2008-12-04 08:47:07 +00:00
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
void CoverSwitchEffect::reconfigure(ReconfigureFlags)
|
|
|
|
{
|
2014-03-25 15:29:03 +00:00
|
|
|
CoverSwitchConfig::self()->read();
|
2018-09-05 19:03:46 +00:00
|
|
|
animationDuration = std::chrono::milliseconds(
|
|
|
|
animationTime<CoverSwitchConfig>(200));
|
2012-09-19 15:59:11 +00:00
|
|
|
animateSwitch = CoverSwitchConfig::animateSwitch();
|
|
|
|
animateStart = CoverSwitchConfig::animateStart();
|
|
|
|
animateStop = CoverSwitchConfig::animateStop();
|
|
|
|
reflection = CoverSwitchConfig::reflection();
|
|
|
|
windowTitle = CoverSwitchConfig::windowTitle();
|
|
|
|
zPosition = CoverSwitchConfig::zPosition();
|
2018-09-05 19:03:46 +00:00
|
|
|
timeLine.setEasingCurve(QEasingCurve::InOutSine);
|
2011-01-30 14:34:42 +00:00
|
|
|
timeLine.setDuration(animationDuration);
|
2012-09-19 15:59:11 +00:00
|
|
|
|
|
|
|
// Defined outside the ui
|
|
|
|
primaryTabBox = CoverSwitchConfig::tabBox();
|
|
|
|
secondaryTabBox = CoverSwitchConfig::tabBoxAlternative();
|
|
|
|
|
|
|
|
QColor tmp = CoverSwitchConfig::mirrorFrontColor();
|
2009-11-12 09:01:09 +00:00
|
|
|
mirrorColor[0][0] = tmp.redF();
|
|
|
|
mirrorColor[0][1] = tmp.greenF();
|
|
|
|
mirrorColor[0][2] = tmp.blueF();
|
|
|
|
mirrorColor[0][3] = 1.0;
|
2012-09-19 15:59:11 +00:00
|
|
|
tmp = CoverSwitchConfig::mirrorRearColor();
|
2009-11-12 09:01:09 +00:00
|
|
|
mirrorColor[1][0] = tmp.redF();
|
|
|
|
mirrorColor[1][1] = tmp.greenF();
|
|
|
|
mirrorColor[1][2] = tmp.blueF();
|
|
|
|
mirrorColor[1][3] = -1.0;
|
2008-02-21 13:20:22 +00:00
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
|
|
|
|
Provide expected presentation time to effects
Effects are given the interval between two consecutive frames. The main
flaw of this approach is that if the Compositor transitions from the idle
state to "active" state, i.e. when there is something to repaint,
effects may see a very large interval between the last painted frame and
the current. In order to address this issue, the Scene invalidates the
timer that is used to measure time between consecutive frames before the
Compositor is about to become idle.
While this works perfectly fine with Xinerama-style rendering, with per
screen rendering, determining whether the compositor is about to idle is
rather a tedious task mostly because a single output can't be used for
the test.
Furthermore, since the Compositor schedules pointless repaints just to
ensure that it's idle, it might take several attempts to figure out
whether the scene timer must be invalidated if you use (true) per screen
rendering.
Ideally, all effects should use a timeline helper that is aware of the
underlying render loop and its timings. However, this option is off the
table because it will involve a lot of work to implement it.
Alternative and much simpler option is to pass the expected presentation
time to effects rather than time between consecutive frames. This means
that effects are responsible for determining how much animation timelines
have to be advanced. Typically, an effect would have to store the
presentation timestamp provided in either prePaint{Screen,Window} and
use it in the subsequent prePaint{Screen,Window} call to estimate the
amount of time passed between the next and the last frames.
Unfortunately, this is an API incompatible change. However, it shouldn't
take a lot of work to port third-party binary effects, which don't use the
AnimationEffect class, to the new API. On the bright side, we no longer
need to be concerned about the Compositor getting idle.
We do still try to determine whether the Compositor is about to idle,
primarily, because the OpenGL render backend swaps buffers on present,
but that will change with the ongoing compositing timing rework.
2020-11-20 15:44:04 +00:00
|
|
|
void CoverSwitchEffect::prePaintScreen(ScreenPrePaintData& data, std::chrono::milliseconds presentTime)
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
Provide expected presentation time to effects
Effects are given the interval between two consecutive frames. The main
flaw of this approach is that if the Compositor transitions from the idle
state to "active" state, i.e. when there is something to repaint,
effects may see a very large interval between the last painted frame and
the current. In order to address this issue, the Scene invalidates the
timer that is used to measure time between consecutive frames before the
Compositor is about to become idle.
While this works perfectly fine with Xinerama-style rendering, with per
screen rendering, determining whether the compositor is about to idle is
rather a tedious task mostly because a single output can't be used for
the test.
Furthermore, since the Compositor schedules pointless repaints just to
ensure that it's idle, it might take several attempts to figure out
whether the scene timer must be invalidated if you use (true) per screen
rendering.
Ideally, all effects should use a timeline helper that is aware of the
underlying render loop and its timings. However, this option is off the
table because it will involve a lot of work to implement it.
Alternative and much simpler option is to pass the expected presentation
time to effects rather than time between consecutive frames. This means
that effects are responsible for determining how much animation timelines
have to be advanced. Typically, an effect would have to store the
presentation timestamp provided in either prePaint{Screen,Window} and
use it in the subsequent prePaint{Screen,Window} call to estimate the
amount of time passed between the next and the last frames.
Unfortunately, this is an API incompatible change. However, it shouldn't
take a lot of work to port third-party binary effects, which don't use the
AnimationEffect class, to the new API. On the bright side, we no longer
need to be concerned about the Compositor getting idle.
We do still try to determine whether the Compositor is about to idle,
primarily, because the OpenGL render backend swaps buffers on present,
but that will change with the ongoing compositing timing rework.
2020-11-20 15:44:04 +00:00
|
|
|
std::chrono::milliseconds delta = std::chrono::milliseconds::zero();
|
|
|
|
if (lastPresentTime.count()) {
|
|
|
|
delta = presentTime - lastPresentTime;
|
|
|
|
}
|
|
|
|
lastPresentTime = presentTime;
|
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
if (mActivated || stop || stopRequested) {
|
2008-02-21 13:20:22 +00:00
|
|
|
data.mask |= Effect::PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS;
|
2011-01-30 14:34:42 +00:00
|
|
|
if (animation || start || stop) {
|
Provide expected presentation time to effects
Effects are given the interval between two consecutive frames. The main
flaw of this approach is that if the Compositor transitions from the idle
state to "active" state, i.e. when there is something to repaint,
effects may see a very large interval between the last painted frame and
the current. In order to address this issue, the Scene invalidates the
timer that is used to measure time between consecutive frames before the
Compositor is about to become idle.
While this works perfectly fine with Xinerama-style rendering, with per
screen rendering, determining whether the compositor is about to idle is
rather a tedious task mostly because a single output can't be used for
the test.
Furthermore, since the Compositor schedules pointless repaints just to
ensure that it's idle, it might take several attempts to figure out
whether the scene timer must be invalidated if you use (true) per screen
rendering.
Ideally, all effects should use a timeline helper that is aware of the
underlying render loop and its timings. However, this option is off the
table because it will involve a lot of work to implement it.
Alternative and much simpler option is to pass the expected presentation
time to effects rather than time between consecutive frames. This means
that effects are responsible for determining how much animation timelines
have to be advanced. Typically, an effect would have to store the
presentation timestamp provided in either prePaint{Screen,Window} and
use it in the subsequent prePaint{Screen,Window} call to estimate the
amount of time passed between the next and the last frames.
Unfortunately, this is an API incompatible change. However, it shouldn't
take a lot of work to port third-party binary effects, which don't use the
AnimationEffect class, to the new API. On the bright side, we no longer
need to be concerned about the Compositor getting idle.
We do still try to determine whether the Compositor is about to idle,
primarily, because the OpenGL render backend swaps buffers on present,
but that will change with the ongoing compositing timing rework.
2020-11-20 15:44:04 +00:00
|
|
|
timeLine.update(delta);
|
2008-02-21 13:20:22 +00:00
|
|
|
}
|
Use nullptr everywhere
Summary:
Because KWin is a very old project, we use three kinds of null pointer
literals: 0, NULL, and nullptr. Since C++11, it's recommended to use
nullptr keyword.
This change converts all usages of 0 and NULL literal to nullptr. Even
though it breaks git history, we need to do it in order to have consistent
code as well to ease code reviews (it's very tempting for some people to
add unrelated changes to their patches, e.g. converting NULL to nullptr).
Test Plan: Compiles.
Reviewers: #kwin, davidedmundson, romangg
Reviewed By: #kwin, davidedmundson, romangg
Subscribers: romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D23618
2019-09-19 14:46:54 +00:00
|
|
|
if (selected_window == nullptr)
|
2011-01-30 14:34:42 +00:00
|
|
|
abort();
|
2008-02-21 13:20:22 +00:00
|
|
|
}
|
Provide expected presentation time to effects
Effects are given the interval between two consecutive frames. The main
flaw of this approach is that if the Compositor transitions from the idle
state to "active" state, i.e. when there is something to repaint,
effects may see a very large interval between the last painted frame and
the current. In order to address this issue, the Scene invalidates the
timer that is used to measure time between consecutive frames before the
Compositor is about to become idle.
While this works perfectly fine with Xinerama-style rendering, with per
screen rendering, determining whether the compositor is about to idle is
rather a tedious task mostly because a single output can't be used for
the test.
Furthermore, since the Compositor schedules pointless repaints just to
ensure that it's idle, it might take several attempts to figure out
whether the scene timer must be invalidated if you use (true) per screen
rendering.
Ideally, all effects should use a timeline helper that is aware of the
underlying render loop and its timings. However, this option is off the
table because it will involve a lot of work to implement it.
Alternative and much simpler option is to pass the expected presentation
time to effects rather than time between consecutive frames. This means
that effects are responsible for determining how much animation timelines
have to be advanced. Typically, an effect would have to store the
presentation timestamp provided in either prePaint{Screen,Window} and
use it in the subsequent prePaint{Screen,Window} call to estimate the
amount of time passed between the next and the last frames.
Unfortunately, this is an API incompatible change. However, it shouldn't
take a lot of work to port third-party binary effects, which don't use the
AnimationEffect class, to the new API. On the bright side, we no longer
need to be concerned about the Compositor getting idle.
We do still try to determine whether the Compositor is about to idle,
primarily, because the OpenGL render backend swaps buffers on present,
but that will change with the ongoing compositing timing rework.
2020-11-20 15:44:04 +00:00
|
|
|
effects->prePaintScreen(data, presentTime);
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2008-02-21 13:20:22 +00:00
|
|
|
|
2019-10-29 22:04:15 +00:00
|
|
|
void CoverSwitchEffect::paintScreen(int mask, const QRegion ®ion, ScreenPaintData& data)
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
|
|
|
effects->paintScreen(mask, region, data);
|
2008-04-14 17:59:37 +00:00
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
if (mActivated || stop || stopRequested) {
|
|
|
|
|
2009-08-29 14:23:30 +00:00
|
|
|
QList< EffectWindow* > tempList = currentWindowList;
|
2011-01-30 14:34:42 +00:00
|
|
|
int index = tempList.indexOf(selected_window);
|
|
|
|
if (animation || start || stop) {
|
|
|
|
if (!start && !stop) {
|
|
|
|
if (direction == Right)
|
2008-11-11 21:32:45 +00:00
|
|
|
index++;
|
2008-02-21 13:20:22 +00:00
|
|
|
else
|
2008-11-11 21:32:45 +00:00
|
|
|
index--;
|
2011-01-30 14:34:42 +00:00
|
|
|
if (index < 0)
|
2008-11-11 21:32:45 +00:00
|
|
|
index = tempList.count() + index;
|
2011-01-30 14:34:42 +00:00
|
|
|
if (index >= tempList.count())
|
2008-11-11 21:32:45 +00:00
|
|
|
index = index % tempList.count();
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
|
|
|
foreach (Direction direction, scheduled_directions) {
|
|
|
|
if (direction == Right)
|
2008-02-21 13:20:22 +00:00
|
|
|
index++;
|
2008-11-11 21:32:45 +00:00
|
|
|
else
|
|
|
|
index--;
|
2011-01-30 14:34:42 +00:00
|
|
|
if (index < 0)
|
2008-11-11 21:32:45 +00:00
|
|
|
index = tempList.count() + index;
|
2011-01-30 14:34:42 +00:00
|
|
|
if (index >= tempList.count())
|
2008-11-11 21:32:45 +00:00
|
|
|
index = index % tempList.count();
|
2008-02-21 13:20:22 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
|
|
|
int leftIndex = index - 1;
|
|
|
|
if (leftIndex < 0)
|
|
|
|
leftIndex = tempList.count() - 1;
|
|
|
|
int rightIndex = index + 1;
|
|
|
|
if (rightIndex == tempList.count())
|
2008-11-11 21:32:45 +00:00
|
|
|
rightIndex = 0;
|
2008-04-29 16:20:06 +00:00
|
|
|
|
2008-02-21 13:20:22 +00:00
|
|
|
EffectWindow* frontWindow = tempList[ index ];
|
2009-01-31 13:15:14 +00:00
|
|
|
leftWindows.clear();
|
|
|
|
rightWindows.clear();
|
2008-04-29 16:20:06 +00:00
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
bool evenWindows = (tempList.count() % 2 == 0) ? true : false;
|
2008-02-21 13:20:22 +00:00
|
|
|
int leftWindowCount = 0;
|
2011-01-30 14:34:42 +00:00
|
|
|
if (evenWindows)
|
|
|
|
leftWindowCount = tempList.count() / 2 - 1;
|
2008-02-21 13:20:22 +00:00
|
|
|
else
|
2011-01-30 14:34:42 +00:00
|
|
|
leftWindowCount = (tempList.count() - 1) / 2;
|
|
|
|
for (int i = 0; i < leftWindowCount; i++) {
|
|
|
|
int tempIndex = (leftIndex - i);
|
|
|
|
if (tempIndex < 0)
|
2008-11-11 21:32:45 +00:00
|
|
|
tempIndex = tempList.count() + tempIndex;
|
2011-01-30 14:34:42 +00:00
|
|
|
leftWindows.prepend(tempList[ tempIndex ]);
|
|
|
|
}
|
2008-02-21 13:20:22 +00:00
|
|
|
int rightWindowCount = 0;
|
2011-01-30 14:34:42 +00:00
|
|
|
if (evenWindows)
|
|
|
|
rightWindowCount = tempList.count() / 2;
|
2008-02-21 13:20:22 +00:00
|
|
|
else
|
2011-01-30 14:34:42 +00:00
|
|
|
rightWindowCount = (tempList.count() - 1) / 2;
|
|
|
|
for (int i = 0; i < rightWindowCount; i++) {
|
|
|
|
int tempIndex = (rightIndex + i) % tempList.count();
|
|
|
|
rightWindows.prepend(tempList[ tempIndex ]);
|
|
|
|
}
|
2008-02-21 13:20:22 +00:00
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
if (reflection) {
|
2008-02-21 13:20:22 +00:00
|
|
|
// no reflections during start and stop animation
|
2010-12-18 20:11:45 +00:00
|
|
|
// except when using a shader
|
2012-09-21 07:09:52 +00:00
|
|
|
if ((!start && !stop) || effects->compositingType() == OpenGL2Compositing)
|
2011-01-30 14:34:42 +00:00
|
|
|
paintScene(frontWindow, leftWindows, rightWindows, true);
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
2008-12-03 19:48:06 +00:00
|
|
|
// we can use a huge scale factor (needed to calculate the rearground vertices)
|
|
|
|
// as we restrict with a PaintClipper painting on the current screen
|
2011-01-30 14:34:42 +00:00
|
|
|
float reflectionScaleFactor = 100000 * tan(60.0 * M_PI / 360.0f) / area.width();
|
2012-11-26 09:27:02 +00:00
|
|
|
const float width = area.width();
|
|
|
|
const float height = area.height();
|
2008-04-17 14:15:55 +00:00
|
|
|
float vertices[] = {
|
2012-11-26 09:27:02 +00:00
|
|
|
-width * 0.5f, height, 0.0,
|
|
|
|
width * 0.5f, height, 0.0,
|
|
|
|
width*reflectionScaleFactor, height, -5000,
|
|
|
|
-width*reflectionScaleFactor, height, -5000
|
2011-01-30 14:34:42 +00:00
|
|
|
};
|
2008-04-14 17:59:37 +00:00
|
|
|
// foreground
|
2010-12-19 13:41:26 +00:00
|
|
|
if (start) {
|
2018-09-05 19:03:46 +00:00
|
|
|
mirrorColor[0][3] = timeLine.value();
|
2010-12-19 13:41:26 +00:00
|
|
|
} else if (stop) {
|
2018-09-05 19:03:46 +00:00
|
|
|
mirrorColor[0][3] = 1.0 - timeLine.value();
|
2010-12-19 13:41:26 +00:00
|
|
|
} else {
|
|
|
|
mirrorColor[0][3] = 1.0;
|
|
|
|
}
|
2008-11-29 09:32:59 +00:00
|
|
|
|
2008-12-03 21:08:38 +00:00
|
|
|
int y = 0;
|
|
|
|
// have to adjust the y values to fit OpenGL
|
|
|
|
// in OpenGL y==0 is at bottom, in Qt at top
|
2011-01-30 14:34:42 +00:00
|
|
|
if (effects->numScreens() > 1) {
|
|
|
|
QRect fullArea = effects->clientArea(FullArea, 0, 1);
|
|
|
|
if (fullArea.height() != area.height()) {
|
|
|
|
if (area.y() == 0)
|
2008-12-03 21:08:38 +00:00
|
|
|
y = fullArea.height() - area.height();
|
|
|
|
else
|
|
|
|
y = fullArea.height() - area.y() - area.height();
|
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2008-12-03 21:08:38 +00:00
|
|
|
// use scissor to restrict painting of the reflection plane to current screen
|
2011-01-30 14:34:42 +00:00
|
|
|
glScissor(area.x(), y, area.width(), area.height());
|
|
|
|
glEnable(GL_SCISSOR_TEST);
|
2008-11-29 09:32:59 +00:00
|
|
|
|
2014-02-25 10:02:32 +00:00
|
|
|
if (m_reflectionShader && m_reflectionShader->isValid()) {
|
2015-02-01 03:29:50 +00:00
|
|
|
ShaderManager::instance()->pushShader(m_reflectionShader);
|
2015-11-30 14:48:14 +00:00
|
|
|
QMatrix4x4 windowTransformation = data.projectionMatrix();
|
2011-01-30 14:34:42 +00:00
|
|
|
windowTransformation.translate(area.x() + area.width() * 0.5f, 0.0, 0.0);
|
2015-11-30 14:48:14 +00:00
|
|
|
m_reflectionShader->setUniform(GLShader::ModelViewProjectionMatrix, windowTransformation);
|
2010-12-19 13:41:26 +00:00
|
|
|
m_reflectionShader->setUniform("u_frontColor", QVector4D(mirrorColor[0][0], mirrorColor[0][1], mirrorColor[0][2], mirrorColor[0][3]));
|
|
|
|
m_reflectionShader->setUniform("u_backColor", QVector4D(mirrorColor[1][0], mirrorColor[1][1], mirrorColor[1][2], mirrorColor[1][3]));
|
|
|
|
// TODO: make this one properly
|
|
|
|
QVector<float> verts;
|
|
|
|
QVector<float> texcoords;
|
|
|
|
verts.reserve(18);
|
|
|
|
texcoords.reserve(12);
|
|
|
|
texcoords << 1.0 << 0.0;
|
|
|
|
verts << vertices[6] << vertices[7] << vertices[8];
|
|
|
|
texcoords << 1.0 << 0.0;
|
|
|
|
verts << vertices[9] << vertices[10] << vertices[11];
|
|
|
|
texcoords << 0.0 << 0.0;
|
|
|
|
verts << vertices[0] << vertices[1] << vertices[2];
|
|
|
|
texcoords << 0.0 << 0.0;
|
|
|
|
verts << vertices[0] << vertices[1] << vertices[2];
|
|
|
|
texcoords << 0.0 << 0.0;
|
|
|
|
verts << vertices[3] << vertices[4] << vertices[5];
|
|
|
|
texcoords << 1.0 << 0.0;
|
|
|
|
verts << vertices[6] << vertices[7] << vertices[8];
|
|
|
|
GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer();
|
|
|
|
vbo->reset();
|
|
|
|
vbo->setData(6, 3, verts.data(), texcoords.data());
|
|
|
|
vbo->render(GL_TRIANGLES);
|
|
|
|
|
2015-02-01 03:29:50 +00:00
|
|
|
ShaderManager::instance()->popShader();
|
2008-02-21 13:20:22 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
glDisable(GL_SCISSOR_TEST);
|
|
|
|
glDisable(GL_BLEND);
|
|
|
|
}
|
|
|
|
paintScene(frontWindow, leftWindows, rightWindows);
|
2008-04-29 16:20:06 +00:00
|
|
|
|
2009-02-21 04:53:13 +00:00
|
|
|
// Render the caption frame
|
2011-01-30 14:34:42 +00:00
|
|
|
if (windowTitle) {
|
2009-05-12 17:18:27 +00:00
|
|
|
double opacity = 1.0;
|
2011-01-30 14:34:42 +00:00
|
|
|
if (start)
|
2018-09-05 19:03:46 +00:00
|
|
|
opacity = timeLine.value();
|
2011-01-30 14:34:42 +00:00
|
|
|
else if (stop)
|
2018-09-05 19:03:46 +00:00
|
|
|
opacity = 1.0 - timeLine.value();
|
2011-01-30 14:34:42 +00:00
|
|
|
if (animation)
|
2018-09-05 19:03:46 +00:00
|
|
|
captionFrame->setCrossFadeProgress(timeLine.value());
|
2011-01-30 14:34:42 +00:00
|
|
|
captionFrame->render(region, opacity);
|
2009-05-12 17:18:27 +00:00
|
|
|
}
|
2008-02-21 13:20:22 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2008-02-21 13:20:22 +00:00
|
|
|
|
|
|
|
void CoverSwitchEffect::postPaintScreen()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
|
|
|
if ((mActivated && (animation || start)) || stop || stopRequested) {
|
2018-09-05 19:03:46 +00:00
|
|
|
if (timeLine.done()) {
|
|
|
|
timeLine.reset();
|
2011-01-30 14:34:42 +00:00
|
|
|
if (stop) {
|
2008-02-21 13:20:22 +00:00
|
|
|
stop = false;
|
Use nullptr everywhere
Summary:
Because KWin is a very old project, we use three kinds of null pointer
literals: 0, NULL, and nullptr. Since C++11, it's recommended to use
nullptr keyword.
This change converts all usages of 0 and NULL literal to nullptr. Even
though it breaks git history, we need to do it in order to have consistent
code as well to ease code reviews (it's very tempting for some people to
add unrelated changes to their patches, e.g. converting NULL to nullptr).
Test Plan: Compiles.
Reviewers: #kwin, davidedmundson, romangg
Reviewed By: #kwin, davidedmundson, romangg
Subscribers: romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D23618
2019-09-19 14:46:54 +00:00
|
|
|
effects->setActiveFullScreenEffect(nullptr);
|
2011-01-30 14:34:42 +00:00
|
|
|
foreach (EffectWindow * window, referrencedWindows) {
|
2009-08-29 14:23:30 +00:00
|
|
|
window->unrefWindow();
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2009-08-29 14:23:30 +00:00
|
|
|
referrencedWindows.clear();
|
|
|
|
currentWindowList.clear();
|
Provide expected presentation time to effects
Effects are given the interval between two consecutive frames. The main
flaw of this approach is that if the Compositor transitions from the idle
state to "active" state, i.e. when there is something to repaint,
effects may see a very large interval between the last painted frame and
the current. In order to address this issue, the Scene invalidates the
timer that is used to measure time between consecutive frames before the
Compositor is about to become idle.
While this works perfectly fine with Xinerama-style rendering, with per
screen rendering, determining whether the compositor is about to idle is
rather a tedious task mostly because a single output can't be used for
the test.
Furthermore, since the Compositor schedules pointless repaints just to
ensure that it's idle, it might take several attempts to figure out
whether the scene timer must be invalidated if you use (true) per screen
rendering.
Ideally, all effects should use a timeline helper that is aware of the
underlying render loop and its timings. However, this option is off the
table because it will involve a lot of work to implement it.
Alternative and much simpler option is to pass the expected presentation
time to effects rather than time between consecutive frames. This means
that effects are responsible for determining how much animation timelines
have to be advanced. Typically, an effect would have to store the
presentation timestamp provided in either prePaint{Screen,Window} and
use it in the subsequent prePaint{Screen,Window} call to estimate the
amount of time passed between the next and the last frames.
Unfortunately, this is an API incompatible change. However, it shouldn't
take a lot of work to port third-party binary effects, which don't use the
AnimationEffect class, to the new API. On the bright side, we no longer
need to be concerned about the Compositor getting idle.
We do still try to determine whether the Compositor is about to idle,
primarily, because the OpenGL render backend swaps buffers on present,
but that will change with the ongoing compositing timing rework.
2020-11-20 15:44:04 +00:00
|
|
|
lastPresentTime = std::chrono::milliseconds::zero();
|
2011-01-30 14:34:42 +00:00
|
|
|
if (startRequested) {
|
2008-02-21 13:20:22 +00:00
|
|
|
startRequested = false;
|
|
|
|
mActivated = true;
|
|
|
|
effects->refTabBox();
|
2009-08-29 14:23:30 +00:00
|
|
|
currentWindowList = effects->currentTabBoxWindowList();
|
2011-01-30 14:34:42 +00:00
|
|
|
if (animateStart) {
|
2008-02-21 13:20:22 +00:00
|
|
|
start = true;
|
|
|
|
}
|
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
} else if (!scheduled_directions.isEmpty()) {
|
2008-11-11 21:32:45 +00:00
|
|
|
direction = scheduled_directions.dequeue();
|
2011-01-30 14:34:42 +00:00
|
|
|
if (start) {
|
2008-02-21 13:20:22 +00:00
|
|
|
animation = true;
|
|
|
|
start = false;
|
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
} else {
|
2008-02-21 13:20:22 +00:00
|
|
|
animation = false;
|
|
|
|
start = false;
|
2011-01-30 14:34:42 +00:00
|
|
|
if (stopRequested) {
|
2008-02-21 13:20:22 +00:00
|
|
|
stopRequested = false;
|
|
|
|
stop = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
effects->addRepaintFull();
|
2008-02-21 13:20:22 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
effects->postPaintScreen();
|
|
|
|
}
|
2008-02-21 13:20:22 +00:00
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
void CoverSwitchEffect::paintScene(EffectWindow* frontWindow, const EffectWindowList& leftWindows,
|
|
|
|
const EffectWindowList& rightWindows, bool reflectedWindows)
|
|
|
|
{
|
2008-02-21 13:20:22 +00:00
|
|
|
// LAYOUT
|
|
|
|
// one window in the front. Other windows left and right rotated
|
|
|
|
// for odd number of windows: left: (n-1)/2; front: 1; right: (n-1)/2
|
|
|
|
// for even number of windows: left: n/2; front: 1; right: n/2 -1
|
|
|
|
//
|
|
|
|
// ANIMATION
|
|
|
|
// forward (alt+tab)
|
|
|
|
// all left windows are moved to next position
|
|
|
|
// top most left window is rotated and moved to front window position
|
|
|
|
// front window is rotated and moved to next right window position
|
|
|
|
// right windows are moved to next position
|
|
|
|
// last right window becomes totally transparent in half the time
|
|
|
|
// appears transparent on left side and becomes totally opaque again
|
|
|
|
// backward (alt+shift+tab) same as forward but opposite direction
|
2008-04-17 14:15:55 +00:00
|
|
|
int width = area.width();
|
2009-08-29 14:23:30 +00:00
|
|
|
int leftWindowCount = leftWindows.count();
|
|
|
|
int rightWindowCount = rightWindows.count();
|
2008-02-21 13:20:22 +00:00
|
|
|
|
|
|
|
|
|
|
|
// Problem during animation: a window which is painted after another window
|
|
|
|
// appears in front of the other
|
|
|
|
// so during animation the painting order has to be rearreanged
|
|
|
|
// paint sequence no animation: left, right, front
|
|
|
|
// paint sequence forward animation: right, front, left
|
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
if (!animation) {
|
|
|
|
paintWindows(leftWindows, true, reflectedWindows);
|
|
|
|
paintWindows(rightWindows, false, reflectedWindows);
|
|
|
|
paintFrontWindow(frontWindow, width, leftWindowCount, rightWindowCount, reflectedWindows);
|
|
|
|
} else {
|
|
|
|
if (direction == Right) {
|
2018-09-05 19:03:46 +00:00
|
|
|
if (timeLine.value() < 0.5) {
|
2008-02-21 13:20:22 +00:00
|
|
|
// paint in normal way
|
2011-01-30 14:34:42 +00:00
|
|
|
paintWindows(leftWindows, true, reflectedWindows);
|
|
|
|
paintWindows(rightWindows, false, reflectedWindows);
|
|
|
|
paintFrontWindow(frontWindow, width, leftWindowCount, rightWindowCount, reflectedWindows);
|
|
|
|
} else {
|
|
|
|
paintWindows(rightWindows, false, reflectedWindows);
|
|
|
|
paintFrontWindow(frontWindow, width, leftWindowCount, rightWindowCount, reflectedWindows);
|
|
|
|
paintWindows(leftWindows, true, reflectedWindows, rightWindows.at(0));
|
2008-02-21 13:20:22 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
} else {
|
|
|
|
paintWindows(leftWindows, true, reflectedWindows);
|
2018-09-05 19:03:46 +00:00
|
|
|
if (timeLine.value() < 0.5) {
|
2011-01-30 14:34:42 +00:00
|
|
|
paintWindows(rightWindows, false, reflectedWindows);
|
|
|
|
paintFrontWindow(frontWindow, width, leftWindowCount, rightWindowCount, reflectedWindows);
|
|
|
|
} else {
|
2008-11-11 21:32:45 +00:00
|
|
|
EffectWindow* leftWindow;
|
2011-01-30 14:34:42 +00:00
|
|
|
if (leftWindowCount > 0) {
|
|
|
|
leftWindow = leftWindows.at(0);
|
|
|
|
paintFrontWindow(frontWindow, width, leftWindowCount, rightWindowCount, reflectedWindows);
|
|
|
|
} else
|
2008-11-11 21:32:45 +00:00
|
|
|
leftWindow = frontWindow;
|
2011-01-30 14:34:42 +00:00
|
|
|
paintWindows(rightWindows, false, reflectedWindows, leftWindow);
|
2008-02-21 13:20:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2008-04-29 16:20:06 +00:00
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
void CoverSwitchEffect::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data)
|
|
|
|
{
|
|
|
|
if (mActivated || stop || stopRequested) {
|
|
|
|
if (!(mask & PAINT_WINDOW_TRANSFORMED) && !w->isDesktop()) {
|
|
|
|
if ((start || stop) && w->isDock()) {
|
2018-09-05 19:03:46 +00:00
|
|
|
data.setOpacity(1.0 - timeLine.value());
|
2011-01-30 14:34:42 +00:00
|
|
|
if (stop)
|
2018-09-05 19:03:46 +00:00
|
|
|
data.setOpacity(timeLine.value());
|
2011-01-30 14:34:42 +00:00
|
|
|
} else
|
2008-02-21 13:20:22 +00:00
|
|
|
return;
|
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
|
|
|
if ((start || stop) && (!w->isOnCurrentDesktop() || w->isMinimized())) {
|
|
|
|
if (stop) // Fade out windows not on the current desktop
|
2018-09-05 19:03:46 +00:00
|
|
|
data.setOpacity(1.0 - timeLine.value());
|
2008-04-29 16:20:06 +00:00
|
|
|
else // Fade in Windows from other desktops when animation is started
|
2018-09-05 19:03:46 +00:00
|
|
|
data.setOpacity(timeLine.value());
|
2008-02-21 13:20:22 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
effects->paintWindow(w, mask, region, data);
|
|
|
|
}
|
2008-04-29 16:20:06 +00:00
|
|
|
|
2011-03-06 11:15:16 +00:00
|
|
|
void CoverSwitchEffect::slotTabBoxAdded(int mode)
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
|
|
|
if (effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this)
|
2008-03-09 14:42:20 +00:00
|
|
|
return;
|
2011-01-30 14:34:42 +00:00
|
|
|
if (!mActivated) {
|
2015-04-30 19:15:58 +00:00
|
|
|
effects->setShowingDesktop(false);
|
2008-02-21 13:20:22 +00:00
|
|
|
// only for windows mode
|
2011-01-30 14:34:42 +00:00
|
|
|
if (((mode == TabBoxWindowsMode && primaryTabBox) ||
|
2012-04-25 20:57:48 +00:00
|
|
|
(mode == TabBoxWindowsAlternativeMode && secondaryTabBox) ||
|
|
|
|
(mode == TabBoxCurrentAppWindowsMode && primaryTabBox) ||
|
|
|
|
(mode == TabBoxCurrentAppWindowsAlternativeMode && secondaryTabBox))
|
2011-01-30 14:34:42 +00:00
|
|
|
&& effects->currentTabBoxWindowList().count() > 0) {
|
2013-04-24 14:51:04 +00:00
|
|
|
effects->startMouseInterception(this, Qt::ArrowCursor);
|
2008-11-11 21:32:45 +00:00
|
|
|
activeScreen = effects->activeScreen();
|
2011-01-30 14:34:42 +00:00
|
|
|
if (!stop && !stopRequested) {
|
2008-02-21 13:20:22 +00:00
|
|
|
effects->refTabBox();
|
2011-01-30 14:34:42 +00:00
|
|
|
effects->setActiveFullScreenEffect(this);
|
2008-11-11 21:32:45 +00:00
|
|
|
scheduled_directions.clear();
|
|
|
|
selected_window = effects->currentTabBoxWindow();
|
2009-08-29 14:23:30 +00:00
|
|
|
currentWindowList = effects->currentTabBoxWindowList();
|
2008-11-11 21:32:45 +00:00
|
|
|
direction = Left;
|
2008-02-21 13:20:22 +00:00
|
|
|
mActivated = true;
|
2011-01-30 14:34:42 +00:00
|
|
|
if (animateStart) {
|
2008-02-21 13:20:22 +00:00
|
|
|
start = true;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2008-04-17 14:15:55 +00:00
|
|
|
|
|
|
|
// Calculation of correct area
|
2011-01-30 14:34:42 +00:00
|
|
|
area = effects->clientArea(FullScreenArea, activeScreen, effects->currentDesktop());
|
2014-02-24 15:13:30 +00:00
|
|
|
const QSize screenSize = effects->virtualScreenSize();
|
|
|
|
scaleFactor = (zPosition + 1100) * 2.0 * tan(60.0 * M_PI / 360.0f) / screenSize.width();
|
|
|
|
if (screenSize.width() - area.width() != 0) {
|
2008-12-06 22:51:57 +00:00
|
|
|
// one of the screens is smaller than the other (horizontal)
|
2014-02-24 15:13:30 +00:00
|
|
|
if (area.width() < screenSize.width() - area.width())
|
|
|
|
scaleFactor *= (float)area.width() / (float)(screenSize.width() - area.width());
|
|
|
|
else if (area.width() != screenSize.width() - area.width()) {
|
2008-12-06 22:51:57 +00:00
|
|
|
// vertical layout with different width
|
|
|
|
// but we don't want to catch screens with same width and different height
|
2014-02-24 15:13:30 +00:00
|
|
|
if (screenSize.height() != area.height())
|
|
|
|
scaleFactor *= (float)area.width() / (float)(screenSize.width());
|
2008-11-11 21:32:45 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2008-04-17 14:15:55 +00:00
|
|
|
|
2015-02-01 03:29:50 +00:00
|
|
|
if (effects->numScreens() > 1) {
|
|
|
|
// unfortunatelly we have to change the projection matrix in dual screen mode
|
|
|
|
// code is adapted from SceneOpenGL2::createProjectionMatrix()
|
|
|
|
QRect fullRect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop());
|
|
|
|
float fovy = 60.0f;
|
|
|
|
float aspect = 1.0f;
|
|
|
|
float zNear = 0.1f;
|
|
|
|
float zFar = 100.0f;
|
|
|
|
|
|
|
|
float ymax = zNear * std::tan(fovy * M_PI / 360.0f);
|
|
|
|
float ymin = -ymax;
|
|
|
|
float xmin = ymin * aspect;
|
|
|
|
float xmax = ymax * aspect;
|
|
|
|
|
|
|
|
if (area.width() != fullRect.width()) {
|
|
|
|
if (area.x() == 0) {
|
|
|
|
// horizontal layout: left screen
|
|
|
|
xmin *= (float)area.width() / (float)fullRect.width();
|
|
|
|
xmax *= (fullRect.width() - 0.5f * area.width()) / (0.5f * fullRect.width());
|
|
|
|
} else {
|
|
|
|
// horizontal layout: right screen
|
|
|
|
xmin *= (fullRect.width() - 0.5f * area.width()) / (0.5f * fullRect.width());
|
|
|
|
xmax *= (float)area.width() / (float)fullRect.width();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (area.height() != fullRect.height()) {
|
|
|
|
if (area.y() == 0) {
|
|
|
|
// vertical layout: top screen
|
|
|
|
ymin *= (fullRect.height() - 0.5f * area.height()) / (0.5f * fullRect.height());
|
|
|
|
ymax *= (float)area.height() / (float)fullRect.height();
|
|
|
|
} else {
|
|
|
|
// vertical layout: bottom screen
|
|
|
|
ymin *= (float)area.height() / (float)fullRect.height();
|
|
|
|
ymax *= (fullRect.height() - 0.5f * area.height()) / (0.5f * fullRect.height());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_projectionMatrix = QMatrix4x4();
|
|
|
|
m_projectionMatrix.frustum(xmin, xmax, ymin, ymax, zNear, zFar);
|
|
|
|
|
|
|
|
const float scaleFactor = 1.1f / zNear;
|
|
|
|
|
|
|
|
// Create a second matrix that transforms screen coordinates
|
|
|
|
// to world coordinates.
|
|
|
|
QMatrix4x4 matrix;
|
|
|
|
matrix.translate(xmin * scaleFactor, ymax * scaleFactor, -1.1);
|
|
|
|
matrix.scale( (xmax - xmin) * scaleFactor / fullRect.width(),
|
|
|
|
-(ymax - ymin) * scaleFactor / fullRect.height(),
|
|
|
|
0.001);
|
|
|
|
// Combine the matrices
|
|
|
|
m_projectionMatrix *= matrix;
|
|
|
|
|
|
|
|
m_modelviewMatrix = QMatrix4x4();
|
|
|
|
m_modelviewMatrix.translate(area.x(), area.y(), 0.0);
|
|
|
|
}
|
|
|
|
|
2009-02-21 04:53:13 +00:00
|
|
|
// Setup caption frame geometry
|
2011-01-30 14:34:42 +00:00
|
|
|
if (windowTitle) {
|
|
|
|
QRect frameRect = QRect(area.width() * 0.25f + area.x(),
|
|
|
|
area.height() * 0.9f + area.y(),
|
|
|
|
area.width() * 0.5f,
|
|
|
|
QFontMetrics(captionFont).height());
|
2012-05-01 17:50:22 +00:00
|
|
|
if (!captionFrame) {
|
|
|
|
captionFrame = effects->effectFrame(EffectFrameStyled);
|
|
|
|
captionFrame->setFont(captionFont);
|
|
|
|
captionFrame->enableCrossFade(true);
|
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
captionFrame->setGeometry(frameRect);
|
|
|
|
captionFrame->setIconSize(QSize(frameRect.height(), frameRect.height()));
|
2009-05-12 17:18:27 +00:00
|
|
|
// And initial contents
|
2012-03-29 08:34:33 +00:00
|
|
|
updateCaption();
|
2009-05-12 17:18:27 +00:00
|
|
|
}
|
2009-02-21 04:53:13 +00:00
|
|
|
|
2008-02-21 13:20:22 +00:00
|
|
|
effects->addRepaintFull();
|
2011-01-30 14:34:42 +00:00
|
|
|
} else {
|
2008-02-21 13:20:22 +00:00
|
|
|
startRequested = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2008-02-21 13:20:22 +00:00
|
|
|
|
2011-03-06 11:15:16 +00:00
|
|
|
void CoverSwitchEffect::slotTabBoxClosed()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
|
|
|
if (mActivated) {
|
|
|
|
if (animateStop) {
|
|
|
|
if (!animation && !start) {
|
2008-02-21 13:20:22 +00:00
|
|
|
stop = true;
|
2011-01-30 14:34:42 +00:00
|
|
|
} else if (start && scheduled_directions.isEmpty()) {
|
2008-04-22 18:09:50 +00:00
|
|
|
start = false;
|
|
|
|
stop = true;
|
2018-09-05 19:03:46 +00:00
|
|
|
timeLine.setElapsed(timeLine.duration() - timeLine.elapsed());
|
2011-01-30 14:34:42 +00:00
|
|
|
} else {
|
2008-02-21 13:20:22 +00:00
|
|
|
stopRequested = true;
|
|
|
|
}
|
2018-09-05 19:03:46 +00:00
|
|
|
} else {
|
Use nullptr everywhere
Summary:
Because KWin is a very old project, we use three kinds of null pointer
literals: 0, NULL, and nullptr. Since C++11, it's recommended to use
nullptr keyword.
This change converts all usages of 0 and NULL literal to nullptr. Even
though it breaks git history, we need to do it in order to have consistent
code as well to ease code reviews (it's very tempting for some people to
add unrelated changes to their patches, e.g. converting NULL to nullptr).
Test Plan: Compiles.
Reviewers: #kwin, davidedmundson, romangg
Reviewed By: #kwin, davidedmundson, romangg
Subscribers: romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D23618
2019-09-19 14:46:54 +00:00
|
|
|
effects->setActiveFullScreenEffect(nullptr);
|
2018-09-05 19:03:46 +00:00
|
|
|
start = false;
|
|
|
|
animation = false;
|
|
|
|
timeLine.reset();
|
Provide expected presentation time to effects
Effects are given the interval between two consecutive frames. The main
flaw of this approach is that if the Compositor transitions from the idle
state to "active" state, i.e. when there is something to repaint,
effects may see a very large interval between the last painted frame and
the current. In order to address this issue, the Scene invalidates the
timer that is used to measure time between consecutive frames before the
Compositor is about to become idle.
While this works perfectly fine with Xinerama-style rendering, with per
screen rendering, determining whether the compositor is about to idle is
rather a tedious task mostly because a single output can't be used for
the test.
Furthermore, since the Compositor schedules pointless repaints just to
ensure that it's idle, it might take several attempts to figure out
whether the scene timer must be invalidated if you use (true) per screen
rendering.
Ideally, all effects should use a timeline helper that is aware of the
underlying render loop and its timings. However, this option is off the
table because it will involve a lot of work to implement it.
Alternative and much simpler option is to pass the expected presentation
time to effects rather than time between consecutive frames. This means
that effects are responsible for determining how much animation timelines
have to be advanced. Typically, an effect would have to store the
presentation timestamp provided in either prePaint{Screen,Window} and
use it in the subsequent prePaint{Screen,Window} call to estimate the
amount of time passed between the next and the last frames.
Unfortunately, this is an API incompatible change. However, it shouldn't
take a lot of work to port third-party binary effects, which don't use the
AnimationEffect class, to the new API. On the bright side, we no longer
need to be concerned about the Compositor getting idle.
We do still try to determine whether the Compositor is about to idle,
primarily, because the OpenGL render backend swaps buffers on present,
but that will change with the ongoing compositing timing rework.
2020-11-20 15:44:04 +00:00
|
|
|
lastPresentTime = std::chrono::milliseconds::zero();
|
2018-09-05 19:03:46 +00:00
|
|
|
}
|
2008-02-21 13:20:22 +00:00
|
|
|
mActivated = false;
|
|
|
|
effects->unrefTabBox();
|
2013-04-24 14:51:04 +00:00
|
|
|
effects->stopMouseInterception(this);
|
2008-02-21 13:20:22 +00:00
|
|
|
effects->addRepaintFull();
|
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2008-02-21 13:20:22 +00:00
|
|
|
|
2011-03-06 11:15:16 +00:00
|
|
|
void CoverSwitchEffect::slotTabBoxUpdated()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
|
|
|
if (mActivated) {
|
|
|
|
if (animateSwitch && currentWindowList.count() > 1) {
|
2008-02-21 13:20:22 +00:00
|
|
|
// determine the switch direction
|
2011-01-30 14:34:42 +00:00
|
|
|
if (selected_window != effects->currentTabBoxWindow()) {
|
Use nullptr everywhere
Summary:
Because KWin is a very old project, we use three kinds of null pointer
literals: 0, NULL, and nullptr. Since C++11, it's recommended to use
nullptr keyword.
This change converts all usages of 0 and NULL literal to nullptr. Even
though it breaks git history, we need to do it in order to have consistent
code as well to ease code reviews (it's very tempting for some people to
add unrelated changes to their patches, e.g. converting NULL to nullptr).
Test Plan: Compiles.
Reviewers: #kwin, davidedmundson, romangg
Reviewed By: #kwin, davidedmundson, romangg
Subscribers: romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D23618
2019-09-19 14:46:54 +00:00
|
|
|
if (selected_window != nullptr) {
|
2011-01-30 14:34:42 +00:00
|
|
|
int old_index = currentWindowList.indexOf(selected_window);
|
|
|
|
int new_index = effects->currentTabBoxWindowList().indexOf(effects->currentTabBoxWindow());
|
2008-11-11 21:32:45 +00:00
|
|
|
Direction new_direction;
|
|
|
|
int distance = new_index - old_index;
|
2011-01-30 14:34:42 +00:00
|
|
|
if (distance > 0)
|
2008-11-11 21:32:45 +00:00
|
|
|
new_direction = Left;
|
2011-01-30 14:34:42 +00:00
|
|
|
if (distance < 0)
|
2008-11-11 21:32:45 +00:00
|
|
|
new_direction = Right;
|
2011-01-30 14:34:42 +00:00
|
|
|
if (effects->currentTabBoxWindowList().count() == 2) {
|
2008-11-11 21:32:45 +00:00
|
|
|
new_direction = Left;
|
|
|
|
distance = 1;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
|
|
|
if (distance != 0) {
|
|
|
|
distance = abs(distance);
|
2008-11-11 21:32:45 +00:00
|
|
|
int tempDistance = effects->currentTabBoxWindowList().count() - distance;
|
2011-01-30 14:34:42 +00:00
|
|
|
if (tempDistance < abs(distance)) {
|
2008-11-11 21:32:45 +00:00
|
|
|
distance = tempDistance;
|
2011-01-30 14:34:42 +00:00
|
|
|
if (new_direction == Left)
|
2008-11-11 21:32:45 +00:00
|
|
|
new_direction = Right;
|
|
|
|
else
|
|
|
|
new_direction = Left;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
|
|
|
if (!animation && !start) {
|
2008-11-11 21:32:45 +00:00
|
|
|
animation = true;
|
|
|
|
direction = new_direction;
|
|
|
|
distance--;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
|
|
|
for (int i = 0; i < distance; i++) {
|
|
|
|
if (!scheduled_directions.isEmpty() && scheduled_directions.last() != new_direction)
|
2008-11-11 21:32:45 +00:00
|
|
|
scheduled_directions.pop_back();
|
|
|
|
else
|
2011-01-30 14:34:42 +00:00
|
|
|
scheduled_directions.enqueue(new_direction);
|
|
|
|
if (scheduled_directions.count() == effects->currentTabBoxWindowList().count())
|
2008-11-11 21:32:45 +00:00
|
|
|
scheduled_directions.clear();
|
|
|
|
}
|
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2008-11-11 21:32:45 +00:00
|
|
|
selected_window = effects->currentTabBoxWindow();
|
2009-08-29 14:23:30 +00:00
|
|
|
currentWindowList = effects->currentTabBoxWindowList();
|
2012-03-29 08:34:33 +00:00
|
|
|
updateCaption();
|
2008-11-11 21:32:45 +00:00
|
|
|
}
|
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
effects->addRepaintFull();
|
2008-11-11 21:32:45 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2008-02-21 13:20:22 +00:00
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
void CoverSwitchEffect::paintWindowCover(EffectWindow* w, bool reflectedWindow, WindowPaintData& data)
|
|
|
|
{
|
2008-11-11 21:32:45 +00:00
|
|
|
QRect windowRect = w->geometry();
|
2012-05-28 12:45:46 +00:00
|
|
|
data.setYTranslation(area.height() - windowRect.y() - windowRect.height());
|
|
|
|
data.setZTranslation(-zPosition);
|
2011-01-30 14:34:42 +00:00
|
|
|
if (start) {
|
|
|
|
if (w->isMinimized()) {
|
2018-09-05 19:03:46 +00:00
|
|
|
data.multiplyOpacity(timeLine.value());
|
2011-01-30 14:34:42 +00:00
|
|
|
} else {
|
2018-09-05 19:03:46 +00:00
|
|
|
const QVector3D translation = data.translation() * timeLine.value();
|
2012-05-28 12:45:46 +00:00
|
|
|
data.setXTranslation(translation.x());
|
|
|
|
data.setYTranslation(translation.y());
|
|
|
|
data.setZTranslation(translation.z());
|
2011-01-30 14:34:42 +00:00
|
|
|
if (effects->numScreens() > 1) {
|
|
|
|
QRect clientRect = effects->clientArea(FullScreenArea, w->screen(), effects->currentDesktop());
|
|
|
|
QRect fullRect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop());
|
|
|
|
if (w->screen() == activeScreen) {
|
|
|
|
if (clientRect.width() != fullRect.width() && clientRect.x() != fullRect.x()) {
|
2018-09-05 19:03:46 +00:00
|
|
|
data.translate(- clientRect.x() * (1.0f - timeLine.value()));
|
2008-11-11 21:32:45 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
if (clientRect.height() != fullRect.height() && clientRect.y() != fullRect.y()) {
|
2018-09-05 19:03:46 +00:00
|
|
|
data.translate(0.0, - clientRect.y() * (1.0f - timeLine.value()));
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (clientRect.width() != fullRect.width() && clientRect.x() < area.x()) {
|
2018-09-05 19:03:46 +00:00
|
|
|
data.translate(- clientRect.width() * (1.0f - timeLine.value()));
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
|
|
|
if (clientRect.height() != fullRect.height() && clientRect.y() < area.y()) {
|
2018-09-05 19:03:46 +00:00
|
|
|
data.translate(0.0, - clientRect.height() * (1.0f - timeLine.value()));
|
2008-11-11 21:32:45 +00:00
|
|
|
}
|
2008-02-21 13:20:22 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2018-09-05 19:03:46 +00:00
|
|
|
data.setRotationAngle(data.rotationAngle() * timeLine.value());
|
2008-11-11 21:32:45 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
|
|
|
if (stop) {
|
|
|
|
if (w->isMinimized() && w != effects->activeWindow()) {
|
2018-09-05 19:03:46 +00:00
|
|
|
data.multiplyOpacity(1.0 - timeLine.value());
|
2011-01-30 14:34:42 +00:00
|
|
|
} else {
|
2018-09-05 19:03:46 +00:00
|
|
|
const QVector3D translation = data.translation() * (1.0 - timeLine.value());
|
2012-05-28 12:45:46 +00:00
|
|
|
data.setXTranslation(translation.x());
|
|
|
|
data.setYTranslation(translation.y());
|
|
|
|
data.setZTranslation(translation.z());
|
2011-01-30 14:34:42 +00:00
|
|
|
if (effects->numScreens() > 1) {
|
|
|
|
QRect clientRect = effects->clientArea(FullScreenArea, w->screen(), effects->currentDesktop());
|
|
|
|
QRect rect = effects->clientArea(FullScreenArea, activeScreen, effects->currentDesktop());
|
|
|
|
QRect fullRect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop());
|
|
|
|
if (w->screen() == activeScreen) {
|
|
|
|
if (clientRect.width() != fullRect.width() && clientRect.x() != fullRect.x()) {
|
2018-09-05 19:03:46 +00:00
|
|
|
data.translate(- clientRect.x() * timeLine.value());
|
2008-11-11 21:32:45 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
if (clientRect.height() != fullRect.height() && clientRect.y() != fullRect.y()) {
|
2018-09-05 19:03:46 +00:00
|
|
|
data.translate(0.0, - clientRect.y() * timeLine.value());
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (clientRect.width() != fullRect.width() && clientRect.x() < rect.x()) {
|
2018-09-05 19:03:46 +00:00
|
|
|
data.translate(- clientRect.width() * timeLine.value());
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
|
|
|
if (clientRect.height() != fullRect.height() && clientRect.y() < area.y()) {
|
2018-09-05 19:03:46 +00:00
|
|
|
data.translate(0.0, - clientRect.height() * timeLine.value());
|
2008-11-11 21:32:45 +00:00
|
|
|
}
|
2008-02-21 13:20:22 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2018-09-05 19:03:46 +00:00
|
|
|
data.setRotationAngle(data.rotationAngle() * (1.0 - timeLine.value()));
|
2008-02-21 13:20:22 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2008-02-21 13:20:22 +00:00
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
if (reflectedWindow) {
|
2014-02-25 10:02:32 +00:00
|
|
|
QMatrix4x4 reflectionMatrix;
|
|
|
|
reflectionMatrix.scale(1.0, -1.0, 1.0);
|
2018-03-25 10:26:50 +00:00
|
|
|
data.setModelViewMatrix(reflectionMatrix*data.modelViewMatrix());
|
2014-02-25 10:02:32 +00:00
|
|
|
data.setYTranslation(- area.height() - windowRect.y() - windowRect.height());
|
|
|
|
if (start) {
|
2018-09-05 19:03:46 +00:00
|
|
|
data.multiplyOpacity(timeLine.value());
|
2014-02-25 10:02:32 +00:00
|
|
|
} else if (stop) {
|
2018-09-05 19:03:46 +00:00
|
|
|
data.multiplyOpacity(1.0 - timeLine.value());
|
2008-02-21 13:20:22 +00:00
|
|
|
}
|
2014-02-25 10:02:32 +00:00
|
|
|
effects->drawWindow(w,
|
|
|
|
PAINT_WINDOW_TRANSFORMED,
|
|
|
|
infiniteRegion(), data);
|
2011-01-30 14:34:42 +00:00
|
|
|
} else {
|
|
|
|
effects->paintWindow(w,
|
|
|
|
PAINT_WINDOW_TRANSFORMED,
|
|
|
|
infiniteRegion(), data);
|
2008-02-21 13:20:22 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2008-02-21 13:20:22 +00:00
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
void CoverSwitchEffect::paintFrontWindow(EffectWindow* frontWindow, int width, int leftWindows, int rightWindows, bool reflectedWindow)
|
|
|
|
{
|
Use nullptr everywhere
Summary:
Because KWin is a very old project, we use three kinds of null pointer
literals: 0, NULL, and nullptr. Since C++11, it's recommended to use
nullptr keyword.
This change converts all usages of 0 and NULL literal to nullptr. Even
though it breaks git history, we need to do it in order to have consistent
code as well to ease code reviews (it's very tempting for some people to
add unrelated changes to their patches, e.g. converting NULL to nullptr).
Test Plan: Compiles.
Reviewers: #kwin, davidedmundson, romangg
Reviewed By: #kwin, davidedmundson, romangg
Subscribers: romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D23618
2019-09-19 14:46:54 +00:00
|
|
|
if (frontWindow == nullptr)
|
2008-04-28 15:01:01 +00:00
|
|
|
return;
|
2008-02-21 13:20:22 +00:00
|
|
|
bool specialHandlingForward = false;
|
2011-01-30 14:34:42 +00:00
|
|
|
WindowPaintData data(frontWindow);
|
2015-02-01 03:29:50 +00:00
|
|
|
if (effects->numScreens() > 1) {
|
|
|
|
data.setProjectionMatrix(m_projectionMatrix);
|
|
|
|
data.setModelViewMatrix(m_modelviewMatrix);
|
|
|
|
}
|
2012-05-28 12:45:46 +00:00
|
|
|
data.setXTranslation(area.width() * 0.5 - frontWindow->geometry().x() - frontWindow->geometry().width() * 0.5);
|
2011-01-30 14:34:42 +00:00
|
|
|
if (leftWindows == 0) {
|
2008-02-21 13:20:22 +00:00
|
|
|
leftWindows = 1;
|
2011-01-30 14:34:42 +00:00
|
|
|
if (!start && !stop)
|
2008-11-11 21:32:45 +00:00
|
|
|
specialHandlingForward = true;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
|
|
|
if (rightWindows == 0) {
|
2008-02-21 13:20:22 +00:00
|
|
|
rightWindows = 1;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
|
|
|
if (animation) {
|
2011-08-02 15:26:21 +00:00
|
|
|
float distance = 0.0;
|
2014-02-24 15:13:30 +00:00
|
|
|
const QSize screenSize = effects->virtualScreenSize();
|
2011-08-02 15:26:21 +00:00
|
|
|
if (direction == Right) {
|
2008-02-21 13:20:22 +00:00
|
|
|
// move to right
|
2011-01-30 14:34:42 +00:00
|
|
|
distance = -frontWindow->geometry().width() * 0.5f + area.width() * 0.5f +
|
2014-02-24 15:13:30 +00:00
|
|
|
(((float)screenSize.width() * 0.5 * scaleFactor) - (float)area.width() * 0.5f) / rightWindows;
|
2018-09-05 19:03:46 +00:00
|
|
|
data.translate(distance * timeLine.value());
|
2012-06-02 19:54:18 +00:00
|
|
|
data.setRotationAxis(Qt::YAxis);
|
2018-09-05 19:03:46 +00:00
|
|
|
data.setRotationAngle(-angle * timeLine.value());
|
2012-06-02 19:54:18 +00:00
|
|
|
data.setRotationOrigin(QVector3D(frontWindow->geometry().width(), 0.0, 0.0));
|
2011-01-30 14:34:42 +00:00
|
|
|
} else {
|
2008-02-21 13:20:22 +00:00
|
|
|
// move to left
|
2011-01-30 14:34:42 +00:00
|
|
|
distance = frontWindow->geometry().width() * 0.5f - area.width() * 0.5f +
|
2014-02-24 15:13:30 +00:00
|
|
|
((float)width * 0.5f - ((float)screenSize.width() * 0.5 * scaleFactor)) / leftWindows;
|
2008-11-11 21:32:45 +00:00
|
|
|
float factor = 1.0;
|
2011-01-30 14:34:42 +00:00
|
|
|
if (specialHandlingForward)
|
2008-11-11 21:32:45 +00:00
|
|
|
factor = 2.0;
|
2018-09-05 19:03:46 +00:00
|
|
|
data.translate(distance * timeLine.value() * factor);
|
2012-06-02 19:54:18 +00:00
|
|
|
data.setRotationAxis(Qt::YAxis);
|
2018-09-05 19:03:46 +00:00
|
|
|
data.setRotationAngle(angle * timeLine.value());
|
2008-02-21 13:20:22 +00:00
|
|
|
}
|
|
|
|
}
|
2018-09-05 19:03:46 +00:00
|
|
|
if (specialHandlingForward && timeLine.value() < 0.5) {
|
|
|
|
data.multiplyOpacity(1.0 - timeLine.value() * 2.0);
|
|
|
|
}
|
|
|
|
paintWindowCover(frontWindow, reflectedWindow, data);
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2008-02-21 13:20:22 +00:00
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
void CoverSwitchEffect::paintWindows(const EffectWindowList& windows, bool left, bool reflectedWindows, EffectWindow* additionalWindow)
|
|
|
|
{
|
2008-04-17 14:15:55 +00:00
|
|
|
int width = area.width();
|
2009-08-29 14:23:30 +00:00
|
|
|
int windowCount = windows.count();
|
2008-02-21 13:20:22 +00:00
|
|
|
EffectWindow* window;
|
2011-01-30 14:34:42 +00:00
|
|
|
|
2008-02-21 13:20:22 +00:00
|
|
|
int rotateFactor = 1;
|
2011-01-30 14:34:42 +00:00
|
|
|
if (!left) {
|
2008-02-21 13:20:22 +00:00
|
|
|
rotateFactor = -1;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2008-11-11 21:32:45 +00:00
|
|
|
|
2014-02-24 15:13:30 +00:00
|
|
|
const QSize screenSize = effects->virtualScreenSize();
|
|
|
|
float xTranslate = -((float)(width) * 0.5f - ((float)screenSize.width() * 0.5 * scaleFactor));
|
2011-01-30 14:34:42 +00:00
|
|
|
if (!left)
|
2014-02-24 15:13:30 +00:00
|
|
|
xTranslate = ((float)screenSize.width() * 0.5 * scaleFactor) - (float)width * 0.5f;
|
2008-02-21 13:20:22 +00:00
|
|
|
// handling for additional window from other side
|
|
|
|
// has to appear on this side after half of the time
|
Use nullptr everywhere
Summary:
Because KWin is a very old project, we use three kinds of null pointer
literals: 0, NULL, and nullptr. Since C++11, it's recommended to use
nullptr keyword.
This change converts all usages of 0 and NULL literal to nullptr. Even
though it breaks git history, we need to do it in order to have consistent
code as well to ease code reviews (it's very tempting for some people to
add unrelated changes to their patches, e.g. converting NULL to nullptr).
Test Plan: Compiles.
Reviewers: #kwin, davidedmundson, romangg
Reviewed By: #kwin, davidedmundson, romangg
Subscribers: romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D23618
2019-09-19 14:46:54 +00:00
|
|
|
if (animation && timeLine.value() >= 0.5 && additionalWindow != nullptr) {
|
2011-01-30 14:34:42 +00:00
|
|
|
WindowPaintData data(additionalWindow);
|
2015-02-01 03:29:50 +00:00
|
|
|
if (effects->numScreens() > 1) {
|
|
|
|
data.setProjectionMatrix(m_projectionMatrix);
|
|
|
|
data.setModelViewMatrix(m_modelviewMatrix);
|
|
|
|
}
|
2012-06-02 19:54:18 +00:00
|
|
|
data.setRotationAxis(Qt::YAxis);
|
|
|
|
data.setRotationAngle(angle * rotateFactor);
|
2012-05-25 20:57:51 +00:00
|
|
|
if (left) {
|
2012-05-28 12:45:46 +00:00
|
|
|
data.translate(-xTranslate - additionalWindow->geometry().x());
|
2012-05-25 20:57:51 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
else {
|
2012-05-28 12:45:46 +00:00
|
|
|
data.translate(xTranslate + area.width() -
|
|
|
|
additionalWindow->geometry().x() - additionalWindow->geometry().width());
|
2012-06-02 19:54:18 +00:00
|
|
|
data.setRotationOrigin(QVector3D(additionalWindow->geometry().width(), 0.0, 0.0));
|
2008-02-21 13:20:22 +00:00
|
|
|
}
|
2018-09-05 19:03:46 +00:00
|
|
|
data.multiplyOpacity((timeLine.value() - 0.5) * 2.0);
|
2011-01-30 14:34:42 +00:00
|
|
|
paintWindowCover(additionalWindow, reflectedWindows, data);
|
|
|
|
}
|
2008-02-21 13:20:22 +00:00
|
|
|
// normal behaviour
|
2011-01-30 14:34:42 +00:00
|
|
|
for (int i = 0; i < windows.count(); i++) {
|
|
|
|
window = windows.at(i);
|
Use nullptr everywhere
Summary:
Because KWin is a very old project, we use three kinds of null pointer
literals: 0, NULL, and nullptr. Since C++11, it's recommended to use
nullptr keyword.
This change converts all usages of 0 and NULL literal to nullptr. Even
though it breaks git history, we need to do it in order to have consistent
code as well to ease code reviews (it's very tempting for some people to
add unrelated changes to their patches, e.g. converting NULL to nullptr).
Test Plan: Compiles.
Reviewers: #kwin, davidedmundson, romangg
Reviewed By: #kwin, davidedmundson, romangg
Subscribers: romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D23618
2019-09-19 14:46:54 +00:00
|
|
|
if (window == nullptr || window->isDeleted()) {
|
2008-04-28 15:01:01 +00:00
|
|
|
continue;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
|
|
|
WindowPaintData data(window);
|
2015-02-01 03:29:50 +00:00
|
|
|
if (effects->numScreens() > 1) {
|
|
|
|
data.setProjectionMatrix(m_projectionMatrix);
|
|
|
|
data.setModelViewMatrix(m_modelviewMatrix);
|
|
|
|
}
|
2012-06-02 19:54:18 +00:00
|
|
|
data.setRotationAxis(Qt::YAxis);
|
|
|
|
data.setRotationAngle(angle);
|
2011-01-30 14:34:42 +00:00
|
|
|
if (left)
|
2012-05-28 12:45:46 +00:00
|
|
|
data.translate(-xTranslate + xTranslate * i / windowCount - window->geometry().x());
|
2008-11-11 21:32:45 +00:00
|
|
|
else
|
2012-05-28 12:45:46 +00:00
|
|
|
data.translate(xTranslate + width - xTranslate * i / windowCount - window->geometry().x() - window->geometry().width());
|
2011-01-30 14:34:42 +00:00
|
|
|
if (animation) {
|
|
|
|
if (direction == Right) {
|
|
|
|
if ((i == windowCount - 1) && left) {
|
2008-02-21 13:20:22 +00:00
|
|
|
// right most window on left side -> move to front
|
2008-11-11 21:32:45 +00:00
|
|
|
// have to move one window distance plus half the difference between the window and the desktop size
|
2018-09-05 19:03:46 +00:00
|
|
|
data.translate((xTranslate / windowCount + (width - window->geometry().width()) * 0.5f) * timeLine.value());
|
|
|
|
data.setRotationAngle(angle - angle * timeLine.value());
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2008-02-21 13:20:22 +00:00
|
|
|
// right most window does not have to be moved
|
2011-01-30 14:34:42 +00:00
|
|
|
else if (!left && (i == 0)); // do nothing
|
|
|
|
else {
|
2008-02-21 13:20:22 +00:00
|
|
|
// all other windows - move to next position
|
2018-09-05 19:03:46 +00:00
|
|
|
data.translate(xTranslate / windowCount * timeLine.value());
|
2008-02-21 13:20:22 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
} else {
|
|
|
|
if ((i == windowCount - 1) && !left) {
|
2008-02-21 13:20:22 +00:00
|
|
|
// left most window on right side -> move to front
|
2018-09-05 19:03:46 +00:00
|
|
|
data.translate(- (xTranslate / windowCount + (width - window->geometry().width()) * 0.5f) * timeLine.value());
|
|
|
|
data.setRotationAngle(angle - angle * timeLine.value());
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2008-02-21 13:20:22 +00:00
|
|
|
// left most window does not have to be moved
|
2011-01-30 14:34:42 +00:00
|
|
|
else if (i == 0 && left); // do nothing
|
|
|
|
else {
|
2008-02-21 13:20:22 +00:00
|
|
|
// all other windows - move to next position
|
2018-09-05 19:03:46 +00:00
|
|
|
data.translate(- xTranslate / windowCount * timeLine.value());
|
2008-02-21 13:20:22 +00:00
|
|
|
}
|
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2012-05-27 17:28:02 +00:00
|
|
|
if (!left)
|
2012-06-02 19:54:18 +00:00
|
|
|
data.setRotationOrigin(QVector3D(window->geometry().width(), 0.0, 0.0));
|
|
|
|
data.setRotationAngle(data.rotationAngle() * rotateFactor);
|
2008-02-21 13:20:22 +00:00
|
|
|
// make window most to edge transparent if animation
|
2011-01-30 14:34:42 +00:00
|
|
|
if (animation && i == 0 && ((direction == Left && left) || (direction == Right && !left))) {
|
2008-02-21 13:20:22 +00:00
|
|
|
// only for the first half of the animation
|
2018-09-05 19:03:46 +00:00
|
|
|
if (timeLine.value() < 0.5) {
|
|
|
|
data.multiplyOpacity((1.0 - timeLine.value() * 2.0));
|
2011-01-30 14:34:42 +00:00
|
|
|
paintWindowCover(window, reflectedWindows, data);
|
2008-11-11 21:32:45 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
} else {
|
|
|
|
paintWindowCover(window, reflectedWindows, data);
|
2008-11-11 21:32:45 +00:00
|
|
|
}
|
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2008-11-11 21:32:45 +00:00
|
|
|
|
2013-04-24 14:51:04 +00:00
|
|
|
void CoverSwitchEffect::windowInputMouseEvent(QEvent* e)
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
|
|
|
if (e->type() != QEvent::MouseButtonPress)
|
2008-11-11 21:32:45 +00:00
|
|
|
return;
|
|
|
|
// we don't want click events during animations
|
2011-01-30 14:34:42 +00:00
|
|
|
if (animation)
|
2008-11-11 21:32:45 +00:00
|
|
|
return;
|
2012-05-25 07:04:58 +00:00
|
|
|
QMouseEvent* event = static_cast< QMouseEvent* >(e);
|
2008-11-11 21:32:45 +00:00
|
|
|
|
2012-05-25 07:04:58 +00:00
|
|
|
switch (event->button()) {
|
|
|
|
case Qt::XButton1: // wheel up
|
|
|
|
selectPreviousWindow();
|
|
|
|
break;
|
|
|
|
case Qt::XButton2: // wheel down
|
|
|
|
selectNextWindow();
|
|
|
|
break;
|
|
|
|
case Qt::LeftButton:
|
|
|
|
case Qt::RightButton:
|
2020-09-01 05:14:58 +00:00
|
|
|
case Qt::MiddleButton:
|
2012-05-25 07:04:58 +00:00
|
|
|
default:
|
|
|
|
QPoint pos = event->pos();
|
2008-11-11 21:32:45 +00:00
|
|
|
|
2012-05-25 07:04:58 +00:00
|
|
|
// determine if a window has been clicked
|
|
|
|
// not interested in events above a fullscreen window (ignoring panel size)
|
|
|
|
if (pos.y() < (area.height()*scaleFactor - area.height()) * 0.5f *(1.0f / scaleFactor))
|
|
|
|
return;
|
|
|
|
|
|
|
|
// if there is no selected window (that is no window at all) we cannot click it
|
|
|
|
if (!selected_window)
|
|
|
|
return;
|
2010-01-09 10:36:31 +00:00
|
|
|
|
2012-05-25 07:04:58 +00:00
|
|
|
if (pos.x() < (area.width()*scaleFactor - selected_window->width()) * 0.5f *(1.0f / scaleFactor)) {
|
|
|
|
float availableSize = (area.width() * scaleFactor - area.width()) * 0.5f * (1.0f / scaleFactor);
|
|
|
|
for (int i = 0; i < leftWindows.count(); i++) {
|
|
|
|
int windowPos = availableSize / leftWindows.count() * i;
|
|
|
|
if (pos.x() < windowPos)
|
2008-11-11 21:32:45 +00:00
|
|
|
continue;
|
2012-05-25 07:04:58 +00:00
|
|
|
if (i + 1 < leftWindows.count()) {
|
|
|
|
if (pos.x() > availableSize / leftWindows.count()*(i + 1))
|
|
|
|
continue;
|
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
|
2012-05-25 07:04:58 +00:00
|
|
|
effects->setTabBoxWindow(leftWindows[i]);
|
|
|
|
return;
|
|
|
|
}
|
2008-11-11 21:32:45 +00:00
|
|
|
}
|
|
|
|
|
2012-05-25 07:04:58 +00:00
|
|
|
if (pos.x() > area.width() - (area.width()*scaleFactor - selected_window->width()) * 0.5f *(1.0f / scaleFactor)) {
|
|
|
|
float availableSize = (area.width() * scaleFactor - area.width()) * 0.5f * (1.0f / scaleFactor);
|
|
|
|
for (int i = 0; i < rightWindows.count(); i++) {
|
|
|
|
int windowPos = area.width() - availableSize / rightWindows.count() * i;
|
|
|
|
if (pos.x() > windowPos)
|
2008-11-11 21:32:45 +00:00
|
|
|
continue;
|
2012-05-25 07:04:58 +00:00
|
|
|
if (i + 1 < rightWindows.count()) {
|
|
|
|
if (pos.x() < area.width() - availableSize / rightWindows.count()*(i + 1))
|
|
|
|
continue;
|
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
|
2012-05-25 07:04:58 +00:00
|
|
|
effects->setTabBoxWindow(rightWindows[i]);
|
|
|
|
return;
|
|
|
|
}
|
2008-02-21 13:20:22 +00:00
|
|
|
}
|
2012-05-25 07:04:58 +00:00
|
|
|
break;
|
2008-02-21 13:20:22 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2008-02-21 13:20:22 +00:00
|
|
|
|
2009-01-31 13:23:58 +00:00
|
|
|
void CoverSwitchEffect::abort()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2009-09-16 09:57:17 +00:00
|
|
|
// it's possible that abort is called after tabbox has been closed
|
|
|
|
// in this case the cleanup is already done (see bug 207554)
|
2011-01-30 14:34:42 +00:00
|
|
|
if (mActivated) {
|
2009-09-16 09:57:17 +00:00
|
|
|
effects->unrefTabBox();
|
2013-04-24 14:51:04 +00:00
|
|
|
effects->stopMouseInterception(this);
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
Use nullptr everywhere
Summary:
Because KWin is a very old project, we use three kinds of null pointer
literals: 0, NULL, and nullptr. Since C++11, it's recommended to use
nullptr keyword.
This change converts all usages of 0 and NULL literal to nullptr. Even
though it breaks git history, we need to do it in order to have consistent
code as well to ease code reviews (it's very tempting for some people to
add unrelated changes to their patches, e.g. converting NULL to nullptr).
Test Plan: Compiles.
Reviewers: #kwin, davidedmundson, romangg
Reviewed By: #kwin, davidedmundson, romangg
Subscribers: romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D23618
2019-09-19 14:46:54 +00:00
|
|
|
effects->setActiveFullScreenEffect(nullptr);
|
2018-09-05 19:03:46 +00:00
|
|
|
timeLine.reset();
|
Provide expected presentation time to effects
Effects are given the interval between two consecutive frames. The main
flaw of this approach is that if the Compositor transitions from the idle
state to "active" state, i.e. when there is something to repaint,
effects may see a very large interval between the last painted frame and
the current. In order to address this issue, the Scene invalidates the
timer that is used to measure time between consecutive frames before the
Compositor is about to become idle.
While this works perfectly fine with Xinerama-style rendering, with per
screen rendering, determining whether the compositor is about to idle is
rather a tedious task mostly because a single output can't be used for
the test.
Furthermore, since the Compositor schedules pointless repaints just to
ensure that it's idle, it might take several attempts to figure out
whether the scene timer must be invalidated if you use (true) per screen
rendering.
Ideally, all effects should use a timeline helper that is aware of the
underlying render loop and its timings. However, this option is off the
table because it will involve a lot of work to implement it.
Alternative and much simpler option is to pass the expected presentation
time to effects rather than time between consecutive frames. This means
that effects are responsible for determining how much animation timelines
have to be advanced. Typically, an effect would have to store the
presentation timestamp provided in either prePaint{Screen,Window} and
use it in the subsequent prePaint{Screen,Window} call to estimate the
amount of time passed between the next and the last frames.
Unfortunately, this is an API incompatible change. However, it shouldn't
take a lot of work to port third-party binary effects, which don't use the
AnimationEffect class, to the new API. On the bright side, we no longer
need to be concerned about the Compositor getting idle.
We do still try to determine whether the Compositor is about to idle,
primarily, because the OpenGL render backend swaps buffers on present,
but that will change with the ongoing compositing timing rework.
2020-11-20 15:44:04 +00:00
|
|
|
lastPresentTime = std::chrono::milliseconds::zero();
|
2009-01-31 13:23:58 +00:00
|
|
|
mActivated = false;
|
|
|
|
stop = false;
|
|
|
|
stopRequested = false;
|
|
|
|
effects->addRepaintFull();
|
2010-07-18 16:32:37 +00:00
|
|
|
captionFrame->free();
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2009-01-31 13:23:58 +00:00
|
|
|
|
2011-02-27 08:25:45 +00:00
|
|
|
void CoverSwitchEffect::slotWindowClosed(EffectWindow* c)
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2011-04-03 22:04:23 +00:00
|
|
|
if (c == selected_window)
|
Use nullptr everywhere
Summary:
Because KWin is a very old project, we use three kinds of null pointer
literals: 0, NULL, and nullptr. Since C++11, it's recommended to use
nullptr keyword.
This change converts all usages of 0 and NULL literal to nullptr. Even
though it breaks git history, we need to do it in order to have consistent
code as well to ease code reviews (it's very tempting for some people to
add unrelated changes to their patches, e.g. converting NULL to nullptr).
Test Plan: Compiles.
Reviewers: #kwin, davidedmundson, romangg
Reviewed By: #kwin, davidedmundson, romangg
Subscribers: romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D23618
2019-09-19 14:46:54 +00:00
|
|
|
selected_window = nullptr;
|
2009-08-29 14:23:30 +00:00
|
|
|
// if the list is not empty, the effect is active
|
2011-01-30 14:34:42 +00:00
|
|
|
if (!currentWindowList.isEmpty()) {
|
2009-08-29 14:23:30 +00:00
|
|
|
c->refWindow();
|
2011-01-30 14:34:42 +00:00
|
|
|
referrencedWindows.append(c);
|
|
|
|
currentWindowList.removeAll(c);
|
|
|
|
leftWindows.removeAll(c);
|
|
|
|
rightWindows.removeAll(c);
|
2009-08-29 14:23:30 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2009-08-29 14:23:30 +00:00
|
|
|
|
2011-08-27 09:21:31 +00:00
|
|
|
bool CoverSwitchEffect::isActive() const
|
|
|
|
{
|
2015-11-20 10:17:16 +00:00
|
|
|
return (mActivated || stop || stopRequested) && !effects->isScreenLocked();
|
2011-08-27 09:21:31 +00:00
|
|
|
}
|
|
|
|
|
2012-03-29 08:34:33 +00:00
|
|
|
void CoverSwitchEffect::updateCaption()
|
|
|
|
{
|
|
|
|
if (!selected_window || !windowTitle) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (selected_window->isDesktop()) {
|
|
|
|
captionFrame->setText(i18nc("Special entry in alt+tab list for minimizing all windows",
|
|
|
|
"Show Desktop"));
|
2013-09-05 08:29:33 +00:00
|
|
|
static QPixmap pix = QIcon::fromTheme(QStringLiteral("user-desktop")).pixmap(captionFrame->iconSize());
|
2012-03-30 06:12:23 +00:00
|
|
|
captionFrame->setIcon(pix);
|
2012-03-29 08:34:33 +00:00
|
|
|
} else {
|
|
|
|
captionFrame->setText(selected_window->caption());
|
2012-03-30 06:12:23 +00:00
|
|
|
captionFrame->setIcon(selected_window->icon());
|
2012-03-29 08:34:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-29 09:11:10 +00:00
|
|
|
void CoverSwitchEffect::slotTabBoxKeyEvent(QKeyEvent *event)
|
|
|
|
{
|
|
|
|
if (event->type() == QEvent::KeyPress) {
|
|
|
|
switch (event->key()) {
|
|
|
|
case Qt::Key_Left:
|
2012-05-25 07:04:58 +00:00
|
|
|
selectPreviousWindow();
|
2012-03-29 09:11:10 +00:00
|
|
|
break;
|
|
|
|
case Qt::Key_Right:
|
2012-05-25 07:04:58 +00:00
|
|
|
selectNextWindow();
|
2012-03-29 09:11:10 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// nothing
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2012-05-25 07:04:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CoverSwitchEffect::selectNextOrPreviousWindow(bool forward)
|
|
|
|
{
|
|
|
|
if (!mActivated || !selected_window) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const int index = effects->currentTabBoxWindowList().indexOf(selected_window);
|
|
|
|
int newIndex = index;
|
|
|
|
if (forward) {
|
|
|
|
++newIndex;
|
|
|
|
} else {
|
|
|
|
--newIndex;
|
|
|
|
}
|
2012-03-29 09:11:10 +00:00
|
|
|
if (newIndex == effects->currentTabBoxWindowList().size()) {
|
|
|
|
newIndex = 0;
|
|
|
|
} else if (newIndex < 0) {
|
|
|
|
newIndex = effects->currentTabBoxWindowList().size() -1;
|
|
|
|
}
|
|
|
|
if (index == newIndex) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
effects->setTabBoxWindow(effects->currentTabBoxWindowList().at(newIndex));
|
|
|
|
}
|
|
|
|
|
2008-02-21 13:20:22 +00:00
|
|
|
} // namespace
|