File indexing completed on 2024-02-25 17:23:29

0001 /*
0002     SPDX-FileCopyrightText: 2022 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "cursorsource.h"
0008 #include "cursor.h"
0009 #include "wayland/clientconnection.h"
0010 #include "wayland/shmclientbuffer.h"
0011 #include "wayland/surface_interface.h"
0012 
0013 namespace KWin
0014 {
0015 
0016 CursorSource::CursorSource(QObject *parent)
0017     : QObject(parent)
0018 {
0019 }
0020 
0021 QImage CursorSource::image() const
0022 {
0023     return m_image;
0024 }
0025 
0026 QSize CursorSource::size() const
0027 {
0028     return m_size;
0029 }
0030 
0031 QPoint CursorSource::hotspot() const
0032 {
0033     return m_hotspot;
0034 }
0035 
0036 ImageCursorSource::ImageCursorSource(QObject *parent)
0037     : CursorSource(parent)
0038 {
0039 }
0040 
0041 void ImageCursorSource::update(const QImage &image, const QPoint &hotspot)
0042 {
0043     m_image = image;
0044     m_size = image.size() / image.devicePixelRatio();
0045     m_hotspot = hotspot;
0046     Q_EMIT changed();
0047 }
0048 
0049 ShapeCursorSource::ShapeCursorSource(QObject *parent)
0050     : CursorSource(parent)
0051 {
0052     m_delayTimer.setSingleShot(true);
0053     connect(&m_delayTimer, &QTimer::timeout, this, &ShapeCursorSource::selectNextSprite);
0054 }
0055 
0056 QByteArray ShapeCursorSource::shape() const
0057 {
0058     return m_shape;
0059 }
0060 
0061 void ShapeCursorSource::setShape(const QByteArray &shape)
0062 {
0063     if (m_shape != shape) {
0064         m_shape = shape;
0065         refresh();
0066     }
0067 }
0068 
0069 void ShapeCursorSource::setShape(Qt::CursorShape shape)
0070 {
0071     setShape(CursorShape(shape).name());
0072 }
0073 
0074 KXcursorTheme ShapeCursorSource::theme() const
0075 {
0076     return m_theme;
0077 }
0078 
0079 void ShapeCursorSource::setTheme(const KXcursorTheme &theme)
0080 {
0081     if (m_theme != theme) {
0082         m_theme = theme;
0083         refresh();
0084     }
0085 }
0086 
0087 void ShapeCursorSource::refresh()
0088 {
0089     m_currentSprite = -1;
0090     m_delayTimer.stop();
0091 
0092     m_sprites = m_theme.shape(m_shape);
0093     if (m_sprites.isEmpty()) {
0094         const auto alternativeNames = Cursor::cursorAlternativeNames(m_shape);
0095         for (const QByteArray &alternativeName : alternativeNames) {
0096             m_sprites = m_theme.shape(alternativeName);
0097             if (!m_sprites.isEmpty()) {
0098                 break;
0099             }
0100         }
0101     }
0102 
0103     if (!m_sprites.isEmpty()) {
0104         selectSprite(0);
0105     }
0106 }
0107 
0108 void ShapeCursorSource::selectNextSprite()
0109 {
0110     selectSprite((m_currentSprite + 1) % m_sprites.size());
0111 }
0112 
0113 void ShapeCursorSource::selectSprite(int index)
0114 {
0115     if (m_currentSprite == index) {
0116         return;
0117     }
0118     const KXcursorSprite &sprite = m_sprites[index];
0119     m_currentSprite = index;
0120     m_image = sprite.data();
0121     m_size = m_image.size() / m_image.devicePixelRatio();
0122     m_hotspot = sprite.hotspot();
0123     if (sprite.delay().count() && m_sprites.size() > 1) {
0124         m_delayTimer.start(sprite.delay());
0125     }
0126     Q_EMIT changed();
0127 }
0128 
0129 SurfaceCursorSource::SurfaceCursorSource(QObject *parent)
0130     : CursorSource(parent)
0131 {
0132 }
0133 
0134 KWaylandServer::SurfaceInterface *SurfaceCursorSource::surface() const
0135 {
0136     return m_surface;
0137 }
0138 
0139 void SurfaceCursorSource::update(KWaylandServer::SurfaceInterface *surface, const QPoint &hotspot)
0140 {
0141     if (!surface) {
0142         m_image = QImage();
0143         m_size = QSize(0, 0);
0144         m_hotspot = QPoint();
0145         m_surface = nullptr;
0146     } else {
0147         // TODO Plasma 6: once Xwayland cursor scaling can be done correctly, remove this
0148         // scaling is intentionally applied "wrong" here to make the cursor stay a consistent size even with un-scaled Xwayland:
0149         // - the device pixel ratio of the image is not multiplied by scaleOverride
0150         // - the surface size is scaled up with scaleOverride, to un-do the scaling done elsewhere
0151         auto buffer = qobject_cast<KWaylandServer::ShmClientBuffer *>(surface->buffer());
0152         if (buffer) {
0153             m_image = buffer->data().copy();
0154             m_image.setDevicePixelRatio(surface->bufferScale());
0155         } else {
0156             m_image = QImage();
0157         }
0158         m_size = (surface->size() * surface->client()->scaleOverride()).toSize();
0159         m_hotspot = hotspot;
0160         m_surface = surface;
0161     }
0162     Q_EMIT changed();
0163 }
0164 
0165 } // namespace KWin