1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the plugins of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#ifndef QXCBCONNECTION_H
41#define QXCBCONNECTION_H
42
43#include <xcb/xcb.h>
44#include <xcb/randr.h>
45
46#include <QtCore/QTimer>
47#include <QtGui/private/qtguiglobal_p.h>
48#include "qxcbexport.h"
49#include <QHash>
50#include <QList>
51#include <QVector>
52#include <qpa/qwindowsysteminterface.h>
53#include <QtCore/QLoggingCategory>
54#include <QtCore/private/qglobal_p.h>
55
56#include "qxcbeventqueue.h"
57#include "qxcbconnection_basic.h"
58
59#if QT_CONFIG(tabletevent)
60#include <QTabletEvent>
61#endif
62
63QT_BEGIN_NAMESPACE
64
65Q_DECLARE_LOGGING_CATEGORY(lcQpaXInput)
66Q_DECLARE_LOGGING_CATEGORY(lcQpaXInputDevices)
67Q_DECLARE_LOGGING_CATEGORY(lcQpaXInputEvents)
68Q_DECLARE_LOGGING_CATEGORY(lcQpaScreen)
69Q_DECLARE_LOGGING_CATEGORY(lcQpaEvents)
70Q_DECLARE_LOGGING_CATEGORY(lcQpaPeeker)
71Q_DECLARE_LOGGING_CATEGORY(lcQpaKeyboard)
72Q_DECLARE_LOGGING_CATEGORY(lcQpaClipboard)
73Q_DECLARE_LOGGING_CATEGORY(lcQpaXDnd)
74Q_DECLARE_LOGGING_CATEGORY(lcQpaEventReader)
75
76class QXcbVirtualDesktop;
77class QXcbScreen;
78class QXcbWindow;
79class QXcbDrag;
80class QXcbKeyboard;
81class QXcbClipboard;
82class QXcbWMSupport;
83class QXcbNativeInterface;
84class QXcbSystemTrayTracker;
85class QXcbGlIntegration;
86
87class QXcbWindowEventListener
88{
89public:
90 virtual ~QXcbWindowEventListener() {}
91 virtual bool handleNativeEvent(xcb_generic_event_t *) { return false; }
92
93 virtual void handleExposeEvent(const xcb_expose_event_t *) {}
94 virtual void handleClientMessageEvent(const xcb_client_message_event_t *) {}
95 virtual void handleConfigureNotifyEvent(const xcb_configure_notify_event_t *) {}
96 virtual void handleMapNotifyEvent(const xcb_map_notify_event_t *) {}
97 virtual void handleUnmapNotifyEvent(const xcb_unmap_notify_event_t *) {}
98 virtual void handleDestroyNotifyEvent(const xcb_destroy_notify_event_t *) {}
99 virtual void handleButtonPressEvent(const xcb_button_press_event_t *) {}
100 virtual void handleButtonReleaseEvent(const xcb_button_release_event_t *) {}
101 virtual void handleMotionNotifyEvent(const xcb_motion_notify_event_t *) {}
102 virtual void handleEnterNotifyEvent(const xcb_enter_notify_event_t *) {}
103 virtual void handleLeaveNotifyEvent(const xcb_leave_notify_event_t *) {}
104 virtual void handleFocusInEvent(const xcb_focus_in_event_t *) {}
105 virtual void handleFocusOutEvent(const xcb_focus_out_event_t *) {}
106 virtual void handlePropertyNotifyEvent(const xcb_property_notify_event_t *) {}
107#if QT_CONFIG(xcb_xinput)
108 virtual void handleXIMouseEvent(xcb_ge_event_t *, Qt::MouseEventSource = Qt::MouseEventNotSynthesized) {}
109 virtual void handleXIEnterLeave(xcb_ge_event_t *) {}
110#endif
111 virtual QXcbWindow *toWindow() { return nullptr; }
112};
113
114typedef QHash<xcb_window_t, QXcbWindowEventListener *> WindowMapper;
115
116class QXcbSyncWindowRequest : public QEvent
117{
118public:
119 QXcbSyncWindowRequest(QXcbWindow *w) : QEvent(QEvent::Type(QEvent::User + 1)), m_window(w) { }
120
121 QXcbWindow *window() const { return m_window; }
122 void invalidate();
123
124private:
125 QXcbWindow *m_window;
126};
127
128class Q_XCB_EXPORT QXcbConnection : public QXcbBasicConnection
129{
130 Q_OBJECT
131public:
132 QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGrabServer, xcb_visualid_t defaultVisualId, const char *displayName = 0);
133 ~QXcbConnection();
134
135 QXcbConnection *connection() const { return const_cast<QXcbConnection *>(this); }
136 QXcbEventQueue *eventQueue() const { return m_eventQueue; }
137
138 const QList<QXcbVirtualDesktop *> &virtualDesktops() const { return m_virtualDesktops; }
139 const QList<QXcbScreen *> &screens() const { return m_screens; }
140 QXcbVirtualDesktop *primaryVirtualDesktop() const {
141 return m_virtualDesktops.value(primaryScreenNumber());
142 }
143 QXcbScreen *primaryScreen() const;
144
145 const xcb_format_t *formatForDepth(uint8_t depth) const;
146
147 bool imageNeedsEndianSwap() const
148 {
149 if (!hasShm())
150 return false; // The non-Shm path does its own swapping
151#if Q_BYTE_ORDER == Q_BIG_ENDIAN
152 return setup()->image_byte_order != XCB_IMAGE_ORDER_MSB_FIRST;
153#else
154 return setup()->image_byte_order != XCB_IMAGE_ORDER_LSB_FIRST;
155#endif
156 }
157
158 QXcbKeyboard *keyboard() const { return m_keyboard; }
159
160#ifndef QT_NO_CLIPBOARD
161 QXcbClipboard *clipboard() const { return m_clipboard; }
162#endif
163#if QT_CONFIG(draganddrop)
164 QXcbDrag *drag() const { return m_drag; }
165#endif
166
167 QXcbWMSupport *wmSupport() const { return m_wmSupport.data(); }
168 xcb_window_t rootWindow();
169 xcb_window_t clientLeader();
170
171 bool hasDefaultVisualId() const { return m_defaultVisualId != UINT_MAX; }
172 xcb_visualid_t defaultVisualId() const { return m_defaultVisualId; }
173
174 void sync();
175
176 void handleXcbError(xcb_generic_error_t *error);
177 void printXcbError(const char *message, xcb_generic_error_t *error);
178 void handleXcbEvent(xcb_generic_event_t *event);
179 void printXcbEvent(const QLoggingCategory &log, const char *message,
180 xcb_generic_event_t *event) const;
181
182 void addWindowEventListener(xcb_window_t id, QXcbWindowEventListener *eventListener);
183 void removeWindowEventListener(xcb_window_t id);
184 QXcbWindowEventListener *windowEventListenerFromId(xcb_window_t id);
185 QXcbWindow *platformWindowFromId(xcb_window_t id);
186
187 inline xcb_timestamp_t time() const { return m_time; }
188 inline void setTime(xcb_timestamp_t t) { if (timeGreaterThan(t, m_time)) m_time = t; }
189
190 inline xcb_timestamp_t netWmUserTime() const { return m_netWmUserTime; }
191 inline void setNetWmUserTime(xcb_timestamp_t t) { if (timeGreaterThan(t, m_netWmUserTime)) m_netWmUserTime = t; }
192
193 xcb_timestamp_t getTimestamp();
194 xcb_window_t getSelectionOwner(xcb_atom_t atom) const;
195 xcb_window_t getQtSelectionOwner();
196
197 void setButtonState(Qt::MouseButton button, bool down);
198 Qt::MouseButtons buttonState() const { return m_buttonState; }
199 Qt::MouseButton button() const { return m_button; }
200 Qt::MouseButton translateMouseButton(xcb_button_t s);
201
202 QXcbWindow *focusWindow() const { return m_focusWindow; }
203 void setFocusWindow(QWindow *);
204 QXcbWindow *mouseGrabber() const { return m_mouseGrabber; }
205 void setMouseGrabber(QXcbWindow *);
206 QXcbWindow *mousePressWindow() const { return m_mousePressWindow; }
207 void setMousePressWindow(QXcbWindow *);
208
209 QByteArray startupId() const { return m_startupId; }
210 void setStartupId(const QByteArray &nextId) { m_startupId = nextId; }
211 void clearStartupId() { m_startupId.clear(); }
212
213 void grabServer();
214 void ungrabServer();
215
216 bool isUnity() const { return m_xdgCurrentDesktop == "unity"; }
217 bool isGnome() const { return m_xdgCurrentDesktop == "gnome"; }
218
219 QXcbNativeInterface *nativeInterface() const { return m_nativeInterface; }
220
221 QXcbSystemTrayTracker *systemTrayTracker() const;
222
223 Qt::MouseButtons queryMouseButtons() const;
224 Qt::KeyboardModifiers queryKeyboardModifiers() const;
225
226 bool isUserInputEvent(xcb_generic_event_t *event) const;
227
228#if QT_CONFIG(xcb_xinput)
229 void xi2SelectStateEvents();
230 void xi2SelectDeviceEvents(xcb_window_t window);
231 void xi2SelectDeviceEventsCompatibility(xcb_window_t window);
232 bool xi2SetMouseGrabEnabled(xcb_window_t w, bool grab);
233 bool xi2MouseEventsDisabled() const;
234 Qt::MouseButton xiToQtMouseButton(uint32_t b);
235 void xi2UpdateScrollingDevices();
236 bool startSystemMoveResizeForTouchBegin(xcb_window_t window, const QPoint &point, int corner);
237 void abortSystemMoveResizeForTouch();
238 bool isTouchScreen(int id);
239#endif
240
241 bool canGrab() const { return m_canGrabServer; }
242
243 QXcbGlIntegration *glIntegration() const;
244
245 void flush() { xcb_flush(xcb_connection()); }
246 void processXcbEvents(QEventLoop::ProcessEventsFlags flags);
247
248 QTimer &focusInTimer() { return m_focusInTimer; }
249
250protected:
251 bool event(QEvent *e) override;
252
253private:
254 void xrandrSelectEvents();
255 QXcbScreen* findScreenForCrtc(xcb_window_t rootWindow, xcb_randr_crtc_t crtc) const;
256 QXcbScreen* findScreenForOutput(xcb_window_t rootWindow, xcb_randr_output_t output) const;
257 QXcbVirtualDesktop* virtualDesktopForRootWindow(xcb_window_t rootWindow) const;
258 void updateScreens(const xcb_randr_notify_event_t *event);
259 bool checkOutputIsPrimary(xcb_window_t rootWindow, xcb_randr_output_t output);
260 void updateScreen(QXcbScreen *screen, const xcb_randr_output_change_t &outputChange);
261 QXcbScreen *createScreen(QXcbVirtualDesktop *virtualDesktop,
262 const xcb_randr_output_change_t &outputChange,
263 xcb_randr_get_output_info_reply_t *outputInfo);
264 void destroyScreen(QXcbScreen *screen);
265 void initializeScreens();
266 bool compressEvent(xcb_generic_event_t *event) const;
267 inline bool timeGreaterThan(xcb_timestamp_t a, xcb_timestamp_t b) const
268 { return static_cast<int32_t>(a - b) > 0 || b == XCB_CURRENT_TIME; }
269
270#if QT_CONFIG(xcb_xinput)
271 void xi2SetupDevice(void *info, bool removeExisting = true);
272 void xi2SetupDevices();
273 struct TouchDeviceData {
274 QTouchDevice *qtTouchDevice = nullptr;
275 QHash<int, QWindowSystemInterface::TouchPoint> touchPoints;
276 QHash<int, QPointF> pointPressedPosition; // in screen coordinates where each point was pressed
277 struct ValuatorClassInfo {
278 double min = 0;
279 double max = 0;
280 int number = -1;
281 QXcbAtom::Atom label;
282 };
283 QVector<ValuatorClassInfo> valuatorInfo;
284
285 // Stuff that is relevant only for touchpads
286 QPointF firstPressedPosition; // in screen coordinates where the first point was pressed
287 QPointF firstPressedNormalPosition; // device coordinates (0 to 1, 0 to 1) where the first point was pressed
288 QSizeF size; // device size in mm
289 bool providesTouchOrientation = false;
290 };
291 TouchDeviceData *populateTouchDevices(void *info);
292 TouchDeviceData *touchDeviceForId(int id);
293 void xi2HandleEvent(xcb_ge_event_t *event);
294 void xi2HandleHierarchyEvent(void *event);
295 void xi2HandleDeviceChangedEvent(void *event);
296 void xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindow);
297#if QT_CONFIG(tabletevent)
298 struct TabletData {
299 int deviceId = 0;
300 QTabletEvent::PointerType pointerType = QTabletEvent::UnknownPointer;
301 QTabletEvent::TabletDevice tool = QTabletEvent::Stylus;
302 Qt::MouseButtons buttons = 0;
303 qint64 serialId = 0;
304 bool inProximity = false;
305 struct ValuatorClassInfo {
306 double minVal = 0;
307 double maxVal = 0;
308 double curVal = 0;
309 int number = -1;
310 };
311 QHash<int, ValuatorClassInfo> valuatorInfo;
312 };
313 friend class QTypeInfo<TabletData>;
314 friend class QTypeInfo<TabletData::ValuatorClassInfo>;
315 bool xi2HandleTabletEvent(const void *event, TabletData *tabletData);
316 void xi2ReportTabletEvent(const void *event, TabletData *tabletData);
317 QVector<TabletData> m_tabletData;
318 TabletData *tabletDataForDevice(int id);
319#endif // QT_CONFIG(tabletevent)
320 struct ScrollingDevice {
321 int deviceId = 0;
322 int verticalIndex = 0;
323 int horizontalIndex = 0;
324 double verticalIncrement = 0;
325 double horizontalIncrement = 0;
326 Qt::Orientations orientations = 0;
327 Qt::Orientations legacyOrientations = 0;
328 QPointF lastScrollPosition;
329 };
330 QHash<int, ScrollingDevice> m_scrollingDevices;
331 void xi2HandleScrollEvent(void *event, ScrollingDevice &scrollingDevice);
332 void xi2UpdateScrollingDevice(ScrollingDevice &scrollingDevice);
333 ScrollingDevice *scrollingDeviceForId(int id);
334
335 static bool xi2GetValuatorValueIfSet(const void *event, int valuatorNum, double *value);
336
337 QHash<int, TouchDeviceData> m_touchDevices;
338 struct StartSystemMoveResizeInfo {
339 xcb_window_t window = XCB_NONE;
340 uint16_t deviceid;
341 uint32_t pointid;
342 int corner;
343 } m_startSystemMoveResizeInfo;
344#endif // QT_CONFIG(xcb_xinput)
345
346 const bool m_canGrabServer;
347 const xcb_visualid_t m_defaultVisualId;
348
349 QList<QXcbVirtualDesktop *> m_virtualDesktops;
350 QList<QXcbScreen *> m_screens;
351
352 xcb_timestamp_t m_time = XCB_CURRENT_TIME;
353 xcb_timestamp_t m_netWmUserTime = XCB_CURRENT_TIME;
354
355 QXcbKeyboard *m_keyboard = nullptr;
356#ifndef QT_NO_CLIPBOARD
357 QXcbClipboard *m_clipboard = nullptr;
358#endif
359#if QT_CONFIG(draganddrop)
360 QXcbDrag *m_drag = nullptr;
361#endif
362 QScopedPointer<QXcbWMSupport> m_wmSupport;
363 QXcbNativeInterface *m_nativeInterface = nullptr;
364
365 QXcbEventQueue *m_eventQueue = nullptr;
366
367 WindowMapper m_mapper;
368
369 Qt::MouseButtons m_buttonState = nullptr;
370 Qt::MouseButton m_button = Qt::NoButton;
371
372 QXcbWindow *m_focusWindow = nullptr;
373 QXcbWindow *m_mouseGrabber = nullptr;
374 QXcbWindow *m_mousePressWindow = nullptr;
375
376 xcb_window_t m_clientLeader = 0;
377 QByteArray m_startupId;
378 QXcbSystemTrayTracker *m_systemTrayTracker = nullptr;
379 mutable QXcbGlIntegration *m_glIntegration = nullptr;
380 mutable bool m_glIntegrationInitialized = false;
381 bool m_xiGrab = false;
382 QVector<int> m_xiMasterPointerIds;
383
384 xcb_window_t m_qtSelectionOwner = 0;
385
386 friend class QXcbEventQueue;
387
388 QByteArray m_xdgCurrentDesktop;
389 QTimer m_focusInTimer;
390
391};
392#if QT_CONFIG(xcb_xinput)
393#if QT_CONFIG(tabletevent)
394Q_DECLARE_TYPEINFO(QXcbConnection::TabletData::ValuatorClassInfo, Q_PRIMITIVE_TYPE);
395Q_DECLARE_TYPEINFO(QXcbConnection::TabletData, Q_MOVABLE_TYPE);
396#endif
397#endif
398
399class QXcbConnectionGrabber
400{
401public:
402 QXcbConnectionGrabber(QXcbConnection *connection);
403 ~QXcbConnectionGrabber();
404 void release();
405private:
406 QXcbConnection *m_connection;
407};
408
409// The xcb_send_event() requires all events to have 32 bytes. It calls memcpy() on the
410// passed in event. If the passed in event is less than 32 bytes, memcpy() reaches into
411// unrelated memory.
412template <typename T>
413struct alignas(32) q_padded_xcb_event : T { };
414
415QT_END_NAMESPACE
416
417#endif
418