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 virtual void handleXIMouseEvent(xcb_ge_event_t *, Qt::MouseEventSource = Qt::MouseEventNotSynthesized) {}
108 virtual void handleXIEnterLeave(xcb_ge_event_t *) {}
109 virtual QXcbWindow *toWindow() { return nullptr; }
110};
111
112typedef QHash<xcb_window_t, QXcbWindowEventListener *> WindowMapper;
113
114class QXcbSyncWindowRequest : public QEvent
115{
116public:
117 QXcbSyncWindowRequest(QXcbWindow *w) : QEvent(QEvent::Type(QEvent::User + 1)), m_window(w) { }
118
119 QXcbWindow *window() const { return m_window; }
120 void invalidate();
121
122private:
123 QXcbWindow *m_window;
124};
125
126class Q_XCB_EXPORT QXcbConnection : public QXcbBasicConnection
127{
128 Q_OBJECT
129public:
130 QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGrabServer, xcb_visualid_t defaultVisualId, const char *displayName = nullptr);
131 ~QXcbConnection();
132
133 QXcbConnection *connection() const { return const_cast<QXcbConnection *>(this); }
134 QXcbEventQueue *eventQueue() const { return m_eventQueue; }
135
136 const QList<QXcbVirtualDesktop *> &virtualDesktops() const { return m_virtualDesktops; }
137 const QList<QXcbScreen *> &screens() const { return m_screens; }
138 QXcbVirtualDesktop *primaryVirtualDesktop() const {
139 return m_virtualDesktops.value(i: primaryScreenNumber());
140 }
141 QXcbScreen *primaryScreen() const;
142
143 const xcb_format_t *formatForDepth(uint8_t depth) const;
144
145 bool imageNeedsEndianSwap() const
146 {
147 if (!hasShm())
148 return false; // The non-Shm path does its own swapping
149#if Q_BYTE_ORDER == Q_BIG_ENDIAN
150 return setup()->image_byte_order != XCB_IMAGE_ORDER_MSB_FIRST;
151#else
152 return setup()->image_byte_order != XCB_IMAGE_ORDER_LSB_FIRST;
153#endif
154 }
155
156 QXcbKeyboard *keyboard() const { return m_keyboard; }
157
158#ifndef QT_NO_CLIPBOARD
159 QXcbClipboard *clipboard() const { return m_clipboard; }
160#endif
161#if QT_CONFIG(draganddrop)
162 QXcbDrag *drag() const { return m_drag; }
163#endif
164
165 QXcbWMSupport *wmSupport() const { return m_wmSupport.data(); }
166 xcb_window_t rootWindow();
167 xcb_window_t clientLeader();
168
169 bool hasDefaultVisualId() const { return m_defaultVisualId != UINT_MAX; }
170 xcb_visualid_t defaultVisualId() const { return m_defaultVisualId; }
171
172 void sync();
173
174 void handleXcbError(xcb_generic_error_t *error);
175 void printXcbError(const char *message, xcb_generic_error_t *error);
176 void handleXcbEvent(xcb_generic_event_t *event);
177 void printXcbEvent(const QLoggingCategory &log, const char *message,
178 xcb_generic_event_t *event) const;
179
180 void addWindowEventListener(xcb_window_t id, QXcbWindowEventListener *eventListener);
181 void removeWindowEventListener(xcb_window_t id);
182 QXcbWindowEventListener *windowEventListenerFromId(xcb_window_t id);
183 QXcbWindow *platformWindowFromId(xcb_window_t id);
184
185 inline xcb_timestamp_t time() const { return m_time; }
186 inline void setTime(xcb_timestamp_t t) { if (timeGreaterThan(a: t, b: m_time)) m_time = t; }
187
188 inline xcb_timestamp_t netWmUserTime() const { return m_netWmUserTime; }
189 inline void setNetWmUserTime(xcb_timestamp_t t) { if (timeGreaterThan(a: t, b: m_netWmUserTime)) m_netWmUserTime = t; }
190
191 xcb_timestamp_t getTimestamp();
192 xcb_window_t getSelectionOwner(xcb_atom_t atom) const;
193 xcb_window_t getQtSelectionOwner();
194
195 void setButtonState(Qt::MouseButton button, bool down);
196 Qt::MouseButtons buttonState() const { return m_buttonState; }
197 Qt::MouseButton button() const { return m_button; }
198 Qt::MouseButton translateMouseButton(xcb_button_t s);
199
200 QXcbWindow *focusWindow() const { return m_focusWindow; }
201 void setFocusWindow(QWindow *);
202 QXcbWindow *mouseGrabber() const { return m_mouseGrabber; }
203 void setMouseGrabber(QXcbWindow *);
204 QXcbWindow *mousePressWindow() const { return m_mousePressWindow; }
205 void setMousePressWindow(QXcbWindow *);
206
207 QByteArray startupId() const { return m_startupId; }
208 void setStartupId(const QByteArray &nextId) { m_startupId = nextId; }
209 void clearStartupId() { m_startupId.clear(); }
210
211 void grabServer();
212 void ungrabServer();
213
214 bool isUnity() const { return m_xdgCurrentDesktop == "unity"; }
215 bool isGnome() const { return m_xdgCurrentDesktop == "gnome"; }
216
217 QXcbNativeInterface *nativeInterface() const { return m_nativeInterface; }
218
219 QXcbSystemTrayTracker *systemTrayTracker() const;
220
221 Qt::MouseButtons queryMouseButtons() const;
222 Qt::KeyboardModifiers queryKeyboardModifiers() const;
223
224 bool isUserInputEvent(xcb_generic_event_t *event) const;
225
226 void xi2SelectStateEvents();
227 void xi2SelectDeviceEvents(xcb_window_t window);
228 void xi2SelectDeviceEventsCompatibility(xcb_window_t window);
229 bool xi2SetMouseGrabEnabled(xcb_window_t w, bool grab);
230 bool xi2MouseEventsDisabled() const;
231 Qt::MouseButton xiToQtMouseButton(uint32_t b);
232 void xi2UpdateScrollingDevices();
233 bool startSystemMoveResizeForTouch(xcb_window_t window, int edges);
234 void abortSystemMoveResizeForTouch();
235 bool isTouchScreen(int id);
236
237 bool canGrab() const { return m_canGrabServer; }
238
239 QXcbGlIntegration *glIntegration() const;
240
241 void flush() { xcb_flush(c: xcb_connection()); }
242 void processXcbEvents(QEventLoop::ProcessEventsFlags flags);
243
244 QTimer &focusInTimer() { return m_focusInTimer; }
245
246protected:
247 bool event(QEvent *e) override;
248
249private:
250 void xrandrSelectEvents();
251 QXcbScreen* findScreenForCrtc(xcb_window_t rootWindow, xcb_randr_crtc_t crtc) const;
252 QXcbScreen* findScreenForOutput(xcb_window_t rootWindow, xcb_randr_output_t output) const;
253 QXcbVirtualDesktop* virtualDesktopForRootWindow(xcb_window_t rootWindow) const;
254 void updateScreens(const xcb_randr_notify_event_t *event);
255 bool checkOutputIsPrimary(xcb_window_t rootWindow, xcb_randr_output_t output);
256 void updateScreen(QXcbScreen *screen, const xcb_randr_output_change_t &outputChange);
257 QXcbScreen *createScreen(QXcbVirtualDesktop *virtualDesktop,
258 const xcb_randr_output_change_t &outputChange,
259 xcb_randr_get_output_info_reply_t *outputInfo);
260 void destroyScreen(QXcbScreen *screen);
261 void initializeScreens();
262 bool compressEvent(xcb_generic_event_t *event) const;
263 inline bool timeGreaterThan(xcb_timestamp_t a, xcb_timestamp_t b) const
264 { return static_cast<int32_t>(a - b) > 0 || b == XCB_CURRENT_TIME; }
265
266 void xi2SetupDevice(void *info, bool removeExisting = true);
267 void xi2SetupDevices();
268 struct TouchDeviceData {
269 QTouchDevice *qtTouchDevice = nullptr;
270 QHash<int, QWindowSystemInterface::TouchPoint> touchPoints;
271 QHash<int, QPointF> pointPressedPosition; // in screen coordinates where each point was pressed
272 struct ValuatorClassInfo {
273 double min = 0;
274 double max = 0;
275 int number = -1;
276 QXcbAtom::Atom label;
277 };
278 QVector<ValuatorClassInfo> valuatorInfo;
279
280 // Stuff that is relevant only for touchpads
281 QPointF firstPressedPosition; // in screen coordinates where the first point was pressed
282 QPointF firstPressedNormalPosition; // device coordinates (0 to 1, 0 to 1) where the first point was pressed
283 QSizeF size; // device size in mm
284 bool providesTouchOrientation = false;
285 };
286 TouchDeviceData *populateTouchDevices(void *info);
287 TouchDeviceData *touchDeviceForId(int id);
288 void xi2HandleEvent(xcb_ge_event_t *event);
289 void xi2HandleHierarchyEvent(void *event);
290 void xi2HandleDeviceChangedEvent(void *event);
291 void xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindow);
292#if QT_CONFIG(tabletevent)
293 struct TabletData {
294 int deviceId = 0;
295 QTabletEvent::PointerType pointerType = QTabletEvent::UnknownPointer;
296 QTabletEvent::TabletDevice tool = QTabletEvent::Stylus;
297 Qt::MouseButtons buttons;
298 qint64 serialId = 0;
299 bool inProximity = false;
300 struct ValuatorClassInfo {
301 double minVal = 0;
302 double maxVal = 0;
303 double curVal = 0;
304 int number = -1;
305 };
306 QHash<int, ValuatorClassInfo> valuatorInfo;
307 };
308 friend class QTypeInfo<TabletData>;
309 friend class QTypeInfo<TabletData::ValuatorClassInfo>;
310 bool xi2HandleTabletEvent(const void *event, TabletData *tabletData);
311 void xi2ReportTabletEvent(const void *event, TabletData *tabletData);
312 QVector<TabletData> m_tabletData;
313 TabletData *tabletDataForDevice(int id);
314#endif // QT_CONFIG(tabletevent)
315 struct ScrollingDevice {
316 int deviceId = 0;
317 int verticalIndex = 0;
318 int horizontalIndex = 0;
319 double verticalIncrement = 0;
320 double horizontalIncrement = 0;
321 Qt::Orientations orientations;
322 Qt::Orientations legacyOrientations;
323 QPointF lastScrollPosition;
324 };
325 QHash<int, ScrollingDevice> m_scrollingDevices;
326 void xi2HandleScrollEvent(void *event, ScrollingDevice &scrollingDevice);
327 void xi2UpdateScrollingDevice(ScrollingDevice &scrollingDevice);
328 ScrollingDevice *scrollingDeviceForId(int id);
329
330 static bool xi2GetValuatorValueIfSet(const void *event, int valuatorNum, double *value);
331
332 QHash<int, TouchDeviceData> m_touchDevices;
333 struct StartSystemMoveResizeInfo {
334 xcb_window_t window = XCB_NONE;
335 uint16_t deviceid;
336 uint32_t pointid;
337 int edges;
338 } m_startSystemMoveResizeInfo;
339
340 const bool m_canGrabServer;
341 const xcb_visualid_t m_defaultVisualId;
342
343 QList<QXcbVirtualDesktop *> m_virtualDesktops;
344 QList<QXcbScreen *> m_screens;
345
346 xcb_timestamp_t m_time = XCB_CURRENT_TIME;
347 xcb_timestamp_t m_netWmUserTime = XCB_CURRENT_TIME;
348
349 QXcbKeyboard *m_keyboard = nullptr;
350#ifndef QT_NO_CLIPBOARD
351 QXcbClipboard *m_clipboard = nullptr;
352#endif
353#if QT_CONFIG(draganddrop)
354 QXcbDrag *m_drag = nullptr;
355#endif
356 QScopedPointer<QXcbWMSupport> m_wmSupport;
357 QXcbNativeInterface *m_nativeInterface = nullptr;
358
359 QXcbEventQueue *m_eventQueue = nullptr;
360
361 WindowMapper m_mapper;
362
363 Qt::MouseButtons m_buttonState;
364 Qt::MouseButton m_button = Qt::NoButton;
365
366 QXcbWindow *m_focusWindow = nullptr;
367 QXcbWindow *m_mouseGrabber = nullptr;
368 QXcbWindow *m_mousePressWindow = nullptr;
369
370 xcb_window_t m_clientLeader = 0;
371 QByteArray m_startupId;
372 QXcbSystemTrayTracker *m_systemTrayTracker = nullptr;
373 mutable QXcbGlIntegration *m_glIntegration = nullptr;
374 mutable bool m_glIntegrationInitialized = false;
375 bool m_xiGrab = false;
376 QVector<int> m_xiMasterPointerIds;
377
378 xcb_window_t m_qtSelectionOwner = 0;
379
380 friend class QXcbEventQueue;
381
382 QByteArray m_xdgCurrentDesktop;
383 QTimer m_focusInTimer;
384
385};
386#if QT_CONFIG(tabletevent)
387Q_DECLARE_TYPEINFO(QXcbConnection::TabletData::ValuatorClassInfo, Q_PRIMITIVE_TYPE);
388Q_DECLARE_TYPEINFO(QXcbConnection::TabletData, Q_MOVABLE_TYPE);
389#endif
390
391class QXcbConnectionGrabber
392{
393public:
394 QXcbConnectionGrabber(QXcbConnection *connection);
395 ~QXcbConnectionGrabber();
396 void release();
397private:
398 QXcbConnection *m_connection;
399};
400
401// The xcb_send_event() requires all events to have 32 bytes. It calls memcpy() on the
402// passed in event. If the passed in event is less than 32 bytes, memcpy() reaches into
403// unrelated memory.
404template <typename T>
405struct alignas(32) q_padded_xcb_event : T { };
406
407QT_END_NAMESPACE
408
409#endif
410

source code of qtbase/src/plugins/platforms/xcb/qxcbconnection.h