Commit 4b0c2e60 authored by Martin Tobias Holmedahl Sandsmark's avatar Martin Tobias Holmedahl Sandsmark Committed by Jonathan Marten
Browse files

New file backends/qtpamainloop.h left out of commit e20ad13f

parent 8bcf9255
/*
* KMix -- KDE's full featured mini mixer
*
*
* Copyright (C) 2020 Martin Sandsmark <martin.sandsmark@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include <QCoreApplication>
#include <QTimer>
#include <QSocketNotifier>
#include <QDateTime>
#include <QDebug>
#include <pulse/mainloop-api.h>
// Not really its own mainloop, just wraps Qt stuff for pulseaudio
struct QtPaMainLoop {
pa_mainloop_api pa_vtable{};
QtPaMainLoop()
{
pa_vtable.userdata = this;
pa_vtable.io_new = newIoEvent;
pa_vtable.io_enable = setIoEnabled;
pa_vtable.io_free = ioDestroy;
pa_vtable.io_set_destroy = setIoDestructor;
pa_vtable.time_new = newTimer;
pa_vtable.time_restart = restartTimer;
pa_vtable.time_free = freeTimer;
pa_vtable.time_set_destroy = timerSetDestructor;
pa_vtable.time_set_destroy = timerSetDestructor;
pa_vtable.defer_new = newDefer;
pa_vtable.defer_enable = setDeferEnabled;
pa_vtable.defer_free = freeDefer;
pa_vtable.defer_set_destroy = deferSetDestructor;
pa_vtable.quit = quit;
}
static int msecsUntilTimeval(const struct timeval *tv)
{
time_t target = tv->tv_sec * 1000;
if (tv->tv_usec) {
target += tv->tv_usec / 1000;
}
return QDateTime::currentDateTime().msecsTo(QDateTime::fromMSecsSinceEpoch(target));
}
static pa_time_event *newTimer(pa_mainloop_api *a, const struct timeval *tv, pa_time_event_cb_t callback, void *userdata)
{
QTimer *timer = new QTimer;
timer->setProperty("PA_USERDATA", QVariant::fromValue(userdata));
timer->setParent(qApp);
timer->setSingleShot(true);
Qt::TimerType timerType = Qt::VeryCoarseTimer;
if (tv->tv_usec) {
timerType = Qt::PreciseTimer;
}
timer->setTimerType(timerType);
QObject::connect(timer, &QTimer::timeout, [ = ]() {
callback(a, reinterpret_cast<pa_time_event *>(timer), tv, userdata);
});
int duration = msecsUntilTimeval(tv);
if (duration < 0) {
qWarning() << "Invalid timer target, sec:" << tv->tv_sec << "usec" << tv->tv_usec;
}
timer->start(duration);
return reinterpret_cast<pa_time_event *>(timer);
}
static void restartTimer(pa_time_event *e, const timeval *tv)
{
QTimer *timer = reinterpret_cast<QTimer *>(e);
Qt::TimerType timerType = Qt::VeryCoarseTimer;
if (tv->tv_usec) {
timerType = Qt::PreciseTimer;
}
timer->setTimerType(timerType);
int duration = msecsUntilTimeval(tv);
if (duration < 0) {
qWarning() << "Invalid restart timer target, sec:" << tv->tv_sec << "usec" << tv->tv_usec;
}
timer->start(duration);
}
static void freeTimer(pa_time_event *e)
{
QTimer *timer = reinterpret_cast<QTimer *>(e);
delete timer;
}
static void timerSetDestructor(pa_time_event *e, pa_time_event_destroy_cb_t destructor)
{
QTimer *timer = reinterpret_cast<QTimer *>(e);
QObject::connect(timer, &QTimer::destroyed, [ = ]() {
destructor(reinterpret_cast<pa_mainloop_api *>(timer->parent()), e, qvariant_cast<void *>(timer->property("PA_USERDATA")));
});
}
struct SocketNotifierWrapper {
SocketNotifierWrapper() = default;
SocketNotifierWrapper(const SocketNotifierWrapper &) = delete;
const SocketNotifierWrapper &operator=(const SocketNotifierWrapper &) = delete;
~SocketNotifierWrapper()
{
delete readNotifier;
delete writeNotifier;
delete errorNotifier;
if (destructor) {
destructor(a, reinterpret_cast<pa_io_event *>(this), userdata);
}
}
QSocketNotifier *readNotifier = nullptr;
QSocketNotifier *writeNotifier = nullptr;
QSocketNotifier *errorNotifier = nullptr;
pa_io_event_destroy_cb_t destructor = nullptr;
void *userdata;
pa_mainloop_api *a;
};
static pa_io_event *newIoEvent(pa_mainloop_api *a, int fd, pa_io_event_flags_t events, pa_io_event_cb_t cb, void *userdata)
{
SocketNotifierWrapper *wrapper = new SocketNotifierWrapper;
wrapper->userdata = userdata;
wrapper->a = a;
wrapper->readNotifier = new QSocketNotifier(fd, QSocketNotifier::Read, qApp);
QObject::connect(wrapper->readNotifier, &QSocketNotifier::activated, [ = ]() {
cb(a, reinterpret_cast<pa_io_event *>(wrapper), fd, PA_IO_EVENT_INPUT, userdata);
});
wrapper->writeNotifier = new QSocketNotifier(fd, QSocketNotifier::Write, qApp);
QObject::connect(wrapper->writeNotifier, &QSocketNotifier::activated, [ = ]() {
cb(a, reinterpret_cast<pa_io_event *>(wrapper), fd, PA_IO_EVENT_OUTPUT, userdata);
});
wrapper->errorNotifier = new QSocketNotifier(fd, QSocketNotifier::Exception, qApp);
QObject::connect(wrapper->errorNotifier, &QSocketNotifier::activated, [ = ]() {
cb(a, reinterpret_cast<pa_io_event *>(wrapper), fd, PA_IO_EVENT_ERROR, userdata);
});
if (events & PA_IO_EVENT_INPUT) {
wrapper->readNotifier->setEnabled(true);
} else {
wrapper->readNotifier->setEnabled(false);
}
if (events & PA_IO_EVENT_OUTPUT) {
wrapper->writeNotifier->setEnabled(true);
} else {
wrapper->writeNotifier->setEnabled(false);
}
if (events & PA_IO_EVENT_ERROR || events & PA_IO_EVENT_HANGUP) {
wrapper->errorNotifier->setEnabled(true);
} else {
wrapper->errorNotifier->setEnabled(false);
}
return reinterpret_cast<pa_io_event *>(wrapper);
}
static void setIoEnabled(pa_io_event *e, pa_io_event_flags_t events)
{
SocketNotifierWrapper *wrapper = reinterpret_cast<SocketNotifierWrapper *>(e);
if (events & PA_IO_EVENT_INPUT) {
wrapper->readNotifier->setEnabled(true);
} else {
wrapper->readNotifier->setEnabled(false);
}
if (events & PA_IO_EVENT_OUTPUT) {
wrapper->writeNotifier->setEnabled(true);
} else {
wrapper->writeNotifier->setEnabled(false);
}
if (events & PA_IO_EVENT_ERROR || events & PA_IO_EVENT_HANGUP) {
wrapper->errorNotifier->setEnabled(true);
} else {
wrapper->errorNotifier->setEnabled(false);
}
}
static void ioDestroy(pa_io_event *e)
{
SocketNotifierWrapper *wrapper = reinterpret_cast<SocketNotifierWrapper *>(e);
delete wrapper;
}
static void setIoDestructor(pa_io_event *e, pa_io_event_destroy_cb_t cb)
{
SocketNotifierWrapper *wrapper = reinterpret_cast<SocketNotifierWrapper *>(e);
wrapper->destructor = cb;
}
static pa_defer_event *newDefer(pa_mainloop_api *a, pa_defer_event_cb_t callback, void *userdata)
{
QTimer *timer = new QTimer();
timer->setProperty("PA_USERDATA", QVariant::fromValue(userdata));
timer->setParent(qApp);
//timer->setParent(reinterpret_cast<QtPaMainLoop*>(a));
timer->setSingleShot(true);
QObject::connect(timer, &QTimer::timeout, [ = ]() {
callback(a, reinterpret_cast<pa_defer_event *>(timer), userdata);
});
timer->start(0);
return reinterpret_cast<pa_defer_event *>(timer);
}
static void setDeferEnabled(pa_defer_event *e, int b)
{
QTimer *timer = reinterpret_cast<QTimer *>(e);
if (b) {
timer->start(0);
} else {
timer->stop();
}
}
static void freeDefer(pa_defer_event *e)
{
QTimer *timer = reinterpret_cast<QTimer *>(e);
delete timer;
}
static void deferSetDestructor(pa_defer_event *e, pa_defer_event_destroy_cb_t destructor)
{
QTimer *timer = reinterpret_cast<QTimer *>(e);
QObject::connect(timer, &QTimer::destroyed, [ = ]() {
destructor(reinterpret_cast<pa_mainloop_api *>(timer->parent()), e, qvariant_cast<void *>(timer->property("PA_USERDATA")));
});
}
static void quit(pa_mainloop_api *a, int retval)
{
Q_UNUSED(a);
qApp->exit(retval);
}
};
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment