• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mcallegari / qlcplus / 27918870711

21 Jun 2026 09:28PM UTC coverage: 35.287% (-0.009%) from 35.296%
27918870711

push

github

mcallegari
engine: fix RGBMatrix dimmer mode not applied correctly to generic dimmers

0 of 3 new or added lines in 1 file covered. (0.0%)

422 existing lines in 3 files now uncovered.

18536 of 52529 relevant lines covered (35.29%)

41043.21 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

62.1
/engine/src/universe.cpp
1
/*
2
  Q Light Controller Plus
3
  universe.cpp
4

5
  Copyright (c) Massimo Callegari
6

7
  Licensed under the Apache License, Version 2.0 (the "License");
8
  you may not use this file except in compliance with the License.
9
  You may obtain a copy of the License at
10

11
      http://www.apache.org/licenses/LICENSE-2.0.txt
12

13
  Unless required by applicable law or agreed to in writing, software
14
  distributed under the License is distributed on an "AS IS" BASIS,
15
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
  See the License for the specific language governing permissions and
17
  limitations under the License.
18
*/
19

20
#include <QXmlStreamReader>
21
#include <QXmlStreamWriter>
22
#include <QElapsedTimer>
23
#include <QDebug>
24
#include <math.h>
25

26
#include "channelmodifier.h"
27
#include "inputoutputmap.h"
28
#include "genericfader.h"
29
#include "qlcioplugin.h"
30
#include "outputpatch.h"
31
#include "grandmaster.h"
32
#include "mastertimer.h"
33
#include "inputpatch.h"
34
#include "qlcmacros.h"
35
#include "universe.h"
36
#include "function.h"
37
#include "qlcfile.h"
38
#include "utils.h"
39

40
#define RELATIVE_ZERO_8BIT   0x7F
41
#define RELATIVE_ZERO_16BIT  0x7F00
42

43
#define KXMLUniverseNormalBlend      QStringLiteral("Normal")
44
#define KXMLUniverseMaskBlend        QStringLiteral("Mask")
45
#define KXMLUniverseAdditiveBlend    QStringLiteral("Additive")
46
#define KXMLUniverseSubtractiveBlend QStringLiteral("Subtractive")
47

48
Universe::Universe(quint32 id, GrandMaster *gm, QObject *parent)
1,132✔
49
    : QThread(parent)
50
    , m_id(id)
1,132✔
51
    , m_name(QString("Universe %1").arg(id + 1))
1,132✔
52
    , m_grandMaster(gm)
1,132✔
53
    , m_passthrough(false)
1,132✔
54
    , m_monitor(false)
1,132✔
55
    , m_inputPatch(NULL)
1,132✔
56
    , m_fbPatch(NULL)
1,132✔
57
    , m_channelsMask(new QByteArray(UNIVERSE_SIZE, char(0)))
1,132✔
58
    , m_modifiedZeroValues(new QByteArray(UNIVERSE_SIZE, char(0)))
1,132✔
59
    , m_running(false)
1,132✔
60
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
61
    , m_fadersMutex(QMutex::Recursive)
62
#endif
63
    , m_usedChannels(0)
1,132✔
64
    , m_totalChannels(0)
1,132✔
65
    , m_totalChannelsChanged(false)
1,132✔
66
    , m_intensityChannelsChanged(false)
1,132✔
67
    , m_preGMValues(new QByteArray(UNIVERSE_SIZE, char(0)))
1,132✔
68
    , m_postGMValues(new QByteArray(UNIVERSE_SIZE, char(0)))
1,132✔
69
    , m_lastPostGMValues(new QByteArray(UNIVERSE_SIZE, char(0)))
1,132✔
70
    , m_blackoutValues(new QByteArray(UNIVERSE_SIZE, char(0)))
1,132✔
71
    , m_passthroughValues()
6,792✔
72
{
73
    m_modifiers.fill(NULL, UNIVERSE_SIZE);
1,132✔
74

75
    connect(m_grandMaster, SIGNAL(valueChanged(uchar)),
1,132✔
76
            this, SLOT(slotGMValueChanged()));
77
}
1,132✔
78

79
Universe::~Universe()
2,084✔
80
{
81
    if (isRunning() == true)
1,117✔
82
    {
83
        // isRunning is inconsistent with m_running,
84
        // so double check if the thread is really in the run loop
85
        while (m_running == false)
×
86
            usleep(10000);
×
87

88
        m_running = false;
×
89
        if (!wait(2000))
×
90
        {
UNCOV
91
            qCritical() << Q_FUNC_INFO << "Universe" << m_id
×
UNCOV
92
                        << "thread did not stop within 2 seconds, forcing termination";
×
UNCOV
93
            terminate();
×
UNCOV
94
            wait();
×
95
        }
96
    }
97

98
    delete m_inputPatch;
1,117✔
99
    int opCount = m_outputPatchList.count();
1,117✔
100
    for (int i = 0; i < opCount; i++)
1,281✔
101
    {
102
        OutputPatch *patch = m_outputPatchList.takeLast();
164✔
103
        delete patch;
164✔
104
    }
105
    delete m_fbPatch;
1,117✔
106
}
2,084✔
107

108
void Universe::setName(QString name)
7✔
109
{
110
    if (name.isEmpty())
7✔
UNCOV
111
        m_name = QString("Universe %1").arg(m_id + 1);
×
112
    else
113
        m_name = name;
7✔
114
    emit nameChanged();
7✔
115
}
7✔
116

117
QString Universe::name() const
107✔
118
{
119
    return m_name;
107✔
120
}
121

122
void Universe::setID(quint32 id)
2✔
123
{
124
    m_id = id;
2✔
125
}
2✔
126

127
quint32 Universe::id() const
160,917✔
128
{
129
    return m_id;
160,917✔
130
}
131

132
ushort Universe::usedChannels() const
223✔
133
{
134
    return m_usedChannels;
223✔
135
}
136

137
ushort Universe::totalChannels() const
6✔
138
{
139
    return m_totalChannels;
6✔
140
}
141

142
bool Universe::hasChanged() const
5,411,662✔
143
{
144
    bool changed =
145
        memcmp(m_lastPostGMValues->constData(), m_postGMValues->constData(), m_usedChannels) != 0;
5,411,662✔
146
    if (changed)
5,411,662✔
147
        memcpy(m_lastPostGMValues->data(), m_postGMValues->constData(), m_usedChannels);
158,719✔
148
    return changed;
5,411,662✔
149
}
150

151
void Universe::setPassthrough(bool enable)
6✔
152
{
153
    if (enable == m_passthrough)
6✔
154
        return;
2✔
155

156
    qDebug() << "Set universe" << id() << "passthrough to" << enable;
4✔
157

158
    disconnectInputPatch();
4✔
159

160
    if (enable && m_passthroughValues.isNull())
4✔
161
    {
162
        // When passthrough is disabled, we don't release the array, since it's only ~512 B and
163
        // we would have to synchronize with other threads
164

165
        // When enabling passthrough, make sure the array is allocated BEFORE m_passthrough is set to
166
        // true. That way we only have to check for m_passthrough, and do not need to check
167
        // m_passthroughValues.isNull()
168
        m_passthroughValues.reset(new QByteArray(UNIVERSE_SIZE, char(0)));
4✔
169
    }
170

171
    m_passthrough = enable;
4✔
172

173
    connectInputPatch();
4✔
174

175
    emit passthroughChanged();
4✔
176
}
177

178
bool Universe::passthrough() const
12✔
179
{
180
    return m_passthrough;
12✔
181
}
182

183
void Universe::setMonitor(bool enable)
1✔
184
{
185
    m_monitor = enable;
1✔
186
}
1✔
187

188
bool Universe::monitor() const
1✔
189
{
190
    return m_monitor;
1✔
191
}
192

193
void Universe::slotGMValueChanged()
5,033✔
194
{
195
    {
196
        for (int i = 0; i < m_intensityChannels.size(); ++i)
2,565,584✔
197
        {
198
            int channel = m_intensityChannels.at(i);
2,560,551✔
199
            updatePostGMValue(channel);
2,560,551✔
200
        }
201
    }
202

203
    if (m_grandMaster->channelMode() == GrandMaster::AllChannels)
5,033✔
204
    {
205
        for (int i = 0; i < m_nonIntensityChannels.size(); ++i)
32✔
206
        {
207
            int channel = m_nonIntensityChannels.at(i);
15✔
208
            updatePostGMValue(channel);
15✔
209
        }
210
    }
211
}
5,033✔
212

213
/************************************************************************
214
 * Faders
215
 ************************************************************************/
216

217
QSharedPointer<GenericFader> Universe::requestFader(Universe::FaderPriority priority)
2,555✔
218
{
219
    int insertPos = 0;
2,555✔
220
    QSharedPointer<GenericFader> fader = QSharedPointer<GenericFader>(new GenericFader());
2,555✔
221
    fader->setPriority(priority);
2,555✔
222

223
    {
224
        QMutexLocker fadersLocker(&m_fadersMutex);
2,555✔
225
        {
226
            for (int i = m_faders.count() - 1; i >= 0; i--)
7,955✔
227
            {
228
                const QSharedPointer<GenericFader>& f = m_faders.at(i);
7,782✔
229
                if (!f.isNull() && f->priority() <= fader->priority())
7,782✔
230
                {
231
                    insertPos = i + 1;
2,382✔
232
                    break;
2,382✔
233
                }
234
            }
235
        }
236

237
        m_faders.insert(insertPos, fader);
2,555✔
238

239
        qDebug() << "[Universe]" << id() << ": Generic fader with priority" << fader->priority()
5,110✔
240
                 << "registered at pos" << insertPos << ", count" << m_faders.count();
2,555✔
241
    }
2,555✔
242
    return fader;
2,555✔
UNCOV
243
}
×
244

245
void Universe::dismissFader(QSharedPointer<GenericFader> fader)
3✔
246
{
247
    QMutexLocker fadersLocker(&m_fadersMutex);
3✔
248
    int index = m_faders.indexOf(fader);
3✔
249
    if (index >= 0)
3✔
250
    {
251
        m_faders.takeAt(index);
3✔
252
        fader.clear();
3✔
253
    }
254
}
3✔
255

256
void Universe::requestFaderPriority(QSharedPointer<GenericFader> fader, Universe::FaderPriority priority)
×
257
{
UNCOV
258
    QMutexLocker fadersLocker(&m_fadersMutex);
×
259
    int pos = m_faders.indexOf(fader);
×
UNCOV
260
    int newPos = 0;
×
261

262
    if (pos == -1)
×
UNCOV
263
        return;
×
264

265
    for (int i = m_faders.count() - 1; i >= 0; i--)
×
266
    {
UNCOV
267
        QSharedPointer<GenericFader> f = m_faders.at(i);
×
268
        if (!f.isNull() && f->priority() <= priority)
×
269
        {
270
            newPos = i;
×
UNCOV
271
            fader->setPriority(priority);
×
272
            break;
×
273
        }
274
    }
×
275

276
    if (newPos != pos)
×
277
    {
278
        m_faders.move(pos, newPos);
×
UNCOV
279
        qDebug() << "[Universe]" << id() << ": Generic fader moved from" << pos
×
280
                 << "to" << m_faders.indexOf(fader) << ". Count:" << m_faders.count();
×
281
    }
UNCOV
282
}
×
283

UNCOV
284
QList<QSharedPointer<GenericFader> > Universe::faders()
×
285
{
UNCOV
286
    return m_faders;
×
287
}
288

289
void Universe::setFaderPause(quint32 functionID, bool enable)
8✔
290
{
291
    QMutexLocker fadersLocker(&m_fadersMutex);
8✔
292
    QMutableListIterator<QSharedPointer<GenericFader> > it(m_faders);
8✔
293
    while (it.hasNext())
17✔
294
    {
295
        QSharedPointer<GenericFader> fader = it.next();
9✔
296
        if (fader.isNull() || fader->parentFunctionID() != functionID)
9✔
297
            continue;
6✔
298

299
        fader->setPaused(enable);
3✔
300
    }
9✔
301
}
8✔
302

303
void Universe::setFaderFadeOut(int fadeTime)
×
304
{
305
    QMutexLocker fadersLocker(&m_fadersMutex);
×
UNCOV
306
    foreach (QSharedPointer<GenericFader> fader, m_faders)
×
307
    {
UNCOV
308
        if (!fader.isNull() && fader->parentFunctionID() != Function::invalidId())
×
UNCOV
309
            fader->setFadeOut(true, uint(fadeTime));
×
UNCOV
310
    }
×
UNCOV
311
}
×
312

313
void Universe::tick()
692✔
314
{
315
    // Keep at most one pending tick to avoid queueing stale work when running late.
316
    if (m_semaphore.available() == 0)
692✔
317
        m_semaphore.release(1);
12✔
318
}
692✔
319

320
void Universe::processFaders(uint elapsedMs)
289,609✔
321
{
322
    flushInput();
289,609✔
323
    zeroIntensityChannels();
289,609✔
324

325
    QList<QSharedPointer<GenericFader>> activeFaders;
289,609✔
326
    {
327
        QMutexLocker fadersLocker(&m_fadersMutex);
289,609✔
328
        activeFaders.reserve(m_faders.size());
289,609✔
329
        QMutableListIterator<QSharedPointer<GenericFader> > it(m_faders);
289,609✔
330
        while (it.hasNext())
1,612,792✔
331
        {
332
            QSharedPointer<GenericFader> fader = it.next(); //m_faders.at(i);
1,323,183✔
333
            if (fader.isNull())
1,323,183✔
UNCOV
334
                continue;
×
335

336
            // destroy a fader if it's been requested
337
            // and it's not fading out
338
            if (fader->deleteRequested() && !fader->isFadingOut())
1,323,183✔
339
            {
340
                fader->removeAll();
72✔
341
                it.remove();
72✔
342
                fader.clear();
72✔
343
                continue;
72✔
344
            }
345

346
            if (fader->isEnabled() == false)
1,323,111✔
UNCOV
347
                continue;
×
348

349
            activeFaders.append(fader);
1,323,111✔
350
        }
1,323,183✔
351
    }
289,609✔
352

353
    foreach (const QSharedPointer<GenericFader> &fader, activeFaders)
1,612,720✔
354
        fader->write(this, elapsedMs);
1,612,720✔
355

356
    bool dataChanged = hasChanged();
289,609✔
357
    const QByteArray postGM = QByteArray::fromRawData(m_postGMValues->constData(), m_usedChannels);
289,609✔
358
    dumpOutput(postGM, dataChanged);
289,609✔
359

360
    if (dataChanged)
289,609✔
361
        emit universeWritten(id(), QByteArray(postGM.constData(), postGM.size()));
157,693✔
362
}
289,609✔
363

UNCOV
364
void Universe::run()
×
365
{
UNCOV
366
    m_running = true;
×
367
    int timeout = int(MasterTimer::tick()) * 2;
×
UNCOV
368
    QElapsedTimer elapsedTimer;
×
369
    elapsedTimer.start();
×
370

UNCOV
371
    qDebug() << "Universe thread started" << id();
×
372

UNCOV
373
    while (m_running)
×
374
    {
UNCOV
375
        if (m_semaphore.tryAcquire(1, timeout) == false)
×
376
        {
377
            //qWarning() << "Semaphore not acquired on universe" << id();
378
            continue;
×
379
        }
380
#if 0
381
        if (m_faders.count())
382
            qDebug() << "<<<<<<<< UNIVERSE TICK - id" << id() << "faders:" << m_faders.count();
383
#endif
384
        const qint64 elapsedNs = elapsedTimer.nsecsElapsed();
×
UNCOV
385
        elapsedTimer.restart();
×
UNCOV
386
        uint elapsedMs = uint((elapsedNs + 500000) / 1000000);
×
387
        if (elapsedMs == 0)
×
388
            elapsedMs = 1;
×
389

UNCOV
390
        processFaders(elapsedMs);
×
391
    }
392

UNCOV
393
    qDebug() << "Universe thread stopped" << id();
×
UNCOV
394
}
×
395

396
/************************************************************************
397
 * Values
398
 ************************************************************************/
399

400
void Universe::reset()
9✔
401
{
402
    m_preGMValues->fill(0);
9✔
403
    m_blackoutValues->fill(0);
9✔
404

405
    if (m_passthrough)
9✔
UNCOV
406
        (*m_postGMValues) = (*m_passthroughValues);
×
407
    else
408
        m_postGMValues->fill(0);
9✔
409

410
    m_modifiers.fill(NULL, UNIVERSE_SIZE);
9✔
411
    m_passthrough = false; // not releasing m_passthroughValues, see comment in setPassthrough
9✔
412
}
9✔
413

414
void Universe::reset(int address, int range)
5,106,780✔
415
{
416
    if (address >= UNIVERSE_SIZE)
5,106,780✔
UNCOV
417
        return;
×
418

419
    if (address + range > UNIVERSE_SIZE)
5,106,780✔
420
       range = UNIVERSE_SIZE - address;
1✔
421

422
    memset(m_preGMValues->data() + address, 0, range * sizeof(*m_preGMValues->data()));
5,106,780✔
423
    memset(m_blackoutValues->data() + address, 0, range * sizeof(*m_blackoutValues->data()));
5,106,780✔
424
    memcpy(m_postGMValues->data() + address, m_modifiedZeroValues->data() + address, range * sizeof(*m_postGMValues->data()));
5,106,780✔
425

426
    applyPassthroughValues(address, range);
5,106,780✔
427
}
428

429
void Universe::applyPassthroughValues(int address, int range)
5,106,780✔
430
{
431
    if (!m_passthrough)
5,106,780✔
432
        return;
5,106,780✔
433

UNCOV
434
    for (int i = address; i < address + range && i < UNIVERSE_SIZE; i++)
×
435
    {
UNCOV
436
        if (static_cast<uchar>(m_postGMValues->at(i)) < static_cast<uchar>(m_passthroughValues->at(i))) // HTP merge
×
437
        {
UNCOV
438
            (*m_postGMValues)[i] = (*m_passthroughValues)[i];
×
439
        }
440
    }
441
}
442

443
void Universe::zeroIntensityChannels()
299,711✔
444
{
445
    updateIntensityChannelsRanges();
299,711✔
446
    int const* channels = m_intensityChannelsRanges.constData();
299,711✔
447
    for (int i = 0; i < m_intensityChannelsRanges.size(); ++i)
5,406,490✔
448
    {
449
        short channel = channels[i] >> 16;
5,106,779✔
450
        short size = channels[i] & 0xffff;
5,106,779✔
451

452
        reset(channel, size);
5,106,779✔
453
    }
454
}
299,711✔
455

456
QHash<int, uchar> Universe::intensityChannels()
1✔
457
{
458
    QHash <int, uchar> intensityList;
1✔
459
    for (int i = 0; i < m_intensityChannels.size(); ++i)
1✔
460
    {
UNCOV
461
        int channel = m_intensityChannels.at(i);
×
UNCOV
462
        intensityList[channel] = m_preGMValues->at(channel);
×
463
    }
464
    return intensityList;
1✔
UNCOV
465
}
×
466

467
uchar Universe::postGMValue(int address) const
4✔
468
{
469
    if (address >= m_postGMValues->size())
4✔
UNCOV
470
        return 0;
×
471

472
    return uchar(m_postGMValues->at(address));
4✔
473
}
474

475
const QByteArray* Universe::postGMValues() const
4,633✔
476
{
477
    return m_postGMValues.data();
4,633✔
478
}
479

480
Universe::BlendMode Universe::stringToBlendMode(QString mode)
5✔
481
{
482
    if (mode == KXMLUniverseNormalBlend)
5✔
483
        return NormalBlend;
1✔
484
    else if (mode == KXMLUniverseMaskBlend)
4✔
485
        return MaskBlend;
1✔
486
    else if (mode == KXMLUniverseAdditiveBlend)
3✔
487
        return AdditiveBlend;
1✔
488
    else if (mode == KXMLUniverseSubtractiveBlend)
2✔
489
        return SubtractiveBlend;
1✔
490

491
    return NormalBlend;
1✔
492
}
493

494
QString Universe::blendModeToString(Universe::BlendMode mode)
7✔
495
{
496
    switch(mode)
7✔
497
    {
498
        default:
1✔
499
        case NormalBlend:
500
            return QString(KXMLUniverseNormalBlend);
1✔
501
        break;
502
        case MaskBlend:
1✔
503
            return QString(KXMLUniverseMaskBlend);
1✔
504
        break;
505
        case AdditiveBlend:
4✔
506
            return QString(KXMLUniverseAdditiveBlend);
4✔
507
        break;
508
        case SubtractiveBlend:
1✔
509
            return QString(KXMLUniverseSubtractiveBlend);
1✔
510
        break;
511
    }
512
}
513

514
const QByteArray Universe::preGMValues() const
1,326,130✔
515
{
516
    return *m_preGMValues;
1,326,130✔
517
}
518

519
uchar Universe::preGMValue(int address) const
19,376,224✔
520
{
521
    if (address < 0 || address >= m_preGMValues->size())
19,376,224✔
UNCOV
522
        return 0U;
×
523

524
    return static_cast<uchar>(m_preGMValues->at(address));
19,376,224✔
525
}
526

527
uchar Universe::applyGM(int channel, uchar value)
14,591,670✔
528
{
529
    if ((m_grandMaster->channelMode() == GrandMaster::Intensity && m_channelsMask->at(channel) & Intensity) ||
20,353,701✔
530
        (m_grandMaster->channelMode() == GrandMaster::AllChannels))
5,762,031✔
531
    {
532
        if (m_grandMaster->valueMode() == GrandMaster::Limit)
8,830,693✔
533
            value = MIN(value, m_grandMaster->value());
792✔
534
        else
535
            value = char(floor((double(value) * m_grandMaster->fraction()) + 0.5));
8,829,901✔
536
    }
537

538
    return value;
14,591,670✔
539
}
540

541
uchar Universe::applyModifiers(int channel, uchar value)
19,315,606✔
542
{
543
    if (m_modifiers.at(channel) != NULL)
19,315,606✔
UNCOV
544
        return m_modifiers.at(channel)->getValue(value);
×
545

546
    return value;
19,315,606✔
547
}
548

549
uchar Universe::applyPassthrough(int channel, uchar value)
19,315,606✔
550
{
551
    if (m_passthrough)
19,315,606✔
552
    {
UNCOV
553
        const uchar passthroughValue = static_cast<uchar>(m_passthroughValues->at(channel));
×
UNCOV
554
        if (value < passthroughValue) // HTP merge
×
555
        {
UNCOV
556
            return passthroughValue;
×
557
        }
558
    }
559

560
    return value;
19,315,606✔
561
}
562

563
void Universe::updatePostGMValue(int channel)
19,315,606✔
564
{
565
    uchar value = preGMValue(channel);
19,315,606✔
566

567
    if (value != 0)
19,315,606✔
568
        value = applyGM(channel, value);
14,589,110✔
569

570
    value = applyModifiers(channel, value);
19,315,606✔
571
    value = applyPassthrough(channel, value);
19,315,606✔
572

573
    (*m_postGMValues)[channel] = static_cast<char>(value);
19,315,606✔
574
}
19,315,606✔
575

576
/************************************************************************
577
 * Patches
578
 ************************************************************************/
579

580
bool Universe::isPatched() const
2✔
581
{
582
    if (m_inputPatch != NULL || m_outputPatchList.count() || m_fbPatch != NULL)
2✔
583
        return true;
1✔
584

585
    return false;
1✔
586
}
587

588
bool Universe::setInputPatch(QLCIOPlugin *plugin,
6✔
589
                             quint32 input, QLCInputProfile *profile)
590
{
591
    qDebug() << "[Universe] setInputPatch - ID:" << m_id << ", plugin:" << ((plugin == NULL)?"None":plugin->name())
12✔
592
             << ", input:" << input << ", profile:" << ((profile == NULL)?"None":profile->name());
6✔
593
    if (m_inputPatch == NULL)
6✔
594
    {
595
        if (plugin == NULL || input == QLCIOPlugin::invalidLine())
5✔
596
            return true;
1✔
597

598
        m_inputPatch = new InputPatch(m_id, this);
4✔
599
        connectInputPatch();
4✔
600
    }
601
    else
602
    {
603
        if (input == QLCIOPlugin::invalidLine())
1✔
604
        {
UNCOV
605
            disconnectInputPatch();
×
UNCOV
606
            delete m_inputPatch;
×
UNCOV
607
            m_inputPatch = NULL;
×
UNCOV
608
            emit inputPatchChanged();
×
UNCOV
609
            return true;
×
610
        }
611
    }
612

613
    if (m_inputPatch != NULL)
5✔
614
    {
615
        bool result = m_inputPatch->set(plugin, input, profile);
5✔
616
        emit inputPatchChanged();
5✔
617
        return result;
5✔
618
    }
619

620
    return true;
×
621
}
622

623
bool Universe::setOutputPatch(QLCIOPlugin *plugin, quint32 output, int index)
169✔
624
{
625
    if (index < 0)
169✔
UNCOV
626
        return false;
×
627

628
    qDebug() << "[Universe] setOutputPatch - ID:" << m_id
169✔
629
             << ", plugin:" << ((plugin == NULL) ? "None" : plugin->name()) << ", output:" << output;
169✔
630

631
    // replace or delete an existing patch
632
    if (index < m_outputPatchList.count())
169✔
633
    {
634
        if (plugin == NULL || output == QLCIOPlugin::invalidLine())
2✔
635
        {
636
            // need to delete an existing patch
637
            OutputPatch *patch = m_outputPatchList.takeAt(index);
2✔
638
            delete patch;
2✔
639
            emit outputPatchesCountChanged();
2✔
640
            return true;
2✔
641
        }
642

UNCOV
643
        OutputPatch *patch = m_outputPatchList.at(index);
×
UNCOV
644
        bool result = patch->set(plugin, output);
×
UNCOV
645
        emit outputPatchChanged();
×
UNCOV
646
        return result;
×
647
    }
648
    else
649
    {
650
        if (plugin == NULL || output == QLCIOPlugin::invalidLine())
167✔
651
            return false;
1✔
652

653
        // add a new patch
654
        OutputPatch *patch = new OutputPatch(m_id, this);
166✔
655
        bool result = patch->set(plugin, output);
166✔
656
        m_outputPatchList.append(patch);
166✔
657
        emit outputPatchesCountChanged();
166✔
658
        return result;
166✔
659
    }
660

661
    return false;
662
}
663

664
bool Universe::setFeedbackPatch(QLCIOPlugin *plugin, quint32 output)
×
665
{
666
    qDebug() << Q_FUNC_INFO << "plugin:" << plugin << "output:" << output;
×
UNCOV
667
    if (m_fbPatch == NULL)
×
668
    {
UNCOV
669
        if (plugin == NULL || output == QLCIOPlugin::invalidLine())
×
670
            return false;
×
671

672
        m_fbPatch = new OutputPatch(m_id, this);
×
673
    }
674
    else
675
    {
UNCOV
676
        if (plugin == NULL || output == QLCIOPlugin::invalidLine())
×
677
        {
678
            delete m_fbPatch;
×
UNCOV
679
            m_fbPatch = NULL;
×
680
            emit hasFeedbackChanged();
×
681
            return true;
×
682
        }
683
    }
UNCOV
684
    if (m_fbPatch != NULL)
×
685
    {
UNCOV
686
        bool result = m_fbPatch->set(plugin, output);
×
UNCOV
687
        emit hasFeedbackChanged();
×
688
        return result;
×
689
    }
690

UNCOV
691
    return false;
×
692
}
693

UNCOV
694
bool Universe::hasFeedback() const
×
695
{
UNCOV
696
    return m_fbPatch != NULL ? true : false;
×
697
}
698

699
InputPatch *Universe::inputPatch() const
161✔
700
{
701
    return m_inputPatch;
161✔
702
}
703

704
OutputPatch *Universe::outputPatch(int index) const
498✔
705
{
706
    if (index < 0 || index >= m_outputPatchList.count())
498✔
707
        return NULL;
14✔
708

709
    return m_outputPatchList.at(index);
484✔
710
}
711

712
int Universe::outputPatchesCount() const
83✔
713
{
714
    return m_outputPatchList.count();
83✔
715
}
716

717
OutputPatch *Universe::feedbackPatch() const
49✔
718
{
719
    return m_fbPatch;
49✔
720
}
721

722
void Universe::dumpOutput(const QByteArray &data, bool dataChanged)
289,657✔
723
{
724
    if (m_outputPatchList.count() == 0)
289,657✔
725
        return;
264,867✔
726

727
    foreach (OutputPatch *op, m_outputPatchList)
49,580✔
728
    {
729
        if (m_totalChannelsChanged == true)
24,790✔
730
            op->setPluginParameter(PLUGIN_UNIVERSECHANNELS, m_totalChannels);
154✔
731

732
        if (op->blackout())
24,790✔
733
            op->dump(m_id, *m_blackoutValues, dataChanged);
20✔
734
        else
735
            op->dump(m_id, data, dataChanged);
24,770✔
736
    }
24,790✔
737
    m_totalChannelsChanged = false;
24,790✔
738
}
739

740
void Universe::flushInput()
289,629✔
741
{
742
    if (m_inputPatch == NULL)
289,629✔
743
        return;
289,624✔
744

745
    m_inputPatch->flush(m_id);
5✔
746
}
747

748
void Universe::slotInputValueChanged(quint32 universe, quint32 channel, uchar value, const QString &key)
×
749
{
UNCOV
750
    if (m_passthrough)
×
751
    {
752
        if (universe == m_id)
×
753
        {
754
            if (channel >= UNIVERSE_SIZE)
×
UNCOV
755
                return;
×
756

UNCOV
757
            if (channel >= m_usedChannels)
×
UNCOV
758
                m_usedChannels = channel + 1;
×
759

760
            (*m_passthroughValues)[channel] = value;
×
761

UNCOV
762
            updatePostGMValue(channel);
×
763
        }
764
    }
765
    else
UNCOV
766
        emit inputValueChanged(universe, channel, value, key);
×
767
}
768

769
void Universe::connectInputPatch()
8✔
770
{
771
    if (m_inputPatch == NULL)
8✔
772
        return;
4✔
773

774
    if (!m_passthrough)
4✔
775
        connect(m_inputPatch, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)),
4✔
776
                this, SIGNAL(inputValueChanged(quint32,quint32,uchar,QString)));
777
    else
UNCOV
778
        connect(m_inputPatch, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)),
×
779
                this, SLOT(slotInputValueChanged(quint32,quint32,uchar,const QString&)));
780
}
781

782
void Universe::disconnectInputPatch()
4✔
783
{
784
    if (m_inputPatch == NULL)
4✔
785
        return;
4✔
786

UNCOV
787
    if (!m_passthrough)
×
UNCOV
788
        disconnect(m_inputPatch, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)),
×
789
                this, SIGNAL(inputValueChanged(quint32,quint32,uchar,QString)));
790
    else
UNCOV
791
        disconnect(m_inputPatch, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)),
×
792
                this, SLOT(slotInputValueChanged(quint32,quint32,uchar,const QString&)));
793
}
794

795
/************************************************************************
796
 * Channels capabilities
797
 ************************************************************************/
798

799
void Universe::setChannelCapability(ushort channel, QLCChannel::Group group, ChannelType forcedType)
67,299✔
800
{
801
    if (channel >= (ushort)m_channelsMask->length())
67,299✔
UNCOV
802
        return;
×
803

804
    if (Utils::vectorRemove(m_intensityChannels, channel))
67,299✔
805
        m_intensityChannelsChanged = true;
835✔
806
    Utils::vectorRemove(m_nonIntensityChannels, channel);
67,299✔
807

808
    if (forcedType != Undefined)
67,299✔
809
    {
810
        (*m_channelsMask)[channel] = char(forcedType);
5✔
811
        if ((forcedType & HTP) == HTP)
5✔
812
        {
813
            //qDebug() << "--- Channel" << channel << "forced type HTP";
814
            Utils::vectorSortedAddUnique(m_intensityChannels, channel);
3✔
815
            m_intensityChannelsChanged = true;
3✔
816
            if (group == QLCChannel::Intensity)
3✔
817
            {
818
                //qDebug() << "--- Channel" << channel << "Intensity + HTP";
UNCOV
819
                (*m_channelsMask)[channel] = char(HTP | Intensity);
×
820
            }
821
        }
822
        else if ((forcedType & LTP) == LTP)
2✔
823
        {
824
            //qDebug() << "--- Channel" << channel << "forced type LTP";
825
            Utils::vectorSortedAddUnique(m_nonIntensityChannels, channel);
2✔
826
        }
827
    }
828
    else
829
    {
830
        if (group == QLCChannel::Intensity)
67,294✔
831
        {
832
            //qDebug() << "--- Channel" << channel << "Intensity + HTP";
833
            (*m_channelsMask)[channel] = char(HTP | Intensity);
45,605✔
834
            Utils::vectorSortedAddUnique(m_intensityChannels, channel);
45,605✔
835
            m_intensityChannelsChanged = true;
45,605✔
836
        }
837
        else
838
        {
839
            //qDebug() << "--- Channel" << channel << "LTP";
840
            (*m_channelsMask)[channel] = char(LTP);
21,689✔
841
            Utils::vectorSortedAddUnique(m_nonIntensityChannels, channel);
21,689✔
842
        }
843
    }
844

845
    // qDebug() << Q_FUNC_INFO << "Channel:" << channel << "mask:" << QString::number(m_channelsMask->at(channel), 16);
846
    if (channel >= m_totalChannels)
67,299✔
847
    {
848
        m_totalChannels = channel + 1;
65,688✔
849
        m_totalChannelsChanged = true;
65,688✔
850
    }
851
}
852

853
uchar Universe::channelCapabilities(ushort channel) const
517✔
854
{
855
    if (channel >= (ushort)m_channelsMask->length())
517✔
UNCOV
856
        return Undefined;
×
857

858
    return m_channelsMask->at(channel);
517✔
859
}
860

861
void Universe::setChannelDefaultValue(ushort channel, uchar value)
2,650✔
862
{
863
    if (channel >= m_totalChannels)
2,650✔
864
    {
UNCOV
865
        m_totalChannels = channel + 1;
×
UNCOV
866
        m_totalChannelsChanged = true;
×
867
    }
868

869
    if (channel >= m_usedChannels)
2,650✔
870
        m_usedChannels = channel + 1;
1,039✔
871

872
    (*m_preGMValues)[channel] = value;
2,650✔
873
    updatePostGMValue(channel);
2,650✔
874
}
2,650✔
875

876
void Universe::setChannelModifier(ushort channel, ChannelModifier *modifier)
2,650✔
877
{
878
    if (channel >= (ushort)m_modifiers.count())
2,650✔
879
        return;
×
880

881
    m_modifiers[channel] = modifier;
2,650✔
882

883
    if (modifier != NULL)
2,650✔
884
    {
UNCOV
885
        (*m_modifiedZeroValues)[channel] = modifier->getValue(0);
×
886

887
        if (channel >= m_totalChannels)
×
888
        {
UNCOV
889
            m_totalChannels = channel + 1;
×
UNCOV
890
            m_totalChannelsChanged = true;
×
891
        }
892

UNCOV
893
        if (channel >= m_usedChannels)
×
894
            m_usedChannels = channel + 1;
×
895
    }
896

897
    updatePostGMValue(channel);
2,650✔
898
}
899

UNCOV
900
ChannelModifier *Universe::channelModifier(ushort channel)
×
901
{
UNCOV
902
    if (channel >= (ushort)m_modifiers.count())
×
UNCOV
903
        return NULL;
×
904

UNCOV
905
    return m_modifiers.at(channel);
×
906
}
907

908
void Universe::updateIntensityChannelsRanges()
299,711✔
909
{
910
    if (!m_intensityChannelsChanged)
299,711✔
911
        return;
299,521✔
912

913
    m_intensityChannelsChanged = false;
190✔
914

915
    m_intensityChannelsRanges.clear();
190✔
916
    short currentPos = -1;
190✔
917
    short currentSize = 0;
190✔
918

919
    for (int i = 0; i < m_intensityChannels.size(); ++i)
41,853✔
920
    {
921
        int channel = m_intensityChannels.at(i);
41,663✔
922
        if (currentPos + currentSize == channel)
41,663✔
923
            ++currentSize;
21,129✔
924
        else
925
        {
926
            if (currentPos != -1)
20,534✔
927
                m_intensityChannelsRanges.append((currentPos << 16) | currentSize);
20,344✔
928
            currentPos = channel;
20,534✔
929
            currentSize = 1;
20,534✔
930
        }
931
    }
932
    if (currentPos != -1)
190✔
933
        m_intensityChannelsRanges.append((currentPos << 16) | currentSize);
190✔
934

935
    qDebug() << Q_FUNC_INFO << ":" << m_intensityChannelsRanges.size() << "ranges";
190✔
936
}
937

938
/****************************************************************************
939
 * Writing
940
 ****************************************************************************/
941

942
bool Universe::write(int address, uchar value, bool forceLTP)
6,069,911✔
943
{
944
    Q_ASSERT(address < UNIVERSE_SIZE);
6,069,911✔
945

946
    //qDebug() << "[Universe]" << id() << ": write channel" << address << ", value:" << value;
947

948
    if (address >= m_usedChannels)
6,069,911✔
949
        m_usedChannels = address + 1;
9,373✔
950

951
    if (m_channelsMask->at(address) & HTP)
6,069,911✔
952
    {
953
        if (forceLTP == false && value < (uchar)m_preGMValues->at(address))
3,503,733✔
954
        {
UNCOV
955
            return false;
×
956
        }
957
    }
958
    else
959
    {
960
        // preserve non HTP channels for blackout
961
        (*m_blackoutValues)[address] = char(value);
2,566,178✔
962
    }
963

964
    (*m_preGMValues)[address] = char(value);
6,069,911✔
965

966
    updatePostGMValue(address);
6,069,911✔
967

968
    return true;
6,069,911✔
969
}
970

971
bool Universe::writeMultiple(int address, quint32 value, int channelCount)
10,184,817✔
972
{
973
    for (int i = 0; i < channelCount; i++)
20,369,634✔
974
    {
975
        //qDebug() << "[Universe]" << id() << ": write channel" << (address + i) << ", value:" << QString::number(((uchar *)&value)[channelCount - 1 - i]);
976

977
        // preserve non HTP channels for blackout
978
        if ((m_channelsMask->at(address + i) & HTP) == 0)
10,184,817✔
979
            (*m_blackoutValues)[address + i] = ((uchar *)&value)[channelCount - 1 - i];
4,543,835✔
980

981
        (*m_preGMValues)[address + i] = ((uchar *)&value)[channelCount - 1 - i];
10,184,817✔
982

983
        updatePostGMValue(address + i);
10,184,817✔
984
    }
985

986
    return true;
10,184,817✔
987
}
988

989
bool Universe::writeRelative(int address, quint32 value, int channelCount)
495,009✔
990
{
991
    Q_ASSERT(address < UNIVERSE_SIZE);
495,009✔
992

993
    //qDebug() << "Write relative channel" << address << "value" << value;
994

995
    if (address + channelCount >= m_usedChannels)
495,009✔
996
        m_usedChannels = address + channelCount;
24,759✔
997

998
    if (channelCount == 1)
495,009✔
999
    {
1000
        short newVal = uchar((*m_preGMValues)[address]);
495,006✔
1001
        newVal += short(value) - RELATIVE_ZERO_8BIT;
495,006✔
1002
        (*m_preGMValues)[address] = char(CLAMP(newVal, 0, UCHAR_MAX));
495,006✔
1003
        (*m_blackoutValues)[address] = char(CLAMP(newVal, 0, UCHAR_MAX));
495,006✔
1004
        updatePostGMValue(address);
495,006✔
1005
    }
1006
    else
1007
    {
1008
        quint32 currentValue = 0;
3✔
1009
        for (int i = 0; i < channelCount; i++)
9✔
1010
            currentValue = (currentValue << 8) + uchar(m_preGMValues->at(address + i));
6✔
1011

1012
        currentValue = qint32(CLAMP((qint32)currentValue + (qint32)value - RELATIVE_ZERO_16BIT, 0, 0xFFFF));
3✔
1013

1014
        for (int i = 0; i < channelCount; i++)
9✔
1015
        {
1016
            (*m_preGMValues)[address + i] = ((uchar *)&currentValue)[channelCount - 1 - i];
6✔
1017
            (*m_blackoutValues)[address + i] = ((uchar *)&currentValue)[channelCount - 1 - i];
6✔
1018
            updatePostGMValue(address + i);
6✔
1019
        }
1020
    }
1021

1022
    return true;
495,009✔
1023
}
1024

1025
bool Universe::writeBlended(int address, quint32 value, int channelCount, Universe::BlendMode blend)
10,184,825✔
1026
{
1027
    if (address + channelCount >= m_usedChannels)
10,184,825✔
1028
        m_usedChannels = address + channelCount;
1,330✔
1029

1030
    quint32 currentValue = 0;
10,184,825✔
1031
    for (int i = 0; i < channelCount; i++)
20,369,650✔
1032
        currentValue = (currentValue << 8) + uchar(m_preGMValues->at(address + i));
10,184,825✔
1033

1034
    switch (blend)
10,184,825✔
1035
    {
1036
        case NormalBlend:
3,848,819✔
1037
        {
1038
            if ((m_channelsMask->at(address) & HTP) && value < currentValue)
3,848,819✔
1039
            {
1040
                return false;
7✔
1041
            }
1042
        }
1043
        break;
3,848,812✔
1044
        case MaskBlend:
2,128,502✔
1045
        {
1046
            const float maxValue = (channelCount == 1)
1047
                                   ? 255.0f
2,128,502✔
UNCOV
1048
                                   : (channelCount == 2 ? 65535.0f : float(pow(255.0f, channelCount)));
×
1049
            if (value)
2,128,502✔
1050
            {
1051
                if (currentValue)
2,095,214✔
1052
                    value = float(currentValue) * (float(value) / maxValue);
1✔
1053
                else
1054
                    value = 0;
2,095,213✔
1055
            }
1056
        }
1057
        break;
2,128,502✔
1058
        case AdditiveBlend:
2,103,751✔
1059
        {
1060
            const float maxValue = (channelCount == 1)
1061
                                   ? 255.0f
2,103,751✔
UNCOV
1062
                                   : (channelCount == 2 ? 65535.0f : float(pow(255.0f, channelCount)));
×
1063
            //qDebug() << "Universe write additive channel" << channel << ", value:" << currVal << "+" << value;
1064
            value = fmin(float(currentValue + value), maxValue);
2,103,751✔
1065
        }
1066
        break;
2,103,751✔
1067
        case SubtractiveBlend:
2,103,752✔
1068
        {
1069
            if (value >= currentValue)
2,103,752✔
1070
                value = 0;
2,103,751✔
1071
            else
1072
                value = currentValue - value;
1✔
1073
        }
1074
        break;
2,103,752✔
1075
        default:
1✔
1076
            qDebug() << "[Universe] Blend mode not handled. Implement me!" << blend;
1✔
1077
            return false;
1✔
1078
        break;
1079
    }
1080

1081
    writeMultiple(address, value, channelCount);
10,184,817✔
1082

1083
    return true;
10,184,817✔
1084
}
1085

1086
/*********************************************************************
1087
 * Load & Save
1088
 *********************************************************************/
1089

1090
bool Universe::loadXML(QXmlStreamReader &root, int index, InputOutputMap *ioMap)
5✔
1091
{
1092
    if (root.name() != KXMLQLCUniverse)
5✔
1093
    {
1094
        qWarning() << Q_FUNC_INFO << "Universe node not found";
1✔
1095
        return false;
1✔
1096
    }
1097

1098
    int outputIndex = 0;
4✔
1099

1100
    QXmlStreamAttributes attrs = root.attributes();
4✔
1101

1102
    if (attrs.hasAttribute(KXMLQLCUniverseName))
4✔
1103
        setName(attrs.value(KXMLQLCUniverseName).toString());
4✔
1104

1105
    if (attrs.hasAttribute(KXMLQLCUniversePassthrough))
4✔
1106
    {
1107
        if (attrs.value(KXMLQLCUniversePassthrough).toString() == KXMLQLCTrue ||
14✔
1108
            attrs.value(KXMLQLCUniversePassthrough).toString() == "1")
7✔
1109
            setPassthrough(true);
2✔
1110
        else
1111
            setPassthrough(false);
1✔
1112
    }
1113
    else
1114
    {
1115
        setPassthrough(false);
1✔
1116
    }
1117

1118
    while (root.readNextStartElement())
4✔
1119
    {
1120
        QXmlStreamAttributes pAttrs = root.attributes();
×
1121

1122
        if (root.name() == KXMLQLCUniverseInputPatch)
×
1123
        {
1124
            QString plugin = KInputNone;
×
1125
            quint32 inputLine = QLCIOPlugin::invalidLine();
×
1126
            QString inputUID;
×
1127
            QString inputName;
×
1128
            QString profile = KInputNone;
×
1129

1130
            if (pAttrs.hasAttribute(KXMLQLCUniversePlugin))
×
1131
                plugin = pAttrs.value(KXMLQLCUniversePlugin).toString();
×
1132
            if (pAttrs.hasAttribute(KXMLQLCUniverseLineUID))
×
1133
                inputUID = pAttrs.value(KXMLQLCUniverseLineUID).toString();
×
1134
            if (pAttrs.hasAttribute(KXMLQLCUniverseLineName))
×
1135
                inputName = pAttrs.value(KXMLQLCUniverseLineName).toString();
×
UNCOV
1136
            else if (pAttrs.hasAttribute(KXMLQLCUniverseLineUID))
×
UNCOV
1137
                inputName = inputUID; // backward compat: old files stored name in UID attribute
×
1138
            if (pAttrs.hasAttribute(KXMLQLCUniverseLine))
×
UNCOV
1139
                inputLine = pAttrs.value(KXMLQLCUniverseLine).toString().toUInt();
×
1140
            if (pAttrs.hasAttribute(KXMLQLCUniverseProfileName))
×
1141
                profile = pAttrs.value(KXMLQLCUniverseProfileName).toString();
×
1142

1143
            // apply the parameters just loaded
UNCOV
1144
            ioMap->setInputPatch(index, plugin, inputUID, inputName, inputLine, profile);
×
1145

UNCOV
1146
            QXmlStreamReader::TokenType tType = root.readNext();
×
1147
            if (tType == QXmlStreamReader::Characters)
×
1148
                tType = root.readNext();
×
1149

1150
            // check if there is a PluginParameters tag defined
1151
            if (tType == QXmlStreamReader::StartElement)
×
1152
            {
UNCOV
1153
                if (root.name() == KXMLQLCUniversePluginParameters)
×
1154
                    loadXMLPluginParameters(root, InputPatchTag, 0);
×
1155
                root.skipCurrentElement();
×
1156
            }
1157
        }
×
UNCOV
1158
        else if (root.name() == KXMLQLCUniverseOutputPatch)
×
1159
        {
1160
            QString plugin = KOutputNone;
×
1161
            QString outputUID;
×
1162
            QString outputName;
×
1163
            quint32 outputLine = QLCIOPlugin::invalidLine();
×
1164

1165
            if (pAttrs.hasAttribute(KXMLQLCUniversePlugin))
×
1166
                plugin = pAttrs.value(KXMLQLCUniversePlugin).toString();
×
1167
            if (pAttrs.hasAttribute(KXMLQLCUniverseLineUID))
×
1168
                outputUID = pAttrs.value(KXMLQLCUniverseLineUID).toString();
×
UNCOV
1169
            if (pAttrs.hasAttribute(KXMLQLCUniverseLineName))
×
UNCOV
1170
                outputName = pAttrs.value(KXMLQLCUniverseLineName).toString();
×
1171
            else if (pAttrs.hasAttribute(KXMLQLCUniverseLineUID))
×
UNCOV
1172
                outputName = outputUID; // backward compat: old files stored name in UID attribute
×
1173
            if (pAttrs.hasAttribute(KXMLQLCUniverseLine))
×
1174
                outputLine = pAttrs.value(KXMLQLCUniverseLine).toString().toUInt();
×
1175

1176
            // apply the parameters just loaded
UNCOV
1177
            ioMap->setOutputPatch(index, plugin, outputUID, outputName, outputLine, false, outputIndex);
×
1178

UNCOV
1179
            QXmlStreamReader::TokenType tType = root.readNext();
×
1180
            if (tType == QXmlStreamReader::Characters)
×
1181
                tType = root.readNext();
×
1182

1183
            // check if there is a PluginParameters tag defined
UNCOV
1184
            if (tType == QXmlStreamReader::StartElement)
×
1185
            {
1186
                if (root.name() == KXMLQLCUniversePluginParameters)
×
1187
                    loadXMLPluginParameters(root, OutputPatchTag, outputIndex);
×
UNCOV
1188
                root.skipCurrentElement();
×
1189
            }
1190

1191
            outputIndex++;
×
1192
        }
×
UNCOV
1193
        else if (root.name() == KXMLQLCUniverseFeedbackPatch)
×
1194
        {
1195
            QString plugin = KOutputNone;
×
1196
            QString outputUID;
×
1197
            QString outputName;
×
1198
            quint32 output = QLCIOPlugin::invalidLine();
×
1199

1200
            if (pAttrs.hasAttribute(KXMLQLCUniversePlugin))
×
1201
                plugin = pAttrs.value(KXMLQLCUniversePlugin).toString();
×
1202
            if (pAttrs.hasAttribute(KXMLQLCUniverseLineUID))
×
1203
                outputUID = pAttrs.value(KXMLQLCUniverseLineUID).toString();
×
UNCOV
1204
            if (pAttrs.hasAttribute(KXMLQLCUniverseLineName))
×
UNCOV
1205
                outputName = pAttrs.value(KXMLQLCUniverseLineName).toString();
×
1206
            else if (pAttrs.hasAttribute(KXMLQLCUniverseLineUID))
×
UNCOV
1207
                outputName = outputUID; // backward compat: old files stored name in UID attribute
×
1208
            if (pAttrs.hasAttribute(KXMLQLCUniverseLine))
×
1209
                output = pAttrs.value(KXMLQLCUniverseLine).toString().toUInt();
×
1210

1211
            // apply the parameters just loaded
UNCOV
1212
            ioMap->setOutputPatch(index, plugin, outputUID, outputName, output, true);
×
1213

UNCOV
1214
            QXmlStreamReader::TokenType tType = root.readNext();
×
1215
            if (tType == QXmlStreamReader::Characters)
×
1216
                tType = root.readNext();
×
1217

1218
            // check if there is a PluginParameters tag defined
1219
            if (tType == QXmlStreamReader::StartElement)
×
1220
            {
UNCOV
1221
                if (root.name() == KXMLQLCUniversePluginParameters)
×
1222
                    loadXMLPluginParameters(root, FeedbackPatchTag, 0);
×
1223
                root.skipCurrentElement();
×
1224
            }
1225
        }
×
1226
        else
1227
        {
UNCOV
1228
            qWarning() << Q_FUNC_INFO << "Unknown Universe tag:" << root.name();
×
UNCOV
1229
            root.skipCurrentElement();
×
1230
        }
UNCOV
1231
    }
×
1232

1233
    return true;
4✔
1234
}
4✔
1235

UNCOV
1236
bool Universe::loadXMLPluginParameters(QXmlStreamReader &root, PatchTagType currentTag, int patchIndex)
×
1237
{
1238
    if (root.name() != KXMLQLCUniversePluginParameters)
×
1239
    {
UNCOV
1240
        qWarning() << Q_FUNC_INFO << "PluginParameters node not found";
×
1241
        return false;
×
1242
    }
1243

1244
    QXmlStreamAttributes pluginAttrs = root.attributes();
×
1245
    for (int i = 0; i < pluginAttrs.count(); i++)
×
1246
    {
UNCOV
1247
        QXmlStreamAttribute attr = pluginAttrs.at(i);
×
1248
        if (currentTag == InputPatchTag)
×
1249
        {
1250
            InputPatch *ip = inputPatch();
×
1251
            if (ip != NULL)
×
1252
                ip->setPluginParameter(attr.name().toString(), attr.value().toString());
×
1253
        }
1254
        else if (currentTag == OutputPatchTag)
×
1255
        {
1256
            OutputPatch *op = outputPatch(patchIndex);
×
1257
            if (op != NULL)
×
1258
                op->setPluginParameter(attr.name().toString(), attr.value().toString());
×
1259
        }
1260
        else if (currentTag == FeedbackPatchTag)
×
1261
        {
UNCOV
1262
            OutputPatch *fbp = feedbackPatch();
×
1263
            if (fbp != NULL)
×
1264
                fbp->setPluginParameter(attr.name().toString(), attr.value().toString());
×
1265
        }
UNCOV
1266
    }
×
UNCOV
1267
    root.skipCurrentElement();
×
1268

UNCOV
1269
    return true;
×
UNCOV
1270
}
×
1271

1272
bool Universe::saveXML(QXmlStreamWriter *doc) const
6✔
1273
{
1274
    Q_ASSERT(doc != NULL);
6✔
1275

1276
    doc->writeStartElement(KXMLQLCUniverse);
12✔
1277
    doc->writeAttribute(KXMLQLCUniverseName, name());
12✔
1278
    doc->writeAttribute(KXMLQLCUniverseID, QString::number(id()));
12✔
1279

1280
    if (passthrough() == true)
6✔
1281
        doc->writeAttribute(KXMLQLCUniversePassthrough, KXMLQLCTrue);
3✔
1282

1283
    if (inputPatch() != NULL)
6✔
1284
    {
1285
        savePatchXML(doc, KXMLQLCUniverseInputPatch, inputPatch()->pluginName(),
×
1286
            inputPatch()->inputName(), inputPatch()->inputUID(),
×
1287
            inputPatch()->input(), inputPatch()->profileName(), inputPatch()->getPluginParameters());
×
1288
    }
1289
    foreach (OutputPatch *op, m_outputPatchList)
6✔
1290
    {
1291
        savePatchXML(doc, KXMLQLCUniverseOutputPatch, op->pluginName(),
×
1292
            op->outputName(), op->outputUID(),
×
1293
            op->output(), "", op->getPluginParameters());
×
1294
    }
6✔
1295
    if (feedbackPatch() != NULL)
6✔
1296
    {
UNCOV
1297
        savePatchXML(doc, KXMLQLCUniverseFeedbackPatch, feedbackPatch()->pluginName(),
×
UNCOV
1298
            feedbackPatch()->outputName(), feedbackPatch()->outputUID(),
×
UNCOV
1299
            feedbackPatch()->output(), "", feedbackPatch()->getPluginParameters());
×
1300
    }
1301

1302
    /* End the <Universe> tag */
1303
    doc->writeEndElement();
6✔
1304

1305
    return true;
6✔
1306
}
1307

UNCOV
1308
void Universe::savePatchXML(
×
1309
    QXmlStreamWriter *doc,
1310
    const QString &tag,
1311
    const QString &pluginName,
1312
    const QString &lineName,
1313
    const QString &lineUID,
1314
    quint32 line,
1315
    QString profileName,
1316
    QMap<QString, QVariant> parameters) const
1317
{
1318
    // sanity check: don't save invalid data
1319
    if (pluginName.isEmpty() || pluginName == KInputNone || line == QLCIOPlugin::invalidLine())
×
1320
        return;
×
1321

1322
    doc->writeStartElement(tag);
×
UNCOV
1323
    doc->writeAttribute(KXMLQLCUniversePlugin, pluginName);
×
1324
    doc->writeAttribute(KXMLQLCUniverseLineName, lineName);
×
1325
    doc->writeAttribute(KXMLQLCUniverseLineUID, lineUID);
×
UNCOV
1326
    doc->writeAttribute(KXMLQLCUniverseLine, QString::number(line));
×
UNCOV
1327
    if (!profileName.isEmpty() && profileName != KInputNone)
×
1328
        doc->writeAttribute(KXMLQLCUniverseProfileName, profileName);
×
1329

UNCOV
1330
    savePluginParametersXML(doc, parameters);
×
1331
    doc->writeEndElement();
×
1332
}
1333

1334
bool Universe::savePluginParametersXML(QXmlStreamWriter *doc,
×
1335
                                       QMap<QString, QVariant> parameters) const
1336
{
1337
    Q_ASSERT(doc != NULL);
×
1338

UNCOV
1339
    if (parameters.isEmpty())
×
1340
        return false;
×
1341

1342
    doc->writeStartElement(KXMLQLCUniversePluginParameters);
×
1343
    QMapIterator<QString, QVariant> it(parameters);
×
1344
    while (it.hasNext())
×
1345
    {
UNCOV
1346
        it.next();
×
1347
        QString pName = it.key();
×
1348
        QVariant pValue = it.value();
×
UNCOV
1349
        doc->writeAttribute(pName, pValue.toString());
×
UNCOV
1350
    }
×
UNCOV
1351
    doc->writeEndElement();
×
1352

UNCOV
1353
    return true;
×
UNCOV
1354
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc