File indexing completed on 2024-12-01 04:21:38

0001 /*
0002     SPDX-FileCopyrightText: 2014-2015 Harald Sitter <sitter@kde.org>
0003     SPDX-FileCopyrightText: 2018 David Rosca <nowrep@gmail.com>
0004 
0005     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0006 */
0007 
0008 #pragma once
0009 
0010 #include <QHash>
0011 #include <QObject>
0012 #include <QSet>
0013 #include <QVector>
0014 
0015 #include <pulse/ext-stream-restore.h>
0016 #include <pulse/pulseaudio.h>
0017 
0018 #include "card_p.h"
0019 #include "client_p.h"
0020 #include "module_p.h"
0021 #include "sink_p.h"
0022 #include "sinkinput_p.h"
0023 #include "source_p.h"
0024 #include "sourceoutput_p.h"
0025 #include "streamrestore_p.h"
0026 
0027 namespace PulseAudioQt
0028 {
0029 // Used for typedefs.
0030 class Card;
0031 class Client;
0032 class Sink;
0033 class SinkInput;
0034 class Source;
0035 class SourceOutput;
0036 class StreamRestore;
0037 class Module;
0038 
0039 /**
0040  * @see MapBase
0041  * This class is nothing more than the QObject base since moc cannot handle
0042  * templates.
0043  */
0044 class MapBaseQObject : public QObject
0045 {
0046     Q_OBJECT
0047 
0048 public:
0049     virtual int count() const = 0;
0050     virtual QObject *objectAt(int index) const = 0;
0051     virtual int indexOfObject(QObject *object) const = 0;
0052 
0053 Q_SIGNALS:
0054     void aboutToBeAdded(int index);
0055     void added(int index, QObject *object);
0056     void aboutToBeRemoved(int index);
0057     void removed(int index, QObject *object);
0058 };
0059 
0060 /**
0061  * Maps a specific index to a specific object pointer.
0062  * This is used to give the unique arbitrary PulseAudio index of a PulseObject a
0063  * serialized list index. Namely it enables us to translate a discrete list
0064  * index to a pulse index to an object, and any permutation thereof.
0065  */
0066 template<typename Type, typename PAInfo>
0067 class MapBase : public MapBaseQObject
0068 {
0069 public:
0070     virtual ~MapBase()
0071     {
0072     }
0073 
0074     const QVector<Type *> &data() const
0075     {
0076         return m_data;
0077     }
0078 
0079     int count() const override
0080     {
0081         return m_data.count();
0082     }
0083 
0084     int indexOfObject(QObject *object) const override
0085     {
0086         return m_data.indexOf(static_cast<Type *>(object));
0087     }
0088 
0089     QObject *objectAt(int index) const override
0090     {
0091         return m_data.at(index);
0092     }
0093 
0094     void reset()
0095     {
0096         while (!m_hash.isEmpty()) {
0097             removeEntry(m_data.at(m_data.count() - 1)->index());
0098         }
0099         m_pendingRemovals.clear();
0100     }
0101 
0102     void insert(Type *object)
0103     {
0104         Q_ASSERT(!m_data.contains(object));
0105 
0106         const int modelIndex = m_data.count();
0107 
0108         Q_EMIT aboutToBeAdded(modelIndex);
0109         m_data.append(object);
0110         m_hash[object->index()] = object;
0111         Q_EMIT added(modelIndex, object);
0112     }
0113 
0114     // Context is passed in as parent because context needs to include the maps
0115     // so we'd cause a circular dep if we were to try to use the instance here.
0116     // Plus that's weird separation anyway.
0117     void updateEntry(const PAInfo *info, QObject *parent)
0118     {
0119         Q_ASSERT(info);
0120 
0121         if (m_pendingRemovals.remove(info->index)) {
0122             // Was already removed again.
0123             return;
0124         }
0125 
0126         auto *obj = m_hash.value(info->index);
0127         if (!obj) {
0128             obj = new Type(parent);
0129             obj->d->update(info);
0130             insert(obj);
0131         } else {
0132             obj->d->update(info);
0133         }
0134     }
0135 
0136     void removeEntry(quint32 index)
0137     {
0138         if (!m_hash.contains(index)) {
0139             m_pendingRemovals.insert(index);
0140         } else {
0141             const int modelIndex = m_data.indexOf(m_hash.value(index));
0142             Q_EMIT aboutToBeRemoved(modelIndex);
0143             m_data.removeAt(modelIndex);
0144             auto object = m_hash.take(index);
0145             Q_EMIT removed(modelIndex, object);
0146             delete object;
0147         }
0148     }
0149 
0150 protected:
0151     QVector<Type *> m_data;
0152     QHash<quint32, Type *> m_hash;
0153     QSet<quint32> m_pendingRemovals;
0154 };
0155 
0156 typedef MapBase<Card, pa_card_info> CardMap;
0157 typedef MapBase<Client, pa_client_info> ClientMap;
0158 typedef MapBase<SinkInput, pa_sink_input_info> SinkInputMap;
0159 typedef MapBase<Sink, pa_sink_info> SinkMap;
0160 typedef MapBase<Source, pa_source_info> SourceMap;
0161 typedef MapBase<SourceOutput, pa_source_output_info> SourceOutputMap;
0162 typedef MapBase<StreamRestore, pa_ext_stream_restore_info> StreamRestoreMap;
0163 typedef MapBase<Module, pa_module_info> ModuleMap;
0164 
0165 } // PulseAudioQt