Skip to content

Commit 1d48bbc

Browse files
Merge pull request mixxxdj#15710 from acolombier/feat/qml-settings-controllers-cpp
Add serialisation and QoL metadata on controller ProductInfo
2 parents 91d79c3 + dcde7b8 commit 1d48bbc

28 files changed

+2392
-85
lines changed

CMakeLists.txt

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2726,6 +2726,7 @@ if(BUILD_TESTING)
27262726
else()
27272727
message(FATAL_ERROR "GTest: not found")
27282728
endif()
2729+
add_compile_definitions(BUILD_TESTING)
27292730
endif()
27302731

27312732
find_package(benchmark)
@@ -3701,8 +3702,9 @@ else()
37013702
find_package(QT 5.12 NAMES Qt5 COMPONENTS Core REQUIRED)
37023703
endif()
37033704
if(QML)
3704-
list(APPEND QT_EXTRA_COMPONENTS "Quick")
37053705
list(APPEND QT_EXTRA_COMPONENTS "LabsQmlModels")
3706+
list(APPEND QT_EXTRA_COMPONENTS "Multimedia")
3707+
list(APPEND QT_EXTRA_COMPONENTS "Quick")
37063708
list(APPEND QT_EXTRA_COMPONENTS "QuickControls2")
37073709
list(APPEND QT_EXTRA_COMPONENTS "QuickControls2Impl")
37083710
list(APPEND QT_EXTRA_COMPONENTS "QuickLayouts")
@@ -3861,6 +3863,7 @@ if(QML)
38613863
src/qml/qmlsettingparameter.cpp
38623864
src/qml/qmltrackproxy.cpp
38633865
src/qml/qmlsoundmanagerproxy.cpp
3866+
src/qml/qmlpreferencesproxy.cpp
38643867
src/waveform/renderers/allshader/digitsrenderer.cpp
38653868
src/waveform/renderers/allshader/waveformrenderbeat.cpp
38663869
src/waveform/renderers/allshader/waveformrenderer.cpp
@@ -4173,6 +4176,14 @@ else()
41734176
COMPONENT applocal
41744177
)
41754178

4179+
# install qml6-module-qtmultimedia
4180+
install(
4181+
DIRECTORY
4182+
"${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}$<$<CONFIG:Debug>:/debug>/Qt6/qml/QtMultimedia"
4183+
DESTINATION "${MIXXX_INSTALL_DATADIR}/Qt6/qml"
4184+
COMPONENT applocal
4185+
)
4186+
41764187
# install qml6-module-qtqml-workerscript
41774188
install(
41784189
DIRECTORY
@@ -4236,6 +4247,22 @@ else()
42364247
DESTINATION "${MIXXX_INSTALL_DATADIR}/Qt6/qml/Qt/labs"
42374248
COMPONENT applocal
42384249
)
4250+
4251+
# install qml6-module-qtquick-dialogs
4252+
install(
4253+
DIRECTORY
4254+
"${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}$<$<CONFIG:Debug>:/debug>/Qt6/qml/QtQuick/Dialogs"
4255+
DESTINATION "${MIXXX_INSTALL_DATADIR}/Qt6/qml/QtQuick"
4256+
COMPONENT applocal
4257+
)
4258+
4259+
# install qml6-module-qt-labs-folderlistmodel
4260+
install(
4261+
DIRECTORY
4262+
"${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}$<$<CONFIG:Debug>:/debug>/Qt6/qml/Qt/labs/folderlistmodel"
4263+
DESTINATION "${MIXXX_INSTALL_DATADIR}/Qt6/qml/Qt/labs"
4264+
COMPONENT applocal
4265+
)
42394266
endif()
42404267

42414268
if(WIN32)
@@ -4547,6 +4574,7 @@ if(RUBBERBAND)
45474574
target_link_libraries(mixxx-lib PUBLIC rubberband::rubberband)
45484575
target_compile_definitions(mixxx-lib PUBLIC __RUBBERBAND__)
45494576
if(QML)
4577+
target_link_libraries(mixxx-qml-lib PRIVATE rubberband::rubberband)
45504578
target_compile_definitions(mixxx-qml-lib PUBLIC __RUBBERBAND__)
45514579
endif()
45524580
target_sources(
@@ -5246,18 +5274,25 @@ if(QT6)
52465274
list(APPEND CPACK_DEBIAN_PACKAGE_DEPENDS "qt6-svg-plugins")
52475275
endif()
52485276
if(QML)
5277+
list(
5278+
APPEND
5279+
CPACK_DEBIAN_PACKAGE_DEPENDS
5280+
"qml6-module-qt-labs-folderlistmodel"
5281+
)
5282+
list(APPEND CPACK_DEBIAN_PACKAGE_DEPENDS "qml6-module-qt-labs-qmlmodels")
52495283
list(
52505284
APPEND
52515285
CPACK_DEBIAN_PACKAGE_DEPENDS
52525286
"qml6-module-qt5compat-graphicaleffects"
52535287
)
5288+
list(APPEND CPACK_DEBIAN_PACKAGE_DEPENDS "qml6-module-qtmultimedia")
5289+
list(APPEND CPACK_DEBIAN_PACKAGE_DEPENDS "qml6-module-qtqml-workerscript")
52545290
list(APPEND CPACK_DEBIAN_PACKAGE_DEPENDS "qml6-module-qtquick-controls")
5291+
list(APPEND CPACK_DEBIAN_PACKAGE_DEPENDS "qml6-module-qtquick-dialogs")
52555292
list(APPEND CPACK_DEBIAN_PACKAGE_DEPENDS "qml6-module-qtquick-layouts")
5293+
list(APPEND CPACK_DEBIAN_PACKAGE_DEPENDS "qml6-module-qtquick-shapes")
52565294
list(APPEND CPACK_DEBIAN_PACKAGE_DEPENDS "qml6-module-qtquick-templates")
52575295
list(APPEND CPACK_DEBIAN_PACKAGE_DEPENDS "qml6-module-qtquick-window")
5258-
list(APPEND CPACK_DEBIAN_PACKAGE_DEPENDS "qml6-module-qt-labs-qmlmodels")
5259-
list(APPEND CPACK_DEBIAN_PACKAGE_DEPENDS "qml6-module-qtquick-shapes")
5260-
list(APPEND CPACK_DEBIAN_PACKAGE_DEPENDS "qml6-module-qtqml-workerscript")
52615296
endif()
52625297
else()
52635298
list(APPEND CPACK_DEBIAN_PACKAGE_DEPENDS "libqt5sql5-sqlite")

packaging/debian/control.in

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,20 @@ Build-Depends: debhelper (>= 11),
1313
qt6-base-private-dev,
1414
qt6-qpa-plugins,
1515
qml6-module-qt5compat-graphicaleffects,
16+
qml6-module-qtmultimedia,
1617
qml6-module-qtqml-workerscript,
1718
qml6-module-qtquick-controls,
1819
qml6-module-qtquick-layouts,
1920
qml6-module-qtquick-shapes,
2021
qml6-module-qtquick-templates,
2122
qml6-module-qtquick-window,
23+
qml6-module-qtquick-dialogs,
2224
qml6-module-qt-labs-qmlmodels,
25+
qml6-module-qt-labs-folderlistmodel,
2326
libqt6core5compat6-dev,
2427
libqt6opengl6-dev,
28+
libqt6multimedia6,
29+
libqt6multimediaquick6,
2530
libqt6sql6-sqlite,
2631
libqt6svg6-dev,
2732
cmake (>= 3.13),

src/controllers/bulk/bulkcontroller.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,14 @@ class BulkController : public Controller {
8686
return m_interfaceNumber;
8787
}
8888

89+
uint8_t getInEndpointAddr() const {
90+
return m_inEndpointAddr;
91+
}
92+
93+
uint8_t getOutEndpointAddr() const {
94+
return m_outEndpointAddr;
95+
}
96+
8997
bool isMappable() const override {
9098
// On raw USB transfer level, there isn't any information about mappable controls
9199
return false;

src/controllers/controllermanager.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ void ControllerManager::slotInitialize() {
168168
#ifdef __HID__
169169
m_enumerators.append(new HidEnumerator());
170170
#endif
171+
emit initialized();
171172
}
172173

173174
void ControllerManager::slotShutdown() {

src/controllers/controllermanager.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class ControllerManager : public QObject {
4545
static QList<QString> getMappingPaths(UserSettingsPointer pConfig);
4646

4747
signals:
48+
void initialized();
4849
void devicesChanged();
4950
void requestSetUpDevices();
5051
void requestShutdown();

src/controllers/controllermappinginfo.cpp

Lines changed: 67 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,65 @@
88

99
Q_LOGGING_CATEGORY(kLogger, "controllers.mappinginfo")
1010

11+
namespace {
12+
// Sanitize a string to a version number, often extracted from the XML property,
13+
// which aims to contain a Mixxx version, from which a given mapping is
14+
// supported. In the current official mapping included in the code base, we have
15+
// a variety of format such as "1.0.0" (correct semver), "2.4" (partial semver),
16+
// "1.7.0+", "1.10.0-beta1+" or "" (invalid semver)
17+
QVersionNumber sanitizeVersion(QString rawVersion) {
18+
return !rawVersion.isEmpty()
19+
? QVersionNumber::fromString(rawVersion.remove(QChar('+')))
20+
: QVersionNumber();
21+
}
22+
} // namespace
23+
24+
bool operator==(const ProductInfo& a, const ProductInfo& b) {
25+
return a.protocol == b.protocol &&
26+
a.vendor_id == b.vendor_id &&
27+
a.product_id == b.product_id &&
28+
a.interface_number == b.interface_number &&
29+
a.usage_page == b.usage_page &&
30+
a.usage == b.usage &&
31+
a.in_epaddr == b.in_epaddr &&
32+
a.out_epaddr == b.out_epaddr;
33+
}
34+
35+
size_t qHash(const ProductInfo& product) {
36+
return qHash(product.protocol) +
37+
qHash(product.vendor_id) +
38+
qHash(product.product_id) +
39+
qHash(product.interface_number) +
40+
qHash(product.usage_page) +
41+
qHash(product.usage) +
42+
qHash(product.in_epaddr) +
43+
qHash(product.out_epaddr);
44+
}
45+
46+
QDebug operator<<(QDebug dbg, const ProductInfo& product) {
47+
dbg << QStringLiteral(
48+
"ProductInfo<protocol=%1, friendlyName=%2, vendor_id=%3, "
49+
"product_id=%4, interface_number=%5>")
50+
.arg(product.protocol,
51+
product.friendlyName,
52+
product.vendor_id,
53+
product.product_id,
54+
product.interface_number);
55+
return dbg;
56+
}
57+
1158
MappingInfo::MappingInfo(const QFileInfo& fileInfo) {
1259
// Parse only the <info> header section from a controller description XML file
1360
// using a streaming parser to avoid loading/parsing the entire (potentially
1461
// very large) XML file.
1562
// Contents parsed by xml path:
16-
// info.name Mapping name, used for drop down menus in dialogs
17-
// info.author Mapping author
18-
// info.description Mapping description
19-
// info.forums Link to mixxx forum discussion for the mapping
20-
// info.wiki Link to mixxx wiki for the mapping
21-
// info.devices.product List of device matches, specific to device type
63+
// MixxxControllerPreset The minimum supported Mixxx version
64+
// info.name Mapping name, used for drop down menus in dialogs
65+
// info.author Mapping author
66+
// info.description Mapping description
67+
// info.forums Link to mixxx forum discussion for the mapping
68+
// info.wiki Link to mixxx wiki for the mapping
69+
// info.devices.product List of device matches, specific to device type
2270
m_path = fileInfo.absoluteFilePath();
2371
m_dirPath = fileInfo.dir().absolutePath();
2472

@@ -94,6 +142,16 @@ MappingInfo::MappingInfo(const QFileInfo& fileInfo) {
94142
}
95143
}
96144

145+
if (xmlElementName == QStringLiteral("MixxxControllerPreset") &&
146+
xmlHierachyDepth == 1) {
147+
QXmlStreamAttributes xmlElementAttributes = xml.attributes();
148+
auto mixxxVersion =
149+
xmlElementAttributes
150+
.value(QStringLiteral("mixxxVersion"))
151+
.toString();
152+
m_mixxxVersion = sanitizeVersion(mixxxVersion);
153+
}
154+
97155
} else if (token == QXmlStreamReader::EndElement) {
98156
const QString name = xml.name().toString();
99157

@@ -145,6 +203,9 @@ ProductInfo MappingInfo::parseBulkProduct(const QXmlStreamAttributes& xmlElement
145203
productInfo.interface_number =
146204
xmlElementAttributes.value(QStringLiteral("interface_number"))
147205
.toString();
206+
productInfo.friendlyName = xmlElementAttributes.value("friendly_name").toString();
207+
208+
productInfo.visualUrl = QUrl(xmlElementAttributes.value("image").toString());
148209
return productInfo;
149210
}
150211

src/controllers/controllermappinginfo.h

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
#pragma once
22

3-
#include <QList>
4-
#include <QString>
3+
#include <QUrl>
4+
#include <QVersionNumber>
55

66
class QXmlStreamAttributes;
77
class QFileInfo;
88

99
struct ProductInfo {
10+
QString friendlyName;
11+
QUrl visualUrl;
12+
1013
QString protocol;
1114
QString vendor_id;
1215
QString product_id;
@@ -21,6 +24,12 @@ struct ProductInfo {
2124
QString out_epaddr;
2225
};
2326

27+
bool operator==(const ProductInfo& a, const ProductInfo& b);
28+
29+
size_t qHash(const ProductInfo& product);
30+
31+
QDebug operator<<(QDebug dbg, const ProductInfo& product);
32+
2433
/// Base class handling enumeration and parsing of mapping info headers
2534
///
2635
/// This class handles enumeration and parsing of controller XML description file
@@ -35,39 +44,55 @@ class MappingInfo {
3544
return m_valid;
3645
}
3746

38-
inline const QString getPath() const {
47+
inline const QString& getPath() const {
3948
return m_path;
4049
}
41-
inline const QString getDirPath() const {
50+
inline const QString& getDirPath() const {
4251
return m_dirPath;
4352
}
44-
inline const QString getName() const {
53+
inline const QString& getName() const {
4554
return m_name;
4655
}
47-
inline const QString getDescription() const {
56+
inline const QVersionNumber& getMixxxVersion() const {
57+
return m_mixxxVersion;
58+
}
59+
inline const QString& getDescription() const {
4860
return m_description;
4961
}
50-
inline const QString getForumLink() const {
62+
inline const QString& getForumLink() const {
5163
return m_forumlink;
5264
}
53-
inline const QString getWikiLink() const {
65+
inline const QString& getWikiLink() const {
5466
return m_wikilink;
5567
}
56-
inline const QString getAuthor() const {
68+
inline const QString& getAuthor() const {
5769
return m_author;
5870
}
5971

6072
inline const QList<ProductInfo>& getProducts() const {
6173
return m_products;
6274
}
6375

76+
bool operator==(const MappingInfo& b) const {
77+
return m_valid == b.m_valid &&
78+
m_path == b.m_path &&
79+
m_dirPath == b.m_dirPath &&
80+
m_name == b.m_name &&
81+
m_author == b.m_author &&
82+
m_description == b.m_description &&
83+
m_forumlink == b.m_forumlink &&
84+
m_wikilink == b.m_wikilink &&
85+
m_products == b.m_products;
86+
}
87+
6488
private:
6589
static ProductInfo parseBulkProduct(const QXmlStreamAttributes& xmlElementAttributes);
6690
static ProductInfo parseHIDProduct(const QXmlStreamAttributes& xmlElementAttributes);
6791

6892
bool m_valid = false;
6993
QString m_path;
7094
QString m_dirPath;
95+
QVersionNumber m_mixxxVersion;
7196
QString m_name;
7297
QString m_author;
7398
QString m_description;

src/controllers/dlgprefcontroller.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class ControllerManager;
2323
class ControllerOutputMappingTableModel;
2424
class ControlPickerMenu;
2525
class DlgControllerLearning;
26+
class LegacyControllerMapping;
2627
class MappingInfoEnumerator;
2728
#ifdef MIXXX_USE_QML
2829
class ControllerScriptEngineLegacy;

src/controllers/hid/hidiooutputreport.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "util/compatibility/qmutex.h"
66

77
struct RuntimeLoggingCategory;
8+
struct hid_device_;
89
typedef struct hid_device_ hid_device;
910

1011
class HidIoOutputReport {

src/controllers/hid/legacyhidcontrollermappingfilehandler.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
#pragma once
22

3+
#ifdef BUILD_TESTING
4+
#include <gtest/gtest_prod.h>
5+
#endif
6+
37
#include "controllers/legacycontrollermappingfilehandler.h"
48

59
class LegacyHidControllerMapping;
@@ -15,4 +19,8 @@ class LegacyHidControllerMappingFileHandler : public LegacyControllerMappingFile
1519
virtual std::shared_ptr<LegacyControllerMapping> load(const QDomElement& root,
1620
const QString& filePath,
1721
const QDir& systemMappingsPath);
22+
23+
#ifdef BUILD_TESTING
24+
FRIEND_TEST(LegacyControllerMappingFileHandlerTest, canSerializeMappingToFile);
25+
#endif
1826
};

0 commit comments

Comments
 (0)