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 ®ion, 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"