1/********************************************************************
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5Copyright (C) 2013 Martin Gräßlin <mgraesslin@kde.org>
6
7This program is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 2 of the License, or
10(at your option) any later version.
11
12This program is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with this program. If not, see <http://www.gnu.org/licenses/>.
19*********************************************************************/
20#ifndef KWIN_CURSOR_H
21#define KWIN_CURSOR_H
22// kwin
23#include <kwinglobals.h>
24// Qt
25#include <QHash>
26#include <QObject>
27#include <QPoint>
28// xcb
29#include <xcb/xcb.h>
30
31class QTimer;
32
33namespace KWin
34{
35
36/**
37 * @short Replacement for QCursor.
38 *
39 * This class provides a similar API to QCursor and should be preferred inside KWin. It allows to
40 * get the position and warp the mouse cursor with static methods just like QCursor. It also provides
41 * the possibility to get an X11 cursor for a Qt::CursorShape - a functionality lost in Qt 5's QCursor
42 * implementation.
43 *
44 * In addition the class provides a mouse polling facility as required by e.g. Effects and ScreenEdges
45 * and emits signals when the mouse position changes. In opposite to QCursor this class is a QObject
46 * and cannot be constructed. Instead it provides a singleton getter, though the most important
47 * methods are wrapped in a static method, just like QCursor.
48 *
49 * The actual implementation is split into two parts: a system independent interface and a windowing
50 * system specific subclass. So far only an X11 backend is implemented which uses query pointer to
51 * fetch the position and warp pointer to set the position. It uses a timer based mouse polling and
52 * can provide X11 cursors through the XCursor library.
53 **/
54class Cursor : public QObject
55{
56 Q_OBJECT
57public:
58 virtual ~Cursor();
59 void startMousePolling();
60 void stopMousePolling();
61 /**
62 * @brief Enables tracking changes of cursor images.
63 *
64 * After enabling cursor change tracking the signal @link cursorChanged will be emitted
65 * whenever a change to the cursor image is recognized.
66 *
67 * Use @link stopCursorTracking to no longer emit this signal. Note: the signal will be
68 * emitted until each call of this method has been matched with a call to stopCursorTracking.
69 *
70 * This tracking is not about pointer position tracking.
71 * @see stopCursorTracking
72 * @see cursorChanged
73 */
74 void startCursorTracking();
75 /**
76 * @brief Disables tracking changes of cursor images.
77 *
78 * Only call after using @link startCursorTracking.
79 *
80 * @see startCursorTracking
81 */
82 void stopCursorTracking();
83 /**
84 * @internal
85 *
86 * Called from X11 event handler.
87 */
88 void notifyCursorChanged(uint32_t serial);
89
90 /**
91 * Returns the current cursor position. This method does an update of the mouse position if
92 * needed. It's save to call it multiple times.
93 *
94 * Implementing subclasses should prefer to use @link currentPos which is not performing a check
95 * for update.
96 **/
97 static QPoint pos();
98 /**
99 * Warps the mouse cursor to new @p pos.
100 **/
101 static void setPos(const QPoint &pos);
102 static void setPos(int x, int y);
103 static xcb_cursor_t x11Cursor(Qt::CursorShape shape);
104
105Q_SIGNALS:
106 void posChanged(QPoint pos);
107 void mouseChanged(const QPoint& pos, const QPoint& oldpos,
108 Qt::MouseButtons buttons, Qt::MouseButtons oldbuttons,
109 Qt::KeyboardModifiers modifiers, Qt::KeyboardModifiers oldmodifiers);
110 /**
111 * @brief Signal emitted when the cursor image changes.
112 *
113 * To enable these signals use @link startCursorTracking.
114 *
115 * @param serial The serial number of the new selected cursor.
116 * @see startCursorTracking
117 * @see stopCursorTracking
118 */
119 void cursorChanged(uint32_t serial);
120
121protected:
122 /**
123 * Called from @link x11Cursor to actually retrieve the X11 cursor. Base implementation returns
124 * a null cursor, an implementing subclass should implement this method if it can provide X11
125 * mouse cursors.
126 **/
127 virtual xcb_cursor_t getX11Cursor(Qt::CursorShape shape);
128 /**
129 * Performs the actual warping of the cursor.
130 **/
131 virtual void doSetPos();
132 /**
133 * Called from @link pos() to allow syncing the internal position with the underlying
134 * system's cursor position.
135 **/
136 virtual void doGetPos();
137 /**
138 * Called from @link startMousePolling when the mouse polling gets activated. Base implementation
139 * does nothing, inheriting classes can overwrite to e.g. start a timer.
140 **/
141 virtual void doStartMousePolling();
142 /**
143 * Called from @link stopMousePolling when the mouse polling gets deactivated. Base implementation
144 * does nothing, inheriting classes can overwrite to e.g. stop a timer.
145 **/
146 virtual void doStopMousePolling();
147 /**
148 * Called from @link startCursorTracking when cursor image tracking gets activated. Inheriting class needs
149 * to overwrite to enable platform specific code for the tracking.
150 */
151 virtual void doStartCursorTracking();
152 /**
153 * Called from @link stopCursorTracking when cursor image tracking gets deactivated. Inheriting class needs
154 * to overwrite to disable platform specific code for the tracking.
155 */
156 virtual void doStopCursorTracking();
157 /**
158 * Provides the actual internal cursor position to inheriting classes. If an inheriting class needs
159 * access to the cursor position this method should be used instead of the static @link pos, as
160 * the static method syncs with the underlying system's cursor.
161 **/
162 const QPoint &currentPos() const;
163 /**
164 * Updates the internal position to @p pos without warping the pointer as
165 * @link setPos does.
166 **/
167 void updatePos(const QPoint &pos);
168 void updatePos(int x, int y);
169
170private:
171 QPoint m_pos;
172 int m_mousePollingCounter;
173 int m_cursorTrackingCounter;
174
175 KWIN_SINGLETON(Cursor)
176};
177
178class X11Cursor : public Cursor
179{
180 Q_OBJECT
181public:
182 virtual ~X11Cursor();
183protected:
184 virtual xcb_cursor_t getX11Cursor(Qt::CursorShape shape);
185 virtual void doSetPos();
186 virtual void doGetPos();
187 virtual void doStartMousePolling();
188 virtual void doStopMousePolling();
189 virtual void doStartCursorTracking();
190 virtual void doStopCursorTracking();
191
192private slots:
193 /**
194 * Because of QTimer's and the impossibility to get events for all mouse
195 * movements (at least I haven't figured out how) the position needs
196 * to be also refetched after each return to the event loop.
197 */
198 void resetTimeStamp();
199 void mousePolled();
200private:
201 X11Cursor(QObject *parent);
202 xcb_cursor_t createCursor(Qt::CursorShape shape);
203 QByteArray cursorName(Qt::CursorShape shape) const;
204 QHash<Qt::CursorShape, xcb_cursor_t > m_cursors;
205 xcb_timestamp_t m_timeStamp;
206 uint16_t m_buttonMask;
207 QTimer *m_resetTimeStampTimer;
208 QTimer *m_mousePollingTimer;
209 friend class Cursor;
210};
211
212inline const QPoint &Cursor::currentPos() const
213{
214 return m_pos;
215}
216
217inline void Cursor::updatePos(int x, int y)
218{
219 updatePos(QPoint(x, y));
220}
221
222}
223
224#endif // KWIN_CURSOR_H
225