kwin/effects/snaphelper/snaphelper.cpp
Vlad Zagorodniy 47c8405388 [effects/snaphelper] Do massive overhaul
Summary:
This patch fixes some of issues that the Snap Helper effect currently
has:

* If a window is being moved, there are visual artifacts (cause: missing
  addRepaint's);
* It uses addRepaintFull;
* For some reason, if a window goes deleted, it will be kept around as
  long as the Snap Helper effect needs it (visually, it doesn't look
  good).

Among other changes:
* Use variables to store color and width of grid lines;
* Use new connect syntax;
* Port to TimeLine;
* Fix coding style in some places.

Test Plan:
Tried the effect with different rendering backends, no longer see visual
artifacts when moving or resizing windows.

Reviewers: #kwin, romangg

Reviewed By: #kwin, romangg

Subscribers: romangg, abetts, kwin

Tags: #kwin

Differential Revision: https://phabricator.kde.org/D15695
2018-11-05 16:59:19 +02:00

335 lines
11 KiB
C++

/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2009 Lucas Murray <lmurray@undefinedfire.com>
Copyright (C) 2018 Vlad Zagorodniy <vladzzag@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "snaphelper.h"
#include <kwinglutils.h>
#ifdef KWIN_HAVE_XRENDER_COMPOSITING
#include <kwinxrenderutils.h>
#include <xcb/render.h>
#endif
#include <QPainter>
namespace KWin
{
static const int s_lineWidth = 4;
static const QColor s_lineColor = QColor(128, 128, 128, 128);
static QRegion computeDirtyRegion(const QRect &windowRect)
{
const QMargins outlineMargins(
s_lineWidth / 2,
s_lineWidth / 2,
s_lineWidth / 2,
s_lineWidth / 2
);
QRegion dirtyRegion;
for (int i = 0; i < effects->numScreens(); ++i) {
const QRect screenRect = effects->clientArea(ScreenArea, i, 0);
QRect screenWindowRect = windowRect;
screenWindowRect.moveCenter(screenRect.center());
QRect verticalBarRect(0, 0, s_lineWidth, screenRect.height());
verticalBarRect.moveCenter(screenRect.center());
verticalBarRect.adjust(-1, -1, 1, 1);
dirtyRegion += verticalBarRect;
QRect horizontalBarRect(0, 0, screenRect.width(), s_lineWidth);
horizontalBarRect.moveCenter(screenRect.center());
horizontalBarRect.adjust(-1, -1, 1, 1);
dirtyRegion += horizontalBarRect;
const QRect outlineOuterRect = screenWindowRect
.marginsAdded(outlineMargins)
.adjusted(-1, -1, 1, 1);
const QRect outlineInnerRect = screenWindowRect
.marginsRemoved(outlineMargins)
.adjusted(1, 1, -1, -1);
dirtyRegion += QRegion(outlineOuterRect) - QRegion(outlineInnerRect);
}
return dirtyRegion;
}
SnapHelperEffect::SnapHelperEffect()
{
reconfigure(ReconfigureAll);
connect(effects, &EffectsHandler::windowClosed, this, &SnapHelperEffect::slotWindowClosed);
connect(effects, &EffectsHandler::windowStartUserMovedResized, this, &SnapHelperEffect::slotWindowStartUserMovedResized);
connect(effects, &EffectsHandler::windowFinishUserMovedResized, this, &SnapHelperEffect::slotWindowFinishUserMovedResized);
connect(effects, &EffectsHandler::windowGeometryShapeChanged, this, &SnapHelperEffect::slotWindowGeometryShapeChanged);
}
SnapHelperEffect::~SnapHelperEffect()
{
}
void SnapHelperEffect::reconfigure(ReconfigureFlags flags)
{
Q_UNUSED(flags)
m_animation.timeLine.setDuration(
std::chrono::milliseconds(static_cast<int>(animationTime(250))));
}
void SnapHelperEffect::prePaintScreen(ScreenPrePaintData &data, int time)
{
if (m_animation.active) {
m_animation.timeLine.update(std::chrono::milliseconds(time));
}
effects->prePaintScreen(data, time);
}
void SnapHelperEffect::paintScreen(int mask, QRegion region, ScreenPaintData &data)
{
effects->paintScreen(mask, region, data);
const qreal opacityFactor = m_animation.active
? m_animation.timeLine.value()
: 1.0;
// Display the guide
if (effects->isOpenGLCompositing()) {
GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer();
vbo->reset();
vbo->setUseColor(true);
ShaderBinder binder(ShaderTrait::UniformColor);
binder.shader()->setUniform(GLShader::ModelViewProjectionMatrix, data.projectionMatrix());
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
QColor color = s_lineColor;
color.setAlphaF(color.alphaF() * opacityFactor);
vbo->setColor(color);
glLineWidth(s_lineWidth);
QVector<float> verts;
verts.reserve(effects->numScreens() * 24);
for (int i = 0; i < effects->numScreens(); ++i) {
const QRect rect = effects->clientArea(ScreenArea, i, 0);
const int midX = rect.x() + rect.width() / 2;
const int midY = rect.y() + rect.height() / 2 ;
const int halfWidth = m_geometry.width() / 2;
const int halfHeight = m_geometry.height() / 2;
// Center vertical line.
verts << rect.x() + rect.width() / 2 << rect.y();
verts << rect.x() + rect.width() / 2 << rect.y() + rect.height();
// Center horizontal line.
verts << rect.x() << rect.y() + rect.height() / 2;
verts << rect.x() + rect.width() << rect.y() + rect.height() / 2;
// Top edge of the window outline.
verts << midX - halfWidth - s_lineWidth / 2 << midY - halfHeight;
verts << midX + halfWidth + s_lineWidth / 2 << midY - halfHeight;
// Right edge of the window outline.
verts << midX + halfWidth << midY - halfHeight + s_lineWidth / 2;
verts << midX + halfWidth << midY + halfHeight - s_lineWidth / 2;
// Bottom edge of the window outline.
verts << midX + halfWidth + s_lineWidth / 2 << midY + halfHeight;
verts << midX - halfWidth - s_lineWidth / 2 << midY + halfHeight;
// Left edge of the window outline.
verts << midX - halfWidth << midY + halfHeight - s_lineWidth / 2;
verts << midX - halfWidth << midY - halfHeight + s_lineWidth / 2;
}
vbo->setData(verts.count() / 2, 2, verts.data(), nullptr);
vbo->render(GL_LINES);
glDisable(GL_BLEND);
glLineWidth(1.0);
}
if (effects->compositingType() == XRenderCompositing) {
#ifdef KWIN_HAVE_XRENDER_COMPOSITING
for (int i = 0; i < effects->numScreens(); ++i) {
const QRect rect = effects->clientArea(ScreenArea, i, 0);
const int midX = rect.x() + rect.width() / 2;
const int midY = rect.y() + rect.height() / 2 ;
const int halfWidth = m_geometry.width() / 2;
const int halfHeight = m_geometry.height() / 2;
xcb_rectangle_t rects[6];
// Center vertical line.
rects[0].x = rect.x() + rect.width() / 2 - s_lineWidth / 2;
rects[0].y = rect.y();
rects[0].width = s_lineWidth;
rects[0].height = rect.height();
// Center horizontal line.
rects[1].x = rect.x();
rects[1].y = rect.y() + rect.height() / 2 - s_lineWidth / 2;
rects[1].width = rect.width();
rects[1].height = s_lineWidth;
// Top edge of the window outline.
rects[2].x = midX - halfWidth - s_lineWidth / 2;
rects[2].y = midY - halfHeight - s_lineWidth / 2;
rects[2].width = 2 * halfWidth + s_lineWidth;
rects[2].height = s_lineWidth;
// Right edge of the window outline.
rects[3].x = midX + halfWidth - s_lineWidth / 2;
rects[3].y = midY - halfHeight + s_lineWidth / 2;
rects[3].width = s_lineWidth;
rects[3].height = 2 * halfHeight - s_lineWidth;
// Bottom edge of the window outline.
rects[4].x = midX - halfWidth - s_lineWidth / 2;
rects[4].y = midY + halfHeight - s_lineWidth / 2;
rects[4].width = 2 * halfWidth + s_lineWidth;
rects[4].height = s_lineWidth;
// Left edge of the window outline.
rects[5].x = midX - halfWidth - s_lineWidth / 2;
rects[5].y = midY - halfHeight + s_lineWidth / 2;
rects[5].width = s_lineWidth;
rects[5].height = 2 * halfHeight - s_lineWidth;
QColor color = s_lineColor;
color.setAlphaF(color.alphaF() * opacityFactor);
xcb_render_fill_rectangles(xcbConnection(), XCB_RENDER_PICT_OP_OVER, effects->xrenderBufferPicture(),
preMultiply(color), 6, rects);
}
#endif
}
if (effects->compositingType() == QPainterCompositing) {
QPainter *painter = effects->scenePainter();
painter->save();
QColor color = s_lineColor;
color.setAlphaF(color.alphaF() * opacityFactor);
QPen pen(color);
pen.setWidth(s_lineWidth);
painter->setPen(pen);
painter->setBrush(Qt::NoBrush);
for (int i = 0; i < effects->numScreens(); ++i) {
const QRect rect = effects->clientArea(ScreenArea, i, 0);
// Center lines.
painter->drawLine(rect.center().x(), rect.y(), rect.center().x(), rect.y() + rect.height());
painter->drawLine(rect.x(), rect.center().y(), rect.x() + rect.width(), rect.center().y());
// Window outline.
QRect outlineRect(0, 0, m_geometry.width(), m_geometry.height());
outlineRect.moveCenter(rect.center());
painter->drawRect(outlineRect);
}
painter->restore();
}
}
void SnapHelperEffect::postPaintScreen()
{
if (m_animation.active) {
effects->addRepaint(computeDirtyRegion(m_geometry));
}
if (m_animation.timeLine.done()) {
m_animation.active = false;
}
effects->postPaintScreen();
}
void SnapHelperEffect::slotWindowClosed(EffectWindow *w)
{
if (w != m_window) {
return;
}
m_window = nullptr;
m_animation.active = true;
m_animation.timeLine.setDirection(TimeLine::Backward);
if (m_animation.timeLine.done()) {
m_animation.timeLine.reset();
}
effects->addRepaint(computeDirtyRegion(m_geometry));
}
void SnapHelperEffect::slotWindowStartUserMovedResized(EffectWindow *w)
{
if (!w->isMovable()) {
return;
}
m_window = w;
m_geometry = w->geometry();
m_animation.active = true;
m_animation.timeLine.setDirection(TimeLine::Forward);
if (m_animation.timeLine.done()) {
m_animation.timeLine.reset();
}
effects->addRepaint(computeDirtyRegion(m_geometry));
}
void SnapHelperEffect::slotWindowFinishUserMovedResized(EffectWindow *w)
{
if (w != m_window) {
return;
}
m_window = nullptr;
m_geometry = w->geometry();
m_animation.active = true;
m_animation.timeLine.setDirection(TimeLine::Backward);
if (m_animation.timeLine.done()) {
m_animation.timeLine.reset();
}
effects->addRepaint(computeDirtyRegion(m_geometry));
}
void SnapHelperEffect::slotWindowGeometryShapeChanged(EffectWindow *w, const QRect &old)
{
if (w != m_window) {
return;
}
m_geometry = w->geometry();
effects->addRepaint(computeDirtyRegion(old));
}
bool SnapHelperEffect::isActive() const
{
return m_window != nullptr || m_animation.active;
}
} // namespace KWin