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
11QT_BEGIN_NAMESPACE
12
13Q_DECLARE_LOGGING_CATEGORY(qLcEglfsKmsDebug)
14
15static 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
26class RegisterWaitFlipEvent : public QEvent
27{
28public:
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
38bool 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
63void 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
88void 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
112void QEglFSKmsEventReaderThread::run()
113{
114 qCDebug(qLcEglfsKmsDebug, "Event reader thread: entering event loop");
115
116 QSocketNotifier notifier(m_fd, QSocketNotifier::Read);
117 QObject::connect(sender: &notifier, signal: &QSocketNotifier::activated, context: &notifier, 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
133QEglFSKmsEventReader::~QEglFSKmsEventReader()
134{
135 destroy();
136}
137
138void 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
158void 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
175void 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
183QT_END_NAMESPACE
184

source code of qtbase/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmseventreader.cpp