summaryrefslogtreecommitdiffstats
path: root/src/PulseAudioQt/maps.h
blob: 206c5cdbba4033474b4cbd30b181043d3ed05004 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/*
    SPDX-FileCopyrightText: 2014-2015 Harald Sitter <sitter@kde.org>
    SPDX-FileCopyrightText: 2018 David Rosca <nowrep@gmail.com>

    SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/

#pragma once

#include <QHash>
#include <QObject>
#include <QSet>
#include <QVector>

#include <pulse/ext-stream-restore.h>
#include <pulse/pulseaudio.h>

#include "card_p.h"
#include "client_p.h"
#include "module_p.h"
#include "sink_p.h"
#include "sinkinput_p.h"
#include "source_p.h"
#include "sourceoutput_p.h"
#include "streamrestore_p.h"

namespace PulseAudioQt
{
// Used for typedefs.
class Card;
class Client;
class Sink;
class SinkInput;
class Source;
class SourceOutput;
class StreamRestore;
class Module;

/**
 * @see MapBase
 * This class is nothing more than the QObject base since moc cannot handle
 * templates.
 */
class MapBaseQObject : public QObject
{
    Q_OBJECT

public:
    virtual int count() const = 0;
    virtual QObject *objectAt(int index) const = 0;
    virtual int indexOfObject(QObject *object) const = 0;

Q_SIGNALS:
    void aboutToBeAdded(int index);
    void added(int index, QObject *object);
    void aboutToBeRemoved(int index);
    void removed(int index, QObject *object);
};

/**
 * Maps a specific index to a specific object pointer.
 * This is used to give the unique arbitrary PulseAudio index of a PulseObject a
 * serialized list index. Namely it enables us to translate a discrete list
 * index to a pulse index to an object, and any permutation thereof.
 */
template<typename Type, typename PAInfo>
class MapBase : public MapBaseQObject
{
public:
    virtual ~MapBase()
    {
    }

    const QVector<Type *> &data() const
    {
        return m_data;
    }

    int count() const override
    {
        return m_data.count();
    }

    int indexOfObject(QObject *object) const override
    {
        return m_data.indexOf(static_cast<Type *>(object));
    }

    QObject *objectAt(int index) const override
    {
        return m_data.at(index);
    }

    void reset()
    {
        while (!m_hash.isEmpty()) {
            removeEntry(m_data.at(m_data.count() - 1)->index());
        }
        m_pendingRemovals.clear();
    }

    void insert(Type *object)
    {
        Q_ASSERT(!m_data.contains(object));

        const int modelIndex = m_data.count();

        Q_EMIT aboutToBeAdded(modelIndex);
        m_data.append(object);
        m_hash[object->index()] = object;
        Q_EMIT added(modelIndex, object);
    }

    // Context is passed in as parent because context needs to include the maps
    // so we'd cause a circular dep if we were to try to use the instance here.
    // Plus that's weird separation anyway.
    void updateEntry(const PAInfo *info, QObject *parent)
    {
        Q_ASSERT(info);

        if (m_pendingRemovals.remove(info->index)) {
            // Was already removed again.
            return;
        }

        auto *obj = m_hash.value(info->index);
        if (!obj) {
            obj = new Type(parent);
            obj->d->update(info);
            insert(obj);
        } else {
            obj->d->update(info);
        }
    }

    void removeEntry(quint32 index)
    {
        if (!m_hash.contains(index)) {
            m_pendingRemovals.insert(index);
        } else {
            const int modelIndex = m_data.indexOf(m_hash.value(index));
            Q_EMIT aboutToBeRemoved(modelIndex);
            m_data.removeAt(modelIndex);
            auto object = m_hash.take(index);
            Q_EMIT removed(modelIndex, object);
            delete object;
        }
    }

protected:
    QVector<Type *> m_data;
    QHash<quint32, Type *> m_hash;
    QSet<quint32> m_pendingRemovals;
};

typedef MapBase<Card, pa_card_info> CardMap;
typedef MapBase<Client, pa_client_info> ClientMap;
typedef MapBase<SinkInput, pa_sink_input_info> SinkInputMap;
typedef MapBase<Sink, pa_sink_info> SinkMap;
typedef MapBase<Source, pa_source_info> SourceMap;
typedef MapBase<SourceOutput, pa_source_output_info> SourceOutputMap;
typedef MapBase<StreamRestore, pa_ext_stream_restore_info> StreamRestoreMap;
typedef MapBase<Module, pa_module_info> ModuleMap;

} // PulseAudioQt