From patchwork Wed Dec 11 14:53:26 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 2422 X-Patchwork-Delegate: jacopo@jmondi.org Return-Path: Received: from relay9-d.mail.gandi.net (relay9-d.mail.gandi.net [217.70.183.199]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 591D0600F5 for ; Wed, 11 Dec 2019 15:51:19 +0100 (CET) X-Originating-IP: 93.34.114.233 Received: from uno.lan (93-34-114-233.ip49.fastwebnet.it [93.34.114.233]) (Authenticated sender: jacopo@jmondi.org) by relay9-d.mail.gandi.net (Postfix) with ESMTPSA id C1727FF809; Wed, 11 Dec 2019 14:51:18 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Wed, 11 Dec 2019 15:53:26 +0100 Message-Id: <20191211145327.58633-1-jacopo@jmondi.org> X-Mailer: git-send-email 2.24.0 MIME-Version: 1.0 Subject: [libcamera-devel] [RFC 1/2] WIP libcamera: controls: Support compound controls X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 11 Dec 2019 14:51:19 -0000 A compound control transports an array of data values. Add support to compound controls to the ControlValue class. Signed-off-by: Jacopo Mondi --- include/libcamera/controls.h | 50 +++- include/libcamera/span.h | 111 +++++++++ src/libcamera/control_ids.in | 0 src/libcamera/control_ids.yaml | 4 + src/libcamera/control_serializer.cpp | 15 +- src/libcamera/controls.cpp | 344 +++++++++++++++++++++++++-- src/libcamera/pipeline/vimc.cpp | 7 + test/controls/compound_controls.cpp | 94 ++++++++ test/controls/meson.build | 1 + 9 files changed, 596 insertions(+), 30 deletions(-) create mode 100644 include/libcamera/span.h create mode 100644 src/libcamera/control_ids.in create mode 100644 test/controls/compound_controls.cpp diff --git a/include/libcamera/controls.h b/include/libcamera/controls.h index 458b84e8fa8c..b9f8d4db0bea 100644 --- a/include/libcamera/controls.h +++ b/include/libcamera/controls.h @@ -11,6 +11,8 @@ #include #include +#include + namespace libcamera { class ControlValidator; @@ -18,8 +20,15 @@ class ControlValidator; enum ControlType { ControlTypeNone, ControlTypeBool, + ControlTypeInteger8, ControlTypeInteger32, ControlTypeInteger64, + ControlTypeFloat, + ControlTypeCompoundBool, + ControlTypeCompoundInt8, + ControlTypeCompoundInt32, + ControlTypeCompoundInt64, + ControlTypeCompoundFloat, }; class ControlValue @@ -27,16 +36,28 @@ class ControlValue public: ControlValue(); ControlValue(bool value); + ControlValue(int8_t value); ControlValue(int32_t value); ControlValue(int64_t value); + ControlValue(float value); + ControlValue(Span &values); + ControlValue(Span &values); + ControlValue(Span &values); + ControlValue(Span &values); + ControlValue(Span &values); + ~ControlValue(); ControlType type() const { return type_; } bool isNone() const { return type_ == ControlTypeNone; } + std::size_t numElements() const { return numElements_; } template - const T &get() const; + const Span get() const; + template void set(const T &value); + template + void set(const Span &values); std::string toString() const; @@ -51,9 +72,23 @@ private: union { bool bool_; + int8_t integer8_; int32_t integer32_; int64_t integer64_; + float float_; }; + + bool *pbool_; + int8_t *p8_; + int32_t *p32_; + int64_t *p64_; + float *pfloat_; + + std::size_t numElements_; + + void release(); + bool compareElement(const ControlValue &other, unsigned int i) const; + std::string elemToString(unsigned int i) const; }; class ControlId @@ -182,6 +217,7 @@ public: private: void generateIdmap(); + bool matchRangeType(enum ControlType type1, enum ControlType type2); ControlIdMap idmap_; }; @@ -212,7 +248,7 @@ public: bool contains(unsigned int id) const; template - const T &get(const Control &ctrl) const + const Span get(const Control &ctrl) const { const ControlValue *val = find(ctrl.id()); if (!val) { @@ -233,6 +269,16 @@ public: val->set(value); } + template + void set(const Control &ctrl, const Span &values) + { + ControlValue *val = find(ctrl.id()); + if (!val) + return; + + val->set(values); + } + const ControlValue &get(unsigned int id) const; void set(unsigned int id, const ControlValue &value); diff --git a/include/libcamera/span.h b/include/libcamera/span.h new file mode 100644 index 000000000000..8ff0ebc7c6d2 --- /dev/null +++ b/include/libcamera/span.h @@ -0,0 +1,111 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * span.h - C++20 std::span<> implementation for C++11 + */ + +#ifndef __LIBCAMERA_SPAN_H__ +#define __LIBCAMERA_SPAN_H__ + +#include + +#include +#include +#include + +namespace libcamera { + +template +class Span +{ +private: + using iterator = T *; + using const_iterator = const T *; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + class Storage + { + public: + Storage(T *ptr, std::size_t size) + : ptr_(ptr), size_(size) + { + } + + T *ptr() const { return ptr_; } + std::size_t size() const { return size_; } + + private: + T *ptr_; + std::size_t size_; + }; + +public: + Span(T &v) + : storage_(&v, 1) + { + } + + Span(const T &v) + : storage_(const_cast(&v), 1) + { + } + + Span(T *v, std::size_t s) + : storage_(v, s) + { + } + + Span(const T *v, std::size_t s) + : storage_(const_cast(v), s) + { + } + + Span(std::initializer_list list) + : storage_(const_cast(list.begin()), list.size()) + { + } + + Span(const Span &other) = default; + + Span &operator=(const Span &other) = default; + operator T() const { return *data(); } + T &operator[](unsigned int index) const + { + if (index >= size()) + return *(end() - 1); + return *(data() + index); + } + + T *data() const { return storage_.ptr(); } + std::size_t size() const { return storage_.size(); } + + constexpr iterator begin() const { return data(); } + constexpr iterator end() const { return data() + size(); } + constexpr iterator cbegin() const { return begin(); } + constexpr iterator cend() const { return end(); } + constexpr reverse_iterator rbegin() const + { + return reverse_iterator(end()); + } + constexpr reverse_iterator rend() const + { + return reverse_iterator(begin()); + } + constexpr const_reverse_iterator crbegin() const + { + return const_reverse_iterator(end()); + } + constexpr const_reverse_iterator crend() const + { + return const_reverse_iterator(begin()); + } + +private: + Storage storage_; +}; + +}; /* namespace libcamera */ + +#endif /* __LIBCAMERA_SPAN_H__ */ diff --git a/src/libcamera/control_ids.in b/src/libcamera/control_ids.in new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/src/libcamera/control_ids.yaml b/src/libcamera/control_ids.yaml index 4befec746a59..b72e81dae060 100644 --- a/src/libcamera/control_ids.yaml +++ b/src/libcamera/control_ids.yaml @@ -50,4 +50,8 @@ controls: type: int32_t description: Specify a fixed gain parameter + - CompoundControl: + type: int8_t + description: A fictional compound control + ... diff --git a/src/libcamera/control_serializer.cpp b/src/libcamera/control_serializer.cpp index b787655e6769..2140d3a5dd3f 100644 --- a/src/libcamera/control_serializer.cpp +++ b/src/libcamera/control_serializer.cpp @@ -30,10 +30,17 @@ LOG_DEFINE_CATEGORY(Serializer) namespace { static constexpr size_t ControlValueSize[] = { - [ControlTypeNone] = 1, - [ControlTypeBool] = sizeof(bool), - [ControlTypeInteger32] = sizeof(int32_t), - [ControlTypeInteger64] = sizeof(int64_t), + [ControlTypeNone] = 1, + [ControlTypeBool] = sizeof(bool), + [ControlTypeInteger8] = sizeof(int8_t), + [ControlTypeInteger32] = sizeof(int32_t), + [ControlTypeInteger64] = sizeof(int64_t), + [ControlTypeFloat] = sizeof(float), + [ControlTypeCompoundBool] = sizeof(bool *), + [ControlTypeCompoundInt8] = sizeof(int8_t *), + [ControlTypeCompoundInt32] = sizeof(int32_t *), + [ControlTypeCompoundInt64] = sizeof(int64_t *), + [ControlTypeCompoundFloat] = sizeof(float *), }; } /* namespace */ diff --git a/src/libcamera/controls.cpp b/src/libcamera/controls.cpp index 1678d3cba4f3..64d99f7669b6 100644 --- a/src/libcamera/controls.cpp +++ b/src/libcamera/controls.cpp @@ -9,7 +9,9 @@ #include #include -#include +#include + +#include #include "control_validator.h" #include "log.h" @@ -69,7 +71,8 @@ LOG_DEFINE_CATEGORY(Controls) * \brief Construct an empty ControlValue. */ ControlValue::ControlValue() - : type_(ControlTypeNone) + : type_(ControlTypeNone), pbool_(nullptr), p8_(nullptr), p32_(nullptr), + p64_(nullptr), pfloat_(nullptr), numElements_(0) { } @@ -78,7 +81,15 @@ ControlValue::ControlValue() * \param[in] value Boolean value to store */ ControlValue::ControlValue(bool value) - : type_(ControlTypeBool), bool_(value) + : type_(ControlTypeBool), bool_(value), pbool_(nullptr), p8_(nullptr), + p32_(nullptr), p64_(nullptr), pfloat_(nullptr), numElements_(1) +{ +} + +ControlValue::ControlValue(int8_t value) + : type_(ControlTypeInteger8), integer8_(value), pbool_(nullptr), + p8_(nullptr), p32_(nullptr), p64_(nullptr), pfloat_(nullptr), + numElements_(1) { } @@ -87,7 +98,9 @@ ControlValue::ControlValue(bool value) * \param[in] value Integer value to store */ ControlValue::ControlValue(int32_t value) - : type_(ControlTypeInteger32), integer32_(value) + : type_(ControlTypeInteger32), integer32_(value), pbool_(nullptr), + p8_(nullptr), p32_(nullptr), p64_(nullptr), pfloat_(nullptr), + numElements_(1) { } @@ -96,8 +109,87 @@ ControlValue::ControlValue(int32_t value) * \param[in] value Integer value to store */ ControlValue::ControlValue(int64_t value) - : type_(ControlTypeInteger64), integer64_(value) + : type_(ControlTypeInteger64), integer64_(value), pbool_(nullptr), + p8_(nullptr), p32_(nullptr), p64_(nullptr), pfloat_(nullptr), + numElements_(1) +{ +} + +ControlValue::ControlValue(float value) + : type_(ControlTypeInteger64), float_(value), pbool_(nullptr), + p8_(nullptr), p32_(nullptr), p64_(nullptr), pfloat_(nullptr), + numElements_(1) +{ +} + +ControlValue::ControlValue(Span &values) + : type_(ControlTypeCompoundBool), pbool_(nullptr), p8_(nullptr), + p32_(nullptr), p64_(nullptr), pfloat_(nullptr), + numElements_(values.size()) +{ + pbool_ = new bool[numElements_]; + memcpy(pbool_, values.data(), sizeof(bool) * numElements_); +} + +ControlValue::ControlValue(Span &values) + : type_(ControlTypeCompoundInt8),pbool_(nullptr), p8_(nullptr), + p32_(nullptr), p64_(nullptr), pfloat_(nullptr), + numElements_(values.size()) +{ + p8_ = new int8_t[numElements_]; + memcpy(p8_, values.data(), sizeof(int8_t) * numElements_); +} + +ControlValue::ControlValue(Span &values) + : type_(ControlTypeCompoundInt32), pbool_(nullptr), p8_(nullptr), + p32_(nullptr), p64_(nullptr), pfloat_(nullptr), + numElements_(values.size()) +{ + p32_ = new int32_t[numElements_]; + memcpy(p32_, values.data(), sizeof(int32_t) * numElements_); +} + +ControlValue::ControlValue(Span &values) + : type_(ControlTypeCompoundInt64), pbool_(nullptr), p8_(nullptr), + p32_(nullptr), p64_(nullptr), pfloat_(nullptr), + numElements_(values.size()) { + p64_ = new int64_t[numElements_]; + memcpy(p64_, values.data(), sizeof(int64_t) * numElements_); +} + +ControlValue::ControlValue(Span &values) + : type_(ControlTypeCompoundFloat), pbool_(nullptr), p8_(nullptr), + p32_(nullptr), p64_(nullptr), pfloat_(nullptr), + numElements_(values.size()) +{ + pfloat_ = new float[numElements_]; + memcpy(pfloat_, values.data(), sizeof(float) * numElements_); +} + +void ControlValue::release() +{ + if (pbool_) + delete[] pbool_; + if (p8_) + delete[] p8_; + if (p32_) + delete[] p32_; + if (p64_) + delete[] p64_; + if (pfloat_) + delete[] pfloat_; + + pbool_ = nullptr; + p8_ = nullptr; + p32_ = nullptr; + p64_ = nullptr; + pfloat_ = nullptr; +} + +ControlValue::~ControlValue() +{ + release(); } /** @@ -130,69 +222,225 @@ ControlValue::ControlValue(int64_t value) #ifndef __DOXYGEN__ template<> -const bool &ControlValue::get() const +const Span ControlValue::get() const { - ASSERT(type_ == ControlTypeBool); + ASSERT(type_ == ControlTypeBool || type_ == ControlTypeCompoundBool); - return bool_; + const bool *p = pbool_ ? pbool_ : &bool_; + /* + * Explicitly create a Span instance, otherwise the compiler + * tries to match the "{ p, numElements_}" construct used in other get() + * operation specialization with the initializer_list constructor of + * class Span (clang++ 8.0.1) + */ + return Span(p, static_cast(numElements_)); } template<> -const int32_t &ControlValue::get() const +const Span ControlValue::get() const { - ASSERT(type_ == ControlTypeInteger32 || type_ == ControlTypeInteger64); + ASSERT(type_ == ControlTypeInteger8 || + type_ == ControlTypeCompoundInt8); - return integer32_; + const int8_t *p = p8_ ? p8_ : &integer8_; + return { p, static_cast(numElements_) }; } template<> -const int64_t &ControlValue::get() const +const Span ControlValue::get() const { - ASSERT(type_ == ControlTypeInteger32 || type_ == ControlTypeInteger64); + ASSERT(type_ == ControlTypeInteger32 || + type_ == ControlTypeInteger64 || + type_ == ControlTypeCompoundInt32 || + type_ == ControlTypeCompoundInt64); - return integer64_; + const int32_t *p = p32_ ? p32_ : &integer32_; + return { p, numElements_ }; +} + +template<> +const Span ControlValue::get() const +{ + ASSERT(type_ == ControlTypeInteger32 || + type_ == ControlTypeInteger64 || + type_ == ControlTypeCompoundInt32 || + type_ == ControlTypeCompoundInt64); + + const int64_t *p = p64_ ? p64_ : &integer64_; + return { p, numElements_ }; +} + +template<> +const Span ControlValue::get() const +{ + ASSERT(type_ == ControlTypeFloat || type_ == ControlTypeCompoundFloat); + + const float *p = pfloat_ ? pfloat_ : &float_; + return { p, numElements_ }; } template<> void ControlValue::set(const bool &value) { + release(); + type_ = ControlTypeBool; bool_ = value; + numElements_ = 1; } template<> void ControlValue::set(const int32_t &value) { + release(); + type_ = ControlTypeInteger32; integer32_ = value; + numElements_ = 1; } template<> void ControlValue::set(const int64_t &value) { + release(); + type_ = ControlTypeInteger64; integer64_ = value; + numElements_ = 1; } + +template<> +void ControlValue::set(const Span &values) +{ + release(); + + type_ = ControlTypeCompoundBool; + numElements_ = values.size(); + + pbool_ = new bool[numElements_]; + memcpy(pbool_, values.data(), sizeof(bool) * numElements_); +} + +template<> +void ControlValue::set(const Span &values) +{ + release(); + + type_ = ControlTypeCompoundInt8; + numElements_ = values.size(); + + p8_ = new int8_t[numElements_]; + memcpy(p8_, values.data(), sizeof(int8_t) * numElements_); +} + +template<> +void ControlValue::set(const Span &values) +{ + release(); + + type_ = ControlTypeCompoundInt32; + numElements_ = values.size(); + + p32_ = new int32_t[numElements_]; + memcpy(p32_, values.data(), sizeof(int32_t) * numElements_); +} + +template<> +void ControlValue::set(const Span &values) +{ + release(); + + type_ = ControlTypeCompoundInt64; + numElements_ = values.size(); + + p64_ = new int64_t[numElements_]; + memcpy(p64_, values.data(), sizeof(int64_t) * numElements_); +} + +template<> +void ControlValue::set(const Span &values) +{ + release(); + + type_ = ControlTypeCompoundFloat; + numElements_ = values.size(); + + pfloat_ = new float[numElements_]; + memcpy(pfloat_, values.data(), sizeof(float) * numElements_); +} + #endif /* __DOXYGEN__ */ /** * \brief Assemble and return a string describing the value * \return A string describing the ControlValue */ -std::string ControlValue::toString() const +std::string ControlValue::elemToString(unsigned int i) const { switch (type_) { - case ControlTypeNone: - return ""; case ControlTypeBool: - return bool_ ? "True" : "False"; + return bool_ ? "True " : "False "; + case ControlTypeInteger8: + return std::to_string(integer8_); case ControlTypeInteger32: return std::to_string(integer32_); case ControlTypeInteger64: return std::to_string(integer64_); + case ControlTypeFloat: + return std::to_string(float_); + case ControlTypeCompoundBool: + return pbool_[i] ? "True " : "False "; + case ControlTypeCompoundInt8: + return std::to_string(p8_[i]) + " "; + case ControlTypeCompoundInt32: + return std::to_string(p32_[i]) + " "; + case ControlTypeCompoundInt64: + return std::to_string(p64_[i]) + " "; + case ControlTypeCompoundFloat: + return std::to_string(pfloat_[i]) + " "; + default: + return ""; } +} + +std::string ControlValue::toString() const +{ + if (ControlTypeNone) + return ""; + + std::string str; + for (unsigned int i = 0; i < numElements_; ++i) + str += elemToString(i); + + return str; +} - return ""; +bool ControlValue::compareElement(const ControlValue &other, unsigned int i) const +{ + switch (type_) { + case ControlTypeBool: + return bool_ == other.bool_; + case ControlTypeInteger8: + return integer8_ == other.integer8_; + case ControlTypeInteger32: + return integer32_ == other.integer32_; + case ControlTypeInteger64: + return integer64_ == other.integer64_; + case ControlTypeFloat: + return float_ == other.float_; + case ControlTypeCompoundBool: + return pbool_[i] == other.pbool_[i]; + case ControlTypeCompoundInt8: + return p8_[i] == other.p8_[i]; + case ControlTypeCompoundInt32: + return p32_[i] == other.p32_[i]; + case ControlTypeCompoundInt64: + return p64_[i] == other.p64_[i]; + case ControlTypeCompoundFloat: + return pfloat_[i] == other.pfloat_[i]; + default: + return false; + } } /** @@ -204,13 +452,27 @@ bool ControlValue::operator==(const ControlValue &other) const if (type_ != other.type_) return false; + if (numElements_ != other.numElements()) + return false; + switch (type_) { case ControlTypeBool: - return bool_ == other.bool_; + case ControlTypeInteger8: case ControlTypeInteger32: - return integer32_ == other.integer32_; case ControlTypeInteger64: - return integer64_ == other.integer64_; + case ControlTypeFloat: + return compareElement(other, 0); + case ControlTypeCompoundBool: + case ControlTypeCompoundInt8: + case ControlTypeCompoundInt32: + case ControlTypeCompoundInt64: + case ControlTypeCompoundFloat: + for (unsigned int i = 0; i < numElements_; ++i) { + if (!compareElement(other, i)) + return false; + } + + return true; default: return false; } @@ -330,6 +592,12 @@ Control::Control(unsigned int id, const char *name) { } +template<> +Control::Control(unsigned int id, const char *name) + : ControlId(id, name, ControlTypeInteger8) +{ +} + template<> Control::Control(unsigned int id, const char *name) : ControlId(id, name, ControlTypeInteger32) @@ -341,6 +609,12 @@ Control::Control(unsigned int id, const char *name) : ControlId(id, name, ControlTypeInteger64) { } + +template<> +Control::Control(unsigned int id, const char *name) + : ControlId(id, name, ControlTypeFloat) +{ +} #endif /* __DOXYGEN__ */ /** @@ -583,15 +857,37 @@ ControlInfoMap::const_iterator ControlInfoMap::find(unsigned int id) const * \return The ControlId map */ +bool ControlInfoMap::matchRangeType(enum ControlType type1, enum ControlType type2) +{ + if (type1 == type2) + return true; + + switch (type1) { + case ControlTypeCompoundInt8: + return type2 == ControlTypeInteger8; + case ControlTypeCompoundInt32: + return type2 == ControlTypeInteger32; + case ControlTypeCompoundInt64: + return type2 == ControlTypeInteger64; + case ControlTypeCompoundFloat: + return type2 == ControlTypeFloat; + default: + return false; + } +} + void ControlInfoMap::generateIdmap() { idmap_.clear(); for (const auto &ctrl : *this) { - if (ctrl.first->type() != ctrl.second.min().type()) { + if (!matchRangeType(ctrl.first->type(), + ctrl.second.min().type())) { LOG(Controls, Error) << "Control " << utils::hex(ctrl.first->id()) - << " type and range type mismatch"; + << " type and range type mismatch: " + << ctrl.first->type() << " - " + << ctrl.second.min().type(); idmap_.clear(); clear(); return; diff --git a/src/libcamera/pipeline/vimc.cpp b/src/libcamera/pipeline/vimc.cpp index f043cf55889e..2bfab35314c4 100644 --- a/src/libcamera/pipeline/vimc.cpp +++ b/src/libcamera/pipeline/vimc.cpp @@ -453,6 +453,13 @@ int VimcCameraData::init(MediaDevice *media) std::forward_as_tuple(range)); } + /* Register a compound control. */ + ctrls.emplace(std::piecewise_construct, + std::forward_as_tuple(&controls::CompoundControl), + std::forward_as_tuple(0, 100)); + + + controlInfo_ = std::move(ctrls); /* Initialize the camera properties. */ diff --git a/test/controls/compound_controls.cpp b/test/controls/compound_controls.cpp new file mode 100644 index 000000000000..de00573df141 --- /dev/null +++ b/test/controls/compound_controls.cpp @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * compound_controls.cpp - CompoundControls test + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "camera_controls.h" + +#include "camera_test.h" +#include "test.h" + +using namespace std; +using namespace libcamera; + +class CompoundControlsTest : public CameraTest, public Test +{ +public: + CompoundControlsTest() + : CameraTest("VIMC Sensor B") + { + } + +protected: + int init() override + { + return status_; + } + + int run() override + { + CameraControlValidator validator(camera_.get()); + ControlList list(controls::controls, &validator); + + /* + * Span + * + * A Span can be initialized with an initializer list + * and sequentially or random accessed + */ + Span span = { 1, 2, 3 }; + for (uint32_t i : span) + cout << i << endl; + cout << span[0] << endl; + + /* + * Compound Controls + * + * As of now, all Controls are now 'compounds' when set with a + * Span<> of values. + * + * We need to define how to establish that a Control is actually + * a compound or supports a single value. + */ + list.set(controls::Brightness, {0, 125, 255}); + Span iSpan = list.get(controls::Brightness); + + cout << iSpan.size() << endl; + for (uint32_t i : iSpan) + cout << i << endl; + + /* + * But they can still be accessed and operated with a single + * value. + */ + list.set(controls::Brightness, 112); + cout << list.get(controls::Brightness) << endl; + + /* + * Or set with a Span and accessed by value. The first item + * is returned. + */ + list.set(controls::Brightness, {50, 125}); + cout << list.get(controls::Brightness) << endl; + + /* The other way around works as well. */ + list.set(controls::Brightness, 133); + Span s = list.get(controls::Brightness); + cout << s << endl; + + return TestPass; + } +}; + +TEST_REGISTER(CompoundControlsTest) diff --git a/test/controls/meson.build b/test/controls/meson.build index f0850df28c8a..f4752dcff33b 100644 --- a/test/controls/meson.build +++ b/test/controls/meson.build @@ -3,6 +3,7 @@ control_tests = [ [ 'control_list', 'control_list.cpp' ], [ 'control_range', 'control_range.cpp' ], [ 'control_value', 'control_value.cpp' ], + [ 'compound_controls', 'compound_controls.cpp'], ] foreach t : control_tests