diff --git a/autotests/effect/windowquadlisttest.cpp b/autotests/effect/windowquadlisttest.cpp index beb844d5b7..80e9feda03 100644 --- a/autotests/effect/windowquadlisttest.cpp +++ b/autotests/effect/windowquadlisttest.cpp @@ -6,7 +6,7 @@ SPDX-License-Identifier: GPL-2.0-or-later */ -#include "effect/effects.h" +#include "scene/itemgeometry.h" #include Q_DECLARE_METATYPE(KWin::WindowQuadList) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dceb04600d..2786209ad3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -160,6 +160,7 @@ target_sources(kwin PRIVATE scene/dndiconitem.cpp scene/imageitem.cpp scene/item.cpp + scene/itemgeometry.cpp scene/itemrenderer.cpp scene/itemrenderer_opengl.cpp scene/itemrenderer_qpainter.cpp @@ -461,6 +462,12 @@ install(FILES DESTINATION ${KDE_INSTALL_INCLUDEDIR}/kwin/opengl COMPONENT Devel ) +install(FILES + scene/item.h + scene/itemgeometry.h + DESTINATION ${KDE_INSTALL_INCLUDEDIR}/kwin/scene COMPONENT Devel +) + set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KWin") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/KWinConfig.cmake" diff --git a/src/compositor_x11.cpp b/src/compositor_x11.cpp index 00a06fc03a..d031c689ef 100644 --- a/src/compositor_x11.cpp +++ b/src/compositor_x11.cpp @@ -12,6 +12,7 @@ #include "core/overlaywindow.h" #include "core/renderbackend.h" #include "core/renderlayer.h" +#include "effect/effects.h" #include "opengl/glplatform.h" #include "options.h" #include "platformsupport/scenes/opengl/openglbackend.h" diff --git a/src/debug_console.cpp b/src/debug_console.cpp index c5ff610322..0ce287c0a2 100644 --- a/src/debug_console.cpp +++ b/src/debug_console.cpp @@ -10,6 +10,7 @@ #include "compositor.h" #include "core/graphicsbufferview.h" #include "core/inputdevice.h" +#include "effect/effects.h" #include "input_event.h" #include "internalwindow.h" #include "keyboard_input.h" diff --git a/src/effect/anidata_p.h b/src/effect/anidata_p.h index 54078c8751..e490907885 100644 --- a/src/effect/anidata_p.h +++ b/src/effect/anidata_p.h @@ -11,6 +11,7 @@ #pragma once #include "effect/animationeffect.h" +#include "effect/effects.h" #include "effect/timeline.h" #include diff --git a/src/effect/effectloader.cpp b/src/effect/effectloader.cpp index 93b82bc480..b77c05d7e9 100644 --- a/src/effect/effectloader.cpp +++ b/src/effect/effectloader.cpp @@ -12,6 +12,7 @@ #include // KWin #include "effect/effect.h" +#include "effect/effects.h" #include "plugin.h" #include "scripting/scriptedeffect.h" #include "scripting/scriptedquicksceneeffect.h" @@ -350,7 +351,7 @@ bool PluginEffectLoader::loadEffect(const KPluginMetaData &info, LoadEffectFlags return false; } - effects->makeOpenGLContextCurrent(); + effects->makeOpenGLContextCurrent(); // TODO: remove it if (!effectFactory->isSupported()) { qCDebug(KWIN_CORE) << "Effect is not supported: " << name; return false; diff --git a/src/effect/effects.cpp b/src/effect/effects.cpp index a116bf1f45..0f2a72a731 100644 --- a/src/effect/effects.cpp +++ b/src/effect/effects.cpp @@ -2119,299 +2119,6 @@ EffectWindowList EffectWindowGroup::members() const return ret; } -/*************************************************************** - WindowQuad -***************************************************************/ - -WindowQuad WindowQuad::makeSubQuad(double x1, double y1, double x2, double y2) const -{ - Q_ASSERT(x1 < x2 && y1 < y2 && x1 >= left() && x2 <= right() && y1 >= top() && y2 <= bottom()); - WindowQuad ret(*this); - // vertices are clockwise starting from topleft - ret.verts[0].px = x1; - ret.verts[3].px = x1; - ret.verts[1].px = x2; - ret.verts[2].px = x2; - ret.verts[0].py = y1; - ret.verts[1].py = y1; - ret.verts[2].py = y2; - ret.verts[3].py = y2; - - const double xOrigin = left(); - const double yOrigin = top(); - - const double widthReciprocal = 1 / (right() - xOrigin); - const double heightReciprocal = 1 / (bottom() - yOrigin); - - for (int i = 0; i < 4; ++i) { - const double w1 = (ret.verts[i].px - xOrigin) * widthReciprocal; - const double w2 = (ret.verts[i].py - yOrigin) * heightReciprocal; - - // Use bilinear interpolation to compute the texture coords. - ret.verts[i].tx = (1 - w1) * (1 - w2) * verts[0].tx + w1 * (1 - w2) * verts[1].tx + w1 * w2 * verts[2].tx + (1 - w1) * w2 * verts[3].tx; - ret.verts[i].ty = (1 - w1) * (1 - w2) * verts[0].ty + w1 * (1 - w2) * verts[1].ty + w1 * w2 * verts[2].ty + (1 - w1) * w2 * verts[3].ty; - } - - return ret; -} - -/*************************************************************** - WindowQuadList -***************************************************************/ - -WindowQuadList WindowQuadList::splitAtX(double x) const -{ - WindowQuadList ret; - ret.reserve(count()); - for (const WindowQuad &quad : *this) { - bool wholeleft = true; - bool wholeright = true; - for (int i = 0; i < 4; ++i) { - if (quad[i].x() < x) { - wholeright = false; - } - if (quad[i].x() > x) { - wholeleft = false; - } - } - if (wholeleft || wholeright) { // is whole in one split part - ret.append(quad); - continue; - } - if (quad.top() == quad.bottom() || quad.left() == quad.right()) { // quad has no size - ret.append(quad); - continue; - } - ret.append(quad.makeSubQuad(quad.left(), quad.top(), x, quad.bottom())); - ret.append(quad.makeSubQuad(x, quad.top(), quad.right(), quad.bottom())); - } - return ret; -} - -WindowQuadList WindowQuadList::splitAtY(double y) const -{ - WindowQuadList ret; - ret.reserve(count()); - for (const WindowQuad &quad : *this) { - bool wholetop = true; - bool wholebottom = true; - for (int i = 0; i < 4; ++i) { - if (quad[i].y() < y) { - wholebottom = false; - } - if (quad[i].y() > y) { - wholetop = false; - } - } - if (wholetop || wholebottom) { // is whole in one split part - ret.append(quad); - continue; - } - if (quad.top() == quad.bottom() || quad.left() == quad.right()) { // quad has no size - ret.append(quad); - continue; - } - ret.append(quad.makeSubQuad(quad.left(), quad.top(), quad.right(), y)); - ret.append(quad.makeSubQuad(quad.left(), y, quad.right(), quad.bottom())); - } - return ret; -} - -WindowQuadList WindowQuadList::makeGrid(int maxQuadSize) const -{ - if (empty()) { - return *this; - } - - // Find the bounding rectangle - double left = first().left(); - double right = first().right(); - double top = first().top(); - double bottom = first().bottom(); - - for (const WindowQuad &quad : std::as_const(*this)) { - left = std::min(left, quad.left()); - right = std::max(right, quad.right()); - top = std::min(top, quad.top()); - bottom = std::max(bottom, quad.bottom()); - } - - WindowQuadList ret; - - for (const WindowQuad &quad : std::as_const(*this)) { - const double quadLeft = quad.left(); - const double quadRight = quad.right(); - const double quadTop = quad.top(); - const double quadBottom = quad.bottom(); - - // sanity check, see BUG 390953 - if (quadLeft == quadRight || quadTop == quadBottom) { - ret.append(quad); - continue; - } - - // Compute the top-left corner of the first intersecting grid cell - const double xBegin = left + qFloor((quadLeft - left) / maxQuadSize) * maxQuadSize; - const double yBegin = top + qFloor((quadTop - top) / maxQuadSize) * maxQuadSize; - - // Loop over all intersecting cells and add sub-quads - for (double y = yBegin; y < quadBottom; y += maxQuadSize) { - const double y0 = std::max(y, quadTop); - const double y1 = std::min(quadBottom, y + maxQuadSize); - - for (double x = xBegin; x < quadRight; x += maxQuadSize) { - const double x0 = std::max(x, quadLeft); - const double x1 = std::min(quadRight, x + maxQuadSize); - - ret.append(quad.makeSubQuad(x0, y0, x1, y1)); - } - } - } - - return ret; -} - -WindowQuadList WindowQuadList::makeRegularGrid(int xSubdivisions, int ySubdivisions) const -{ - if (empty()) { - return *this; - } - - // Find the bounding rectangle - double left = first().left(); - double right = first().right(); - double top = first().top(); - double bottom = first().bottom(); - - for (const WindowQuad &quad : *this) { - left = std::min(left, quad.left()); - right = std::max(right, quad.right()); - top = std::min(top, quad.top()); - bottom = std::max(bottom, quad.bottom()); - } - - double xIncrement = (right - left) / xSubdivisions; - double yIncrement = (bottom - top) / ySubdivisions; - - WindowQuadList ret; - - for (const WindowQuad &quad : *this) { - const double quadLeft = quad.left(); - const double quadRight = quad.right(); - const double quadTop = quad.top(); - const double quadBottom = quad.bottom(); - - // sanity check, see BUG 390953 - if (quadLeft == quadRight || quadTop == quadBottom) { - ret.append(quad); - continue; - } - - // Compute the top-left corner of the first intersecting grid cell - const double xBegin = left + qFloor((quadLeft - left) / xIncrement) * xIncrement; - const double yBegin = top + qFloor((quadTop - top) / yIncrement) * yIncrement; - - // Loop over all intersecting cells and add sub-quads - for (double y = yBegin; y < quadBottom; y += yIncrement) { - const double y0 = std::max(y, quadTop); - const double y1 = std::min(quadBottom, y + yIncrement); - - for (double x = xBegin; x < quadRight; x += xIncrement) { - const double x0 = std::max(x, quadLeft); - const double x1 = std::min(quadRight, x + xIncrement); - - ret.append(quad.makeSubQuad(x0, y0, x1, y1)); - } - } - } - - return ret; -} - -void RenderGeometry::copy(std::span destination) -{ - Q_ASSERT(int(destination.size()) >= size()); - std::copy(cbegin(), cend(), destination.begin()); -} - -void RenderGeometry::appendWindowVertex(const WindowVertex &windowVertex, qreal deviceScale) -{ - GLVertex2D glVertex; - switch (m_vertexSnappingMode) { - case VertexSnappingMode::None: - glVertex.position = QVector2D(windowVertex.x(), windowVertex.y()) * deviceScale; - break; - case VertexSnappingMode::Round: - glVertex.position = roundVector(QVector2D(windowVertex.x(), windowVertex.y()) * deviceScale); - break; - } - glVertex.texcoord = QVector2D(windowVertex.u(), windowVertex.v()); - append(glVertex); -} - -void RenderGeometry::appendWindowQuad(const WindowQuad &quad, qreal deviceScale) -{ - // Geometry assumes we're rendering triangles, so add the quad's - // vertices as two triangles. Vertex order is top-left, bottom-left, - // top-right followed by top-right, bottom-left, bottom-right. - appendWindowVertex(quad[0], deviceScale); - appendWindowVertex(quad[3], deviceScale); - appendWindowVertex(quad[1], deviceScale); - - appendWindowVertex(quad[1], deviceScale); - appendWindowVertex(quad[3], deviceScale); - appendWindowVertex(quad[2], deviceScale); -} - -void RenderGeometry::appendSubQuad(const WindowQuad &quad, const QRectF &subquad, qreal deviceScale) -{ - std::array vertices; - vertices[0].position = QVector2D(subquad.topLeft()); - vertices[1].position = QVector2D(subquad.topRight()); - vertices[2].position = QVector2D(subquad.bottomRight()); - vertices[3].position = QVector2D(subquad.bottomLeft()); - - const auto deviceQuad = QRectF{QPointF(std::round(quad.left() * deviceScale), std::round(quad.top() * deviceScale)), - QPointF(std::round(quad.right() * deviceScale), std::round(quad.bottom() * deviceScale))}; - - const QPointF origin = deviceQuad.topLeft(); - const QSizeF size = deviceQuad.size(); - -#pragma GCC unroll 4 - for (int i = 0; i < 4; ++i) { - const double weight1 = (vertices[i].position.x() - origin.x()) / size.width(); - const double weight2 = (vertices[i].position.y() - origin.y()) / size.height(); - const double oneMinW1 = 1.0 - weight1; - const double oneMinW2 = 1.0 - weight2; - - const float u = oneMinW1 * oneMinW2 * quad[0].u() + weight1 * oneMinW2 * quad[1].u() - + weight1 * weight2 * quad[2].u() + oneMinW1 * weight2 * quad[3].u(); - const float v = oneMinW1 * oneMinW2 * quad[0].v() + weight1 * oneMinW2 * quad[1].v() - + weight1 * weight2 * quad[2].v() + oneMinW1 * weight2 * quad[3].v(); - vertices[i].texcoord = QVector2D(u, v); - } - - append(vertices[0]); - append(vertices[3]); - append(vertices[1]); - - append(vertices[1]); - append(vertices[3]); - append(vertices[2]); -} - -void RenderGeometry::postProcessTextureCoordinates(const QMatrix4x4 &textureMatrix) -{ - if (!textureMatrix.isIdentity()) { - const QVector2D coeff(textureMatrix(0, 0), textureMatrix(1, 1)); - const QVector2D offset(textureMatrix(0, 3), textureMatrix(1, 3)); - - for (auto &vertex : (*this)) { - vertex.texcoord = vertex.texcoord * coeff + offset; - } - } -} - } // namespace #include "moc_effects.cpp" diff --git a/src/effect/effects.h b/src/effect/effects.h index 0e27655161..a2d3178da2 100644 --- a/src/effect/effects.h +++ b/src/effect/effects.h @@ -13,7 +13,6 @@ #pragma once #include "effect/effect.h" -#include "opengl/glvertexbuffer.h" #include #include @@ -21,8 +20,6 @@ #include #include #include -#include -#include #include #include @@ -80,8 +77,6 @@ class TabletToolId; class Window; class WindowItem; class WindowPropertyNotifyX11Filter; -class WindowQuad; -class WindowQuadList; class WorkspaceScene; class VirtualDesktop; @@ -2067,289 +2062,11 @@ private: Group *m_group; }; -/** - * @short Vertex class - * - * A vertex is one position in a window. WindowQuad consists of four WindowVertex objects - * and represents one part of a window. - */ -class KWIN_EXPORT WindowVertex -{ -public: - WindowVertex(); - WindowVertex(const QPointF &position, const QPointF &textureCoordinate); - WindowVertex(double x, double y, double tx, double ty); - - double x() const - { - return px; - } - double y() const - { - return py; - } - double u() const - { - return tx; - } - double v() const - { - return ty; - } - void move(double x, double y); - void setX(double x); - void setY(double y); - -private: - friend class WindowQuad; - friend class WindowQuadList; - double px, py; // position - double tx, ty; // texture coords -}; - -/** - * @short Class representing one area of a window. - * - * WindowQuads consists of four WindowVertex objects and represents one part of a window. - */ -// NOTE: This class expects the (original) vertices to be in the clockwise order starting from topleft. -class KWIN_EXPORT WindowQuad -{ -public: - WindowQuad(); - WindowQuad makeSubQuad(double x1, double y1, double x2, double y2) const; - WindowVertex &operator[](int index); - const WindowVertex &operator[](int index) const; - double left() const; - double right() const; - double top() const; - double bottom() const; - QRectF bounds() const; - -private: - friend class WindowQuadList; - WindowVertex verts[4]; -}; - -class KWIN_EXPORT WindowQuadList - : public QList -{ -public: - WindowQuadList splitAtX(double x) const; - WindowQuadList splitAtY(double y) const; - WindowQuadList makeGrid(int maxquadsize) const; - WindowQuadList makeRegularGrid(int xSubdivisions, int ySubdivisions) const; -}; - -/** - * A helper class for render geometry in device coordinates. - * - * This mostly represents a vector of vertices, with some convenience methods - * for easily converting from WindowQuad and related classes to lists of - * GLVertex2D. This class assumes rendering happens as unindexed triangles. - */ -class KWIN_EXPORT RenderGeometry : public QList -{ -public: - /** - * In what way should vertices snap to integer device coordinates? - * - * Vertices are converted to device coordinates before being sent to the - * rendering system. Depending on scaling factors, this may lead to device - * coordinates with fractional parts. For some cases, this may not be ideal - * as fractional coordinates need to be interpolated and can lead to - * "blurry" rendering. To avoid that, we can snap the vertices to integer - * device coordinates when they are added. - */ - enum class VertexSnappingMode { - None, //< No rounding, device coordinates containing fractional parts - // are passed directly to the rendering system. - Round, //< Perform a simple rounding, device coordinates will not have - // any fractional parts. - }; - - /** - * The vertex snapping mode to use for this geometry. - * - * By default, this is VertexSnappingMode::Round. - */ - inline VertexSnappingMode vertexSnappingMode() const - { - return m_vertexSnappingMode; - } - /** - * Set the vertex snapping mode to use for this geometry. - * - * Note that this doesn't change vertices retroactively, so you should set - * this before adding any vertices, or clear and rebuild the geometry after - * setting it. - * - * @param mode The new rounding mode. - */ - void setVertexSnappingMode(VertexSnappingMode mode) - { - m_vertexSnappingMode = mode; - } - /** - * Copy geometry data into another buffer. - * - * This is primarily intended for copying into a vertex buffer for rendering. - * - * @param destination The destination buffer. This needs to be at least large - * enough to contain all elements. - */ - void copy(std::span destination); - /** - * Append a WindowVertex as a geometry vertex. - * - * WindowVertex is assumed to be in logical coordinates. It will be converted - * to device coordinates using the specified device scale and then rounded - * so it fits correctly on the device pixel grid. - * - * @param windowVertex The WindowVertex instance to append. - * @param deviceScale The scaling factor to use to go from logical to device - * coordinates. - */ - void appendWindowVertex(const WindowVertex &windowVertex, qreal deviceScale); - /** - * Append a WindowQuad as two triangles. - * - * This will append the corners of the specified WindowQuad in the right - * order so they make two triangles that can be rendered by OpenGL. The - * corners are converted to device coordinates and rounded, just like - * `appendWindowVertex()` does. - * - * @param quad The WindowQuad instance to append. - * @param deviceScale The scaling factor to use to go from logical to device - * coordinates. - */ - void appendWindowQuad(const WindowQuad &quad, qreal deviceScale); - /** - * Append a sub-quad of a WindowQuad as two triangles. - * - * This will append the sub-quad specified by `intersection` as two - * triangles. The quad is expected to be in logical coordinates, while the - * intersection is expected to be in device coordinates. The texture - * coordinates of the resulting vertices are based upon those of the quad, - * using bilinear interpolation for interpolating how much of the original - * texture coordinates to use. - * - * @param quad The WindowQuad instance to use a sub-quad of. - * @param subquad The sub-quad to append. - * @param deviceScale The scaling factor used to convert from logical to - * device coordinates. - */ - void appendSubQuad(const WindowQuad &quad, const QRectF &subquad, qreal deviceScale); - /** - * Modify this geometry's texture coordinates based on a matrix. - * - * This is primarily intended to convert from non-normalised to normalised - * texture coordinates. - * - * @param textureMatrix The texture matrix to use for modifying the - * texture coordinates. Note that only the 2D scale and - * translation are used. - */ - void postProcessTextureCoordinates(const QMatrix4x4 &textureMatrix); - -private: - VertexSnappingMode m_vertexSnappingMode = VertexSnappingMode::Round; -}; - /** * Pointer to the global EffectsHandler object. */ extern KWIN_EXPORT EffectsHandler *effects; -/*************************************************************** - WindowVertex -***************************************************************/ - -inline WindowVertex::WindowVertex() - : px(0) - , py(0) - , tx(0) - , ty(0) -{ -} - -inline WindowVertex::WindowVertex(double _x, double _y, double _tx, double _ty) - : px(_x) - , py(_y) - , tx(_tx) - , ty(_ty) -{ -} - -inline WindowVertex::WindowVertex(const QPointF &position, const QPointF &texturePosition) - : px(position.x()) - , py(position.y()) - , tx(texturePosition.x()) - , ty(texturePosition.y()) -{ -} - -inline void WindowVertex::move(double x, double y) -{ - px = x; - py = y; -} - -inline void WindowVertex::setX(double x) -{ - px = x; -} - -inline void WindowVertex::setY(double y) -{ - py = y; -} - -/*************************************************************** - WindowQuad -***************************************************************/ - -inline WindowQuad::WindowQuad() -{ -} - -inline WindowVertex &WindowQuad::operator[](int index) -{ - Q_ASSERT(index >= 0 && index < 4); - return verts[index]; -} - -inline const WindowVertex &WindowQuad::operator[](int index) const -{ - Q_ASSERT(index >= 0 && index < 4); - return verts[index]; -} - -inline double WindowQuad::left() const -{ - return std::min(verts[0].px, std::min(verts[1].px, std::min(verts[2].px, verts[3].px))); -} - -inline double WindowQuad::right() const -{ - return std::max(verts[0].px, std::max(verts[1].px, std::max(verts[2].px, verts[3].px))); -} - -inline double WindowQuad::top() const -{ - return std::min(verts[0].py, std::min(verts[1].py, std::min(verts[2].py, verts[3].py))); -} - -inline double WindowQuad::bottom() const -{ - return std::max(verts[0].py, std::max(verts[1].py, std::max(verts[2].py, verts[3].py))); -} - -inline QRectF WindowQuad::bounds() const -{ - return QRectF(QPointF(left(), top()), QPointF(right(), bottom())); -} - /*************************************************************** EffectWindow ***************************************************************/ diff --git a/src/effect/offscreeneffect.cpp b/src/effect/offscreeneffect.cpp index 716d57f4f4..5fca683955 100644 --- a/src/effect/offscreeneffect.cpp +++ b/src/effect/offscreeneffect.cpp @@ -8,6 +8,7 @@ #include "core/output.h" #include "core/rendertarget.h" #include "core/renderviewport.h" +#include "effect/effects.h" #include "opengl/gltexture.h" #include "opengl/glutils.h" diff --git a/src/effect/offscreeneffect.h b/src/effect/offscreeneffect.h index f4fa598004..8b518ed3e7 100644 --- a/src/effect/offscreeneffect.h +++ b/src/effect/offscreeneffect.h @@ -6,7 +6,8 @@ #pragma once -#include "effect/effects.h" +#include "effect/effect.h" +#include "scene/itemgeometry.h" namespace KWin { diff --git a/src/group.cpp b/src/group.cpp index 5f795e732a..31711a389a 100644 --- a/src/group.cpp +++ b/src/group.cpp @@ -9,6 +9,7 @@ */ #include "group.h" +#include "effect/effects.h" #include "workspace.h" #include "x11window.h" diff --git a/src/input.cpp b/src/input.cpp index 77f26fe42e..7ef54e2452 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -15,6 +15,7 @@ #include "backends/libinput/device.h" #include "core/inputbackend.h" #include "core/session.h" +#include "effect/effects.h" #include "gestures.h" #include "globalshortcuts.h" #include "hide_cursor_spy.h" diff --git a/src/plugins/fallapart/fallapart.h b/src/plugins/fallapart/fallapart.h index 3e0f01a937..7655cd7657 100644 --- a/src/plugins/fallapart/fallapart.h +++ b/src/plugins/fallapart/fallapart.h @@ -9,6 +9,7 @@ #pragma once +#include "effect/effects.h" #include "effect/offscreeneffect.h" namespace KWin diff --git a/src/plugins/highlightwindow/highlightwindow.cpp b/src/plugins/highlightwindow/highlightwindow.cpp index 780e2d4dca..2afc067993 100644 --- a/src/plugins/highlightwindow/highlightwindow.cpp +++ b/src/plugins/highlightwindow/highlightwindow.cpp @@ -9,6 +9,7 @@ */ #include "highlightwindow.h" +#include "effect/effects.h" #include diff --git a/src/plugins/invert/invert.cpp b/src/plugins/invert/invert.cpp index 6ca4897cd0..8434203f2a 100644 --- a/src/plugins/invert/invert.cpp +++ b/src/plugins/invert/invert.cpp @@ -10,6 +10,7 @@ #include "invert.h" +#include "effect/effects.h" #include "opengl/glplatform.h" #include "opengl/glutils.h" #include diff --git a/src/plugins/magiclamp/magiclamp.h b/src/plugins/magiclamp/magiclamp.h index b59b3aa036..22bc43608c 100644 --- a/src/plugins/magiclamp/magiclamp.h +++ b/src/plugins/magiclamp/magiclamp.h @@ -9,6 +9,7 @@ #pragma once +#include "effect/effects.h" #include "effect/offscreeneffect.h" #include "effect/timeline.h" diff --git a/src/plugins/screencast/windowscreencastsource.cpp b/src/plugins/screencast/windowscreencastsource.cpp index e9d8f63925..caa35382b0 100644 --- a/src/plugins/screencast/windowscreencastsource.cpp +++ b/src/plugins/screencast/windowscreencastsource.cpp @@ -12,6 +12,7 @@ #include "core/renderloop.h" #include "core/rendertarget.h" #include "core/renderviewport.h" +#include "effect/effect.h" #include "opengl/gltexture.h" #include "opengl/glutils.h" #include "scene/itemrenderer.h" diff --git a/src/plugins/wobblywindows/wobblywindows.cpp b/src/plugins/wobblywindows/wobblywindows.cpp index 1cdd49e1d1..d8522c5035 100644 --- a/src/plugins/wobblywindows/wobblywindows.cpp +++ b/src/plugins/wobblywindows/wobblywindows.cpp @@ -8,6 +8,7 @@ */ #include "wobblywindows.h" +#include "effect/effects.h" #include "wobblywindowsconfig.h" #include diff --git a/src/pointer_input.cpp b/src/pointer_input.cpp index 0dddf6039a..dadcbf5d2e 100644 --- a/src/pointer_input.cpp +++ b/src/pointer_input.cpp @@ -16,6 +16,7 @@ #include "core/output.h" #include "cursorsource.h" #include "decorations/decoratedclient.h" +#include "effect/effects.h" #include "input_event.h" #include "input_event_spy.h" #include "mousebuttons.h" diff --git a/src/scene/cursoritem.cpp b/src/scene/cursoritem.cpp index 96011941c0..014f5516a1 100644 --- a/src/scene/cursoritem.cpp +++ b/src/scene/cursoritem.cpp @@ -7,6 +7,7 @@ #include "scene/cursoritem.h" #include "cursor.h" #include "cursorsource.h" +#include "effect/effect.h" #include "scene/imageitem.h" #include "scene/itemrenderer.h" #include "scene/scene.h" diff --git a/src/scene/cursoritem.h b/src/scene/cursoritem.h index 810f3f6542..ca934c1107 100644 --- a/src/scene/cursoritem.h +++ b/src/scene/cursoritem.h @@ -12,6 +12,7 @@ namespace KWin { class ImageItem; +class SurfaceInterface; class SurfaceItemWayland; class CursorItem : public Item diff --git a/src/scene/cursorscene.cpp b/src/scene/cursorscene.cpp index 44c04bca5b..6f3ba58f57 100644 --- a/src/scene/cursorscene.cpp +++ b/src/scene/cursorscene.cpp @@ -8,6 +8,7 @@ #include "core/output.h" #include "core/rendertarget.h" #include "core/renderviewport.h" +#include "effect/effect.h" #include "scene/cursoritem.h" #include "scene/itemrenderer.h" diff --git a/src/scene/dndiconitem.h b/src/scene/dndiconitem.h index 0ac4e97e13..0e0f08b43f 100644 --- a/src/scene/dndiconitem.h +++ b/src/scene/dndiconitem.h @@ -14,6 +14,7 @@ namespace KWin { class DragAndDropIcon; +class SurfaceInterface; class SurfaceItemWayland; class PresentationFeedback; diff --git a/src/scene/imageitem.h b/src/scene/imageitem.h index fde467e7e0..40e7967cf9 100644 --- a/src/scene/imageitem.h +++ b/src/scene/imageitem.h @@ -8,6 +8,8 @@ #include "scene/item.h" +#include + namespace KWin { diff --git a/src/scene/item.h b/src/scene/item.h index e0ad6c8a53..853c8f386d 100644 --- a/src/scene/item.h +++ b/src/scene/item.h @@ -7,8 +7,7 @@ #pragma once #include "core/colorspace.h" -#include "effect/effects.h" -#include "effect/globals.h" +#include "scene/itemgeometry.h" #include #include diff --git a/src/scene/itemgeometry.cpp b/src/scene/itemgeometry.cpp new file mode 100644 index 0000000000..414c4d50de --- /dev/null +++ b/src/scene/itemgeometry.cpp @@ -0,0 +1,301 @@ +/* + SPDX-FileCopyrightText: 2006 Lubos Lunak + SPDX-FileCopyrightText: 2022 Arjen Hiemstra + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#include "scene/itemgeometry.h" +#include "effect/globals.h" + +#include + +namespace KWin +{ + +WindowQuad WindowQuad::makeSubQuad(double x1, double y1, double x2, double y2) const +{ + Q_ASSERT(x1 < x2 && y1 < y2 && x1 >= left() && x2 <= right() && y1 >= top() && y2 <= bottom()); + WindowQuad ret(*this); + // vertices are clockwise starting from topleft + ret.verts[0].px = x1; + ret.verts[3].px = x1; + ret.verts[1].px = x2; + ret.verts[2].px = x2; + ret.verts[0].py = y1; + ret.verts[1].py = y1; + ret.verts[2].py = y2; + ret.verts[3].py = y2; + + const double xOrigin = left(); + const double yOrigin = top(); + + const double widthReciprocal = 1 / (right() - xOrigin); + const double heightReciprocal = 1 / (bottom() - yOrigin); + + for (int i = 0; i < 4; ++i) { + const double w1 = (ret.verts[i].px - xOrigin) * widthReciprocal; + const double w2 = (ret.verts[i].py - yOrigin) * heightReciprocal; + + // Use bilinear interpolation to compute the texture coords. + ret.verts[i].tx = (1 - w1) * (1 - w2) * verts[0].tx + w1 * (1 - w2) * verts[1].tx + w1 * w2 * verts[2].tx + (1 - w1) * w2 * verts[3].tx; + ret.verts[i].ty = (1 - w1) * (1 - w2) * verts[0].ty + w1 * (1 - w2) * verts[1].ty + w1 * w2 * verts[2].ty + (1 - w1) * w2 * verts[3].ty; + } + + return ret; +} + +WindowQuadList WindowQuadList::splitAtX(double x) const +{ + WindowQuadList ret; + ret.reserve(count()); + for (const WindowQuad &quad : *this) { + bool wholeleft = true; + bool wholeright = true; + for (int i = 0; i < 4; ++i) { + if (quad[i].x() < x) { + wholeright = false; + } + if (quad[i].x() > x) { + wholeleft = false; + } + } + if (wholeleft || wholeright) { // is whole in one split part + ret.append(quad); + continue; + } + if (quad.top() == quad.bottom() || quad.left() == quad.right()) { // quad has no size + ret.append(quad); + continue; + } + ret.append(quad.makeSubQuad(quad.left(), quad.top(), x, quad.bottom())); + ret.append(quad.makeSubQuad(x, quad.top(), quad.right(), quad.bottom())); + } + return ret; +} + +WindowQuadList WindowQuadList::splitAtY(double y) const +{ + WindowQuadList ret; + ret.reserve(count()); + for (const WindowQuad &quad : *this) { + bool wholetop = true; + bool wholebottom = true; + for (int i = 0; i < 4; ++i) { + if (quad[i].y() < y) { + wholebottom = false; + } + if (quad[i].y() > y) { + wholetop = false; + } + } + if (wholetop || wholebottom) { // is whole in one split part + ret.append(quad); + continue; + } + if (quad.top() == quad.bottom() || quad.left() == quad.right()) { // quad has no size + ret.append(quad); + continue; + } + ret.append(quad.makeSubQuad(quad.left(), quad.top(), quad.right(), y)); + ret.append(quad.makeSubQuad(quad.left(), y, quad.right(), quad.bottom())); + } + return ret; +} + +WindowQuadList WindowQuadList::makeGrid(int maxQuadSize) const +{ + if (empty()) { + return *this; + } + + // Find the bounding rectangle + double left = first().left(); + double right = first().right(); + double top = first().top(); + double bottom = first().bottom(); + + for (const WindowQuad &quad : std::as_const(*this)) { + left = std::min(left, quad.left()); + right = std::max(right, quad.right()); + top = std::min(top, quad.top()); + bottom = std::max(bottom, quad.bottom()); + } + + WindowQuadList ret; + + for (const WindowQuad &quad : std::as_const(*this)) { + const double quadLeft = quad.left(); + const double quadRight = quad.right(); + const double quadTop = quad.top(); + const double quadBottom = quad.bottom(); + + // sanity check, see BUG 390953 + if (quadLeft == quadRight || quadTop == quadBottom) { + ret.append(quad); + continue; + } + + // Compute the top-left corner of the first intersecting grid cell + const double xBegin = left + qFloor((quadLeft - left) / maxQuadSize) * maxQuadSize; + const double yBegin = top + qFloor((quadTop - top) / maxQuadSize) * maxQuadSize; + + // Loop over all intersecting cells and add sub-quads + for (double y = yBegin; y < quadBottom; y += maxQuadSize) { + const double y0 = std::max(y, quadTop); + const double y1 = std::min(quadBottom, y + maxQuadSize); + + for (double x = xBegin; x < quadRight; x += maxQuadSize) { + const double x0 = std::max(x, quadLeft); + const double x1 = std::min(quadRight, x + maxQuadSize); + + ret.append(quad.makeSubQuad(x0, y0, x1, y1)); + } + } + } + + return ret; +} + +WindowQuadList WindowQuadList::makeRegularGrid(int xSubdivisions, int ySubdivisions) const +{ + if (empty()) { + return *this; + } + + // Find the bounding rectangle + double left = first().left(); + double right = first().right(); + double top = first().top(); + double bottom = first().bottom(); + + for (const WindowQuad &quad : *this) { + left = std::min(left, quad.left()); + right = std::max(right, quad.right()); + top = std::min(top, quad.top()); + bottom = std::max(bottom, quad.bottom()); + } + + double xIncrement = (right - left) / xSubdivisions; + double yIncrement = (bottom - top) / ySubdivisions; + + WindowQuadList ret; + + for (const WindowQuad &quad : *this) { + const double quadLeft = quad.left(); + const double quadRight = quad.right(); + const double quadTop = quad.top(); + const double quadBottom = quad.bottom(); + + // sanity check, see BUG 390953 + if (quadLeft == quadRight || quadTop == quadBottom) { + ret.append(quad); + continue; + } + + // Compute the top-left corner of the first intersecting grid cell + const double xBegin = left + qFloor((quadLeft - left) / xIncrement) * xIncrement; + const double yBegin = top + qFloor((quadTop - top) / yIncrement) * yIncrement; + + // Loop over all intersecting cells and add sub-quads + for (double y = yBegin; y < quadBottom; y += yIncrement) { + const double y0 = std::max(y, quadTop); + const double y1 = std::min(quadBottom, y + yIncrement); + + for (double x = xBegin; x < quadRight; x += xIncrement) { + const double x0 = std::max(x, quadLeft); + const double x1 = std::min(quadRight, x + xIncrement); + + ret.append(quad.makeSubQuad(x0, y0, x1, y1)); + } + } + } + + return ret; +} + +void RenderGeometry::copy(std::span destination) +{ + Q_ASSERT(int(destination.size()) >= size()); + std::copy(cbegin(), cend(), destination.begin()); +} + +void RenderGeometry::appendWindowVertex(const WindowVertex &windowVertex, qreal deviceScale) +{ + GLVertex2D glVertex; + switch (m_vertexSnappingMode) { + case VertexSnappingMode::None: + glVertex.position = QVector2D(windowVertex.x(), windowVertex.y()) * deviceScale; + break; + case VertexSnappingMode::Round: + glVertex.position = roundVector(QVector2D(windowVertex.x(), windowVertex.y()) * deviceScale); + break; + } + glVertex.texcoord = QVector2D(windowVertex.u(), windowVertex.v()); + append(glVertex); +} + +void RenderGeometry::appendWindowQuad(const WindowQuad &quad, qreal deviceScale) +{ + // Geometry assumes we're rendering triangles, so add the quad's + // vertices as two triangles. Vertex order is top-left, bottom-left, + // top-right followed by top-right, bottom-left, bottom-right. + appendWindowVertex(quad[0], deviceScale); + appendWindowVertex(quad[3], deviceScale); + appendWindowVertex(quad[1], deviceScale); + + appendWindowVertex(quad[1], deviceScale); + appendWindowVertex(quad[3], deviceScale); + appendWindowVertex(quad[2], deviceScale); +} + +void RenderGeometry::appendSubQuad(const WindowQuad &quad, const QRectF &subquad, qreal deviceScale) +{ + std::array vertices; + vertices[0].position = QVector2D(subquad.topLeft()); + vertices[1].position = QVector2D(subquad.topRight()); + vertices[2].position = QVector2D(subquad.bottomRight()); + vertices[3].position = QVector2D(subquad.bottomLeft()); + + const auto deviceQuad = QRectF{QPointF(std::round(quad.left() * deviceScale), std::round(quad.top() * deviceScale)), + QPointF(std::round(quad.right() * deviceScale), std::round(quad.bottom() * deviceScale))}; + + const QPointF origin = deviceQuad.topLeft(); + const QSizeF size = deviceQuad.size(); + +#pragma GCC unroll 4 + for (int i = 0; i < 4; ++i) { + const double weight1 = (vertices[i].position.x() - origin.x()) / size.width(); + const double weight2 = (vertices[i].position.y() - origin.y()) / size.height(); + const double oneMinW1 = 1.0 - weight1; + const double oneMinW2 = 1.0 - weight2; + + const float u = oneMinW1 * oneMinW2 * quad[0].u() + weight1 * oneMinW2 * quad[1].u() + + weight1 * weight2 * quad[2].u() + oneMinW1 * weight2 * quad[3].u(); + const float v = oneMinW1 * oneMinW2 * quad[0].v() + weight1 * oneMinW2 * quad[1].v() + + weight1 * weight2 * quad[2].v() + oneMinW1 * weight2 * quad[3].v(); + vertices[i].texcoord = QVector2D(u, v); + } + + append(vertices[0]); + append(vertices[3]); + append(vertices[1]); + + append(vertices[1]); + append(vertices[3]); + append(vertices[2]); +} + +void RenderGeometry::postProcessTextureCoordinates(const QMatrix4x4 &textureMatrix) +{ + if (!textureMatrix.isIdentity()) { + const QVector2D coeff(textureMatrix(0, 0), textureMatrix(1, 1)); + const QVector2D offset(textureMatrix(0, 3), textureMatrix(1, 3)); + + for (auto &vertex : (*this)) { + vertex.texcoord = vertex.texcoord * coeff + offset; + } + } +} + +} // namespace KWin diff --git a/src/scene/itemgeometry.h b/src/scene/itemgeometry.h new file mode 100644 index 0000000000..1fa9dc4472 --- /dev/null +++ b/src/scene/itemgeometry.h @@ -0,0 +1,285 @@ +/* + SPDX-FileCopyrightText: 2006 Lubos Lunak + SPDX-FileCopyrightText: 2022 Arjen Hiemstra + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#pragma once + +#include "opengl/glvertexbuffer.h" + +namespace KWin +{ + +/** + * @short Vertex class + * + * A vertex is one position in a window. WindowQuad consists of four WindowVertex objects + * and represents one part of a window. + */ +class KWIN_EXPORT WindowVertex +{ +public: + WindowVertex(); + WindowVertex(const QPointF &position, const QPointF &textureCoordinate); + WindowVertex(double x, double y, double tx, double ty); + + double x() const + { + return px; + } + double y() const + { + return py; + } + double u() const + { + return tx; + } + double v() const + { + return ty; + } + void move(double x, double y); + void setX(double x); + void setY(double y); + +private: + friend class WindowQuad; + friend class WindowQuadList; + double px, py; // position + double tx, ty; // texture coords +}; + +/** + * @short Class representing one area of a window. + * + * WindowQuads consists of four WindowVertex objects and represents one part of a window. + */ +// NOTE: This class expects the (original) vertices to be in the clockwise order starting from topleft. +class KWIN_EXPORT WindowQuad +{ +public: + WindowQuad(); + WindowQuad makeSubQuad(double x1, double y1, double x2, double y2) const; + WindowVertex &operator[](int index); + const WindowVertex &operator[](int index) const; + double left() const; + double right() const; + double top() const; + double bottom() const; + QRectF bounds() const; + +private: + friend class WindowQuadList; + WindowVertex verts[4]; +}; + +class KWIN_EXPORT WindowQuadList + : public QList +{ +public: + WindowQuadList splitAtX(double x) const; + WindowQuadList splitAtY(double y) const; + WindowQuadList makeGrid(int maxquadsize) const; + WindowQuadList makeRegularGrid(int xSubdivisions, int ySubdivisions) const; +}; + +/** + * A helper class for render geometry in device coordinates. + * + * This mostly represents a vector of vertices, with some convenience methods + * for easily converting from WindowQuad and related classes to lists of + * GLVertex2D. This class assumes rendering happens as unindexed triangles. + */ +class KWIN_EXPORT RenderGeometry : public QList +{ +public: + /** + * In what way should vertices snap to integer device coordinates? + * + * Vertices are converted to device coordinates before being sent to the + * rendering system. Depending on scaling factors, this may lead to device + * coordinates with fractional parts. For some cases, this may not be ideal + * as fractional coordinates need to be interpolated and can lead to + * "blurry" rendering. To avoid that, we can snap the vertices to integer + * device coordinates when they are added. + */ + enum class VertexSnappingMode { + None, //< No rounding, device coordinates containing fractional parts + // are passed directly to the rendering system. + Round, //< Perform a simple rounding, device coordinates will not have + // any fractional parts. + }; + + /** + * The vertex snapping mode to use for this geometry. + * + * By default, this is VertexSnappingMode::Round. + */ + inline VertexSnappingMode vertexSnappingMode() const + { + return m_vertexSnappingMode; + } + /** + * Set the vertex snapping mode to use for this geometry. + * + * Note that this doesn't change vertices retroactively, so you should set + * this before adding any vertices, or clear and rebuild the geometry after + * setting it. + * + * @param mode The new rounding mode. + */ + void setVertexSnappingMode(VertexSnappingMode mode) + { + m_vertexSnappingMode = mode; + } + /** + * Copy geometry data into another buffer. + * + * This is primarily intended for copying into a vertex buffer for rendering. + * + * @param destination The destination buffer. This needs to be at least large + * enough to contain all elements. + */ + void copy(std::span destination); + /** + * Append a WindowVertex as a geometry vertex. + * + * WindowVertex is assumed to be in logical coordinates. It will be converted + * to device coordinates using the specified device scale and then rounded + * so it fits correctly on the device pixel grid. + * + * @param windowVertex The WindowVertex instance to append. + * @param deviceScale The scaling factor to use to go from logical to device + * coordinates. + */ + void appendWindowVertex(const WindowVertex &windowVertex, qreal deviceScale); + /** + * Append a WindowQuad as two triangles. + * + * This will append the corners of the specified WindowQuad in the right + * order so they make two triangles that can be rendered by OpenGL. The + * corners are converted to device coordinates and rounded, just like + * `appendWindowVertex()` does. + * + * @param quad The WindowQuad instance to append. + * @param deviceScale The scaling factor to use to go from logical to device + * coordinates. + */ + void appendWindowQuad(const WindowQuad &quad, qreal deviceScale); + /** + * Append a sub-quad of a WindowQuad as two triangles. + * + * This will append the sub-quad specified by `intersection` as two + * triangles. The quad is expected to be in logical coordinates, while the + * intersection is expected to be in device coordinates. The texture + * coordinates of the resulting vertices are based upon those of the quad, + * using bilinear interpolation for interpolating how much of the original + * texture coordinates to use. + * + * @param quad The WindowQuad instance to use a sub-quad of. + * @param subquad The sub-quad to append. + * @param deviceScale The scaling factor used to convert from logical to + * device coordinates. + */ + void appendSubQuad(const WindowQuad &quad, const QRectF &subquad, qreal deviceScale); + /** + * Modify this geometry's texture coordinates based on a matrix. + * + * This is primarily intended to convert from non-normalised to normalised + * texture coordinates. + * + * @param textureMatrix The texture matrix to use for modifying the + * texture coordinates. Note that only the 2D scale and + * translation are used. + */ + void postProcessTextureCoordinates(const QMatrix4x4 &textureMatrix); + +private: + VertexSnappingMode m_vertexSnappingMode = VertexSnappingMode::Round; +}; + +inline WindowVertex::WindowVertex() + : px(0) + , py(0) + , tx(0) + , ty(0) +{ +} + +inline WindowVertex::WindowVertex(double _x, double _y, double _tx, double _ty) + : px(_x) + , py(_y) + , tx(_tx) + , ty(_ty) +{ +} + +inline WindowVertex::WindowVertex(const QPointF &position, const QPointF &texturePosition) + : px(position.x()) + , py(position.y()) + , tx(texturePosition.x()) + , ty(texturePosition.y()) +{ +} + +inline void WindowVertex::move(double x, double y) +{ + px = x; + py = y; +} + +inline void WindowVertex::setX(double x) +{ + px = x; +} + +inline void WindowVertex::setY(double y) +{ + py = y; +} + +inline WindowQuad::WindowQuad() +{ +} + +inline WindowVertex &WindowQuad::operator[](int index) +{ + Q_ASSERT(index >= 0 && index < 4); + return verts[index]; +} + +inline const WindowVertex &WindowQuad::operator[](int index) const +{ + Q_ASSERT(index >= 0 && index < 4); + return verts[index]; +} + +inline double WindowQuad::left() const +{ + return std::min(verts[0].px, std::min(verts[1].px, std::min(verts[2].px, verts[3].px))); +} + +inline double WindowQuad::right() const +{ + return std::max(verts[0].px, std::max(verts[1].px, std::max(verts[2].px, verts[3].px))); +} + +inline double WindowQuad::top() const +{ + return std::min(verts[0].py, std::min(verts[1].py, std::min(verts[2].py, verts[3].py))); +} + +inline double WindowQuad::bottom() const +{ + return std::max(verts[0].py, std::max(verts[1].py, std::max(verts[2].py, verts[3].py))); +} + +inline QRectF WindowQuad::bounds() const +{ + return QRectF(QPointF(left(), top()), QPointF(right(), bottom())); +} + +} // namespace KWin diff --git a/src/scene/itemrenderer_qpainter.cpp b/src/scene/itemrenderer_qpainter.cpp index 0fb8276569..b4ea3e0a88 100644 --- a/src/scene/itemrenderer_qpainter.cpp +++ b/src/scene/itemrenderer_qpainter.cpp @@ -6,6 +6,7 @@ #include "scene/itemrenderer_qpainter.h" #include "core/renderviewport.h" +#include "effect/effect.h" #include "platformsupport/scenes/qpainter/qpaintersurfacetexture.h" #include "scene/imageitem.h" #include "scene/workspacescene_qpainter.h" diff --git a/src/scene/windowitem.cpp b/src/scene/windowitem.cpp index ba09efa1df..e3ddfc623e 100644 --- a/src/scene/windowitem.cpp +++ b/src/scene/windowitem.cpp @@ -5,6 +5,7 @@ */ #include "scene/windowitem.h" +#include "effect/effects.h" #include "internalwindow.h" #include "scene/decorationitem.h" #include "scene/shadowitem.h" diff --git a/src/scripting/scriptedeffect.cpp b/src/scripting/scriptedeffect.cpp index c83a66af8b..7a8e194042 100644 --- a/src/scripting/scriptedeffect.cpp +++ b/src/scripting/scriptedeffect.cpp @@ -14,6 +14,7 @@ #include "workspace_wrapper.h" #include "core/output.h" +#include "effect/effects.h" #include "input.h" #include "screenedge.h" #include "workspace.h" diff --git a/src/scripting/scriptedeffect.h b/src/scripting/scriptedeffect.h index 88144c5ef9..8ef3157976 100644 --- a/src/scripting/scriptedeffect.h +++ b/src/scripting/scriptedeffect.h @@ -18,6 +18,8 @@ class KConfigLoader; class KPluginMetaData; +class QAction; + namespace KWin { diff --git a/src/scripting/windowthumbnailitem.cpp b/src/scripting/windowthumbnailitem.cpp index 2b09a25c14..2fc2380a73 100644 --- a/src/scripting/windowthumbnailitem.cpp +++ b/src/scripting/windowthumbnailitem.cpp @@ -11,6 +11,7 @@ #include "core/renderbackend.h" #include "core/rendertarget.h" #include "core/renderviewport.h" +#include "effect/effect.h" #include "scene/itemrenderer.h" #include "scene/windowitem.h" #include "scene/workspacescene.h"