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

mcallegari / qlcplus / 15847744647

24 Jun 2025 10:17AM UTC coverage: 31.889% (+0.02%) from 31.874%
15847744647

Pull #1771

github

web-flow
Merge c362b01e4 into c410657e5
Pull Request #1771: GenericFader: protect map to fix crash

15 of 17 new or added lines in 1 file covered. (88.24%)

3 existing lines in 1 file now uncovered.

16451 of 51589 relevant lines covered (31.89%)

19307.07 hits per line

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

83.25
/engine/src/genericfader.cpp
1
/*
2
  Q Light Controller
3
  genericfader.cpp
4

5
  Copyright (c) Heikki Junnila
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 <QDebug>
21

22
#include "genericfader.h"
23
#include "fadechannel.h"
24
#include "doc.h"
25

26
GenericFader::GenericFader(QObject *parent)
151✔
27
    : QObject(parent)
28
    , m_fid(Function::invalidId())
151✔
29
    , m_priority(Universe::Auto)
151✔
30
    , m_handleSecondary(false)
151✔
31
    , m_intensity(1.0)
151✔
32
    , m_parentIntensity(1.0)
151✔
33
    , m_paused(false)
151✔
34
    , m_enabled(true)
151✔
35
    , m_fadeOut(false)
151✔
36
    , m_deleteRequest(false)
151✔
37
    , m_blendMode(Universe::NormalBlend)
151✔
38
    , m_monitoring(false)
302✔
39
{
40
}
151✔
41

42
GenericFader::~GenericFader()
300✔
43
{
44
}
300✔
45

46
QString GenericFader::name() const
×
47
{
48
    return m_name;
×
49
}
50

51
void GenericFader::setName(QString name)
123✔
52
{
53
    m_name = name;
123✔
54
}
123✔
55

56
quint32 GenericFader::parentFunctionID() const
9✔
57
{
58
    return m_fid;
9✔
59
}
60

61
void GenericFader::setParentFunctionID(quint32 fid)
123✔
62
{
63
    m_fid = fid;
123✔
64
}
123✔
65

66
int GenericFader::priority() const
408✔
67
{
68
    return m_priority;
408✔
69
}
70

71
void GenericFader::setPriority(int priority)
150✔
72
{
73
    m_priority = priority;
150✔
74
}
150✔
75

76
bool GenericFader::handleSecondary()
1,589,837✔
77
{
78
    return m_handleSecondary;
1,589,837✔
79
}
80

81
void GenericFader::setHandleSecondary(bool enable)
123✔
82
{
83
    m_handleSecondary = enable;
123✔
84
}
123✔
85

86
quint32 GenericFader::channelHash(quint32 fixtureID, quint32 channel)
795,255✔
87
{
88
    return ((fixtureID & 0x0000FFFF) << 16) | (channel & 0x0000FFFF);
795,255✔
89
}
90

91
void GenericFader::add(const FadeChannel& ch)
17✔
92
{
93
    quint32 hash = channelHash(ch.fixture(), ch.channel());
17✔
94

95
    QWriteLocker l(&m_channelsLock);
17✔
96
    QHash<quint32,FadeChannel>::iterator channelIterator = m_channels.find(hash);
17✔
97
    if (channelIterator != m_channels.end())
17✔
98
    {
99
        // perform a HTP check
100
        if (channelIterator.value().current() <= ch.current())
3✔
101
            channelIterator.value() = ch;
3✔
102
    }
103
    else
104
    {
105
        m_channels.insert(hash, ch);
14✔
106
        qDebug() << "Added new fader with hash" << hash;
14✔
107
    }
108
}
17✔
109

110
void GenericFader::replace(const FadeChannel &ch)
1✔
111
{
112
    quint32 hash = channelHash(ch.fixture(), ch.channel());
1✔
113
    QWriteLocker l(&m_channelsLock);
1✔
114
    m_channels.insert(hash, ch);
1✔
115
}
1✔
116

117
void GenericFader::remove(FadeChannel *ch)
2✔
118
{
119
    if (ch == NULL)
2✔
120
        return;
×
121

122
    quint32 hash = channelHash(ch->fixture(), ch->channel());
2✔
123
    QWriteLocker l(&m_channelsLock);
2✔
124
    if (m_channels.remove(hash) == 0)
2✔
125
        qDebug() << "No FadeChannel found with hash" << hash;
1✔
126
}
2✔
127

128
void GenericFader::removeAll()
5✔
129
{
130
    QWriteLocker l(&m_channelsLock);
5✔
131
    m_channels.clear();
5✔
132
}
5✔
133

134
bool GenericFader::deleteRequested()
927,101✔
135
{
136
    return m_deleteRequest;
927,101✔
137
}
138

139
void GenericFader::requestDelete()
103✔
140
{
141
    m_deleteRequest = true;
103✔
142
}
103✔
143

144
FadeChannel *GenericFader::getChannelFader(const Doc *doc, Universe *universe, quint32 fixtureID, quint32 channel)
795,210✔
145
{
146
    FadeChannel fc(doc, fixtureID, channel);
795,210✔
147
    quint32 primary = fc.primaryChannel();
795,210✔
148
    quint32 hash;
149

150
    // calculate hash depending on primary channel presence
151
    if (handleSecondary() && primary != QLCChannel::invalid())
795,210✔
152
        hash = channelHash(fc.fixture(), primary);
×
153
    else
154
        hash = channelHash(fc.fixture(), fc.channel());
795,210✔
155

156
    m_channelsLock.lockForRead();
795,210✔
157
    // search for existing FadeChannel
158
    QHash<quint32,FadeChannel>::iterator channelIterator = m_channels.find(hash);
795,210✔
159
    if (channelIterator != m_channels.end())
795,210✔
160
    {
161
        FadeChannel *fcFound = &channelIterator.value();
794,625✔
162
        m_channelsLock.unlock();
794,625✔
163

164
        if (handleSecondary() &&
794,625✔
165
            fcFound->channelCount() == 1 &&
794,625✔
166
            primary != QLCChannel::invalid())
×
167
        {
168
            qDebug() << "Adding channel to primary" << channel;
×
169
            fcFound->addChannel(channel);
×
170
            if (universe)
×
171
                fcFound->setCurrent(universe->preGMValue(fcFound->address() + 1), 1);
×
172
        }
173
        return fcFound;
794,625✔
174
    }
175
    m_channelsLock.unlock();
585✔
176

177
    // set current universe value
178
    if (universe)
585✔
179
        fc.setCurrent(universe->preGMValue(fc.address()));
585✔
180

181
    // new channel. Add to GenericFader
182
    QWriteLocker l(&m_channelsLock);
585✔
183
    channelIterator = m_channels.insert(hash, fc);
585✔
184
    //qDebug() << "Added new fader with hash" << hash;
185

186
    return &channelIterator.value();
585✔
187
}
795,210✔
188

189
QHash<quint32, FadeChannel> GenericFader::channels() const
74✔
190
{
191
    QReadLocker l(&m_channelsLock);
74✔
192
    return m_channels;
148✔
193
}
74✔
194

UNCOV
195
int GenericFader::channelsCount() const
×
196
{
NEW
197
    QReadLocker l(&m_channelsLock);
×
UNCOV
198
    return m_channels.count();
×
UNCOV
199
}
×
200

201
void GenericFader::write(Universe *universe)
927,198✔
202
{
203
    if (m_monitoring)
927,198✔
204
        emit preWriteData(universe->id(), universe->preGMValues());
×
205

206
    qreal compIntensity = intensity() * parentIntensity();
927,198✔
207

208
    //qDebug() << "[GenericFader] writing channels: " << this << m_channels.count();
209

210
    // iterate through all the channels handled by this fader
211
    QWriteLocker l(&m_channelsLock);
927,198✔
212
    QMutableHashIterator <quint32,FadeChannel> it(m_channels);
927,198✔
213
    while (it.hasNext() == true)
2,649,599✔
214
    {
215
        FadeChannel& fc(it.next().value());
1,722,401✔
216
        int flags = fc.flags();
1,722,401✔
217
        quint32 address = fc.addressInUniverse();
1,722,401✔
218
        int channelCount = fc.channelCount();
1,722,401✔
219

220
        if (address == QLCChannel::invalid())
1,722,401✔
221
        {
222
            qWarning() << "Invalid channel found";
×
223
            continue;
8✔
224
        }
225

226
        if (flags & FadeChannel::SetTarget)
1,722,401✔
227
        {
228
            fc.removeFlag(FadeChannel::SetTarget);
4✔
229
            fc.addFlag(FadeChannel::AutoRemove);
4✔
230
            for (int i = 0; i < channelCount; i++)
8✔
231
                fc.setTarget(universe->preGMValue(address + i), i);
4✔
232
        }
233

234
        // Calculate the next step
235
        if (m_paused == false)
1,722,401✔
236
            fc.nextStep(MasterTimer::tick());
1,722,401✔
237

238
        quint32 value = fc.current();
1,722,401✔
239

240
        // Apply intensity to channels that can fade
241
        if (fc.canFade())
1,722,401✔
242
        {
243
            if ((flags & FadeChannel::CrossFade) && fc.fadeTime() == 0)
1,722,401✔
244
            {
245
                // morph start <-> target depending on intensities
246
                bool rampUp = fc.target() > fc.start() ? true : false;
20✔
247
                value = rampUp ? fc.target() - fc.start() : fc.start() - fc.target();
20✔
248
                value = qreal(value) * intensity();
20✔
249
                value = qreal(rampUp ? fc.start() + value : fc.start() - value) * parentIntensity();
20✔
250
            }
251
            else if (flags & FadeChannel::Intensity)
1,722,381✔
252
            {
253
                value = fc.current(compIntensity);
126✔
254
            }
255
        }
256

257
        //qDebug() << "[GenericFader] >>> uni:" << universe->id() << ", address:" << address << ", value:" << value << "int:" << compIntensity;
258
        if (flags & FadeChannel::Override)
1,722,401✔
259
        {
260
            universe->write(address, value, true);
×
261
            continue;
×
262
        }
263
        else if (flags & FadeChannel::Relative)
1,722,401✔
264
        {
265
            universe->writeRelative(address, value, channelCount);
×
266
        }
267
        else if (flags & FadeChannel::Flashing)
1,722,401✔
268
        {
269
            for (int i = 0; i < channelCount; i++)
16✔
270
                universe->write(address + i, ((uchar *)&value)[channelCount - 1 - i],
8✔
271
                                flags & FadeChannel::ForceLTP ? true : false);
8✔
272
            continue;
8✔
273
        }
8✔
274
        else
275
        {
276
            // treat value as a whole, so do this just once per FadeChannel
277
            universe->writeBlended(address, value, channelCount, m_blendMode);
1,722,393✔
278
        }
279

280
        if (((flags & FadeChannel::Intensity) &&
1,722,393✔
281
            (flags & FadeChannel::HTP) &&
118✔
282
            m_blendMode == Universe::NormalBlend) || m_fadeOut)
1,722,393✔
283
        {
284
            // Remove all channels that reach their target _zero_ value.
285
            // They have no effect either way so removing them saves a bit of CPU.
286
            if (fc.current() == 0 && fc.target() == 0 && fc.isReady())
122✔
287
                it.remove();
3✔
288
        }
289

290
        if (flags & FadeChannel::AutoRemove && value == fc.target())
1,722,393✔
291
            it.remove();
4✔
292
    }
293

294
    // self-request deletion when fadeout is complete
295
    if (m_fadeOut && m_channels.isEmpty())
927,198✔
296
    {
297
        m_fadeOut = false;
2✔
298
        requestDelete();
2✔
299
    }
300
}
927,198✔
301

302
qreal GenericFader::intensity() const
927,219✔
303
{
304
    return m_intensity;
927,219✔
305
}
306

307
void GenericFader::adjustIntensity(qreal fraction)
130✔
308
{
309
    //qDebug() << name() << "I FADER intensity" << fraction << ", PARENT:" << m_parentIntensity;
310
    m_intensity = fraction;
130✔
311
}
130✔
312

313
qreal GenericFader::parentIntensity() const
927,218✔
314
{
315
    return m_parentIntensity;
927,218✔
316
}
317

318
void GenericFader::setParentIntensity(qreal fraction)
125✔
319
{
320
    //qDebug() << name() << "P FADER intensity" << m_intensity << ", PARENT:" << fraction;
321
    m_parentIntensity = fraction;
125✔
322
}
125✔
323

324
bool GenericFader::isPaused() const
×
325
{
326
    return m_paused;
×
327
}
328

329
void GenericFader::setPaused(bool paused)
5✔
330
{
331
    m_paused = paused;
5✔
332
}
5✔
333

334
bool GenericFader::isEnabled() const
927,097✔
335
{
336
    return m_enabled;
927,097✔
337
}
338

339
void GenericFader::setEnabled(bool enable)
×
340
{
341
    m_enabled = enable;
×
342
}
×
343

344
bool GenericFader::isFadingOut() const
4✔
345
{
346
    return m_fadeOut;
4✔
347
}
348

349
void GenericFader::setFadeOut(bool enable, uint fadeTime)
6✔
350
{
351
    m_fadeOut = enable;
6✔
352

353
    if (fadeTime == 0)
6✔
354
        return;
×
355

356
    QReadLocker l(&m_channelsLock);
6✔
357
    QMutableHashIterator <quint32,FadeChannel> it(m_channels);
6✔
358
    while (it.hasNext() == true)
24✔
359
    {
360
        FadeChannel& fc(it.next().value());
18✔
361

362
        fc.setStart(fc.current());
18✔
363
        // all channels should fade to the current universe value
364
        if ((fc.flags() & FadeChannel::Flashing) == 0)
18✔
365
            fc.addFlag(FadeChannel::SetTarget);
18✔
366
        fc.setTarget(0);
18✔
367
        fc.setElapsed(0);
18✔
368
        fc.setReady(false);
18✔
369
        fc.setFadeTime(fc.canFade() ? fadeTime : 0);
18✔
370
        // if flashing, remove the flag and treat
371
        // it like a regular fade out to target
372
        fc.removeFlag(FadeChannel::Flashing);
18✔
373
    }
374
}
6✔
375

376
void GenericFader::setBlendMode(Universe::BlendMode mode)
123✔
377
{
378
    m_blendMode = mode;
123✔
379
}
123✔
380

381
void GenericFader::setMonitoring(bool enable)
×
382
{
383
    m_monitoring = enable;
×
384
}
×
385

386
void GenericFader::resetCrossfade()
×
387
{
388
    qDebug() << name() << "resetting crossfade channels";
×
NEW
389
    QReadLocker l(&m_channelsLock);
×
390
    QMutableHashIterator <quint32,FadeChannel> it(m_channels);
×
391
    while (it.hasNext() == true)
×
392
    {
393
        FadeChannel& fc(it.next().value());
×
394
        fc.removeFlag(FadeChannel::CrossFade);
×
395
    }
396
}
×
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