1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2019 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 | #include "qeglfskmseventreader.h" |
41 | #include "qeglfskmsdevice.h" |
42 | #include <QSocketNotifier> |
43 | #include <QCoreApplication> |
44 | #include <QLoggingCategory> |
45 | |
46 | QT_BEGIN_NAMESPACE |
47 | |
48 | Q_DECLARE_LOGGING_CATEGORY(qLcEglfsKmsDebug) |
49 | |
50 | static void pageFlipHandler(int fd, unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec, void *user_data) |
51 | { |
52 | Q_UNUSED(fd); |
53 | Q_UNUSED(sequence); |
54 | Q_UNUSED(tv_sec); |
55 | Q_UNUSED(tv_usec); |
56 | |
57 | QEglFSKmsEventReaderThread *t = static_cast<QEglFSKmsEventReaderThread *>(QThread::currentThread()); |
58 | t->eventHost()->handlePageFlipCompleted(key: user_data); |
59 | } |
60 | |
61 | class RegisterWaitFlipEvent : public QEvent |
62 | { |
63 | public: |
64 | static const QEvent::Type TYPE = QEvent::Type(QEvent::User + 1); |
65 | RegisterWaitFlipEvent(void *key, QMutex *mutex, QWaitCondition *cond) |
66 | : QEvent(TYPE), key(key), mutex(mutex), cond(cond) |
67 | { } |
68 | void *key; |
69 | QMutex *mutex; |
70 | QWaitCondition *cond; |
71 | }; |
72 | |
73 | bool QEglFSKmsEventHost::event(QEvent *event) |
74 | { |
75 | if (event->type() == RegisterWaitFlipEvent::TYPE) { |
76 | RegisterWaitFlipEvent *e = static_cast<RegisterWaitFlipEvent *>(event); |
77 | PendingFlipWait *p = &pendingFlipWaits[0]; |
78 | PendingFlipWait *end = p + MAX_FLIPS; |
79 | while (p < end) { |
80 | if (!p->key) { |
81 | p->key = e->key; |
82 | p->mutex = e->mutex; |
83 | p->cond = e->cond; |
84 | updateStatus(); |
85 | return true; |
86 | } |
87 | ++p; |
88 | } |
89 | qWarning(msg: "Cannot queue page flip wait (more than %d screens?)" , MAX_FLIPS); |
90 | e->mutex->lock(); |
91 | e->cond->wakeOne(); |
92 | e->mutex->unlock(); |
93 | return true; |
94 | } |
95 | return QObject::event(event); |
96 | } |
97 | |
98 | void QEglFSKmsEventHost::updateStatus() |
99 | { |
100 | void **begin = &completedFlips[0]; |
101 | void **end = begin + MAX_FLIPS; |
102 | |
103 | for (int i = 0; i < MAX_FLIPS; ++i) { |
104 | PendingFlipWait *w = pendingFlipWaits + i; |
105 | if (!w->key) |
106 | continue; |
107 | |
108 | void **p = begin; |
109 | while (p < end) { |
110 | if (*p == w->key) { |
111 | *p = nullptr; |
112 | w->key = nullptr; |
113 | w->mutex->lock(); |
114 | w->cond->wakeOne(); |
115 | w->mutex->unlock(); |
116 | return; |
117 | } |
118 | ++p; |
119 | } |
120 | } |
121 | } |
122 | |
123 | void QEglFSKmsEventHost::handlePageFlipCompleted(void *key) |
124 | { |
125 | void **begin = &completedFlips[0]; |
126 | void **end = begin + MAX_FLIPS; |
127 | void **p = begin; |
128 | while (p < end) { |
129 | if (*p == key) { |
130 | updateStatus(); |
131 | return; |
132 | } |
133 | ++p; |
134 | } |
135 | p = begin; |
136 | while (p < end) { |
137 | if (!*p) { |
138 | *p = key; |
139 | updateStatus(); |
140 | return; |
141 | } |
142 | ++p; |
143 | } |
144 | qWarning(msg: "Cannot store page flip status (more than %d screens?)" , MAX_FLIPS); |
145 | } |
146 | |
147 | void QEglFSKmsEventReaderThread::run() |
148 | { |
149 | qCDebug(qLcEglfsKmsDebug, "Event reader thread: entering event loop" ); |
150 | |
151 | QSocketNotifier notifier(m_fd, QSocketNotifier::Read); |
152 | QObject::connect(sender: ¬ifier, signal: &QSocketNotifier::activated, context: ¬ifier, slot: [this] { |
153 | drmEventContext drmEvent; |
154 | memset(s: &drmEvent, c: 0, n: sizeof(drmEvent)); |
155 | drmEvent.version = 2; |
156 | drmEvent.vblank_handler = nullptr; |
157 | drmEvent.page_flip_handler = pageFlipHandler; |
158 | drmHandleEvent(fd: m_fd, evctx: &drmEvent); |
159 | }); |
160 | |
161 | exec(); |
162 | |
163 | m_ev.moveToThread(thread: thread()); // move back to the thread where m_ev was created |
164 | |
165 | qCDebug(qLcEglfsKmsDebug, "Event reader thread: event loop stopped" ); |
166 | } |
167 | |
168 | QEglFSKmsEventReader::~QEglFSKmsEventReader() |
169 | { |
170 | destroy(); |
171 | } |
172 | |
173 | void QEglFSKmsEventReader::create(QEglFSKmsDevice *device) |
174 | { |
175 | destroy(); |
176 | |
177 | if (!device) |
178 | return; |
179 | |
180 | m_device = device; |
181 | |
182 | qCDebug(qLcEglfsKmsDebug, "Initalizing event reader for device %p fd %d" , |
183 | m_device, m_device->fd()); |
184 | |
185 | m_thread = new QEglFSKmsEventReaderThread(m_device->fd()); |
186 | m_thread->start(); |
187 | |
188 | // Change thread affinity for the event host, so that postEvent() |
189 | // goes through the event reader thread's event loop for that object. |
190 | m_thread->eventHost()->moveToThread(thread: m_thread); |
191 | } |
192 | |
193 | void QEglFSKmsEventReader::destroy() |
194 | { |
195 | if (!m_device) |
196 | return; |
197 | |
198 | qCDebug(qLcEglfsKmsDebug, "Stopping event reader for device %p" , m_device); |
199 | |
200 | if (m_thread) { |
201 | m_thread->quit(); |
202 | m_thread->wait(); |
203 | delete m_thread; |
204 | m_thread = nullptr; |
205 | } |
206 | |
207 | m_device = nullptr; |
208 | } |
209 | |
210 | void QEglFSKmsEventReader::startWaitFlip(void *key, QMutex *mutex, QWaitCondition *cond) |
211 | { |
212 | if (m_thread) { |
213 | QCoreApplication::postEvent(receiver: m_thread->eventHost(), |
214 | event: new RegisterWaitFlipEvent(key, mutex, cond)); |
215 | } |
216 | } |
217 | |
218 | QT_END_NAMESPACE |
219 | |