1
/*  This file is part of the KDE project
2
    Copyright (C) 2006-2008 Matthias Kretz <kretz@kde.org>
3
4
    This library is free software; you can redistribute it and/or
5
    modify it under the terms of the GNU Lesser General Public
6
    License as published by the Free Software Foundation; either
7
    version 2.1 of the License, or (at your option) version 3, or any
8
    later version accepted by the membership of KDE e.V. (or its
9
    successor approved by the membership of KDE e.V.), Nokia Corporation 
10
    (or its successors, if any) and the KDE Free Qt Foundation, which shall
11
    act as a proxy defined in Section 6 of version 3 of the license.
12
13
    This library is distributed in the hope that it will be useful,
14
    but WITHOUT ANY WARRANTY; without even the implied warranty of
15
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
    Lesser General Public License for more details.
17
18
    You should have received a copy of the GNU Lesser General Public 
19
    License along with this library.  If not, see <http://www.gnu.org/licenses/>.
20
21
*/
22
23
#include "globalconfig.h"
24
#include "globalconfig_p.h"
25
26
#include "factory_p.h"
27
#include "objectdescription.h"
28
#include "phonondefs_p.h"
29
#include "platformplugin.h"
30
#include "backendinterface.h"
31
#include "qsettingsgroup_p.h"
32
#include "phononnamespace_p.h"
33
#include "pulsesupport.h"
34
35
#include <QtCore/QList>
36
#include <QtCore/QVariant>
37
38
QT_BEGIN_NAMESPACE
39
40
namespace Phonon
41
{
42
43
GlobalConfigPrivate::GlobalConfigPrivate() : config(QLatin1String("kde.org"), QLatin1String("libphonon"))
44
{
45
}
46
47
GlobalConfig::GlobalConfig()
48
    : k_ptr(new GlobalConfigPrivate)
49
{
50
}
51
52
GlobalConfig::~GlobalConfig()
53
{
54
    delete k_ptr;
55
}
56
57
enum WhatToFilter {
58
    FilterAdvancedDevices = 1,
59
    FilterHardwareDevices = 2,
60
    FilterUnavailableDevices = 4
61
};
62
63
static void filter(ObjectDescriptionType type, BackendInterface *backendIface, QList<int> *list, int whatToFilter)
64
{
65
    QMutableListIterator<int> it(*list);
66
    while (it.hasNext()) {
67
        QHash<QByteArray, QVariant> properties;
68
        if (backendIface)
69
            properties = backendIface->objectDescriptionProperties(type, it.next());
70
        else
71
            properties = PulseSupport::getInstance()->objectDescriptionProperties(type, it.next());
72
        QVariant var;
73
        if (whatToFilter & FilterAdvancedDevices) {
74
            var = properties.value("isAdvanced");
75
            if (var.isValid() && var.toBool()) {
76
                it.remove();
77
                continue;
78
            }
79
        }
80
        if (whatToFilter & FilterHardwareDevices) {
81
            var = properties.value("isHardwareDevice");
82
            if (var.isValid() && var.toBool()) {
83
                it.remove();
84
                continue;
85
#ifndef QT_NO_PHONON_SETTINGSGROUP
86
            }
87
        }
88
        if (whatToFilter & FilterUnavailableDevices) {
89
            var = properties.value("available");
90
            if (var.isValid() && !var.toBool()) {
91
                it.remove();
92
                continue;
93
            }
94
        }
95
    }
96
}
97
98
static QList<int> sortDevicesByCategoryPriority(const GlobalConfig *config, const QSettingsGroup *backendConfig, ObjectDescriptionType type, Phonon::Category category, QList<int> &defaultList)
99
{
100
    Q_ASSERT(config); Q_UNUSED(config);
101
    Q_ASSERT(backendConfig);
102
    Q_ASSERT(type == AudioOutputDeviceType || type == AudioCaptureDeviceType);
103
104
    if (defaultList.size() <= 1) {
105
        // nothing to sort
106
        return defaultList;
107
    } else {
108
        // make entries unique
109
        QSet<int> seen;
110
        QMutableListIterator<int> it(defaultList);
111
        while (it.hasNext()) {
112
            if (seen.contains(it.next())) {
113
                it.remove();
114
            } else {
115
                seen.insert(it.value());
116
            }
117
        }
118
    }
119
120
    QList<int> deviceList;
121
    PulseSupport *pulse = PulseSupport::getInstance();
122
    if (pulse->isActive()) {
123
        deviceList = pulse->objectIndexesByCategory(type, category);
124
    } else {
125
        QString categoryKey = QLatin1String("Category_") + QString::number(static_cast<int>(category));
126
        if (!backendConfig->hasKey(categoryKey)) {
127
            // no list in config for the given category
128
            categoryKey = QLatin1String("Category_") + QString::number(static_cast<int>(Phonon::NoCategory));
129
            if (!backendConfig->hasKey(categoryKey)) {
130
                // no list in config for NoCategory
131
                return defaultList;
132
            }
133
        }
134
135
        //Now the list from d->config
136
        deviceList = backendConfig->value(categoryKey, QList<int>());
137
    }
138
139
    //if there are devices in d->config that the backend doesn't report, remove them from the list
140
    QMutableListIterator<int> i(deviceList);
141
    while (i.hasNext()) {
142
        if (0 == defaultList.removeAll(i.next())) {
143
            i.remove();
144
        }
145
    }
146
147
    //if the backend reports more devices that are not in d->config append them to the list
148
    deviceList += defaultList;
149
150
    return deviceList;
151
}
152
153
bool GlobalConfig::hideAdvancedDevices() const
154
{
155
    K_D(const GlobalConfig);
156
    //The devices need to be stored independently for every backend
157
    const QSettingsGroup generalGroup(&d->config, QLatin1String("General"));
158
    return generalGroup.value(QLatin1String("HideAdvancedDevices"), true);
159
}
160
161
void GlobalConfig::setHideAdvancedDevices(bool hide)
162
{
163
    K_D(GlobalConfig);
164
    QSettingsGroup generalGroup(&d->config, QLatin1String("General"));
165
    generalGroup.setValue(QLatin1String("HideAdvancedDevices"), hide);
166
}
167
168
static bool isHiddenAudioOutputDevice(const GlobalConfig *config, int i)
169
{
170
    Q_ASSERT(config);
171
172
    if (!config->hideAdvancedDevices())
173
        return false;
174
175
    AudioOutputDevice ad = AudioOutputDevice::fromIndex(i);
176
    const QVariant var = ad.property("isAdvanced");
177
    return (var.isValid() && var.toBool());
178
}
179
180
#ifndef QT_NO_PHONON_AUDIOCAPTURE
181
static bool isHiddenAudioCaptureDevice(const GlobalConfig *config, int i)
182
{
183
    Q_ASSERT(config);
184
185
    if (!config->hideAdvancedDevices())
186
        return false;
187
188
    AudioCaptureDevice ad = AudioCaptureDevice::fromIndex(i);
189
    const QVariant var = ad.property("isAdvanced");
190
    return (var.isValid() && var.toBool());
191
}
192
#endif
193
194
static QList<int> reindexList(const GlobalConfig *config, Phonon::Category category, QList<int>newOrder, bool output)
195
{
196
    Q_ASSERT(config);
197
#ifdef QT_NO_PHONON_AUDIOCAPTURE
198
    Q_ASSERT(output);
199
#endif
200
201
    /*QString sb;
202
    sb = QString("(Size %1)").arg(currentList.size());
203
    foreach (int i, currentList)
204
    sb += QString("%1, ").arg(i);
205
    fprintf(stderr, "=== Reindex Current: %s\n", sb.toUtf8().constData());
206
    sb = QString("(Size %1)").arg(newOrder.size());
207
    foreach (int i, newOrder)
208
    sb += QString("%1, ").arg(i);
209
    fprintf(stderr, "=== Reindex Before : %s\n", sb.toUtf8().constData());*/
210
211
    QList<int> currentList;
212
    if (output)
213
        currentList = config->audioOutputDeviceListFor(category, GlobalConfig::ShowUnavailableDevices|GlobalConfig::ShowAdvancedDevices);
214
#ifndef QT_NO_PHONON_AUDIOCAPTURE
215
    else
216
        currentList = config->audioCaptureDeviceListFor(category, GlobalConfig::ShowUnavailableDevices|GlobalConfig::ShowAdvancedDevices);
217
#endif
218
219
    QList<int> newList;
220
221
    foreach (int i, newOrder) {
222
        int found = currentList.indexOf(i);
223
        if (found < 0) {
224
            // It's not in the list, so something is odd (e.g. client error). Ignore it.
225
            continue;
226
        }
227
228
        // Iterate through the list from this point onward. If there are hidden devices
229
        // immediately following, take them too.
230
        newList.append(currentList.takeAt(found));
231
        while (found < currentList.size()) {
232
            bool hidden = true;
233
            if (output)
234
                hidden = isHiddenAudioOutputDevice(config, currentList.at(found));
235
#ifndef QT_NO_PHONON_AUDIOCAPTURE
236
            else
237
                hidden = isHiddenAudioCaptureDevice(config, currentList.at(found));
238
#endif
239
240
            if (!hidden)
241
                break;
242
            newList.append(currentList.takeAt(found));
243
        }
244
    }
245
246
    // If there are any devices left in.. just tack them on the end.
247
    if (currentList.size() > 0)
248
        newList += currentList;
249
250
    /*sb = QString("(Size %1)").arg(newList.size());
251
    foreach (int i, newList)
252
    sb += QString("%1, ").arg(i);
253
    fprintf(stderr, "=== Reindex After  : %s\n", sb.toUtf8().constData());*/
254
    return newList;
255
}
256
257
258
void GlobalConfig::setAudioOutputDeviceListFor(Phonon::Category category, QList<int> order)
259
{
260
    PulseSupport *pulse = PulseSupport::getInstance();
261
    if (pulse->isActive()) {
262
        pulse->setOutputDevicePriorityForCategory(category, order);
263
        return;
264
    }
265
266
    K_D(GlobalConfig);
267
    QSettingsGroup backendConfig(&d->config, QLatin1String("AudioOutputDevice")); // + Factory::identifier());
268
269
    order = reindexList(this, category, order, true);
270
271
    const QList<int> noCategoryOrder = audioOutputDeviceListFor(Phonon::NoCategory, ShowUnavailableDevices|ShowAdvancedDevices);
272
    if (category != Phonon::NoCategory && order == noCategoryOrder) {
273
        backendConfig.removeEntry(QLatin1String("Category_") + QString::number(category));
274
    } else {
275
        backendConfig.setValue(QLatin1String("Category_") + QString::number(category), order);
276
    }
277
}
278
#endif //QT_NO_PHONON_SETTINGSGROUP
279
280
#ifndef QT_NO_PHONON_SETTINGSGROUP
281
QList<int> GlobalConfig::audioOutputDeviceListFor(Phonon::Category category, int override) const
282
{
283
    K_D(const GlobalConfig);
284
285
    const bool hide = ((override & AdvancedDevicesFromSettings)
286
            ? hideAdvancedDevices()
287
            : static_cast<bool>(override & HideAdvancedDevices));
288
289
    QList<int> defaultList;
290
291
    PulseSupport *pulse = PulseSupport::getInstance();
292
    if (pulse->isActive()) {
293
        defaultList = pulse->objectDescriptionIndexes(Phonon::AudioOutputDeviceType);
294
        if (hide || (override & HideUnavailableDevices)) {
295
            filter(AudioOutputDeviceType, NULL, &defaultList,
296
                    (hide ? FilterAdvancedDevices : 0)
297
                    | ((override & HideUnavailableDevices) ? FilterUnavailableDevices : 0)
298
                    );
299
        }
300
    } else {
301
        BackendInterface *backendIface = qobject_cast<BackendInterface *>(Factory::backend());
302
303
#ifndef QT_NO_PHONON_PLATFORMPLUGIN
304
        if (PlatformPlugin *platformPlugin = Factory::platformPlugin()) {
305
            // the platform plugin lists the audio devices for the platform
306
            // this list already is in default order (as defined by the platform plugin)
307
            defaultList = platformPlugin->objectDescriptionIndexes(Phonon::AudioOutputDeviceType);
308
            if (hide) {
309
                QMutableListIterator<int> it(defaultList);
310
                while (it.hasNext()) {
311
                    AudioOutputDevice objDesc = AudioOutputDevice::fromIndex(it.next());
312
                    const QVariant var = objDesc.property("isAdvanced");
313
                    if (var.isValid() && var.toBool()) {
314
                        it.remove();
315
                    }
316
                }
317
            }
318
        }
319
#endif //QT_NO_PHONON_PLATFORMPLUGIN
320
321
        // lookup the available devices directly from the backend
322
        if (backendIface) {
323
            // this list already is in default order (as defined by the backend)
324
            QList<int> list = backendIface->objectDescriptionIndexes(Phonon::AudioOutputDeviceType);
325
            if (hide || !defaultList.isEmpty() || (override & HideUnavailableDevices)) {
326
                filter(AudioOutputDeviceType, backendIface, &list,
327
                        (hide ? FilterAdvancedDevices : 0)
328
                        // the platform plugin maybe already provided the hardware devices?
329
                        | (defaultList.isEmpty() ? 0 : FilterHardwareDevices)
330
                        | ((override & HideUnavailableDevices) ? FilterUnavailableDevices : 0)
331
                        );
332
            }
333
            defaultList += list;
334
        }
335
    }
336
337
    const QSettingsGroup backendConfig(&d->config, QLatin1String("AudioOutputDevice")); // + Factory::identifier());
338
    return sortDevicesByCategoryPriority(this, &backendConfig, AudioOutputDeviceType, category, defaultList);
339
}
340
#endif //QT_NO_PHONON_SETTINGSGROUP
341
int GlobalConfig::audioOutputDeviceFor(Phonon::Category category, int override) const
342
{
343
#ifndef QT_NO_PHONON_SETTINGSGROUP
344
    QList<int> ret = audioOutputDeviceListFor(category, override);
345
    if (!ret.isEmpty())
346
        return ret.first();
347
#endif //QT_NO_PHONON_SETTINGSGROUP
348
    return -1;
349
}
350
351
#ifndef QT_NO_PHONON_AUDIOCAPTURE
352
void GlobalConfig::setAudioCaptureDeviceListFor(Phonon::Category category, QList<int> order)
353
{
354
#ifndef QT_NO_PHONON_SETTINGSGROUP
355
    PulseSupport *pulse = PulseSupport::getInstance();
356
    if (pulse->isActive()) {
357
        pulse->setCaptureDevicePriorityForCategory(category, order);
358
        return;
359
    }
360
361
    K_D(GlobalConfig);
362
    QSettingsGroup backendConfig(&d->config, QLatin1String("AudioCaptureDevice")); // + Factory::identifier());
363
364
    order = reindexList(this, category, order, false);
365
366
    const QList<int> noCategoryOrder = audioCaptureDeviceListFor(Phonon::NoCategory, ShowUnavailableDevices|ShowAdvancedDevices);
367
    if (category != Phonon::NoCategory && order == noCategoryOrder) {
368
        backendConfig.removeEntry(QLatin1String("Category_") + QString::number(category));
369
    } else {
370
        backendConfig.setValue(QLatin1String("Category_") + QString::number(category), order);
371
    }
372
}
373
374
QList<int> GlobalConfig::audioCaptureDeviceListFor(Phonon::Category category, int override) const
375
{
376
    K_D(const GlobalConfig);
377
378
    const bool hide = ((override & AdvancedDevicesFromSettings)
379
            ? hideAdvancedDevices()
380
            : static_cast<bool>(override & HideAdvancedDevices));
381
382
    QList<int> defaultList;
383
384
    PulseSupport *pulse = PulseSupport::getInstance();
385
    if (pulse->isActive()) {
386
        defaultList = pulse->objectDescriptionIndexes(Phonon::AudioCaptureDeviceType);
387
        if (hide || (override & HideUnavailableDevices)) {
388
            filter(AudioCaptureDeviceType, NULL, &defaultList,
389
                    (hide ? FilterAdvancedDevices : 0)
390
                    | ((override & HideUnavailableDevices) ? FilterUnavailableDevices : 0)
391
                    );
392
        }
393
    } else {
394
        BackendInterface *backendIface = qobject_cast<BackendInterface *>(Factory::backend());
395
396
#ifndef QT_NO_PHONON_PLATFORMPLUGIN
397
#else //QT_NO_SETTINGSGROUP
398
    return QList<int>();
399
#endif //QT_NO_SETTINGSGROUP
400
        if (PlatformPlugin *platformPlugin = Factory::platformPlugin()) {
401
            // the platform plugin lists the audio devices for the platform
402
            // this list already is in default order (as defined by the platform plugin)
403
            defaultList = platformPlugin->objectDescriptionIndexes(Phonon::AudioCaptureDeviceType);
404
            if (hide) {
405
                QMutableListIterator<int> it(defaultList);
406
                while (it.hasNext()) {
407
                    AudioCaptureDevice objDesc = AudioCaptureDevice::fromIndex(it.next());
408
                    const QVariant var = objDesc.property("isAdvanced");
409
                    if (var.isValid() && var.toBool()) {
410
                        it.remove();
411
                    }
412
                }
413
            }
414
        }
415
#endif //QT_NO_PHONON_PLATFORMPLUGIN
416
417
        // lookup the available devices directly from the backend
418
        if (backendIface) {
419
            // this list already is in default order (as defined by the backend)
420
            QList<int> list = backendIface->objectDescriptionIndexes(Phonon::AudioCaptureDeviceType);
421
            if (hide || !defaultList.isEmpty() || (override & HideUnavailableDevices)) {
422
                filter(AudioCaptureDeviceType, backendIface, &list,
423
                        (hide ? FilterAdvancedDevices : 0)
424
                        // the platform plugin maybe already provided the hardware devices?
425
                        | (defaultList.isEmpty() ? 0 : FilterHardwareDevices)
426
                        | ((override & HideUnavailableDevices) ? FilterUnavailableDevices : 0)
427
                      );
428
            }
429
            defaultList += list;
430
        }
431
    }
432
433
    const QSettingsGroup backendConfig(&d->config, QLatin1String("AudioCaptureDevice")); // + Factory::identifier());
434
    return sortDevicesByCategoryPriority(this, &backendConfig, AudioCaptureDeviceType, category, defaultList);
435
}
436
437
int GlobalConfig::audioCaptureDeviceFor(Phonon::Category category, int override) const
438
{
439
    QList<int> ret = audioCaptureDeviceListFor(category, override);
440
    if (ret.isEmpty())
441
        return -1;
442
    return ret.first();
443
}
444
#endif //QT_NO_PHONON_AUDIOCAPTURE
445
446
447
} // namespace Phonon
448
449
QT_END_NAMESPACE