1// Copyright (C) 2018 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3#ifndef QXCBEVENTQUEUE_H
4#define QXCBEVENTQUEUE_H
5
6#include <QtCore/QThread>
7#include <QtCore/QHash>
8#include <QtCore/QEventLoop>
9#include <QtCore/QList>
10#include <QtCore/QMutex>
11#include <QtCore/QWaitCondition>
12
13#include <xcb/xcb.h>
14
15#include <atomic>
16#include <limits>
17
18QT_BEGIN_NAMESPACE
19
20struct QXcbEventNode {
21 QXcbEventNode(xcb_generic_event_t *e = nullptr)
22 : event(e) { }
23
24 xcb_generic_event_t *event;
25 QXcbEventNode *next = nullptr;
26 bool fromHeap = false;
27};
28
29class QXcbConnection;
30class QAbstractEventDispatcher;
31
32class QXcbEventQueue : public QThread
33{
34 Q_OBJECT
35public:
36 QXcbEventQueue(QXcbConnection *connection);
37 ~QXcbEventQueue();
38
39 enum { PoolSize = 100 }; // 2.4 kB with 100 nodes
40
41 enum PeekOption {
42 // See qx11info_x11.cpp in X11 Extras module.
43 PeekDefault = 0,
44 // See qx11info_x11.cpp in X11 Extras module.
45 PeekFromCachedIndex = 1,
46 // Used by the event compression algorithms to determine if
47 // the currently processed event (which has been already dequeued)
48 // can be compressed. Returns from the QXcbEventQueue::peek()
49 // on the first match.
50 PeekRetainMatch = 2,
51 // Marks the event in the node as "nullptr". The actual
52 // node remains in the queue. The nodes are unlinked only
53 // by dequeueNode(). Returns from the QXcbEventQueue::peek()
54 // on the first match.
55 PeekConsumeMatch = 3,
56 // Same as above, but continues to the next node in the
57 // queue. Repeats this until the flushed tailed node has
58 // been reached.
59 PeekConsumeMatchAndContinue = 4
60 };
61 Q_DECLARE_FLAGS(PeekOptions, PeekOption)
62
63 void run() override;
64
65 bool isEmpty() const { return m_head == m_flushedTail && !m_head->event; }
66 xcb_generic_event_t *takeFirst(QEventLoop::ProcessEventsFlags flags);
67 xcb_generic_event_t *takeFirst();
68 void flushBufferedEvents();
69 void wakeUpDispatcher();
70
71 // ### peek() and peekEventQueue() could be unified. Note that peekEventQueue()
72 // is public API exposed via QX11Extras/QX11Info. PeekOption could be reworked to
73 // have values that can be OR-ed together.
74 template<typename Peeker>
75 xcb_generic_event_t *peek(Peeker &&peeker) {
76 return peek(PeekConsumeMatch, std::forward<Peeker>(peeker));
77 }
78 template<typename Peeker>
79 inline xcb_generic_event_t *peek(PeekOption config, Peeker &&peeker);
80
81 qint32 generatePeekerId();
82 bool removePeekerId(qint32 peekerId);
83
84 using PeekerCallback = bool (*)(xcb_generic_event_t *event, void *peekerData);
85 bool peekEventQueue(PeekerCallback peeker, void *peekerData = nullptr,
86 PeekOptions option = PeekDefault, qint32 peekerId = -1);
87
88 const QXcbEventNode *flushedTail() const { return m_flushedTail; }
89 void waitForNewEvents(const QXcbEventNode *sinceFlushedTail,
90 unsigned long time = (std::numeric_limits<unsigned long>::max)());
91
92private:
93 QXcbEventNode *qXcbEventNodeFactory(xcb_generic_event_t *event);
94 void dequeueNode();
95
96 void sendCloseConnectionEvent() const;
97 bool isCloseConnectionEvent(const xcb_generic_event_t *event);
98
99 QXcbEventNode *m_head = nullptr;
100 QXcbEventNode *m_flushedTail = nullptr;
101 std::atomic<QXcbEventNode *> m_tail { nullptr };
102 std::atomic_uint m_nodesRestored { 0 };
103
104 QXcbConnection *m_connection = nullptr;
105 bool m_closeConnectionDetected = false;
106
107 uint m_freeNodes = PoolSize;
108 uint m_poolIndex = 0;
109
110 qint32 m_peekerIdSource = 0;
111 bool m_queueModified = false;
112 bool m_peekerIndexCacheDirty = false;
113 QHash<qint32, QXcbEventNode *> m_peekerToNode;
114
115 QList<xcb_generic_event_t *> m_inputEvents;
116
117 // debug stats
118 quint64 m_nodesOnHeap = 0;
119
120 QMutex m_newEventsMutex;
121 QWaitCondition m_newEventsCondition;
122};
123
124template<typename Peeker>
125xcb_generic_event_t *QXcbEventQueue::peek(PeekOption option, Peeker &&peeker)
126{
127 flushBufferedEvents();
128 if (isEmpty())
129 return nullptr;
130
131 QXcbEventNode *node = m_head;
132 do {
133 xcb_generic_event_t *event = node->event;
134 if (event && peeker(event, event->response_type & ~0x80)) {
135 if (option == PeekConsumeMatch || option == PeekConsumeMatchAndContinue)
136 node->event = nullptr;
137
138 if (option != PeekConsumeMatchAndContinue)
139 return event;
140 }
141 if (node == m_flushedTail)
142 break;
143 node = node->next;
144 } while (true);
145
146 return nullptr;
147}
148
149QT_END_NAMESPACE
150
151#endif
152

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