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

mcallegari / qlcplus / 13633248611

03 Mar 2025 02:31PM UTC coverage: 31.871% (+0.4%) from 31.5%
13633248611

push

github

web-flow
actions: add chrpath to profile

14689 of 46089 relevant lines covered (31.87%)

26426.11 hits per line

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

76.84
/engine/src/rgbscript.cpp
1
/*
2
  Q Light Controller
3
  rgbscript.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 <QXmlStreamReader>
21
#include <QXmlStreamWriter>
22
#include <QCoreApplication>
23
#include <QScriptEngine>
24
#include <QScriptValue>
25
#include <QStringList>
26
#include <QDebug>
27
#include <QFile>
28
#include <QSize>
29
#include <QDir>
30

31
#if defined(WIN32) || defined(Q_OS_WIN)
32
#   include <windows.h>
33
#else
34
#   include <unistd.h>
35
#endif
36

37
#include "rgbscript.h"
38
#include "rgbscriptscache.h"
39

40
QScriptEngine* RGBScript::s_engine = NULL;
41
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
42
  QMutex* RGBScript::s_engineMutex = NULL;
43
#else
44
  QRecursiveMutex* RGBScript::s_engineMutex = NULL;
45
#endif
46

47
/****************************************************************************
48
 * Initialization
49
 ****************************************************************************/
50

51
RGBScript::RGBScript(Doc * doc)
105✔
52
    : RGBAlgorithm(doc)
53
    , m_apiVersion(0)
105✔
54
{
55
}
105✔
56

57
RGBScript::RGBScript(const RGBScript& s)
61✔
58
    : RGBAlgorithm(s.doc())
59
    , m_fileName(s.m_fileName)
61✔
60
    , m_contents(s.m_contents)
61✔
61
    , m_apiVersion(0)
122✔
62
{
63
    if (!m_fileName.isEmpty())
61✔
64
    {
65
        evaluate();
58✔
66
        foreach (RGBScriptProperty cap, s.m_properties)
217✔
67
            setProperty(cap.m_name, s.property(cap.m_name));
202✔
68
    }
69
}
61✔
70

71
RGBScript::~RGBScript()
84✔
72
{
73
}
84✔
74

75
RGBScript &RGBScript::operator=(const RGBScript &s)
1✔
76
{
77
    if (this != &s)
1✔
78
    {
79
        m_fileName = s.m_fileName;
1✔
80
        m_contents = s.m_contents;
1✔
81
        m_apiVersion = s.m_apiVersion;
1✔
82
        evaluate();
1✔
83
        foreach (RGBScriptProperty cap, s.m_properties)
2✔
84
        {
85
            setProperty(cap.m_name, s.property(cap.m_name));
2✔
86
        }
1✔
87
    }
88

89
    return *this;
1✔
90
}
91

92
bool RGBScript::operator==(const RGBScript& s) const
×
93
{
94
    if (this->fileName().isEmpty() == false && this->fileName() == s.fileName())
×
95
        return true;
96
    else
97
        return false;
×
98
}
99

100
RGBAlgorithm* RGBScript::clone() const
18✔
101
{
102
    RGBScript* script = new RGBScript(*this);
18✔
103
    return static_cast<RGBAlgorithm*> (script);
18✔
104
}
105

106
/****************************************************************************
107
 * Load & Evaluation
108
 ****************************************************************************/
109

110
bool RGBScript::load(const QString& fileName)
97✔
111
{
112
    // Create the script engine when it's first needed
113
    initEngine();
97✔
114

115
    QMutexLocker engineLocker(s_engineMutex);
97✔
116

117
    m_contents.clear();
97✔
118
    m_script = QScriptValue();
97✔
119
    m_rgbMap = QScriptValue();
97✔
120
    m_rgbMapStepCount = QScriptValue();
97✔
121
    m_rgbMapSetColors = QScriptValue();
97✔
122
    m_apiVersion = 0;
97✔
123

124
    m_fileName = fileName;
97✔
125
    QFile file(m_fileName);
97✔
126
    if (file.open(QIODevice::ReadOnly) == false)
97✔
127
    {
128
        qWarning() << "Unable to load RGB script" << m_fileName;
×
129
        return false;
×
130
    }
131

132
    QTextStream stream(&file);
97✔
133
    m_contents = stream.readAll();
97✔
134
    file.close();
97✔
135

136
    QScriptSyntaxCheckResult result = QScriptEngine::checkSyntax(m_contents);
97✔
137
    if (result.state() == QScriptSyntaxCheckResult::Valid)
97✔
138
        return evaluate();
97✔
139
    else
140
    {
141
        qWarning() << m_fileName << "Error at line:" << result.errorLineNumber()
×
142
                   << ", column:" << result.errorColumnNumber()
×
143
                   << ":" << result.errorMessage();
×
144
        return false;
×
145
    }
146
}
97✔
147

148
QString RGBScript::fileName() const
41✔
149
{
150
    return m_fileName;
41✔
151
}
152

153
bool RGBScript::evaluate()
161✔
154
{
155
    QMutexLocker engineLocker(s_engineMutex);
161✔
156

157
    m_rgbMap = QScriptValue();
161✔
158
    m_rgbMapStepCount = QScriptValue();
161✔
159
    m_rgbMapSetColors = QScriptValue();
161✔
160
    m_apiVersion = 0;
161✔
161

162
    m_script = s_engine->evaluate(m_contents, m_fileName);
161✔
163
    if (s_engine->hasUncaughtException() == true)
161✔
164
    {
165
        QString msg("%1: %2");
1✔
166
        qWarning() << msg.arg(m_fileName).arg(s_engine->uncaughtException().toString());
1✔
167
        foreach (QString s, s_engine->uncaughtExceptionBacktrace())
2✔
168
            qDebug() << s;
×
169
        return false;
170
    }
1✔
171
    else
172
    {
173
        m_rgbMap = m_script.property("rgbMap");
160✔
174
        if (m_rgbMap.isFunction() == false)
160✔
175
        {
176
            qWarning() << m_fileName << "is missing the rgbMap() function!";
1✔
177
            return false;
1✔
178
        }
179

180
        m_rgbMapStepCount = m_script.property("rgbMapStepCount");
159✔
181
        if (m_rgbMapStepCount.isFunction() == false)
159✔
182
        {
183
            qWarning() << m_fileName << "is missing the rgbMapStepCount() function!";
1✔
184
            return false;
1✔
185
        }
186

187
        m_apiVersion = m_script.property("apiVersion").toInteger();
158✔
188
        if (m_apiVersion > 0)
158✔
189
        {
190
            if (m_apiVersion >= 3)
157✔
191
            {
192
                m_rgbMapSetColors = m_script.property("rgbMapSetColors");
20✔
193
                if (m_rgbMapSetColors.isFunction() == false)
20✔
194
                {
195
                    qWarning() << m_fileName << "is missing the rgbMapSetColors() function!";
×
196
                    return false;
×
197
                }
198

199
                // retrieve the non-mandatory get color function
200
                m_rgbMapGetColors = m_script.property("rgbMapGetColors");
20✔
201
                if (m_rgbMapGetColors.isFunction() == false)
20✔
202
                    qWarning() << m_fileName << "is missing the rgbMapGetColors() function!";
17✔
203
            }
204
            if (m_apiVersion >= 2)
157✔
205
                return loadProperties();
130✔
206
            return true;
207
        }
208
        else
209
        {
210
            qWarning() << m_fileName << "has an invalid apiVersion:" << m_apiVersion;
1✔
211
            return false;
1✔
212
        }
213
    }
214
}
215

216
void RGBScript::initEngine()
97✔
217
{
218
    if (s_engineMutex == NULL)
97✔
219
    {
220
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
221
        s_engineMutex = new QMutex(QMutex::Recursive);
222
#else
223
        s_engineMutex = new QRecursiveMutex();
6✔
224
#endif
225
        s_engine = new QScriptEngine(QCoreApplication::instance());
3✔
226
    }
227
    Q_ASSERT(s_engineMutex != NULL);
228
    Q_ASSERT(s_engine != NULL);
229
}
97✔
230

231
void RGBScript::displayError(QScriptValue e, const QString& fileName)
×
232
{
233
    if (e.isError()) 
×
234
    {
235
        QString msg("%1: Exception at line %2. Error: %3");
×
236
        qWarning() << msg.arg(fileName)
×
237
                         .arg(e.property("lineNumber").toInt32())
×
238
                         .arg(e.toString());
×
239
        qDebug() << "Stack: " << e.property("stack").toString();
240
    }
×
241
}
×
242

243
/****************************************************************************
244
 * Script API
245
 ****************************************************************************/
246

247
int RGBScript::rgbMapStepCount(const QSize& size)
50✔
248
{
249
    QMutexLocker engineLocker(s_engineMutex);
50✔
250

251
    if (m_rgbMapStepCount.isValid() == false)
50✔
252
        return -1;
253

254
    QScriptValueList args;
255
    args << size.width() << size.height();
147✔
256
    QScriptValue value = m_rgbMapStepCount.call(QScriptValue(), args);
49✔
257
    if (value.isError())
49✔
258
    {
259
        displayError(value, m_fileName);
×
260
        return -1;
×
261
    } 
262
    else 
263
    {
264
        int ret = value.isNumber() ? value.toInteger() : -1;
49✔
265
        return ret;
49✔
266
    }
267
}
49✔
268

269
void RGBScript::rgbMapSetColors(QVector<uint> &colors)
1,148✔
270
{
271
    QMutexLocker engineLocker(s_engineMutex);
1,148✔
272
    if (m_apiVersion <= 2)
1,148✔
273
        return;
274

275
    if (m_rgbMapSetColors.isValid() == false)
115✔
276
        return;
277

278
    int accColors = acceptColors();
115✔
279
    int rawColorCount = colors.count();
280
    QScriptValue jsRawColors = s_engine->newArray(accColors);
115✔
281
    for (int i = 0; i < rawColorCount && i < accColors; i++)
315✔
282
        jsRawColors.setProperty(i, QScriptValue(colors.at(i)));
200✔
283

284
    QScriptValueList args;
285
    args << jsRawColors;
286

287
    QScriptValue value = m_rgbMapSetColors.call(QScriptValue(), args);
115✔
288
    if (value.isError())
115✔
289
        displayError(value, m_fileName);
×
290
}
115✔
291

292
QVector<uint> RGBScript::rgbMapGetColors()
1✔
293
{
294
    QMutexLocker engineLocker(s_engineMutex);
1✔
295
    QVector<uint> colArray;
296

297
    if (m_apiVersion <= 2)
1✔
298
        return colArray;
299

300
    if (m_rgbMapGetColors.isValid() == false)
×
301
        return colArray;
302

303
    QScriptValue colors = m_rgbMapGetColors.call();
×
304
    if (colors.isValid() && colors.isArray())
×
305
    {
306
        QVariantList arr = colors.toVariant().toList();
×
307
        foreach (QVariant color, arr)
×
308
            colArray.append(color.toUInt());
×
309
    }
×
310

311
    return colArray;
312
}
×
313

314
void RGBScript::rgbMap(const QSize& size, uint rgb, int step, RGBMap &map)
1,161✔
315
{
316
    QMutexLocker engineLocker(s_engineMutex);
1,161✔
317

318
    if (m_rgbMap.isValid() == false)
1,161✔
319
        return;
320

321
    QScriptValueList args;
322
    args << size.width() << size.height() << rgb << step;
5,800✔
323

324
    QScriptValue yarray = m_rgbMap.call(QScriptValue(), args);
1,160✔
325

326
    if (yarray.isError())
1,160✔
327
        displayError(yarray, m_fileName);
×
328

329
    if (yarray.isArray())
1,160✔
330
    {
331
        int ylen = yarray.property("length").toInteger();
1,160✔
332
        map.resize(ylen);
1,160✔
333
        for (int y = 0; y < ylen && y < size.height(); y++)
15,634✔
334
        {
335
            QScriptValue xarray = yarray.property(QString::number(y));
28,948✔
336
            int xlen = xarray.property("length").toInteger();
14,474✔
337
            map[y].resize(xlen);
14,474✔
338
            for (int x = 0; x < xlen && x < size.width(); x++)
133,586✔
339
            {
340
                QScriptValue yx = xarray.property(QString::number(x));
119,112✔
341
                map[y][x] = yx.toInteger();
119,112✔
342
            }
119,112✔
343
        }
14,474✔
344
    }
345
    else
346
    {
347
        qWarning() << "Returned value is not an array within an array!";
×
348
    }
349
}
1,160✔
350

351
QString RGBScript::name() const
55✔
352
{
353
    QMutexLocker engineLocker(s_engineMutex);
55✔
354

355
    QScriptValue name = m_script.property("name");
55✔
356
    QString ret = name.isValid() ? name.toString() : QString();
55✔
357
    return ret;
55✔
358
}
55✔
359

360
QString RGBScript::author() const
41✔
361
{
362
    QMutexLocker engineLocker(s_engineMutex);
41✔
363

364
    QScriptValue author = m_script.property("author");
41✔
365
    QString ret = author.isValid() ? author.toString() : QString();
41✔
366
    return ret;
41✔
367
}
41✔
368

369
int RGBScript::apiVersion() const
186✔
370
{
371
    return m_apiVersion;
186✔
372
}
373

374
RGBAlgorithm::Type RGBScript::type() const
58✔
375
{
376
    return RGBAlgorithm::Script;
58✔
377
}
378

379
int RGBScript::acceptColors() const
46,988✔
380
{
381
    QMutexLocker engineLocker(s_engineMutex);
46,988✔
382

383
    QScriptValue accColors = m_script.property("acceptColors");
46,988✔
384
    if (accColors.isValid())
46,988✔
385
        return accColors.toInt32();
25,263✔
386
    // if no property is provided, let's assume the script
387
    // will accept both start and end colors
388
    return 2;
389
}
46,988✔
390

391
bool RGBScript::loadXML(QXmlStreamReader &root)
×
392
{
393
    Q_UNUSED(root)
394

395
    return false;
×
396
}
397

398
bool RGBScript::saveXML(QXmlStreamWriter *doc) const
1✔
399
{
400
    Q_ASSERT(doc != NULL);
401

402
    if (apiVersion() > 0 && name().isEmpty() == false)
1✔
403
    {
404
        doc->writeStartElement(KXMLQLCRGBAlgorithm);
1✔
405
        doc->writeAttribute(KXMLQLCRGBAlgorithmType, KXMLQLCRGBScript);
1✔
406
        doc->writeCharacters(name());
1✔
407
        doc->writeEndElement();
1✔
408
        return true;
1✔
409
    }
410
    else
411
    {
412
        return false;
413
    }
414
}
415

416
/************************************************************************
417
 * Capabilities
418
 ************************************************************************/
419

420
QList<RGBScriptProperty> RGBScript::properties()
106✔
421
{
422
    return m_properties;
106✔
423
}
424

425
QHash<QString, QString> RGBScript::propertiesAsStrings()
×
426
{
427
    QMutexLocker engineLocker(s_engineMutex);
×
428

429
    QHash<QString, QString> properties;
430
    foreach (RGBScriptProperty cap, m_properties)
×
431
    {
432
        QScriptValue readMethod = m_script.property(cap.m_readMethod);
×
433
        if (readMethod.isFunction())
×
434
        {
435
            QScriptValueList args;
436
            QScriptValue value = readMethod.call(QScriptValue(), args);
×
437
            if (value.isError())
×
438
            {
439
                displayError(value, m_fileName);
×
440
            }
441
            else if (value.isValid())
×
442
            {
443
                properties.insert(cap.m_name, value.toString());
×
444
            }
445
        }
×
446
    }
×
447
    return properties;
×
448
}
×
449

450
bool RGBScript::setProperty(QString propertyName, QString value)
327✔
451
{
452
    QMutexLocker engineLocker(s_engineMutex);
327✔
453

454
    foreach (RGBScriptProperty cap, m_properties)
915✔
455
    {
456
        if (cap.m_name == propertyName)
915✔
457
        {
458
            QScriptValue writeMethod = m_script.property(cap.m_writeMethod);
327✔
459
            if (writeMethod.isFunction() == false)
327✔
460
            {
461
                qWarning() << name() << "doesn't have a write function for" << propertyName;
×
462
                return false;
×
463
            }
464
            QScriptValueList args;
465
            args << value;
654✔
466
            QScriptValue written = writeMethod.call(QScriptValue(), args);
327✔
467
            if (written.isError())
327✔
468
            {
469
                displayError(written, m_fileName);
×
470
                return false;
×
471
            }
472
            else
473
            {
474
                return true;
475
            }
476
        }
327✔
477
    }
915✔
478
    return false;
×
479
}
480

481
QString RGBScript::property(QString propertyName) const
437✔
482
{
483
    QMutexLocker engineLocker(s_engineMutex);
437✔
484

485
    foreach (RGBScriptProperty cap, m_properties)
1,639✔
486
    {
487
        if (cap.m_name == propertyName)
1,201✔
488
        {
489
            QScriptValue readMethod = m_script.property(cap.m_readMethod);
436✔
490
            if (readMethod.isFunction() == false)
436✔
491
            {
492
                qWarning() << name() << "doesn't have a read function for" << propertyName;
×
493
                return QString();
494
            }
495
            QScriptValueList args;
496
            QScriptValue value = readMethod.call(QScriptValue(), args);
436✔
497
            if (value.isError())
436✔
498
            {
499
                displayError(value, m_fileName);
×
500
                return QString();
501
            }
502
            else if (value.isValid())
436✔
503
            {
504
                return value.toString();
436✔
505
            }
506
            else
507
            {
508
                return QString();
509
            }
510
        }
436✔
511
    }
1,201✔
512
    return QString();
513
}
514

515
bool RGBScript::loadProperties()
160✔
516
{
517
    QMutexLocker engineLocker(s_engineMutex);
160✔
518

519
    QScriptValue svCaps = m_script.property("properties");
160✔
520
    if (svCaps.isArray() == false)
160✔
521
    {
522
        qWarning() << m_fileName << "properties is not an array!";
×
523
        return false;
×
524
    }
525
    QVariant varCaps = svCaps.toVariant();
160✔
526
    if (varCaps.isValid() == false)
160✔
527
    {
528
        qWarning() << m_fileName << "has invalid properties!";
×
529
        return false;
×
530
    }
531

532
    m_properties.clear();
160✔
533

534
    QStringList slCaps = varCaps.toStringList();
160✔
535
    foreach (QString cap, slCaps)
519✔
536
    {
537
        RGBScriptProperty newCap;
359✔
538

539
        QStringList propsList = cap.split("|");
718✔
540
        foreach (QString prop, propsList)
2,513✔
541
        {
542
            QStringList keyValue = prop.split(":");
4,308✔
543
            if (keyValue.length() < 2)
2,154✔
544
            {
545
                qWarning() << prop << ": malformed property. Please fix it.";
×
546
                continue;
547
            }
548
            QString key = keyValue.at(0).simplified();
549
            QString value = keyValue.at(1);
550
            if (key == "name")
2,154✔
551
            {
552
                newCap.m_name = value;
359✔
553
            }
554
            else if (key == "type")
1,795✔
555
            {
556
                if (value == "list") newCap.m_type = RGBScriptProperty::List;
359✔
557
                else if (value == "float") newCap.m_type = RGBScriptProperty::Float;
142✔
558
                else if (value == "range") newCap.m_type = RGBScriptProperty::Range;
142✔
559
                else if (value == "string") newCap.m_type = RGBScriptProperty::String;
×
560
            }
561
            else if (key == "display")
1,436✔
562
            {
563
                newCap.m_displayName = value.simplified();
359✔
564
            }
565
            else if (key == "values")
1,077✔
566
            {
567
                QStringList values = value.split(",");
359✔
568
                switch(newCap.m_type)
359✔
569
                {
570
                    case RGBScriptProperty::List:
571
                        newCap.m_listValues = values;
572
                    break;
573
                    case RGBScriptProperty::Range:
574
                    {
575
                        if (values.length() < 2)
142✔
576
                        {
577
                            qWarning() << value << ": malformed property. A range should be defined as 'min,max'. Please fix it.";
×
578
                        }
579
                        else
580
                        {
581
                            newCap.m_rangeMinValue = values.at(0).toInt();
142✔
582
                            newCap.m_rangeMaxValue = values.at(1).toInt();
142✔
583
                        }
584
                    }
585
                    break;
586
                    default:
587
                        qWarning() << value << ": values cannot be applied before the 'type' property or on type:integer and type:string";
×
588
                    break;
×
589
                }
590
            }
591
            else if (key == "write")
718✔
592
            {
593
                newCap.m_writeMethod = value.simplified();
359✔
594
            }
595
            else if (key == "read")
359✔
596
            {
597
                newCap.m_readMethod = value.simplified();
359✔
598
            }
599
            else
600
            {
601
                qWarning() << value << ": unknown property!";
×
602
            }
603
        }
4,308✔
604

605
        if (newCap.m_name.isEmpty() == false &&
359✔
606
            newCap.m_type != RGBScriptProperty::None)
359✔
607
                m_properties.append(newCap);
359✔
608
    }
359✔
609

610
    return true;
611
}
160✔
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

© 2025 Coveralls, Inc