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

mcallegari / qlcplus / 28807794184

06 Jul 2026 04:42PM UTC coverage: 35.285% (+1.1%) from 34.233%
28807794184

push

github

mcallegari
qmlui: VC Speed dial tap button can tap global BPM

18560 of 52600 relevant lines covered (35.29%)

40987.85 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
        {
91
            qCritical() << Q_FUNC_INFO << "Universe" << m_id
×
92
                        << "thread did not stop within 2 seconds, forcing termination";
×
93
            terminate();
×
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✔
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✔
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
{
258
    QMutexLocker fadersLocker(&m_fadersMutex);
×
259
    int pos = m_faders.indexOf(fader);
×
260
    int newPos = 0;
×
261

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

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

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

284
QList<QSharedPointer<GenericFader> > Universe::faders()
×
285
{
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);
×
306
    foreach (QSharedPointer<GenericFader> fader, m_faders)
×
307
    {
308
        if (!fader.isNull() && fader->parentFunctionID() != Function::invalidId())
×
309
            fader->setFadeOut(true, uint(fadeTime));
×
310
    }
×
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✔
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✔
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

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

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

373
    while (m_running)
×
374
    {
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();
×
385
        elapsedTimer.restart();
×
386
        uint elapsedMs = uint((elapsedNs + 500000) / 1000000);
×
387
        if (elapsedMs == 0)
×
388
            elapsedMs = 1;
×
389

390
        processFaders(elapsedMs);
×
391
    }
392

393
    qDebug() << "Universe thread stopped" << id();
×
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✔
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✔
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

434
    for (int i = address; i < address + range && i < UNIVERSE_SIZE; i++)
×
435
    {
436
        if (static_cast<uchar>(m_postGMValues->at(i)) < static_cast<uchar>(m_passthroughValues->at(i))) // HTP merge
×
437
        {
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
    {
461
        int channel = m_intensityChannels.at(i);
×
462
        intensityList[channel] = m_preGMValues->at(channel);
×
463
    }
464
    return intensityList;
1✔
465
}
×
466

467
uchar Universe::postGMValue(int address) const
4✔
468
{
469
    if (address >= m_postGMValues->size())
4✔
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✔
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✔
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
    {
553
        const uchar passthroughValue = static_cast<uchar>(m_passthroughValues->at(channel));
×
554
        if (value < passthroughValue) // HTP merge
×
555
        {
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
        {
605
            disconnectInputPatch();
×
606
            delete m_inputPatch;
×
607
            m_inputPatch = NULL;
×
608
            emit inputPatchChanged();
×
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✔
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

643
        OutputPatch *patch = m_outputPatchList.at(index);
×
644
        bool result = patch->set(plugin, output);
×
645
        emit outputPatchChanged();
×
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;
×
667
    if (m_fbPatch == NULL)
×
668
    {
669
        if (plugin == NULL || output == QLCIOPlugin::invalidLine())
×
670
            return false;
×
671

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

691
    return false;
×
692
}
693

694
bool Universe::hasFeedback() const
×
695
{
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
{
750
    if (m_passthrough)
×
751
    {
752
        if (universe == m_id)
×
753
        {
754
            if (channel >= UNIVERSE_SIZE)
×
755
                return;
×
756

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

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

762
            updatePostGMValue(channel);
×
763
        }
764
    }
765
    else
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
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

787
    if (!m_passthrough)
×
788
        disconnect(m_inputPatch, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)),
×
789
                this, SIGNAL(inputValueChanged(quint32,quint32,uchar,QString)));
790
    else
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✔
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";
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✔
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
    {
865
        m_totalChannels = channel + 1;
×
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
    {
885
        (*m_modifiedZeroValues)[channel] = modifier->getValue(0);
×
886

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

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

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

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

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,372✔
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
        {
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,909✔
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,630✔
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✔
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✔
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();
×
1136
            else if (pAttrs.hasAttribute(KXMLQLCUniverseLineUID))
×
1137
                inputName = inputUID; // backward compat: old files stored name in UID attribute
×
1138
            if (pAttrs.hasAttribute(KXMLQLCUniverseLine))
×
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
1144
            ioMap->setInputPatch(index, plugin, inputUID, inputName, inputLine, profile);
×
1145

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
            {
1153
                if (root.name() == KXMLQLCUniversePluginParameters)
×
1154
                    loadXMLPluginParameters(root, InputPatchTag, 0);
×
1155
                root.skipCurrentElement();
×
1156
            }
1157
        }
×
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();
×
1169
            if (pAttrs.hasAttribute(KXMLQLCUniverseLineName))
×
1170
                outputName = pAttrs.value(KXMLQLCUniverseLineName).toString();
×
1171
            else if (pAttrs.hasAttribute(KXMLQLCUniverseLineUID))
×
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
1177
            ioMap->setOutputPatch(index, plugin, outputUID, outputName, outputLine, false, outputIndex);
×
1178

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
1184
            if (tType == QXmlStreamReader::StartElement)
×
1185
            {
1186
                if (root.name() == KXMLQLCUniversePluginParameters)
×
1187
                    loadXMLPluginParameters(root, OutputPatchTag, outputIndex);
×
1188
                root.skipCurrentElement();
×
1189
            }
1190

1191
            outputIndex++;
×
1192
        }
×
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();
×
1204
            if (pAttrs.hasAttribute(KXMLQLCUniverseLineName))
×
1205
                outputName = pAttrs.value(KXMLQLCUniverseLineName).toString();
×
1206
            else if (pAttrs.hasAttribute(KXMLQLCUniverseLineUID))
×
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
1212
            ioMap->setOutputPatch(index, plugin, outputUID, outputName, output, true);
×
1213

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
            {
1221
                if (root.name() == KXMLQLCUniversePluginParameters)
×
1222
                    loadXMLPluginParameters(root, FeedbackPatchTag, 0);
×
1223
                root.skipCurrentElement();
×
1224
            }
1225
        }
×
1226
        else
1227
        {
1228
            qWarning() << Q_FUNC_INFO << "Unknown Universe tag:" << root.name();
×
1229
            root.skipCurrentElement();
×
1230
        }
1231
    }
×
1232

1233
    return true;
4✔
1234
}
4✔
1235

1236
bool Universe::loadXMLPluginParameters(QXmlStreamReader &root, PatchTagType currentTag, int patchIndex)
×
1237
{
1238
    if (root.name() != KXMLQLCUniversePluginParameters)
×
1239
    {
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
    {
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
        {
1262
            OutputPatch *fbp = feedbackPatch();
×
1263
            if (fbp != NULL)
×
1264
                fbp->setPluginParameter(attr.name().toString(), attr.value().toString());
×
1265
        }
1266
    }
×
1267
    root.skipCurrentElement();
×
1268

1269
    return true;
×
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
    {
1297
        savePatchXML(doc, KXMLQLCUniverseFeedbackPatch, feedbackPatch()->pluginName(),
×
1298
            feedbackPatch()->outputName(), feedbackPatch()->outputUID(),
×
1299
            feedbackPatch()->output(), "", feedbackPatch()->getPluginParameters());
×
1300
    }
1301

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

1305
    return true;
6✔
1306
}
1307

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);
×
1323
    doc->writeAttribute(KXMLQLCUniversePlugin, pluginName);
×
1324
    doc->writeAttribute(KXMLQLCUniverseLineName, lineName);
×
1325
    doc->writeAttribute(KXMLQLCUniverseLineUID, lineUID);
×
1326
    doc->writeAttribute(KXMLQLCUniverseLine, QString::number(line));
×
1327
    if (!profileName.isEmpty() && profileName != KInputNone)
×
1328
        doc->writeAttribute(KXMLQLCUniverseProfileName, profileName);
×
1329

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

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

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

1353
    return true;
×
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