diff options
Diffstat (limited to 'src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmseventreader.cpp')
-rw-r--r-- | src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmseventreader.cpp | 218 |
1 files changed, 218 insertions, 0 deletions
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmseventreader.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmseventreader.cpp new file mode 100644 index 0000000000..645a0ae2e9 --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmseventreader.cpp @@ -0,0 +1,218 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qeglfskmseventreader.h" +#include "qeglfskmsdevice.h" +#include <QSocketNotifier> +#include <QCoreApplication> +#include <QLoggingCategory> + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(qLcEglfsKmsDebug) + +static void pageFlipHandler(int fd, unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec, void *user_data) +{ + Q_UNUSED(fd); + Q_UNUSED(sequence); + Q_UNUSED(tv_sec); + Q_UNUSED(tv_usec); + + QEglFSKmsEventReaderThread *t = static_cast<QEglFSKmsEventReaderThread *>(QThread::currentThread()); + t->eventHost()->handlePageFlipCompleted(user_data); +} + +class RegisterWaitFlipEvent : public QEvent +{ +public: + static const QEvent::Type TYPE = QEvent::Type(QEvent::User + 1); + RegisterWaitFlipEvent(void *key, QMutex *mutex, QWaitCondition *cond) + : QEvent(TYPE), key(key), mutex(mutex), cond(cond) + { } + void *key; + QMutex *mutex; + QWaitCondition *cond; +}; + +bool QEglFSKmsEventHost::event(QEvent *event) +{ + if (event->type() == RegisterWaitFlipEvent::TYPE) { + RegisterWaitFlipEvent *e = static_cast<RegisterWaitFlipEvent *>(event); + PendingFlipWait *p = &pendingFlipWaits[0]; + PendingFlipWait *end = p + MAX_FLIPS; + while (p < end) { + if (!p->key) { + p->key = e->key; + p->mutex = e->mutex; + p->cond = e->cond; + updateStatus(); + return true; + } + ++p; + } + qWarning("Cannot queue page flip wait (more than %d screens?)", MAX_FLIPS); + e->mutex->lock(); + e->cond->wakeOne(); + e->mutex->unlock(); + return true; + } + return QObject::event(event); +} + +void QEglFSKmsEventHost::updateStatus() +{ + void **begin = &completedFlips[0]; + void **end = begin + MAX_FLIPS; + + for (int i = 0; i < MAX_FLIPS; ++i) { + PendingFlipWait *w = pendingFlipWaits + i; + if (!w->key) + continue; + + void **p = begin; + while (p < end) { + if (*p == w->key) { + *p = nullptr; + w->key = nullptr; + w->mutex->lock(); + w->cond->wakeOne(); + w->mutex->unlock(); + return; + } + ++p; + } + } +} + +void QEglFSKmsEventHost::handlePageFlipCompleted(void *key) +{ + void **begin = &completedFlips[0]; + void **end = begin + MAX_FLIPS; + void **p = begin; + while (p < end) { + if (*p == key) { + updateStatus(); + return; + } + ++p; + } + p = begin; + while (p < end) { + if (!*p) { + *p = key; + updateStatus(); + return; + } + ++p; + } + qWarning("Cannot store page flip status (more than %d screens?)", MAX_FLIPS); +} + +void QEglFSKmsEventReaderThread::run() +{ + qCDebug(qLcEglfsKmsDebug, "Event reader thread: entering event loop"); + + QSocketNotifier notifier(m_fd, QSocketNotifier::Read); + QObject::connect(¬ifier, &QSocketNotifier::activated, ¬ifier, [this] { + drmEventContext drmEvent; + memset(&drmEvent, 0, sizeof(drmEvent)); + drmEvent.version = 2; + drmEvent.vblank_handler = nullptr; + drmEvent.page_flip_handler = pageFlipHandler; + drmHandleEvent(m_fd, &drmEvent); + }); + + exec(); + + m_ev.moveToThread(thread()); // move back to the thread where m_ev was created + + qCDebug(qLcEglfsKmsDebug, "Event reader thread: event loop stopped"); +} + +QEglFSKmsEventReader::~QEglFSKmsEventReader() +{ + destroy(); +} + +void QEglFSKmsEventReader::create(QEglFSKmsDevice *device) +{ + destroy(); + + if (!device) + return; + + m_device = device; + + qCDebug(qLcEglfsKmsDebug, "Initalizing event reader for device %p fd %d", + m_device, m_device->fd()); + + m_thread = new QEglFSKmsEventReaderThread(m_device->fd()); + m_thread->start(); + + // Change thread affinity for the event host, so that postEvent() + // goes through the event reader thread's event loop for that object. + m_thread->eventHost()->moveToThread(m_thread); +} + +void QEglFSKmsEventReader::destroy() +{ + if (!m_device) + return; + + qCDebug(qLcEglfsKmsDebug, "Stopping event reader for device %p", m_device); + + if (m_thread) { + m_thread->quit(); + m_thread->wait(); + delete m_thread; + m_thread = nullptr; + } + + m_device = nullptr; +} + +void QEglFSKmsEventReader::startWaitFlip(void *key, QMutex *mutex, QWaitCondition *cond) +{ + if (m_thread) { + QCoreApplication::postEvent(m_thread->eventHost(), + new RegisterWaitFlipEvent(key, mutex, cond)); + } +} + +QT_END_NAMESPACE |