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

mcallegari / qlcplus / 21760395819

06 Feb 2026 05:52PM UTC coverage: 33.994% (-0.08%) from 34.078%
21760395819

push

github

mcallegari
Back to 5.2.1 debug

17635 of 51877 relevant lines covered (33.99%)

19809.79 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_name(QString("Universe %1").arg(id + 1))
926✔
51
    , m_grandMaster(gm)
926✔
52
    , m_passthrough(false)
926✔
53
    , m_monitor(false)
926✔
54
    , m_inputPatch(NULL)
926✔
55
    , m_fbPatch(NULL)
926✔
56
    , m_channelsMask(new QByteArray(UNIVERSE_SIZE, char(0)))
926✔
57
    , m_modifiedZeroValues(new QByteArray(UNIVERSE_SIZE, char(0)))
926✔
58
    , m_running(false)
926✔
59
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
60
    , m_fadersMutex(QMutex::Recursive)
61
#endif
62
    , m_usedChannels(0)
926✔
63
    , m_totalChannels(0)
926✔
64
    , m_totalChannelsChanged(false)
926✔
65
    , m_intensityChannelsChanged(false)
926✔
66
    , m_preGMValues(new QByteArray(UNIVERSE_SIZE, char(0)))
926✔
67
    , m_postGMValues(new QByteArray(UNIVERSE_SIZE, char(0)))
926✔
68
    , m_lastPostGMValues(new QByteArray(UNIVERSE_SIZE, char(0)))
926✔
69
    , m_blackoutValues(new QByteArray(UNIVERSE_SIZE, char(0)))
926✔
70
    , m_passthroughValues()
4,630✔
71
{
72
    m_modifiers.fill(NULL, UNIVERSE_SIZE);
926✔
73

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

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

87
        m_running = false;
×
88
        wait(1000);
×
89
    }
90

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

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

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

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

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

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

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

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

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

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

151
    disconnectInputPatch();
4✔
152

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

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

164
    m_passthrough = enable;
4✔
165

166
    connectInputPatch();
4✔
167

168
    emit passthroughChanged();
4✔
169
}
170

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

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

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

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

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

206
/************************************************************************
207
 * Faders
208
 ************************************************************************/
209

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

375
/************************************************************************
376
 * Values
377
 ************************************************************************/
378

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

470
    return NormalBlend;
1✔
471
}
472

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

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

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

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

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

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

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

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

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

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

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

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

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

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

555
/************************************************************************
556
 * Patches
557
 ************************************************************************/
558

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

564
    return false;
1✔
565
}
566

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

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

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

599
    return true;
×
600
}
601

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

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

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

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

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

640
    return false;
641
}
642

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

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

670
    return false;
×
671
}
672

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

776
/************************************************************************
777
 * Channels capabilities
778
 ************************************************************************/
779

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

894
    m_intensityChannelsChanged = false;
38✔
895

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

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

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

919
/****************************************************************************
920
 * Writing
921
 ****************************************************************************/
922

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1004
    return true;
9✔
1005
}
1006

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

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

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

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

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

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

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

1076
    int outputIndex = 0;
4✔
1077

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1196
    return true;
4✔
1197
}
4✔
1198

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

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

1232
    return true;
×
1233
}
×
1234

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

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

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

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

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

1265
    return true;
6✔
1266
}
1267

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

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

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

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

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

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

1311
    return true;
×
1312
}
×
1313

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