1 | // Copyright (C) 2019 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 | |
4 | #include "qeglfskmseventreader_p.h" |
5 | #include "qeglfskmsdevice_p.h" |
6 | #include "qeglfskmsscreen_p.h" |
7 | #include <QSocketNotifier> |
8 | #include <QCoreApplication> |
9 | #include <QLoggingCategory> |
10 | |
11 | QT_BEGIN_NAMESPACE |
12 | |
13 | Q_DECLARE_LOGGING_CATEGORY(qLcEglfsKmsDebug) |
14 | |
15 | static void pageFlipHandler(int fd, unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec, void *user_data) |
16 | { |
17 | Q_UNUSED(fd); |
18 | |
19 | QEglFSKmsEventReaderThread *t = static_cast<QEglFSKmsEventReaderThread *>(QThread::currentThread()); |
20 | t->eventHost()->handlePageFlipCompleted(key: user_data); |
21 | |
22 | QEglFSKmsScreen *screen = static_cast<QEglFSKmsScreen *>(user_data); |
23 | screen->pageFlipped(sequence, tv_sec, tv_usec); |
24 | } |
25 | |
26 | class RegisterWaitFlipEvent : public QEvent |
27 | { |
28 | public: |
29 | static const QEvent::Type TYPE = QEvent::Type(QEvent::User + 1); |
30 | RegisterWaitFlipEvent(void *key, QMutex *mutex, QWaitCondition *cond) |
31 | : QEvent(TYPE), key(key), mutex(mutex), cond(cond) |
32 | { } |
33 | void *key; |
34 | QMutex *mutex; |
35 | QWaitCondition *cond; |
36 | }; |
37 | |
38 | bool QEglFSKmsEventHost::event(QEvent *event) |
39 | { |
40 | if (event->type() == RegisterWaitFlipEvent::TYPE) { |
41 | RegisterWaitFlipEvent *e = static_cast<RegisterWaitFlipEvent *>(event); |
42 | PendingFlipWait *p = &pendingFlipWaits[0]; |
43 | PendingFlipWait *end = p + MAX_FLIPS; |
44 | while (p < end) { |
45 | if (!p->key) { |
46 | p->key = e->key; |
47 | p->mutex = e->mutex; |
48 | p->cond = e->cond; |
49 | updateStatus(); |
50 | return true; |
51 | } |
52 | ++p; |
53 | } |
54 | qWarning(msg: "Cannot queue page flip wait (more than %d screens?)" , MAX_FLIPS); |
55 | e->mutex->lock(); |
56 | e->cond->wakeOne(); |
57 | e->mutex->unlock(); |
58 | return true; |
59 | } |
60 | return QObject::event(event); |
61 | } |
62 | |
63 | void QEglFSKmsEventHost::updateStatus() |
64 | { |
65 | void **begin = &completedFlips[0]; |
66 | void **end = begin + MAX_FLIPS; |
67 | |
68 | for (int i = 0; i < MAX_FLIPS; ++i) { |
69 | PendingFlipWait *w = pendingFlipWaits + i; |
70 | if (!w->key) |
71 | continue; |
72 | |
73 | void **p = begin; |
74 | while (p < end) { |
75 | if (*p == w->key) { |
76 | *p = nullptr; |
77 | w->key = nullptr; |
78 | w->mutex->lock(); |
79 | w->cond->wakeOne(); |
80 | w->mutex->unlock(); |
81 | return; |
82 | } |
83 | ++p; |
84 | } |
85 | } |
86 | } |
87 | |
88 | void QEglFSKmsEventHost::handlePageFlipCompleted(void *key) |
89 | { |
90 | void **begin = &completedFlips[0]; |
91 | void **end = begin + MAX_FLIPS; |
92 | void **p = begin; |
93 | while (p < end) { |
94 | if (*p == key) { |
95 | updateStatus(); |
96 | return; |
97 | } |
98 | ++p; |
99 | } |
100 | p = begin; |
101 | while (p < end) { |
102 | if (!*p) { |
103 | *p = key; |
104 | updateStatus(); |
105 | return; |
106 | } |
107 | ++p; |
108 | } |
109 | qWarning(msg: "Cannot store page flip status (more than %d screens?)" , MAX_FLIPS); |
110 | } |
111 | |
112 | void QEglFSKmsEventReaderThread::run() |
113 | { |
114 | qCDebug(qLcEglfsKmsDebug, "Event reader thread: entering event loop" ); |
115 | |
116 | QSocketNotifier notifier(m_fd, QSocketNotifier::Read); |
117 | QObject::connect(sender: ¬ifier, signal: &QSocketNotifier::activated, context: ¬ifier, slot: [this] { |
118 | drmEventContext drmEvent; |
119 | memset(s: &drmEvent, c: 0, n: sizeof(drmEvent)); |
120 | drmEvent.version = 2; |
121 | drmEvent.vblank_handler = nullptr; |
122 | drmEvent.page_flip_handler = pageFlipHandler; |
123 | drmHandleEvent(fd: m_fd, evctx: &drmEvent); |
124 | }); |
125 | |
126 | exec(); |
127 | |
128 | m_ev.moveToThread(thread: thread()); // move back to the thread where m_ev was created |
129 | |
130 | qCDebug(qLcEglfsKmsDebug, "Event reader thread: event loop stopped" ); |
131 | } |
132 | |
133 | QEglFSKmsEventReader::~QEglFSKmsEventReader() |
134 | { |
135 | destroy(); |
136 | } |
137 | |
138 | void QEglFSKmsEventReader::create(QEglFSKmsDevice *device) |
139 | { |
140 | destroy(); |
141 | |
142 | if (!device) |
143 | return; |
144 | |
145 | m_device = device; |
146 | |
147 | qCDebug(qLcEglfsKmsDebug, "Initializing event reader for device %p fd %d" , |
148 | m_device, m_device->fd()); |
149 | |
150 | m_thread = new QEglFSKmsEventReaderThread(m_device->fd()); |
151 | m_thread->start(); |
152 | |
153 | // Change thread affinity for the event host, so that postEvent() |
154 | // goes through the event reader thread's event loop for that object. |
155 | m_thread->eventHost()->moveToThread(thread: m_thread); |
156 | } |
157 | |
158 | void QEglFSKmsEventReader::destroy() |
159 | { |
160 | if (!m_device) |
161 | return; |
162 | |
163 | qCDebug(qLcEglfsKmsDebug, "Stopping event reader for device %p" , m_device); |
164 | |
165 | if (m_thread) { |
166 | m_thread->quit(); |
167 | m_thread->wait(); |
168 | delete m_thread; |
169 | m_thread = nullptr; |
170 | } |
171 | |
172 | m_device = nullptr; |
173 | } |
174 | |
175 | void QEglFSKmsEventReader::startWaitFlip(void *key, QMutex *mutex, QWaitCondition *cond) |
176 | { |
177 | if (m_thread) { |
178 | QCoreApplication::postEvent(receiver: m_thread->eventHost(), |
179 | event: new RegisterWaitFlipEvent(key, mutex, cond)); |
180 | } |
181 | } |
182 | |
183 | QT_END_NAMESPACE |
184 | |