diff --git a/scene_opengl.h b/scene_opengl.h --- a/scene_opengl.h +++ b/scene_opengl.h @@ -100,6 +100,7 @@ virtual void doPaintBackground(const QVector &vertices) = 0; virtual void updateProjectionMatrix() = 0; + virtual void paintCursor(); Q_SIGNALS: void resetCompositing(); @@ -137,6 +138,7 @@ virtual Scene::Window *createWindow(Toplevel *t); virtual void finalDrawWindow(EffectWindowImpl* w, int mask, QRegion region, WindowPaintData& data); virtual void updateProjectionMatrix() override; + virtual void paintCursor() override; private Q_SLOTS: void resetLanczosFilter(); @@ -147,6 +149,7 @@ private: LanczosFilter *m_lanczosFilter; + QScopedPointer m_cursorTexture; QMatrix4x4 m_projectionMatrix; QMatrix4x4 m_screenProjectionMatrix; GLuint vao; diff --git a/scene_opengl.cpp b/scene_opengl.cpp --- a/scene_opengl.cpp +++ b/scene_opengl.cpp @@ -42,6 +42,7 @@ #include "main.h" #include "overlaywindow.h" #include "screens.h" +#include "cursor.h" #include "decorations/decoratedclient.h" #include @@ -678,6 +679,62 @@ } } +void SceneOpenGL::paintCursor() +{ + +} + +/** + * Render cursor texture in case hardware cursor is disabled. + * Useful for screen recording apps or backends that can't do planes. + */ +void SceneOpenGL2::paintCursor() +{ + // don't paint if we use hardware cursor + if (!kwinApp()->platform()->usesSoftwareCursor()) { + return; + } + + // lazy init texture cursor only in case we need software rendering + if (!m_cursorTexture) { + auto updateCursorTexture = [this] { + // don't paint if no image for cursor is set + const QImage img = kwinApp()->platform()->softwareCursor(); + if (img.isNull()) { + return; + } + m_cursorTexture.reset(new GLTexture(img)); + }; + + // init now + updateCursorTexture(); + + // handle shape update on case cursor image changed + connect(Cursor::self(), &Cursor::cursorChanged, this, updateCursorTexture); + } + + // get cursor position in projection coordinates + const QPoint cursorPos = Cursor::pos() - kwinApp()->platform()->softwareCursorHotspot(); + const QRect cursorRect(0, 0, m_cursorTexture->width(), m_cursorTexture->height()); + QMatrix4x4 mvp = m_projectionMatrix; + mvp.translate(cursorPos.x(), cursorPos.y()); + + // handle transparence + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + // paint texture in cursor offset + m_cursorTexture->bind(); + ShaderBinder binder(ShaderTrait::MapTexture); + binder.shader()->setUniform(GLShader::ModelViewProjectionMatrix, mvp); + m_cursorTexture->render(QRegion(cursorRect), cursorRect); + m_cursorTexture->unbind(); + + kwinApp()->platform()->markCursorAsRendered(); + + glDisable(GL_BLEND); +} + qint64 SceneOpenGL::paint(QRegion damage, ToplevelList toplevels) { // actually paint the frame, flushed with the NEXT frame @@ -711,6 +768,7 @@ int mask = 0; updateProjectionMatrix(); paintScreen(&mask, damage.intersected(geo), repaint, &update, &valid, projectionMatrix(), geo); // call generic implementation + paintCursor(); GLVertexBuffer::streamingBuffer()->endOfFrame();