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

mcallegari / qlcplus / 19144422256

06 Nov 2025 05:33PM UTC coverage: 34.256% (-0.1%) from 34.358%
19144422256

push

github

mcallegari
Back to 5.1.0 debug

17718 of 51723 relevant lines covered (34.26%)

19528.23 hits per line

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

63.82
/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 <QDebug>
23
#include <math.h>
24

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

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

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

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

73
    m_name = QString("Universe %1").arg(id + 1);
926✔
74

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

79
Universe::~Universe()
1,822✔
80
{
81
    if (isRunning() == true)
911✔
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
        wait(1000);
×
90
    }
91

92
    delete m_inputPatch;
911✔
93
    int opCount = m_outputPatchList.count();
911✔
94
    for (int i = 0; i < opCount; i++)
925✔
95
    {
96
        OutputPatch *patch = m_outputPatchList.takeLast();
14✔
97
        delete patch;
14✔
98
    }
99
    delete m_fbPatch;
911✔
100
}
1,822✔
101

102
void Universe::setName(QString name)
7✔
103
{
104
    if (name.isEmpty())
7✔
105
        m_name = QString("Universe %1").arg(m_id + 1);
×
106
    else
107
        m_name = name;
7✔
108
    emit nameChanged();
7✔
109
}
7✔
110

111
QString Universe::name() const
107✔
112
{
113
    return m_name;
107✔
114
}
115

116
void Universe::setID(quint32 id)
2✔
117
{
118
    m_id = id;
2✔
119
}
2✔
120

121
quint32 Universe::id() const
133,743✔
122
{
123
    return m_id;
133,743✔
124
}
125

126
ushort Universe::usedChannels()
73✔
127
{
128
    return m_usedChannels;
73✔
129
}
130

131
ushort Universe::totalChannels()
6✔
132
{
133
    return m_totalChannels;
6✔
134
}
135

136
bool Universe::hasChanged()
5,386,909✔
137
{
138
    bool changed =
139
        memcmp(m_lastPostGMValues->constData(), m_postGMValues->constData(), m_usedChannels) != 0;
5,386,909✔
140
    if (changed)
5,386,909✔
141
        memcpy(m_lastPostGMValues->data(), m_postGMValues->constData(), m_usedChannels);
133,967✔
142
    return changed;
5,386,909✔
143
}
144

145
void Universe::setPassthrough(bool enable)
6✔
146
{
147
    if (enable == m_passthrough)
6✔
148
        return;
2✔
149

150
    qDebug() << "Set universe" << id() << "passthrough to" << enable;
4✔
151

152
    disconnectInputPatch();
4✔
153

154
    if (enable && m_passthroughValues.isNull())
4✔
155
    {
156
        // When passthrough is disabled, we don't release the array, since it's only ~512 B and
157
        // we would have to synchronize with other threads
158

159
        // When enabling passthrough, make sure the array is allocated BEFORE m_passthrough is set to
160
        // true. That way we only have to check for m_passthrough, and do not need to check
161
        // m_passthroughValues.isNull()
162
        m_passthroughValues.reset(new QByteArray(UNIVERSE_SIZE, char(0)));
4✔
163
    }
164

165
    m_passthrough = enable;
4✔
166

167
    connectInputPatch();
4✔
168

169
    emit passthroughChanged();
4✔
170
}
171

172
bool Universe::passthrough() const
12✔
173
{
174
    return m_passthrough;
12✔
175
}
176

177
void Universe::setMonitor(bool enable)
1✔
178
{
179
    m_monitor = enable;
1✔
180
}
1✔
181

182
bool Universe::monitor() const
1✔
183
{
184
    return m_monitor;
1✔
185
}
186

187
void Universe::slotGMValueChanged()
5,033✔
188
{
189
    {
190
        for (int i = 0; i < m_intensityChannels.size(); ++i)
2,565,584✔
191
        {
192
            int channel = m_intensityChannels.at(i);
2,560,551✔
193
            updatePostGMValue(channel);
2,560,551✔
194
        }
195
    }
196

197
    if (m_grandMaster->channelMode() == GrandMaster::AllChannels)
5,033✔
198
    {
199
        for (int i = 0; i < m_nonIntensityChannels.size(); ++i)
32✔
200
        {
201
            int channel = m_nonIntensityChannels.at(i);
15✔
202
            updatePostGMValue(channel);
15✔
203
        }
204
    }
205
}
5,033✔
206

207
/************************************************************************
208
 * Faders
209
 ************************************************************************/
210

211
QSharedPointer<GenericFader> Universe::requestFader(Universe::FaderPriority priority)
151✔
212
{
213
    int insertPos = 0;
151✔
214
    QSharedPointer<GenericFader> fader = QSharedPointer<GenericFader>(new GenericFader());
151✔
215
    fader->setPriority(priority);
151✔
216

217
    {
218
        QMutexLocker fadersLocker(&m_fadersMutex);
151✔
219
        {
220
            for (int i = m_faders.count() - 1; i >= 0; i--)
151✔
221
            {
222
                const QSharedPointer<GenericFader>& f = m_faders.at(i);
129✔
223
                if (!f.isNull() && f->priority() <= fader->priority())
129✔
224
                {
225
                    insertPos = i + 1;
129✔
226
                    break;
129✔
227
                }
228
            }
229
        }
230

231
        m_faders.insert(insertPos, fader);
151✔
232

233
        qDebug() << "[Universe]" << id() << ": Generic fader with priority" << fader->priority()
302✔
234
                 << "registered at pos" << insertPos << ", count" << m_faders.count();
151✔
235
    }
151✔
236
    return fader;
151✔
237
}
×
238

239
void Universe::dismissFader(QSharedPointer<GenericFader> fader)
3✔
240
{
241
    QMutexLocker fadersLocker(&m_fadersMutex);
3✔
242
    int index = m_faders.indexOf(fader);
3✔
243
    if (index >= 0)
3✔
244
    {
245
        m_faders.takeAt(index);
3✔
246
        fader.clear();
3✔
247
    }
248
}
3✔
249

250
void Universe::requestFaderPriority(QSharedPointer<GenericFader> fader, Universe::FaderPriority priority)
×
251
{
252
    QMutexLocker fadersLocker(&m_fadersMutex);
×
253
    int pos = m_faders.indexOf(fader);
×
254
    int newPos = 0;
×
255

256
    if (pos == -1)
×
257
        return;
×
258

259
    for (int i = m_faders.count() - 1; i >= 0; i--)
×
260
    {
261
        QSharedPointer<GenericFader> f = m_faders.at(i);
×
262
        if (!f.isNull() && f->priority() <= priority)
×
263
        {
264
            newPos = i;
×
265
            fader->setPriority(priority);
×
266
            break;
×
267
        }
268
    }
×
269

270
    if (newPos != pos)
×
271
    {
272
        m_faders.move(pos, newPos);
×
273
        qDebug() << "[Universe]" << id() << ": Generic fader moved from" << pos
×
274
                 << "to" << m_faders.indexOf(fader) << ". Count:" << m_faders.count();
×
275
    }
276
}
×
277

278
QList<QSharedPointer<GenericFader> > Universe::faders()
×
279
{
280
    return m_faders;
×
281
}
282

283
void Universe::setFaderPause(quint32 functionID, bool enable)
8✔
284
{
285
    QMutexLocker fadersLocker(&m_fadersMutex);
8✔
286
    QMutableListIterator<QSharedPointer<GenericFader> > it(m_faders);
8✔
287
    while (it.hasNext())
17✔
288
    {
289
        QSharedPointer<GenericFader> fader = it.next();
9✔
290
        if (fader.isNull() || fader->parentFunctionID() != functionID)
9✔
291
            continue;
6✔
292

293
        fader->setPaused(enable);
3✔
294
    }
9✔
295
}
8✔
296

297
void Universe::setFaderFadeOut(int fadeTime)
×
298
{
299
    QMutexLocker fadersLocker(&m_fadersMutex);
×
300
    foreach (QSharedPointer<GenericFader> fader, m_faders)
×
301
    {
302
        if (!fader.isNull() && fader->parentFunctionID() != Function::invalidId())
×
303
            fader->setFadeOut(true, uint(fadeTime));
×
304
    }
×
305
}
×
306

307
void Universe::tick()
692✔
308
{
309
    m_semaphore.release(1);
692✔
310
}
692✔
311

312
void Universe::processFaders()
264,856✔
313
{
314
    flushInput();
264,856✔
315
    zeroIntensityChannels();
264,856✔
316

317
    {
318
        QMutexLocker fadersLocker(&m_fadersMutex);
264,856✔
319
        QMutableListIterator<QSharedPointer<GenericFader> > it(m_faders);
264,856✔
320
        while (it.hasNext())
1,191,957✔
321
        {
322
            QSharedPointer<GenericFader> fader = it.next(); //m_faders.at(i);
927,101✔
323
            if (fader.isNull())
927,101✔
324
                continue;
×
325

326
            // destroy a fader if it's been requested
327
            // and it's not fading out
328
            if (fader->deleteRequested() && !fader->isFadingOut())
927,101✔
329
            {
330
                fader->removeAll();
4✔
331
                it.remove();
4✔
332
                fader.clear();
4✔
333
                continue;
4✔
334
            }
335

336
            if (fader->isEnabled() == false)
927,097✔
337
                continue;
×
338

339
            //qDebug() << "Processing fader" << fader->name() << fader->channelsCount();
340
            fader->write(this);
927,097✔
341
        }
927,101✔
342
    }
264,856✔
343

344
    bool dataChanged = hasChanged();
264,856✔
345
    const QByteArray postGM = m_postGMValues->mid(0, m_usedChannels);
264,856✔
346
    dumpOutput(postGM, dataChanged);
264,856✔
347

348
    if (dataChanged)
264,856✔
349
        emit universeWritten(id(), postGM);
132,941✔
350
}
264,856✔
351

352
void Universe::run()
×
353
{
354
    m_running = true;
×
355
    int timeout = int(MasterTimer::tick()) * 2;
×
356

357
    qDebug() << "Universe thread started" << id();
×
358

359
    while (m_running)
×
360
    {
361
        if (m_semaphore.tryAcquire(1, timeout) == false)
×
362
        {
363
            //qWarning() << "Semaphore not acquired on universe" << id();
364
            continue;
×
365
        }
366
#if 0
367
        if (m_faders.count())
368
            qDebug() << "<<<<<<<< UNIVERSE TICK - id" << id() << "faders:" << m_faders.count();
369
#endif
370
        processFaders();
×
371
    }
372

373
    qDebug() << "Universe thread stopped" << id();
×
374
}
×
375

376
/************************************************************************
377
 * Values
378
 ************************************************************************/
379

380
void Universe::reset()
9✔
381
{
382
    m_preGMValues->fill(0);
9✔
383
    m_blackoutValues->fill(0);
9✔
384

385
    if (m_passthrough)
9✔
386
        (*m_postGMValues) = (*m_passthroughValues);
×
387
    else
388
        m_postGMValues->fill(0);
9✔
389

390
    m_modifiers.fill(NULL, UNIVERSE_SIZE);
9✔
391
    m_passthrough = false; // not releasing m_passthroughValues, see comment in setPassthrough
9✔
392
}
9✔
393

394
void Universe::reset(int address, int range)
1,815,027✔
395
{
396
    if (address >= UNIVERSE_SIZE)
1,815,027✔
397
        return;
×
398

399
    if (address + range > UNIVERSE_SIZE)
1,815,027✔
400
       range = UNIVERSE_SIZE - address;
1✔
401

402
    memset(m_preGMValues->data() + address, 0, range * sizeof(*m_preGMValues->data()));
1,815,027✔
403
    memset(m_blackoutValues->data() + address, 0, range * sizeof(*m_blackoutValues->data()));
1,815,027✔
404
    memcpy(m_postGMValues->data() + address, m_modifiedZeroValues->data() + address, range * sizeof(*m_postGMValues->data()));
1,815,027✔
405

406
    applyPassthroughValues(address, range);
1,815,027✔
407
}
408

409
void Universe::applyPassthroughValues(int address, int range)
1,815,027✔
410
{
411
    if (!m_passthrough)
1,815,027✔
412
        return;
1,815,027✔
413

414
    for (int i = address; i < address + range && i < UNIVERSE_SIZE; i++)
×
415
    {
416
        if (static_cast<uchar>(m_postGMValues->at(i)) < static_cast<uchar>(m_passthroughValues->at(i))) // HTP merge
×
417
        {
418
            (*m_postGMValues)[i] = (*m_passthroughValues)[i];
×
419
        }
420
    }
421
}
422

423
void Universe::zeroIntensityChannels()
274,958✔
424
{
425
    updateIntensityChannelsRanges();
274,958✔
426
    int const* channels = m_intensityChannelsRanges.constData();
274,958✔
427
    for (int i = 0; i < m_intensityChannelsRanges.size(); ++i)
2,089,984✔
428
    {
429
        short channel = channels[i] >> 16;
1,815,026✔
430
        short size = channels[i] & 0xffff;
1,815,026✔
431

432
        reset(channel, size);
1,815,026✔
433
    }
434
}
274,958✔
435

436
QHash<int, uchar> Universe::intensityChannels()
1✔
437
{
438
    QHash <int, uchar> intensityList;
1✔
439
    for (int i = 0; i < m_intensityChannels.size(); ++i)
1✔
440
    {
441
        int channel = m_intensityChannels.at(i);
×
442
        intensityList[channel] = m_preGMValues->at(channel);
×
443
    }
444
    return intensityList;
1✔
445
}
×
446

447
uchar Universe::postGMValue(int address) const
×
448
{
449
    if (address >= m_postGMValues->size())
×
450
        return 0;
×
451

452
    return uchar(m_postGMValues->at(address));
×
453
}
454

455
const QByteArray* Universe::postGMValues() const
4,633✔
456
{
457
    return m_postGMValues.data();
4,633✔
458
}
459

460
Universe::BlendMode Universe::stringToBlendMode(QString mode)
5✔
461
{
462
    if (mode == KXMLUniverseNormalBlend)
5✔
463
        return NormalBlend;
1✔
464
    else if (mode == KXMLUniverseMaskBlend)
4✔
465
        return MaskBlend;
1✔
466
    else if (mode == KXMLUniverseAdditiveBlend)
3✔
467
        return AdditiveBlend;
1✔
468
    else if (mode == KXMLUniverseSubtractiveBlend)
2✔
469
        return SubtractiveBlend;
1✔
470

471
    return NormalBlend;
1✔
472
}
473

474
QString Universe::blendModeToString(Universe::BlendMode mode)
7✔
475
{
476
    switch(mode)
7✔
477
    {
478
        default:
1✔
479
        case NormalBlend:
480
            return QString(KXMLUniverseNormalBlend);
1✔
481
        break;
482
        case MaskBlend:
1✔
483
            return QString(KXMLUniverseMaskBlend);
1✔
484
        break;
485
        case AdditiveBlend:
4✔
486
            return QString(KXMLUniverseAdditiveBlend);
4✔
487
        break;
488
        case SubtractiveBlend:
1✔
489
            return QString(KXMLUniverseSubtractiveBlend);
1✔
490
        break;
491
    }
492
}
493

494
const QByteArray Universe::preGMValues() const
1,326,130✔
495
{
496
    return *m_preGMValues;
1,326,130✔
497
}
498

499
uchar Universe::preGMValue(int address) const
9,418,175✔
500
{
501
    if (address >= m_preGMValues->size())
9,418,175✔
502
        return 0U;
×
503

504
    return static_cast<uchar>(m_preGMValues->at(address));
9,418,175✔
505
}
506

507
uchar Universe::applyGM(int channel, uchar value)
9,411,149✔
508
{
509
    if ((m_grandMaster->channelMode() == GrandMaster::Intensity && m_channelsMask->at(channel) & Intensity) ||
13,697,652✔
510
        (m_grandMaster->channelMode() == GrandMaster::AllChannels))
4,286,503✔
511
    {
512
        if (m_grandMaster->valueMode() == GrandMaster::Limit)
5,125,700✔
513
            value = MIN(value, m_grandMaster->value());
792✔
514
        else
515
            value = char(floor((double(value) * m_grandMaster->fraction()) + 0.5));
5,124,908✔
516
    }
517

518
    return value;
9,411,149✔
519
}
520

521
uchar Universe::applyModifiers(int channel, uchar value)
9,417,571✔
522
{
523
    if (m_modifiers.at(channel) != NULL)
9,417,571✔
524
        return m_modifiers.at(channel)->getValue(value);
×
525

526
    return value;
9,417,571✔
527
}
528

529
uchar Universe::applyPassthrough(int channel, uchar value)
9,417,571✔
530
{
531
    if (m_passthrough)
9,417,571✔
532
    {
533
        const uchar passthroughValue = static_cast<uchar>(m_passthroughValues->at(channel));
×
534
        if (value < passthroughValue) // HTP merge
×
535
        {
536
            return passthroughValue;
×
537
        }
538
    }
539

540
    return value;
9,417,571✔
541
}
542

543
void Universe::updatePostGMValue(int channel)
9,417,571✔
544
{
545
    uchar value = preGMValue(channel);
9,417,571✔
546

547
    if (value != 0)
9,417,571✔
548
        value = applyGM(channel, value);
9,408,589✔
549

550
    value = applyModifiers(channel, value);
9,417,571✔
551
    value = applyPassthrough(channel, value);
9,417,571✔
552

553
    (*m_postGMValues)[channel] = static_cast<char>(value);
9,417,571✔
554
}
9,417,571✔
555

556
/************************************************************************
557
 * Patches
558
 ************************************************************************/
559

560
bool Universe::isPatched()
2✔
561
{
562
    if (m_inputPatch != NULL || m_outputPatchList.count() || m_fbPatch != NULL)
2✔
563
        return true;
1✔
564

565
    return false;
1✔
566
}
567

568
bool Universe::setInputPatch(QLCIOPlugin *plugin,
6✔
569
                             quint32 input, QLCInputProfile *profile)
570
{
571
    qDebug() << "[Universe] setInputPatch - ID:" << m_id << ", plugin:" << ((plugin == NULL)?"None":plugin->name())
12✔
572
             << ", input:" << input << ", profile:" << ((profile == NULL)?"None":profile->name());
6✔
573
    if (m_inputPatch == NULL)
6✔
574
    {
575
        if (plugin == NULL || input == QLCIOPlugin::invalidLine())
5✔
576
            return true;
1✔
577

578
        m_inputPatch = new InputPatch(m_id, this);
4✔
579
        connectInputPatch();
4✔
580
    }
581
    else
582
    {
583
        if (input == QLCIOPlugin::invalidLine())
1✔
584
        {
585
            disconnectInputPatch();
×
586
            delete m_inputPatch;
×
587
            m_inputPatch = NULL;
×
588
            emit inputPatchChanged();
×
589
            return true;
×
590
        }
591
    }
592

593
    if (m_inputPatch != NULL)
5✔
594
    {
595
        bool result = m_inputPatch->set(plugin, input, profile);
5✔
596
        emit inputPatchChanged();
5✔
597
        return result;
5✔
598
    }
599

600
    return true;
×
601
}
602

603
bool Universe::setOutputPatch(QLCIOPlugin *plugin, quint32 output, int index)
19✔
604
{
605
    if (index < 0)
19✔
606
        return false;
×
607

608
    qDebug() << "[Universe] setOutputPatch - ID:" << m_id
19✔
609
             << ", plugin:" << ((plugin == NULL) ? "None" : plugin->name()) << ", output:" << output;
19✔
610

611
    // replace or delete an existing patch
612
    if (index < m_outputPatchList.count())
19✔
613
    {
614
        if (plugin == NULL || output == QLCIOPlugin::invalidLine())
2✔
615
        {
616
            // need to delete an existing patch
617
            OutputPatch *patch = m_outputPatchList.takeAt(index);
2✔
618
            delete patch;
2✔
619
            emit outputPatchesCountChanged();
2✔
620
            return true;
2✔
621
        }
622

623
        OutputPatch *patch = m_outputPatchList.at(index);
×
624
        bool result = patch->set(plugin, output);
×
625
        emit outputPatchChanged();
×
626
        return result;
×
627
    }
628
    else
629
    {
630
        if (plugin == NULL || output == QLCIOPlugin::invalidLine())
17✔
631
            return false;
1✔
632

633
        // add a new patch
634
        OutputPatch *patch = new OutputPatch(m_id, this);
16✔
635
        bool result = patch->set(plugin, output);
16✔
636
        m_outputPatchList.append(patch);
16✔
637
        emit outputPatchesCountChanged();
16✔
638
        return result;
16✔
639
    }
640

641
    return false;
642
}
643

644
bool Universe::setFeedbackPatch(QLCIOPlugin *plugin, quint32 output)
×
645
{
646
    qDebug() << Q_FUNC_INFO << "plugin:" << plugin << "output:" << output;
×
647
    if (m_fbPatch == NULL)
×
648
    {
649
        if (plugin == NULL || output == QLCIOPlugin::invalidLine())
×
650
            return false;
×
651

652
        m_fbPatch = new OutputPatch(m_id, this);
×
653
    }
654
    else
655
    {
656
        if (plugin == NULL || output == QLCIOPlugin::invalidLine())
×
657
        {
658
            delete m_fbPatch;
×
659
            m_fbPatch = NULL;
×
660
            emit hasFeedbackChanged();
×
661
            return true;
×
662
        }
663
    }
664
    if (m_fbPatch != NULL)
×
665
    {
666
        bool result = m_fbPatch->set(plugin, output);
×
667
        emit hasFeedbackChanged();
×
668
        return result;
×
669
    }
670

671
    return false;
×
672
}
673

674
bool Universe::hasFeedback() const
×
675
{
676
    return m_fbPatch != NULL ? true : false;
×
677
}
678

679
InputPatch *Universe::inputPatch() const
161✔
680
{
681
    return m_inputPatch;
161✔
682
}
683

684
OutputPatch *Universe::outputPatch(int index) const
48✔
685
{
686
    if (index < 0 || index >= m_outputPatchList.count())
48✔
687
        return NULL;
14✔
688

689
    return m_outputPatchList.at(index);
34✔
690
}
691

692
int Universe::outputPatchesCount() const
83✔
693
{
694
    return m_outputPatchList.count();
83✔
695
}
696

697
OutputPatch *Universe::feedbackPatch() const
49✔
698
{
699
    return m_fbPatch;
49✔
700
}
701

702
void Universe::dumpOutput(const QByteArray &data, bool dataChanged)
264,904✔
703
{
704
    if (m_outputPatchList.count() == 0)
264,904✔
705
        return;
264,864✔
706

707
    foreach (OutputPatch *op, m_outputPatchList)
80✔
708
    {
709
        if (m_totalChannelsChanged == true)
40✔
710
            op->setPluginParameter(PLUGIN_UNIVERSECHANNELS, m_totalChannels);
4✔
711

712
        if (op->blackout())
40✔
713
            op->dump(m_id, *m_blackoutValues, dataChanged);
20✔
714
        else
715
            op->dump(m_id, data, dataChanged);
20✔
716
    }
40✔
717
    m_totalChannelsChanged = false;
40✔
718
}
719

720
void Universe::flushInput()
264,876✔
721
{
722
    if (m_inputPatch == NULL)
264,876✔
723
        return;
264,871✔
724

725
    m_inputPatch->flush(m_id);
5✔
726
}
727

728
void Universe::slotInputValueChanged(quint32 universe, quint32 channel, uchar value, const QString &key)
×
729
{
730
    if (m_passthrough)
×
731
    {
732
        if (universe == m_id)
×
733
        {
734
            qDebug() << "write" << channel << value;
×
735

736
            if (channel >= UNIVERSE_SIZE)
×
737
                return;
×
738

739
            if (channel >= m_usedChannels)
×
740
                m_usedChannels = channel + 1;
×
741

742
            (*m_passthroughValues)[channel] = value;
×
743

744
            updatePostGMValue(channel);
×
745
        }
746
    }
747
    else
748
        emit inputValueChanged(universe, channel, value, key);
×
749
}
750

751
void Universe::connectInputPatch()
8✔
752
{
753
    if (m_inputPatch == NULL)
8✔
754
        return;
4✔
755

756
    if (!m_passthrough)
4✔
757
        connect(m_inputPatch, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)),
4✔
758
                this, SIGNAL(inputValueChanged(quint32,quint32,uchar,QString)));
759
    else
760
        connect(m_inputPatch, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)),
×
761
                this, SLOT(slotInputValueChanged(quint32,quint32,uchar,const QString&)));
762
}
763

764
void Universe::disconnectInputPatch()
4✔
765
{
766
    if (m_inputPatch == NULL)
4✔
767
        return;
4✔
768

769
    if (!m_passthrough)
×
770
        disconnect(m_inputPatch, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)),
×
771
                this, SIGNAL(inputValueChanged(quint32,quint32,uchar,QString)));
772
    else
773
        disconnect(m_inputPatch, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)),
×
774
                this, SLOT(slotInputValueChanged(quint32,quint32,uchar,const QString&)));
775
}
776

777
/************************************************************************
778
 * Channels capabilities
779
 ************************************************************************/
780

781
void Universe::setChannelCapability(ushort channel, QLCChannel::Group group, ChannelType forcedType)
7,241✔
782
{
783
    if (channel >= (ushort)m_channelsMask->length())
7,241✔
784
        return;
×
785

786
    if (Utils::vectorRemove(m_intensityChannels, channel))
7,241✔
787
        m_intensityChannelsChanged = true;
830✔
788
    Utils::vectorRemove(m_nonIntensityChannels, channel);
7,241✔
789

790
    if (forcedType != Undefined)
7,241✔
791
    {
792
        (*m_channelsMask)[channel] = char(forcedType);
5✔
793
        if ((forcedType & HTP) == HTP)
5✔
794
        {
795
            //qDebug() << "--- Channel" << channel << "forced type HTP";
796
            Utils::vectorSortedAddUnique(m_intensityChannels, channel);
3✔
797
            m_intensityChannelsChanged = true;
3✔
798
            if (group == QLCChannel::Intensity)
3✔
799
            {
800
                //qDebug() << "--- Channel" << channel << "Intensity + HTP";
801
                (*m_channelsMask)[channel] = char(HTP | Intensity);
×
802
            }
803
        }
804
        else if ((forcedType & LTP) == LTP)
2✔
805
        {
806
            //qDebug() << "--- Channel" << channel << "forced type LTP";
807
            Utils::vectorSortedAddUnique(m_nonIntensityChannels, channel);
2✔
808
        }
809
    }
810
    else
811
    {
812
        if (group == QLCChannel::Intensity)
7,236✔
813
        {
814
            //qDebug() << "--- Channel" << channel << "Intensity + HTP";
815
            (*m_channelsMask)[channel] = char(HTP | Intensity);
5,676✔
816
            Utils::vectorSortedAddUnique(m_intensityChannels, channel);
5,676✔
817
            m_intensityChannelsChanged = true;
5,676✔
818
        }
819
        else
820
        {
821
            //qDebug() << "--- Channel" << channel << "LTP";
822
            (*m_channelsMask)[channel] = char(LTP);
1,560✔
823
            Utils::vectorSortedAddUnique(m_nonIntensityChannels, channel);
1,560✔
824
        }
825
    }
826

827
    // qDebug() << Q_FUNC_INFO << "Channel:" << channel << "mask:" << QString::number(m_channelsMask->at(channel), 16);
828
    if (channel >= m_totalChannels)
7,241✔
829
    {
830
        m_totalChannels = channel + 1;
5,648✔
831
        m_totalChannelsChanged = true;
5,648✔
832
    }
833
}
834

835
uchar Universe::channelCapabilities(ushort channel)
517✔
836
{
837
    if (channel >= (ushort)m_channelsMask->length())
517✔
838
        return Undefined;
×
839

840
    return m_channelsMask->at(channel);
517✔
841
}
842

843
void Universe::setChannelDefaultValue(ushort channel, uchar value)
2,592✔
844
{
845
    if (channel >= m_totalChannels)
2,592✔
846
    {
847
        m_totalChannels = channel + 1;
×
848
        m_totalChannelsChanged = true;
×
849
    }
850

851
    if (channel >= m_usedChannels)
2,592✔
852
        m_usedChannels = channel + 1;
999✔
853

854
    (*m_preGMValues)[channel] = value;
2,592✔
855
    updatePostGMValue(channel);
2,592✔
856
}
2,592✔
857

858
void Universe::setChannelModifier(ushort channel, ChannelModifier *modifier)
2,592✔
859
{
860
    if (channel >= (ushort)m_modifiers.count())
2,592✔
861
        return;
×
862

863
    m_modifiers[channel] = modifier;
2,592✔
864

865
    if (modifier != NULL)
2,592✔
866
    {
867
        (*m_modifiedZeroValues)[channel] = modifier->getValue(0);
×
868

869
        if (channel >= m_totalChannels)
×
870
        {
871
            m_totalChannels = channel + 1;
×
872
            m_totalChannelsChanged = true;
×
873
        }
874

875
        if (channel >= m_usedChannels)
×
876
            m_usedChannels = channel + 1;
×
877
    }
878

879
    updatePostGMValue(channel);
2,592✔
880
}
881

882
ChannelModifier *Universe::channelModifier(ushort channel)
×
883
{
884
    if (channel >= (ushort)m_modifiers.count())
×
885
        return NULL;
×
886

887
    return m_modifiers.at(channel);
×
888
}
889

890
void Universe::updateIntensityChannelsRanges()
274,958✔
891
{
892
    if (!m_intensityChannelsChanged)
274,958✔
893
        return;
274,920✔
894

895
    m_intensityChannelsChanged = false;
38✔
896

897
    m_intensityChannelsRanges.clear();
38✔
898
    short currentPos = -1;
38✔
899
    short currentSize = 0;
38✔
900

901
    for (int i = 0; i < m_intensityChannels.size(); ++i)
1,799✔
902
    {
903
        int channel = m_intensityChannels.at(i);
1,761✔
904
        if (currentPos + currentSize == channel)
1,761✔
905
            ++currentSize;
1,179✔
906
        else
907
        {
908
            if (currentPos != -1)
582✔
909
                m_intensityChannelsRanges.append((currentPos << 16) | currentSize);
544✔
910
            currentPos = channel;
582✔
911
            currentSize = 1;
582✔
912
        }
913
    }
914
    if (currentPos != -1)
38✔
915
        m_intensityChannelsRanges.append((currentPos << 16) | currentSize);
38✔
916

917
    qDebug() << Q_FUNC_INFO << ":" << m_intensityChannelsRanges.size() << "ranges";
38✔
918
}
919

920
/****************************************************************************
921
 * Writing
922
 ****************************************************************************/
923

924
bool Universe::write(int address, uchar value, bool forceLTP)
5,129,411✔
925
{
926
    Q_ASSERT(address < UNIVERSE_SIZE);
5,129,411✔
927

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

930
    if (address >= m_usedChannels)
5,129,411✔
931
        m_usedChannels = address + 1;
9,372✔
932

933
    if (m_channelsMask->at(address) & HTP)
5,129,411✔
934
    {
935
        if (forceLTP == false && value < (uchar)m_preGMValues->at(address))
2,563,233✔
936
        {
937
            qDebug() << "[Universe] HTP check not passed" << address << value;
×
938
            return false;
×
939
        }
940
    }
941
    else
942
    {
943
        // preserve non HTP channels for blackout
944
        (*m_blackoutValues)[address] = char(value);
2,566,178✔
945
    }
946

947
    (*m_preGMValues)[address] = char(value);
5,129,411✔
948

949
    updatePostGMValue(address);
5,129,411✔
950

951
    return true;
5,129,411✔
952
}
953

954
bool Universe::writeMultiple(int address, quint32 value, int channelCount)
1,722,398✔
955
{
956
    for (int i = 0; i < channelCount; i++)
3,444,796✔
957
    {
958
        //qDebug() << "[Universe]" << id() << ": write channel" << (address + i) << ", value:" << QString::number(((uchar *)&value)[channelCount - 1 - i]);
959

960
        // preserve non HTP channels for blackout
961
        if ((m_channelsMask->at(address + i) & HTP) == 0)
1,722,398✔
962
            (*m_blackoutValues)[address + i] = ((uchar *)&value)[channelCount - 1 - i];
1,722,275✔
963

964
        (*m_preGMValues)[address + i] = ((uchar *)&value)[channelCount - 1 - i];
1,722,398✔
965

966
        updatePostGMValue(address + i);
1,722,398✔
967
    }
968

969
    return true;
1,722,398✔
970
}
971

972
bool Universe::writeRelative(int address, quint32 value, int channelCount)
9✔
973
{
974
    Q_ASSERT(address < UNIVERSE_SIZE);
9✔
975

976
    //qDebug() << "Write relative channel" << address << "value" << value;
977

978
    if (address + channelCount >= m_usedChannels)
9✔
979
        m_usedChannels = address + channelCount;
9✔
980

981
    if (channelCount == 1)
9✔
982
    {
983
        short newVal = uchar((*m_preGMValues)[address]);
6✔
984
        newVal += short(value) - RELATIVE_ZERO_8BIT;
6✔
985
        (*m_preGMValues)[address] = char(CLAMP(newVal, 0, UCHAR_MAX));
6✔
986
        (*m_blackoutValues)[address] = char(CLAMP(newVal, 0, UCHAR_MAX));
6✔
987
        updatePostGMValue(address);
6✔
988
    }
989
    else
990
    {
991
        quint32 currentValue = 0;
3✔
992
        for (int i = 0; i < channelCount; i++)
9✔
993
            currentValue = (currentValue << 8) + uchar(m_preGMValues->at(address + i));
6✔
994

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

997
        for (int i = 0; i < channelCount; i++)
9✔
998
        {
999
            (*m_preGMValues)[address + i] = ((uchar *)&currentValue)[channelCount - 1 - i];
6✔
1000
            (*m_blackoutValues)[address + i] = ((uchar *)&currentValue)[channelCount - 1 - i];
6✔
1001
            updatePostGMValue(address + i);
6✔
1002
        }
1003
    }
1004

1005
    return true;
9✔
1006
}
1007

1008
bool Universe::writeBlended(int address, quint32 value, int channelCount, Universe::BlendMode blend)
1,722,399✔
1009
{
1010
    if (address + channelCount >= m_usedChannels)
1,722,399✔
1011
        m_usedChannels = address + channelCount;
116✔
1012

1013
    quint32 currentValue = 0;
1,722,399✔
1014
    for (int i = 0; i < channelCount; i++)
3,444,798✔
1015
        currentValue = (currentValue << 8) + uchar(m_preGMValues->at(address + i));
1,722,399✔
1016

1017
    switch (blend)
1,722,399✔
1018
    {
1019
        case NormalBlend:
1,722,393✔
1020
        {
1021
            if ((m_channelsMask->at(address) & HTP) && value < currentValue)
1,722,393✔
1022
            {
1023
                qDebug() << "[Universe] HTP check not passed" << address << value;
×
1024
                return false;
×
1025
            }
1026
        }
1027
        break;
1,722,393✔
1028
        case MaskBlend:
2✔
1029
        {
1030
            if (value)
2✔
1031
            {
1032
                qDebug() << "Current value" << currentValue << "value" << value;
2✔
1033
                if (currentValue)
2✔
1034
                    value = float(currentValue) * (float(value) / pow(255.0, channelCount));
1✔
1035
                else
1036
                    value = 0;
1✔
1037
            }
1038
        }
1039
        break;
2✔
1040
        case AdditiveBlend:
1✔
1041
        {
1042
            //qDebug() << "Universe write additive channel" << channel << ", value:" << currVal << "+" << value;
1043
            value = fmin(float(currentValue + value), pow(255.0, channelCount));
1✔
1044
        }
1045
        break;
1✔
1046
        case SubtractiveBlend:
2✔
1047
        {
1048
            if (value >= currentValue)
2✔
1049
                value = 0;
1✔
1050
            else
1051
                value = currentValue - value;
1✔
1052
        }
1053
        break;
2✔
1054
        default:
1✔
1055
            qDebug() << "[Universe] Blend mode not handled. Implement me!" << blend;
1✔
1056
            return false;
1✔
1057
        break;
1058
    }
1059

1060
    writeMultiple(address, value, channelCount);
1,722,398✔
1061

1062
    return true;
1,722,398✔
1063
}
1064

1065
/*********************************************************************
1066
 * Load & Save
1067
 *********************************************************************/
1068

1069
bool Universe::loadXML(QXmlStreamReader &root, int index, InputOutputMap *ioMap)
5✔
1070
{
1071
    if (root.name() != KXMLQLCUniverse)
5✔
1072
    {
1073
        qWarning() << Q_FUNC_INFO << "Universe node not found";
1✔
1074
        return false;
1✔
1075
    }
1076

1077
    int outputIndex = 0;
4✔
1078

1079
    QXmlStreamAttributes attrs = root.attributes();
4✔
1080

1081
    if (attrs.hasAttribute(KXMLQLCUniverseName))
4✔
1082
        setName(attrs.value(KXMLQLCUniverseName).toString());
4✔
1083

1084
    if (attrs.hasAttribute(KXMLQLCUniversePassthrough))
4✔
1085
    {
1086
        if (attrs.value(KXMLQLCUniversePassthrough).toString() == KXMLQLCTrue ||
14✔
1087
            attrs.value(KXMLQLCUniversePassthrough).toString() == "1")
7✔
1088
            setPassthrough(true);
2✔
1089
        else
1090
            setPassthrough(false);
1✔
1091
    }
1092
    else
1093
    {
1094
        setPassthrough(false);
1✔
1095
    }
1096

1097
    while (root.readNextStartElement())
4✔
1098
    {
1099
        QXmlStreamAttributes pAttrs = root.attributes();
×
1100

1101
        if (root.name() == KXMLQLCUniverseInputPatch)
×
1102
        {
1103
            QString plugin = KInputNone;
×
1104
            quint32 inputLine = QLCIOPlugin::invalidLine();
×
1105
            QString inputUID;
×
1106
            QString profile = KInputNone;
×
1107

1108
            if (pAttrs.hasAttribute(KXMLQLCUniversePlugin))
×
1109
                plugin = pAttrs.value(KXMLQLCUniversePlugin).toString();
×
1110
            if (pAttrs.hasAttribute(KXMLQLCUniverseLineUID))
×
1111
                inputUID = pAttrs.value(KXMLQLCUniverseLineUID).toString();
×
1112
            if (pAttrs.hasAttribute(KXMLQLCUniverseLine))
×
1113
                inputLine = pAttrs.value(KXMLQLCUniverseLine).toString().toUInt();
×
1114
            if (pAttrs.hasAttribute(KXMLQLCUniverseProfileName))
×
1115
                profile = pAttrs.value(KXMLQLCUniverseProfileName).toString();
×
1116

1117
            // apply the parameters just loaded
1118
            ioMap->setInputPatch(index, plugin, inputUID, inputLine, profile);
×
1119

1120
            QXmlStreamReader::TokenType tType = root.readNext();
×
1121
            if (tType == QXmlStreamReader::Characters)
×
1122
                tType = root.readNext();
×
1123

1124
            // check if there is a PluginParameters tag defined
1125
            if (tType == QXmlStreamReader::StartElement)
×
1126
            {
1127
                if (root.name() == KXMLQLCUniversePluginParameters)
×
1128
                    loadXMLPluginParameters(root, InputPatchTag, 0);
×
1129
                root.skipCurrentElement();
×
1130
            }
1131
        }
×
1132
        else if (root.name() == KXMLQLCUniverseOutputPatch)
×
1133
        {
1134
            QString plugin = KOutputNone;
×
1135
            QString outputUID;
×
1136
            quint32 outputLine = QLCIOPlugin::invalidLine();
×
1137

1138
            if (pAttrs.hasAttribute(KXMLQLCUniversePlugin))
×
1139
                plugin = pAttrs.value(KXMLQLCUniversePlugin).toString();
×
1140
            if (pAttrs.hasAttribute(KXMLQLCUniverseLineUID))
×
1141
                outputUID = pAttrs.value(KXMLQLCUniverseLineUID).toString();
×
1142
            if (pAttrs.hasAttribute(KXMLQLCUniverseLine))
×
1143
                outputLine = pAttrs.value(KXMLQLCUniverseLine).toString().toUInt();
×
1144

1145
            // apply the parameters just loaded
1146
            ioMap->setOutputPatch(index, plugin, outputUID, outputLine, false, outputIndex);
×
1147

1148
            QXmlStreamReader::TokenType tType = root.readNext();
×
1149
            if (tType == QXmlStreamReader::Characters)
×
1150
                tType = root.readNext();
×
1151

1152
            // check if there is a PluginParameters tag defined
1153
            if (tType == QXmlStreamReader::StartElement)
×
1154
            {
1155
                if (root.name() == KXMLQLCUniversePluginParameters)
×
1156
                    loadXMLPluginParameters(root, OutputPatchTag, outputIndex);
×
1157
                root.skipCurrentElement();
×
1158
            }
1159

1160
            outputIndex++;
×
1161
        }
×
1162
        else if (root.name() == KXMLQLCUniverseFeedbackPatch)
×
1163
        {
1164
            QString plugin = KOutputNone;
×
1165
            QString outputUID;
×
1166
            quint32 output = QLCIOPlugin::invalidLine();
×
1167

1168
            if (pAttrs.hasAttribute(KXMLQLCUniversePlugin))
×
1169
                plugin = pAttrs.value(KXMLQLCUniversePlugin).toString();
×
1170
            if (pAttrs.hasAttribute(KXMLQLCUniverseLineUID))
×
1171
                outputUID = pAttrs.value(KXMLQLCUniverseLineUID).toString();
×
1172
            if (pAttrs.hasAttribute(KXMLQLCUniverseLine))
×
1173
                output = pAttrs.value(KXMLQLCUniverseLine).toString().toUInt();
×
1174

1175
            // apply the parameters just loaded
1176
            ioMap->setOutputPatch(index, plugin, outputUID, output, true);
×
1177

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

1182
            // check if there is a PluginParameters tag defined
1183
            if (tType == QXmlStreamReader::StartElement)
×
1184
            {
1185
                if (root.name() == KXMLQLCUniversePluginParameters)
×
1186
                    loadXMLPluginParameters(root, FeedbackPatchTag, 0);
×
1187
                root.skipCurrentElement();
×
1188
            }
1189
        }
×
1190
        else
1191
        {
1192
            qWarning() << Q_FUNC_INFO << "Unknown Universe tag:" << root.name();
×
1193
            root.skipCurrentElement();
×
1194
        }
1195
    }
×
1196

1197
    return true;
4✔
1198
}
4✔
1199

1200
bool Universe::loadXMLPluginParameters(QXmlStreamReader &root, PatchTagType currentTag, int patchIndex)
×
1201
{
1202
    if (root.name() != KXMLQLCUniversePluginParameters)
×
1203
    {
1204
        qWarning() << Q_FUNC_INFO << "PluginParameters node not found";
×
1205
        return false;
×
1206
    }
1207

1208
    QXmlStreamAttributes pluginAttrs = root.attributes();
×
1209
    for (int i = 0; i < pluginAttrs.count(); i++)
×
1210
    {
1211
        QXmlStreamAttribute attr = pluginAttrs.at(i);
×
1212
        if (currentTag == InputPatchTag)
×
1213
        {
1214
            InputPatch *ip = inputPatch();
×
1215
            if (ip != NULL)
×
1216
                ip->setPluginParameter(attr.name().toString(), attr.value().toString());
×
1217
        }
1218
        else if (currentTag == OutputPatchTag)
×
1219
        {
1220
            OutputPatch *op = outputPatch(patchIndex);
×
1221
            if (op != NULL)
×
1222
                op->setPluginParameter(attr.name().toString(), attr.value().toString());
×
1223
        }
1224
        else if (currentTag == FeedbackPatchTag)
×
1225
        {
1226
            OutputPatch *fbp = feedbackPatch();
×
1227
            if (fbp != NULL)
×
1228
                fbp->setPluginParameter(attr.name().toString(), attr.value().toString());
×
1229
        }
1230
    }
×
1231
    root.skipCurrentElement();
×
1232

1233
    return true;
×
1234
}
×
1235

1236
bool Universe::saveXML(QXmlStreamWriter *doc) const
6✔
1237
{
1238
    Q_ASSERT(doc != NULL);
6✔
1239

1240
    doc->writeStartElement(KXMLQLCUniverse);
12✔
1241
    doc->writeAttribute(KXMLQLCUniverseName, name());
12✔
1242
    doc->writeAttribute(KXMLQLCUniverseID, QString::number(id()));
12✔
1243

1244
    if (passthrough() == true)
6✔
1245
        doc->writeAttribute(KXMLQLCUniversePassthrough, KXMLQLCTrue);
3✔
1246

1247
    if (inputPatch() != NULL)
6✔
1248
    {
1249
        savePatchXML(doc, KXMLQLCUniverseInputPatch, inputPatch()->pluginName(), inputPatch()->inputName(),
×
1250
            inputPatch()->input(), inputPatch()->profileName(), inputPatch()->getPluginParameters());
×
1251
    }
1252
    foreach (OutputPatch *op, m_outputPatchList)
6✔
1253
    {
1254
        savePatchXML(doc, KXMLQLCUniverseOutputPatch, op->pluginName(), op->outputName(),
×
1255
            op->output(), "", op->getPluginParameters());
×
1256
    }
6✔
1257
    if (feedbackPatch() != NULL)
6✔
1258
    {
1259
        savePatchXML(doc, KXMLQLCUniverseFeedbackPatch, feedbackPatch()->pluginName(), feedbackPatch()->outputName(),
×
1260
            feedbackPatch()->output(), "", feedbackPatch()->getPluginParameters());
×
1261
    }
1262

1263
    /* End the <Universe> tag */
1264
    doc->writeEndElement();
6✔
1265

1266
    return true;
6✔
1267
}
1268

1269
void Universe::savePatchXML(
×
1270
    QXmlStreamWriter *doc,
1271
    const QString &tag,
1272
    const QString &pluginName,
1273
    const QString &lineName,
1274
    quint32 line,
1275
    QString profileName,
1276
    QMap<QString, QVariant> parameters) const
1277
{
1278
    // sanity check: don't save invalid data
1279
    if (pluginName.isEmpty() || pluginName == KInputNone || line == QLCIOPlugin::invalidLine())
×
1280
        return;
×
1281

1282
    doc->writeStartElement(tag);
×
1283
    doc->writeAttribute(KXMLQLCUniversePlugin, pluginName);
×
1284
    doc->writeAttribute(KXMLQLCUniverseLineUID, lineName);
×
1285
    doc->writeAttribute(KXMLQLCUniverseLine, QString::number(line));
×
1286
    if (!profileName.isEmpty() && profileName != KInputNone)
×
1287
        doc->writeAttribute(KXMLQLCUniverseProfileName, profileName);
×
1288

1289
    savePluginParametersXML(doc, parameters);
×
1290
    doc->writeEndElement();
×
1291
}
1292

1293
bool Universe::savePluginParametersXML(QXmlStreamWriter *doc,
×
1294
                                       QMap<QString, QVariant> parameters) const
1295
{
1296
    Q_ASSERT(doc != NULL);
×
1297

1298
    if (parameters.isEmpty())
×
1299
        return false;
×
1300

1301
    doc->writeStartElement(KXMLQLCUniversePluginParameters);
×
1302
    QMapIterator<QString, QVariant> it(parameters);
×
1303
    while (it.hasNext())
×
1304
    {
1305
        it.next();
×
1306
        QString pName = it.key();
×
1307
        QVariant pValue = it.value();
×
1308
        doc->writeAttribute(pName, pValue.toString());
×
1309
    }
×
1310
    doc->writeEndElement();
×
1311

1312
    return true;
×
1313
}
×
1314

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