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 ¤tPos() 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