File indexing completed on 2024-12-01 13:37:21

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 #pragma once
0010 // kwin
0011 #include <kwinglobals.h>
0012 // Qt
0013 #include <QHash>
0014 #include <QObject>
0015 #include <QPoint>
0016 // KF
0017 #include <KSharedConfig>
0018 // xcb
0019 #include <xcb/xcb.h>
0020 
0021 class QTimer;
0022 
0023 namespace KWin
0024 {
0025 
0026 class CursorSource;
0027 class Output;
0028 
0029 namespace ExtendedCursor
0030 {
0031 /**
0032  * Extension of Qt::CursorShape with values not currently present there
0033  */
0034 enum Shape {
0035     SizeNorthWest = 0x100 + 0,
0036     SizeNorth = 0x100 + 1,
0037     SizeNorthEast = 0x100 + 2,
0038     SizeEast = 0x100 + 3,
0039     SizeWest = 0x100 + 4,
0040     SizeSouthEast = 0x100 + 5,
0041     SizeSouth = 0x100 + 6,
0042     SizeSouthWest = 0x100 + 7
0043 };
0044 }
0045 
0046 /**
0047  * @brief Wrapper round Qt::CursorShape with extensions enums into a single entity
0048  */
0049 class KWIN_EXPORT CursorShape
0050 {
0051 public:
0052     CursorShape() = default;
0053     CursorShape(Qt::CursorShape qtShape)
0054     {
0055         m_shape = qtShape;
0056     }
0057     CursorShape(KWin::ExtendedCursor::Shape kwinShape)
0058     {
0059         m_shape = kwinShape;
0060     }
0061     bool operator==(const CursorShape &o) const
0062     {
0063         return m_shape == o.m_shape;
0064     }
0065     operator int() const
0066     {
0067         return m_shape;
0068     }
0069     /**
0070      * @brief The name of a cursor shape in the theme.
0071      */
0072     QByteArray name() const;
0073 
0074 private:
0075     int m_shape = Qt::ArrowCursor;
0076 };
0077 
0078 /**
0079  * @short Replacement for QCursor.
0080  *
0081  * This class provides a similar API to QCursor and should be preferred inside KWin. It allows to
0082  * get the position and warp the mouse cursor with static methods just like QCursor. It also provides
0083  * the possibility to get an X11 cursor for a Qt::CursorShape - a functionality lost in Qt 5's QCursor
0084  * implementation.
0085  *
0086  * In addition the class provides a mouse polling facility as required by e.g. Effects and ScreenEdges
0087  * and emits signals when the mouse position changes. In opposite to QCursor this class is a QObject
0088  * and cannot be constructed. Instead it provides a singleton getter, though the most important
0089  * methods are wrapped in a static method, just like QCursor.
0090  *
0091  * The actual implementation is split into two parts: a system independent interface and a windowing
0092  * system specific subclass. So far only an X11 backend is implemented which uses query pointer to
0093  * fetch the position and warp pointer to set the position. It uses a timer based mouse polling and
0094  * can provide X11 cursors through the XCursor library.
0095  */
0096 class KWIN_EXPORT Cursor : public QObject
0097 {
0098     Q_OBJECT
0099 public:
0100     Cursor(QObject *parent);
0101     ~Cursor() override;
0102     void startMousePolling();
0103     void stopMousePolling();
0104     /**
0105      * @brief Enables tracking changes of cursor images.
0106      *
0107      * After enabling cursor change tracking the signal cursorChanged will be emitted
0108      * whenever a change to the cursor image is recognized.
0109      *
0110      * Use stopCursorTracking to no longer emit this signal. Note: the signal will be
0111      * emitted until each call of this method has been matched with a call to stopCursorTracking.
0112      *
0113      * This tracking is not about pointer position tracking.
0114      * @see stopCursorTracking
0115      * @see cursorChanged
0116      */
0117     void startCursorTracking();
0118     /**
0119      * @brief Disables tracking changes of cursor images.
0120      *
0121      * Only call after using startCursorTracking.
0122      *
0123      * @see startCursorTracking
0124      */
0125     void stopCursorTracking();
0126 
0127     /**
0128      * @brief The name of the currently used Cursor theme.
0129      *
0130      * @return const QString&
0131      */
0132     const QString &themeName() const;
0133     /**
0134      * @brief The size of the currently used Cursor theme.
0135      *
0136      * @return int
0137      */
0138     int themeSize() const;
0139     /**
0140      * @return list of alternative names for the cursor with @p name
0141      */
0142     static QVector<QByteArray> cursorAlternativeNames(const QByteArray &name);
0143     /**
0144      * Returns the default Xcursor theme name.
0145      */
0146     static QString defaultThemeName();
0147     /**
0148      * Returns the default Xcursor theme size.
0149      */
0150     static int defaultThemeSize();
0151 
0152     /**
0153      * Returns the current cursor position. This method does an update of the mouse position if
0154      * needed. It's save to call it multiple times.
0155      *
0156      * Implementing subclasses should prefer to use currentPos which is not performing a check
0157      * for update.
0158      */
0159     QPoint pos();
0160     /**
0161      * Warps the mouse cursor to new @p pos.
0162      * Values will be rounded to an integer
0163      */
0164     void setPos(const QPointF &pos);
0165     /**
0166      * Warps the mouse cursor to new @p pos.
0167      */
0168     void setPos(const QPoint &pos);
0169     void setPos(int x, int y);
0170     xcb_cursor_t x11Cursor(CursorShape shape);
0171     /**
0172      * Notice: if available always use the CursorShape variant to avoid cache duplicates for
0173      * ambiguous cursor names in the non existing cursor name specification
0174      */
0175     xcb_cursor_t x11Cursor(const QByteArray &name);
0176 
0177     QImage image() const;
0178     QPoint hotspot() const;
0179     QRect geometry() const;
0180     QRect rect() const;
0181 
0182     CursorSource *source() const;
0183     void setSource(CursorSource *source);
0184 
0185     /**
0186      * Returns @c true if the cursor is visible on the given output; otherwise returns @c false.
0187      */
0188     bool isOnOutput(Output *output) const;
0189 
0190     void markAsRendered(std::chrono::milliseconds timestamp);
0191 
0192 Q_SIGNALS:
0193     void posChanged(const QPoint &pos);
0194     void mouseChanged(const QPoint &pos, const QPoint &oldpos,
0195                       Qt::MouseButtons buttons, Qt::MouseButtons oldbuttons,
0196                       Qt::KeyboardModifiers modifiers, Qt::KeyboardModifiers oldmodifiers);
0197     /**
0198      * @brief Signal emitted when the cursor image changes.
0199      *
0200      * To enable these signals use startCursorTracking.
0201      *
0202      * @see startCursorTracking
0203      * @see stopCursorTracking
0204      */
0205     void cursorChanged();
0206     void themeChanged();
0207     void rendered(std::chrono::milliseconds timestamp);
0208 
0209 protected:
0210     /**
0211      * Performs the actual warping of the cursor.
0212      */
0213     virtual void doSetPos();
0214     /**
0215      * Called from @ref pos() to allow syncing the internal position with the underlying
0216      * system's cursor position.
0217      */
0218     virtual void doGetPos();
0219     /**
0220      * Called from startMousePolling when the mouse polling gets activated. Base implementation
0221      * does nothing, inheriting classes can overwrite to e.g. start a timer.
0222      */
0223     virtual void doStartMousePolling();
0224     /**
0225      * Called from stopMousePolling when the mouse polling gets deactivated. Base implementation
0226      * does nothing, inheriting classes can overwrite to e.g. stop a timer.
0227      */
0228     virtual void doStopMousePolling();
0229     /**
0230      * Called from startCursorTracking when cursor image tracking gets activated. Inheriting class needs
0231      * to overwrite to enable platform specific code for the tracking.
0232      */
0233     virtual void doStartCursorTracking();
0234     /**
0235      * Called from stopCursorTracking when cursor image tracking gets deactivated. Inheriting class needs
0236      * to overwrite to disable platform specific code for the tracking.
0237      */
0238     virtual void doStopCursorTracking();
0239     bool isCursorTracking() const;
0240     /**
0241      * Provides the actual internal cursor position to inheriting classes. If an inheriting class needs
0242      * access to the cursor position this method should be used instead of the static @ref pos, as
0243      * the static method syncs with the underlying system's cursor.
0244      */
0245     const QPoint &currentPos() const;
0246     /**
0247      * Updates the internal position to @p pos without warping the pointer as
0248      * setPos does.
0249      */
0250     void updatePos(const QPoint &pos);
0251     void updatePos(int x, int y);
0252 
0253 private Q_SLOTS:
0254     void loadThemeSettings();
0255     void slotKGlobalSettingsNotifyChange(int type, int arg);
0256 
0257 private:
0258     void updateTheme(const QString &name, int size);
0259     void loadThemeFromKConfig();
0260     CursorSource *m_source = nullptr;
0261     QHash<QByteArray, xcb_cursor_t> m_cursors;
0262     QPoint m_pos;
0263     int m_mousePollingCounter;
0264     int m_cursorTrackingCounter;
0265     QString m_themeName;
0266     int m_themeSize;
0267 };
0268 
0269 class KWIN_EXPORT Cursors : public QObject
0270 {
0271     Q_OBJECT
0272 public:
0273     Cursor *mouse() const
0274     {
0275         return m_mouse;
0276     }
0277 
0278     void setMouse(Cursor *mouse)
0279     {
0280         if (m_mouse != mouse) {
0281             m_mouse = mouse;
0282 
0283             addCursor(m_mouse);
0284             setCurrentCursor(m_mouse);
0285         }
0286     }
0287 
0288     void addCursor(Cursor *cursor);
0289     void removeCursor(Cursor *cursor);
0290 
0291     ///@returns the last cursor that moved
0292     Cursor *currentCursor() const
0293     {
0294         return m_currentCursor;
0295     }
0296 
0297     void hideCursor();
0298     void showCursor();
0299     bool isCursorHidden() const;
0300 
0301     static Cursors *self();
0302 
0303 Q_SIGNALS:
0304     void currentCursorChanged(Cursor *cursor);
0305     void hiddenChanged();
0306     void positionChanged(Cursor *cursor, const QPoint &position);
0307 
0308 private:
0309     void emitCurrentCursorChanged();
0310     void setCurrentCursor(Cursor *cursor);
0311 
0312     static Cursors *s_self;
0313     Cursor *m_currentCursor = nullptr;
0314     Cursor *m_mouse = nullptr;
0315     QVector<Cursor *> m_cursors;
0316     int m_cursorHideCounter = 0;
0317 };
0318 
0319 class InputConfig
0320 {
0321 public:
0322     KSharedConfigPtr inputConfig() const
0323     {
0324         return m_inputConfig;
0325     }
0326     void setInputConfig(KSharedConfigPtr config)
0327     {
0328         m_inputConfig = std::move(config);
0329     }
0330 
0331     static InputConfig *self();
0332 
0333 private:
0334     InputConfig();
0335 
0336     KSharedConfigPtr m_inputConfig;
0337     static InputConfig *s_self;
0338 };
0339 
0340 inline const QPoint &Cursor::currentPos() const
0341 {
0342     return m_pos;
0343 }
0344 
0345 inline void Cursor::updatePos(int x, int y)
0346 {
0347     updatePos(QPoint(x, y));
0348 }
0349 
0350 inline const QString &Cursor::themeName() const
0351 {
0352     return m_themeName;
0353 }
0354 
0355 inline int Cursor::themeSize() const
0356 {
0357     return m_themeSize;
0358 }
0359 
0360 inline bool Cursor::isCursorTracking() const
0361 {
0362     return m_cursorTrackingCounter > 0;
0363 }
0364 
0365 }
0366 
0367 Q_DECLARE_METATYPE(KWin::CursorShape)