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_p.h"
41#include "qeglfskmsdevice_p.h"
42#include <QSocketNotifier>
43#include <QCoreApplication>
44#include <QLoggingCategory>
45
46QT_BEGIN_NAMESPACE
47
48Q_DECLARE_LOGGING_CATEGORY(qLcEglfsKmsDebug)
49
50static 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(user_data);
59}
60
61class RegisterWaitFlipEvent : public QEvent
62{
63public:
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
73bool 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("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
98void 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
123void 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("Cannot store page flip status (more than %d screens?)", MAX_FLIPS);
145}
146
147void QEglFSKmsEventReaderThread::run()
148{
149 qCDebug(qLcEglfsKmsDebug, "Event reader thread: entering event loop");
150
151 QSocketNotifier notifier(m_fd, QSocketNotifier::Read);
152 QObject::connect(&notifier, &QSocketNotifier::activated, &notifier, [this] {
153 drmEventContext drmEvent;
154 memset(&drmEvent, 0, sizeof(drmEvent));
155 drmEvent.version = 2;
156 drmEvent.vblank_handler = nullptr;
157 drmEvent.page_flip_handler = pageFlipHandler;
158 drmHandleEvent(m_fd, &drmEvent);
159 });
160
161 exec();
162
163 m_ev.moveToThread(thread()); // move back to the thread where m_ev was created
164
165 qCDebug(qLcEglfsKmsDebug, "Event reader thread: event loop stopped");
166}
167
168QEglFSKmsEventReader::~QEglFSKmsEventReader()
169{
170 destroy();
171}
172
173void 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(m_thread);
191}
192
193void 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
210void QEglFSKmsEventReader::startWaitFlip(void *key, QMutex *mutex, QWaitCondition *cond)
211{
212 if (m_thread) {
213 QCoreApplication::postEvent(m_thread->eventHost(),
214 new RegisterWaitFlipEvent(key, mutex, cond));
215 }
216}
217
218QT_END_NAMESPACE
219