1     /*
2     KWin - the KDE window manager
3     This file is part of the KDE project.
4 
5     SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org>
6 
7     SPDX-License-Identifier: GPL-2.0-or-later
8 */
9 #ifndef KWIN_CURSOR_H
10 #define KWIN_CURSOR_H
11 // kwin
12 #include <kwinglobals.h>
13 // Qt
14 #include <QHash>
15 #include <QObject>
16 #include <QPoint>
17 // KF
18 #include <KSharedConfig>
19 // xcb
20 #include <xcb/xcb.h>
21 
22 class QTimer;
23 
24 namespace KWin
25 {
26 
27 namespace ExtendedCursor {
28 /**
29  * Extension of Qt::CursorShape with values not currently present there
30  */
31 enum Shape {
32     SizeNorthWest = 0x100 + 0,
33     SizeNorth = 0x100 + 1,
34     SizeNorthEast = 0x100 + 2,
35     SizeEast = 0x100 + 3,
36     SizeWest = 0x100 + 4,
37     SizeSouthEast = 0x100 + 5,
38     SizeSouth = 0x100 + 6,
39     SizeSouthWest = 0x100 + 7
40 };
41 }
42 
43 /**
44  * @brief Wrapper round Qt::CursorShape with extensions enums into a single entity
45  */
46 class KWIN_EXPORT CursorShape {
47 public:
48     CursorShape() = default;
CursorShape(Qt::CursorShape qtShape)49     CursorShape(Qt::CursorShape qtShape) {
50         m_shape = qtShape;
51     }
CursorShape(KWin::ExtendedCursor::Shape kwinShape)52     CursorShape(KWin::ExtendedCursor::Shape kwinShape) {
53         m_shape = kwinShape;
54     }
55     bool operator==(const CursorShape &o) const {
56         return m_shape == o.m_shape;
57     }
58     operator int() const {
59         return m_shape;
60     }
61     /**
62      * @brief The name of a cursor shape in the theme.
63      */
64     QByteArray name() const;
65 private:
66     int m_shape = Qt::ArrowCursor;
67 };
68 
69 /**
70  * @short Replacement for QCursor.
71  *
72  * This class provides a similar API to QCursor and should be preferred inside KWin. It allows to
73  * get the position and warp the mouse cursor with static methods just like QCursor. It also provides
74  * the possibility to get an X11 cursor for a Qt::CursorShape - a functionality lost in Qt 5's QCursor
75  * implementation.
76  *
77  * In addition the class provides a mouse polling facility as required by e.g. Effects and ScreenEdges
78  * and emits signals when the mouse position changes. In opposite to QCursor this class is a QObject
79  * and cannot be constructed. Instead it provides a singleton getter, though the most important
80  * methods are wrapped in a static method, just like QCursor.
81  *
82  * The actual implementation is split into two parts: a system independent interface and a windowing
83  * system specific subclass. So far only an X11 backend is implemented which uses query pointer to
84  * fetch the position and warp pointer to set the position. It uses a timer based mouse polling and
85  * can provide X11 cursors through the XCursor library.
86  */
87 class KWIN_EXPORT Cursor : public QObject
88 {
89     Q_OBJECT
90 public:
91     Cursor(QObject* parent);
92     ~Cursor() override;
93     void startMousePolling();
94     void stopMousePolling();
95     /**
96      * @brief Enables tracking changes of cursor images.
97      *
98      * After enabling cursor change tracking the signal cursorChanged will be emitted
99      * whenever a change to the cursor image is recognized.
100      *
101      * Use stopCursorTracking to no longer emit this signal. Note: the signal will be
102      * emitted until each call of this method has been matched with a call to stopCursorTracking.
103      *
104      * This tracking is not about pointer position tracking.
105      * @see stopCursorTracking
106      * @see cursorChanged
107      */
108     void startCursorTracking();
109     /**
110      * @brief Disables tracking changes of cursor images.
111      *
112      * Only call after using startCursorTracking.
113      *
114      * @see startCursorTracking
115      */
116     void stopCursorTracking();
117 
118     /**
119      * @brief The name of the currently used Cursor theme.
120      *
121      * @return const QString&
122      */
123     const QString &themeName() const;
124     /**
125      * @brief The size of the currently used Cursor theme.
126      *
127      * @return int
128      */
129     int themeSize() const;
130     /**
131      * @return list of alternative names for the cursor with @p name
132      */
133     static QVector<QByteArray> cursorAlternativeNames(const QByteArray &name);
134     /**
135      * Returns the default Xcursor theme name.
136      */
137     static QString defaultThemeName();
138     /**
139      * Returns the default Xcursor theme size.
140      */
141     static int defaultThemeSize();
142 
143     /**
144      * Returns the current cursor position. This method does an update of the mouse position if
145      * needed. It's save to call it multiple times.
146      *
147      * Implementing subclasses should prefer to use currentPos which is not performing a check
148      * for update.
149      */
150     QPoint pos();
151     /**
152      * Warps the mouse cursor to new @p pos.
153      */
154     void setPos(const QPoint &pos);
155     void setPos(int x, int y);
156     xcb_cursor_t x11Cursor(CursorShape shape);
157     /**
158      * Notice: if available always use the CursorShape variant to avoid cache duplicates for
159      * ambiguous cursor names in the non existing cursor name specification
160      */
161     xcb_cursor_t x11Cursor(const QByteArray &name);
162 
image()163     QImage image() const { return m_image; }
hotspot()164     QPoint hotspot() const { return m_hotspot; }
165     QRect geometry() const;
166     QRect rect() const;
167 
168     void updateCursor(const QImage &image, const QPoint &hotspot);
markAsRendered()169     void markAsRendered() {
170         Q_EMIT rendered(geometry());
171     }
172 
173 Q_SIGNALS:
174     void posChanged(const QPoint& pos);
175     void mouseChanged(const QPoint& pos, const QPoint& oldpos,
176                       Qt::MouseButtons buttons, Qt::MouseButtons oldbuttons,
177                       Qt::KeyboardModifiers modifiers, Qt::KeyboardModifiers oldmodifiers);
178     /**
179      * @brief Signal emitted when the cursor image changes.
180      *
181      * To enable these signals use startCursorTracking.
182      *
183      * @see startCursorTracking
184      * @see stopCursorTracking
185      */
186     void cursorChanged();
187     void themeChanged();
188 
189     void rendered(const QRect &geometry);
190 
191 protected:
192     /**
193      * Performs the actual warping of the cursor.
194      */
195     virtual void doSetPos();
196     /**
197      * Called from @ref pos() to allow syncing the internal position with the underlying
198      * system's cursor position.
199      */
200     virtual void doGetPos();
201     /**
202      * Called from startMousePolling when the mouse polling gets activated. Base implementation
203      * does nothing, inheriting classes can overwrite to e.g. start a timer.
204      */
205     virtual void doStartMousePolling();
206     /**
207      * Called from stopMousePolling when the mouse polling gets deactivated. Base implementation
208      * does nothing, inheriting classes can overwrite to e.g. stop a timer.
209      */
210     virtual void doStopMousePolling();
211     /**
212      * Called from startCursorTracking when cursor image tracking gets activated. Inheriting class needs
213      * to overwrite to enable platform specific code for the tracking.
214      */
215     virtual void doStartCursorTracking();
216     /**
217      * Called from stopCursorTracking when cursor image tracking gets deactivated. Inheriting class needs
218      * to overwrite to disable platform specific code for the tracking.
219      */
220     virtual void doStopCursorTracking();
221     bool isCursorTracking() const;
222     /**
223      * Provides the actual internal cursor position to inheriting classes. If an inheriting class needs
224      * access to the cursor position this method should be used instead of the static @ref pos, as
225      * the static method syncs with the underlying system's cursor.
226      */
227     const QPoint &currentPos() const;
228     /**
229      * Updates the internal position to @p pos without warping the pointer as
230      * setPos does.
231      */
232     void updatePos(const QPoint &pos);
233     void updatePos(int x, int y);
234 
235 private Q_SLOTS:
236     void loadThemeSettings();
237     void slotKGlobalSettingsNotifyChange(int type, int arg);
238 
239 private:
240     void updateTheme(const QString &name, int size);
241     void loadThemeFromKConfig();
242     QHash<QByteArray, xcb_cursor_t > m_cursors;
243     QPoint m_pos;
244     QPoint m_hotspot;
245     QImage m_image;
246     int m_mousePollingCounter;
247     int m_cursorTrackingCounter;
248     QString m_themeName;
249     int m_themeSize;
250 };
251 
252 
253 class KWIN_EXPORT Cursors : public QObject
254 {
255     Q_OBJECT
256 public:
mouse()257     Cursor* mouse() const {
258         return m_mouse;
259     }
260 
setMouse(Cursor * mouse)261     void setMouse(Cursor* mouse) {
262         if (m_mouse != mouse) {
263             m_mouse = mouse;
264 
265             addCursor(m_mouse);
266             setCurrentCursor(m_mouse);
267         }
268     }
269 
270     void addCursor(Cursor* cursor);
271     void removeCursor(Cursor* cursor);
272 
273     ///@returns the last cursor that moved
currentCursor()274     Cursor* currentCursor() const {
275         return m_currentCursor;
276     }
277 
278     static Cursors* self();
279 
280 Q_SIGNALS:
281     void currentCursorChanged(Cursor* cursor);
282     void currentCursorRendered(const QRect &geometry);
283     void positionChanged(Cursor* cursor, const QPoint &position);
284 
285 private:
286     void emitCurrentCursorChanged();
287     void setCurrentCursor(Cursor* cursor);
288 
289     static Cursors* s_self;
290     Cursor* m_currentCursor = nullptr;
291     Cursor* m_mouse = nullptr;
292     QVector<Cursor*> m_cursors;
293 };
294 
295 class InputConfig
296 {
297 public:
inputConfig()298     KSharedConfigPtr inputConfig() const {
299         return m_inputConfig;
300     }
setInputConfig(KSharedConfigPtr config)301     void setInputConfig(KSharedConfigPtr config) {
302         m_inputConfig = std::move(config);
303     }
304 
305     static InputConfig *self();
306 private:
307     InputConfig();
308 
309     KSharedConfigPtr m_inputConfig;
310     static InputConfig *s_self;
311 };
312 
currentPos()313 inline const QPoint &Cursor::currentPos() const
314 {
315     return m_pos;
316 }
317 
updatePos(int x,int y)318 inline void Cursor::updatePos(int x, int y)
319 {
320     updatePos(QPoint(x, y));
321 }
322 
themeName()323 inline const QString& Cursor::themeName() const
324 {
325     return m_themeName;
326 }
327 
themeSize()328 inline int Cursor::themeSize() const
329 {
330     return m_themeSize;
331 }
332 
isCursorTracking()333 inline bool Cursor::isCursorTracking() const
334 {
335     return m_cursorTrackingCounter > 0;
336 }
337 
338 }
339 
340 Q_DECLARE_METATYPE(KWin::CursorShape)
341 
342 #endif // KWIN_CURSOR_H
343