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

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2009 Lucas Murray <lmurray@undefinedfire.com>
0006     SPDX-FileCopyrightText: 2018 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
0007 
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 
0011 #include "snaphelper.h"
0012 
0013 #include "core/rendertarget.h"
0014 #include "core/renderviewport.h"
0015 #include "effect/effecthandler.h"
0016 #include "opengl/glutils.h"
0017 
0018 #include <QPainter>
0019 
0020 namespace KWin
0021 {
0022 
0023 static const int s_lineWidth = 4;
0024 static const QColor s_lineColor = QColor(128, 128, 128, 128);
0025 
0026 static QRegion computeDirtyRegion(const QRectF &windowRect)
0027 {
0028     const QMargins outlineMargins(
0029         s_lineWidth / 2,
0030         s_lineWidth / 2,
0031         s_lineWidth / 2,
0032         s_lineWidth / 2);
0033 
0034     QRegion dirtyRegion;
0035 
0036     const QList<Output *> screens = effects->screens();
0037     for (Output *screen : screens) {
0038         const QRectF screenRect = effects->clientArea(ScreenArea, screen, effects->currentDesktop());
0039 
0040         QRectF screenWindowRect = windowRect;
0041         screenWindowRect.moveCenter(screenRect.center());
0042 
0043         QRectF verticalBarRect(0, 0, s_lineWidth, screenRect.height());
0044         verticalBarRect.moveCenter(screenRect.center());
0045         verticalBarRect.adjust(-1, -1, 1, 1);
0046         dirtyRegion += verticalBarRect.toAlignedRect();
0047 
0048         QRectF horizontalBarRect(0, 0, screenRect.width(), s_lineWidth);
0049         horizontalBarRect.moveCenter(screenRect.center());
0050         horizontalBarRect.adjust(-1, -1, 1, 1);
0051         dirtyRegion += horizontalBarRect.toAlignedRect();
0052 
0053         const QRectF outlineOuterRect = screenWindowRect
0054                                             .marginsAdded(outlineMargins)
0055                                             .adjusted(-1, -1, 1, 1);
0056         const QRectF outlineInnerRect = screenWindowRect
0057                                             .marginsRemoved(outlineMargins)
0058                                             .adjusted(1, 1, -1, -1);
0059         dirtyRegion += QRegion(outlineOuterRect.toRect()) - QRegion(outlineInnerRect.toRect());
0060     }
0061 
0062     return dirtyRegion;
0063 }
0064 
0065 SnapHelperEffect::SnapHelperEffect()
0066 {
0067     reconfigure(ReconfigureAll);
0068 
0069     connect(effects, &EffectsHandler::windowAdded, this, &SnapHelperEffect::slotWindowAdded);
0070     connect(effects, &EffectsHandler::windowClosed, this, &SnapHelperEffect::slotWindowClosed);
0071 
0072     const auto windows = effects->stackingOrder();
0073     for (EffectWindow *window : windows) {
0074         slotWindowAdded(window);
0075     }
0076 }
0077 
0078 SnapHelperEffect::~SnapHelperEffect()
0079 {
0080 }
0081 
0082 void SnapHelperEffect::reconfigure(ReconfigureFlags flags)
0083 {
0084     m_animation.timeLine.setDuration(
0085         std::chrono::milliseconds(static_cast<int>(animationTime(250))));
0086 }
0087 
0088 void SnapHelperEffect::prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime)
0089 {
0090     if (m_animation.active) {
0091         m_animation.timeLine.advance(presentTime);
0092     }
0093 
0094     effects->prePaintScreen(data, presentTime);
0095 }
0096 
0097 void SnapHelperEffect::paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const QRegion &region, Output *screen)
0098 {
0099     effects->paintScreen(renderTarget, viewport, mask, region, screen);
0100 
0101     const qreal opacityFactor = m_animation.active
0102         ? m_animation.timeLine.value()
0103         : 1.0;
0104     const QList<Output *> screens = effects->screens();
0105 
0106     const auto scale = viewport.scale();
0107 
0108     // Display the guide
0109     if (effects->isOpenGLCompositing()) {
0110         GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer();
0111         vbo->reset();
0112         ShaderBinder binder(ShaderTrait::UniformColor | ShaderTrait::TransformColorspace);
0113         binder.shader()->setUniform(GLShader::Mat4Uniform::ModelViewProjectionMatrix, viewport.projectionMatrix());
0114         binder.shader()->setColorspaceUniformsFromSRGB(renderTarget.colorDescription());
0115         glEnable(GL_BLEND);
0116         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
0117 
0118         QColor color = s_lineColor;
0119         color.setAlphaF(color.alphaF() * opacityFactor);
0120         binder.shader()->setUniform(GLShader::ColorUniform::Color, color);
0121 
0122         glLineWidth(s_lineWidth);
0123         QList<QVector2D> verts;
0124         verts.reserve(screens.count() * 24);
0125         for (Output *screen : screens) {
0126             const QRectF rect = effects->clientArea(ScreenArea, screen, effects->currentDesktop());
0127             const int midX = rect.x() + rect.width() / 2;
0128             const int midY = rect.y() + rect.height() / 2;
0129             const int halfWidth = m_geometry.width() / 2;
0130             const int halfHeight = m_geometry.height() / 2;
0131 
0132             // Center vertical line.
0133             verts.push_back(QVector2D((rect.x() + rect.width() / 2) * scale, rect.y() * scale));
0134             verts.push_back(QVector2D((rect.x() + rect.width() / 2) * scale, (rect.y() + rect.height()) * scale));
0135 
0136             // Center horizontal line.
0137             verts.push_back(QVector2D(rect.x() * scale, (rect.y() + rect.height() / 2) * scale));
0138             verts.push_back(QVector2D((rect.x() + rect.width()) * scale, (rect.y() + rect.height() / 2) * scale));
0139 
0140             // Top edge of the window outline.
0141             verts.push_back(QVector2D((midX - halfWidth - s_lineWidth / 2) * scale, (midY - halfHeight) * scale));
0142             verts.push_back(QVector2D((midX + halfWidth + s_lineWidth / 2) * scale, (midY - halfHeight) * scale));
0143 
0144             // Right edge of the window outline.
0145             verts.push_back(QVector2D((midX + halfWidth) * scale, (midY - halfHeight + s_lineWidth / 2) * scale));
0146             verts.push_back(QVector2D((midX + halfWidth) * scale, (midY + halfHeight - s_lineWidth / 2) * scale));
0147 
0148             // Bottom edge of the window outline.
0149             verts.push_back(QVector2D((midX + halfWidth + s_lineWidth / 2) * scale, (midY + halfHeight) * scale));
0150             verts.push_back(QVector2D((midX - halfWidth - s_lineWidth / 2) * scale, (midY + halfHeight) * scale));
0151 
0152             // Left edge of the window outline.
0153             verts.push_back(QVector2D((midX - halfWidth) * scale, (midY + halfHeight - s_lineWidth / 2) * scale));
0154             verts.push_back(QVector2D((midX - halfWidth) * scale, (midY - halfHeight + s_lineWidth / 2) * scale));
0155         }
0156         vbo->setVertices(verts);
0157         vbo->render(GL_LINES);
0158 
0159         glDisable(GL_BLEND);
0160         glLineWidth(1.0);
0161     } else if (effects->compositingType() == QPainterCompositing) {
0162         QPainter *painter = effects->scenePainter();
0163         painter->save();
0164         QColor color = s_lineColor;
0165         color.setAlphaF(color.alphaF() * opacityFactor);
0166         QPen pen(color);
0167         pen.setWidth(s_lineWidth);
0168         painter->setPen(pen);
0169         painter->setBrush(Qt::NoBrush);
0170 
0171         for (Output *screen : screens) {
0172             const QRectF rect = effects->clientArea(ScreenArea, screen, effects->currentDesktop());
0173             // Center lines.
0174             painter->drawLine(rect.center().x(), rect.y(), rect.center().x(), rect.y() + rect.height());
0175             painter->drawLine(rect.x(), rect.center().y(), rect.x() + rect.width(), rect.center().y());
0176 
0177             // Window outline.
0178             QRectF outlineRect(0, 0, m_geometry.width(), m_geometry.height());
0179             outlineRect.moveCenter(rect.center());
0180             painter->drawRect(outlineRect);
0181         }
0182         painter->restore();
0183     }
0184 }
0185 
0186 void SnapHelperEffect::postPaintScreen()
0187 {
0188     if (m_animation.active) {
0189         effects->addRepaint(computeDirtyRegion(m_geometry));
0190     }
0191 
0192     if (m_animation.timeLine.done()) {
0193         m_animation.active = false;
0194     }
0195 
0196     effects->postPaintScreen();
0197 }
0198 
0199 void SnapHelperEffect::slotWindowAdded(EffectWindow *w)
0200 {
0201     connect(w, &EffectWindow::windowStartUserMovedResized, this, &SnapHelperEffect::slotWindowStartUserMovedResized);
0202     connect(w, &EffectWindow::windowFinishUserMovedResized, this, &SnapHelperEffect::slotWindowFinishUserMovedResized);
0203     connect(w, &EffectWindow::windowFrameGeometryChanged, this, &SnapHelperEffect::slotWindowFrameGeometryChanged);
0204 }
0205 
0206 void SnapHelperEffect::slotWindowClosed(EffectWindow *w)
0207 {
0208     if (w != m_window) {
0209         return;
0210     }
0211 
0212     m_window = nullptr;
0213 
0214     m_animation.active = true;
0215     m_animation.timeLine.setDirection(TimeLine::Backward);
0216 
0217     if (m_animation.timeLine.done()) {
0218         m_animation.timeLine.reset();
0219     }
0220 
0221     effects->addRepaint(computeDirtyRegion(m_geometry));
0222 }
0223 
0224 void SnapHelperEffect::slotWindowStartUserMovedResized(EffectWindow *w)
0225 {
0226     if (!w->isMovable()) {
0227         return;
0228     }
0229 
0230     m_window = w;
0231     m_geometry = w->frameGeometry();
0232 
0233     m_animation.active = true;
0234     m_animation.timeLine.setDirection(TimeLine::Forward);
0235 
0236     if (m_animation.timeLine.done()) {
0237         m_animation.timeLine.reset();
0238     }
0239 
0240     effects->addRepaint(computeDirtyRegion(m_geometry));
0241 }
0242 
0243 void SnapHelperEffect::slotWindowFinishUserMovedResized(EffectWindow *w)
0244 {
0245     if (w != m_window) {
0246         return;
0247     }
0248 
0249     m_window = nullptr;
0250     m_geometry = w->frameGeometry();
0251 
0252     m_animation.active = true;
0253     m_animation.timeLine.setDirection(TimeLine::Backward);
0254 
0255     if (m_animation.timeLine.done()) {
0256         m_animation.timeLine.reset();
0257     }
0258 
0259     effects->addRepaint(computeDirtyRegion(m_geometry));
0260 }
0261 
0262 void SnapHelperEffect::slotWindowFrameGeometryChanged(EffectWindow *w, const QRectF &old)
0263 {
0264     if (w != m_window) {
0265         return;
0266     }
0267 
0268     m_geometry = w->frameGeometry();
0269 
0270     effects->addRepaint(computeDirtyRegion(old));
0271 }
0272 
0273 bool SnapHelperEffect::isActive() const
0274 {
0275     return m_window != nullptr || m_animation.active;
0276 }
0277 
0278 } // namespace KWin
0279 
0280 #include "moc_snaphelper.cpp"